Index: firmware/App/Modes/ModeHeatDisinfect.c =================================================================== diff -u -r1d97505ddf46e07f45bcc3d54c8a3654912f1fdf -r54abf84364e737dd350153d5fab7dd652f917ef4 --- firmware/App/Modes/ModeHeatDisinfect.c (.../ModeHeatDisinfect.c) (revision 1d97505ddf46e07f45bcc3d54c8a3654912f1fdf) +++ firmware/App/Modes/ModeHeatDisinfect.c (.../ModeHeatDisinfect.c) (revision 54abf84364e737dd350153d5fab7dd652f917ef4) @@ -1,14 +1,14 @@ /************************************************************************** * -* Copyright (c) 2020-2023 Diality Inc. - All Rights Reserved. +* Copyright (c) 2020-2024 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) Dara Navaei -* @date (last) 14-Sep-2023 +* @date (last) 09-Sep-2024 * * @author (original) Sean * @date (original) 20-Apr-2020 @@ -102,6 +102,7 @@ #define POST_HEAT_DISINFECT_WAIT_TIME_MS ( 3 * SEC_PER_MIN * MS_PER_SECOND ) ///< Heat disinfect final wait time before flushing the system in milliseconds. #define HEAT_DISINFECT_MAX_TEMP_GRADIENT_C 15.0F ///< Heat disinfect maximum allowed temperature gradient in between hottest and coldest sensors. #define HEAT_DISINFECT_TEMP_GRAD_OUT_RANGE_TIME_MS ( 0.16 * SEC_PER_MIN * MS_PER_SECOND ) ///< Heat disinfect temperature gradient out of range timeout in milliseconds. +#define HEAT_DIS_TEMP_GRAD_WAIT_2_CHECK_TIME_MS ( 5 * SEC_PER_MIN * MS_PER_SECOND ) #define HEAT_DISINFECT_TARGET_RO_PUMP_DC 0.4F ///< Heat disinfect target RO pump duty cycle. #define HEAT_DISINFECT_REF_RSRVR_TIMEOUT_MS ( 5 * MS_PER_SECOND ) ///< Heat disinfect getting reference reservoirs value timeout in milliseconds. #define HEAT_DISINFECT_AT_82_C_TIME_MS ( 10 * SEC_PER_MIN * MS_PER_SECOND ) ///< Heat disinfect time at 82 C in milliseconds. @@ -116,6 +117,10 @@ #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. +// Passive cool defines +#define PASSIVE_COOL_TIME_TO_DRAIN_MS ( 20 * MS_PER_SECOND ) ///< Passive cool time needed to be below temperature range in milliseconds. +#define PASSIVE_COOL_TEMP_THRESHOLD_TO_DRAIN_C 59.0F ///< Passive 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. @@ -161,6 +166,14 @@ F32 stopTempC; ///< Heat disinfect temperature to stop disinfect in C. } HEAT_DISINFECT_TIME_STATUS_T; +/// Passive heat disinfect status +typedef struct +{ + BOOL isPassiveHeatDisRqustd; ///< Boolean flag to indicate the status of passive heat disinfect. + BOOL isTempBelowRange; ///< Boolean flag to indicate whether the temperature is below range. + U32 tempsCheckStartTimeMS; ///< Temperatures below range start time in milliseconds. +} PASSIVE_COOL_HEAT_DISINFECT_STATUS_T; + // ********** private data ********** static DG_HEAT_DISINFECT_STATE_T heatDisinfectState; ///< Current active heat disinfect state. @@ -189,8 +202,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 PASSIVE_COOL_HEAT_DISINFECT_STATUS_T passiveCoolHeatDisStatus; ///< Passive cool 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 @@ -250,7 +264,7 @@ * hasROFCirculationBeenStarted, ROFCirculationTimer, targetDisinfectTime * ROFCirculationCoolingCounter, concentratePumpsPrimeTimer, areRsrvrsLeaking * haveDrainParamsBeenInit, tempGradOutOfRangeTimer, disinfectNVOps, - * dataPublishCounter, timeStatus, concPumpsStartTemperatureC + * dataPublishCounter, timeStatus, concPumpsStartTemperatureC, passiveHeatDisStatus * @return none *************************************************************************/ void initHeatDisinfectMode( void ) @@ -281,6 +295,8 @@ dataPublishCounter = 0; concPumpsStartTemperatureC = HEAT_DISINFECT_CONC_PUMPS_START_TEMP_C; isRODisinfectDone = FALSE; + passiveCoolHeatDisStatus.tempsCheckStartTimeMS = 0.0F; // NOTE: the "is passive" flag cannot be set here because it is set in with the request functions + passiveCoolHeatDisStatus.isTempBelowRange = FALSE; // Initialize the disinfect times timeStatus[ RO_AT_77_C ].startTempC = HEAT_DISINFECT_START_TEMP_AT_77_C; @@ -515,6 +531,21 @@ return status; } +/*********************************************************************//** + * @brief + * The setActiveOrPassiveHeatDisinfectStatus function sets the status of the + * heat disinfect as to whether it is active cool or passive cool + * @details Inputs: none + * @details Outputs: nocturnalHeatDisStatus + * @param isPassive boolean flag to indicate whether to run passive heat + * disinfect or not + * @return none + *************************************************************************/ +void setActiveOrPassiveHeatDisinfectStatus( BOOL isPassive ) +{ + passiveCoolHeatDisStatus.isPassiveHeatDisRqustd = isPassive; +} + // ********** private functions ********** /*********************************************************************//** @@ -544,6 +575,7 @@ setValveState( VPO, VALVE_STATE_FILL_C_TO_NC); setValveState( VRD1, VALVE_STATE_OPEN ); setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + SEND_EVENT_WITH_2_U32_DATA( DG_EVENT_PASSIVE_COOL_HEAT_DISINFECT_STATUS, (U32)passiveCoolHeatDisStatus.isPassiveHeatDisRqustd, 0 ) return state; } @@ -1089,6 +1121,12 @@ startHeater( DG_TRIMMER_HEATER ); } + // If the user has selected the passive cool heat disinfect, trigger the alarm to prompt the user to turn off the inlet water valves + if ( TRUE == passiveCoolHeatDisStatus.isPassiveHeatDisRqustd ) + { + activateAlarmNoData( ALARM_ID_DG_TURN_OFF_INLET_WATER_VALVES ); + } + stateTimer = getMSTimerCount(); rsrvrsVolMonitorTimer = getMSTimerCount(); state = DG_HEAT_DISINFECT_STATE_DISINFECT_R1_TO_R2; @@ -1375,32 +1413,43 @@ *************************************************************************/ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectCoolDownHeatersState( void ) { - DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_COOL_DOWN_HEATERS; + DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_COOL_DOWN_HEATERS; + BOOL setActuatorsPriorToTransition = FALSE; heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_COOL_DOWN_DEVICE; writeDisinfectDataToNV( USAGE_INFO_HEAT_DIS ); - if ( TRUE == didTimeout( stateTimer, POST_HEAT_DISINFECT_WAIT_TIME_MS ) ) + if ( ( TRUE == didTimeout( stateTimer, POST_HEAT_DISINFECT_WAIT_TIME_MS ) ) && ( FALSE == passiveCoolHeatDisStatus.isPassiveHeatDisRqustd ) ) { - signalROPumpHardStop(); + setActuatorsPriorToTransition = TRUE; + } + else + { + F32 TDiC = getTemperatureValue( TEMPSENSORS_INLET_DIALYSATE ); + F32 THdC = getTemperatureValue( TEMPSENSORS_HEAT_DISINFECT ); - if ( TRUE == isDrainPumpOn() ) + if ( ( TDiC > PASSIVE_COOL_TEMP_THRESHOLD_TO_DRAIN_C ) || ( THdC > PASSIVE_COOL_TEMP_THRESHOLD_TO_DRAIN_C ) ) { - // Stop the drain pump and the RO pump to exit the closed loop - signalDrainPumpHardStop(); - setValveState( VRD1, VALVE_STATE_CLOSED ); + passiveCoolHeatDisStatus.tempsCheckStartTimeMS = getMSTimerCount(); } - else + else if ( TRUE == didTimeout( passiveCoolHeatDisStatus.tempsCheckStartTimeMS, PASSIVE_COOL_TIME_TO_DRAIN_MS ) ) { - 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; + setActuatorsPriorToTransition = TRUE; } } + if ( TRUE == setActuatorsPriorToTransition ) + { + signalROPumpHardStop(); + setValveState( VRD1, VALVE_STATE_CLOSED ); + 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; + } + return state; } @@ -1411,31 +1460,36 @@ * times out, it transitions to basic cancellation state. Otherwise, it * transitions to the next state. * @details Inputs: stateTimer, rsrvr1Status, rsrvr2Status, - * isDrainPumpOnInMixDrain + * isDrainPumpOnInMixDrain, passiveCoolHeatDisStatus * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, * isDrainPumpOnInMixDrain * @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; + U32 targetDrainRPM = ( TRUE == passiveCoolHeatDisStatus.isPassiveHeatDisRqustd ? DRAIN_PUMP_TARGET_RPM : DRAIN_PUMP_RPM_IN_MIX_DRAIN ); heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_FLUSH_AFTER_DISINFECT; if ( ( TRUE == didTimeout( stateTimer, DRAIN_PUMP_START_TIME_IN_MIX_DRAIN_MS ) ) && ( FALSE == isDrainPumpInMixDrainOn ) ) { isDrainPumpInMixDrainOn = TRUE; - setValveState( VPI, VALVE_STATE_OPEN ); + if ( FALSE == passiveCoolHeatDisStatus.isPassiveHeatDisRqustd ) + { + setValveState( VPI, VALVE_STATE_OPEN ); + turnOnUVReactor( INLET_UV_REACTOR ); + } + setValveState( VRD1, VALVE_STATE_OPEN ); setValveState( VBF, VALVE_STATE_CLOSED ); 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( VRO, VALVE_STATE_R1_C_TO_NO ); // Turn on the drain pump to drain the reservoirs in open loop mode - setDrainPumpTargetRPM( DRAIN_PUMP_RPM_IN_MIX_DRAIN ); - turnOnUVReactor( INLET_UV_REACTOR ); + setDrainPumpTargetRPM( targetDrainRPM ); } else if ( TRUE == isDrainPumpInMixDrainOn ) { @@ -1549,8 +1603,10 @@ // Stop all the actuators first then decide who should run next deenergizeActuators( NO_PARK_CONC_PUMPS ); - // 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 ) ) + // The two sensors must be less than a threshold to decide if mix drain is needed to normal drain or the + // passive cool heat disinfect should have been selected + if ( ( ( TDi < MIX_DRAIN_TEMPERATURE_THRESHOLD_C ) && ( TRo < MIX_DRAIN_TEMPERATURE_THRESHOLD_C ) ) || + ( TRUE == passiveCoolHeatDisStatus.isPassiveHeatDisRqustd ) ) { targetRPM = DRAIN_PUMP_TARGET_RPM; cancellationMode = CANCELLATION_MODE_COLD; @@ -1651,9 +1707,16 @@ } else #endif + + if ( FALSE == passiveCoolHeatDisStatus.isPassiveHeatDisRqustd ) { requestNewOperationMode( DG_MODE_HCOL ); } + else + { + // After nocturnal run transition to standby since there is not active cool + requestNewOperationMode( DG_MODE_STAN ); + } return state; } @@ -1846,7 +1909,15 @@ } else if ( 0 == tempGradOutOfRangeTimer ) { - tempGradOutOfRangeTimer = getMSTimerCount(); + if ( ( TRUE == didTimeout( stateTimer, HEAT_DIS_TEMP_GRAD_WAIT_2_CHECK_TIME_MS ) ) && + ( DG_HEAT_DISINFECT_STATE_DISINFECT_R1_TO_R2 == heatDisinfectState ) ) + { + tempGradOutOfRangeTimer = getMSTimerCount(); + } + else if ( DG_HEAT_DISINFECT_STATE_DISINFECT_R2_TO_R1 == heatDisinfectState ) + { + tempGradOutOfRangeTimer = getMSTimerCount(); + } } else if ( TRUE == didTimeout( tempGradOutOfRangeTimer, HEAT_DISINFECT_TEMP_GRAD_OUT_RANGE_TIME_MS ) ) { @@ -2031,15 +2102,18 @@ { if ( ( STATE_OPEN == getSwitchStatus( CONCENTRATE_CAP ) ) || ( STATE_OPEN == getSwitchStatus( DIALYSATE_CAP ) ) ) { - // Set the variables to fail and go to cancel water path. Set the pending alarm to no alarm so the cancel water path - // will not be raising the alarm at end of the cancel water path. The recoverable alarm is raised here in this function - prevHeatDisinfectState = heatDisinfectState; - heatDisinfectState = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; - alarmDetectedPendingTrigger = ALARM_ID_DG_DIALYSATE_CAP_NOT_IN_PROPER_POSITION; - - if ( STATE_OPEN == getSwitchStatus( CONCENTRATE_CAP ) ) + if ( ( heatDisinfectState != DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH ) && ( heatDisinfectState != DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH ) ) { - alarmDetectedPendingTrigger = ALARM_ID_DG_CONCENTRATE_CAP_NOT_IN_PROPER_POSITION; + // Set the variables to fail and go to cancel water path. Set the pending alarm to no alarm so the cancel water path + // will not be raising the alarm at end of the cancel water path. The recoverable alarm is raised here in this function + prevHeatDisinfectState = heatDisinfectState; + heatDisinfectState = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; + alarmDetectedPendingTrigger = ALARM_ID_DG_DIALYSATE_CAP_NOT_IN_PROPER_POSITION; + + if ( STATE_OPEN == getSwitchStatus( CONCENTRATE_CAP ) ) + { + alarmDetectedPendingTrigger = ALARM_ID_DG_CONCENTRATE_CAP_NOT_IN_PROPER_POSITION; + } } } }