/************************************************************************** * * 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 DRAIN_WEIGHT_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 15.0 ///< Minimum water inlet temperature in C. TODO original temperature was 25 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. 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 55.0 ///< Heat disinfect target water temperature in C. TODO original temperature was 85.0 #define HEAT_DISINFECT_START_TEMPERATURE_C 50.0 ///< Heat disinfect minimum acceptable temperature in C. TODO original temperature was 81.0 // R1 to R2 & R2 to R1 heat disinfect circulation #define HEAT_DISINFECT_TARGET_RO_FLOW_LPM 0.8 ///< Heat disinfect target RO flow rate in L/min. TODO original value was 0.8 #define HEAT_DISINFECT_MAX_RO_PRESSURE_PSI 30 ///< Heat disinfect maximum RO pressure in psi. #define HEAT_DISINFECT_TARGET_DRAIN_PRES_PSI 1.0 ///< Heat disinfect target drain outlet pressure in psi. #define HEAT_DISINFECT_TIME_MS ( 0.5 * 60 * MS_PER_SECOND ) ///< Heat disinfect time for each section in milliseconds. TODO original time was 10 minutes #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 the actual value is 45.0 // Cancellation paths #define MIX_DRAIN_TEMPERATURE_THRESHOLD_C 60.0 ///< Temperature threshold for performing mix drain or normal drain. /// Cancellation paths typedef enum Cancellation_modes { CANCELLATION_MODE_NONE = 0, ///< Cancellation mode none. CANCELLATION_MODE_BASIC, ///< Cancellation mode basic. CANCELLATION_MODE_HOT, ///< Cancellation mode hot. CANCELLATION_MODE_COLD, ///< Cancellation mode cold. NUM_OF_CANCELLATION_MODES ///< Number of cancellation modes. } CANCELLATION_MODES_T; /// Reservoirs status typedef enum Reservoirs_status { RESERVOIR_EMPTY = 0, RESERVOIR_FULL, RESERVOIR_TIMEOUT, NUM_OF_RESERVOIR_STATES } TYPE_T ; // ********** private data ********** static DG_HEAT_DISINFECT_STATE_T heatDisinfectState = DG_HEAT_DISINFECT_STATE_START; ///< Current active heat disinfect state. static DG_HEAT_DISINFECT_STATE_T prevHeatDisinfectState = DG_HEAT_DISINFECT_STATE_START; ///< Previous active heat disinfect state before alarm. 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. static CANCELLATION_MODES_T cancellationMode = CANCELLATION_MODE_NONE; ///< Cancellation mode. // ********** 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 handleHeatDisinfectFillR2WithHotWaterState( 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 handleHeatDisinfectCoolDownROFilterState( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectRinseR1ToR2State( 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 handleHeatDisinfectCancelModeWaterPath( 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 cancellationState ); static BOOL isRsrvrEmpty( DG_RESERVOIR_ID_T r, U32 drainSteadyStateTimeout, U32 timeout, DG_HEAT_DISINFECT_STATE_T cancellationState ); 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; prevHeatDisinfectState = DG_HEAT_DISINFECT_STATE_START; stateTimer = 0; isThisLastDrain = FALSE; stateTrialCounter = 0; areTempSensorsInRange = FALSE; isR1Full = FALSE; isR2Full = FALSE; R1HeatDisinfectVol = 0; R2HeatDisinfectVol = 0; hasPostHeatDisinfectWaitStarted = FALSE; overallHeatDisinfectTimer = 0; cancellationMode = CANCELLATION_MODE_NONE; } /*********************************************************************//** * @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 = handleHeatDisinfectFillR2WithHotWaterState(); 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_COOL_DOWN_RO_FILTER: heatDisinfectState = handleHeatDisinfectCoolDownROFilterState(); break; case DG_HEAT_DISINFECT_STATE_RINSE_R1_TO_R2: heatDisinfectState = handleHeatDisinfectRinseR1ToR2State(); 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_BASIC_PATH: heatDisinfectState = handleHeatDisinfectCancelModeBasicPath(); break; case DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH: heatDisinfectState = handleHeatDisinfectCancelModeWaterPath(); break; case DG_HEAT_DISINFECT_STATE_COMPLETE: // Done with heat disinfect cycle, failed 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; // Reset all the actuators resetActuators(); // Transition to mode standby 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; // Start overall heat disinfect timer overallHeatDisinfectTimer = getMSTimerCount(); // 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 ) { prevHeatDisinfectState = state; state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; } 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_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS, DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH ) ) { isR1Full = FALSE; if ( isThisLastDrain ) { // Done with draining signalDrainPumpHardStop(); // Set the valves to flush the recirculation line setValveState( VPI, VALVE_STATE_OPEN ); 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 ); //TODO turn on the concentrate pumps // Set the RO pump to run at full pressure setROPumpTargetFlowRate( RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM, MAX_RO_PUMP_FLUSH_FILL_PRESSURE_PSI ); // Done with final draining isThisLastDrain = FALSE; 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 ); state = DG_HEAT_DISINFECT_STATE_DRAIN_R2; } // Start the timer stateTimer = getMSTimerCount(); } 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_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS, DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH ) ) { 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 ) > MIN_INLET_TEMPERATURE_C && 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 disinfect cycle failed else { prevHeatDisinfectState = state; state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; } } 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 ) { F32 ThdTemp = getTemperatureValue( TEMPSENSORS_INLET_DIALYSATE ); // TODO add TPm later. This is the new temp sensor of the coldest spot. It is temporarily TDi. F32 TPoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); F32 TD1Temp = getTemperatureValue( TEMPSENSORS_CONDUCTIVITY_SENSOR_1 ); F32 TD2Temp = getTemperatureValue( TEMPSENSORS_CONDUCTIVITY_SENSOR_2 ); F32 avgTemp = ( ThdTemp + TPoTemp + TD1Temp + TD2Temp ) / 4.0; BOOL isThdOut = fabs( ThdTemp - 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( isThdOut || 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 { prevHeatDisinfectState = state; state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; } } 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 ) { isR1Full = FALSE; isR2Full = FALSE; 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, DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH ); } // 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, DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH ); // 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_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS, DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH ) ) { isR1Full = FALSE; // 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, DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH ); } // 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, DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH ); // 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_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS, DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH ) ) { 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_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS, DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH ) ) { // 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, DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH ) ) { 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, DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH ); // 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 drain pump to control mode setDrainPumpTargetOutletPressure( HEAT_DISINFECT_TARGET_DRAIN_PRES_PSI ); // 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 ); // Start the trimmer heater since we are recirculating water //setTrimmerHeaterTargetTemperature( HEAT_DISINFECT_TARGET_TEMPERATURE_C ); //startTrimmerHeater(); TODO do we need this? // 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 = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); R2HeatDisinfectVol = getLoadCellSmallFilteredWeight( 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 handleHeatDisinfectFillR2WithHotWaterState 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 handleHeatDisinfectFillR2WithHotWaterState( 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( getLoadCellSmallFilteredWeight( 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, DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH ) && isR1PartiallyFull ) { // Get the current volumes to be monitored during R2 to R1 heat disinfect state R1HeatDisinfectVol = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); R2HeatDisinfectVol = getLoadCellSmallFilteredWeight( 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_DISINFECT_R2_TO_R1; // Keep monitoring for the end of heat disinfect and while the post delay has not started if ( isStateHeatDisinfectComplete( state ) && hasPostHeatDisinfectWaitStarted != TRUE ) { // 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 and the RO pump to exit the closed loop signalDrainPumpHardStop(); signalROPumpHardStop(); // De-energize 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 ); setValveState( VPD, VALVE_STATE_DRAIN_C_TO_NC ); // 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_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS, DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH ) ) { 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_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS, DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH ) ) { isR2Full = FALSE; // Done with draining the reservoirs signalDrainPumpHardStop(); stateTimer = getMSTimerCount(); state = DG_HEAT_DISINFECT_STATE_COOL_DOWN_RO_FILTER; } return state; } /*********************************************************************//** * @brief * The handleHeatDisinfectCoolDownROFilterState function handles the heat * disinfect cool down RO filter state. * @details Inputs: stateTimer * @details Outputs: stateTimer * @return next state of the heat disinfect state machine *************************************************************************/ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectCoolDownROFilterState( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_COOL_DOWN_RO_FILTER; F32 ThdTemp = getTemperatureValue( TEMPSENSORS_INLET_DIALYSATE ); //TODO change this to actual TPm sensor later // Check if the coldest spot temperature is less than 45 C so the RO filter // can safely run fluid through if ( ThdTemp < ROF_MIN_LOW_PRESSURE_TEMPERATURE_C ) { // Set the valves to fill up R1 and overflow to R2 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 ); setROPumpTargetFlowRate( RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM, MAX_RO_PUMP_FLUSH_FILL_PRESSURE_PSI ); // Set both reservoirs status to FLASE isR1Full = FALSE; isR2Full = FALSE; stateTimer = getMSTimerCount(); state = DG_HEAT_DISINFECT_STATE_RINSE_R1_TO_R2; } return state; } /*********************************************************************//** * @brief * The handleHeatDisinfectRinseR1ToR2State 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 handleHeatDisinfectRinseR1ToR2State( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_RINSE_R1_TO_R2; if ( isR1Full != TRUE ) { isR1Full = isRsrvrFull( DG_RESERVOIR_1, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS, DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH ); } if ( isR1Full ) { isR2Full = isRsrvrFull( DG_RESERVOIR_2, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_500ML_FILL_UP_TIMEOUT_MS, DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH ); if ( isR2Full ) { // Set the valves to rinse R2 to R1 and drain R1 setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); setValveState( VRO, VALVE_STATE_R2_C_TO_NC ); setValveState( VRD, VALVE_STATE_R1_C_TO_NC ); setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); // Set both reservoirs status to FLASE isR1Full = FALSE; isR2Full = FALSE; 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 ( isRsrvrEmpty( DG_RESERVOIR_1, DRAIN_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS, DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH ) ) { // 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, DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH ); } // Once reservoir 2 is completely full, monitor reservoir 1 if ( isR2Full ) { isR1Full = isRsrvrFull( DG_RESERVOIR_1, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_500ML_FILL_UP_TIMEOUT_MS, DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH ); if ( isR1Full ) { // Done with filling, turn off the RO pump signalROPumpHardStop(); // De-energize 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 handleHeatDisinfectCancelModeBasicPath function handles the * heat disinfect cancel mode basic path state. * @details Inputs: none * @details Outputs: none * @return next state of the heat disinfect state machine *************************************************************************/ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectCancelModeBasicPath( void ) { // Go to state complete, we are done DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_COMPLETE; // Set the cancellation mode cancellationMode = CANCELLATION_MODE_BASIC; // Pass the previous heat disinfect state for alarm report setModeToFailed( prevHeatDisinfectState ); return state; } /*********************************************************************//** * @brief * The handleHeatDisinfectCancelModeWaterPath function handles the * heat disinfect cancel mode cold water path state. * @details Inputs: none TODO fill up * @details Outputs: none TODO fill up * @return next state of the heat disinfect state machine *************************************************************************/ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectCancelModeWaterPath( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; // Stop all the actuators first then decide who should run next resetActuators(); if ( cancellationMode == CANCELLATION_MODE_NONE ) { // Check inlet dialysate and redundant outlet temperature sensors F32 TDi = getTemperatureValue( TEMPSENSORS_INLET_DIALYSATE ); F32 TRo = getTemperatureValue( TEMPSENSORS_OUTLET_REDUNDANT ); // The two sensors must be less than a threshold to decide if mix drain is needed to normal drain if ( TDi < MIX_DRAIN_TEMPERATURE_THRESHOLD_C && TRo < MIX_DRAIN_TEMPERATURE_THRESHOLD_C ) { // Set the reservoirs status to FALSE isR1Full = FALSE; isR2Full = FALSE; cancellationMode = CANCELLATION_MODE_COLD; } else { // Set the reservoirs status to FALSE isR1Full = FALSE; isR2Full = FALSE; // The fluid is hot so this is a mix drain. Set the VPd to direct the cold inlet fluid to drain setValveState( VPI, VALVE_STATE_OPEN ); setValveState( VPD, VALVE_STATE_DRAIN_C_TO_NC ); cancellationMode = CANCELLATION_MODE_HOT; } // The drain is set to start from reservoir 2 since all the actuators have been de-energized // Start the drain pump setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); // Start the timer for drain timeout stateTimer = getMSTimerCount(); } // If reservoir 2 is empty, set to drain reservoir 1 if ( isRsrvrEmpty( DG_RESERVOIR_2, DRAIN_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS, state ) ) { // Set the drain valve to reservoir 1 setValveState( VRD, VALVE_STATE_R1_C_TO_NC ); isR2Full = FALSE; } // If reservoir 2 has already been drained and reservoir 1 is empty, reset and switch to complete if ( ( isR2Full != TRUE ) && isRsrvrEmpty( DG_RESERVOIR_1, DRAIN_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS, state ) ) { // Done with heat disinfect mode state = DG_HEAT_DISINFECT_STATE_COMPLETE; // Pass the previous heat disinfect state for alarm report setModeToFailed( prevHeatDisinfectState ); } return state; } /*********************************************************************//** * @brief * The resetActuators function sets all the actuators to reset and * de-energized 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 ); // De-energize 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 ) { 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 cancellationState is the cancellation state to be called in case * the operation timed out. * @return none *************************************************************************/ static BOOL isRsrvrFull( DG_RESERVOIR_ID_T r, F32 targetVol, U32 timeout, DG_HEAT_DISINFECT_STATE_T cancellationState ) { BOOL rsrvrStatus = FALSE; F32 volume = 0.0; if ( r == DG_RESERVOIR_1 ) { volume = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); } else if ( r == DG_RESERVOIR_2 ) { volume = getLoadCellSmallFilteredWeight( 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 ) ) { // Failed to fill ontime. Update the previous heat disinfect state and transition to basic cancellation prevHeatDisinfectState = heatDisinfectState; heatDisinfectState = cancellationState; } 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 cancellationState is the cancellation state to be called in case * the operation timed out. * @return none *************************************************************************/ static BOOL isRsrvrEmpty( DG_RESERVOIR_ID_T r, U32 drainSteadyStateTimeout, U32 timeout, DG_HEAT_DISINFECT_STATE_T cancellationState ) { 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 ) ) { // Failed to drain ontime. Update the previous heat disinfect state and transition to basic cancellation prevHeatDisinfectState = heatDisinfectState; heatDisinfectState = cancellationState; } 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 ThdTemp = getTemperatureValue( TEMPSENSORS_INLET_DIALYSATE ); //TODO change this to actual TPm sensor later BOOL isR1OutOfRange = fabs( getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ) - R1HeatDisinfectVol ) > RSRVRS_MAX_TARGET_VOL_CHANGE_ML; BOOL isR2OutOfRange = fabs( getLoadCellSmallFilteredWeight( 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 ( ThdTemp < HEAT_DISINFECT_START_TEMPERATURE_C ) { // Keep reseting the disinfect timer so the elapsed time is always 0 until disinfection truly starts heatDisinfectTimer = getMSTimerCount(); hasHeatDisinfectStarted = FALSE; if ( didTimeout( stateTimer, HEAT_DISINFECT_START_TEMP_TIMOUT_MS ) ) { // Heating up to minimum temperature for heat disinfect failed // TODO we need to know how long it takes to reach to temperature before turning on the timeout //setModeToFailed( state ); } } else if ( hasHeatDisinfectStarted != TRUE && ThdTemp > HEAT_DISINFECT_START_TEMPERATURE_C ) { // The temperature of the coldest spot is in range to start the disinfect timer 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_TIME_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; data.overallElapsedTime = calcTimeSince( overallHeatDisinfectTimer ); data.stateElapsedTime = calcTimeSince( stateTimer ); data.cancellationMode = (U32)cancellationMode; // If the mode is in the actual heat disinfect states, publish the elapsed time, otherwise publish 0 to avoid confusion if ( heatDisinfectState == DG_HEAT_DISINFECT_STATE_DISINFECT_R1_TO_R2 || heatDisinfectState == DG_HEAT_DISINFECT_STATE_DISINFECT_R2_TO_R1 ) { data.heatDisinfectElapsedTime = calcTimeSince( heatDisinfectTimer ); } else { data.heatDisinfectElapsedTime = 0; } broadcastHeatDisinfectData( &data ); dataPublishCounter = 0; } } /**@}*/