Index: firmware/App/Modes/ModeHeatDisinfect.c =================================================================== diff -u -r5fc16235c1752c993b3f1285f3a2b9738372af7a -r4b7029ae5b0a163d7c54f26d57ad8f186b96c76f --- firmware/App/Modes/ModeHeatDisinfect.c (.../ModeHeatDisinfect.c) (revision 5fc16235c1752c993b3f1285f3a2b9738372af7a) +++ firmware/App/Modes/ModeHeatDisinfect.c (.../ModeHeatDisinfect.c) (revision 4b7029ae5b0a163d7c54f26d57ad8f186b96c76f) @@ -36,32 +36,65 @@ // ********** private definitions ********** // General defines -#define MAX_ALLOWED_STATE_TRIALS 1 ///< Max allowed trials on a state. This is general among all the states. +#define MAX_ALLOWED_STATE_TRIALS 1 ///< Max allowed trials on a state. This is general among all the states. // Start state defines -#define MIN_INLET_PRESSURE_PSI 30.0 ///< Minimum water inlet pressure in psi. -#define MAX_START_STATE_TEMP_SENSORS_DIFF_C 1.0 ///< Max start state TDi and TRo difference tolerance in C. +#define MIN_INLET_PRESSURE_PSI 30.0 ///< Minimum water inlet pressure in psi. +#define MAX_START_STATE_TEMP_SENSORS_DIFF_C 1.0 ///< Max start state TDi and TRo difference tolerance in C. // Drain R1 & R2 states defines -#define DRAIN_PUMP_TARGET_RPM 2500 ///< Drain pump target RPM during drain. -#define DRAIN_RESERVOIRS_TIME_OUT_MS ( 60 * MS_PER_SECOND ) ///< Drain reservoirs time out in milliseconds. -#define EMPTY_RESERVOIRS_WEIGHT_GRAMS 50 ///< The weight of an empty reservoir. //TODO Change this value to actual value +#define DRAIN_PUMP_TARGET_RPM 2700 ///< Drain pump target RPM during drain. +#define RSRVRS_INITIAL_DRAIN_TIME_OUT_MS ( 60 * MS_PER_SECOND ) ///< Reservoirs 1 & 2 initial drain time out in milliseconds. +#define RSRVRS_EMPTY_VOL_ML 50.0 ///< Reservoirs 1 & 2 empty volume in mL. //TODO Change this value to actual value // Flush drain path state defines -#define FLUSH_DRAIN_WAIT_TIME_MS ( 60 * MS_PER_SECOND ) ///< Flush Drain path wait time in milliseconds. -#define MIN_INLET_TEMPERATURE_C 25.0 ///< Minimum water inlet temperature in C. -#define MIN_INLET_CONDUCTIVITY_S_PER_CM 0.0 ///< Minimum water inlet conductivity in mS/cm? TODO find out the real value +#define FLUSH_DRAIN_WAIT_TIME_MS ( 60 * MS_PER_SECOND ) ///< Flush Drain path wait time in milliseconds. +#define MIN_INLET_TEMPERATURE_C 25.0 ///< Minimum water inlet temperature in C. +#define MIN_INLET_CONDUCTIVITY_US_PER_CM 0.0 ///< Minimum water inlet conductivity in uS/cm +#define MAX_INLET_CONDUCTIVITY_US_PER_CM 2000.0 ///< Maximum water inlet conductivity in us/cm // Flush circulation path state defines -#define RO_PUMP_TARGET_FLOW_RATE_LPM 0.8 ///< RO pump target flow rate in L/min. -#define MAX_RO_PUMP_PRESSURE_PSI 130 ///< Maximum RO pump pressure in psi. -#define FLUSH_CICRCULATION_WAIT_TIME_MS ( 30 * MS_PER_SECOND ) ///< Flush circulation path wait time in milliseconds. -#define MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C 3.0 ///< Maximum flush circulation temperature difference tolerance in C. +#define RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM 0.8 ///< RO pump target flow rate during flush/fill in L/min. +#define MAX_RO_PUMP_FLUSH_FILL_PRESSURE_PSI 130 ///< Maximum RO pump pressure during flush/fill states in psi. +#define FLUSH_CICRCULATION_WAIT_TIME_MS ( 30 * MS_PER_SECOND ) ///< Flush circulation path wait time in milliseconds. +#define MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C 3.0 ///< Maximum flush circulation temperature difference tolerance in C. +// Flush and drain R1 and R2 +#define RSRVRS_FULL_VOL_ML 2200.0 ///< Reservoirs 1 & 2 full volume in mL. +#define RSRVRS_PARTIAL_FILL_VOL_ML 500.0 ///< Reservoirs 1 & 2 partial volume in mL. +#define RSRVRS_FILL_UP_TIMEOUT_MS ( 5 * 60 * MS_PER_SECOND ) ///< Reservoirs 1 & 2 full fill up timeout in ms. +#define RSRVRS_500ML_FILL_UP_TIMEOUT_MS ( 2 * 60 * MS_PER_SECOND ) ///< Reservoirs 1 & 2 partial fill up timeout in ms. +#define RSRVRS_DRAIN_TIMEOUT_MS ( 2 * 60 * MS_PER_SECOND ) ///< Reservoirs 1 & 2 drain timeout in ms. +// Fill and heat water +#define HEAT_DISINFECT_TARGET_TEMPERATURE_C 88.0 ///< Heat disinfect target water temperature in C. +// R1 to R2 & R2 to R1 heat disinfect circulation +#define HEAT_DISINFECT_TARGET_RO_FLOW_LPM 0.2 ///< Heat disinfect target RO flow rate in L/min. +#define HEAT_DISINFECT_MAX_RO_PRESSURE_PSI 30 ///< Heat disinfect maximum RO pressure in psi. +#define HEAT_DISINFECT_START_TEMPERATURE_C 81.0 ///< Heat disinfect minimum acceptable temperature in C. +#define HEAT_DISINFECT_TIME_MS ( 10 * 60 * MS_PER_SECOND ) ///< Heat disinfect time for each section in milliseconds. +#define HEAT_DISINFECT_START_TEMP_TIMOUT_MS ( 30 * 60 * MS_PER_SECOND ) ///< Heat disinfect reaching to minimum temperature timeout in milliseconds. +#define RSRVRS_TARGET_VOL_OUT_TIMEOUT_MS ( 2 * 60 * MS_PER_SECOND ) ///< Reservoirs 1 & 2 maximum volume out of range timeout during heat disnfect. +#define RSRVRS_MAX_TARGET_VOL_CHANGE_ML 100.0 ///< Reservoirs 1 & 2 maximum allowed volume change when full during heat disinfect. +#define POST_HEAT_DISINFECT_WAIT_TIME_MS ( 1 * 60 * MS_PER_SECOND ) ///< Heat disinfect final wait time before flushing the system in milliseconds. +/// Name of reservoirs +typedef enum rsrvrs +{ + R1 = 0, ///< Reservoir 1 + R2, ///< Reservoir 2 + NUM_OF_RSRVRS ///< Number of reservoirs +} RSRVRS_T; +/// TODO remove? +typedef enum rsvrs_status +{ + RSRVR_EMPTY = 0, + RSRVR_PARTIALLY_FULL, + RSVR_FULL, + NUM_OF_RSRVRS_STATUS +} RSRVRS_STATUS_T; // ********** private data ********** @@ -71,6 +104,15 @@ static BOOL areTempSensorsInRange = FALSE; ///< Heat disinfect temperature sensors in/out range flag. /// Boolean flag to check whether draining R1 and R2 is at the end of the heat disinfect cycle or in the beginning. So the drain states can be reused. static BOOL isThisLastDrain = FALSE; +static BOOL isR1Full = FALSE; ///< Reservoir 1 full/empty flag. +static BOOL isR2Full = FALSE; ///< Reservoir 2 full/empty flag. +static U32 R1HeatDisinfectVol = 0; ///< Reservoir 1 full volume during heat disinfect. +static U32 R2HeatDisinfectVol = 0; ///< Reservoir 2 full volume during heat disinfect. +static U32 heatDisinfectTimer = 0; ///< Heat disinfect timer. +static BOOL hasHeatDisinfectStarted = FALSE; ///< Heat disinfect start/not start flag. +static U32 rsrvrsVolMonitorTimer = 0; ///< Reservoir 1 & 2 volume monitor timers during heat disinfect. +static BOOL areRsrvrsLeaking = FALSE; ///< Reservoir 1 & 2 leak check flag during heat disinfect. +static BOOL hasPostHeatDisinfectWaitStarted = FALSE; ///< Final delay at the end of heat disinfect and before flush flag. // ********** private function prototypes ********** @@ -79,9 +121,22 @@ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectDrainR2State( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFlushDrainState( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFlushCirculationState( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFlushR1AndR2State( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFlushR2AndDrainR1State( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFlushDrainR2State( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFlushDrainR1State( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFillWithWaterState( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectDisinfectR1ToR2State( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFillR2WithHotWater( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectDisinfectR2ToR1State( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectMixDrainR1State( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectMixDrainR2State( void ); static void resetActuators( void ); -static void setModeToFailed( DG_HEAT_DISINFECT_STATE_T failedState ); +static void setModeToFailed( DG_HEAT_DISINFECT_STATE_T state ); +static BOOL isRsrvrFull( RSRVRS_T r, F32 targetVol, U32 timeout, DG_HEAT_DISINFECT_STATE_T state ); +static BOOL isRsrvrEmpty( RSRVRS_T r, U32 timeout, DG_HEAT_DISINFECT_STATE_T failedState ); +static BOOL isStateHeatDisinfectComplete( DG_HEAT_DISINFECT_STATE_T state ); /*********************************************************************//** * @brief @@ -98,6 +153,11 @@ isThisLastDrain = FALSE; stateTrialCounter = 0; areTempSensorsInRange = FALSE; + isR1Full = FALSE; + isR2Full = FALSE; + R1HeatDisinfectVol = 0; + R2HeatDisinfectVol = 0; + hasPostHeatDisinfectWaitStarted = FALSE; } /*********************************************************************//** @@ -148,30 +208,39 @@ break; case DG_HEAT_DISINFECT_STATE_FLUSH_R1_AND_R2: + heatDisinfectState = handleHeatDisinfectFlushR1AndR2State(); break; - case DG_HEAT_DISINFECT_STATE_FLUSH_R2_AND_DRAIN_R2: + case DG_HEAT_DISINFECT_STATE_FLUSH_R2_AND_DRAIN_R1: + heatDisinfectState = handleHeatDisinfectFlushR2AndDrainR1State(); break; case DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN_R2: + heatDisinfectState = handleHeatDisinfectFlushDrainR2State(); break; case DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN_R1: + heatDisinfectState = handleHeatDisinfectFlushDrainR1State(); break; case DG_HEAT_DISINFECT_STATE_FILL_WITH_WATER: + heatDisinfectState = handleHeatDisinfectFillWithWaterState(); break; case DG_HEAT_DISINFECT_STATE_DISINFECT_R1_TO_R2: + heatDisinfectState = handleHeatDisinfectDisinfectR1ToR2State(); break; case DG_HEAT_DISINFECT_STATE_FILL_R2_WITH_HOT_WATER: + heatDisinfectState = handleHeatDisinfectFillR2WithHotWater(); break; case DG_HEAT_DISINFECT_STATE_DISINFECT_R2_TO_R1: + heatDisinfectState = handleHeatDisinfectDisinfectR2ToR1State(); break; case DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R1: + heatDisinfectState = handleHeatDisinfectMixDrainR1State(); break; case DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R2: @@ -270,28 +339,24 @@ * @brief * The handleHeatDisinfectDrainR1State function handles the heat disinfect * drain R1 state. - * @details Inputs: stateTimer - * @details Outputs: stateTimer + * @details Inputs: stateTimer, isR1Full + * @details Outputs: stateTimer, isR1Full * @return next state of the heat disinfect state machine *************************************************************************/ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectDrainR1State( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_DRAIN_R1; - F32 R1Weight = getLoadCellFilteredWeight( LOAD_CELL_A1 ); - - if ( R1Weight < EMPTY_RESERVOIRS_WEIGHT_GRAMS ) + if ( isRsrvrEmpty( R1, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS, state ) ) { + isR1Full = FALSE; + // Set the actuators to drain R2. - // NOTE: Drain pump is already on and VDr is already on drain + // NOTE: Drain pump is already on and VDr is already on drain state setValveState( VRD, VALVE_STATE_R2_C_TO_NO ); stateTimer = getMSTimerCount(); state = DG_HEAT_DISINFECT_STATE_DRAIN_R2; } - else if ( didTimeout( stateTimer, DRAIN_RESERVOIRS_TIME_OUT_MS ) ) - { - setModeToFailed( state ); - } return state; } @@ -300,18 +365,18 @@ * @brief * The handleHeatDisinfectDrainR2State function handles the heat disinfect * drain R2 state. - * @details Inputs: stateTimer - * @details Outputs: stateTimer + * @details Inputs: stateTimer, isR2Full + * @details Outputs: stateTimer, isR2Full * @return next state of the heat disinfect state machine *************************************************************************/ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectDrainR2State( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_DRAIN_R2; - F32 R2Weight = getLoadCellFilteredWeight( LOAD_CELL_A2 ); - - if ( R2Weight < EMPTY_RESERVOIRS_WEIGHT_GRAMS ) + if ( isRsrvrEmpty( R2, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS, state ) ) { + isR2Full = FALSE; + if ( isThisLastDrain ) { //TODO set the rest of the actuators @@ -325,10 +390,6 @@ state = DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN; } } - else if ( didTimeout( stateTimer, DRAIN_RESERVOIRS_TIME_OUT_MS ) ) - { - setModeToFailed( state ); - } return state; } @@ -350,10 +411,10 @@ { // If the inlet temperature and conductivity are in range, move onto the next state if ( getTemperatureValue( TEMPSENSORS_INLET_PRIMARY_HEATER ) > MIN_INLET_TEMPERATURE_C && - getConductivityValue( CONDUCTIVITYSENSORS_CPI_SENSOR ) > MIN_INLET_CONDUCTIVITY_S_PER_CM ) + getConductivityValue( CONDUCTIVITYSENSORS_CPI_SENSOR ) > MIN_INLET_CONDUCTIVITY_US_PER_CM ) { setValveState( VPD, VALVE_STATE_DRAIN_C_TO_NC ); - setROPumpTargetFlowRate( RO_PUMP_TARGET_FLOW_RATE_LPM, MAX_RO_PUMP_PRESSURE_PSI ); + setROPumpTargetFlowRate( RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM, MAX_RO_PUMP_FLUSH_FILL_PRESSURE_PSI ); stateTimer = getMSTimerCount(); stateTrialCounter = 0; state = DG_HEAT_DISINFECT_STATE_FLUSH_CIRCULATION; @@ -388,13 +449,14 @@ // Check if the flush circulation time has elapsed and the temperature sensors are not in range yet if ( didTimeout( stateTimer, FLUSH_CICRCULATION_WAIT_TIME_MS ) && FALSE == areTempSensorsInRange ) { - F32 TPmTemp = 0; //TODO add TPm later. This is the new temp sensor of the coldest spot + // TODO add TPm later. This is the new temp sensor of the coldest spot. It is temporarily TDi. + F32 TPmTemp = getTemperatureValue( TEMPSENSORS_INLET_DIALYSATE ); F32 TPoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); F32 TD1Temp = getTemperatureValue( TEMPSENSORS_CONDUCTIVITY_SENSOR_1 ); F32 TD2Temp = getTemperatureValue( TEMPSENSORS_CONDUCTIVITY_SENSOR_2 ); - F32 avgTemp = ( TPmTemp + TPoTemp + TD1Temp + TD2Temp ) / 3.0; //TODO change the number to 4 once the new sensor has been added + F32 avgTemp = ( TPmTemp + TPoTemp + TD1Temp + TD2Temp ) / 4.0; - BOOL isTPmOut = FALSE; //TODO change this calculations once TPm is added + BOOL isTPmOut = fabs( TPmTemp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C; BOOL isTPoOut = fabs( TPoTemp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C; BOOL isTD1Out = fabs( TD1Temp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C; BOOL isTD2Out = fabs( TD2Temp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C; @@ -417,7 +479,7 @@ { areTempSensorsInRange = TRUE; stateTimer = getMSTimerCount(); - // TODO Turn on the composite pumps and wait for 30 seconds + // TODO Turn on the concentrate pumps and wait for 30 seconds } } @@ -428,17 +490,384 @@ //if ( didTimeout( stateTimer, FLUSH_CICRCULATION_WAIT_TIME_MS ) ) if ( TRUE ) { + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); + setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); + setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_FLUSH_R1_AND_R2; + } + } - state = DG_HEAT_DISINFECT_STATE_COMPLETE; + return state; +} + +/*********************************************************************//** + * @brief + * The handleHeatDisinfectFlushR1AndR2State function handles the heat + * disinfect flush reservoir 1 and reservoir 2 state. + * @details Inputs: stateTimer, isR1Full, isR2Full + * @details Outputs: stateTimer, isR1Full, isR2Full + * @return next state of the heat disinfect state machine + *************************************************************************/ +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFlushR1AndR2State( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_FLUSH_CIRCULATION; + + // If R1 is not full, keep monitoring for R1 level and timeout + if ( FALSE == isR1Full ) + { + isR1Full = isRsrvrFull( R1, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS, state ); + } + // Once R1 is full, keep monitoring for R2 level and timeout + if( isR1Full ) + { + isR2Full = isRsrvrFull( R2, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_500ML_FILL_UP_TIMEOUT_MS, state ); + + // Once R2 is full (to 500mL in this case), transition to the next state + if ( isR2Full ) + { + // Set the actuators to flush R2 and drain R1 state + setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); + setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); + setValveState( VRO, VALVE_STATE_R2_C_TO_NC ); + setValveState( VRD, VALVE_STATE_R1_C_TO_NC ); + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_FLUSH_R2_AND_DRAIN_R1; } } return state; } +/*********************************************************************//** + * @brief + * The handleHeatDisinfectFlushR2AndDrainR1State function handles the heat + * disinfect flush reservoir 2 and drain reservoir 1 state. + * @details Inputs: stateTimer + * @details Outputs: stateTimer + * @return next state of the heat disinfect state machine + *************************************************************************/ +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFlushR2AndDrainR1State( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_FLUSH_CIRCULATION; + // If reservoir 1 is empty, turn off the drain pump + if ( FALSE == isR1Full ) + { + isR1Full = isRsrvrEmpty( R1, RSRVRS_DRAIN_TIMEOUT_MS, state ); + + // Done with draining R1 + signalDrainPumpHardStop(); + } + + // First reservoir 2 must be completely full + if ( FALSE == isR2Full ) + { + isR2Full = isRsrvrFull( R2, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS, state ); + } + // Once R2 is full, R1 must be partially full + else + { + isR1Full = isRsrvrFull( R1, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_500ML_FILL_UP_TIMEOUT_MS, state ); + + // Once R1 is partially full, transition to the next state + if ( isR1Full ) + { + // Done with filing turn off the RO pump + signalROPumpHardStop(); + + // Set the valves to drain R2 and no fill + setValveState( VPI, VALVE_STATE_CLOSED ); + setValveState( VPD, VALVE_STATE_OPEN_C_TO_NO ); + setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); + setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); + setValveState( VRD, VALVE_STATE_R2_C_TO_NO ); + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + // Start the timer for drain timeout + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN_R2; + } + } + + return state; +} + /*********************************************************************//** * @brief + * The handleHeatDisinfectFlushDrainR2State function handles the heat + * disinfect flush drain reservoir 2 state. + * @details Inputs: stateTimer + * @details Outputs: stateTimer + * @return next state of the heat disinfect state machine + *************************************************************************/ +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFlushDrainR2State( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN_R2; + + // If reservoir 2 is empty, set the drain valve to drain R1 + if ( isRsrvrEmpty( R2, RSRVRS_DRAIN_TIMEOUT_MS, state ) ) + { + setValveState( VRD, VALVE_STATE_R1_C_TO_NC ); + // Start the timer for drain timeout + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN_R1; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleHeatDisinfectFlushDrainR1State function handles the heat + * disinfect flush drain reservoir 1 state. + * @details Inputs: stateTimer + * @details Outputs: stateTimer + * @return next state of the heat disinfect state machine + *************************************************************************/ +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFlushDrainR1State( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN_R1; + + // If reservoir 1 is empty, set the state to fill water state + if ( isRsrvrEmpty( R1, RSRVRS_DRAIN_TIMEOUT_MS, state ) ) + { + setValveState( VPI, VALVE_STATE_OPEN ); + setValveState( VPD, VALVE_STATE_OPEN_C_TO_NO ); + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); + setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); + + // Turn on the RO pump + setROPumpTargetFlowRate( RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM, MAX_RO_PUMP_FLUSH_FILL_PRESSURE_PSI ); + + // Start heating the water while we are filling up the rsrvrs + setPrimaryHeaterTargetTemperature( HEAT_DISINFECT_TARGET_TEMPERATURE_C ); + startPrimaryHeater(); + + // Start the timer for drain timeout + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_FILL_WITH_WATER; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleHeatDisinfectFillWithWaterState function handles the heat + * disinfect fill with water state. + * @details Inputs: stateTimer, isR1Full, isR2Full + * @details Outputs: stateTimer, isR1Full, isR2Full + * @return next state of the heat disinfect state machine + *************************************************************************/ +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFillWithWaterState( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_FILL_WITH_WATER; + + // First reservoir 1 must be full + if ( FALSE == isR1Full ) + { + isR1Full = isRsrvrFull( R1, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS, state ); + } + // Once reservoir 1 is full, check the status of reservoir 2 since the water overflows to reservoir 2 + else + { + isR2Full = isRsrvrFull( R2, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_500ML_FILL_UP_TIMEOUT_MS, state ); + + // Once reservoir 2 is full, set the actuators for recirculation + if ( isR2Full ) + { + // Set the valves to drain R2 and no fill + setValveState( VPI, VALVE_STATE_CLOSED ); + setValveState( VBF, VALVE_STATE_OPEN ); + setValveState( VPD, VALVE_STATE_OPEN_C_TO_NO ); + setValveState( VRD, VALVE_STATE_R2_C_TO_NO ); + setValveState( VDR, VALVE_STATE_RECIRC_C_TO_NC ); + setValveState( VRC, VALVE_STATE_RECIRC_C_TO_NC ); + + // Set the RO flow to maximum pressure of 25psi since it is the maximum pressure on the RO filter + // at inlet temperature > 45 C + setROPumpTargetFlowRate( HEAT_DISINFECT_TARGET_RO_FLOW_LPM, HEAT_DISINFECT_MAX_RO_PRESSURE_PSI ); + + //TODO add the new control api for the drain pump + + // Start the trimmer heater since we are recirculating water + setTrimmerHeaterTargetTemperature( HEAT_DISINFECT_TARGET_TEMPERATURE_C ); + startTrimmerHeater(); + + // Get the current volumes of R1 & R2. These values will be used to make sure the reservoirs' + // volume does not change more than a certain amount during the actual heat disinfect cycle + R1HeatDisinfectVol = getLoadCellFilteredWeight( LOAD_CELL_A1 ); + R2HeatDisinfectVol = getLoadCellFilteredWeight( LOAD_CELL_A2 ); + + stateTimer = getMSTimerCount(); + rsrvrsVolMonitorTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_DISINFECT_R1_TO_R2; + } + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleHeatDisinfectDisinfectR1ToR2State function handles the heat + * disinfect R1 to R2 state. + * @details Inputs: stateTimer + * @details Outputs: stateTimer + * @return next state of the heat disinfect state machine + *************************************************************************/ +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectDisinfectR1ToR2State( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_DISINFECT_R1_TO_R2; + + if ( isStateHeatDisinfectComplete( state ) ) + { + //TODO turn off CP1 and CP2 + + // Set the valves to transfer hot water from R1 to R2 and fill up R2. + setValveState( VRO, VALVE_STATE_R2_C_TO_NC ); + setValveState( VRD, VALVE_STATE_R1_C_TO_NC ); + setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); + setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); + + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_FILL_R2_WITH_HOT_WATER; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleHeatDisinfectFillR2WithHotWater function handles fill R2 + * with water state. + * @details Inputs: R1HeatDisinfectVol, R2HeatDisinfectVol + * @details Outputs: R1HeatDisinfectVol, R2HeatDisinfectVol + * @return next state of the heat disinfect state machine + *************************************************************************/ +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFillR2WithHotWater( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_FILL_R2_WITH_HOT_WATER; + + // In this state, R2 is fully filled up and R1 is partially filled up with hot water + // So waiting for R1 to get to the level of defined partially full + BOOL isR1PartiallyFull = fabs( getLoadCellFilteredWeight( LOAD_CELL_A1 ) - RSRVRS_PARTIAL_FILL_VOL_ML ) < RSRVRS_MAX_TARGET_VOL_CHANGE_ML; + + if ( isRsrvrFull( R2, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS, state ) && isR1PartiallyFull ) + { + // Get the current volumes to be monitored during R2 to R1 heat disinfect state + R1HeatDisinfectVol = getLoadCellFilteredWeight( LOAD_CELL_A1 ); + R2HeatDisinfectVol = getLoadCellFilteredWeight( LOAD_CELL_A2 ); + + //TODO turn on CP1 and CP2 + + state = DG_HEAT_DISINFECT_STATE_DISINFECT_R2_TO_R1; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleHeatDisinfectDisinfectR2ToR1State function handles the heat + * disinfect R2 to R1 state. + * @details Inputs: hasPostHeatDisinfectWaitStarted, stateTimer + * @details Outputs: hasPostHeatDisinfectWaitStarted, stateTimer + * @return next state of the heat disinfect state machine + *************************************************************************/ +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectDisinfectR2ToR1State( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_FILL_R2_WITH_HOT_WATER; + + // Keep monitoring for the end of heat disinfect and while the port delay has not started + if ( isStateHeatDisinfectComplete( state ) && FALSE == hasPostHeatDisinfectWaitStarted ) + { + // Turn off the pumps and heaters + //TODO turn off CP1 and CP2 + stopPrimaryHeater(); + stopTrimmerHeater(); + + hasPostHeatDisinfectWaitStarted = TRUE; + stateTimer = getMSTimerCount(); + } + else if ( hasPostHeatDisinfectWaitStarted && didTimeout( stateTimer, POST_HEAT_DISINFECT_WAIT_TIME_MS ) ) + { + // Stop the drain pump to exit the closed loop + signalDrainPumpHardStop(); + + // Deenergize all the valves that are not in the path anymore and set the other + // valves to drain the hot water + setValveState( VPI, VALVE_STATE_OPEN ); + setValveState( VBF, VALVE_STATE_CLOSED ); + setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); + setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); + setValveState( VRC, VALVE_STATE_DRAIN_C_TO_NO ); + setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); + setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); + setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); + + // Turn on the drain pump to drain the reservoirs in open loop mode + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + + stateTimer = getMSTimerCount(); + hasPostHeatDisinfectWaitStarted = FALSE; + state = DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R1; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleHeatDisinfectMixDrainR1State function handles the heat + * disinfect mix drain R1 state. + * @details Inputs: stateTimer + * @details Outputs: stateTimer + * @return next state of the heat disinfect state machine + *************************************************************************/ +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectMixDrainR1State( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R1; + + if ( isRsrvrEmpty( R1, RSRVRS_DRAIN_TIMEOUT_MS, state ) ) + { + // Set the drain valve to reservoir 2 + setValveState( VRD, VALVE_STATE_R2_C_TO_NO ); + + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R2; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleHeatDisinfectMixDrainR2State function handles the heat + * disinfect mix drain R1 state. + * @details Inputs: stateTimer + * @details Outputs: stateTimer + * @return next state of the heat disinfect state machine + *************************************************************************/ +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectMixDrainR2State( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R2; + + if ( isRsrvrEmpty( R2, RSRVRS_DRAIN_TIMEOUT_MS, state ) ) + { + // Set the drain valve to reservoir 2 + setValveState( VRD, VALVE_STATE_R2_C_TO_NO ); + + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_RINSE_R1_TO_R2; + } + + return state; +} + +/*********************************************************************//** + * @brief * The resetActuators function sets all the actuators to reset and * deenergized state. * @details Inputs: none @@ -496,4 +925,160 @@ SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_HEAT_DISINFECT_CYCLE_FAILED, failedState ) } +/*********************************************************************//** + * @brief + * The isRsrvrFull function checks whether the target reservoir is full or + * not. If the fill times out, it sets the state machine to complete and + * exits the heat disinfect mode. + * @details Inputs: none + * @details Outputs: none + * @param r is either R1 or R2 + * @param targetVol is the target fill volume + * @param timeout is the fill up timeout + * @param state is the state that called this function + * @return none + *************************************************************************/ +static BOOL isRsrvrFull( RSRVRS_T r, F32 targetVol, U32 timeout, DG_HEAT_DISINFECT_STATE_T state ) +{ + BOOL rsrvrStatus = FALSE; + F32 volume = 0.0; + + if ( r == R1 ) + { + volume = getLoadCellFilteredWeight( LOAD_CELL_A1 ); + } + else if ( r == R2 ) + { + volume = getLoadCellFilteredWeight( LOAD_CELL_A2 ); + } + + // Check the volume of the reservoir against the target volume + if ( volume >= targetVol ) + { + rsrvrStatus = TRUE; + // Set the state timer in case it needs to be used for another timeout check + stateTimer = getMSTimerCount(); + } + else if ( didTimeout( stateTimer, timeout ) ) + { + // Fill timed out + setModeToFailed( state ); + } + + return rsrvrStatus; +} + +/*********************************************************************//** + * @brief + * The isRsrvrEmpty function checks whether the target reservoir is empty + * or not. If the fill times out, it sets the state machine to complete and + * exits the heat disinfect mode. + * @details Inputs: none + * @details Outputs: none + * @param r is R1 or R2 + * @param state is the state that called this function + * @return none + *************************************************************************/ +static BOOL isRsrvrEmpty( RSRVRS_T r, U32 timeout, DG_HEAT_DISINFECT_STATE_T state ) +{ + BOOL rsrvrStatus = FALSE; + F32 volume = 0.0; + + if ( r == R1 ) + { + volume = getLoadCellFilteredWeight( LOAD_CELL_A1 ); + } + else if ( r == R2 ) + { + volume = getLoadCellFilteredWeight( LOAD_CELL_A2 ); + } + + if ( volume < RSRVRS_EMPTY_VOL_ML ) + { + rsrvrStatus = TRUE; + // Set the state timer in case it needs to be used for another timeout check + stateTimer = getMSTimerCount(); + } + else if ( didTimeout( stateTimer, RSRVRS_DRAIN_TIMEOUT_MS ) ) + { + setModeToFailed( state ); + } + + return rsrvrStatus; +} + +/*********************************************************************//** + * @brief + * The isStateHeatDisinfectComplete function monitors and runs the current + * stage of heat disinfect cycle. + * @details Inputs: areRsrvrsLeaking, areRsrvrsLeaking + * @details Outputs: areRsrvrsLeaking, areRsrvrsLeaking + * @param state is the state that called this function + * @return TRUE if this stage of heat disinfect cycle is done + *************************************************************************/ +static BOOL isStateHeatDisinfectComplete( DG_HEAT_DISINFECT_STATE_T state ) +{ + BOOL heatDisinfectStatus = FALSE; + + F32 TPoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); + F32 TPmTemp = getTemperatureValue( TEMPSENSORS_INLET_DIALYSATE ); //TODO change this to actual TPm sensor later + + BOOL isR1OutOfRange = fabs( getLoadCellFilteredWeight( LOAD_CELL_A1 ) - R1HeatDisinfectVol ) > RSRVRS_MAX_TARGET_VOL_CHANGE_ML; + BOOL isR2OutOfRange = fabs( getLoadCellFilteredWeight( LOAD_CELL_A2 ) - R2HeatDisinfectVol ) > RSRVRS_MAX_TARGET_VOL_CHANGE_ML; + + // Check if either reservoir 1 or reservoir 2 are losing volume more than allowed volume + if ( isR1OutOfRange || isR2OutOfRange ) + { + // If the leak is the first time after a while, set the flag and start the timer + if ( FALSE == areRsrvrsLeaking ) + { + areRsrvrsLeaking = TRUE; + rsrvrsVolMonitorTimer = getMSTimerCount(); + } + // If the volume is out of range and it has timed out, exit + else if ( didTimeout( rsrvrsVolMonitorTimer, RSRVRS_TARGET_VOL_OUT_TIMEOUT_MS ) ) + { + areRsrvrsLeaking = FALSE; + setModeToFailed( state ); + } + } + // We are in range + else + { + areRsrvrsLeaking = FALSE; + } + + // If the coldest spot which is TPm is less than minimum heat disinfect temperature, + // reset the heat disinfect timers and check whether heating up has timed out + if ( TPmTemp < HEAT_DISINFECT_START_TEMPERATURE_C ) + { + hasHeatDisinfectStarted = FALSE; + + if ( didTimeout( stateTimer, HEAT_DISINFECT_START_TEMP_TIMOUT_MS ) ) + { + // Heating up to minimum temperature for heat disinfect failed + setModeToFailed( state ); + } + } + else + { + // The temperature of the coldest spot is in range start. Start the timer for heat disinfect cycle + heatDisinfectTimer = getMSTimerCount(); + hasHeatDisinfectStarted = TRUE; + } + + // If the flag is TRUE, check if this stage of heat disinfect is done + if ( hasHeatDisinfectStarted ) + { + if ( didTimeout( heatDisinfectTimer, HEAT_DISINFECT_START_TEMP_TIMOUT_MS ) ) + { + // Done with this stage of heat disnfect. Reset the variables + heatDisinfectStatus = TRUE; + hasHeatDisinfectStarted = FALSE; + } + } + + return heatDisinfectStatus; +} + /**@}*/