Index: firmware/App/Modes/ModeHeatDisinfect.c =================================================================== diff -u -r4d7d40a27130dc813d653f044cbb856b1b7d8481 -r9ce06772b2f651c57144327e6cbf886e2bc22dee --- firmware/App/Modes/ModeHeatDisinfect.c (.../ModeHeatDisinfect.c) (revision 4d7d40a27130dc813d653f044cbb856b1b7d8481) +++ firmware/App/Modes/ModeHeatDisinfect.c (.../ModeHeatDisinfect.c) (revision 9ce06772b2f651c57144327e6cbf886e2bc22dee) @@ -15,9 +15,21 @@ * ***************************************************************************/ +#include "ConductivitySensors.h" +#include "DrainPump.h" +#include "Heaters.h" +#include "LoadCell.h" #include "ModeHeatDisinfect.h" #include "OperationModes.h" #include "Pressures.h" +#include "Reservoirs.h" +#include "ROPump.h" +#include "SystemCommMessages.h" +#include "TaskGeneral.h" +#include "TemperatureSensors.h" +#include "Timers.h" +#include "UVReactors.h" +#include "Valves.h" /** * @addtogroup DGHeatDisinfectMode @@ -26,29 +38,156 @@ // ********** private definitions ********** +// General defines +#define MAX_ALLOWED_STATE_TRIALS 1 ///< Max allowed trials on a state. This is general among all the states. +#define HEAT_DISINFECT_DATA_PUB_INTERVAL ( MS_PER_SECOND / \ + TASK_GENERAL_INTERVAL ) ///< Mode Heat Disinfect data publish interval in counts. + +// 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. + +// Drain R1 & R2 states defines +#define DRAIN_PUMP_TARGET_RPM 1800 ///< 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 +#define DRAIN_WEIGH_UNCHANGE_TIMEOUT ( 2 * MS_PER_SECOND ) ///< Time period of unchanged weight during draining before timeout. + +// 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_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_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/rinse 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. + +// Rinse R1 to R2 +#define ROF_MIN_LOW_PRESSURE_TEMPERATURE_C 45.0 ///< RO filter minimum temperature that the pressure must be no more than 30psi in C. + +/// Name of reservoirs +typedef enum rsrvrs +{ + R1 = 0, ///< Reservoir 1 + R2, ///< Reservoir 2 + NUM_OF_RSRVRS ///< Number of reservoirs +} RSRVRS_T; //TODO remove + +/// TODO remove? +typedef enum rsvrs_status +{ + RSRVR_EMPTY = 0, + RSRVR_PARTIALLY_FULL, + RSVR_FULL, + NUM_OF_RSRVRS_STATUS +} RSRVRS_STATUS_T; + // ********** private data ********** -static DG_HEAT_DISINFECT_STATE_T heatState = DG_HEAT_DISINFECT_STATE_START; ///< Currently active heat disinfect state. +static DG_HEAT_DISINFECT_STATE_T heatDisinfectState = DG_HEAT_DISINFECT_STATE_START; ///< Currently active heat disinfect state. +static U32 overallHeatDisinfectTimer = 0; ///< Heat disinfect cycle total timer. +static U32 stateTimer = 0; ///< Heat disinfect state timer to be used in different states. +static U32 stateTrialCounter = 0; ///< Heat disinfect state trial counter to be used for retries in different states. +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. +static U32 dataPublishCounter = 0; ///< Heat Disinfect data publish counter. // ********** private function prototypes ********** +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectStartState( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectDrainR1State( void ); +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 DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectRinseR1ToR2( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectRinseR2ToR1AndDrainR1State( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectRinseCirculationState( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectCancelModeBasicPath( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectCancelModeColdWaterPath( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectCancelModeHotWaterPath( void ); + +static void resetActuators( void ); +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( DG_RESERVOIR_ID_T r, U32 drainSteadyStateTimeout, U32 timeout, DG_HEAT_DISINFECT_STATE_T failedState ); +static BOOL isStateHeatDisinfectComplete( DG_HEAT_DISINFECT_STATE_T state ); +static void publishHeatDisinfectData( void ); + /*********************************************************************//** * @brief - * The initHeatDisinfectMode function initializes the heat disinfect mode module. + * The initHeatDisinfectMode function initializes the heat disinfect mode + * module. * @details Inputs: none - * @details Outputs: Initialized heat disinfect mode module + * @details Outputs: heatDisinfectState, stateTimer, isThisLastDrain, + * stateTrialCounter, areTempSensorsInRange, isR1Full, isR2Full, + * R1HeatDisinfectVol, R2HeatDisinfectVol, hasPostHeatDisinfectWaitStarted, + * overallHeatDisinfectTimer TODO fill up as we go * @return none *************************************************************************/ void initHeatDisinfectMode( void ) { - heatState = DG_HEAT_DISINFECT_STATE_START; + heatDisinfectState = DG_HEAT_DISINFECT_STATE_START; + stateTimer = 0; + isThisLastDrain = FALSE; + stateTrialCounter = 0; + areTempSensorsInRange = FALSE; + isR1Full = FALSE; + isR2Full = FALSE; + R1HeatDisinfectVol = 0; + R2HeatDisinfectVol = 0; + hasPostHeatDisinfectWaitStarted = FALSE; + overallHeatDisinfectTimer = getMSTimerCount(); } /*********************************************************************//** * @brief - * The transitionToHeatDisinfectMode function prepares for transition to heat disinfect mode. + * The transitionToHeatDisinfectMode function prepares for transition to + * heat disinfect mode. * @details Inputs: none - * @details Outputs: Prepare for transition to heat disinfect mode + * @details Outputs: none * @return none *************************************************************************/ void transitionToHeatDisinfectMode( void ) @@ -58,41 +197,1089 @@ /*********************************************************************//** * @brief - * The execHeatDisinfectMode function executes the heat disinfect mode state machine. - * @details Inputs: none - * @details Outputs: Heat disinfect mode state machine executed + * The execHeatDisinfectMode function executes the heat disinfect mode + * state machine. + * @details Inputs: heatDisinfectState + * @details Outputs: heatDisinfectState * @return current state *************************************************************************/ U32 execHeatDisinfectMode( void ) { - checkInletPressureFault(); + checkInletPressureFault(); // TODO what is this? - // execute current heat disinfect state - switch ( heatState ) + switch ( heatDisinfectState ) { case DG_HEAT_DISINFECT_STATE_START: + heatDisinfectState = handleHeatDisinfectStartState(); break; + case DG_HEAT_DISINFECT_STATE_DRAIN_R1: + heatDisinfectState = handleHeatDisinfectDrainR1State(); + break; + + case DG_HEAT_DISINFECT_STATE_DRAIN_R2: + heatDisinfectState = handleHeatDisinfectDrainR2State(); + break; + + case DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN: + heatDisinfectState = handleHeatDisinfectFlushDrainState(); + break; + + case DG_HEAT_DISINFECT_STATE_FLUSH_CIRCULATION: + heatDisinfectState = handleHeatDisinfectFlushCirculationState(); + break; + + case DG_HEAT_DISINFECT_STATE_FLUSH_R1_AND_R2: + heatDisinfectState = handleHeatDisinfectFlushR1AndR2State(); + break; + + 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: + heatDisinfectState = handleHeatDisinfectMixDrainR2State(); + break; + + case DG_HEAT_DISINFECT_STATE_RINSE_R1_TO_R2: + heatDisinfectState = handleHeatDisinfectRinseR1ToR2(); + break; + + case DG_HEAT_DISINFECT_STATE_RINSE_R2_TO_R1_AND_DRAIN_R1: + heatDisinfectState = handleHeatDisinfectRinseR2ToR1AndDrainR1State(); + break; + + case DG_HEAT_DISINFECT_STATE_RINSE_CIRCULATION: + heatDisinfectState = handleHeatDisinfectRinseCirculationState(); + break; + + case DG_HEAT_DISINFECT_STATE_CANCEL_MODE_BASIC_PATH: + break; + + case DG_HEAT_DISINFECT_STATE_CANCEL_MODE_COLD_WATER_PATH: + break; + + case DG_HEAT_DISINFECT_STATE_CANCEL_MODE_HOT_WATER_PATH: + break; + + case DG_HEAT_DISINFECT_STATE_COMPLETE: + // Done with heat disinfect cycle, complete or not + break; + default: - // TODO - s/w fault - heatState = DG_HEAT_DISINFECT_STATE_START; + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_HEAT_DISINFECT_INVALID_EXEC_STATE, heatDisinfectState ) + heatDisinfectState = DG_HEAT_DISINFECT_STATE_START; break; } - return heatState; + publishHeatDisinfectData(); + + return heatDisinfectState; } /*********************************************************************//** * @brief - * The getCurrentHeatDisinfectState function returns the current state of the - * heat disinfect mode. - * @details Inputs: heatState + * The getCurrentHeatDisinfectState function returns the current state of + * the heat disinfect mode. + * @details Inputs: heatDisinfectState * @details Outputs: none * @return the current state of heat disinfect mode. *************************************************************************/ DG_HEAT_DISINFECT_STATE_T getCurrentHeatDisinfectState( void ) { - return heatState; + return heatDisinfectState; } +/*********************************************************************//** + * @brief + * The stopDGHeatDisinfect function stops heat disinfect mode. + * @details Inputs: heatDisinfectionState + * @details Outputs: heatDisinfectionState + * @return none + *************************************************************************/ +void stopDGHeatDisinfect( void ) +{ + heatDisinfectState = DG_HEAT_DISINFECT_STATE_COMPLETE; + resetActuators(); + + requestNewOperationMode( DG_MODE_STAN ); +} + +// ********** private function prototypes ********** + +/*********************************************************************//** + * @brief + * The handleHeatDisinfectStartState function handles the heat disinfect + * start state. + * @details Inputs: none + * @details Outputs: none + * @return next state of the heat disinfect state machine + *************************************************************************/ +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectStartState( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_DRAIN_R1; + + // Set all the actuators to reset and deenergized state + resetActuators(); + + F32 ppiPressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); + F32 TDiTemp = getTemperatureValue( TEMPSENSORS_INLET_DIALYSATE ); + F32 TRoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_REDUNDANT ); + + // If the inlet pressure is less than the threshold or TDi and TRo difference is greater than 1 C, the cycle + // should be canceled + if ( ppiPressure < MIN_INLET_PRESSURE_PSI && fabs( TDiTemp - TRoTemp ) > MAX_START_STATE_TEMP_SENSORS_DIFF_C ) + { + setModeToFailed( state ); + } + else + { + // Close VPi to prevent wasting water + setValveState( VPI, VALVE_STATE_CLOSED ); + + // Set the actuators to drain R1 + setValveState( VRD, VALVE_STATE_R1_C_TO_NC ); + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + stateTimer = getMSTimerCount(); + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleHeatDisinfectDrainR1State function handles the heat disinfect + * drain R1 state. + * @details Inputs: stateTimer, isR1Full, isThisLastDrain + * @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; + + if ( isRsrvrEmpty( DG_RESERVOIR_1, DRAIN_WEIGH_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS, state ) ) + { + isR1Full = FALSE; + + if ( isThisLastDrain ) + { + setValveState( VPI, VALVE_STATE_OPEN ); + setValveState( VPD, VALVE_STATE_OPEN_C_TO_NO ); + + //TODO turn on the concentrate pumps + + // TODO discuss the cooling process for proper flow rate and pressure set + setROPumpTargetFlowRate( RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM, MAX_RO_PUMP_FLUSH_FILL_PRESSURE_PSI ); + + // Done with final rinsing + isThisLastDrain = FALSE; + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_RINSE_CIRCULATION; + } + else + { + // Set the actuators to drain R2. + // 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; + } + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleHeatDisinfectDrainR2State function handles the heat disinfect + * drain R2 state. + * @details Inputs: stateTimer, isR2Full, isThisLastDrain + * @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; + + if ( isRsrvrEmpty( DG_RESERVOIR_2, DRAIN_WEIGH_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS, state ) ) + { + isR2Full = FALSE; + + if ( isThisLastDrain ) + { + setValveState( VRD, VALVE_STATE_R1_C_TO_NC ); + state = DG_HEAT_DISINFECT_STATE_DRAIN_R1; + } + else + { + stateTrialCounter = 0; + setValveState( VPI, VALVE_STATE_OPEN ); + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN; + } + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleHeatDisinfectFlushDrainState function handles the heat disinfect + * flush drain state. + * @details Inputs: stateTimer, stateTrialCounter + * @details Outputs: stateTimer, stateTrialCounter + * @return next state of the heat disinfect state machine + *************************************************************************/ +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFlushDrainState( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN; + + // Check if flush time has elapsed + if ( didTimeout( stateTimer, FLUSH_DRAIN_WAIT_TIME_MS ) ) + { + // 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_US_PER_CM*/ ) + { + setValveState( VPD, VALVE_STATE_DRAIN_C_TO_NC ); + 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; + } + // If the failure is still in range, reset the timer and start over + else if ( ++stateTrialCounter < MAX_ALLOWED_STATE_TRIALS ) + { + stateTimer = getMSTimerCount(); + } + // Couldn't get a good water sample after a couple of trials and the disinfection cycle failed + else + { + setModeToFailed( state ); + } + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleHeatDisinfectFlushCirculationState function handles the heat + * disinfect flush circulation state. + * @details Inputs: stateTimer, stateTrialCounter + * @details Outputs: stateTimer, stateTrialCounter + * @return next state of the heat disinfect state machine + *************************************************************************/ +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFlushCirculationState( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_FLUSH_CIRCULATION; + + // 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 ) + { + // 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 ) / 4.0; + + 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; + + // Check if any of the temperature sensors are out of tolerance + if( isTPmOut || isTPoOut || isTD1Out || isTD2Out ) + { + // Check if we have exceeded the number of trials. If not, try another time + if ( ++stateTrialCounter < MAX_ALLOWED_STATE_TRIALS ) + { + stateTimer = getMSTimerCount(); + } + // State failed. Cancel heat disinfect mode + else + { + setModeToFailed( state ); + } + } + else + { + areTempSensorsInRange = TRUE; + stateTimer = getMSTimerCount(); + // TODO Turn on the concentrate pumps and wait for 30 seconds + } + } + + // Only start the concentrate pumps if the temperature sensors are in range + if ( areTempSensorsInRange ) + { + // TODO: enable the timeout once the concentrate pumps are available. + //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; + } + } + + 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( DG_RESERVOIR_1, DRAIN_WEIGH_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_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( DG_RESERVOIR_2, DRAIN_WEIGH_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_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( DG_RESERVOIR_1, DRAIN_WEIGH_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_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_RESERVOIR_1_PRIMARY ); + R2HeatDisinfectVol = getLoadCellFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); + + 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_RESERVOIR_1_PRIMARY ) - 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_RESERVOIR_1_PRIMARY ); + R2HeatDisinfectVol = getLoadCellFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); + + //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( DG_RESERVOIR_1, DRAIN_WEIGH_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS, state ) ) + { + isR1Full = FALSE; + + // 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 R2 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( DG_RESERVOIR_2, DRAIN_WEIGH_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS, state ) ) + { + isR2Full = FALSE; + + // Done with draining the reservoirs + signalDrainPumpHardStop(); + + // Set the valves to fill up R1 and overflow to R2 + // The RO pump is still running at low pressure until the coldest + // spot temperature is less than 45 C. + setValveState( VPD, VALVE_STATE_OPEN_C_TO_NO ); + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); + setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); + setValveState( VRC, VALVE_STATE_DRAIN_C_TO_NO ); + setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); + setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); + setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); + + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_RINSE_R1_TO_R2; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleHeatDisinfectRinseR1ToR2 function handles the heat + * disinfect rinse R1 to R2 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 handleHeatDisinfectRinseR1ToR2( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_RINSE_R1_TO_R2; + + if ( FALSE == isR1Full ) + { + // Since reservoir 1 is being filled up at a lower flow rate, the timeout is twice as long as a normal fill up + isR1Full = isRsrvrFull( R1, RSRVRS_FULL_VOL_ML, 2 * RSRVRS_FILL_UP_TIMEOUT_MS, state ); + } + else + { + // Since reservoir 2 is being filled up at a lower flow rate, the timeout is longer than a normal fill up + isR2Full = isRsrvrFull( R2, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS, state ); + + if ( isR2Full ) + { + // Set the valves to rinses R2 to R1 and drain R1 + setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); + setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); + setValveState( VRD, VALVE_STATE_R1_C_TO_NC ); + setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); + + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_RINSE_R2_TO_R1_AND_DRAIN_R1; + } + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleHeatDisinfectRinseR2ToR1AndDrainR1State function handles the + * heat disinfect rinse R2 to R1 and drain R1 state. + * @details Inputs: stateTimer, isR1Full, isR2Full, isThisLastDrain + * @details Outputs: stateTimer, isR1Full, isR2Full, isThisLastDrain + * @return next state of the heat disinfect state machine + *************************************************************************/ +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectRinseR2ToR1AndDrainR1State( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_RINSE_R2_TO_R1_AND_DRAIN_R1; + + if ( isR1Full ) + { + // Since reservoir 1 is being filled up at a lower flow rate, the timeout is twice as long as a normal fill up + isR1Full = isRsrvrEmpty( DG_RESERVOIR_1, DRAIN_WEIGH_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS, state ); + + // Done with draining R1 + signalDrainPumpHardStop(); + } + else if ( FALSE == isR1Full ) + { + if ( FALSE == isR2Full ) + { + isR2Full = isRsrvrFull( R2, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS, state ); + } + else + { + isR1Full = isRsrvrFull( R1, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_500ML_FILL_UP_TIMEOUT_MS, state ); + + if ( isR1Full ) + { + // Done with filling, turn off the RO pump + signalROPumpHardStop(); + + // Deenergize all the valves and set the VDr to drain R2 + 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 ); + setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); + setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); + + // Turn on the drain pump to drain R2 + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + + // This is the last drain of heat disinfect cycle + isThisLastDrain = TRUE; + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_DRAIN_R2; + } + } + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleHeatDisinfectRinseCirculationState function handles the + * heat disinfect rinse RO circulation and conectrate pumps state. + * @details Inputs: none + * @details Outputs: none + * @return next state of the heat disinfect state machine + *************************************************************************/ +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectRinseCirculationState( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_RINSE_CIRCULATION; + + if ( didTimeout( stateTimer, FLUSH_CICRCULATION_WAIT_TIME_MS ) ) + { + // Done with heat disinfect cycle + resetActuators(); + + state = DG_HEAT_DISINFECT_STATE_COMPLETE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The resetActuators function sets all the actuators to reset and + * deenergized state. + * @details Inputs: none + * @details Outputs: none + * @return none + *************************************************************************/ +static void resetActuators( void ) +{ + // UV reactors will not be used in the heat disinfection since their operating temperature + // range is below 85C and they might be damaged by the high temperature. + turnOffUVReactor( INLET_UV_REACTOR ); + turnOffUVReactor( OUTLET_UV_REACTOR ); + + // Deenergize all the valves + setValveState( VPI, VALVE_STATE_OPEN ); + setValveState( VBF, VALVE_STATE_CLOSED ); + setValveState( VSP, VALVE_STATE_CLOSED ); + setValveState( VPD, VALVE_STATE_OPEN_C_TO_NO ); + 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( VRD, VALVE_STATE_R2_C_TO_NO ); + setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); + setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); + + //TODO add the composition pumps + signalROPumpHardStop(); + signalDrainPumpHardStop(); + stopPrimaryHeater(); + stopTrimmerHeater(); +} + +/*********************************************************************//** + * @brief + * The setModeToFailed function sets the heat disinfect mode to failed + * by changing the state to complete. + * @details Inputs: heatDisinfectState + * @details Outputs: heatDisinfectState + * @param failedStated which is the state heat disinfect mode failed + * @return none + *************************************************************************/ +static void setModeToFailed( DG_HEAT_DISINFECT_STATE_T failedState ) +{ + heatDisinfectState = DG_HEAT_DISINFECT_STATE_COMPLETE; + + 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_RESERVOIR_1_PRIMARY ); + } + else if ( r == R2 ) + { + volume = getLoadCellFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); + } + + // 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( DG_RESERVOIR_ID_T r, U32 drainSteadyStateTimeout, U32 timeout, DG_HEAT_DISINFECT_STATE_T failedState ) +{ + BOOL isDrainComplete = FALSE; + + isDrainComplete = hasTargetDrainVolumeBeenReached( r, drainSteadyStateTimeout); + + if ( TRUE == isDrainComplete ) + { + // 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( failedState ); + } + + return isDrainComplete; +} + +/*********************************************************************//** + * @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_RESERVOIR_1_PRIMARY ) - R1HeatDisinfectVol ) > RSRVRS_MAX_TARGET_VOL_CHANGE_ML; + BOOL isR2OutOfRange = fabs( getLoadCellFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ) - 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; +} + +/*********************************************************************//** + * @brief + * The publishHeatDisinfectData function publishes heat disinfect data at + * the set interval. + * @details Inputs: dataPublishCounter + * @details Outputs: dataPublishCounter + * @return: none + *************************************************************************/ +static void publishHeatDisinfectData( void ) +{ + if ( ++dataPublishCounter > HEAT_DISINFECT_DATA_PUB_INTERVAL ) + { + MODE_HEAT_DISINFECT_DATA_T data; + + data.heatDisinfectState = (U32)heatDisinfectState; + // Get the time elapsed so far and convert them to minutes TODO remove the timings? + data.overallElapsedTime = calcTimeSince( overallHeatDisinfectTimer ) / ( 60 * MS_PER_SECOND ); + data.heatDisinfectElapsedTime = calcTimeSince( stateTimer ) / ( 60 * MS_PER_SECOND ); + + broadcastHeatDisinfectData( &data ); + + dataPublishCounter = 0; + } +} + /**@}*/