Index: firmware/App/Controllers/ConductivitySensors.c =================================================================== diff -u -r069fa54e253b9eca056ad51743a414854867b1ac -r9279d01e7e539ad1e788b85db4ea2288965b03c6 --- firmware/App/Controllers/ConductivitySensors.c (.../ConductivitySensors.c) (revision 069fa54e253b9eca056ad51743a414854867b1ac) +++ firmware/App/Controllers/ConductivitySensors.c (.../ConductivitySensors.c) (revision 9279d01e7e539ad1e788b85db4ea2288965b03c6) @@ -1,14 +1,14 @@ /************************************************************************** * -* Copyright (c) 2019-2022 Diality Inc. - All Rights Reserved. +* Copyright (c) 2020-2022 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file ConductivitySensors.c * * @author (last) Dara Navaei -* @date (last) 06-Nov-2021 +* @date (last) 02-Mar-2022 * * @author (original) Quang Nguyen * @date (original) 13-Jul-2020 @@ -18,8 +18,9 @@ #include "ConductivitySensors.h" #include "FPGA.h" -#include "PersistentAlarm.h" +#include "NVDataMgmt.h" #include "MessageSupport.h" +#include "PersistentAlarm.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "TaskPriority.h" @@ -39,7 +40,7 @@ #define COND_CPO_SENSOR_PROBE_TYPE 10 ///< 0.1 K cell constant conductivity probe. #define COND_SENSOR_DECIMAL_CONVERSION 100 ///< Conductivity value from FPGA has two decimal place. -#define COND_SENSOR_TEMPERATURE_COEF 0.02 ///< Linear temperature coefficient of variation at 25 Celcius for fresh water. +#define COND_SENSOR_TEMPERATURE_COEF 0.02F ///< Linear temperature coefficient of variation at 25 Celcius for fresh water. #define COND_SENSOR_REFERENCE_TEMPERATURE 25 ///< Reference temperature for conductivity sensor. #define COND_SENSOR_REPORT_PERIOD ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Broadcast conductivity values message every second. @@ -51,9 +52,9 @@ #define MAX_CONDUCTIVITY_SENSOR_FAILURES 5 ///< maximum number of conductivity sensor errors within window period before alarm. #define MAX_CONDUCTIVITY_SENSOR_FAILURE_WINDOW_MS ( 60 * MS_PER_SECOND ) ///< Conductivity sensor error window. -#define RO_REJECTION_RATIO_OUT_OF_RANGE_VALUE 1.0 ///< Out of range value for RO rejection ratio when CPi conductivity is zero. -#define MAX_RO_REJECTION_RATIO_ALLOW 0.15 ///< Maximum RO rejection ratio. -#define MAX_CPO_CONDUCTIVITY_ALLOW 30.0 ///< Maximum CPo sensor conductivity value. +#define RO_REJECTION_RATIO_OUT_OF_RANGE_VALUE 1.0F ///< Out of range value for RO rejection ratio when CPi conductivity is zero. +#define MAX_RO_REJECTION_RATIO_ALLOW 0.1F ///< Maximum RO rejection ratio. +#define MAX_CPO_CONDUCTIVITY_ALLOW 15.0F ///< Maximum CPo sensor conductivity value. #define COND_SENSOR_PERSISTENCE_PERIOD ( 5 * MS_PER_SECOND ) ///< Persistence period for conductivity sensor out of range error. #define RO_REJECTION_RATIO_PERSISTENCE_PERIOD ( 10 * MS_PER_SECOND ) ///< Persistence period for RO rejection ratio. @@ -62,6 +63,8 @@ #define EMSTAT_PICO_GOOD_STATUS 0x10 ///< Measurement good status. #define EMSTAT_PICO_TIMING_NOT_MET_STATUS 0x11 ///< Measurement takes too long status. #define EMSTAT_PICO_FIFO_EMPTY_MASK 0x8000 ///< Emstat Pico buffer empty indication bit. +#define DATA_PUBLISH_COUNTER_START_COUNT 40 ///< Data publish counter start count. +#define COND_SENSOR_BAD_STATUS_PERSISTENCE_PERIOD ( 1 * MS_PER_SECOND ) ///< Conductivity sensor bad status persistence period. #pragma pack(push,1) /// Emstat pico measurement data package structure @@ -90,14 +93,16 @@ static U08 readCount[ NUM_OF_CONDUCTIVITY_SENSORS ]; ///< Read count for conductivity readings. static U32 internalErrorCount[ NUM_OF_CONDUCTIVITY_SENSORS ]; ///< Internal error count for conductivity readings. static OVERRIDE_F32_T compensatedConductivityValues[ NUM_OF_CONDUCTIVITY_SENSORS ]; ///< Latest compensated conductivity values. +static F32 rawConductivityValues[ NUM_OF_CONDUCTIVITY_SENSORS ]; ///< Latest raw conductivity values. static F32 roRejectionRatio; ///< Latest RO rejection ratio. +static U32 sensorStatus[ NUM_OF_CONDUCTIVITY_SENSORS ]; ///< Latest sensor hardware status. static OVERRIDE_U32_T conductivityDataPublishInterval = { COND_SENSOR_REPORT_PERIOD, COND_SENSOR_REPORT_PERIOD, 0, 0 }; ///< Conductivity sensors publish time interval override. -static U32 conductivityDataPublicationTimerCounter = 0; ///< Conductivity sensors data publish timer counter. +static U32 conductivityDataPublicationTimerCounter; ///< Conductivity sensors data publish timer counter. -static BOOL packageStarted = FALSE; ///< Flag to indicate the start of a package measurement data. -static U08 packageIndex = 0U; ///< Current package measurement data bytes index. +static BOOL packageStarted; ///< Flag to indicate the start of a package measurement data. +static U08 packageIndex; ///< Current package measurement data bytes index. static U08 package[ 50 ]; ///< Storage of package bytes until ready to process. static DG_COND_SENSORS_CAL_RECORD_T condSensorsCalRecord; ///< Conductivity sensors' calibration record. @@ -110,7 +115,6 @@ static U32 prefixStrToSIFactor( U08 prefix ); static void processMeasurementDataPackage( U32 sensorId ); static void processCD1CD2SensorRead( U16 fpgaReadCount, U08 fpgaErrorCount ); -static BOOL processCalibrationData( void ); static F32 getCalibrationAppliedConductivityValue( U32 sensorId, F32 compensatedValue ); /*********************************************************************//** @@ -123,27 +127,34 @@ void initConductivitySensors( void ) { U32 i; - roRejectionRatio = 0.0; + roRejectionRatio = 0.0; + packageIndex = 0U; + packageStarted = FALSE; + conductivityDataPublicationTimerCounter = DATA_PUBLISH_COUNTER_START_COUNT; + for ( i = 0; i < NUM_OF_CONDUCTIVITY_SENSORS; i++ ) { - readCount[ i ] = 0; - internalErrorCount[ i ] = 0; + readCount[ i ] = 0; + internalErrorCount[ i ] = 0; + rawConductivityValues[ i ] = 0.0; - compensatedConductivityValues[ i ].data = 0.0; - compensatedConductivityValues[ i ].ovData = 0.0; + compensatedConductivityValues[ i ].data = 0.0; + compensatedConductivityValues[ i ].ovData = 0.0; compensatedConductivityValues[ i ].ovInitData = 0.0; - compensatedConductivityValues[ i ].override = OVERRIDE_RESET; + compensatedConductivityValues[ i ].override = OVERRIDE_RESET; } setFPGACPiProbeType( COND_CPI_SENSOR_PROBE_TYPE ); setFPGACPoProbeType( COND_CPO_SENSOR_PROBE_TYPE ); initTimeWindowedCount( TIME_WINDOWED_COUNT_FPGA_CONDUCTIVITY_SENSOR_ERROR, MAX_CONDUCTIVITY_SENSOR_FAILURES, MAX_CONDUCTIVITY_SENSOR_FAILURE_WINDOW_MS ); initPersistentAlarm( ALARM_ID_INLET_WATER_HIGH_CONDUCTIVITY, COND_SENSOR_PERSISTENCE_PERIOD, COND_SENSOR_PERSISTENCE_PERIOD ); + initPersistentAlarm( ALARM_ID_INLET_WATER_CONDUCTIVITY_IN_WARNING_RANGE, COND_SENSOR_PERSISTENCE_PERIOD, COND_SENSOR_PERSISTENCE_PERIOD ); initPersistentAlarm( ALARM_ID_INLET_WATER_LOW_CONDUCTIVITY, COND_SENSOR_PERSISTENCE_PERIOD, COND_SENSOR_PERSISTENCE_PERIOD ); initPersistentAlarm( ALARM_ID_RO_REJECTION_RATIO_OUT_OF_RANGE, RO_REJECTION_RATIO_PERSISTENCE_PERIOD, RO_REJECTION_RATIO_PERSISTENCE_PERIOD ); + initPersistentAlarm( ALARM_ID_DG_CONDUCTIVITY_SENSOR_BAD_STATUS, COND_SENSOR_BAD_STATUS_PERSISTENCE_PERIOD, COND_SENSOR_BAD_STATUS_PERSISTENCE_PERIOD ); } /*********************************************************************//** @@ -160,8 +171,8 @@ // Check if a new calibration is available if ( TRUE == isNewCalibrationRecordAvailable() ) { - // Get the new calibration data and check its validity - processCalibrationData(); + getNVRecord2Driver( GET_CAL_CONDUCTIVITY_SENSORS, (U08*)&condSensorsCalRecord, sizeof( condSensorsCalRecord ), + NUM_OF_CAL_DATA_COND_SENSORS, ALARM_ID_DG_COND_SENSORS_INVALID_CAL_RECORD ); } processCPiCPoSensorRead( CONDUCTIVITYSENSORS_CPI_SENSOR, getFPGACPi(), getFPGACPiReadCount(), getFPGACPiErrorCount(), getFPGACPiFault() ); @@ -175,12 +186,23 @@ calcRORejectionRatio(); conductivityDataPublicationTimerCounter = 0; - data.roRejectionRatio = roRejectionRatio; + data.roRejectionRatio = roRejectionRatio; + data.cpi = getConductivityValue( CONDUCTIVITYSENSORS_CPI_SENSOR ); data.cpo = getConductivityValue( CONDUCTIVITYSENSORS_CPO_SENSOR ); data.cd1 = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); data.cd2 = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); + data.cpiRaw = rawConductivityValues[ CONDUCTIVITYSENSORS_CPI_SENSOR ]; + data.cpoRaw = rawConductivityValues[ CONDUCTIVITYSENSORS_CPO_SENSOR ]; + data.cd1Raw = rawConductivityValues[ CONDUCTIVITYSENSORS_CD1_SENSOR ]; + data.cd2Raw = rawConductivityValues[ CONDUCTIVITYSENSORS_CD2_SENSOR ]; + + data.cpiSensorStatus = sensorStatus[ CONDUCTIVITYSENSORS_CPI_SENSOR ]; + data.cpoSensorStatus = sensorStatus[ CONDUCTIVITYSENSORS_CPO_SENSOR ]; + data.cd1SensorStatus = sensorStatus[ CONDUCTIVITYSENSORS_CD1_SENSOR ]; + data.cd2SensorStatus = sensorStatus[ CONDUCTIVITYSENSORS_CD2_SENSOR ]; + broadcastData( MSG_ID_DG_CONDUCTIVITY_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&data, sizeof( CONDUCTIVITY_DATA_T ) ); } } @@ -197,7 +219,8 @@ { SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; - BOOL calStatus = processCalibrationData(); + BOOL calStatus = getNVRecord2Driver( GET_CAL_CONDUCTIVITY_SENSORS, (U08*)&condSensorsCalRecord, sizeof( condSensorsCalRecord ), + NUM_OF_CAL_DATA_COND_SENSORS, ALARM_ID_DG_COND_SENSORS_INVALID_CAL_RECORD ); if ( TRUE == calStatus ) { @@ -214,17 +237,14 @@ /*********************************************************************//** * @brief * The checkInletWaterConductivity function checks inlet water conductivity value - * and triggers an alarm when conductivity value is not within the specified - * values. + * and triggers an alarm when conductivity value is out of allowed range. * @details Inputs: CPi sensor conductivity - * @details Outputs: Trigger warning alarm if conductivity is in the warning - * range. Trigger alarm if conductivity is below minimum conductivity. + * @details Outputs: Trigger alarms when conductivity is out of allowed range * @return none *************************************************************************/ void checkInletWaterConductivity( void ) { #ifndef DISABLE_WATER_QUALITY_CHECK - F32 const conductivity = getConductivityValue( CONDUCTIVITYSENSORS_CPI_SENSOR ); BOOL const isConductTooLow = ( conductivity <= COND_SENSOR_CPI_MIN_VALUE ) ? TRUE : FALSE; @@ -246,7 +266,6 @@ { checkPersistentAlarm( ALARM_ID_INLET_WATER_CONDUCTIVITY_IN_WARNING_RANGE, isConductInWarningRange, conductivity, COND_SENSOR_CPI_WARNING_HIGH ); } - #endif } @@ -298,7 +317,7 @@ * @brief * The calcCompensatedConductivity function calculates the compensated * conductivity based on given temperature and conductivity taken at - * reference temperature of 25 Celcius. + * reference temperature of 25 degree Celsius. * @details Inputs: conductivity, temperature * @details Outputs: none * @param conductivity conductivity value @@ -354,13 +373,14 @@ { if ( ( readCount[ sensorId ] != fpgaReadCount ) ) { - F32 const temperature = getTemperatureValue( associateTempSensor[ sensorId ] ); - F32 const conductivity = ( (F32)( fgpaRead ) / COND_SENSOR_DECIMAL_CONVERSION ); - F32 const compensatedCond = calcCompensatedConductivity( conductivity, temperature ); + F32 temperature = getTemperatureValue( associateTempSensor[ sensorId ] ); + F32 conductivity = ( (F32)( fgpaRead ) / COND_SENSOR_DECIMAL_CONVERSION ); + F32 compensatedCond = calcCompensatedConductivity( conductivity, temperature ); readCount[ sensorId ] = fpgaReadCount; internalErrorCount[ sensorId ] = 0; compensatedConductivityValues[ sensorId ].data = getCalibrationAppliedConductivityValue( sensorId, compensatedCond ); + rawConductivityValues[ sensorId ] = conductivity; // store raw conductivity data from CPi and CPo } else { @@ -427,22 +447,29 @@ static void processMeasurementDataPackage( U32 sensorId ) { EMSTAT_VARIABLE_T const * const measurementPtr = (EMSTAT_VARIABLE_T *)&package; - U32 const status = hexStrToDec( (U08 *)&measurementPtr->status, sizeof( measurementPtr->status ) ); + U32 status = hexStrToDec( (U08 *)&measurementPtr->status, sizeof( measurementPtr->status ) ); + sensorStatus[ sensorId ] = status; if ( EMSTAT_PICO_GOOD_STATUS == status ) { - U32 const prefix = prefixStrToSIFactor( measurementPtr->prefix ); - F32 const resistance = ( ( F32 )( hexStrToDec( measurementPtr->value, sizeof( measurementPtr->value ) ) - EMSTAT_PICO_MEASUREMENT_OFFSET ) / prefix ); - F32 const temperature = getTemperatureValue( associateTempSensor[ sensorId ] ); - F32 const conductivity = ( 1 / resistance * SIEMENS_TO_MICROSIEMENS_CONVERSION ); - F32 const compensatedCond = calcCompensatedConductivity( conductivity, temperature ); + U32 prefix = prefixStrToSIFactor( measurementPtr->prefix ); + F32 resistance = ( ( F32 )( hexStrToDec( measurementPtr->value, sizeof( measurementPtr->value ) ) - EMSTAT_PICO_MEASUREMENT_OFFSET ) / prefix ); + F32 temperature = getTemperatureValue( associateTempSensor[ sensorId ] ); + F32 conductivity = ( 1.0 / resistance * SIEMENS_TO_MICROSIEMENS_CONVERSION ); + F32 compensatedCond = calcCompensatedConductivity( conductivity, temperature ); - internalErrorCount[ sensorId ] = 0; + internalErrorCount[ sensorId ] = 0; compensatedConductivityValues[ sensorId ].data = getCalibrationAppliedConductivityValue( sensorId, compensatedCond ); + rawConductivityValues[ sensorId ] = conductivity; // store raw conductivity data from CD1 and CD2 + + // Clear the alarm + checkPersistentAlarm( ALARM_ID_DG_CONDUCTIVITY_SENSOR_BAD_STATUS, FALSE, status, EMSTAT_PICO_GOOD_STATUS ); } else { - compensatedConductivityValues[ sensorId ].data = 0.0; +#ifndef DISABLE_COND_STATUS_CHECK + checkPersistentAlarm( ALARM_ID_DG_CONDUCTIVITY_SENSOR_BAD_STATUS, TRUE, status, EMSTAT_PICO_GOOD_STATUS ); +#endif } if ( EMSTAT_PICO_TIMING_NOT_MET_STATUS == status ) @@ -523,47 +550,6 @@ /*********************************************************************//** * @brief - * The processCalibrationData function gets the calibration data and makes - * sure it is valid by checking the calibration date. The calibration date - * should not be 0. - * @details Inputs: none - * @details Outputs: condSensorsCalRecord - * @return TRUE if the calibration record is valid, otherwise FALSE - *************************************************************************/ -static BOOL processCalibrationData( void ) -{ - BOOL status = TRUE; - U32 sensor; - - // Get the calibration record from NVDataMgmt - DG_COND_SENSORS_CAL_RECORD_T calData = getDGConducitivitySensorsCalibrationRecord(); - - for ( sensor = 0; sensor < NUM_OF_CAL_DATA_COND_SENSORS; sensor++ ) - { -#ifndef SKIP_CAL_CHECK - // Check if the calibration data that was received from NVDataMgmt is legitimate - // The calibration date item should not be zero. If the calibration date is 0, - // then the data is not stored in the NV memory or it was corrupted. - if ( 0 == calData.condSensors[ sensor ].calibrationTime ) - { - SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_COND_SENSORS_INVALID_CAL_RECORD, (U32)sensor ); - status = FALSE; - } -#endif - - // The calibration data was valid, update the local copy - condSensorsCalRecord.condSensors[ sensor ].fourthOrderCoeff = calData.condSensors[ sensor ].fourthOrderCoeff; - condSensorsCalRecord.condSensors[ sensor ].thirdOrderCoeff = calData.condSensors[ sensor ].thirdOrderCoeff; - condSensorsCalRecord.condSensors[ sensor ].secondOrderCoeff = calData.condSensors[ sensor ].secondOrderCoeff; - condSensorsCalRecord.condSensors[ sensor ].gain = calData.condSensors[ sensor ].gain; - condSensorsCalRecord.condSensors[ sensor ].offset = calData.condSensors[ sensor ].offset; - } - - return status; -} - -/*********************************************************************//** - * @brief * The getCalibrationAppliedConductivityValue function gets the temperature * compensated conductivity value and applies calibration to it. * @details Inputs: condSensorsCalRecord Index: firmware/App/Controllers/ConductivitySensors.h =================================================================== diff -u -r487057777532342a4034df8296310270c8827f14 -r9279d01e7e539ad1e788b85db4ea2288965b03c6 --- firmware/App/Controllers/ConductivitySensors.h (.../ConductivitySensors.h) (revision 487057777532342a4034df8296310270c8827f14) +++ firmware/App/Controllers/ConductivitySensors.h (.../ConductivitySensors.h) (revision 9279d01e7e539ad1e788b85db4ea2288965b03c6) @@ -1,14 +1,14 @@ /************************************************************************** * -* Copyright (c) 2019-2022 Diality Inc. - All Rights Reserved. +* Copyright (c) 2020-2022 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file ConductivitySensors.h * -* @author (last) Quang Nguyen -* @date (last) 18-Aug-2021 +* @author (last) Hung Nguyen +* @date (last) 09-Feb-2022 * * @author (original) Quang Nguyen * @date (original) 13-Jul-2020 @@ -24,8 +24,22 @@ * @defgroup ConductivitySensors ConductivitySensors * @brief Conductivity Sensors monitor module. Monitors and filters conductivity sensor readings. * The module uses Atlas Scientific EC OEM for conductivity sensors CPi and CPo with probe type K 1.0 and 0.1 respectively. - * The module uses Emstat Pico from PalmSens to measure electrical resistance and convert to conductivity. * + * Conductivity Sensor Pre-ROF (CPi) + * Diality P/N: 100511-002 + * Manufacturer: Sensor Development Inc. + * Manufacture P/N: CS51-1.0-PT1000-1/2” NPT-4’ + * + * Conductivity Sensor Post-ROF (CPo) + * Diality P/N: 100511-001 + * Manufacturer: Sensor Development Inc. + * Manufacture P/N: CS51-0.1-PT1000-1/2” NPT-4’ + * + * Conductivity Sensor Module (same module type is used for CPi and CPo) + * Diality P/N: 100512-001 + * Manufacturer: Atlas Scientific + * Manufacture P/N: EC-OEM + * * @addtogroup ConductivitySensors * @{ */ @@ -53,6 +67,14 @@ F32 cpo; ///< CPo conductivity sensor value F32 cd1; ///< CD1 conductivity sensor value F32 cd2; ///< CD2 conductivity sensor value + F32 cpiRaw; ///< CPi raw conductivity sensor value (without 2% temperature compensated) + F32 cpoRaw; ///< CPo raw conductivity sensor value (without 2% temperature compensated) + F32 cd1Raw; ///< CD1 raw conductivity sensor value (without 2% temperature compensated) + F32 cd2Raw; ///< CD2 raw conductivity sensor value (without 2% temperature compensated) + U32 cpiSensorStatus; ///< CPi conductivity sensor status + U32 cpoSensorStatus; ///< CPo conductivity sensor status + U32 cd1SensorStatus; ///< CD1 conductivity sensor status + U32 cd2SensorStatus; ///< CD2 conductivity sensor status } CONDUCTIVITY_DATA_T; // ********** public function prototypes ********** @@ -66,10 +88,14 @@ void checkRORejectionRatio( void ); F32 getConductivityValue( U32 sensorId ); +F32 getRawConductivityValue( U32 sensorId ); BOOL testSetConductivityOverride( U32 sensorId, F32 value ); BOOL testResetConductivityOverride( U32 sensorId ); +BOOL testSetRawConductivityOverride( U32 sensorId, F32 value ); +BOOL testResetRawConductivityOverride( U32 sensorId ); + BOOL testSetConductivityDataPublishIntervalOverride( U32 interval_ms ); BOOL testResetConductivityDataPublishIntervalOverride( void ); Index: firmware/App/Controllers/TemperatureSensors.c =================================================================== diff -u -r2f3090feeb06eb738aefc8eae5f080070a0a8919 -r9279d01e7e539ad1e788b85db4ea2288965b03c6 --- firmware/App/Controllers/TemperatureSensors.c (.../TemperatureSensors.c) (revision 2f3090feeb06eb738aefc8eae5f080070a0a8919) +++ firmware/App/Controllers/TemperatureSensors.c (.../TemperatureSensors.c) (revision 9279d01e7e539ad1e788b85db4ea2288965b03c6) @@ -1,14 +1,14 @@ /************************************************************************** * -* Copyright (c) 2019-2022 Diality Inc. - All Rights Reserved. +* Copyright (c) 2020-2022 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file TemperatureSensors.c * * @author (last) Dara Navaei -* @date (last) 10-Dec-2021 +* @date (last) 02-Mar-2022 * * @author (original) Dara Navaei * @date (original) 08-Apr-2020 @@ -65,14 +65,14 @@ #define MAX_WATER_TEMPERATURE_ALARM 39U ///< High water temperature alarm. #define HEATERS_INTERNAL_TEMPERTURE_CALCULATION_INTERVAL 20U ///< Time interval that is used to calculate the heaters internal temperature. -#define HEATERS_INTERNAL_TC_ADC_TO_TEMP_CONVERSION_COEFF 0.25 ///< Heaters internal temperature sensors ADC to temperature conversion coefficient. -#define HEATERS_COLD_JUNCTION_ADC_TO_TEMP_CONVERSION_COEFF 0.0625 ///< Heaters cold junction temperature sensors ADC to temperature conversion coefficient. +#define HEATERS_INTERNAL_TC_ADC_TO_TEMP_CONVERSION_COEFF 0.25F ///< Heaters internal temperature sensors ADC to temperature conversion coefficient. +#define HEATERS_COLD_JUNCTION_ADC_TO_TEMP_CONVERSION_COEFF 0.0625F ///< Heaters cold junction temperature sensors ADC to temperature conversion coefficient. -#define K_THERMOCOUPLE_TEMP_2_MILLI_VOLT_CONVERSION_COEFF 0.041276 ///< K thermocouple temperature to millivolt conversion coefficient. +#define K_THERMOCOUPLE_TEMP_2_MILLI_VOLT_CONVERSION_COEFF 0.041276F ///< K thermocouple temperature to millivolt conversion coefficient. #define SIZE_OF_THERMOCOUPLE_COEFFICIENTS 10U ///< Size of the thermocouple coefficients. -#define CELSIUS_TO_KELVIN_CONVERSION 273.15 ///< Celsius to Kelvin temperature conversion. -#define ADC_BOARD_TEMP_SENSORS_CONVERSION_CONST 272.5 ///< ADC board temperature sensors conversion constant. +#define CELSIUS_TO_KELVIN_CONVERSION 273.15F ///< Celsius to Kelvin temperature conversion. +#define ADC_BOARD_TEMP_SENSORS_CONVERSION_CONST 272.5F ///< ADC board temperature sensors conversion constant. #define TWELVE_BIT_RESOLUTION 4096U ///< 12 bit resolution conversion. #define ADC_BOARD_TEMP_SENSORS_CONST 0x800000 ///< ADC board temperature sensors constant. @@ -85,10 +85,11 @@ #define FPGA_RAW_ADC_READ_INTERVAL_COUNT 8 ///< Time interval in counts to read the raw ADC reads from FPGA. #define TEMPERATURE_SENSORS_ERROR_FLAG_PERSISTENT_PERIOD ( 5 * MS_PER_SECOND ) ///< Temperature sensors error flag persistent period. -#define TEMP_SENSORS_MIN_ALLOWED_DEGREE_C 0.0 ///< Temperature sensors minimum allowed temperature in C. -#define TEMP_SENSORS_MAX_ALLOWED_DEGREE_C 120.0 ///< Temperature sensors maximum allowed temperature in C. -#define NON_FLUID_PATH_TEMP_SENSORS_MAX_ALLOWED_DEGREE_C 80.0 ///< Non fluid temperature sensors path maximum allowed temperature in C. +#define TEMP_SENSORS_MIN_ALLOWED_DEGREE_C 0.0F ///< Temperature sensors minimum allowed temperature in C. +#define TEMP_SENSORS_MAX_ALLOWED_DEGREE_C 120.0F ///< Temperature sensors maximum allowed temperature in C. +#define NON_FLUID_PATH_TEMP_SENSORS_MAX_ALLOWED_DEGREE_C 80.0F ///< Non fluid temperature sensors path maximum allowed temperature in C. #define TEMP_SENSORS_OUT_OF_RANGE_PERSISTENT_PEROID_MS ( 5 * MS_PER_SECOND ) ///< Temperature sensor out of range persistent period in milliseconds. +#define DATA_PUBLISH_COUNTER_START_COUNT 30 ///< Data publish counter start count. // The count cannot be within 0.1V of the rail on both sides therefore: // ADC count = ((2^12) - 1 / ref voltage) * voltage @@ -133,6 +134,7 @@ static U32 dataPublicationTimerCounter; ///< Temperature sensors data publish timer counter. static OVERRIDE_U32_T tempSensorsPublishInterval = { TEMP_SENSORS_DATA_PUBLISH_INTERVAL, TEMP_SENSORS_DATA_PUBLISH_INTERVAL, 0, 0 }; ///< Temperature sensors publish time interval override. +static DG_TEMP_SENSORS_CAL_RECORD_T tempSensorCalRecord; ///< Temperature sensors calibration record. static const F32 POSITIVE_TC_EXP_A0 = 0.118597600000E0; ///< K TC positive temperature exponent coefficient A0. static const F32 POSITIVE_TC_EXP_A1 = -0.118343200000E-3; ///< K TC positive temperature exponent coefficient A1. @@ -189,7 +191,7 @@ tempSensorsExecState = TEMPSENSORS_EXEC_STATE_START; elapsedTime = 0; internalHeatersConversionTimer = 0; - dataPublicationTimerCounter = 0; + dataPublicationTimerCounter = DATA_PUBLISH_COUNTER_START_COUNT; fpgaRawADCReadInterval = 0; /* NOTE: The temperature sensors do not have conversion coefficient. @@ -309,14 +311,21 @@ *************************************************************************/ SELF_TEST_STATUS_T execTemperatureSensorsSelfTest( void ) { - SELF_TEST_STATUS_T status = SELF_TEST_STATUS_IN_PROGRESS; + SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; - // TODO implement the calibration processing function. - // It returns a pass for now + BOOL calStatus = getNVRecord2Driver( GET_CAL_TEMP_SENSORS, (U08*)&tempSensorCalRecord, sizeof( DG_TEMP_SENSORS_CAL_RECORD_T ), + NUM_OF_CAL_DATA_TEMP_SENSORS, ALARM_ID_NO_ALARM ); - status = SELF_TEST_STATUS_PASSED; + if ( TRUE == calStatus ) + { + result = SELF_TEST_STATUS_PASSED; + } + else + { + result = SELF_TEST_STATUS_FAILED; + } - return status; + return result; } /*********************************************************************//** @@ -577,16 +586,16 @@ case TEMPSENSORS_INTERNAL_TRO_RTD: // 307 case TEMPSENSORS_INLET_DIALYSATE: // 311 case TEMPSENSORS_INTERNAL_TDI_RTD: // 315 - { - // The MSB bit of the last byte is the error flag, so that MSB - // is shifted 31 bits to the first bit of the U32 variable. - // If that bit is a 1, there is either CRC error or Status error - // that are ored on top of each other - U32 errorBit = adc >> 31; - isTemperatureNotValid = errorBit > 0; - checkPersistentAlarm( ALARM_ID_DG_TEMPERATURE_SENSOR_FAULT, isTemperatureNotValid, sensorIndex, - TEMPERATURE_SENSORS_ERROR_FLAG_PERSISTENT_PERIOD ); - } + { + // The MSB bit of the last byte is the error flag, so that MSB + // is shifted 31 bits to the first bit of the U32 variable. + // If that bit is a 1, there is either CRC error or Status error + // that are ored on top of each other + U32 errorBit = adc >> 31; + isTemperatureNotValid = errorBit > 0; + checkPersistentAlarm( ALARM_ID_DG_TEMPERATURE_SENSOR_FAULT, isTemperatureNotValid, sensorIndex, + TEMPERATURE_SENSORS_ERROR_FLAG_PERSISTENT_PERIOD ); + } break; default: @@ -697,9 +706,13 @@ // Check if the ADC value of the sensor is not out of range if ( ( (U32)avgADCReads < TEMP_SESNORS_MIN_ALLOWED_ADC_COUNT ) || ( (U32)avgADCReads > TEMP_SENSORS_MAX_ALLOWED_ADC_COUNT ) ) { - // TODO investigate why the new count check is out of range - //checkPersistentAlarm( ALARM_ID_DG_TEMPERATURE_SENSOR_ADC_OUT_OF_RANGE, TRUE, sensorIndex, avgADCReads ); + checkPersistentAlarm( ALARM_ID_DG_TEMPERATURE_SENSOR_ADC_OUT_OF_RANGE, TRUE, sensorIndex, avgADCReads ); } + else + { + // Clear the alarm if it there was no alarm + checkPersistentAlarm( ALARM_ID_DG_TEMPERATURE_SENSOR_ADC_OUT_OF_RANGE, FALSE, sensorIndex, avgADCReads ); + } // Different sensors have different ADC to temperature conversion methods switch( sensorIndex ) Index: firmware/App/DGCommon.h =================================================================== diff -u -r9f4c7b3eb733b5de7b1667d1b1f43ff3c1ca8b21 -r9279d01e7e539ad1e788b85db4ea2288965b03c6 --- firmware/App/DGCommon.h (.../DGCommon.h) (revision 9f4c7b3eb733b5de7b1667d1b1f43ff3c1ca8b21) +++ firmware/App/DGCommon.h (.../DGCommon.h) (revision 9279d01e7e539ad1e788b85db4ea2288965b03c6) @@ -1,14 +1,14 @@ /************************************************************************** * -* Copyright (c) 2019-2022 Diality Inc. - All Rights Reserved. +* Copyright (c) 2020-2022 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file DGCommon.h * * @author (last) Dara Navaei -* @date (last) 05-Jan-2022 +* @date (last) 02-Mar-2022 * * @author (original) Sean * @date (original) 27-Feb-2020 @@ -25,7 +25,7 @@ #define DG_VERSION_MAJOR 0 #define DG_VERSION_MINOR 6 #define DG_VERSION_MICRO 0 -#define DG_VERSION_BUILD 18 +#define DG_VERSION_BUILD 145 // ********** build switches ********** @@ -35,7 +35,7 @@ // #define BOARD_WITH_NO_HARDWARE 1 // #define TASK_TIMING_OUTPUT_ENABLED 1 // re-purposes drain pump enable pin for task timing // #define DISABLE_HEATERS_AND_TEMPS 1 -// #define DISABLE_ACCELS 1 + #define DISABLE_ACCELS 1 // #define SKIP_POST 1 #define SKIP_CAL_CHECK 1 // #define ENABLE_DIP_SWITCHES 1 @@ -54,15 +54,16 @@ #define DISABLE_FLOW_CHECK_IN_FILL 1 #define IGNORE_CONC_PUMP_IN_HEAT_DISINFECT 1 // #define NEW_FMD_FLOW_SENSOR 1 + #define DISABLE_COND_STATUS_CHECK 1 /// Build switch #define DISABLE_PRIME_CONCENT_LINES 1 #define DISABLE_BICARB_CONDUCTIVITY_TEST 1 #define DISABLE_ACID_CONDUCTIVITY_TEST 1 // Turn these flags on to disable dialysate mixing - #define DISABLE_DIALYSATE_CHECK 1 // Disabled for Tom -// #define DISABLE_MIXING 1 - //#define DISABLE_FLOW_CONTROL_TREATMENT 1 - #define DISABLE_ACK_ALARM 1 + #define DISABLE_DIALYSATE_CHECK 1 // Disabled for Tom // Implement +// #define DISABLE_MIXING 1 // Implement + //#define DISABLE_FLOW_CONTROL_TREATMENT 1 // Not needed + #define DISABLE_ACK_ALARM 1 // Build switch #include #include Index: firmware/App/Modes/ModeDrain.c =================================================================== diff -u -r549b7594d3c9fc908cc95aec23bb131590334879 -r9279d01e7e539ad1e788b85db4ea2288965b03c6 --- firmware/App/Modes/ModeDrain.c (.../ModeDrain.c) (revision 549b7594d3c9fc908cc95aec23bb131590334879) +++ firmware/App/Modes/ModeDrain.c (.../ModeDrain.c) (revision 9279d01e7e539ad1e788b85db4ea2288965b03c6) @@ -8,7 +8,7 @@ * @file ModeDrain.c * * @author (last) Dara Navaei -* @date (last) 01-Jan-2022 +* @date (last) 25-Feb-2022 * * @author (original) Leonardo Baloa * @date (original) 20-Dec-2019 @@ -44,25 +44,26 @@ #define DRAIN_EMPTY_TARE_WAIT ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) /// Time period to wait after drain complete and before taring load cells. #define TARGET_RO_PRESSURE_PSI 130 ///< Target pressure for RO pump. -#define TARGET_RO_FLOW_RATE_L 0.3 ///< Target flow rate for RO pump. +#define TARGET_RO_FLOW_RATE_L 0.3F ///< Target flow rate for RO pump. #define DELAY_RES_DRAIN_VALVE_MS 1000 ///< Delay reservoir drain valve open by 1 second. #define DELAY_DRAIN_PUMP_MS 2000 ///< Delay drain pump on by 2 seconds. #define DIALYSATE_DRAIN_TIME_OUT ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Dialysate drain time out. -/// Time period to wait for concentrate lines to rinse. -#define RINSE_CONCENTRATE_LINES_WAIT ( 25 * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) -/// Reserver the concentrate speed to rinse out concentrate lines. -#define RINSE_SPEED ( ( CONCENTRATE_PUMP_MAX_SPEED - 3.0 ) * -1.0 ) +#define RINSE_CONCENTRATE_LINES_WAIT ( 25 * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Time period to wait for concentrate lines to rinse. +#define RINSE_SPEED ( ( CONCENTRATE_PUMP_MAX_SPEED - 3.0F ) * -1.0F ) ///< Reserver the concentrate speed to rinse out concentrate lines. + // ********** private data ********** static DG_DRAIN_STATE_T drainState; ///< Currently active drain state. static U32 drainEmptyTareTimerCtr; ///< Timer counter for delay between drain complete and load cell tare. static BOOL rinseConcentrateLines; ///< Flag indicates to rinse concentrate lines. static U32 rinseConcentrateLinesTimerCtr; ///< Timer counter for rinsing concentrate lines. static U32 dialysateDrainStartTime; ///< Dialysate drain start time. +static BOOL rinseConcentrateLines; ///< Flag indicates to rinse concentrate lines. +static U32 rinseConcentrateLinesTimerCtr; ///< Timer counter for rinsing concentrate lines. // ********** private function prototypes ********** static DG_DRAIN_STATE_T handleDrainStateStart( void ); @@ -109,6 +110,9 @@ { setValveStateDelayed( VRD2, VALVE_STATE_OPEN, DELAY_RES_DRAIN_VALVE_MS ); } + + initDrainParameters( inactiveReservoir ); + // set initial actuator states setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); setDrainPumpTargetRPMDelayed( TARGET_DRAIN_PUMP_RPM, DELAY_DRAIN_PUMP_MS ); Index: firmware/App/Modes/ModeFill.c =================================================================== diff -u -r395ea41cfa6f0ac980acfa3c6d220bcc12876390 -r9279d01e7e539ad1e788b85db4ea2288965b03c6 --- firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision 395ea41cfa6f0ac980acfa3c6d220bcc12876390) +++ firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision 9279d01e7e539ad1e788b85db4ea2288965b03c6) @@ -8,7 +8,7 @@ * @file ModeFill.c * * @author (last) Dara Navaei -* @date (last) 03-Feb-2022 +* @date (last) 05-Jan-2022 * * @author (original) Leonardo Baloa * @date (original) 19-Nov-2019 @@ -23,6 +23,7 @@ #include "Heaters.h" #include "LoadCell.h" #include "ModeFill.h" +#include "ModeGenIdle.h" #include "NVDataMgmtDGRecords.h" #include "NVDataMgmt.h" #include "OperationModes.h" @@ -31,6 +32,7 @@ #include "Reservoirs.h" #include "ROPump.h" #include "SystemComm.h" +#include "SystemCommMessages.h" #include "TaskGeneral.h" #include "TemperatureSensors.h" #include "Timers.h" @@ -45,26 +47,39 @@ // ********** private definitions ********** #define TARGET_RO_PRESSURE_PSI 130 ///< Target pressure for RO pump. +#define RO_PUMP_400_ML_PER_MIN 400.0 ///< RO pump speed of 400.0 mL/minute. +#define RO_PUMP_800_ML_PER_MIN 800.0 ///< RO pump speed of 800.0 mL/minute. +#define MILLILITERS_PER_LITER 1000.0 ///< One liter is 1000 milliliters +#define ACID_PUMP_20_ML_PER_MIN 20.0 ///< Acid pump speed of 20.0 mL/minute. +#define BICARB_PUMP_40_ML_PER_MIN 40.0 ///< Bicarb pump speed of 40.0 mL/minute. +#define CONCENTRATE_PUMP_40_ML_PER_MIN 40.0 ///< Concentrate pump speed of 40.0 mL/minute. +#define FILL_MODE_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the fill mode data is published on the CAN bus. #define DIALYSATE_FILL_TIME_OUT ( 5 * SEC_PER_MIN * MS_PER_SECOND ) ///< Time out period when reservoir is not filled with correct dialysate. -#define EMPTY_BOTTLE_DETECT_PERSISTENT_PERIOD_MS ( 5 * MS_PER_SECOND ) ///< Persistent period for empty bottle detect. +#define EMPTY_BOTTLE_DETECT_PERSISTENT_PERIOD_MS ( 1 * MS_PER_SECOND ) ///< Persistent period for empty bottle detect. #define CONCENTRATE_PUMP_PRIME_INTERVAL ( 3 * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Persistent time interval for concentrate pumps prime. -#define ACID_BICARB_CONCENTRATE_ADDITION_MULTIPLER 1.06F ///< Acid and bicarbonate concentrates make up around 6% to total volume. -#define FLOW_INTEGRATED_VOLUME_CHECK_TOLERANCE 0.1F ///< Flow integrated volume has 10% tolerance compare to load cell reading. +#define ACID_BICARB_CONCENTRATE_ADDITION_MULTIPLER 1.06 ///< Acid and bicarbonate concentrates make up around 6% to total volume. +#define FLOW_INTEGRATED_VOLUME_CHECK_TOLERANCE 0.1 ///< Flow integrated volume has 10% tolerance compare to load cell reading. -#define CONCENTRATE_PUMP_PRIME_EXTRA_SPEED_ML_MIN 5.0F ///< Concentrate pump additional speed during priming in mL/min. -#define CONCENTRATE_TEST_TIME_OUT_MS ( 45 * MS_PER_SECOND ) ///< Concentrate test time out period in ms. -#define WATER_QUALITY_CHECK_TIME_OUT_MS ( 30 * MS_PER_SECOND ) ///< Inlet water quality check time out period in ms. - -#define DIALYSATE_TEMPERATURE_TOLERANCE_C 2.0F ///< Dialysate temperature tolerance in degree C. -#define DIALYSATE_TEMPERATURE_SENSORS_MAX_DRIFT_C 2.0F ///< Dialysate temperature sensors maximum allowed drift in C. +#define ACID_TEST_CD1_TCD 12252.1 ///< Used for testing CD1 acid theoretical conductivity. +#define ACID_TEST_CD2_TCD ACID_TEST_CD1_TCD ///< Used for testing CD2 acid theoretical conductivity. +#define BICARB_TEST_CD2_TCD 6820.91 ///< Used for testing CD2 bicarb theoretical conductivity. +#define FIVE_PERCENT_FACTOR 0.05 ///< 5.0 / 100.0 used to calculate conductivity within range of -/+ 5%. +#define RO_PUMP_LOOKUP_TABLE_SIZE 4 ///< Size of array used as RO pump speed lookup table. +#define CONCENTRATE_PUMP_PRIME_EXTRA_SPEED_ML_MIN 5.0 ///< Concentrate pump additional speed during priming in mL/min. +#define CONCENTRATE_TEST_TIME_OUT_MS ( 30 * MS_PER_SECOND ) ///< Concentrate test time out period in ms. +#define WATER_QUALITY_TEST_TIME_OUT_MS ( 30 * MS_PER_SECOND ) ///< Inlet water quality test time out period in ms. +#define PRIME_CONCENTRATE_LINES_TIME_OUT_MS ( 95 * MS_PER_SECOND ) ///< Time required to prime the concentrate lines. +#define FLUSH_BUBBLES_PUMP_TIME_OUT_MS ( 2 * MS_PER_SECOND ) ///< RO pump on during flush bubble interval in ms. +#define DIALYSATE_TEMPERATURE_TOLERANCE_C 2.0 ///< Dialysate temperature tolerance in degree C. +#define DIALYSATE_TEMPERATURE_SENSORS_MAX_DRIFT_C 2.0 ///< Dialysate temperature sensors maximum allowed drift in C. #define DIALYSATE_TEMPERATURE_SENSORS_DRIFT_TIMEOUT_MS ( 10 * MS_PER_SECOND ) ///< Dialysate temperature sensors drift timeout in milliseconds. -#define ACID_CONCENTRATION_BOTTLE_VOLUME_ML 3000.0F ///< Volume of acid concentration in ml. -#define BICARB_CONCENTRATION_BOTTLE_VOLUME_ML 3000.0F ///< Volume of bicarb concentration in ml. -#define CONCENTRATION_BOTTLE_LOW_VOLUME_ML 100.0F ///< Concentration bottle low volume in ml. +#define ACID_CONCENTRATION_BOTTLE_VOLUME_ML 3430.0 ///< Bottle volume of acid concentration in mL. +#define BICARB_CONCENTRATION_BOTTLE_VOLUME_ML 3780.0 ///< Bottle volume of bicarb concentration in mL. +#define CONCENTRATION_BOTTLE_LOW_VOLUME_ML 100.0 ///< Concentration bottle low volume in mL. /// Multiplier to convert flow (mL/min) into volume (mL) for period of general task interval. static const F32 FLOW_INTEGRATOR = ( (F32)TASK_GENERAL_INTERVAL / (F32)( SEC_PER_MIN * MS_PER_SECOND ) ); @@ -81,65 +96,101 @@ F32 fillFlowRateAverage; ///< Fill flow average value. F32 fillLastTemperature; ///< Fill last temperature value. BOOL isThisFirstFill; ///< Fill flag to indicate whether it is the first fill or not. + BOOL fillEmptyAcidBottleDetected; ///< Fill acid bottle empty detected. + BOOL fillEmptyBicarbBottleDetected; ///< Fill bicarb bottle empty detected. } FILL_CONDITION_STATUS_T; +static U32 fillModeDataPublicationTimerCounter; ///< Used to schedule dialysate fill data publication to CAN bus. static DG_FILL_MODE_STATE_T fillState; ///< Currently active fill state. static U32 dialysateFillStartTime; ///< Current time when starting to fill dialysate. static F32 reservoirBaseWeight; ///< Fill reservoir base weight. static FILL_CONDITION_STATUS_T fillStatus; ///< Fill condition status. -static U32 waterQualityCheckStartTime; ///< Starting time for inlet water quality check. +static U32 waterQualityTestStartTime; ///< Starting time for inlet water quality test. static U32 concentrateTestStartTime; ///< Starting time for concentrate test. +static U32 concentratePrimingStartTime; ///< Starting time for concentrate priming. +static U32 flushBubblesStartTime; ///< Starting time for flush bubbles. static U32 concentratePumpPrimeCount; ///< Interval count for concentrate pump prime. static F32 totalROFlowRateMLPM; ///< Total RO flow rate over period of time. static F32 acidConductivityTotal; ///< Total of acid conductivity during fill. -static F32 dialysateConductivityTotal; ///< Total of dialysate conductivity during fill. +static F32 bicarbConductivityTotal; ///< Total of bicarb conductivity during fill. static U32 conductivitySampleCount; ///< Sample count of conductivity during fill. -static OVERRIDE_F32_T usedAcidVolumeML = { 0.0, 0.0, 0.0, 0.0 }; ///< The integrated acid concentration volume has been used in ml. -static OVERRIDE_F32_T usedBicarbVolumeML = { 0.0, 0.0, 0.0, 0.0 }; ///< The integrated bicarb concentration volume has been used in ml. +static F32 averageBicarbConductivity; ///< Average bicarb conductivity over 30 seconds. +static F32 averageAcidConductivity; ///< Average acid conductivity over 30 seconds. +static F32 pctDiffInConductivity; ///< Percent difference in conductivity between CD1 (acid) and CD2 (bicarb). +static U32 bicarbConductivitySampleCount; ///< Sample count of bicarb conductivity over 30 seconds. +static U32 acidConductivitySampleCount; ///< Sample count of acid conductivity over 30 seconds. + +static F32 totalBicarbConductivity; ///< Total bicarb conductivity over 30 seconds. +static F32 totalAcidConductivity; ///< Total acid conductivity over 30 seconds. + +static OVERRIDE_F32_T usedAcidVolumeML = { 0.0, 0.0, 0.0, 0.0 }; ///< The integrated acid concentration volume has been used in mL. +static OVERRIDE_F32_T usedBicarbVolumeML = { 0.0, 0.0, 0.0, 0.0 }; ///< The integrated bicarb concentration volume has been used in mL. +static OVERRIDE_U32_T fillModeDataPublishInterval = { FILL_MODE_DATA_PUB_INTERVAL, + FILL_MODE_DATA_PUB_INTERVAL, + 0, 0 }; ///< Interval (in ms) at which to publish fill mode data to CAN bus. + +static F32 roPumpFlushBubblesSpeed[ RO_PUMP_LOOKUP_TABLE_SIZE ] = { RO_PUMP_400_ML_PER_MIN, ///< Lookup table to determine the desired RO speed in mL/min when flushing bubbles. + RO_PUMP_800_ML_PER_MIN, + RO_PUMP_400_ML_PER_MIN, + RO_PUMP_800_ML_PER_MIN }; +static U32 pumpSpeedIndex; ///< Index used to access the desired pump speed in roPumpFlushBubblesSpeed table. + // ********** private function prototypes ********** -static DG_FILL_MODE_STATE_T handleCheckInletWaterState( void ); -static DG_FILL_MODE_STATE_T handleBicarbPumpCheckState( void ); -static DG_FILL_MODE_STATE_T handleAcidPumpCheckState( void ); -static DG_FILL_MODE_STATE_T handleDialysateProductionState( void ); +static DG_FILL_MODE_STATE_T handleTestInletWaterState( void ); +static DG_FILL_MODE_STATE_T handlePrimeConcentrateLines( void ); +static DG_FILL_MODE_STATE_T handleFlushBubbles( void ); +static DG_FILL_MODE_STATE_T handleTestBicarbConductivityState( void ); +static DG_FILL_MODE_STATE_T handleTestAcidConductivityState( void ); +static DG_FILL_MODE_STATE_T handleProduceDialysateState( void ); static DG_FILL_MODE_STATE_T handleDeliverDialysateState( void ); static DG_FILL_MODE_STATE_T handlePausedState( void ); static BOOL isWaterQualityGood( void ); static void checkDialysateTemperatureSensorsDrift( void ); -static BOOL checkDialysateTemperature( void ); static void handleDialysateMixing( F32 measuredROFlowRate_mL_min ); static void setFillInfoToRTCRAM( void ); +static BOOL isValueWithinPercentRange( F32 testValue, F32 baseValue, F32 percentFactor ); +static void publishFillModeData( void ); /*********************************************************************//** * @brief * The initFillMode function initializes the fill mode module. * @details Inputs: none * @details Outputs: fillState, dialysateFillStartTime, reservoirBaseWeight, * totalROFlowRateMLPM, concentrateTestStartTime, acidConductivityTotal, - * dialysateConductivityTotal, conductivitySampleCount, + * bicarbConductivityTotal, conductivitySampleCount, * concentratePumpPrimeCount * @return none *************************************************************************/ void initFillMode( void ) { fillState = DG_FILL_MODE_STATE_START; dialysateFillStartTime = 0; + fillModeDataPublicationTimerCounter = 0; reservoirBaseWeight = 0.0; totalROFlowRateMLPM = 0.0; concentrateTestStartTime = 0; acidConductivityTotal = 0.0; - dialysateConductivityTotal = 0.0; + bicarbConductivityTotal = 0.0; conductivitySampleCount = 0; concentratePumpPrimeCount = 0; + pumpSpeedIndex = 0; + averageBicarbConductivity = 0.0; + averageAcidConductivity = 0.0; + pctDiffInConductivity = 0.0; + bicarbConductivitySampleCount = 0; + acidConductivitySampleCount = 0; + totalBicarbConductivity = 0.0; + totalAcidConductivity = 0.0; - initPersistentAlarm( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE, 0, EMPTY_BOTTLE_DETECT_PERSISTENT_PERIOD_MS ); - initPersistentAlarm( ALARM_ID_BICARB_CONDUCTIVITY_OUT_OF_RANGE, 0, EMPTY_BOTTLE_DETECT_PERSISTENT_PERIOD_MS ); - //initPersistentAlarm( ALARM_ID_DG_DIALYSATE_TEMPERATURE_SENSORS_DRFIT_TIMEOUT, 0, DIALYSATE_TEMPERATURE_SENSORS_DRIFT_TIMEOUT_MS ); + initPersistentAlarm( ALARM_ID_DG_ACID_BOTTLE_LOW_VOLUME, 0, EMPTY_BOTTLE_DETECT_PERSISTENT_PERIOD_MS ); + initPersistentAlarm( ALARM_ID_DG_BICARB_BOTTLE_LOW_VOLUME, 0, EMPTY_BOTTLE_DETECT_PERSISTENT_PERIOD_MS ); + initPersistentAlarm( ALARM_ID_DG_DIALYSATE_TEMPERATURE_SENSORS_DRIFT_TIMEOUT, 0, DIALYSATE_TEMPERATURE_SENSORS_DRIFT_TIMEOUT_MS ); } /*********************************************************************//** @@ -191,6 +242,7 @@ checkInletPressure(); checkRORejectionRatio(); checkDialysateTemperatureSensorsDrift(); + publishFillModeData(); // TODO: Check for open straw door status and alarm if closed // Check if run out of time to fill the reservoir @@ -204,26 +256,34 @@ switch ( fillState ) { case DG_FILL_MODE_STATE_START: - waterQualityCheckStartTime = getMSTimerCount(); - fillState = DG_FILL_MODE_STATE_CHECK_INLET_WATER; + waterQualityTestStartTime = getMSTimerCount(); + fillState = DG_FILL_MODE_STATE_TEST_INLET_WATER; break; - case DG_FILL_MODE_STATE_CHECK_INLET_WATER: - fillState = handleCheckInletWaterState(); + case DG_FILL_MODE_STATE_TEST_INLET_WATER: + fillState = handleTestInletWaterState(); break; - case DG_FILL_MODE_STATE_BICARB_PUMP_CHECK: - fillState = handleBicarbPumpCheckState(); + case DG_FILL_MODE_STATE_PRIME_CONCENTRATE_LINES: + fillState = handlePrimeConcentrateLines(); break; - case DG_FILL_MODE_STATE_ACID_PUMP_CHECK: - fillState = handleAcidPumpCheckState(); + case DG_FILL_MODE_STATE_FLUSH_BUBBLES: + fillState = handleFlushBubbles(); break; - case DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION: - fillState = handleDialysateProductionState(); + case DG_FILL_MODE_STATE_TEST_BICARB_CONDUCTIVITY: + fillState = handleTestBicarbConductivityState(); break; + case DG_FILL_MODE_STATE_TEST_ACID_CONDUCTIVITY: + fillState = handleTestAcidConductivityState(); + break; + + case DG_FILL_MODE_STATE_PRODUCE_DIALYSATE: + fillState = handleProduceDialysateState(); + break; + case DG_FILL_MODE_STATE_DELIVER_DIALYSATE: fillState = handleDeliverDialysateState(); break; @@ -305,6 +365,8 @@ fillStatus.fillTemperatureAverage = getHeaterTargetTemperature( DG_TRIMMER_HEATER ); fillStatus.fillLastTemperature = getHeaterTargetTemperature( DG_TRIMMER_HEATER ) + RESERVOIR_EXTRA_TEMPERATURE; fillStatus.isThisFirstFill = TRUE; + fillStatus.fillEmptyAcidBottleDetected = FALSE; + fillStatus.fillEmptyBicarbBottleDetected = FALSE; } /*********************************************************************//** @@ -322,193 +384,310 @@ /*********************************************************************//** * @brief - * The handleCheckInletWaterState function checks for inlet water quality - * before jumping to dialysate production state. - * @details Inputs: Temperature and conductivity alarms - * @details Outputs: request concentrate pump on and set RO pump flow rate + * The isEmptyAcidBottle function returns the boolean flag that indicates + * whether the acid bottle is empty. + * @details Inputs: none + * @details Outputs: none + * @return fillStatus.fillEmtyAcidBottleDetected + *************************************************************************/ +BOOL isEmptyAcidBottle( void ) +{ + return fillStatus.fillEmptyAcidBottleDetected; +} + +/*********************************************************************//** + * @brief + * The isEmptyBicarbBottle function returns the boolean flag that indicates + * whether the bicarb bottle is empty. + * @details Inputs: none + * @details Outputs: none + * @return fillStatus.fillEmtyBicarbBottleDetected + *************************************************************************/ +BOOL isEmptyBicarbBottle( void ) +{ + return fillStatus.fillEmptyBicarbBottleDetected; +} + +/*********************************************************************//** + * @brief + * The setThisFisrtFillFlag function sets the boolean flag that indicates + * the acid and bicarb bottle need priming. + * @details Inputs: none + * @details Outputs: none + * @param flag to TRUE if prime is needed otherwise FALSE + *************************************************************************/ +void setThisFisrtFillFlag( BOOL flag ) +{ + fillStatus.isThisFirstFill = flag; +} + +/*********************************************************************//** + * @brief + * The handleTestInletWaterState function tests for inlet water quality + * and if this is the first fill of a treatment, prime the acid and bicarb + * lines before jumping to dialysate production state. + * @details Inputs: Temperature, pressure, and conductivity alarms + * @details Outputs: request acid and bicarb pumps on * @return the next state *************************************************************************/ -static DG_FILL_MODE_STATE_T handleCheckInletWaterState( void ) +static DG_FILL_MODE_STATE_T handleTestInletWaterState( void ) { - DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_CHECK_INLET_WATER; + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_TEST_INLET_WATER; #ifndef DISABLE_DIALYSATE_CHECK if ( TRUE == isWaterQualityGood() ) #endif { - // If this is the first fill of a treatment, prime and acid and bicarb lines, otherwise transition + // If this is the first fill of a treatment, prime acid and bicarb lines, otherwise transition // to dialysate production directly +#ifndef DISABLE_MIXING if ( TRUE == isThisTheFirstFill() ) { - concentrateTestStartTime = getMSTimerCount(); - #ifndef DISABLE_MIXING - + // Prepare the acid and bicarb pumps to prime the concentrate lines + setROPumpTargetFlowRateLPM( RO_PUMP_800_ML_PER_MIN / MILLILITERS_PER_LITER, TARGET_RO_PRESSURE_PSI ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, CONCENTRATE_PUMP_MAX_SPEED ); setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, CONCENTRATE_PUMP_MAX_SPEED ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); // TODO if requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); - #endif - result = DG_FILL_MODE_STATE_BICARB_PUMP_CHECK; + + concentratePrimingStartTime = getMSTimerCount(); + result = DG_FILL_MODE_STATE_PRIME_CONCENTRATE_LINES; } else +#endif + { - result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; + result = DG_FILL_MODE_STATE_PRODUCE_DIALYSATE; } } +#ifndef DISABLE_DIALYSATE_CHECK + if ( TRUE == didTimeout( waterQualityTestStartTime, WATER_QUALITY_TEST_TIME_OUT_MS ) ) + { + activateAlarmNoData( ALARM_ID_DG_BAD_INLET_WATER_QUALITY ); // alarm is recoverable should go to fill paused state + result = DG_FILL_MODE_STATE_PAUSED; // paused state should go back to DG_FILL_MODE_STATE_TEST_INLET_WATER + } // when alarm acknowledged +#endif - if ( TRUE == didTimeout( waterQualityCheckStartTime, WATER_QUALITY_CHECK_TIME_OUT_MS ) ) + return result; +} + +/*********************************************************************//** + * @brief + * The handlePrimeConcentrateLines function primes the acid and bicarb + * lines. + * @details Inputs: None + * @details Outputs: None + * @return the next state + *************************************************************************/ +static DG_FILL_MODE_STATE_T handlePrimeConcentrateLines( void ) +{ + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_PRIME_CONCENTRATE_LINES; + +#ifndef DISABLE_PRIME_CONCENT_LINES + if ( TRUE == didTimeout( concentratePrimingStartTime, PRIME_CONCENTRATE_LINES_TIME_OUT_MS ) ) { - activateAlarmNoData( ALARM_ID_DG_BAD_INLET_WATER_QUALITY ); - requestNewOperationMode( DG_MODE_GENE ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); + + // Set the RO pump flow rate in according to the roPumpFlushBubblesSpeed table to flush bubbles + pumpSpeedIndex = 0; + setROPumpTargetFlowRateLPM( roPumpFlushBubblesSpeed[ pumpSpeedIndex ] / MILLILITERS_PER_LITER, TARGET_RO_PRESSURE_PSI ); + flushBubblesStartTime = getMSTimerCount(); } +#endif + // State transition + result = DG_FILL_MODE_STATE_FLUSH_BUBBLES; return result; } /*********************************************************************//** * @brief - * The handleBicarbPumpCheckState function checks conductivity value for + * The handleFlushBubbles function removes the bubbles in the RO lines by + * running the RP pump at 400 mL/minute for 2 seconds 0 + * then 800 mL/minute for 2 seconds 1 + * then 400 mL/minute for 2 seconds 2 + * then 800 mL/minute for 2 seconds 3 + * @details Inputs: None + * @details Outputs: request RO pump flow rate in mL/minute + * @return the next state + *************************************************************************/ +static DG_FILL_MODE_STATE_T handleFlushBubbles( void ) +{ + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_FLUSH_BUBBLES; + + if ( TRUE == didTimeout( flushBubblesStartTime, FLUSH_BUBBLES_PUMP_TIME_OUT_MS ) ) + { + if ( ++pumpSpeedIndex < RO_PUMP_LOOKUP_TABLE_SIZE ) + { + setROPumpTargetFlowRateLPM( roPumpFlushBubblesSpeed[ pumpSpeedIndex ] / MILLILITERS_PER_LITER, TARGET_RO_PRESSURE_PSI ); + flushBubblesStartTime = getMSTimerCount(); + } + else + { + // Initialization + totalBicarbConductivity = 0.0; + averageBicarbConductivity = 0.0; + bicarbConductivitySampleCount = 0; + // Set pumps flow rate to prepare for bicarb conductivity testing + setROPumpTargetFlowRateLPM( RO_PUMP_400_ML_PER_MIN / MILLILITERS_PER_LITER, TARGET_RO_PRESSURE_PSI ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, BICARB_PUMP_40_ML_PER_MIN ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); + // State transition + concentrateTestStartTime = getMSTimerCount(); + result = DG_FILL_MODE_STATE_TEST_BICARB_CONDUCTIVITY; + } + } + + return result; +} + +/*********************************************************************//** + * @brief + * The handleTestBicarbConductivityState function tests conductivity value for * bicarbonate concentrate. * @details Inputs: Bicarbonate conductivity sensor value * @details Outputs: Verified bicarbonate conductivity value * @return the next state *************************************************************************/ -static DG_FILL_MODE_STATE_T handleBicarbPumpCheckState( void ) +static DG_FILL_MODE_STATE_T handleTestBicarbConductivityState( void ) { - DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_BICARB_PUMP_CHECK; - - DG_BICARB_CONCENTRATES_RECORD_T bicarb = getBicarbConcentrateCalRecord(); - F32 measuredROFlowRateMLPM = getMeasuredROFlowRateLPM() * ML_PER_LITER; - F32 bicarbPumpFlowRateMLPM = measuredROFlowRateMLPM * bicarb.bicarbConcentrate[ CAL_DATA_BICARB_CONCENTRATE_1 ].bicarbConcMixRatio - + CONCENTRATE_PUMP_PRIME_EXTRA_SPEED_ML_MIN; - -#ifndef DISABLE_DIALYSATE_CHECK + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_TEST_BICARB_CONDUCTIVITY; F32 bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); -#else - F32 bicarbConductivity = MAX_BICARB_CONCENTRATE_CONDUCTIVITY; -#endif - bicarbPumpFlowRateMLPM = MIN( bicarbPumpFlowRateMLPM, CONCENTRATE_PUMP_MAX_SPEED ); +#ifndef DISABLE_BICARB_CONDUCTIVITY_TEST + totalBicarbConductivity += bicarbConductivity; + bicarbConductivitySampleCount++; - if ( bicarbConductivity >= MIN_BICARB_CONCENTRATE_CONDUCTIVITY ) + if ( TRUE == didTimeout( concentrateTestStartTime, CONCENTRATE_TEST_TIME_OUT_MS ) ) { - // Reduce acid pump speed after reaching minimum conductivity - // This prevents conductivity value to go out of sensor's range - setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, bicarbPumpFlowRateMLPM ); + averageBicarbConductivity = totalBicarbConductivity / bicarbConductivitySampleCount; - if ( concentratePumpPrimeCount++ > CONCENTRATE_PUMP_PRIME_INTERVAL ) + if ( TRUE == isValueWithinPercentRange( averageBicarbConductivity, BICARB_TEST_CD2_TCD, FIVE_PERCENT_FACTOR ) ) { - concentratePumpPrimeCount = 0; - concentrateTestStartTime = getMSTimerCount(); -#ifndef DISABLE_MIXING - setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, CONCENTRATE_PUMP_MAX_SPEED ); - requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); + // Initialization requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); + totalBicarbConductivity = 0.0; + totalAcidConductivity = 0.0; + averageBicarbConductivity = 0.0; + averageAcidConductivity = 0.0; + pctDiffInConductivity = 0.0; + bicarbConductivitySampleCount = 0; + acidConductivitySampleCount = 0; #endif + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); - result = DG_FILL_MODE_STATE_ACID_PUMP_CHECK; + // Set pumps flow rate to prepare for acid conductivity testing + setROPumpTargetFlowRateLPM( RO_PUMP_800_ML_PER_MIN / MILLILITERS_PER_LITER, TARGET_RO_PRESSURE_PSI ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, ACID_PUMP_20_ML_PER_MIN ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); + // State transition + concentrateTestStartTime = getMSTimerCount(); + result = DG_FILL_MODE_STATE_TEST_ACID_CONDUCTIVITY; + +#ifndef DISABLE_BICARB_CONDUCTIVITY_TEST } + else + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_BICARB_CONDUCTIVITY_OUT_OF_RANGE, averageBicarbConductivity ); + requestNewOperationMode( DG_MODE_GENE ); + } } - else - { - concentratePumpPrimeCount = 0; - } +#endif - if ( TRUE == didTimeout( concentrateTestStartTime, CONCENTRATE_TEST_TIME_OUT_MS ) ) - { - SET_ALARM_WITH_1_F32_DATA( ALARM_ID_BICARB_CONDUCTIVITY_OUT_OF_RANGE, bicarbConductivity ); - requestNewOperationMode( DG_MODE_GENE ); - } - return result; } /*********************************************************************//** * @brief - * The handleAcidPumpCheckState function checks conductivity value for + * The handleTestAcidConductivityState function validates that the average + * conductivity of the acid and bicarb concentrate are within 5% of the + * theoretical conductivity over 30 seconds sampling period. * acid concentrate. - * @details Inputs: Acid conductivity sensor value - * @details Outputs: Verified acid conductivity value + * @details Inputs: Acid and bicarb conductivity sensor values + * @details Outputs: Verified acid, bicarb, acid & bicarb conductivity values * @return the next state *************************************************************************/ -static DG_FILL_MODE_STATE_T handleAcidPumpCheckState( void ) +static DG_FILL_MODE_STATE_T handleTestAcidConductivityState( void ) { - DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_ACID_PUMP_CHECK; + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_TEST_ACID_CONDUCTIVITY; + F32 acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); + F32 bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); - DG_ACID_CONCENTRATES_RECORD_T acid = getAcidConcentrateCalRecord(); - F32 measuredROFlowRateMLPM = getMeasuredROFlowRateLPM() * ML_PER_LITER; - F32 acidPumpFlowRateMLPM = measuredROFlowRateMLPM * acid.acidConcentrate[ CAL_DATA_ACID_CONCENTRATE_1 ].acidConcMixRatio + - CONCENTRATE_PUMP_PRIME_EXTRA_SPEED_ML_MIN; +#ifndef DISABLE_ACID_CONDUCTIVITY_TEST + totalBicarbConductivity += bicarbConductivity; + bicarbConductivitySampleCount++; -#ifndef DISABLE_DIALYSATE_CHECK - F32 acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); -#else - F32 acidConductivity = MAX_ACID_CONCENTRATE_CONDUCTIVITY; -#endif + totalAcidConductivity += acidConductivity; + acidConductivitySampleCount++; - acidPumpFlowRateMLPM = MIN( acidPumpFlowRateMLPM, CONCENTRATE_PUMP_MAX_SPEED ); - - if ( acidConductivity >= MIN_ACID_CONCENTRATE_CONDUCTIVITY ) + if ( TRUE == didTimeout( concentrateTestStartTime, CONCENTRATE_TEST_TIME_OUT_MS ) ) { - // Reduce acid pump speed after reaching minimum conductivity - // This prevents conductivity value to go out of sensor's range - setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, acidPumpFlowRateMLPM ); + averageBicarbConductivity = totalBicarbConductivity / bicarbConductivitySampleCount; + averageAcidConductivity = totalAcidConductivity / acidConductivitySampleCount; + pctDiffInConductivity = fabs( 2.0 * ( averageAcidConductivity - averageBicarbConductivity ) / ( averageAcidConductivity + averageBicarbConductivity ) ); - if ( concentratePumpPrimeCount++ > CONCENTRATE_PUMP_PRIME_INTERVAL ) + if ( ( TRUE == isValueWithinPercentRange(averageBicarbConductivity, BICARB_TEST_CD2_TCD, FIVE_PERCENT_FACTOR) ) && + ( TRUE == isValueWithinPercentRange(averageAcidConductivity, ACID_TEST_CD2_TCD, FIVE_PERCENT_FACTOR) ) && + ( pctDiffInConductivity < FIVE_PERCENT_FACTOR ) ) { - concentratePumpPrimeCount = 0; +#endif + // Do the necessary setup here before transition to Produce Dialysate State fillStatus.isThisFirstFill = FALSE; requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); - result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; + setROPumpTargetFlowRateLPM( getTargetFillFlowRateLPM(), TARGET_RO_PRESSURE_PSI ); + result = DG_FILL_MODE_STATE_PRODUCE_DIALYSATE; + +#ifndef DISABLE_ACID_CONDUCTIVITY_TEST } + else + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE, averageAcidConductivity ); + requestNewOperationMode( DG_MODE_GENE ); + } } - else - { - concentratePumpPrimeCount = 0; - } - if ( TRUE == didTimeout( concentrateTestStartTime, CONCENTRATE_TEST_TIME_OUT_MS ) ) - { - SET_ALARM_WITH_1_F32_DATA( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE, acidConductivity ); - requestNewOperationMode( DG_MODE_GENE ); - } +#endif return result; } /*********************************************************************//** * @brief - * The handleDialysateProductionState function executes the dialysate production + * The handleProduceDialysateState function executes the dialysate production * state of the fill mode state machine. * @details Inputs: inlet water quality and dialysate temperature * @details Outputs: none * @return the next state *************************************************************************/ -static DG_FILL_MODE_STATE_T handleDialysateProductionState( void ) +static DG_FILL_MODE_STATE_T handleProduceDialysateState( void ) { - DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_PRODUCE_DIALYSATE; F32 measuredROFlowRateMLPM = getMeasuredROFlowRateLPM() * ML_PER_LITER; #ifndef DISABLE_DIALYSATE_CHECK - if ( ( TRUE == isWaterQualityGood() ) && ( TRUE == checkDialysateTemperature() ) ) + if ( TRUE == isWaterQualityGood() ) // SRSDG 216 #else if ( TRUE ) #endif { // Prime mixing before deliver result to reservoir handleDialysateMixing( measuredROFlowRateMLPM ); #ifndef DISABLE_MIXING - requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); // SRSDG 217 + requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); // SRSDG 217 + + if ( concentratePumpPrimeCount++ > CONCENTRATE_PUMP_PRIME_INTERVAL ) #endif - setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); - result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; + { + fillStatus.isThisFirstFill = FALSE; + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); + result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; + } } else { concentratePumpPrimeCount = 0; - requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); } return result; @@ -524,20 +703,15 @@ *************************************************************************/ static DG_FILL_MODE_STATE_T handleDeliverDialysateState( void ) { - F32 integratedVolumeML; - - F32 measuredROFlowRateMLPM = getMeasuredROFlowRateLPM() * ML_PER_LITER; - F32 acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); - F32 dialysateConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); - BOOL isAcidConductivityOutOfRange = ( acidConductivity <= MIN_ACID_CONCENTRATE_CONDUCTIVITY ) || - ( acidConductivity >= MAX_ACID_CONCENTRATE_CONDUCTIVITY ) ? TRUE : FALSE; - BOOL isDialysateConductivityOutOfRange = ( dialysateConductivity <= MIN_DIALYSATE_CONDUCTIVITY ) || - ( dialysateConductivity >= MAX_DIALYSATE_CONDUCTIVITY ) ? TRUE : FALSE; - DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; DG_RESERVOIR_ID_T inactiveReservoir = getInactiveReservoir(); - // Set concentrate pumps speed based off RO pump flow rate + F32 integratedVolumeML; + F32 measuredROFlowRateMLPM = getMeasuredROFlowRateLPM() * ML_PER_LITER; + F32 acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); + F32 bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); + + // Set concentrate pumps speed based on the RO pump flow rate handleDialysateMixing( measuredROFlowRateMLPM ); totalROFlowRateMLPM += measuredROFlowRateMLPM; @@ -546,7 +720,7 @@ usedBicarbVolumeML.data += getMeasuredPumpSpeed( CONCENTRATEPUMPS_CP2_BICARB ) * FLOW_INTEGRATOR; acidConductivityTotal += acidConductivity; - dialysateConductivityTotal += dialysateConductivity; + bicarbConductivityTotal += bicarbConductivity; conductivitySampleCount++; // DG is delivering dialysate keep collecting the sample counter and the measured flow @@ -555,65 +729,74 @@ fillStatus.fillTemperatureRunningSum += getTemperatureValue( (U32)TEMPSENSORS_OUTLET_PRIMARY_HEATER ); #ifndef DISABLE_DIALYSATE_CHECK - if ( ( isWaterQualityGood() != TRUE ) || ( checkDialysateTemperature() != TRUE ) ) + if ( ( isWaterQualityGood() != TRUE ) ) // SRSDG 240 , SRSDG 397 { requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); - result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; + result = DG_FILL_MODE_STATE_PRODUCE_DIALYSATE; } +#endif - if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE, isAcidConductivityOutOfRange ) ) +#ifndef DISABLE_MIXING + // Detect empty bottles using integrated volumes // TODO: empty bottles detection using conductivity sensors + if ( ( ( ACID_CONCENTRATION_BOTTLE_VOLUME_ML - getF32OverrideValue( &usedAcidVolumeML ) ) <= CONCENTRATION_BOTTLE_LOW_VOLUME_ML ) ) // || // SRSDG 437 { usedAcidVolumeML.data = 0.0; requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); - result = DG_FILL_MODE_STATE_PAUSED; + fillStatus.isThisFirstFill = TRUE; // empty bottles need replaced, set isThisFirstFill to FALSE so that prime, flush, acid & bicarb test are needed + fillStatus.fillEmptyAcidBottleDetected = TRUE; // set this variable to FALSE when user presses OK on the alarm to confirm bottle has been replaced and resume } - if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_BICARB_CONDUCTIVITY_OUT_OF_RANGE, isDialysateConductivityOutOfRange ) ) + if ( ( ( BICARB_CONCENTRATION_BOTTLE_VOLUME_ML - getF32OverrideValue( &usedBicarbVolumeML ) ) <= CONCENTRATION_BOTTLE_LOW_VOLUME_ML ) ) // || // SRSDG 438 { usedBicarbVolumeML.data = 0.0; requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); - result = DG_FILL_MODE_STATE_PAUSED; + fillStatus.isThisFirstFill = TRUE; + fillStatus.fillEmptyBicarbBottleDetected = TRUE; } #endif -#ifndef DISABLE_ACID_BICARB_ALARMS - if ( ( ACID_CONCENTRATION_BOTTLE_VOLUME_ML - getF32OverrideValue( &usedAcidVolumeML ) ) <= CONCENTRATION_BOTTLE_LOW_VOLUME_ML ) - { - activateAlarmNoData( ALARM_ID_DG_ACID_BOTTLE_LOW_VOLUME ); - } - if ( ( BICARB_CONCENTRATION_BOTTLE_VOLUME_ML - getF32OverrideValue( &usedBicarbVolumeML ) ) <= CONCENTRATION_BOTTLE_LOW_VOLUME_ML ) - { - activateAlarmNoData( ALARM_ID_DG_BICARB_BOTTLE_LOW_VOLUME ); - } -#endif - // If we've reached our target fill to volume (by weight), we're done filling - go back to generation idle mode + setBadAvgConductivityDetectedFlag( FALSE ); + + // If we've reached our target fill to volume (by weight), we're done filling - go back to generation idle mode // SRSDG 398 if ( TRUE == hasTargetFillVolumeBeenReached( inactiveReservoir ) ) { F32 filledVolumeML = getReservoirWeight( inactiveReservoir ) - reservoirBaseWeight; F32 integratedVolumeToLoadCellReadingPercent = fabs( 1 - ( filledVolumeML / integratedVolumeML ) ); - F32 avgAcidConductivity = acidConductivityTotal / conductivitySampleCount; // TODO - should we be checking this below? - F32 avgDialysateConductivity = dialysateConductivityTotal / conductivitySampleCount; + F32 avgAcidConductivity = acidConductivityTotal / conductivitySampleCount; + F32 avgBicarbConductivity = bicarbConductivityTotal / conductivitySampleCount; - if ( integratedVolumeToLoadCellReadingPercent > FLOW_INTEGRATED_VOLUME_CHECK_TOLERANCE ) - { #ifndef DISABLE_FLOW_CHECK_IN_FILL + if ( integratedVolumeToLoadCellReadingPercent > FLOW_INTEGRATED_VOLUME_CHECK_TOLERANCE ) // SRSDG 439 + { SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DG_FLOW_METER_CHECK_FAILURE, filledVolumeML, integratedVolumeML ); + } #endif + +#ifndef DISABLE_DIALYSATE_CHECK // SRSDG 400 + if ( TRUE == isValueWithinPercentRange( avgBicarbConductivity, BICARB_NORMAL_CONDUCTIVITY, FIVE_PERCENT_FACTOR ) ) + { + fillStatus.isThisFirstFill = TRUE; + setBadAvgConductivityDetectedFlag( TRUE ); // signal idle bad avg conductivity detected + setThisFisrtFillFlag( TRUE ); + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_FILL_CONDUCTIVITY_OUT_OF_RANGE, avgBicarbConductivity, BICARB_NORMAL_CONDUCTIVITY ); // trigger replace bottles alarm #1 + activateAlarmNoData ( ALARM_ID_CREATING_DIALYSATE_PLEASE_WAIT ); } -#ifndef DISABLE_DIALYSATE_CHECK - if ( ( avgDialysateConductivity < MIN_DIALYSATE_CONDUCTIVITY ) || ( avgDialysateConductivity > MAX_DIALYSATE_CONDUCTIVITY ) ) + if ( TRUE == isValueWithinPercentRange( avgAcidConductivity, ACID_NORMAL_CONDUCTIVITY, FIVE_PERCENT_FACTOR ) ) { - SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DIALYSATE_CONDUCTIVITY_FAULT, avgAcidConductivity, avgDialysateConductivity ); + fillStatus.isThisFirstFill = TRUE; + setBadAvgConductivityDetectedFlag( TRUE ); // signal idle bad avg conductivity detected + setThisFisrtFillFlag( TRUE ); + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_FILL_CONDUCTIVITY_OUT_OF_RANGE, avgAcidConductivity, ACID_NORMAL_CONDUCTIVITY ); // trigger replace bottles alarm #1 then + activateAlarmNoData ( ALARM_ID_CREATING_DIALYSATE_PLEASE_WAIT ); } #endif - // Done with this fill. Calculate the average fill flow rate and average temperature // Reset the variables for the next fill // Get the last fill temperature before leaving to Generation Idle @@ -626,7 +809,7 @@ // Write the latest fill data into the RTC RAM for heaters control // TODO test this and make sure it is writing it correctly - setFillInfoToRTCRAM(); + setFillInfoToRTCRAM(); // SRSDG ??? requestNewOperationMode( DG_MODE_GENE ); } @@ -646,10 +829,9 @@ { DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_PAUSED; - if ( ( FALSE == isAlarmActive( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE ) ) && - ( FALSE == isAlarmActive( ALARM_ID_BICARB_CONDUCTIVITY_OUT_OF_RANGE ) ) ) + if ( FALSE == isAlarmActive( ALARM_ID_DG_BAD_INLET_WATER_QUALITY ) ) { - result = DG_FILL_MODE_STATE_CHECK_INLET_WATER; + result = DG_FILL_MODE_STATE_TEST_INLET_WATER; } return result; @@ -667,9 +849,11 @@ BOOL isInletPressureGood = ( FALSE == isAlarmActive( ALARM_ID_INLET_WATER_LOW_PRESSURE ) ? FALSE : TRUE ); BOOL isWaterTemperatureGood = ( ( FALSE == isAlarmActive( ALARM_ID_INLET_WATER_HIGH_TEMPERATURE ) && - FALSE == isAlarmActive( ALARM_ID_INLET_WATER_LOW_TEMPERATURE ) ) ? FALSE : TRUE ); + FALSE == isAlarmActive( ALARM_ID_INLET_WATER_LOW_TEMPERATURE ) && + FALSE == isAlarmActive( ALARM_ID_INLET_WATER_TEMPERATURE_IN_LOW_RANGE ) && + FALSE == isAlarmActive( ALARM_ID_INLET_WATER_TEMPERATURE_IN_HIGH_RANGE ) ) ? FALSE : TRUE ); - BOOL isWaterConductivityGood = ( ( FALSE == isAlarmActive( ALARM_ID_INLET_WATER_HIGH_CONDUCTIVITY ) && + BOOL isWaterConductivityGood = ( ( FALSE == isAlarmActive( ALARM_ID_INLET_WATER_CONDUCTIVITY_IN_WARNING_RANGE ) && FALSE == isAlarmActive( ALARM_ID_INLET_WATER_LOW_CONDUCTIVITY ) && FALSE == isAlarmActive( ALARM_ID_RO_REJECTION_RATIO_OUT_OF_RANGE ) ) ? FALSE : TRUE ); @@ -701,18 +885,27 @@ /*********************************************************************//** * @brief - * The checkDialysateTemperature function checks dialysate temperature after - * it gets heated up by primary heater. - * @details Inputs: TPo temperature value + * The isValueWithinPercentRange function validates if the test value + * is within the percentage range specified by the baseValue and + * percentFactor. + * @details Inputs: None * @details Outputs: None - * @return TRUE if dialysate temperature is in range, otherwise FALSE + * @param: testValue, baseValue, percentFactor + * @return TRUE if testValue is within range. Otherwise, return FALSE *************************************************************************/ -static BOOL checkDialysateTemperature( void ) +static BOOL isValueWithinPercentRange( F32 testValue, F32 baseValue, F32 percentFactor ) { - F32 dialysateTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); - F32 targetTemp = getHeaterTargetTemperature( DG_PRIMARY_HEATER ); + BOOL testFlag = FALSE; + F32 percentValue = baseValue * percentFactor; + F32 valueMax = baseValue + percentValue; + F32 valueMin = baseValue - percentValue; - return ( ( fabs( dialysateTemp - targetTemp ) <= DIALYSATE_TEMPERATURE_TOLERANCE_C ) ? TRUE : FALSE ); + if ( ( testValue >= valueMin ) && ( testValue <= valueMax ) ) + { + testFlag = TRUE; + } + + return testFlag; } /*********************************************************************//** @@ -756,10 +949,180 @@ static void setFillInfoToRTCRAM( void ) { DG_HEATERS_RECORD_T record; - record.averageFillFlow = fillStatus.fillFlowRateAverage; - setHeatersInfoRecord( (U08*)&record, sizeof( DG_HEATERS_RECORD_T ) ); + setHeatersInfoRecord( (U08*)&record, sizeof(DG_HEATERS_RECORD_T) ); } +/*********************************************************************//** + * @brief + * The publishFillModeData function publishes fill mode data + * at the set interval. + * @details Inputs: fillModeDataPublicationTimerCounter + * @details Outputs: fillModeDataPublicationTimerCounter + * @return none + *************************************************************************/ +static void publishFillModeData( void ) +{ + // publish Drain pump data on interval + if ( ++fillModeDataPublicationTimerCounter >= getU32OverrideValue( &fillModeDataPublishInterval ) ) + { + DG_FILL_MODE_DATA_T fillModeData; + + // Populate the data structure for publication + fillModeData.averageAcidConductivity = averageAcidConductivity; + fillModeData.averageBicarbConductivity = averageBicarbConductivity; + fillModeData.isThisTheFirstFill = isThisTheFirstFill(); + fillModeData.pctDiffInConductivity = pctDiffInConductivity; + fillModeData.usedAcidVolumeML = usedAcidVolumeML.data; + fillModeData.usedBicarbVolumeML = usedBicarbVolumeML.data; + + broadcastData( MSG_ID_DG_FILL_MODE_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&fillModeData, sizeof( DG_FILL_MODE_DATA_T ) ); + + fillModeDataPublicationTimerCounter = 0; + } +} + +/*********************************************************************//** + * @brief + * The testSetUsedAcidVolumeMLOverride function overrides the + * acid volume. + * @details Inputs: used acid volume + * @details Outputs: used acid volume + * @param: value : override used acid volume in mL + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetUsedAcidVolumeMLOverride( F32 value ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + usedAcidVolumeML.ovInitData = usedAcidVolumeML.data; + usedAcidVolumeML.ovData = value; + usedAcidVolumeML.override = OVERRIDE_KEY; + result = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetUsedAcidVolumeMLOverride function resets the override + * of the used acid volume. + * @details Inputs: used acid volume + * @details Outputs: used acid volume + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testResetUsedAcidVolumeMLOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + usedAcidVolumeML.data = usedAcidVolumeML.ovInitData; + usedAcidVolumeML.override = OVERRIDE_RESET; + usedAcidVolumeML.ovInitData = 0.0; + usedAcidVolumeML.ovData = 0.0; + result = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testSetUsedBicarbVolumeMLOverride function overrides the + * bicarb volume. + * @details Inputs: used bicarb volume + * @details Outputs: used bicarb volume + * @param: value : override used bicarb volume in mL + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetUsedBicarbVolumeMLOverride( F32 value ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + usedBicarbVolumeML.ovInitData = usedBicarbVolumeML.data; + usedBicarbVolumeML.ovData = value; + usedBicarbVolumeML.override = OVERRIDE_KEY; + result = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetUsedBicarbVolumeMLOverride function resets the override + * of the used bicarb volume. + * @details Inputs: used bicarb volume + * @details Outputs: used bicarb volume + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testResetUsedBicarbVolumeMLOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + usedBicarbVolumeML.data = usedBicarbVolumeML.ovInitData; + usedBicarbVolumeML.override = OVERRIDE_RESET; + usedBicarbVolumeML.ovInitData = 0.0; + usedBicarbVolumeML.ovData = 0.0; + result = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testSetFillModeDataPublishIntervalOverride function overrides the + * fill mode data publish interval. + * @details Inputs: FillModeDataPublishInterval + * @details Outputs: FillModeDataPublishInterval + * @param: value override fill mode data publish interval with (in ms) + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetFillModeDataPublishIntervalOverride( U32 value ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + U32 intvl = value / TASK_GENERAL_INTERVAL; + fillModeDataPublishInterval.ovData = intvl; + fillModeDataPublishInterval.override = OVERRIDE_KEY; + result = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetFillModeDataPublishIntervalOverride function resets the + * override of the fill mode data publish interval. + * @details Inputs: FillModeDataPublishInterval + * @details Outputs: FillModeDataPublishInterval + * @return TRUE if override reset successful, FALSE if not + *************************************************************************/ +BOOL testResetFillModeDataPublishIntervalOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + fillModeDataPublishInterval.override = OVERRIDE_RESET; + fillModeDataPublishInterval.ovData = fillModeDataPublishInterval.ovInitData; + result = TRUE; + } + + return result; +} + /**@}*/ Index: firmware/App/Modes/ModeGenIdle.c =================================================================== diff -u -re0cdf49eb0f54239e5d765282e0952cea7ded1bd -r9279d01e7e539ad1e788b85db4ea2288965b03c6 --- firmware/App/Modes/ModeGenIdle.c (.../ModeGenIdle.c) (revision e0cdf49eb0f54239e5d765282e0952cea7ded1bd) +++ firmware/App/Modes/ModeGenIdle.c (.../ModeGenIdle.c) (revision 9279d01e7e539ad1e788b85db4ea2288965b03c6) @@ -21,13 +21,15 @@ #include "DrainPump.h" #include "FPGA.h" #include "Heaters.h" +#include "ModeFill.h" #include "ModeGenIdle.h" #include "NVDataMgmt.h" #include "OperationModes.h" #include "Pressures.h" #include "Reservoirs.h" #include "ROPump.h" #include "SystemComm.h" +#include "SystemCommMessages.h" #include "TaskGeneral.h" #include "TemperatureSensors.h" #include "Timers.h" @@ -46,20 +48,38 @@ #define TARGET_FLUSH_LINES_RO_FLOW_RATE_L 0.3F ///< Target flow rate for RO pump. +#define BAD_FLUSH_FILL_TARGET_VOLUME_ML 1000 ///< Target fill volume in the bad flush fill state. + /// The time of HD lost comm before DG transition back to standby. #define HD_LOST_COMM_TIMEOUT_MS (5 * SEC_PER_MIN * MS_PER_SECOND ) +#define BAD_FILL_SUBSTATES_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the bad fill sub-states is published on the CAN bus. // ********** private data ********** -static DG_GEN_IDLE_MODE_STATE_T genIdleState; ///< Currently active generation idle state. -static F32 flushLinesVolumeL = 0.0; ///< Volume of water pumped by RO pump during flush lines state. -static U32 hdLostCommStartTime_ms = 0; ///< Lost communication with HD start time in ms. +static DG_GEN_IDLE_MODE_STATE_T genIdleState; ///< Currently active generation idle state. +static DG_GEN_IDLE_MODE_BAD_FILL_STATE_T badFillState = DG_HANDLE_BAD_FILL_STATE_START; ///< Initialize bad fill sub-state. +static U32 hdLostCommStartTime_ms = 0; ///< Lost communication with HD start time in ms. +static U32 badFillSubstatesPublicationTimerCounter; ///< Used to schedule bad fill sub-states publication to CAN bus. +static U32 targetFillVolumeML; ///< Save the target fill volume before calling startFillCmd(). +static BOOL handleBadFillFlag; ///< Internal signal flag to handle bad fill. +static OVERRIDE_U32_T badFillSubstatesPublishInterval = { BAD_FILL_SUBSTATES_PUB_INTERVAL, ///< Interval (in ms) at which to publish bad fill sub-states to CAN bus. + BAD_FILL_SUBSTATES_PUB_INTERVAL, + 0, 0 }; // ********** private function prototypes ********** -static DG_GEN_IDLE_MODE_STATE_T handleFlushLinesState( void ); +static DG_GEN_IDLE_MODE_STATE_T handleIdleStartState( void ); static DG_GEN_IDLE_MODE_STATE_T handleFlushWaterState( void ); +static DG_GEN_IDLE_MODE_STATE_T handleBadFillState( void ); // This state has sub-states 1.0 to 1.4 +static DG_GEN_IDLE_MODE_BAD_FILL_STATE_T handleFirstDrainState( void ); // idle 1.0 +static DG_GEN_IDLE_MODE_BAD_FILL_STATE_T handleFlushFillState( void ); // idle 1.1 +static DG_GEN_IDLE_MODE_BAD_FILL_STATE_T handleSecondDrainState( void ); // idle 1.2 +static DG_GEN_IDLE_MODE_BAD_FILL_STATE_T handleRefillState( void ); // idle 1.3 +static DG_GEN_IDLE_MODE_BAD_FILL_STATE_T handleClearAlarmState( void ); // idle 1.4 + +static void publishBadFillSubstates( void ); + /*********************************************************************//** * @brief * The initGenIdleMode function initializes the generation idle mode module. @@ -70,8 +90,10 @@ void initGenIdleMode( void ) { genIdleState = DG_GEN_IDLE_MODE_STATE_START; - flushLinesVolumeL = 0.0; + hdLostCommStartTime_ms = 0; + targetFillVolumeML = 0; + handleBadFillFlag = FALSE; } /*********************************************************************//** @@ -108,7 +130,7 @@ // because the initial guess in the heaters driver needs the target flow to calculate // the new PWMs for the main and small primary heaters #ifndef DISABLE_FLOW_CONTROL_TREATMENT - setROPumpTargetFlowRateLPM( TARGET_FLUSH_LINES_RO_FLOW_RATE_L, TARGET_RO_PRESSURE_PSI ); + setROPumpTargetFlowRateLPM( TARGET_RO_FLOW_RATE_L, TARGET_RO_PRESSURE_PSI ); setHeaterTargetTemperature( DG_PRIMARY_HEATER, getPrimaryHeaterTargetTemperature() ); startHeater( DG_PRIMARY_HEATER ); #endif @@ -118,6 +140,47 @@ /*********************************************************************//** * @brief + * The getCurrentGenIdleState function returns the current state of the + * generation idle mode. + * @details Inputs: genIdleState + * @details Outputs: none + * @return the current state of generation idle mode + *************************************************************************/ +DG_GEN_IDLE_MODE_STATE_T getCurrentGenIdleState( void ) +{ + return genIdleState; +} + +/*********************************************************************//** + * @brief + * The requestDGStop function handles an HD request to stop (return to standby mode). + * @details Inputs: none + * @details Outputs: DG standby mode requested + * @return TRUE if request accepted, FALSE if not. + *************************************************************************/ +BOOL requestDGStop( void ) +{ + BOOL result = TRUE; + + requestNewOperationMode( DG_MODE_STAN ); + + return result; +} + +/*********************************************************************//** + * @brief + * The setBadAvgConductivityDetectedFlag function sets a flag to indicate + * that bad average conductivity is detected. + * @details Inputs: none + * @details Outputs: none + * @param flag to TRUE if bad avg conductivity otherwise FALSE + *************************************************************************/ +void setBadAvgConductivityDetectedFlag( BOOL badAvgConducitivyflag ) +{ + handleBadFillFlag = badAvgConducitivyflag; +} +/*********************************************************************//** + * @brief * The execGenIdleMode function executes the generation idle mode state machine. * @details Inputs: genIdleState * @details Outputs: Check water quality, generation idle mode state machine executed @@ -130,6 +193,7 @@ checkInletWaterTemperature(); checkInletPressure(); checkRORejectionRatio(); + publishBadFillSubstates(); // Transition to standby mode when HD is not communicating if ( TRUE == isHDCommunicating() ) @@ -148,17 +212,17 @@ switch ( genIdleState ) { case DG_GEN_IDLE_MODE_STATE_START: - genIdleState = DG_GEN_IDLE_MODE_STATE_FLUSH_LINES; + genIdleState = handleIdleStartState(); break; - case DG_GEN_IDLE_MODE_STATE_FLUSH_LINES: - genIdleState = handleFlushLinesState(); - break; - case DG_GEN_IDLE_MODE_STATE_FLUSH_WATER: genIdleState = handleFlushWaterState(); break; + case DG_GEN_IDLE_MODE_STATE_HANDLE_BAD_FILL: + genIdleState = handleBadFillState(); + break; + default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_GEN_IDLE_MODE_INVALID_EXEC_STATE, genIdleState ) genIdleState = DG_GEN_IDLE_MODE_STATE_START; @@ -170,73 +234,257 @@ /*********************************************************************//** * @brief - * The getCurrentGenIdleState function returns the current state of the - * generation idle mode. - * @details Inputs: genIdleState + * The handleIdleStartState function executes the start state of the + * generation idle mode state machine. + * @details Inputs: none * @details Outputs: none - * @return the current state of generation idle mode + * @return the next state *************************************************************************/ -DG_GEN_IDLE_MODE_STATE_T getCurrentGenIdleState( void ) +static DG_GEN_IDLE_MODE_STATE_T handleIdleStartState( void ) { - return genIdleState; + DG_GEN_IDLE_MODE_STATE_T result = DG_GEN_IDLE_MODE_STATE_START; + + if ( TRUE == handleBadFillFlag ) + { + result = DG_GEN_IDLE_MODE_STATE_HANDLE_BAD_FILL; + } + else + { + badFillState = DG_HANDLE_BAD_FILL_STATE_START; + result = DG_GEN_IDLE_MODE_STATE_FLUSH_WATER; + } + + return result; } /*********************************************************************//** * @brief - * The requestDGStop function handles an HD request to stop (return to standby mode). + * The handleFlushWaterState function executes the flush water state + * generation idle mode state machine. * @details Inputs: none - * @details Outputs: DG standby mode requested - * @return TRUE if request accepted, FALSE if not. + * @details Outputs: none + * @return the next state *************************************************************************/ -BOOL requestDGStop( void ) +static DG_GEN_IDLE_MODE_STATE_T handleFlushWaterState( void ) { - BOOL result = TRUE; + DG_GEN_IDLE_MODE_STATE_T result = DG_GEN_IDLE_MODE_STATE_FLUSH_WATER; - requestNewOperationMode( DG_MODE_STAN ); - return result; } /*********************************************************************//** * @brief - * The handleFlushLinesState function executes the flush lines state of the + * The handleBadFillState function executes the bad fill state of the * generation idle mode state machine. - * @details Inputs: flushLinesVolumeL - * @details Outputs: Integrate volume of water moved through line + * @details Inputs: none + * @details Outputs: badFillState * @return the next state *************************************************************************/ -static DG_GEN_IDLE_MODE_STATE_T handleFlushLinesState( void ) +static DG_GEN_IDLE_MODE_STATE_T handleBadFillState( void ) { - DG_GEN_IDLE_MODE_STATE_T result = DG_GEN_IDLE_MODE_STATE_FLUSH_LINES; - F32 waterFlowRate = getMeasuredROFlowRateLPM(); - F32 waterVolume = ( ( waterFlowRate / SEC_PER_MIN ) / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ); + DG_GEN_IDLE_MODE_STATE_T result = DG_GEN_IDLE_MODE_STATE_HANDLE_BAD_FILL; - // Integrate volume of water moved through line - flushLinesVolumeL += waterVolume; + // Execute current bad fill state + switch ( badFillState ) + { + case DG_HANDLE_BAD_FILL_STATE_START: + badFillState = DG_HANDLE_BAD_FILL_STATE_FIRST_DRAIN; + break; - // When enough water volume has flowed to flush the lines, transition to flush water state - if ( flushLinesVolumeL >= getFlushLineVolumeL() ) + case DG_HANDLE_BAD_FILL_STATE_FIRST_DRAIN: // idle 1.0 + badFillState = handleFirstDrainState(); + break; + + case DG_HANDLE_BAD_FILL_STATE_FLUSH_FILL: // idle 1.1 + badFillState = handleFlushFillState(); + break; + + case DG_HANDLE_BAD_FILL_STATE_SECOND_DRAIN: // idle 1.2. + badFillState = handleSecondDrainState(); + break; + + case DG_HANDLE_BAD_FILL_STATE_REFILL: + badFillState = handleRefillState(); // idle 1.3 + break; + + case DG_HANDLE_BAD_FILL_STATE_CLEAR_ALARM: + badFillState = handleClearAlarmState(); // idle 1.4 + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_GEN_IDLE_MODE_INVALID_EXEC_STATE, genIdleState ) + badFillState = DG_HANDLE_BAD_FILL_STATE_START; + break; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The handleFirstDrainState function executes the first drain state of the + * handle bad fill state machine. + * @details Inputs: none + * @details Outputs: none + * @return the next state + *************************************************************************/ +static DG_GEN_IDLE_MODE_BAD_FILL_STATE_T handleFirstDrainState( void ) +{ + DG_GEN_IDLE_MODE_BAD_FILL_STATE_T result = DG_HANDLE_BAD_FILL_STATE_FLUSH_FILL; // after first drain is completed, go to next bad fill sub-state + + requestNewOperationMode( DG_MODE_DRAI ); // go to drain mode to empty bad dialysate because this is a bad fill + + return result; +} + +/*********************************************************************//** + * @brief + * The handleFlushFillState function executes the flush fill state of the + * handle bad fill state machine. + * @details Inputs: none + * @details Outputs: none + * @return the next state + *************************************************************************/ +static DG_GEN_IDLE_MODE_BAD_FILL_STATE_T handleFlushFillState( void ) +{ + DG_GEN_IDLE_MODE_BAD_FILL_STATE_T result = DG_HANDLE_BAD_FILL_STATE_FLUSH_FILL; + + if ( FALSE == isAlarmActive( ALARM_ID_FILL_CONDUCTIVITY_OUT_OF_RANGE ) ) // alarm is no longer active - cleared by user { -#ifndef DISABLE_FLOW_CONTROL_TREATMENT - setROPumpTargetFlowRateLPM( TARGET_RO_FLOW_RATE_L, TARGET_RO_PRESSURE_PSI ); -#endif - result = DG_GEN_IDLE_MODE_STATE_FLUSH_WATER; + targetFillVolumeML = getTargetFillVolumeML(); // save the HD target fill volume before command 1000 mL fill volume + startFillCmd( BAD_FLUSH_FILL_TARGET_VOLUME_ML, getTargetFillFlowRateLPM() ); + + if ( TRUE == handleBadFillFlag ) + { + result = DG_HANDLE_BAD_FILL_STATE_FIRST_DRAIN; // (idle 1.0) + } + else + { + result = DG_HANDLE_BAD_FILL_STATE_SECOND_DRAIN; // (idle 1.2) + } } return result; } /*********************************************************************//** * @brief - * The handleFlushWaterState function executes the flush water state - * of the generation idle mode state machine. + * The handleSecondDrainState function executes the second drain state of the + * handle bad fill state machine. * @details Inputs: none * @details Outputs: none * @return the next state *************************************************************************/ -static DG_GEN_IDLE_MODE_STATE_T handleFlushWaterState( void ) +static DG_GEN_IDLE_MODE_BAD_FILL_STATE_T handleSecondDrainState( void ) { - return DG_GEN_IDLE_MODE_STATE_FLUSH_WATER; + DG_GEN_IDLE_MODE_BAD_FILL_STATE_T result = DG_HANDLE_BAD_FILL_STATE_REFILL; // after second drain completed, go to refill sub-state (idle 1.3) + + requestNewOperationMode( DG_MODE_DRAI ); // go to drain mode to empty bad dialysate because this is a bad fill + + return result; } +/*********************************************************************//** + * @brief + * The handleRefillState function executes refill state of the handle bad + * fill state machine. + * @details Inputs: none + * @details Outputs: none + * @return the next state + *************************************************************************/ +static DG_GEN_IDLE_MODE_BAD_FILL_STATE_T handleRefillState( void ) +{ + DG_GEN_IDLE_MODE_BAD_FILL_STATE_T result = DG_HANDLE_BAD_FILL_STATE_CLEAR_ALARM; // (idle 1.4) + + startFillCmd( targetFillVolumeML, getTargetFillFlowRateLPM() ); // refill to the saved target fill volume (~1500 mL) + + return result; +} + +/*********************************************************************//** + * @brief + * The handleClearAlarmState function executes the clear alarm state of the + * handle bad fill state machine. + * @details Inputs: none + * @details Outputs: none + * @return the next state + *************************************************************************/ +static DG_GEN_IDLE_MODE_BAD_FILL_STATE_T handleClearAlarmState( void ) +{ + DG_GEN_IDLE_MODE_BAD_FILL_STATE_T result = DG_HANDLE_BAD_FILL_STATE_START; + + // clear wait for dialysate alarm condition to allow resume + clearAlarmCondition( ALARM_ID_CREATING_DIALYSATE_PLEASE_WAIT ); // resume option will appear + handleBadFillFlag = FALSE; // set flag to FALSE here so next call to idle exec will move to normal flush water state + genIdleState = DG_GEN_IDLE_MODE_STATE_START; + + return result; +} + +/*********************************************************************//** + * @brief + * The publishBadFillSubstates function publishes idle mode bad fill + * sub-states at the set interval. + * @details Inputs: badFillSubstatesPublicationTimerCounter + * @details Outputs: badFillSubstatesPublicationTimerCounter + * @return none + *************************************************************************/ +static void publishBadFillSubstates( void ) +{ + // publish bad fill sub-states on interval + if ( ++badFillSubstatesPublicationTimerCounter >= getU32OverrideValue( &badFillSubstatesPublishInterval ) ) + { + U32 badFillData = (U32)badFillState; + + broadcastData( MSG_ID_DG_BAD_FILL_SUB_STATE, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&badFillData, sizeof( U32 ) ); + badFillSubstatesPublicationTimerCounter = 0; + } +} + +/*********************************************************************//** + * @brief + * The testSetBadFillSubstatesPublishIntervalOverride function overrides the + * bad fill sub-states publish interval. + * @details Inputs: badFillSubstatesPublishInterval + * @details Outputs: badFillSubstatesPublishInterval + * @param: value override bad fill sub-states publish interval with (in ms) + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetBadFillSubstatesPublishIntervalOverride( U32 value ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + U32 intvl = value / TASK_GENERAL_INTERVAL; + badFillSubstatesPublishInterval.ovData = intvl; + badFillSubstatesPublishInterval.override = OVERRIDE_KEY; + result = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetBadFillSubstatesPublishIntervalOverride function resets the + * override of the bad fill sub-states publish interval. + * @details Inputs: badFillSubstatesPublishInterval + * @details Outputs: badFillSubstatesPublishInterval + * @return TRUE if override reset successful, FALSE if not + *************************************************************************/ +BOOL testResetBadFillSubstatesPublishIntervalOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + badFillSubstatesPublishInterval.override = OVERRIDE_RESET; + badFillSubstatesPublishInterval.ovData = badFillSubstatesPublishInterval.ovInitData; + result = TRUE; + } + + return result; +} + /**@}*/ Index: firmware/App/Modes/ModeGenIdle.h =================================================================== diff -u -ra9315539f527b92523b1598ff91e47db4d71dae2 -r9279d01e7e539ad1e788b85db4ea2288965b03c6 --- firmware/App/Modes/ModeGenIdle.h (.../ModeGenIdle.h) (revision a9315539f527b92523b1598ff91e47db4d71dae2) +++ firmware/App/Modes/ModeGenIdle.h (.../ModeGenIdle.h) (revision 9279d01e7e539ad1e788b85db4ea2288965b03c6) @@ -34,12 +34,13 @@ // ********** public function prototypes ********** void initGenIdleMode( void ); // initialize this module -U32 transitionToGenIdleMode( void ); // prepares for transition to generation idle mode +U32 transitionToGenIdleMode( void ); // prepares for transition to generation idle mode U32 execGenIdleMode( void ); // execute the generation idle mode state machine (call from OperationModes) +void setBadAvgConductivityDetectedFlag( BOOL badAvgConducitivyflag ); // used by fill mode to signal idle mode of bad average conductivity DG_GEN_IDLE_MODE_STATE_T getCurrentGenIdleState( void ); // get the current state of generation idle mode -BOOL requestDGStop( void ); // HD requests DG stop (go back to standby mode) +BOOL requestDGStop( void ); // HD requests DG stop (go back to standby mode) /**@}*/ Index: firmware/App/Services/Reservoirs.c =================================================================== diff -u -re0cdf49eb0f54239e5d765282e0952cea7ded1bd -r9279d01e7e539ad1e788b85db4ea2288965b03c6 --- firmware/App/Services/Reservoirs.c (.../Reservoirs.c) (revision e0cdf49eb0f54239e5d765282e0952cea7ded1bd) +++ firmware/App/Services/Reservoirs.c (.../Reservoirs.c) (revision 9279d01e7e539ad1e788b85db4ea2288965b03c6) @@ -318,7 +318,11 @@ fillVolumeTargetMl.data = fillToVolMl; cmdResponse.rejected = FALSE; - requestNewOperationMode( DG_MODE_FILL ); + if ( ( FALSE == isAlarmActive( ALARM_ID_DG_ACID_BOTTLE_LOW_VOLUME ) ) || // reject moving to fill mode if + ( FALSE == isAlarmActive( ALARM_ID_DG_BICARB_BOTTLE_LOW_VOLUME ) ) ) // alarm is active + { + requestNewOperationMode( DG_MODE_FILL ); + } } else { Index: firmware/App/Services/SystemComm.c =================================================================== diff -u -re28e96fba3f2293208e6d91673288acba2514cca -r9279d01e7e539ad1e788b85db4ea2288965b03c6 --- firmware/App/Services/SystemComm.c (.../SystemComm.c) (revision e28e96fba3f2293208e6d91673288acba2514cca) +++ firmware/App/Services/SystemComm.c (.../SystemComm.c) (revision 9279d01e7e539ad1e788b85db4ea2288965b03c6) @@ -8,7 +8,7 @@ * @file SystemComm.c * * @author (last) Dara Navaei -* @date (last) 21-Dec-2021 +* @date (last) 22-Feb-2022 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 @@ -100,7 +100,7 @@ static volatile BOOL dgIsOnlyCANNode = TRUE; ///< flag indicating whether DG is alone on CAN bus. static U32 canXmitRetryCtr = 0; ///< counter for CAN transmit retries. -static volatile BOOL hdIsCommunicating = FALSE; ///< has HD sent a message since last check +static OVERRIDE_U32_T hdCommunicationStatus = {0, 0, 0, 0}; ///< has HD sent a message since last check static volatile U32 timeOfLastHDCheckIn = 0; ///< last time we received an HD broadcast // ********** private function prototypes ********** @@ -153,7 +153,7 @@ *************************************************************************/ BOOL isHDCommunicating( void ) { - return hdIsCommunicating; + return getU32OverrideValue(&hdCommunicationStatus); } /*********************************************************************//** @@ -496,7 +496,7 @@ // if message from HD broadcast channel, update HD comm status if ( COMM_BUFFER_IN_CAN_HD_BROADCAST == MSG_IN_BUFFERS[ i ] ) { - hdIsCommunicating = TRUE; + hdCommunicationStatus.data = (U32)TRUE; timeOfLastHDCheckIn = getMSTimerCount(); } } @@ -620,7 +620,7 @@ if ( message.crc == crc8( (U08*)(&message), sizeof(MESSAGE_HEADER_T) + message.msg.hdr.payloadLen ) ) { // if ACK, mark pending message ACK'd - if ( MSG_ID_ACK == message.msg.hdr.msgID ) + if ( MSG_ID_ACK_MESSAGE_THAT_REQUIRES_ACK == message.msg.hdr.msgID ) { matchACKtoPendingACKList( message.msg.hdr.seqNo ); } @@ -654,7 +654,7 @@ { if ( TRUE == didTimeout( timeOfLastHDCheckIn, HD_COMM_TIMEOUT_IN_MS ) ) { - hdIsCommunicating = FALSE; + hdCommunicationStatus.data = FALSE; //activateAlarmNoData( ALARM_ID_HD_COMM_TIMEOUT ); } } @@ -877,6 +877,10 @@ handleDGSendConcentrateMixingRatios( message ); break; + case MSG_ID_DG_SCHEDULED_RUNS_INFO: + handleDGScheduledRunsRequest( message ); + break; + // NOTE: This case must be last case MSG_ID_DG_TESTER_LOGIN_REQUEST: handleTesterLogInRequest( message ); @@ -888,7 +892,8 @@ } // handle any test messages if tester has logged in successfully - if ( ( msgID > MSG_ID_FIRST_DG_TESTER_MESSAGE ) && ( msgID <= END_OF_MSG_IDS ) && ( TRUE == isTestingActivated() ) ) + // NOTE: END_OF_MSG_IDS = 65536 which is out of the range of a U16 so it is subtracted by 1. This is unreachable in development testing + if ( ( msgID > MSG_ID_FIRST_DG_TESTER_MESSAGE ) && ( msgID <= END_OF_MSG_IDS - 1 ) && ( TRUE == isTestingActivated() ) ) { switch ( msgID ) { @@ -1117,11 +1122,11 @@ break; case MSG_ID_FILTER_FLUSH_TIME_PERIOD_OVERRIDE: - handleFilterFlushTimePeriodOverride( message ); + handleFilterFlushTimePeriodOverride(message); break; - case MSG_ID_DG_FANS_RPM_OVERRIDE: - handleFansRPMOverride( message ); + case MSG_ID_DG_BLOCK_MESSAGE_TRANSMISSION: + handleTestBlockMessagesRequest( message ); break; case MSG_ID_DG_STOP_RTC_CLOCK: @@ -1132,10 +1137,6 @@ handleSetDrainPumpMeasuredRPMOverrideRequest( message ); break; - case MSG_ID_DG_BLOCK_MESSAGE_TRANSMISSION: - handleTestBlockMessagesRequest( message ); - break; - case MSG_ID_DG_SUPER_CLEAR_ALARMS_CMD: handleTestSuperClearAlarmsRequest( message ); break; @@ -1144,10 +1145,30 @@ handleTestAlarmInfoSendIntervalOverrideRequest( message ); break; - case MSG_ID_DG_FAN_RPM_ALARM_START_TIME_OFFSET_OVERRIDE: - handleTestFansRPMAlarmStartTimeOffsetOverrideRequest( message ); + case MSG_ID_DG_SET_FANS_RPM_ALARM_START_TIME_OFFSET: + handleTestFansRPMAlarmStartTimeOffsetRequest( message ); break; + case MSG_ID_DG_FANS_RPM_OVERRIDE: + handleFansRPMOverride( message ); + break; + + case MSG_ID_DG_GET_SW_CONFIG_RECORD: + handleGetDGSoftwareConfigRecord( message ); + break; + + case MSG_ID_DG_SET_SW_CONFIG_RECORD: + handleSetDGSoftwareConfigRecord( message ); + break; + + case MSG_ID_DG_FANS_DUTY_CYCLE_OVERRIDE: + handleSetFansDutyCycleOverrideRequest( message ); + break; + + case MSG_ID_DG_HD_COMMUNICATION_STATUS_OVERRIDE: + handleTestHDCommunicationStatusOverrideRequest( message ); + break; + case MSG_ID_DG_USED_ACID_VOLUME_ML_OVERRIDE: handleTestUsedAcidVolumeMLOverrideRequest( message ); break; @@ -1163,4 +1184,53 @@ } } + +/************************************************************************* + * TEST SUPPORT FUNCTIONS + *************************************************************************/ + +/*********************************************************************//** + * @brief + * The testSetHDCommunicationStatus function sets the override + * of the HD communication status. + * @details Inputs: none + * @details Outputs: hdCommunicationStatus + * @return TRUE if reset successful, FALSE if not + *************************************************************************/ +BOOL testSetHDCommunicationStatus( U32 value ) +{ + BOOL result = FALSE; + + if (TRUE == isTestingActivated() ) + { + result = TRUE; + hdCommunicationStatus.ovData = value; + hdCommunicationStatus.override = OVERRIDE_KEY; + + } + return result; +} + +/*********************************************************************//** + * @brief + * The testResetHDCommuncationStatus function resets the override + * of the HD communication status. + * @details Inputs: none + * @details Outputs: hdCommunicationStatus + * @return TRUE if reset successful, FALSE if not + *************************************************************************/ +BOOL testResetHDCommuncationStatus( void ) +{ + BOOL result = FALSE; + + if (TRUE == isTestingActivated() ) + { + result = TRUE; + hdCommunicationStatus.override = OVERRIDE_RESET; + hdCommunicationStatus.ovData = hdCommunicationStatus.ovInitData; + + } + return result; +} + /**@}*/ Index: firmware/App/Services/SystemCommMessages.c =================================================================== diff -u -re28e96fba3f2293208e6d91673288acba2514cca -r9279d01e7e539ad1e788b85db4ea2288965b03c6 --- firmware/App/Services/SystemCommMessages.c (.../SystemCommMessages.c) (revision e28e96fba3f2293208e6d91673288acba2514cca) +++ firmware/App/Services/SystemCommMessages.c (.../SystemCommMessages.c) (revision 9279d01e7e539ad1e788b85db4ea2288965b03c6) @@ -8,7 +8,7 @@ * @file SystemCommMessages.c * * @author (last) Dara Navaei -* @date (last) 05-Jan-2022 +* @date (last) 02-Mar-2022 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 @@ -20,9 +20,9 @@ #include "reg_system.h" #include "Accel.h" +#include "Compatible.h" #include "ConcentratePumps.h" #include "ConductivitySensors.h" -#include "Fans.h" #include "FPGA.h" #include "Heaters.h" #include "ModeFill.h" @@ -105,7 +105,7 @@ data[ msgSize++ ] = MESSAGE_SYNC_BYTE; // set sequence # and ACK bit (unless this is an ACK to a received message) - if ( msg.hdr.msgID != MSG_ID_ACK ) + if ( msg.hdr.msgID != MSG_ID_ACK_MESSAGE_THAT_REQUIRES_ACK ) { // thread protect next sequence # access & increment _disable_IRQ(); @@ -178,7 +178,7 @@ // send ACK back with same seq. #, but w/o ACK bit msg.hdr.seqNo = message->hdr.seqNo * -1; // ACK messages always have this ID - msg.hdr.msgID = MSG_ID_ACK; + msg.hdr.msgID = MSG_ID_ACK_MESSAGE_THAT_REQUIRES_ACK; // ACK messages always have no payload msg.hdr.payloadLen = 0; @@ -364,7 +364,6 @@ return result; } - // *********************************************************************** // **************** Message Handling Helper Functions ******************** // *********************************************************************** @@ -454,6 +453,7 @@ payload.minor = (U08)DG_VERSION_MINOR; payload.micro = (U08)DG_VERSION_MICRO; payload.build = (U16)DG_VERSION_BUILD; + payload.compatibilityRev = (U32)SW_COMPATIBILITY_REV; getFPGAVersions( &payload.fpgaId, &payload.fpgaMajor, &payload.fpgaMinor, &payload.fpgaLab ); // create a message record @@ -479,7 +479,12 @@ void handleDGSerialNumberRequest( void ) { MESSAGE_T msg; - DG_SYSTEM_RECORD_T system = getDGSystemRecord(); + DG_SYSTEM_RECORD_T system; + + // Get the system's record. There are no arrays of system to check and also, raise no alarm since the system record + // has been already checked in POST + getNVRecord2Driver( GET_SYS_RECORD, (U08*)&system, sizeof( DG_SYSTEM_RECORD_T ), 0, ALARM_ID_NO_ALARM ); + U08 *payloadPtr = msg.payload; // Create a message record @@ -510,27 +515,32 @@ void handleDGServiceScheduleRequest( MESSAGE_T *message ) { MESSAGE_T msg; - DG_SERVICE_RECORD_T payload = getDGServiceRecord(); + DG_SERVICE_RECORD_T service; + + // Get the service record. There are no arrays of service to check and also, raise no alarm since the service record + // has been already checked in POST + getNVRecord2Driver( GET_SRV_RECORD, (U08*)&service, sizeof( DG_SERVICE_RECORD_T ), 0, ALARM_ID_NO_ALARM ); + U08 *payloadPtr = msg.payload; // Create a message record blankMessage( &msg ); - msg.hdr.msgID = MSG_ID_DG_SERVICE_SCHEDULE_DATA; + msg.hdr.msgID = MSG_ID_DG_SERVICE_SCHEDULE_DATA; msg.hdr.payloadLen = sizeof( U32 ) + sizeof( U32 ); // Fill message payload - memcpy( payloadPtr, &payload.lastServiceEpochDate, sizeof( U32 ) ); + memcpy( payloadPtr, &service.lastServiceEpochDate, sizeof( U32 ) ); payloadPtr += sizeof( U32 ); - memcpy( payloadPtr, &payload.serviceIntervalSeconds, sizeof( U32 ) ); + memcpy( payloadPtr, &service.serviceIntervalSeconds, sizeof( U32 ) ); // Serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer serializeMessage( msg, COMM_BUFFER_OUT_CAN_DG_2_UI, ACK_REQUIRED ); } /*********************************************************************//** * @brief - * The handleDGServiceScheduleRequest function handles a request for DG - * service information. + * The handleDGSendConcentrateMixingRatios function handles a request for DG + * mixing ratios. * @details Inputs: none * @details Outputs: message handled, response constructed and queued for * transmit. @@ -784,6 +794,76 @@ /*********************************************************************//** * @brief + * The sendDGSWConfigRecord function sends out the DG software configuration record. + * @details Inputs: none + * @details Outputs: DG software configuration record msg constructed and queued + * @param msgCurrNum: current payload number + * @param msgTotalNum: total number of payloads + * @param length: buffer length to be written + * @param swRcrdAddress: start address of the software configuration record + * @return TRUE if msg successfully queued for transmit, FALSE if not + *************************************************************************/ +BOOL sendDGSWConfigRecord( U32 payloadCurrNum, U32 payloadTotalNum, U32 length, U08* swRcrdAddress ) +{ + BOOL result; + MESSAGE_T msg; + U08 *payloadPtr = msg.payload; + + // Create a message record + blankMessage( &msg ); + msg.hdr.msgID = MSG_ID_DG_SEND_SW_CONFIG_RECORD; + msg.hdr.payloadLen = sizeof( U32 ) + sizeof( U32 ) + sizeof( U32 ) + length; + + memcpy( payloadPtr, &payloadCurrNum, sizeof( U32 ) ); + payloadPtr += sizeof( U32 ); + + memcpy( payloadPtr, &payloadTotalNum, sizeof( U32 ) ); + payloadPtr += sizeof( U32 ); + + memcpy( payloadPtr, &length, sizeof( U32 ) ); + payloadPtr += sizeof( U32 ); + + memcpy( payloadPtr, swRcrdAddress, length ); + + // Serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer + result = serializeMessage( msg, COMM_BUFFER_OUT_CAN_PC, ACK_NOT_REQUIRED ); + + return result; +} + +/*********************************************************************//** + * @brief + * The handleDGScheduledRunsRequest function handles a request for DG + * scheduled runs information. + * @details Inputs: none + * @details Outputs: message handled, response constructed and queued for + * transmit. + * @return none + *************************************************************************/ +void handleDGScheduledRunsRequest( MESSAGE_T *message ) +{ + MESSAGE_T msg; + DG_SCHEDULED_RUN_RECORD_T scheduledService; + + // Get the service record. There are no arrays of service to check and also, raise no alarm since the service record + // has been already checked in POST + getNVRecord2Driver( GET_SRR_RECORD, (U08*)&scheduledService, sizeof( DG_SCHEDULED_RUN_RECORD_T ), 0, ALARM_ID_NO_ALARM ); + + U08 *payloadPtr = msg.payload; + + // Create a message record + blankMessage( &msg ); + msg.hdr.msgID = MSG_ID_DG_SCHEDULED_RUNS_DATA; + msg.hdr.payloadLen = sizeof( U32 ) + sizeof( U32 ); + + // TODO this message is for Phase 1B. + + // Serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer + serializeMessage( msg, COMM_BUFFER_OUT_CAN_DG_2_UI, ACK_REQUIRED ); +} + +/*********************************************************************//** + * @brief * The sendCommandResponseMsg function constructs a command response to HD * and queues the msg for transmit on the appropriate CAN channel. * @details Inputs: none @@ -2690,7 +2770,7 @@ memcpy(&payloadLength, payloadPtr, sizeof(U32)); payloadPtr += sizeof(U32); - status = setCalibrationRecord( currentMessage, totalMessages, payloadLength, payloadPtr ); + status = receiveRecordFromDialin( NVDATAMGMT_CALIBRATION_RECORD, currentMessage, totalMessages, payloadLength, payloadPtr ); // Respond to request sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, status ); @@ -2750,7 +2830,7 @@ // Tester must be logged in if ( TRUE == isTestingActivated() ) { - result = getCalibrationRecord(); + result = sendRecordToDialin( NVDATAMGMT_CALIBRATION_RECORD ); } } @@ -2784,7 +2864,7 @@ memcpy(&payloadLength, payloadPtr, sizeof(U32)); payloadPtr += sizeof(U32); - status = setSystemRecord( currentMessage, totalMessages, payloadLength, payloadPtr ); + status = receiveRecordFromDialin( NVDATAMGMT_SYSTEM_RECORD, currentMessage, totalMessages, payloadLength, payloadPtr ); // Respond to request sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, status ); @@ -2809,7 +2889,7 @@ // Tester must be logged in if ( TRUE == isTestingActivated() ) { - result = getSystemRecord(); + result = sendRecordToDialin( NVDATAMGMT_SYSTEM_RECORD ); } } @@ -2836,7 +2916,7 @@ // Tester must be logged in if ( TRUE == isTestingActivated() ) { - result = getServiceRecord(); + result = sendRecordToDialin( NVDATAMGMT_SERVICE_RECORD ); } } @@ -2870,7 +2950,7 @@ memcpy(&payloadLength, payloadPtr, sizeof(U32)); payloadPtr += sizeof(U32); - status = setServiceRecord( currentMessage, totalMessages, payloadLength, payloadPtr ); + status = receiveRecordFromDialin( NVDATAMGMT_SERVICE_RECORD, currentMessage, totalMessages, payloadLength, payloadPtr ); // Respond to request sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, status ); @@ -2895,7 +2975,7 @@ // Tester must be logged in if ( TRUE == isTestingActivated() ) { - result = getScheduledRunsRecord(); + result = sendRecordToDialin( NVDATAMGMT_SCHEDULED_RUNS_RECORD ); } } @@ -2929,7 +3009,7 @@ memcpy(&payloadLength, payloadPtr, sizeof(U32)); payloadPtr += sizeof(U32); - status = setScheduledRunsRecord( currentMessage, totalMessages, payloadLength, payloadPtr ); + status = receiveRecordFromDialin( NVDATAMGMT_SCHEDULED_RUNS_RECORD, currentMessage, totalMessages, payloadLength, payloadPtr ); // Respond to request sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, status ); @@ -2968,37 +3048,6 @@ } /*********************************************************************//** - * @brief - * The handleFansRPMOverride function handles a request to override a fans RPM value. - * @details Inputs: none - * @details Outputs: message handled - * @param message a pointer to the message to handle - * @return none - *************************************************************************/ -void handleFansRPMOverride( MESSAGE_T *message ) -{ - TEST_OVERRIDE_ARRAY_PAYLOAD_T payload; - BOOL result = FALSE; - - // verify payload length - if ( sizeof(TEST_OVERRIDE_ARRAY_PAYLOAD_T) == message->hdr.payloadLen ) - { - memcpy( &payload, message->payload, sizeof(TEST_OVERRIDE_ARRAY_PAYLOAD_T) ); - if ( FALSE == payload.reset ) - { - result = testSetFanRPMOverride( payload.index, payload.state.f32 ); - } - else - { - result = testResetFanRPMOverride( payload.index ); - } - } - - // respond to request - sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); -} - -/*********************************************************************//** * @brief * The handleStopDGRTCClock function handles a request to stop the RTC clock. * @details Inputs: none @@ -3172,30 +3221,88 @@ /*********************************************************************//** * @brief + * The handleFansRPMOverride function handles a request to override a fans RPM value. + * @details Inputs: none + * @details Outputs: message handled + * @param message a pointer to the message to handle + * @return none + *************************************************************************/ +void handleFansRPMOverride( MESSAGE_T *message ) +{ + TEST_OVERRIDE_ARRAY_PAYLOAD_T payload; + BOOL result = FALSE; + + // verify payload length + if ( sizeof(TEST_OVERRIDE_ARRAY_PAYLOAD_T) == message->hdr.payloadLen ) + { + memcpy( &payload, message->payload, sizeof(TEST_OVERRIDE_ARRAY_PAYLOAD_T) ); + if ( FALSE == payload.reset ) + { + result = testSetFanRPMOverride( payload.index, payload.state.f32 ); + } + else + { + result = testResetFanRPMOverride( payload.index ); + } + } + + // respond to request + sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); +} + +/*********************************************************************//** + * @brief * The handleTestFansRPMAlarmStartTimeOverrideRequest function handles a * request to override the fan RPM alarm start time. * @details Inputs: none * @details Outputs: message handled + * @param message : a pointer to the message to handle + * @return none + *************************************************************************/ +void handleTestFansRPMAlarmStartTimeOffsetRequest( MESSAGE_T *message ) +{ + U32 rpmTimeOffset; + + BOOL result = FALSE; + + // Verify payload length + if ( sizeof(U32) == message->hdr.payloadLen ) + { + memcpy( &rpmTimeOffset, message->payload, sizeof(U32) ); + + result = testSetFanRPMAlarmStartTimestamp( rpmTimeOffset ); + } + + // Respond to request + sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); +} + +/*********************************************************************//** + * @brief + * The handleSetFansDutyCycleOverrideRequest function handles a + * request to override the fans duty cycle. + * @details Inputs: none + * @details Outputs: message handled * @param message a pointer to the message to handle * @return none *************************************************************************/ -void handleTestFansRPMAlarmStartTimeOffsetOverrideRequest( MESSAGE_T *message ) +void handleSetFansDutyCycleOverrideRequest( MESSAGE_T *message ) { - TEST_OVERRIDE_ARRAY_PAYLOAD_T payload; + TEST_OVERRIDE_PAYLOAD_T payload; BOOL result = FALSE; // verify payload length - if ( sizeof( TEST_OVERRIDE_ARRAY_PAYLOAD_T ) == message->hdr.payloadLen ) + if ( sizeof( TEST_OVERRIDE_PAYLOAD_T ) == message->hdr.payloadLen ) { - memcpy( &payload, message->payload, sizeof( TEST_OVERRIDE_ARRAY_PAYLOAD_T ) ); + memcpy( &payload, message->payload, sizeof( TEST_OVERRIDE_PAYLOAD_T ) ); if ( FALSE == payload.reset ) { - result = testSetFanRPMAlarmStartTimeOffsetOverride( payload.state.u32, payload.index ); + result = testSetFansDutyCycleOverride( payload.state.f32 ); } else { - result = testResetFanRPMAlarmStartTimeOffsetOverride(); + result = testResetFansDutyCycleOverride(); } } @@ -3204,7 +3311,102 @@ } /*********************************************************************//** +* @brief +* The handleGetDGSoftwareConfigRecord function handles a request to get the DG +* software configuration record. +* @details Inputs: none +* @details Outputs: message handled +* @param message a pointer to the message to handle +* @return none +*************************************************************************/ +void handleGetDGSoftwareConfigRecord( MESSAGE_T *message ) +{ + BOOL result = FALSE; + + // verify payload length + if ( 0 == message->hdr.payloadLen ) + { + // Tester must be logged in + if ( TRUE == isTestingActivated() ) + { + result = sendRecordToDialin( NVDATAMGMT_SW_CONFIG_RECORD ); + } + } + + // respond to request + sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); +} + +/*********************************************************************//** +* @brief +* The handleSetDGSoftwareConfigRecord function handles a request to set the DG +* software configuration record. +* @details Inputs: none +* @details Outputs: message handled +* @param message a pointer to the message to handle +* @return none +*************************************************************************/ +void handleSetDGSoftwareConfigRecord( MESSAGE_T *message ) +{ + U32 currentMessage; + U32 totalMessages; + U32 payloadLength; + + BOOL status = FALSE; + U08* payloadPtr = message->payload; + + if ( message->hdr.payloadLen >= ( sizeof(currentMessage) + sizeof(totalMessages) + sizeof(payloadLength) ) ) + { + memcpy(¤tMessage, payloadPtr, sizeof(U32)); + payloadPtr += sizeof(U32); + + memcpy(&totalMessages, payloadPtr, sizeof(U32)); + payloadPtr += sizeof(U32); + + memcpy(&payloadLength, payloadPtr, sizeof(U32)); + payloadPtr += sizeof(U32); + + status = receiveRecordFromDialin( NVDATAMGMT_SW_CONFIG_RECORD, currentMessage, totalMessages, payloadLength, payloadPtr ); + } + + // Respond to request + sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, status ); +} + +/*********************************************************************//** * @brief + * The handleTestHDCommunicationStatusOverrideRequest function handles a request + * request to override the HD Communication Status. + * @details Inputs: none + * @details Outputs: message handled + * @param message a pointer to the message to handle + * @return none + *************************************************************************/ +void handleTestHDCommunicationStatusOverrideRequest(MESSAGE_T *message) +{ + TEST_OVERRIDE_PAYLOAD_T payload; + BOOL result = FALSE; + + // verify payload length + if ( sizeof( TEST_OVERRIDE_PAYLOAD_T ) == message->hdr.payloadLen ) + { + memcpy( &payload, message->payload, sizeof( TEST_OVERRIDE_PAYLOAD_T ) ); + if ( FALSE == payload.reset ) + { + result = testSetHDCommunicationStatus(payload.state.u32); + } + else + { + result = testResetHDCommuncationStatus(); + } + } + + // respond to request + sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); +} + +/*********************************************************************//** + * @brief * The handleTestUsedAcidVolumeMLOverrideRequest function handles a * request to override the acid volume. * @details Inputs: none Index: firmware/App/Services/SystemCommMessages.h =================================================================== diff -u -re28e96fba3f2293208e6d91673288acba2514cca -r9279d01e7e539ad1e788b85db4ea2288965b03c6 --- firmware/App/Services/SystemCommMessages.h (.../SystemCommMessages.h) (revision e28e96fba3f2293208e6d91673288acba2514cca) +++ firmware/App/Services/SystemCommMessages.h (.../SystemCommMessages.h) (revision 9279d01e7e539ad1e788b85db4ea2288965b03c6) @@ -8,7 +8,7 @@ * @file SystemCommMessages.h * * @author (last) Dara Navaei -* @date (last) 21-Dec-2021 +* @date (last) 03-Feb-2022 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 @@ -140,6 +140,12 @@ // MSG_ID_DG_POST_FINAL_TEST_RESULT BOOL sendPOSTFinalResult( BOOL passed ); +// MSG_ID_DG_SEND_SW_CONFIG_RECORD +BOOL sendDGSWConfigRecord( U32 payloadCurrNum, U32 payloadTotalNum, U32 length, U08* swRcrdAddress ); + +// MSG_ID_DG_SCHEDULED_RUNS_INFO +void handleDGScheduledRunsRequest( MESSAGE_T *message ); + // *********** public test support message functions ********** // MSG_TESTER_LOG_IN @@ -350,9 +356,27 @@ // MSG_ID_DG_DRAIN_PUMP_MEASURED_RPM_OVERRIDE void handleSetDrainPumpMeasuredRPMOverrideRequest( MESSAGE_T *message ); -// MSG_ID_DG_FAN_RPM_ALARM_START_TIME_OFFSET_OVERRIDE -void handleTestFansRPMAlarmStartTimeOffsetOverrideRequest( MESSAGE_T *message ); +// MSG_ID_DG_ALARM_INFO_SEND_INTERVAL_OVERRIDE +void handleTestAlarmInfoSendIntervalOverrideRequest( MESSAGE_T *message ); +// MSG_ID_DG_SUPER_CLEAR_ALARMS_CMD +void handleTestSuperClearAlarmsRequest( MESSAGE_T *message ); + +// MSG_ID_DG_SET_FANS_RPM_ALARM_START_TIME_OFFSET +void handleTestFansRPMAlarmStartTimeOffsetRequest( MESSAGE_T *message ); + +// MSG_ID_DG_FANS_DUTY_CYCLE_OVERRIDE +void handleSetFansDutyCycleOverrideRequest( MESSAGE_T *message ); + +// MSG_ID_DG_GET_SW_CONFIG_RECORD +void handleGetDGSoftwareConfigRecord( MESSAGE_T *message ); + +// MSG_ID_DG_SET_SW_CONFIG_RECORD +void handleSetDGSoftwareConfigRecord( MESSAGE_T *message ); + +// MSG_ID_DG_HD_COMMUNICATION_STATUS +void handleTestHDCommunicationStatusOverrideRequest(MESSAGE_T *message); + // MSG_ID_DG_USED_ACID_VOLUME_ML_OVERRIDE void handleTestUsedAcidVolumeMLOverrideRequest(MESSAGE_T *message);