Index: firmware/App/Modes/ModeHeatDisinfect.c =================================================================== diff -u -r7d4711edd7b40cd3e29f43e766f79a8a09586fe9 -rd4e7cd17654a007b5e140e32bd368751dca7977a --- firmware/App/Modes/ModeHeatDisinfect.c (.../ModeHeatDisinfect.c) (revision 7d4711edd7b40cd3e29f43e766f79a8a09586fe9) +++ firmware/App/Modes/ModeHeatDisinfect.c (.../ModeHeatDisinfect.c) (revision d4e7cd17654a007b5e140e32bd368751dca7977a) @@ -117,6 +117,12 @@ #define MIX_DRAIN_WEIGHT_UNCHANGE_TIMEOUT ( 15 * MS_PER_SECOND ) ///< Time period of unchanged weight during mix draining before timeout. #define MIX_DRAIN_TEMPERATURE_THRESHOLD_C 60.0F ///< Temperature threshold for performing mix drain or normal drain. +// Nocturnal cool defines +#define NOCTURNAL_COOL_IDLE_TIME_MS ( 10 * SEC_PER_MIN * MS_PER_SECOND ) ///< Nocturnal cool idle time in milliseconds. +#define NOCTURNAL_COOL_RUN_TIME_PRIOR_TEMP_AVG_MS ( 40 * MS_PER_SECOND ) ///< Nocturnal cool run the fluid before averaging temperature in milliseconds. +#define NOCTURNAL_COOL_RUN_TIME_TO_TEMP_AVG_MS ( 20 * MS_PER_SECOND ) ///< Nocturnal cool run time to average the temperature in milliseconds. +#define NOCTURNAL_COOL_TEMP_THRESHOLD_TO_DRAIN_C 58.0F ///< Nocturnal cool threshold temperature to drain in C. + #ifndef _RELEASE_ #define NELSON_SUPPORT_TARGET_TEMP_C 6.0F // Nelson support heat disinfect target temperature in C. #define NELSON_SUPPORT_STOP_TEMP_C ( NELSON_SUPPORT_TARGET_TEMP_C - 5.0F ) // Nelson support heat disinfect stop temperature in C. @@ -162,6 +168,15 @@ F32 stopTempC; ///< Heat disinfect temperature to stop disinfect in C. } HEAT_DISINFECT_TIME_STATUS_T; +/// Nocturnal heat disinfect status +typedef struct +{ + BOOL isNocturnalHeatDisRqustd; ///< Boolean flag to indicate the status of nocturnal heat disinfect. + F32 TDiRunningSumC; ///< TDi running sum temperature in C. + F32 THdRunninSumC; ///< THd running sum temperature in C. + U32 sampleCount; ///< Temperature sample count. +} NOCTURNAL_HEAT_DISINFECT_STATUS_T; + // ********** private data ********** static DG_HEAT_DISINFECT_STATE_T heatDisinfectState; ///< Current active heat disinfect state. @@ -190,8 +205,9 @@ static HEAT_DISINFECT_TIME_STATUS_T timeStatus[ NUM_OF_HEAT_DISINFECT_TIMES ]; ///< Heat disinfect time status. static F32 concPumpsStartTemperatureC; ///< Heat disinfect concentrate pumps start temperature in C. static BOOL isRODisinfectDone; ///< Heat disinfect is RO disinfect done flag. -static OVERRIDE_U32_T targetTimer77C = { HEAT_DISINFECT_AT_77_C_TIME_MS, 0, 0, 0 }; ///< Heat disinfection override target timer at 77 C -static OVERRIDE_U32_T targetTimer82C = { HEAT_DISINFECT_AT_82_C_TIME_MS, 0, 0, 0 }; ///< Heat disinfection override target timer at 82 C +static OVERRIDE_U32_T targetTimer77C = { HEAT_DISINFECT_AT_77_C_TIME_MS, 0, 0, 0 }; ///< Heat disinfection override target timer at 77 C. +static OVERRIDE_U32_T targetTimer82C = { HEAT_DISINFECT_AT_82_C_TIME_MS, 0, 0, 0 }; ///< Heat disinfection override target timer at 82 C. +static NOCTURNAL_HEAT_DISINFECT_STATUS_T noctHeatDisStatus; ///< Nocturnal heat disinfect status. #ifndef _RELEASE_ /* Nelson Labs is in charge of testing the efficacy of the disinfects (heat and chem). The codes that contain the name Nelson are used to @@ -218,6 +234,10 @@ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFillR2WithHotWaterState( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectDisinfectR2ToR1State( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectCoolDownHeatersState( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectNocturnalCoolIdleState( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectNocturnalCoolCheckWaterTempState( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectNocturnalCoolDrainR1State( void ); +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectNocturnalCoolDrainR2State( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectMixDrainR1State( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectMixDrainR2State( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectCancelModeBasicPathState( void ); @@ -232,6 +252,8 @@ static void publishHeatDisinfectData( void ); static void monitorModeHeatDisinfect( void ); static void writeDisinfectDataToNV( DG_USAGE_INFO_ITEMS_T info ); +static void setHeatDisinfectActuators( DG_HEAT_DISINFECT_STATE_T state ); +static BOOL areThereAnyDGPumpsAlarm( void ); #ifndef _RELEASE_ static void setNelsonSupportConditions( void ); @@ -251,7 +273,7 @@ * hasROFCirculationBeenStarted, ROFCirculationTimer, targetDisinfectTime * ROFCirculationCoolingCounter, concentratePumpsPrimeTimer, areRsrvrsLeaking * haveDrainParamsBeenInit, tempGradOutOfRangeTimer, disinfectNVOps, - * dataPublishCounter, timeStatus, concPumpsStartTemperatureC + * dataPublishCounter, timeStatus, concPumpsStartTemperatureC, noctHeatDisStatus * @return none *************************************************************************/ void initHeatDisinfectMode( void ) @@ -282,6 +304,9 @@ dataPublishCounter = 0; concPumpsStartTemperatureC = HEAT_DISINFECT_CONC_PUMPS_START_TEMP_C; isRODisinfectDone = FALSE; + noctHeatDisStatus.TDiRunningSumC = 0.0F; // NOTE: the "is nocturnal" flag cannot be set here because it is set in with the request functions + noctHeatDisStatus.THdRunninSumC = 0.0F; + noctHeatDisStatus.sampleCount = 0; // Initialize the disinfect times timeStatus[ RO_AT_77_C ].startTempC = HEAT_DISINFECT_START_TEMP_AT_77_C; @@ -440,6 +465,22 @@ heatDisinfectState = handleHeatDisinfectCoolDownHeatersState(); break; + case DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_IDLE: + heatDisinfectState = handleHeatDisinfectNocturnalCoolIdleState(); + break; + + case DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_CHECK_WATER_TEMP: + heatDisinfectState = handleHeatDisinfectNocturnalCoolCheckWaterTempState(); + break; + + case DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_DRAIN_R1: + heatDisinfectState = handleHeatDisinfectNocturnalCoolDrainR1State(); + break; + + case DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_DRAIN_R2: + heatDisinfectState = handleHeatDisinfectNocturnalCoolDrainR2State(); + break; + case DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R1: heatDisinfectState = handleHeatDisinfectMixDrainR1State(); break; @@ -516,6 +557,21 @@ return status; } +/*********************************************************************//** + * @brief + * The setNocturnalHeatDisinfectStatus function sets the status of the + * nocturnal heat disinfect. + * @details Inputs: none + * @details Outputs: nocturnalHeatDisStatus + * @param isNocturnal boolean flag to indicate whether to run nocturnal heat + * disinfect or not + * @return none + *************************************************************************/ +void setNocturnalHeatDisinfectStatus( BOOL isNocturnal ) +{ + noctHeatDisStatus.isNocturnalHeatDisRqustd = isNocturnal; +} + // ********** private functions ********** /*********************************************************************//** @@ -1090,6 +1146,12 @@ startHeater( DG_TRIMMER_HEATER ); } + // If the user has selected the nocturnal heat disinfect, trigger the alarm to prompt the user to turn off the inlet water valves + if ( TRUE == noctHeatDisStatus.isNocturnalHeatDisRqustd ) + { + activateAlarmNoData( ALARM_ID_DG_TURN_OFF_INLET_WATER_VALVES ); + } + stateTimer = getMSTimerCount(); rsrvrsVolMonitorTimer = getMSTimerCount(); state = DG_HEAT_DISINFECT_STATE_DISINFECT_R1_TO_R2; @@ -1394,17 +1456,117 @@ } else { - setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); - setValveState( VRC, VALVE_STATE_DRAIN_C_TO_NO ); - rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; - stateTimer = getMSTimerCount(); - state = DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R1; + stateTimer = getMSTimerCount(); + state = ( TRUE == noctHeatDisStatus.isNocturnalHeatDisRqustd ? DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_IDLE : DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R1 ); + setHeatDisinfectActuators( state ); } } return state; } +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectNocturnalCoolIdleState( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_IDLE; + + if ( TRUE == didTimeout( stateTimer, NOCTURNAL_COOL_IDLE_TIME_MS ) ) + { + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_CHECK_WATER_TEMP; + setHeatDisinfectActuators( state ); + } + + if ( TRUE == areThereAnyDGPumpsAlarm() ) + { + state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; + } + + return state; +} + +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectNocturnalCoolCheckWaterTempState( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_CHECK_WATER_TEMP; + + if ( TRUE == didTimeout( stateTimer, NOCTURNAL_COOL_RUN_TIME_PRIOR_TEMP_AVG_MS ) ) + { + deenergizeActuators( FALSE ); + + noctHeatDisStatus.TDiRunningSumC += getTemperatureValue( TEMPSENSORS_INLET_DIALYSATE ); + noctHeatDisStatus.THdRunninSumC += getTemperatureValue( TEMPSENSORS_HEAT_DISINFECT ); + noctHeatDisStatus.sampleCount += 1; + + if ( TRUE == didTimeout( stateTimer, ( NOCTURNAL_COOL_RUN_TIME_PRIOR_TEMP_AVG_MS + NOCTURNAL_COOL_RUN_TIME_TO_TEMP_AVG_MS ) ) ) + { + F32 avgTDiC = noctHeatDisStatus.TDiRunningSumC / (F32)noctHeatDisStatus.sampleCount; + F32 avgTHdC = noctHeatDisStatus.THdRunninSumC / (F32)noctHeatDisStatus.sampleCount; + + // Assume the default state transition is the nocturnal cool idle + state = DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_IDLE; + + if ( ( avgTDiC <= NOCTURNAL_COOL_TEMP_THRESHOLD_TO_DRAIN_C ) && ( avgTHdC <= NOCTURNAL_COOL_TEMP_THRESHOLD_TO_DRAIN_C ) ) + { + state = DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_DRAIN_R1; + } + + setHeatDisinfectActuators( state ); + } + } + + if ( TRUE == areThereAnyDGPumpsAlarm() ) + { + state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; + } + + return state; +} + +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectNocturnalCoolDrainR1State( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_DRAIN_R1; + + if ( DG_RESERVOIR_ABOVE_TARGET == rsrvr1Status ) + { + rsrvr1Status = getRsrvrDrainStatus( DG_RESERVOIR_1, DRAIN_WEIGHT_UNCHANGE_TIMEOUT_MS, RSRVRS_INITIAL_DRAIN_TIMEOUT_MS ); + } + else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) + { + state = DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_DRAIN_R2; + stateTimer = getMSTimerCount(); + setHeatDisinfectActuators( state ); + } + else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) + { + prevHeatDisinfectState = state; + state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; + } + + return state; +} + +static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectNocturnalCoolDrainR2State( void ) +{ + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_DRAIN_R2; + + if ( DG_RESERVOIR_ABOVE_TARGET == rsrvr2Status ) + { + rsrvr2Status = getRsrvrDrainStatus( DG_RESERVOIR_2, DRAIN_WEIGHT_UNCHANGE_TIMEOUT_MS, RSRVRS_INITIAL_DRAIN_TIMEOUT_MS ); + } + else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) + { + deenergizeActuators( NO_PARK_CONC_PUMPS ); + + state = ( ALARM_ID_NO_ALARM == alarmDetectedPendingTrigger ? DG_HEAT_DISINFECT_STATE_COMPLETE : DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH ); + } + else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) + { + prevHeatDisinfectState = state; + state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; + } + + return state; +} + /*********************************************************************//** * @brief * The handleHeatDisinfectMixDrainR1State function handles the heat @@ -1537,6 +1699,7 @@ { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; U32 drainTimeoutMS = ( CANCELLATION_MODE_COLD == cancellationMode ? RSRVRS_INITIAL_DRAIN_TIMEOUT_MS : RSRVRS_MIX_DRAIN_TIMEOUT_MS ); + BOOL isDrainAllowed = TRUE; // Set the heat disinfect UI state heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_CANCEL_DISINFECT; @@ -1564,69 +1727,86 @@ targetRPM = DRAIN_PUMP_RPM_IN_MIX_DRAIN; cancellationMode = CANCELLATION_MODE_HOT; + + if ( TRUE == noctHeatDisStatus.isNocturnalHeatDisRqustd ) + { + isDrainAllowed = FALSE; + state = DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_IDLE; + } } - rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; - rsrvr2Status = DG_RESERVOIR_ABOVE_TARGET; - haveDrainParamsBeenInit[ DG_RESERVOIR_1 ] = FALSE; - haveDrainParamsBeenInit[ DG_RESERVOIR_2 ] = FALSE; - stateTimer = getMSTimerCount(); + stateTimer = getMSTimerCount(); - // The drain is set to start from reservoir 2 since all the actuators have been de-energized - // Set the drain valve to reservoir 2 - setValveState( VRD2, VALVE_STATE_OPEN ); - setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); - setDrainPumpTargetRPM( targetRPM ); + if ( TRUE == isDrainAllowed ) + { + rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; + rsrvr2Status = DG_RESERVOIR_ABOVE_TARGET; + haveDrainParamsBeenInit[ DG_RESERVOIR_1 ] = FALSE; + haveDrainParamsBeenInit[ DG_RESERVOIR_2 ] = FALSE; + + // The drain is set to start from reservoir 2 since all the actuators have been de-energized + // Set the drain valve to reservoir 2 + setValveState( VRD2, VALVE_STATE_OPEN ); + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); + setDrainPumpTargetRPM( targetRPM ); + } + else + { + setHeatDisinfectActuators( DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_IDLE ); + } } - // If reservoir 2 is empty, set to drain reservoir 1 - if ( DG_RESERVOIR_ABOVE_TARGET == rsrvr2Status ) + if ( TRUE == isDrainAllowed ) { - // If the cancellation water path cannot be done, got to basic cancellation path - rsrvr2Status = getRsrvrDrainStatus( DG_RESERVOIR_2, DRAIN_WEIGHT_UNCHANGE_TIMEOUT_MS, drainTimeoutMS ); + // If reservoir 2 is empty, set to drain reservoir 1 + if ( DG_RESERVOIR_ABOVE_TARGET == rsrvr2Status ) + { + // If the cancellation water path cannot be done, got to basic cancellation path + rsrvr2Status = getRsrvrDrainStatus( DG_RESERVOIR_2, DRAIN_WEIGHT_UNCHANGE_TIMEOUT_MS, drainTimeoutMS ); - if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) + if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) + { + // Set the drain valve to reservoir 1 and close reservoir 2 + setValveState( VRD1, VALVE_STATE_OPEN ); + setValveState( VRD2, VALVE_STATE_CLOSED ); + } + } + else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) { - // Set the drain valve to reservoir 1 and close reservoir 2 - setValveState( VRD1, VALVE_STATE_OPEN ); + // Stop the actuators that are running before going to basic cancellation path + setValveState( VRD1, VALVE_STATE_CLOSED ); setValveState( VRD2, VALVE_STATE_CLOSED ); + signalDrainPumpHardStop(); + + prevHeatDisinfectState = state; + state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; } - } - else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) - { - // Stop the actuators that are running before going to basic cancellation path - setValveState( VRD1, VALVE_STATE_CLOSED ); - setValveState( VRD2, VALVE_STATE_CLOSED ); - signalDrainPumpHardStop(); - prevHeatDisinfectState = state; - state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; - } + // If reservoir 2 has already been drained and reservoir 1 is empty, reset and switch to complete + if ( ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) && ( DG_RESERVOIR_ABOVE_TARGET == rsrvr1Status ) ) + { + // If the cancellation water path cannot be done, got to basic cancellation path + rsrvr1Status = getRsrvrDrainStatus( DG_RESERVOIR_1, DRAIN_WEIGHT_UNCHANGE_TIMEOUT_MS, drainTimeoutMS ); - // If reservoir 2 has already been drained and reservoir 1 is empty, reset and switch to complete - if ( ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) && ( DG_RESERVOIR_ABOVE_TARGET == rsrvr1Status ) ) - { - // If the cancellation water path cannot be done, got to basic cancellation path - rsrvr1Status = getRsrvrDrainStatus( DG_RESERVOIR_1, DRAIN_WEIGHT_UNCHANGE_TIMEOUT_MS, drainTimeoutMS ); - - if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) + if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) + { + setValveState( VRD1, VALVE_STATE_CLOSED ); + setValveState( VRD2, VALVE_STATE_CLOSED ); + signalDrainPumpHardStop(); + failHeatDisinfect(); + } + } + else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { setValveState( VRD1, VALVE_STATE_CLOSED ); setValveState( VRD2, VALVE_STATE_CLOSED ); signalDrainPumpHardStop(); - failHeatDisinfect(); + + prevHeatDisinfectState = state; + state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; } } - else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) - { - setValveState( VRD1, VALVE_STATE_CLOSED ); - setValveState( VRD2, VALVE_STATE_CLOSED ); - signalDrainPumpHardStop(); - prevHeatDisinfectState = state; - state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; - } - return state; } @@ -2010,6 +2190,7 @@ uiData.r82CountdownTimeS = ( 0 == timeStatus[ RSRVR_AT_82_C ].startTimeMS ? 0 : ( timeStatus[ RSRVR_AT_82_C ].targetTimeMS - calcTimeSince( timeStatus[ RSRVR_AT_82_C ].startTimeMS ) ) / 1000 ); data.R1FillLevel = rsrvr1RefVolML; data.R2FillLevel = rsrvr2RefVolML; + data.noctHeatDisinfectStatus = noctHeatDisStatus.isNocturnalHeatDisRqustd; broadcastData( MSG_ID_DG_HEAT_DISINFECT_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&data, sizeof( MODE_HEAT_DISINFECT_DATA_T ) ); broadcastData( MSG_ID_DG_HEAT_DISINFECT_TIME_DATA, COMM_BUFFER_OUT_CAN_DG_2_UI, (U08*)&uiData, sizeof( MODE_HEAT_DISINFECT_UI_DATA_T ) ); @@ -2081,7 +2262,81 @@ } } +/*********************************************************************//** + * @brief + * The setHeatDisinfectActuators function set the actuators prior to transitioning + * to the new heat disinfect state + * @details Inputs: none + * @details Outputs: rsrvr1Status, rsrvr2Status, haveDrainParamsBeenInit + * @param state the heat disinfect state that the exec is about to transition + * @return: none + *************************************************************************/ +static void setHeatDisinfectActuators( DG_HEAT_DISINFECT_STATE_T state ) +{ + switch( state ) + { + case DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_IDLE: + deenergizeActuators( FALSE ); + break; + case DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_CHECK_WATER_TEMP: + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); + setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); + setValveState( VRO, VALVE_STATE_R2_C_TO_NC ); + setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); + setValveState( VRD1, VALVE_STATE_OPEN ); + setValveState( VRD2, VALVE_STATE_CLOSED ); + setValveState( VDR, VALVE_STATE_RECIRC_C_TO_NC ); + setValveState( VRC, VALVE_STATE_RECIRC_C_TO_NC ); + setROPumpTargetDutyCycle( HEAT_DISINFECT_TARGET_RO_PUMP_DC ); + setDrainPumpTargetOutletFlowLPM( HEAT_DISINFECT_TARGET_RO_FLOW_LPM ); + break; + + case DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_DRAIN_R1: + haveDrainParamsBeenInit[ DG_RESERVOIR_1 ] = FALSE; + rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC); + setValveState( VRD1, VALVE_STATE_OPEN ); + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + break; + + case DG_HEAT_DISINFECT_STATE_NOCTURNAL_COOL_DRAIN_R2: + haveDrainParamsBeenInit[ DG_RESERVOIR_2 ] = FALSE; + rsrvr2Status = DG_RESERVOIR_ABOVE_TARGET; + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC); + setValveState( VRD1, VALVE_STATE_CLOSED ); + setValveState( VRD2, VALVE_STATE_OPEN ); + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + break; + + case DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R1: + setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); + setValveState( VRC, VALVE_STATE_DRAIN_C_TO_NO ); + rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; + break; + + default: + // Do nothing. More state should be added in the future. + break; + } +} + +static BOOL areThereAnyDGPumpsAlarm( void ) +{ + BOOL status = FALSE; + + status |= isAlarmActive( ALARM_ID_DG_RO_PUMP_PRESSURE_OUT_OF_RANGE ); + status |= isAlarmActive( ALARM_ID_DG_MAX_RO_PUMP_PWM_EXCEEDED ); + status |= isAlarmActive( ALARM_ID_DG_RO_PUMP_DUTY_CYCLE_OUT_OF_RANGE ); + status |= isAlarmActive( ALARM_ID_DG_DRAIN_PUMP_DIRECTION_FPGA_FAULT ); + status |= isAlarmActive( ALARM_ID_DG_DRAIN_PUMP_DIRECTION_INVALID ); + status |= isAlarmActive( ALARM_ID_DG_DRAIN_PUMP_OFF_FAULT ); + status |= isAlarmActive( ALARM_ID_DG_DRAIN_PUMP_RPM_OUT_OF_RANGE ); + + return status; +} + + /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/