/************************************************************************** * * Copyright (c) 2019-2020 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 ModeHeatDisinfect.c * * @author (last) Quang Nguyen * @date (last) 01-Sep-2020 * * @author (original) Sean * @date (original) 20-Apr-2020 * ***************************************************************************/ #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 * @{ */ // ********** 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 ( 1 * MS_PER_SECOND ) ///< Flush Drain path wait time in milliseconds. TODo original time was 60 seconds #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.6 ///< RO pump target flow rate during flush/fill in L/min. TODO original flow was 0.8 #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. TODO original time was 30 seconds #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 1900.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. /// 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 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( DG_RESERVOIR_ID_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. * @details Inputs: none * @details Outputs: heatDisinfectState, stateTimer, isThisLastDrain, * stateTrialCounter, areTempSensorsInRange, isR1Full, isR2Full, * R1HeatDisinfectVol, R2HeatDisinfectVol, hasPostHeatDisinfectWaitStarted, * overallHeatDisinfectTimer TODO fill up as we go * @return none *************************************************************************/ void initHeatDisinfectMode( void ) { 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. * @details Inputs: none * @details Outputs: none * @return none *************************************************************************/ void transitionToHeatDisinfectMode( void ) { initHeatDisinfectMode(); } /*********************************************************************//** * @brief * The execHeatDisinfectMode function executes the heat disinfect mode * state machine. * @details Inputs: heatDisinfectState * @details Outputs: heatDisinfectState * @return current state *************************************************************************/ U32 execHeatDisinfectMode( void ) { checkInletPressureFault(); // TODO what is this? 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: 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; } publishHeatDisinfectData(); return heatDisinfectState; } /*********************************************************************//** * @brief * 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 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 ); // Request a tare for reservoir 1 tareReservoir(); // 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. // Request a tare for reservoir 2 tareReservoir(); // 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; signalDrainPumpHardStop(); 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 ) > 15 /*MIN_INLET_TEMPERATURE_C && TODO bring the code back getConductivityValue( CONDUCTIVITYSENSORS_CPI_SENSOR ) > MIN_INLET_CONDUCTIVITY_US_PER_CM*/ ) { setValveState( VPD, VALVE_STATE_OPEN_C_TO_NO ); 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 ) / 3.0; //TODO add TPm once we have the sensor in change the average count to 4.0 BOOL isTPmOut = FALSE; //fabs( TPmTemp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C; TODO uncomment this when 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_R1_AND_R2; // If R1 is not full, keep monitoring for R1 level and timeout if ( isR1Full != TRUE ) { isR1Full = isRsrvrFull( DG_RESERVOIR_1, 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( DG_RESERVOIR_2, 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(); // Set both reservoirs to full status to FALSE isR2Full = FALSE; isR1Full = FALSE; 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_R2_AND_DRAIN_R1; // If reservoir 1 is empty, turn off the drain pump if ( 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 ( isR2Full != TRUE ) { isR2Full = isRsrvrFull( DG_RESERVOIR_2, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS, state ); } // Once R2 is full, R1 must be partially full if( isR2Full ) { isR1Full = isRsrvrFull( DG_RESERVOIR_1, 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 ) ) { // Done with draining the reservoirs signalDrainPumpHardStop(); // Prepare for filling the reservoirs and heating the water setValveState( VPI, VALVE_STATE_OPEN ); setValveState( VPD, VALVE_STATE_OPEN_C_TO_NO ); setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); setValveState( VRF, VALVE_STATE_R1_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 ( isRsrvrFull( DG_RESERVOIR_1, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS, state ) ) { isR1Full = TRUE; } // Once reservoir 1 is full, check the status of reservoir 2 since the water overflows to reservoir 2 if ( isR1Full ) { isR2Full = isRsrvrFull( DG_RESERVOIR_2, 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( DG_RESERVOIR_2, 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( DG_RESERVOIR_1, 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( DG_RESERVOIR_2, 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( DG_RESERVOIR_2, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS, state ); } else { isR1Full = isRsrvrFull( DG_RESERVOIR_1, 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( DG_RESERVOIR_ID_T r, F32 targetVol, U32 timeout, DG_HEAT_DISINFECT_STATE_T state ) { BOOL rsrvrStatus = FALSE; F32 volume = 0.0; if ( r == DG_RESERVOIR_1 ) { volume = getLoadCellFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); } else if ( r == DG_RESERVOIR_2 ) { 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 ); //TODO un-comment } 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; } } /**@}*/