Index: firmware/App/Controllers/ConductivitySensors.c =================================================================== diff -u -ra9315539f527b92523b1598ff91e47db4d71dae2 -rc7ed4f90b600fe684097654f150e4972646ffec9 --- firmware/App/Controllers/ConductivitySensors.c (.../ConductivitySensors.c) (revision a9315539f527b92523b1598ff91e47db4d71dae2) +++ firmware/App/Controllers/ConductivitySensors.c (.../ConductivitySensors.c) (revision c7ed4f90b600fe684097654f150e4972646ffec9) @@ -44,16 +44,19 @@ #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. -#define COND_SENSOR_CPI_MAX_VALUE 2000 ///< Maximum inlet water conductivity. -#define COND_SENSOR_CPI_MIN_VALUE 100 ///< Minimum inlet water conductivity. +#define MAX_COND_SENSOR_CPI_WARNING_HIGH 2000.0F ///< Maximum allowed high conductivity value. +#define MIN_COND_SENSOR_CPI_WARNING_HIGH 1990.0F ///< Minimum allowed high conductivity value. +#define MAX_COND_SENSOR_CPI_WARNING_LOW 100.0F ///< Maximum allowed low conductivity value. +#define MIN_COND_SENSOR_CPI_WARNING_LOW 110.0F ///< Minimum allowed low conductivity value. + #define MAX_ALLOWED_UNCHANGED_CONDUCTIVITY_READS ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< New reading every 800 ms, expect to get valid new reading in 1s. #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.1 ///< Maximum RO rejection ratio. -#define MAX_CPO_CONDUCTIVITY_ALLOW 15.0 ///< Maximum CPo sensor conductivity value. +#define MAX_RO_REJECTION_RATIO_ALLOW 0.10F ///< Maximum RO rejection ratio. +#define MAX_CPO_CONDUCTIVITY_ALLOW 30.0 ///< 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. @@ -146,7 +149,8 @@ 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_LOW_CONDUCTIVITY, COND_SENSOR_PERSISTENCE_PERIOD, COND_SENSOR_PERSISTENCE_PERIOD ); + initPersistentAlarm( ALARM_ID_INLET_WATER_CONDUCTIVITY_IN_HIGH_RANGE, COND_SENSOR_PERSISTENCE_PERIOD, COND_SENSOR_PERSISTENCE_PERIOD ); + initPersistentAlarm( ALARM_ID_INLET_WATER_CONDUCTIVITY_IN_LOW_RANGE, 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 ); } @@ -225,20 +229,41 @@ /*********************************************************************//** * @brief * The checkInletWaterConductivity function checks inlet water conductivity value - * and triggers an alarm when conductivity value is out of allowed range. + * and triggers an alarm when conductivity value is not within the specified + * values. * @details Inputs: CPi sensor conductivity - * @details Outputs: Trigger alarms when conductivity is out of allowed range + * @details Outputs: Trigger warning alarm if conductivity is in the warning + * range. Trigger alarm if conductivity is below minimum conductivity. * @return none *************************************************************************/ void checkInletWaterConductivity( void ) { #ifndef DISABLE_WATER_QUALITY_CHECK - F32 const conductivity = getConductivityValue( CONDUCTIVITYSENSORS_CPI_SENSOR ); - BOOL const isCondTooHigh = ( conductivity > COND_SENSOR_CPI_MAX_VALUE ); - BOOL const isCondTooLow = ( conductivity < COND_SENSOR_CPI_MIN_VALUE ); - checkPersistentAlarm( ALARM_ID_INLET_WATER_HIGH_CONDUCTIVITY, isCondTooHigh, conductivity, COND_SENSOR_CPI_MAX_VALUE ); - checkPersistentAlarm( ALARM_ID_INLET_WATER_LOW_CONDUCTIVITY, isCondTooLow, conductivity, COND_SENSOR_CPI_MIN_VALUE ); + F32 conductivity = getConductivityValue( CONDUCTIVITYSENSORS_CPI_SENSOR ); + BOOL isConductTooLow = ( conductivity < MAX_COND_SENSOR_CPI_WARNING_LOW ? TRUE : FALSE ); + BOOL isConductInWarningRange = ( conductivity > MAX_COND_SENSOR_CPI_WARNING_HIGH ? TRUE : FALSE ); + + // Alarm per PRS 403 + if ( TRUE == isConductTooLow ) + { + checkPersistentAlarm( ALARM_ID_INLET_WATER_CONDUCTIVITY_IN_LOW_RANGE, isConductTooLow, conductivity, MAX_COND_SENSOR_CPI_WARNING_LOW ); + } + else if ( conductivity >= MIN_COND_SENSOR_CPI_WARNING_LOW ) + { + checkPersistentAlarm( ALARM_ID_INLET_WATER_CONDUCTIVITY_IN_LOW_RANGE, FALSE, conductivity, MAX_COND_SENSOR_CPI_WARNING_LOW ); + } + + // Alarm per PRS 404 + if ( TRUE == isConductInWarningRange ) + { + checkPersistentAlarm( ALARM_ID_INLET_WATER_CONDUCTIVITY_IN_HIGH_RANGE, isConductInWarningRange, conductivity, MAX_COND_SENSOR_CPI_WARNING_HIGH ); + } + else if ( conductivity <= MIN_COND_SENSOR_CPI_WARNING_HIGH ) + { + checkPersistentAlarm( ALARM_ID_INLET_WATER_CONDUCTIVITY_IN_HIGH_RANGE, FALSE, conductivity, MAX_COND_SENSOR_CPI_WARNING_HIGH ); + } + #endif } @@ -254,10 +279,11 @@ void checkRORejectionRatio( void ) { #ifndef DISABLE_RO_RATIO_CHECK - F32 const cpo = getConductivityValue( CONDUCTIVITYSENSORS_CPO_SENSOR ); - BOOL const isRORejectionRatioOutOfRange = ( roRejectionRatio > MAX_RO_REJECTION_RATIO_ALLOW ) || ( cpo >= MAX_CPO_CONDUCTIVITY_ALLOW ); + F32 cpo = getConductivityValue( CONDUCTIVITYSENSORS_CPO_SENSOR ); + BOOL isRORectionOutOfRange = ( ( roRejectionRatio > MAX_RO_REJECTION_RATIO_ALLOW ) || ( cpo >= MAX_CPO_CONDUCTIVITY_ALLOW ) ? TRUE : FALSE ); - checkPersistentAlarm( ALARM_ID_RO_REJECTION_RATIO_OUT_OF_RANGE, isRORejectionRatioOutOfRange, roRejectionRatio, MAX_RO_REJECTION_RATIO_ALLOW ); + // Fault alarm per PRS 483 + checkPersistentAlarm( ALARM_ID_RO_REJECTION_RATIO_OUT_OF_RANGE, isRORectionOutOfRange, roRejectionRatio, MAX_RO_REJECTION_RATIO_ALLOW ); #endif } @@ -299,10 +325,10 @@ *************************************************************************/ static F32 calcCompensatedConductivity( F32 conductivity, F32 temperature ) { - // EC = EC_25 * (1 + temp_coef * ( 25 - temperature )) - F32 const compensatedCoef = ( 1.0 + ( COND_SENSOR_TEMPERATURE_COEF * ( COND_SENSOR_REFERENCE_TEMPERATURE - temperature ) ) ); + // EC = EC_25 * (1 + temp_coef * ( temperature - 25 )) + F32 const compensatedCoef = ( 1.0 + ( COND_SENSOR_TEMPERATURE_COEF * ( temperature - COND_SENSOR_REFERENCE_TEMPERATURE ) ) ); - return conductivity * compensatedCoef; + return conductivity / compensatedCoef; } /*********************************************************************//** Index: firmware/App/Controllers/ConductivitySensors.h =================================================================== diff -u -r487057777532342a4034df8296310270c8827f14 -rc7ed4f90b600fe684097654f150e4972646ffec9 --- firmware/App/Controllers/ConductivitySensors.h (.../ConductivitySensors.h) (revision 487057777532342a4034df8296310270c8827f14) +++ firmware/App/Controllers/ConductivitySensors.h (.../ConductivitySensors.h) (revision c7ed4f90b600fe684097654f150e4972646ffec9) @@ -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,10 @@ 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) } CONDUCTIVITY_DATA_T; // ********** public function prototypes ********** @@ -66,10 +84,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/Pressures.c =================================================================== diff -u -ra9315539f527b92523b1598ff91e47db4d71dae2 -rc7ed4f90b600fe684097654f150e4972646ffec9 --- firmware/App/Controllers/Pressures.c (.../Pressures.c) (revision a9315539f527b92523b1598ff91e47db4d71dae2) +++ firmware/App/Controllers/Pressures.c (.../Pressures.c) (revision c7ed4f90b600fe684097654f150e4972646ffec9) @@ -46,7 +46,9 @@ #define MIN_VALID_PRESSURE_RANGE 0.0 ///< Minimum valid range on pressure reading. #define MAX_VALID_PRESSURE_RANGE 200.0 ///< Maximum valid range on pressure reading. -#define MIN_INLET_WATER_PRESSURE 25 ///< Minimum water input pressure. +#define MAX_INLET_WATER_PRESSURE_WARNING_LOW 25.0F ///< Maximum allowed low pressure value. +#define MIN_INLET_WATER_PRESSURE_WARNING_LOW 28.0F ///< Minimum allowed low pressure value. + #define INLET_WATER_PRESSURE_PERSISTENCE_PERIOD ( 5 * MS_PER_SECOND ) ///< Persistence period for pressure out of range error. #define PRESSURES_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Interval (ms/task time) at which the pressures data is published on the CAN bus. #define DATA_PUBLISH_COUNTER_START_COUNT 10 ///< Data publish counter start count. @@ -117,7 +119,7 @@ pressuresSelfTestState = PRESSURE_SELF_TEST_STATE_START; pressuresDataPublicationTimerCounter = DATA_PUBLISH_COUNTER_START_COUNT; - initPersistentAlarm( ALARM_ID_INLET_WATER_LOW_PRESSURE, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD ); + initPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_IN_LOW_RANGE, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD ); initPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_FAULT, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD ); } @@ -132,31 +134,23 @@ void checkInletPressure( void ) { #ifndef DISABLE_WATER_QUALITY_CHECK - F32 const pressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); - BOOL const isPressureTooLow = ( pressure < MIN_INLET_WATER_PRESSURE ); + F32 pressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); + BOOL isPressureTooLow = ( pressure < MAX_INLET_WATER_PRESSURE_WARNING_LOW ? TRUE : FALSE ); - checkPersistentAlarm( ALARM_ID_INLET_WATER_LOW_PRESSURE, isPressureTooLow, pressure, MIN_INLET_WATER_PRESSURE ); + // Alarm per PRS 401 + if ( TRUE == isPressureTooLow ) + { + checkPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_IN_LOW_RANGE, isPressureTooLow, pressure, MAX_INLET_WATER_PRESSURE_WARNING_LOW ); + } + else if ( pressure >= MIN_INLET_WATER_PRESSURE_WARNING_LOW ) + { + checkPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_IN_LOW_RANGE, FALSE, pressure, MAX_INLET_WATER_PRESSURE_WARNING_LOW ); + } #endif } /*********************************************************************//** * @brief - * The checkInletPressureFault function checks inlet water pressure value - * and triggers a machine fault when pressure value is out of allowed range. - * @details Inputs: RO pump inlet pressure sensor value - * @details Outputs: Triggers pressure fault persistent alarm - * @return none - *************************************************************************/ -void checkInletPressureFault( void ) -{ - F32 const pressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); - BOOL const isPressureTooLow = ( pressure < MIN_INLET_WATER_PRESSURE ); - - checkPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_FAULT, isPressureTooLow, pressure, MIN_INLET_WATER_PRESSURE ); -} - -/*********************************************************************//** - * @brief * The getMeasuredArterialPressure function gets the current arterial pressure. * @details Inputs: arterialPressure * @details Outputs: none Index: firmware/App/Controllers/Pressures.h =================================================================== diff -u -ra9315539f527b92523b1598ff91e47db4d71dae2 -rc7ed4f90b600fe684097654f150e4972646ffec9 --- firmware/App/Controllers/Pressures.h (.../Pressures.h) (revision a9315539f527b92523b1598ff91e47db4d71dae2) +++ firmware/App/Controllers/Pressures.h (.../Pressures.h) (revision c7ed4f90b600fe684097654f150e4972646ffec9) @@ -58,7 +58,6 @@ void execPressures( void ); SELF_TEST_STATUS_T execPressureSelfTest( void ); void checkInletPressure( void ); -void checkInletPressureFault( void ); F32 getMeasuredDGPressure( U32 pressureID ); BOOL testSetPressuresDataPublishIntervalOverride( U32 value ); Index: firmware/App/Controllers/ROPump.c =================================================================== diff -u -ra9315539f527b92523b1598ff91e47db4d71dae2 -rc7ed4f90b600fe684097654f150e4972646ffec9 --- firmware/App/Controllers/ROPump.c (.../ROPump.c) (revision a9315539f527b92523b1598ff91e47db4d71dae2) +++ firmware/App/Controllers/ROPump.c (.../ROPump.c) (revision c7ed4f90b600fe684097654f150e4972646ffec9) @@ -139,8 +139,8 @@ static S32 measuredFlowReadingsSum = 0; ///< Raw flow reading sums for averaging. static U32 flowFilterCounter = 0; ///< Flow filtering counter. static DG_FLOW_SENSORS_CAL_RECORD_T flowSensorsCalRecord; ///< Flow sensors calibration record. +static OVERRIDE_F32_T measuredROFlowRateWithConcPumpsLPM = { 0.0, 0.0, 0.0, 0 }; ///< Measure RO flow rate with concentrate pumps (L/min). static DG_RO_PUMP_CAL_RECORD_T roPumpCalRecord; ///< RO pump calibration record. -static F32 rawFlowLP; ///< Raw flow rate without subtracting the CP1 and CP2 in L/min. // ********** private function prototypes ********** @@ -198,7 +198,6 @@ roPumpControlMode = NUM_OF_PUMP_CONTROL_MODES; isROPumpOn = FALSE; roPumpFeedbackDutyCyclePct = 0.0; - rawFlowLP = 0.0; } /*********************************************************************//** @@ -335,7 +334,7 @@ *************************************************************************/ void execROPumpMonitor( void ) { - U16 roFlowReading = getFPGAROPumpFlowRate(); + U32 roFlowReading = (U32)getFPGAROPumpFlowRate(); F32 roFeedbackVoltage = getIntADCVoltageConverted( INT_ADC_RO_PUMP_FEEDBACK_DUTY_CYCLE ); // Update sum for flow average calculation @@ -365,9 +364,9 @@ flow * flowSensorsCalRecord.flowSensors[ CAL_DATA_RO_PUMP_FLOW_SENSOR ].gain + flowSensorsCalRecord.flowSensors[ CAL_DATA_RO_PUMP_FLOW_SENSOR ].offset; - rawFlowLP = flow; - measuredROFlowRateLPM.data = flow - ( getMeasuredPumpSpeed( CONCENTRATEPUMPS_CP1_ACID ) / ML_PER_LITER ) - - ( getMeasuredPumpSpeed( CONCENTRATEPUMPS_CP2_BICARB ) / ML_PER_LITER ); + measuredROFlowRateWithConcPumpsLPM.data = flow; + measuredROFlowRateLPM.data = flow - ( getMeasuredPumpSpeed( CONCENTRATEPUMPS_CP1_ACID ) / ML_PER_LITER ) - + ( getMeasuredPumpSpeed( CONCENTRATEPUMPS_CP2_BICARB ) / ML_PER_LITER ); // If the flow is less than a certain value, FPGA will return 0xFFFF meaning that // the flow is 0. @@ -546,6 +545,19 @@ /*********************************************************************//** * @brief + * The getMeasuredROFlowRateWithConcPumpsLPM function gets the measured RO + * pump flow rate with the concentrate pumps. + * @details Inputs: measuredROFlowRateWithConcPumpsLPM + * @details Outputs: measuredROFlowRateWithConcPumpsLPM + * @return the current RO pump flow rate with the concentrate pumps (in L/min). + *************************************************************************/ +F32 getMeasuredROFlowRateWithConcPumpsLPM( void ) +{ + return getF32OverrideValue( &measuredROFlowRateWithConcPumpsLPM ); +} + +/*********************************************************************//** + * @brief * The getTargetROPumpPressure function gets the current target RO pump * pressure. * @details Inputs: targetROPumpPressure @@ -808,15 +820,14 @@ { RO_PUMP_DATA_T pumpData; - pumpData.roPumpTgtFlowRateLM = getTargetROPumpFlowRate(); - pumpData.roPumpTgtPressure = getTargetROPumpPressure(); - pumpData.measROFlowRate = getMeasuredROFlowRateLPM(); - pumpData.roPumpDutyCycle = roPumpDutyCyclePctSet * FRACTION_TO_PERCENT_FACTOR; - pumpData.roPumpState = (U32)roPumpState; - pumpData.roPumpFBDutyCycle = roPumpFeedbackDutyCyclePct * FRACTION_TO_PERCENT_FACTOR; - pumpData.roPumpRawFlowRateMLP = rawFlowLP * ML_PER_LITER; + pumpData.roPumpTgtFlowRateLM = getTargetROPumpFlowRate(); + pumpData.roPumpTgtPressure = getTargetROPumpPressure(); + pumpData.measROFlowRate = getMeasuredROFlowRateLPM(); + pumpData.roPumpDutyCycle = roPumpDutyCyclePctSet * FRACTION_TO_PERCENT_FACTOR; + pumpData.roPumpState = (U32)roPumpState; + pumpData.roPumpFBDutyCycle = roPumpFeedbackDutyCyclePct * FRACTION_TO_PERCENT_FACTOR; + pumpData.roPumpMeasFlowWithConcPumps = getMeasuredROFlowRateWithConcPumpsLPM(); - broadcastData( MSG_ID_RO_PUMP_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&pumpData, sizeof( RO_PUMP_DATA_T ) ); roPumpDataPublicationTimerCounter = 0; } Index: firmware/App/Controllers/ROPump.h =================================================================== diff -u -ra9315539f527b92523b1598ff91e47db4d71dae2 -rc7ed4f90b600fe684097654f150e4972646ffec9 --- firmware/App/Controllers/ROPump.h (.../ROPump.h) (revision a9315539f527b92523b1598ff91e47db4d71dae2) +++ firmware/App/Controllers/ROPump.h (.../ROPump.h) (revision c7ed4f90b600fe684097654f150e4972646ffec9) @@ -43,7 +43,7 @@ U32 roPumpState; ///< RO pump current state. F32 roPumpTgtFlowRateLM; ///< RO pump target flow rate in L/min F32 roPumpFBDutyCycle; ///< RO pump feedback duty cycle. - F32 roPumpRawFlowRateMLP; ///< RO pump raw flow rate in mL/min. + F32 roPumpMeasFlowWithConcPumps; ///< RO pump measured flow with the concentrate pumps. } RO_PUMP_DATA_T; // ********** public function prototypes ********** @@ -63,6 +63,7 @@ F32 getTargetROPumpFlowRate( void ); F32 getMeasuredROFlowRateLPM( void ); +F32 getMeasuredROFlowRateWithConcPumpsLPM( void ); F32 getTargetROPumpPressure( void ); Index: firmware/App/Controllers/TemperatureSensors.c =================================================================== diff -u -ra9315539f527b92523b1598ff91e47db4d71dae2 -rc7ed4f90b600fe684097654f150e4972646ffec9 --- firmware/App/Controllers/TemperatureSensors.c (.../TemperatureSensors.c) (revision a9315539f527b92523b1598ff91e47db4d71dae2) +++ firmware/App/Controllers/TemperatureSensors.c (.../TemperatureSensors.c) (revision c7ed4f90b600fe684097654f150e4972646ffec9) @@ -55,9 +55,14 @@ #define SHIFT_BITS_BY_2_FOR_AVERAGING 2U ///< Shift the ADCs of the temperature sensors by 2 to average them. #define INLET_WATER_TEMPERATURE_PERSISTENCE_PERIOD ( 5 * MS_PER_SECOND ) ///< Persistence period for temperature sensors out of range error period. -#define MIN_WATER_INPUT_TEMPERATURE 22U ///< Minimum water input temperature. -#define MAX_WATER_INPUT_TEMPERATURE 35U ///< Maximum water input temperature. +#define MIN_WATER_TEMPERATURE_WARNING_LOW_RANGE 22.0F ///< Low range minimum water input temperature. +#define MAX_WATER_TEMPERATURE_WARNING_LOW_RANGE 24.0F ///< Low range maximum water input temperature. +#define MIN_WATER_TEMPERATURE_WARNING_HIGH_RANGE 37.0F ///< High range minimum water input temperature. +#define MAX_WATER_TEMPERATURE_WARNING_HIGH_RANGE 39.0F ///< High range maximum water input temperature. + +#define MAX_WATER_TEMPERATURE_ALARM 50.0F ///< 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. @@ -267,9 +272,15 @@ initPersistentAlarm( ALARM_ID_INLET_WATER_HIGH_TEMPERATURE, INLET_WATER_TEMPERATURE_PERSISTENCE_PERIOD, INLET_WATER_TEMPERATURE_PERSISTENCE_PERIOD ); - initPersistentAlarm( ALARM_ID_INLET_WATER_LOW_TEMPERATURE, INLET_WATER_TEMPERATURE_PERSISTENCE_PERIOD, + initPersistentAlarm( ALARM_ID____AVAILABLE_28, INLET_WATER_TEMPERATURE_PERSISTENCE_PERIOD, INLET_WATER_TEMPERATURE_PERSISTENCE_PERIOD ); + initPersistentAlarm( ALARM_ID_INLET_WATER_TEMPERATURE_IN_LOW_RANGE, INLET_WATER_TEMPERATURE_PERSISTENCE_PERIOD, + INLET_WATER_TEMPERATURE_PERSISTENCE_PERIOD ); + + initPersistentAlarm( ALARM_ID_INLET_WATER_TEMPERATURE_IN_HIGH_RANGE, INLET_WATER_TEMPERATURE_PERSISTENCE_PERIOD, + INLET_WATER_TEMPERATURE_PERSISTENCE_PERIOD ); + // Persistent alarm for the temperature sensors range check initPersistentAlarm( ALARM_ID_DG_TEMPERATURE_SENSOR_OUT_OF_RANGE, TEMP_SENSORS_OUT_OF_RANGE_PERSISTENT_PEROID_MS, TEMP_SENSORS_OUT_OF_RANGE_PERSISTENT_PEROID_MS ); @@ -358,12 +369,33 @@ void checkInletWaterTemperature( void ) { #ifndef DISABLE_WATER_QUALITY_CHECK - F32 const temperature = getTemperatureValue( TEMPSENSORS_INLET_PRIMARY_HEATER ); - BOOL const isWaterTempTooHigh = temperature > MAX_WATER_INPUT_TEMPERATURE; - BOOL const isWaterTempTooLow = temperature < MIN_WATER_INPUT_TEMPERATURE; - checkPersistentAlarm( ALARM_ID_INLET_WATER_HIGH_TEMPERATURE, isWaterTempTooHigh, temperature, MAX_WATER_INPUT_TEMPERATURE ); - checkPersistentAlarm( ALARM_ID_INLET_WATER_LOW_TEMPERATURE, isWaterTempTooLow, temperature, MIN_WATER_INPUT_TEMPERATURE ); + F32 temperature = getTemperatureValue( TEMPSENSORS_INLET_PRIMARY_HEATER ); + BOOL isWaterTempInHighRange = ( temperature > MAX_WATER_TEMPERATURE_WARNING_HIGH_RANGE ? TRUE : FALSE ); + BOOL isWaterTempInLowRange = ( temperature < MIN_WATER_TEMPERATURE_WARNING_LOW_RANGE ? TRUE : FALSE ); + BOOL isWaterTempTooHigh = ( temperature > MAX_WATER_TEMPERATURE_ALARM ? TRUE : FALSE ); + + // Fault alarm per PRS 557 for high temperature + checkPersistentAlarm( ALARM_ID_INLET_WATER_HIGH_TEMPERATURE, isWaterTempTooHigh, temperature, MAX_WATER_TEMPERATURE_ALARM ); + + if ( TRUE == isWaterTempInHighRange ) // warning alarm per PRS 406 + { + checkPersistentAlarm( ALARM_ID_INLET_WATER_TEMPERATURE_IN_HIGH_RANGE, isWaterTempInHighRange, temperature, MAX_WATER_TEMPERATURE_WARNING_HIGH_RANGE ); + } + else if ( temperature <= MIN_WATER_TEMPERATURE_WARNING_HIGH_RANGE ) + { + checkPersistentAlarm( ALARM_ID_INLET_WATER_TEMPERATURE_IN_HIGH_RANGE, FALSE, temperature, MAX_WATER_TEMPERATURE_WARNING_HIGH_RANGE ); + } + + + if ( TRUE == isWaterTempInLowRange ) // warning alarm per PRS 405 + { + checkPersistentAlarm( ALARM_ID_INLET_WATER_TEMPERATURE_IN_LOW_RANGE, isWaterTempInLowRange, temperature, MIN_WATER_TEMPERATURE_WARNING_LOW_RANGE ); + } + else if ( temperature >= MAX_WATER_TEMPERATURE_WARNING_LOW_RANGE ) + { + checkPersistentAlarm( ALARM_ID_INLET_WATER_TEMPERATURE_IN_LOW_RANGE, FALSE, temperature, MIN_WATER_TEMPERATURE_WARNING_LOW_RANGE ); + } #endif } Index: firmware/App/DGCommon.h =================================================================== diff -u -rbff9e37080178dad76312cb199f01568bcfc3e7f -rc7ed4f90b600fe684097654f150e4972646ffec9 --- firmware/App/DGCommon.h (.../DGCommon.h) (revision bff9e37080178dad76312cb199f01568bcfc3e7f) +++ firmware/App/DGCommon.h (.../DGCommon.h) (revision c7ed4f90b600fe684097654f150e4972646ffec9) @@ -57,6 +57,9 @@ #define DISABLE_HEATERS_EFFICIENCY 1 #define DISABLE_HEAT_DISINFECT_CONDUCTIVITY 1 // Implement +// #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 // Implement // #define DISABLE_MIXING 1 // Implement Index: firmware/App/Modes/ModeDrain.c =================================================================== diff -u -ra9315539f527b92523b1598ff91e47db4d71dae2 -rc7ed4f90b600fe684097654f150e4972646ffec9 --- firmware/App/Modes/ModeDrain.c (.../ModeDrain.c) (revision a9315539f527b92523b1598ff91e47db4d71dae2) +++ firmware/App/Modes/ModeDrain.c (.../ModeDrain.c) (revision c7ed4f90b600fe684097654f150e4972646ffec9) @@ -19,6 +19,7 @@ #include "ConductivitySensors.h" #include "DrainPump.h" #include "Heaters.h" +#include "ModeFill.h" #include "ModeDrain.h" #include "OperationModes.h" #include "Pressures.h" @@ -242,6 +243,19 @@ setValveState( VRD2, VALVE_STATE_CLOSED ); } + // check for empty bottles at the end of drain + if ( TRUE == isEmptyAcidBottle() ) // is acid volume < 10% + { + setThisFisrtFillFlag( TRUE ); // indicates bottles need prime + activateAlarmNoData( ALARM_ID_DG_ACID_BOTTLE_LOW_VOLUME ); // trigger empty acid bottle alarm + } + + if ( TRUE == isEmptyBicarbBottle() ) + { + setThisFisrtFillFlag( TRUE ); + activateAlarmNoData( ALARM_ID_DG_BICARB_BOTTLE_LOW_VOLUME ); + } + if ( TRUE == isReservoirTarePending() ) { // Tare reservoir load cells at empty if requested result = DG_DRAIN_STATE_TARE; @@ -251,9 +265,7 @@ requestNewOperationMode( DG_MODE_GENE ); } } - - // Drain timed out raise the alarm - if ( TRUE == didTimeout( dialysateDrainStartTime, DIALYSATE_DRAIN_TIME_OUT ) ) + else if ( TRUE == didTimeout( dialysateDrainStartTime, DIALYSATE_DRAIN_TIME_OUT ) ) // Drain timed out raise the alarm { activateAlarmNoData( ALARM_ID_DG_DIALYSATE_DRAIN_TIME_OUT ); } Index: firmware/App/Modes/ModeFill.c =================================================================== diff -u -raa1f3f772497ece7928affb06214269d93d036e0 -rc7ed4f90b600fe684097654f150e4972646ffec9 --- firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision aa1f3f772497ece7928affb06214269d93d036e0) +++ firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision c7ed4f90b600fe684097654f150e4972646ffec9) @@ -7,8 +7,8 @@ * * @file ModeFill.c * -* @author (last) Michael Garthwaite -* @date (last) 10-Mar-2022 +* @author (last) Dara Navaei +* @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,30 +47,49 @@ // ********** 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.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 FLOW_INTEGRATED_VOLUME_CHECK_TOLERANCE 0.1F ///< Flow integrated volume has 10% tolerance compare to load cell reading. +#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 ( 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 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.0 ///< Volume of acid concentration in ml. -#define BICARB_CONCENTRATION_BOTTLE_VOLUME_ML 3000.0 ///< Volume of bicarb concentration in ml. -#define CONCENTRATION_BOTTLE_LOW_VOLUME_ML 100.0 ///< 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. +#define DATA_PUBLISH_COUNTER_START_COUNT 63 ///< Data publish counter start count. + /// 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 ) ); +static const F32 RO_PUMP_FLUSH_BUBBLES_FLOWS[ RO_PUMP_LOOKUP_TABLE_SIZE ] = { RO_PUMP_400_ML_PER_MIN, + RO_PUMP_800_ML_PER_MIN, + RO_PUMP_400_ML_PER_MIN, + RO_PUMP_800_ML_PER_MIN }; ///< Lookup table to determine the desired RO speed in mL/min when flushing bubbles. + // ********** private data ********** /// Fill conditions status @@ -81,64 +102,95 @@ 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 dataPublishCounter; ///< 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 U32 pumpSpeedIndex; ///< Index used to access the desired pump speed in roPumpFlushBubblesSpeed table. + +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. + // ********** 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 handlePrimeConcentrateLinesState( void ); +static DG_FILL_MODE_STATE_T handleFlushBubblesState( 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; - reservoirBaseWeight = 0.0; - totalROFlowRateMLPM = 0.0; - concentrateTestStartTime = 0; - acidConductivityTotal = 0.0; - dialysateConductivityTotal = 0.0; - conductivitySampleCount = 0; - concentratePumpPrimeCount = 0; + fillState = DG_FILL_MODE_STATE_START; + dialysateFillStartTime = 0; + dataPublishCounter = DATA_PUBLISH_COUNTER_START_COUNT; + reservoirBaseWeight = 0.0; + totalROFlowRateMLPM = 0.0; + concentrateTestStartTime = 0; + acidConductivityTotal = 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_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 +243,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 +257,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 = handlePrimeConcentrateLinesState(); break; - case DG_FILL_MODE_STATE_ACID_PUMP_CHECK: - fillState = handleAcidPumpCheckState(); + case DG_FILL_MODE_STATE_FLUSH_BUBBLES: + fillState = handleFlushBubblesState(); 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; @@ -302,9 +363,11 @@ fillStatus.fillTemperatureRunningSum = 0.0; // At the beginning the last and average temperatures are considered as the trimmer heater target temperature which // is the dialysate temperature - fillStatus.fillTemperatureAverage = getHeaterTargetTemperature( DG_TRIMMER_HEATER ); - fillStatus.fillLastTemperature = getHeaterTargetTemperature( DG_TRIMMER_HEATER ) + RESERVOIR_EXTRA_TEMPERATURE; - fillStatus.isThisFirstFill = TRUE; + 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,194 +385,291 @@ /*********************************************************************//** * @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; + // If this is the first fill of a treatment, prime acid and bicarb lines, otherwise transition + // to dialysate production directly + if ( TRUE == isThisTheFirstFill() ) + { + // 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 ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); // TODO if + requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); + + concentratePrimingStartTime = getMSTimerCount(); + result = DG_FILL_MODE_STATE_PRIME_CONCENTRATE_LINES; + } + else + { + result = DG_FILL_MODE_STATE_PRODUCE_DIALYSATE; + } + + // TODO get back to this #ifndef DISABLE_DIALYSATE_CHECK - if ( TRUE == isWaterQualityGood() ) + 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 + + return result; +} + +/*********************************************************************//** + * @brief + * The handlePrimeConcentrateLinesState function primes the acid and bicarb + * lines. + * @details Inputs: None + * @details Outputs: None + * @return the next state + *************************************************************************/ +static DG_FILL_MODE_STATE_T handlePrimeConcentrateLinesState( void ) +{ + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_PRIME_CONCENTRATE_LINES; + + if ( TRUE == didTimeout( concentratePrimingStartTime, PRIME_CONCENTRATE_LINES_TIME_OUT_MS ) ) { - // If this is the first fill of a treatment, prime and acid and bicarb lines, otherwise transition - // to dialysate production directly - if ( TRUE == isThisTheFirstFill() ) - { - concentrateTestStartTime = getMSTimerCount(); - #ifndef DISABLE_MIXING + requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); - setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, CONCENTRATE_PUMP_MAX_SPEED ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); - #endif - result = DG_FILL_MODE_STATE_BICARB_PUMP_CHECK; + // Set the RO pump flow rate in according to the roPumpFlushBubblesSpeed table to flush bubbles + pumpSpeedIndex = 0; + setROPumpTargetFlowRateLPM( RO_PUMP_FLUSH_BUBBLES_FLOWS[ pumpSpeedIndex ] / MILLILITERS_PER_LITER, TARGET_RO_PRESSURE_PSI ); + + flushBubblesStartTime = getMSTimerCount(); + result = DG_FILL_MODE_STATE_FLUSH_BUBBLES; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The handleFlushBubblesState 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 handleFlushBubblesState( 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( RO_PUMP_FLUSH_BUBBLES_FLOWS[ pumpSpeedIndex++ ] / MILLILITERS_PER_LITER, TARGET_RO_PRESSURE_PSI ); + flushBubblesStartTime = getMSTimerCount(); } else { - result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; + // 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; } } - if ( TRUE == didTimeout( waterQualityCheckStartTime, WATER_QUALITY_CHECK_TIME_OUT_MS ) ) - { - activateAlarmNoData( ALARM_ID_DG_BAD_INLET_WATER_QUALITY ); - requestNewOperationMode( DG_MODE_GENE ); - } - return result; } /*********************************************************************//** * @brief - * The handleBicarbPumpCheckState function checks conductivity value for + * 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_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_TEST_BICARB_CONDUCTIVITY; + F32 bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); - 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_BICARB_CONDUCTIVITY_TEST + totalBicarbConductivity += bicarbConductivity; + bicarbConductivitySampleCount++; -#ifndef DISABLE_DIALYSATE_CHECK - F32 bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); -#else - F32 bicarbConductivity = MAX_BICARB_CONCENTRATE_CONDUCTIVITY; -#endif - - bicarbPumpFlowRateMLPM = MIN( bicarbPumpFlowRateMLPM, CONCENTRATE_PUMP_MAX_SPEED ); - - 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; + bicarbConductivitySampleCount = 0; + acidConductivitySampleCount = 0; #endif - 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 + { + // Bicarb test failed, go to fault + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_BICARB_CONDUCTIVITY_OUT_OF_RANGE, averageBicarbConductivity ); + } } - 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( averageAcidConductivity, ACID_TEST_CD2_TCD, FIVE_PERCENT_FACTOR ) ) && + ( pctDiffInConductivity < FIVE_PERCENT_FACTOR ) ) { - concentratePumpPrimeCount = 0; +#endif + setROPumpTargetFlowRateLPM( getTargetFillFlowRateLPM(), TARGET_RO_PRESSURE_PSI ); + + handleDialysateMixing( getMeasuredROFlowRateLPM() * ML_PER_LITER ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); + + // 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; + result = DG_FILL_MODE_STATE_PRODUCE_DIALYSATE; + +#ifndef DISABLE_ACID_CONDUCTIVITY_TEST } + else + { + // Acid conductivity did not pass, fault + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE, averageAcidConductivity ); + } } - 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; - F32 measuredROFlowRateMLPM = getMeasuredROFlowRateLPM() * ML_PER_LITER; + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_PRODUCE_DIALYSATE; -#ifndef DISABLE_DIALYSATE_CHECK - if ( ( TRUE == isWaterQualityGood() ) && ( TRUE == checkDialysateTemperature() ) ) -#else - if ( TRUE ) -#endif - { - // Prime mixing before deliver result to reservoir - handleDialysateMixing( measuredROFlowRateMLPM ); + // Prime mixing before deliver result to reservoir + handleDialysateMixing( getMeasuredROFlowRateLPM() * ML_PER_LITER ); + #ifndef DISABLE_MIXING - requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); + + if ( concentratePumpPrimeCount++ > CONCENTRATE_PUMP_PRIME_INTERVAL ) #endif + { + 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,96 +684,91 @@ *************************************************************************/ static DG_FILL_MODE_STATE_T handleDeliverDialysateState( void ) { - F32 integratedVolumeML; + 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(); + F32 acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); + F32 bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); - // Set concentrate pumps speed based off RO pump flow rate - handleDialysateMixing( measuredROFlowRateMLPM ); + // Set concentrate pumps speed based on the RO pump flow rate + handleDialysateMixing( getMeasuredROFlowRateLPM() * ML_PER_LITER ); - totalROFlowRateMLPM += measuredROFlowRateMLPM; - integratedVolumeML = totalROFlowRateMLPM * FLOW_INTEGRATOR * ACID_BICARB_CONCENTRATE_ADDITION_MULTIPLER; - usedAcidVolumeML.data += getMeasuredPumpSpeed( CONCENTRATEPUMPS_CP1_ACID ) * FLOW_INTEGRATOR; - usedBicarbVolumeML.data += getMeasuredPumpSpeed( CONCENTRATEPUMPS_CP2_BICARB ) * FLOW_INTEGRATOR; + totalROFlowRateMLPM += getMeasuredROFlowRateWithConcPumpsLPM() * ML_PER_LITER; + integratedVolumeML = totalROFlowRateMLPM * FLOW_INTEGRATOR; + usedAcidVolumeML.data += getMeasuredPumpSpeed( CONCENTRATEPUMPS_CP1_ACID ) * FLOW_INTEGRATOR; + usedBicarbVolumeML.data += getMeasuredPumpSpeed( CONCENTRATEPUMPS_CP2_BICARB ) * FLOW_INTEGRATOR; - acidConductivityTotal += acidConductivity; - dialysateConductivityTotal += dialysateConductivity; + acidConductivityTotal += acidConductivity; + bicarbConductivityTotal += bicarbConductivity; conductivitySampleCount++; // DG is delivering dialysate keep collecting the sample counter and the measured flow fillStatus.fillSampleCounter += 1; fillStatus.fillFlowRateRunningSum += getMeasuredROFlowRateLPM(); fillStatus.fillTemperatureRunningSum += getTemperatureValue( (U32)TEMPSENSORS_OUTLET_PRIMARY_HEATER ); -#ifndef DISABLE_DIALYSATE_CHECK - if ( ( isWaterQualityGood() != TRUE ) || ( checkDialysateTemperature() != TRUE ) ) - { - requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); - setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); - result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; - } +#ifndef DISABLE_MIXING + // If you start the fill you should be finished. - if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE, isAcidConductivityOutOfRange ) ) + // 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_MIXING - 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 integratedVolumeToLoadCellReadingPercent = fabs( 1.0F - ( filledVolumeML / integratedVolumeML ) ); + 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 ( FALSE == 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 ( FALSE == 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 +781,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,38 +801,16 @@ { 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; } /*********************************************************************//** * @brief - * The isWaterQualityGood function checks for inlet water quality. - * @details Inputs: Temperature and conductivity alarms - * @details Outputs: none - * @return TRUE if water quality is good, otherwise FALSE - *************************************************************************/ -static BOOL isWaterQualityGood( void ) -{ - 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 ); - - BOOL isWaterConductivityGood = ( ( FALSE == isAlarmActive( ALARM_ID_INLET_WATER_HIGH_CONDUCTIVITY ) && - FALSE == isAlarmActive( ALARM_ID_INLET_WATER_LOW_CONDUCTIVITY ) && - FALSE == isAlarmActive( ALARM_ID_RO_REJECTION_RATIO_OUT_OF_RANGE ) ) ? FALSE : TRUE ); - - return ( ( isInletPressureGood && isWaterTemperatureGood && isWaterConductivityGood ) ); -} - -/*********************************************************************//** - * @brief * The checkDialysateTemperatureSensorsDrift function checks whether the * dialysate temperature sensors have drifted. If they are drifted, it raises * an alarm. @@ -701,18 +834,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; } /*********************************************************************//** @@ -762,4 +904,181 @@ 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 ( ++dataPublishCounter >= 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; + fillModeData.totalIntegratedVolumeML = totalROFlowRateMLPM; + + broadcastData( MSG_ID_DG_FILL_MODE_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&fillModeData, sizeof( DG_FILL_MODE_DATA_T ) ); + + dataPublishCounter = 0; + } +} + + +/************************************************************************* + * TEST SUPPORT FUNCTIONS + *************************************************************************/ + +/*********************************************************************//** + * @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 -ra9315539f527b92523b1598ff91e47db4d71dae2 -rc7ed4f90b600fe684097654f150e4972646ffec9 --- firmware/App/Modes/ModeGenIdle.c (.../ModeGenIdle.c) (revision a9315539f527b92523b1598ff91e47db4d71dae2) +++ firmware/App/Modes/ModeGenIdle.c (.../ModeGenIdle.c) (revision c7ed4f90b600fe684097654f150e4972646ffec9) @@ -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" @@ -41,25 +43,39 @@ // ********** private definitions ********** -#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_PRESSURE_PSI 130 ///< Target pressure for RO pump. +#define TARGET_RO_FLOW_RATE_L 0.3 ///< Target flow rate for RO pump. +#define BAD_FLUSH_FILL_TARGET_VOLUME_ML 1000 ///< Target fill volume in the bad flush fill state. +#define HD_LOST_COMM_TIMEOUT_MS (5 * SEC_PER_MIN * MS_PER_SECOND ) ///< The time of HD lost comm before DG transition back to standby. +#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. +#define DATA_PUBLISH_COUNTER_START_COUNT 61 ///< Data publish counter start count. -#define TARGET_FLUSH_LINES_RO_FLOW_RATE_L 0.3 ///< Target flow rate for RO pump. - -/// 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 ) - // ********** 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; ///< Lost communication with HD start time in ms. +static U32 genIdlePublicationTimerCounter; ///< 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. @@ -69,9 +85,11 @@ *************************************************************************/ void initGenIdleMode( void ) { - genIdleState = DG_GEN_IDLE_MODE_STATE_START; - flushLinesVolumeL = 0.0; - hdLostCommStartTime_ms = 0; + genIdleState = DG_GEN_IDLE_MODE_STATE_START; + hdLostCommStartTime_ms = 0; + targetFillVolumeML = 0; + genIdlePublicationTimerCounter = DATA_PUBLISH_COUNTER_START_COUNT; + handleBadFillFlag = FALSE; } /*********************************************************************//** @@ -108,7 +126,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 +136,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 @@ -148,95 +207,289 @@ 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; break; } + publishBadFillSubstates(); + return (U32)genIdleState; } /*********************************************************************//** * @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: genIdlePublicationTimerCounter + * @details Outputs: genIdlePublicationTimerCounter + * @return none + *************************************************************************/ +static void publishBadFillSubstates( void ) +{ + // publish bad fill sub-states on interval + if ( ++genIdlePublicationTimerCounter >= getU32OverrideValue( &badFillSubstatesPublishInterval ) ) + { + GEN_IDLE_BAD_FILL_STATE data; + + data.badFillState = (U32)badFillState; + + broadcastData( MSG_ID_DG_BAD_FILL_SUB_STATE, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&data, sizeof( GEN_IDLE_BAD_FILL_STATE ) ); + genIdlePublicationTimerCounter = 0; + } +} + + +/************************************************************************* + * TEST SUPPORT FUNCTIONS + *************************************************************************/ + + +/*********************************************************************//** + * @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 -rc7ed4f90b600fe684097654f150e4972646ffec9 --- firmware/App/Modes/ModeGenIdle.h (.../ModeGenIdle.h) (revision a9315539f527b92523b1598ff91e47db4d71dae2) +++ firmware/App/Modes/ModeGenIdle.h (.../ModeGenIdle.h) (revision c7ed4f90b600fe684097654f150e4972646ffec9) @@ -31,16 +31,26 @@ // ********** public definitions ********** +/// Gen Idle bad states data structure +typedef struct +{ + U32 badFillState; ///< Gen Idle bad fill substate. +} GEN_IDLE_BAD_FILL_STATE; + // ********** 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) +BOOL testSetBadFillSubstatesPublishIntervalOverride( U32 value ); +BOOL testResetBadFillSubstatesPublishIntervalOverride( void ); + /**@}*/ #endif Index: firmware/App/Services/Reservoirs.c =================================================================== diff -u -ra9315539f527b92523b1598ff91e47db4d71dae2 -rc7ed4f90b600fe684097654f150e4972646ffec9 --- firmware/App/Services/Reservoirs.c (.../Reservoirs.c) (revision a9315539f527b92523b1598ff91e47db4d71dae2) +++ firmware/App/Services/Reservoirs.c (.../Reservoirs.c) (revision c7ed4f90b600fe684097654f150e4972646ffec9) @@ -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 -r509bb1d86829ca4fc315b6f3502bfbc66489d0b6 -rc7ed4f90b600fe684097654f150e4972646ffec9 --- firmware/App/Services/SystemComm.c (.../SystemComm.c) (revision 509bb1d86829ca4fc315b6f3502bfbc66489d0b6) +++ firmware/App/Services/SystemComm.c (.../SystemComm.c) (revision c7ed4f90b600fe684097654f150e4972646ffec9) @@ -1153,6 +1153,14 @@ handleTestFansRPMAlarmStartTimeOffsetOverrideRequest( message ); break; + case MSG_ID_DG_USED_ACID_VOLUME_ML_OVERRIDE: + handleTestUsedAcidVolumeMLOverrideRequest( message ); + break; + + case MSG_ID_DG_USED_BICARB_VOLUME_ML_OVERRIDE: + handleTestUsedBicarbVolumeMLOverrideRequest( message ); + break; + case MSG_ID_DG_GET_SW_CONFIG_RECORD: handleGetDGSoftwareConfigRecord( message ); break; Index: firmware/App/Services/SystemCommMessages.c =================================================================== diff -u -r509bb1d86829ca4fc315b6f3502bfbc66489d0b6 -rc7ed4f90b600fe684097654f150e4972646ffec9 --- firmware/App/Services/SystemCommMessages.c (.../SystemCommMessages.c) (revision 509bb1d86829ca4fc315b6f3502bfbc66489d0b6) +++ firmware/App/Services/SystemCommMessages.c (.../SystemCommMessages.c) (revision c7ed4f90b600fe684097654f150e4972646ffec9) @@ -26,6 +26,7 @@ #include "Fans.h" #include "FPGA.h" #include "Heaters.h" +#include "ModeFill.h" #include "ModeFlush.h" #include "ModeGenIdle.h" #include "ModeInitPOST.h" @@ -3286,13 +3287,43 @@ /*********************************************************************//** * @brief - * The handleSetFansDutyCycleOverrideRequest function handles a - * request to override the fans duty cycle. + * The handleTestUsedAcidVolumeMLOverrideRequest function handles a + * request to override the acid volume. * @details Inputs: none * @details Outputs: message handled - * @param message a pointer to the message to handle + * @param message : a pointer to the message to handle * @return none *************************************************************************/ +void handleTestUsedAcidVolumeMLOverrideRequest( 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 = testSetUsedAcidVolumeMLOverride( payload.state.f32 ); + } + else + { + result = testResetUsedAcidVolumeMLOverride(); + } + } + + // Respond to request + sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); +} +/*********************************************************************//** +* 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 handleSetFansDutyCycleOverrideRequest( MESSAGE_T *message ) { TEST_OVERRIDE_PAYLOAD_T payload; @@ -3318,6 +3349,38 @@ } /*********************************************************************//** + * @brief + * The handleTestUsedBicarbVolumeMLOverrideRequest function handles a + * request to override the used bicarb volume. + * @details Inputs: none + * @details Outputs: message handled + * @param message : a pointer to the message to handle + * @return none + *************************************************************************/ +void handleTestUsedBicarbVolumeMLOverrideRequest( 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 = testSetUsedBicarbVolumeMLOverride( payload.state.f32 ); + } + else + { + result = testResetUsedBicarbVolumeMLOverride(); + } + } + + // Respond to request + sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); +} + +/*********************************************************************//** * @brief * The handleGetDGSoftwareConfigRecord function handles a request to get the DG * software configuration record. Index: firmware/App/Services/SystemCommMessages.h =================================================================== diff -u -r509bb1d86829ca4fc315b6f3502bfbc66489d0b6 -rc7ed4f90b600fe684097654f150e4972646ffec9 --- firmware/App/Services/SystemCommMessages.h (.../SystemCommMessages.h) (revision 509bb1d86829ca4fc315b6f3502bfbc66489d0b6) +++ firmware/App/Services/SystemCommMessages.h (.../SystemCommMessages.h) (revision c7ed4f90b600fe684097654f150e4972646ffec9) @@ -359,6 +359,12 @@ // MSG_ID_DG_FAN_RPM_ALARM_START_TIME_OFFSET_OVERRIDE void handleTestFansRPMAlarmStartTimeOffsetOverrideRequest( MESSAGE_T *message ); +// MSG_ID_DG_USED_ACID_VOLUME_ML_OVERRIDE +void handleTestUsedAcidVolumeMLOverrideRequest(MESSAGE_T *message); + +// MSG_ID_DG_USED_BICARB_VOLUME_ML_OVERRIDE +void handleTestUsedBicarbVolumeMLOverrideRequest(MESSAGE_T *message); + // MSG_ID_DG_FANS_DUTY_CYCLE_OVERRIDE void handleSetFansDutyCycleOverrideRequest( MESSAGE_T *message );