/************************************************************************** * * Copyright (c) 2022-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 ModeHeatDisinfectActiveCool.c * * @author (last) Dara Navaei * @date (last) 16-Aug-2023 * * @author (original) Dara Navaei * @date (original) 18-Dec-2022 * ***************************************************************************/ #include "ConcentratePumps.h" #include "ConductivitySensors.h" #include "CPLD.h" #include "DrainPump.h" #include "Heaters.h" #include "LoadCell.h" #include "MessageSupport.h" #include "ModeFault.h" #include "ModeHeatDisinfectActiveCool.h" #include "OperationModes.h" #include "Pressures.h" #include "Reservoirs.h" #include "ROPump.h" #include "RTC.h" #include "Switches.h" #include "TaskGeneral.h" #include "TemperatureSensors.h" #include "Timers.h" #include "UVReactors.h" #include "Valves.h" /** * @addtogroup DGHeatDisinfectActiveCoolMode * @{ */ // ********** private data ********** #define HEAT_DISINFECT_ACTIVE_COOL_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Mode heat disinfect active cool data publish interval in counts. #define ACID_PUMP_SPEED_ML_PER_MIN -30.0F ///< Acid concentrate pump speed in mL/min. // The acid pump is 2% faster than the acid pump to create a flow from acid to bicarb line during heat disinfect #define BICARB_PUMP_SPEED_ML_PER_MIN 30.6F ///< Bicarb concentrate pump speed in mL/min. #define RSRVR_FILL_TIMEOUT_MS ( 5 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoir fill timeout in milliseconds. #define RSRVR_DRAIN_TIMEOUT_MS ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoir drain timeout in milliseconds. #define RSRVR_DRAIN_STEADY_TIMEOUT_MS ( 6 * MS_PER_SECOND ) ///< Reservoir drain steady timeout in milliseconds. #define RSRVR_DRAIN_TARGET_RPM 2400 ///< Reservoir drain target RPM. #define RSRVR_MIX_DRAIN_TIMEOUT_MS ( 4 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 mix drain timeout in ms. #define RSRVR_MIX_DRAIN_TEMPERATURE_THRESHOLD_C 60.0F ///< Temperature threshold for performing mix drain or normal drain. // RO filter cool down defines #define ROF_ACTIVE_COOL_TARGET_FLOW_LPM 0.3F ///< RO active cool down target flow in L/min. #define ROF_ACTIVE_COOL_TARGET_RSRVR_FILL_ML 600.0F ///< RO active cool target reservoir fill in mL. #define ROF_ACTIVE_COOL_TARGET_TEMP_C 40.0F ///< RO active cool target temperature in C. #define ROF_ACTIVE_COOL_BELOW_TEMP_TIMEOUT_MS ( 30 * MS_PER_SECOND ) ///< RO active cool temperature below target for cooling timeout in milliseconds. #define ROF_ACTIVE_COOL_INITIAL_TARGET_DRAIN_RPM 600 ///< RO active cool initial target drain RPM. #define ROF_ACTIVE_COOL_TARGET_DARIN_RPM 300 ///< RO active cool target drain RPM. #define ROF_ACTIVE_COOL_MIX_DRAIN_VALVE_ON_TIMEOUT_MS ( 5 * MS_PER_SECOND ) ///< RO active cool mix drain VPd turn on timeout in milliseconds. #define ROF_ACTIVE_COOL_RSRVR_MIX_DRAIN_TIMEOUT_MS ( 4 * SEC_PER_MIN * MS_PER_SECOND ) ///< RO active cool reservoir mix drain timeout in milliseconds. #define ROF_ACTIVE_COOL_MIX_DRAIN_STEADY_TIMEOUT_MS ( 15 * MS_PER_SECOND ) ///< RO active cool mix drain steady timeout in milliseconds. // Reservoir cool down defines #define RSRVR_ACTIVE_COOL_TARGET_FLOW_LPM 0.8F ///< Reservoir active cool target flow in L/min. #define RSRVR_ACTIVE_COOL_MAX_ALLOWED_PRESSURE_PSI 130 ///< Reservoir active cool max allowed pressure in psi. #define RSRVR_ACTIVE_COOL_FILL_TARGET_FILL_ML 1850.0F ///< Reservoir active cool over fill target in mL. #define RSRVR_ACTIVE_COOL_TARGET_TEMP_C 40.0F ///< Reservoir active cool target temperature in C. #define RSRVR_ACTIVE_COOL_BELOW_TARGET_TEMP_TIMEOUT_MS ( 15 * MS_PER_SECOND ) ///< Reservoir active cool temperature below target timeout in milliseconds. #define RSRVR_ACTIVE_COOL_TARGET_TEMP_TIMEOUT_MS ( 10 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoir active cool target temperature met in milliseconds. /// Non-volatile write structure typedef struct { BOOL hasDisStatusBeenWrittenToNV; ///< Boolean flag to indicate whether the disinfect status been written to NV or not. } DISINFECT_NV_OPS_T; /// Temperature below target structure typedef struct { U32 tempBelowTargetStartTimeMS; ///< Mode heat disinfect active cool temperature below target time in milliseconds. BOOL hasTempTargetTimeBeenSet; ///< Mode heat disinfect active cool temperature target time has been set. } TEMP_BELOW_TARGET_T; static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T heatDisinfectActiveCoolState; ///< Mode heat disinfect active cool state. static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T heatDisinfectActiceCoolPrevState; ///< Mode heat disinfect active cool previous state. static U32 stateStartTimeMS; ///< Mode heat disinfect active cool state timer in milliseconds. static U32 dataPublishCounter; ///< Mode heat disinfect active cool data publish counter. static U32 overallHeatDisinfectActiveCoolTimer; ///< Mode heat disinfect active cool over mode timer. static ALARM_ID_T alarmDetectedPendingTrigger; ///< Mode heat disinfect active cool pending alarm trigger. static DIS_RSRVR_STATUS_T rsrvrsStatus; ///< Mode heat disinfect active cool reservoirs status. static TEMP_BELOW_TARGET_T tempBelowTarget; ///< Mode heat disinfect active cool temperature below target time in milliseconds. static DISINFECT_NV_OPS_T disinfectNVOps; ///< Disinfect non-volatile memory operations. static CANCELLATION_MODE_T cancellationMode; ///< Cancellation mode. // ********** private function prototypes ********** static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolStartState( void ); static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolMixDrainR1State( void ); static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolMixDrainR2State( void ); static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolFillR1State( void ); static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolFillR2State( void ); static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolDrainR2FillR1ToR2State( void ); static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolDrainR1FillR2ToR1State( void ); static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolDrainR1State( void ); static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolDrainR2State( void ); static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolCompleteState( void ); static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolCancelWaterPathState( void ); static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolCancelBasicPathState( void ); static void failHeatDisinfectActiveCool( void ); static void publishHeatDisinfectActiveCoolData( void ); static void monitorModeHeatDisinfectActiveCool( void ); static void setHeatDisinfectActiveCoolActuators( DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state ); static void writeDisinfectDataToNV( DG_USAGE_INFO_ITEMS_T info ); static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T checkRsrvrMgmtTimeoutStatus( DG_RESERVOIR_ID_T rsrvrID, DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state ); static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T checkRsrvrPartialFillStatus( DG_RESERVOIR_ID_T rsrvrID, DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state ); static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T checkRsrvrMixDrainStatus( DG_RESERVOIR_ID_T rsrvrID, DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state ); static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T checkRsrvrFillStatus( DG_RESERVOIR_ID_T rsrvrID, DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state ); static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T checkRsrvrDrainStatus( DG_RESERVOIR_ID_T rsrvrID, DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state ); /*********************************************************************//** * @brief * The initHeatDisinfectActiveCoolMode function initializes the heat disinfect * active cool mode module. * @details Inputs: none * @details Outputs: heatDisinfectActiveCoolState, stateStartTimeMS, * dataPublishCounter, overallHeatDisinfectActiveCoolTimer, * alarmDetectedPendingTrigger, heatDisinfectActiceCoolPrevState, rsrvrsStatus, * tempBelowTarget, disinfectNVOps * @return none *************************************************************************/ void initHeatDisinfectActiveCoolMode( void ) { heatDisinfectActiveCoolState = DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_START; heatDisinfectActiceCoolPrevState = DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_START; stateStartTimeMS = getMSTimerCount(); dataPublishCounter = 0; overallHeatDisinfectActiveCoolTimer = getMSTimerCount(); alarmDetectedPendingTrigger = ALARM_ID_NO_ALARM; tempBelowTarget.tempBelowTargetStartTimeMS = getMSTimerCount(); tempBelowTarget.hasTempTargetTimeBeenSet = FALSE; cancellationMode = CANCELLATION_MODE_NONE; disinfectNVOps.hasDisStatusBeenWrittenToNV = FALSE; // Initialize the reservoirs rsrvrsStatus.rsrvrFillStableTime = 0; rsrvrsStatus.rsrvrFillStableTimeoutMS = RSRVRS_FULL_STABLE_TIME_COUNT; rsrvrsStatus.isThisInitialDrain = TRUE; rsrvrsStatus.rsrvr[ DG_RESERVOIR_1 ].drainInit = FALSE; rsrvrsStatus.rsrvr[ DG_RESERVOIR_1 ].loadCell = LOAD_CELL_RESERVOIR_1_PRIMARY; rsrvrsStatus.rsrvr[ DG_RESERVOIR_1 ].rStatus = NUM_OF_DG_RESERVOIR_STATUS; rsrvrsStatus.rsrvr[ DG_RESERVOIR_2 ].drainInit = FALSE; rsrvrsStatus.rsrvr[ DG_RESERVOIR_2 ].loadCell = LOAD_CELL_RESERVOIR_2_PRIMARY; rsrvrsStatus.rsrvr[ DG_RESERVOIR_2 ].rStatus = NUM_OF_DG_RESERVOIR_STATUS; // In the active cool mode the heaters are not turned on stopHeater( DG_PRIMARY_HEATER ); stopHeater( DG_TRIMMER_HEATER ); } /*********************************************************************//** * @brief * The transitionToHeatDisinfectActiveCoolMode function prepares for transition to * heat disinfect active cool mode * @details Inputs: none * @details Outputs: none * @return initial state *************************************************************************/ U32 transitionToHeatDisinfectActiveCoolMode( void ) { deenergizeActuators( NO_PARK_CONC_PUMPS ); initHeatDisinfectActiveCoolMode(); setCurrentSubState( NO_SUB_STATE ); setCPLDCleanLEDColor( CPLD_CLEAN_LED_ORANGE ); return heatDisinfectActiveCoolState; } /*********************************************************************//** * @brief * The execHeatDisinfectActiveCoolMode function executes the heat disinfect * active cool mode state machine. * @details Inputs: heatDisinfectActiveCoolState * @details Outputs: heatDisinfectActiveCoolState * @return current state *************************************************************************/ U32 execHeatDisinfectActiveCoolMode( void ) { // The inlet pressure shall be checked all the time as long as VPi is open checkInletWaterPressure(); if ( heatDisinfectActiveCoolState > DG_HEAT_DISINFECT_ACTIVE_COOL_FILL_R1_STATE ) { // Do not check on the inlet water temperature and conductivity until there has been some inlet water flow // The initial states are drain reservoirs but in those states VPi is closed so these alarms are not checked checkInletWaterTemperature(); checkInletWaterConductivity(); } monitorModeHeatDisinfectActiveCool(); switch( heatDisinfectActiveCoolState ) { case DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_START: heatDisinfectActiveCoolState = handleHeatDisinfectActiveCoolStartState(); break; case DG_HEAT_DISINFECT_ACITVE_COOL_MIX_DRAIN_R1_STATE: heatDisinfectActiveCoolState = handleHeatDisinfectActiveCoolMixDrainR1State(); break; case DG_HEAT_DISINFECT_ACTIVE_COOL_MIX_DRAIN_R2_STATE: heatDisinfectActiveCoolState = handleHeatDisinfectActiveCoolMixDrainR2State(); break; case DG_HEAT_DISINFECT_ACTIVE_COOL_FILL_R1_STATE: heatDisinfectActiveCoolState = handleHeatDisinfectActiveCoolFillR1State(); break; case DG_HEAT_DISINFECT_ACTIVE_COOL_FILL_R2_STATE: heatDisinfectActiveCoolState = handleHeatDisinfectActiveCoolFillR2State(); break; case DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R2_FILL_R1_TO_R2_STATE: heatDisinfectActiveCoolState = handleHeatDisinfectActiveCoolDrainR2FillR1ToR2State(); break; case DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R1_FILL_R2_TO_R1_STATE: heatDisinfectActiveCoolState = handleHeatDisinfectActiveCoolDrainR1FillR2ToR1State(); break; case DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R1_STATE: heatDisinfectActiveCoolState = handleHeatDisinfectActiveCoolDrainR1State(); break; case DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R2_STATE: heatDisinfectActiveCoolState = handleHeatDisinfectActiveCoolDrainR2State(); break; case DG_HEAT_DISINFECT_ACTIVE_COOL_CANCEL_WATER_PATH_STATE: heatDisinfectActiveCoolState = handleHeatDisinfectActiveCoolCancelWaterPathState(); break; case DG_HEAT_DISINFECT_ACTIVE_COOL_CANCEL_BASIC_PATH_STATE: heatDisinfectActiveCoolState = handleHeatDisinfectActiveCoolCancelBasicPathState(); break; case DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_COMPLETE: heatDisinfectActiveCoolState = handleHeatDisinfectActiveCoolCompleteState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_HEAT_DISINFECT_ACTIVE_COOL_INVALID_EXEC_STATE, heatDisinfectActiveCoolState ) heatDisinfectActiveCoolState = DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_START; break; } publishHeatDisinfectActiveCoolData(); return heatDisinfectActiveCoolState; } /*********************************************************************//** * @brief * The getCurrentHeatDisinfectActiveCoolState function returns the current * state of the heat disinfect active cool mode. * @details Inputs: heatDisinfectActiveCoolState * @details Outputs: none * @return the current state of heat disinfect active cool mode. *************************************************************************/ DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T getCurrentHeatDisinfectActiveCoolState( void ) { return heatDisinfectActiveCoolState; } /*********************************************************************//** * @brief * The stopDGHeatDisinfectActiveCool function stops heat disinfect active * cool mode. * @details Inputs: none * @details Outputs: none * @return TRUE is current operation mode is heat disinfect active cool, * otherwise FALSE *************************************************************************/ BOOL stopDGHeatDisinfectActiveCool( void ) { BOOL status = FALSE; // Check if the current operation mode is heat disinfect active cool if ( DG_MODE_HCOL == getCurrentOperationMode() ) { // Reset all the actuators deenergizeActuators( PARK_CONC_PUMPS ); // Transition to mode standby requestNewOperationMode( DG_MODE_STAN ); status = TRUE; } return status; } /*********************************************************************//** * @brief * The getDisinfectRsrvrFillStatus function gets the disinfect reservoir * fill status. * @details Inputs: none * @details Outputs: none * @param rsrvrID the reservoir to check * @param *rsrvrStatus pointer to the reservoir status structure * @param targetVolML the fill target for the reservoir in milliliters * @param fillTimeoutMS the timeout in milliseconds that the reservoir should * be filled by then * @param stateTimerMS the time of the state that the reservoir fill is being * requested in milliseconds * @return status of the reservoir fill *************************************************************************/ DG_RESERVOIR_STATUS_T getDisinfectRsrvrFillStatus( DG_RESERVOIR_ID_T rsrvrID, DIS_RSRVR_STATUS_T *rsrvrStatus, F32 targetVolML, U32 fillTimeoutMS, U32 stateTimerMS ) { DG_RESERVOIR_STATUS_T status = DG_RESERVOIR_BELOW_TARGET; F32 volumeML = 0.0F; if ( rsrvrID < NUM_OF_DG_RESERVOIRS ) { volumeML = getLoadCellSmallFilteredWeight( rsrvrStatus->rsrvr[ rsrvrID ].loadCell ); } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_DG_RESERVOIR_SELECTED, rsrvrID ) } if ( volumeML >= targetVolML ) { // If the fill volume is greater than the target volume and the stable time has elapsed, call it reached to target if ( ++rsrvrStatus->rsrvrFillStableTime >= rsrvrStatus->rsrvrFillStableTimeoutMS ) { rsrvrStatus->rsrvrFillStableTime = 0; status = DG_RESERVOIR_REACHED_TARGET; } } else if ( TRUE == didTimeout( stateTimerMS, fillTimeoutMS ) ) { status = DG_RESERVOIR_NOT_REACHED_TARGET; } return status; } /*********************************************************************//** * @brief * The getDisinfectRsrvrDrainStatus function gets the disinfect reservoir * drain status. * @details Inputs: none * @details Outputs: none * @param rsrvrID the reservoir to check * @param *rsrvrStatus pointer to the reservoir status structure * @param drainSteayTimeoutMS the time in milliseconds that the reservoir's level * should not change and stay steady * @param drainStateTimeoutMS the timeout in milliseconds that the reservoir should * be drained by then * @param stateTimerMS the time of the state that the reservoir fill is being * requested in milliseconds * @return status of the reservoir drain *************************************************************************/ DG_RESERVOIR_STATUS_T getDisinfectRsrvrDrainStatus( DG_RESERVOIR_ID_T rsrvrID, DIS_RSRVR_STATUS_T *rsrvrStatus, U32 drainSteadyTimeoutMS, U32 drainStateTimeoutMS, U32 stateTimerMS ) { DG_RESERVOIR_STATUS_T status = DG_RESERVOIR_ABOVE_TARGET; BOOL isDrainComplete = FALSE; if ( rsrvrID >= NUM_OF_DG_RESERVOIRS ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_DG_RESERVOIR_SELECTED, rsrvrID ) } else { if ( FALSE == rsrvrStatus->rsrvr[ rsrvrID ].drainInit ) { initDrainParameters( rsrvrID ); rsrvrStatus->rsrvr[ rsrvrID ].drainInit = TRUE; } // NOTE: the drain status should be checked once the reservoirs parameters are initialized. This is to make sure the // the timers for stable drain time are initialized prior to using them again isDrainComplete = hasTargetDrainToZeroBeenReached( rsrvrID, drainSteadyTimeoutMS ); if ( TRUE == isDrainComplete ) { rsrvrStatus->rsrvr[ rsrvrID ].drainInit = FALSE; status = DG_RESERVOIR_REACHED_TARGET; } else if ( TRUE == didTimeout( stateTimerMS, drainStateTimeoutMS ) ) { rsrvrStatus->rsrvr[ rsrvrID ].drainInit = FALSE; status = DG_RESERVOIR_NOT_REACHED_TARGET; } } return status; } // ********** private functions ********** /*********************************************************************//** * @brief * The handleHeatDisinfectActiveCoolStartState function handles the heat * disinfect active cool start state. * @details Inputs: none * @details Outputs: stateStartTimeMS, rsrvrsStatus * @return next state of the heat disinfect active cool state machine *************************************************************************/ static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolStartState( void ) { DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state = DG_HEAT_DISINFECT_ACITVE_COOL_MIX_DRAIN_R1_STATE; stateStartTimeMS = getMSTimerCount(); rsrvrsStatus.rsrvr[ DG_RESERVOIR_1 ].rStatus = DG_RESERVOIR_ABOVE_TARGET; return state; } /*********************************************************************//** * @brief * The handleHeatDisinfectActiveCoolMixDrainR1State function handles the heat * disinfect active cool mix drain R1 state. * @details Inputs: none * @details Outputs: none * @return next state of the heat disinfect active cool state machine *************************************************************************/ static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolMixDrainR1State( void ) { DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state = DG_HEAT_DISINFECT_ACITVE_COOL_MIX_DRAIN_R1_STATE; state = checkRsrvrMixDrainStatus( DG_RESERVOIR_1, state ); return state; } /*********************************************************************//** * @brief * The handleHeatDisinfectActiveCoolMixDrainR2State function handles the heat * disinfect active cool mix drain R2 state. * @details Inputs: none * @details Outputs: tempBelowTarget * @return next state of the heat disinfect active cool state machine *************************************************************************/ static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolMixDrainR2State( void ) { DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state = DG_HEAT_DISINFECT_ACTIVE_COOL_MIX_DRAIN_R2_STATE; tempBelowTarget.tempBelowTargetStartTimeMS = getMSTimerCount(); tempBelowTarget.hasTempTargetTimeBeenSet = FALSE; state = checkRsrvrMixDrainStatus( DG_RESERVOIR_2, state ); return state; } /*********************************************************************//** * @brief * The handleHeatDisinfectActiveCoolFillR1State function handles the heat * disinfect active cool fill R1 state. * @details Inputs: none * @details Outputs: none * @return next state of the heat disinfect active cool state machine *************************************************************************/ static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolFillR1State( void ) { DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state = DG_HEAT_DISINFECT_ACTIVE_COOL_FILL_R1_STATE; state = checkRsrvrPartialFillStatus( DG_RESERVOIR_1, state ); return state; } /*********************************************************************//** * @brief * The handleHeatDisinfectActiveCoolFillR2State function handles the heat * disinfect active cool fill R2 state. * @details Inputs: none * @details Outputs: none * @return next state of the heat disinfect active cool state machine *************************************************************************/ static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolFillR2State( void ) { DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state = DG_HEAT_DISINFECT_ACTIVE_COOL_FILL_R2_STATE; state = checkRsrvrPartialFillStatus( DG_RESERVOIR_2, state ); return state; } /*********************************************************************//** * @brief * The handleHeatDisinfectActiveCoolDrainR2FillR1ToR2State function handles * the heat disinfect active cool drain R2 and fill R1 to R2 state. * @details Inputs: none * @details Outputs: none * @return next state of the heat disinfect active cool state machine *************************************************************************/ static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolDrainR2FillR1ToR2State( void ) { DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state = DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R2_FILL_R1_TO_R2_STATE; state = checkRsrvrFillStatus( DG_RESERVOIR_1, state ); return state; } /*********************************************************************//** * @brief * The handleHeatDisinfectActiveCoolDrainR1FillR2ToR1State function handles * the heat disinfect active cool drain R1 and fill R2 to R1 state. * @details Inputs: none * @details Outputs: none * @return next state of the heat disinfect active cool state machine *************************************************************************/ static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolDrainR1FillR2ToR1State( void ) { DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state = DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R1_FILL_R2_TO_R1_STATE; state = checkRsrvrFillStatus( DG_RESERVOIR_2, state ); return state; } /*********************************************************************//** * @brief * The handleHeatDisinfectActiveCoolDrainR1State function handles the heat * disinfect active cool drain R1 state. * @details Inputs: none * @details Outputs: none * @return next state of the heat disinfect active cool state machine *************************************************************************/ static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolDrainR1State( void ) { DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state = DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R1_STATE; state = checkRsrvrDrainStatus( DG_RESERVOIR_1, state ); return state; } /*********************************************************************//** * @brief * The handleHeatDisinfectActiveCoolDrainR1State function handles the heat * disinfect active cool drain R2 state. * @details Inputs: none * @details Outputs: none * @return next state of the heat disinfect active cool state machine *************************************************************************/ static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolDrainR2State( void ) { DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state = DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R2_STATE; state = checkRsrvrDrainStatus( DG_RESERVOIR_2, state ); return state; } /*********************************************************************//** * @brief * The handleHeatDisinfectActiveCoolCompleteState function handles the * heat disinfect active cool complete state. * @details Inputs: none * @details Outputs: none * @return next state of the heat disinfect active cool state machine *************************************************************************/ static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolCompleteState( void ) { DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state = DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_COMPLETE; writeDisinfectDataToNV( USAGE_INFO_HEAT_DIS_ACTIVE_COOL ); if ( TRUE == disinfectNVOps.hasDisStatusBeenWrittenToNV ) { stopDGHeatDisinfectActiveCool(); } return state; } /*********************************************************************//** * @brief * The handleHeatDisinfectActiveCoolCancelWaterPathState function handles * the heat disinfect active cool cancel water path state. * @details Inputs: cancellationMode, rsrvrsStatus, stateStartTimeMS * @details Outputs: cancellationMode, rsrvrsStatus, stateStartTimeMS * @return next state of the heat disinfect active cool state machine *************************************************************************/ static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolCancelWaterPathState( void ) { DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state = DG_HEAT_DISINFECT_ACTIVE_COOL_CANCEL_WATER_PATH_STATE; F32 TDi = getTemperatureValue( TEMPSENSORS_INLET_DIALYSATE ); F32 TRo = getTemperatureValue( TEMPSENSORS_OUTLET_REDUNDANT ); BOOL isMixDrainNeeded = ( ( TDi >= RSRVR_MIX_DRAIN_TEMPERATURE_THRESHOLD_C ) && ( TRo >= RSRVR_MIX_DRAIN_TEMPERATURE_THRESHOLD_C ) ? TRUE : FALSE ); U32 drainTimeoutMS = ( TRUE == isMixDrainNeeded ? RSRVR_MIX_DRAIN_TIMEOUT_MS : RSRVR_DRAIN_TIMEOUT_MS ); U32 drainSteadyTimeoutMS = ( TRUE == isMixDrainNeeded ? ROF_ACTIVE_COOL_MIX_DRAIN_STEADY_TIMEOUT_MS : RSRVR_DRAIN_STEADY_TIMEOUT_MS ); if ( CANCELLATION_MODE_NONE == cancellationMode ) { U32 targetRPM = RSRVR_DRAIN_TARGET_RPM; cancellationMode = CANCELLATION_MODE_COLD; // The two sensors must be less than a threshold to decide if mix drain is needed to normal drain if ( TRUE == isMixDrainNeeded ) { // 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_NO ); turnOnUVReactor( INLET_UV_REACTOR ); targetRPM = ROF_ACTIVE_COOL_INITIAL_TARGET_DRAIN_RPM; cancellationMode = CANCELLATION_MODE_HOT; } rsrvrsStatus.rsrvr[ DG_RESERVOIR_1 ].rStatus = DG_RESERVOIR_ABOVE_TARGET; rsrvrsStatus.rsrvr[ DG_RESERVOIR_2 ].rStatus = DG_RESERVOIR_ABOVE_TARGET; // 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 ); // Start the timer for drain timeout stateStartTimeMS = getMSTimerCount(); } // If reservoir 2 is empty, set to drain reservoir 1 if ( DG_RESERVOIR_ABOVE_TARGET == rsrvrsStatus.rsrvr[ DG_RESERVOIR_2 ].rStatus ) { // If the cancellation water path cannot be done, got to basic cancellation path rsrvrsStatus.rsrvr[ DG_RESERVOIR_2 ].rStatus = getDisinfectRsrvrDrainStatus( DG_RESERVOIR_2, &rsrvrsStatus, drainSteadyTimeoutMS, drainTimeoutMS, stateStartTimeMS ); if ( DG_RESERVOIR_REACHED_TARGET == rsrvrsStatus.rsrvr[ DG_RESERVOIR_2 ].rStatus ) { // Reset the state timer for the next reservoir to drain stateStartTimeMS = getMSTimerCount(); // 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 == rsrvrsStatus.rsrvr[ DG_RESERVOIR_2 ].rStatus ) { // Stop the actuators that are running before going to basic cancellation path setValveState( VRD1, VALVE_STATE_CLOSED ); setValveState( VRD2, VALVE_STATE_CLOSED ); signalDrainPumpHardStop(); alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_DRAIN_TIMEOUT; state = DG_HEAT_DISINFECT_ACTIVE_COOL_CANCEL_BASIC_PATH_STATE; } // If reservoir 2 has already been drained and reservoir 1 is empty, reset and switch to complete if ( ( DG_RESERVOIR_REACHED_TARGET == rsrvrsStatus.rsrvr[ DG_RESERVOIR_2 ].rStatus ) && ( DG_RESERVOIR_ABOVE_TARGET == rsrvrsStatus.rsrvr[ DG_RESERVOIR_1 ].rStatus ) ) { // If the cancellation water path cannot be done, got to basic cancellation path rsrvrsStatus.rsrvr[ DG_RESERVOIR_1 ].rStatus = getDisinfectRsrvrDrainStatus( DG_RESERVOIR_1, &rsrvrsStatus, drainSteadyTimeoutMS, drainTimeoutMS, stateStartTimeMS ); if ( DG_RESERVOIR_REACHED_TARGET == rsrvrsStatus.rsrvr[ DG_RESERVOIR_1 ].rStatus ) { setValveState( VRD1, VALVE_STATE_CLOSED ); setValveState( VRD2, VALVE_STATE_CLOSED ); signalDrainPumpHardStop(); failHeatDisinfectActiveCool(); } } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvrsStatus.rsrvr[ DG_RESERVOIR_1 ].rStatus ) { setValveState( VRD1, VALVE_STATE_CLOSED ); setValveState( VRD2, VALVE_STATE_CLOSED ); signalDrainPumpHardStop(); alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_DRAIN_TIMEOUT; state = DG_HEAT_DISINFECT_ACTIVE_COOL_CANCEL_BASIC_PATH_STATE; } return state; } /*********************************************************************//** * @brief * The handleHeatDisinfectActiveCoolCancelBasicPathState function handles the * heat disinfect active cool cancel mode basic path state. The state sets * the state to complete and raises an alarm. * @details Inputs: none * @details Outputs: none * @return next state of the heat disinfect active cool state machine *************************************************************************/ static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T handleHeatDisinfectActiveCoolCancelBasicPathState( void ) { DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state = DG_HEAT_DISINFECT_ACTIVE_COOL_CANCEL_BASIC_PATH_STATE; failHeatDisinfectActiveCool(); return state; } /*********************************************************************//** * @brief * The failHeatDisinfect function sets the alarm that failed the heat * disinfect mode. * @details Inputs: alarmDetectedPendingTrigger, prevHeatDisinfectState * @details Outputs: none * @return none *************************************************************************/ static void failHeatDisinfectActiveCool( void ) { // If a fault alarm is active go to mode fault otherwise for cleaning mode alarms, transition to standby DG_OP_MODE_T nextOpMode = ( FALSE == isDGFaultAlarmActive() ? DG_MODE_STAN : DG_MODE_FAUL ); // In the cleaning modes the alarms are triggered but the mode is not transitioned to fault automatically // so transition to fault mode is done here if ( alarmDetectedPendingTrigger != ALARM_ID_NO_ALARM ) { SET_ALARM_WITH_1_U32_DATA( alarmDetectedPendingTrigger, heatDisinfectActiceCoolPrevState ) } requestNewOperationMode( nextOpMode ); } /*********************************************************************//** * @brief * The publishHeatDisinfectActiveCoolData function publishes heat disinfect * active cool data at the set interval. * @details Inputs: dataPublishCounter * @details Outputs: dataPublishCounter * @return: none *************************************************************************/ static void publishHeatDisinfectActiveCoolData( void ) { if ( ++dataPublishCounter >= HEAT_DISINFECT_ACTIVE_COOL_DATA_PUB_INTERVAL ) { MODE_HEAT_DISINFECT_ACTIVE_COOL_DATA_T data; data.heatDisinfectActivCoolState = (U32)heatDisinfectActiveCoolState; data.overallElapsedTime = calcTimeSince( overallHeatDisinfectActiveCoolTimer ); data.stateElapsedTime = calcTimeSince( stateStartTimeMS ); data.cancellationMode = 0; broadcastData( MSG_ID_DG_HEAT_DISINFECT_ACTIVE_COOL_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&data, sizeof( MODE_HEAT_DISINFECT_ACTIVE_COOL_DATA_T ) ); dataPublishCounter = 0; } } /*********************************************************************//** * @brief * The monitorModeHeatDisinfectActiveCool function monitors the status of * the caps and sets the state of the state machine to water cancellation * path if the caps are not closed during the run. * @details Inputs: none * @details Outputs: heatDisinfectActiceCoolPrevState, * heatDisinfectActiceCoolPrevState, alarmDetectedPendingTrigger * @return: none *************************************************************************/ static void monitorModeHeatDisinfectActiveCool( void ) { #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_CAPS_MONITOR ) != SW_CONFIG_ENABLE_VALUE ) #endif { if ( ( STATE_OPEN == getSwitchStatus( CONCENTRATE_CAP ) ) || ( STATE_OPEN == getSwitchStatus( DIALYSATE_CAP ) ) ) { if ( ( heatDisinfectActiveCoolState != DG_HEAT_DISINFECT_ACTIVE_COOL_CANCEL_WATER_PATH_STATE ) && ( heatDisinfectActiveCoolState != DG_HEAT_DISINFECT_ACTIVE_COOL_CANCEL_BASIC_PATH_STATE ) ) { // 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 heatDisinfectActiceCoolPrevState = heatDisinfectActiveCoolState; heatDisinfectActiveCoolState = DG_HEAT_DISINFECT_ACTIVE_COOL_CANCEL_WATER_PATH_STATE; 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; } } } } if ( ( TRUE == isDGFaultAlarmActive() ) || ( TRUE == isAnyCleaningModeInletWaterConditionActive() ) ) { if ( heatDisinfectActiveCoolState != DG_HEAT_DISINFECT_ACTIVE_COOL_CANCEL_WATER_PATH_STATE ) { // If there is any fault alarm and we are not already in the cancel water path state, set it to cancel water path state heatDisinfectActiceCoolPrevState = heatDisinfectActiveCoolState; heatDisinfectActiveCoolState = DG_HEAT_DISINFECT_ACTIVE_COOL_CANCEL_WATER_PATH_STATE; setHeatDisinfectActiveCoolActuators( heatDisinfectActiveCoolState ); } } } /*********************************************************************//** * @brief * The setHeatDisinfectActiveCoolActuators function sets all the actuators * of the provided state. * @details Inputs: none * @details Outputs: none * @param state the state of the heat disinfect active cool mode * @return: none *************************************************************************/ static void setHeatDisinfectActiveCoolActuators( DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state ) { switch ( state ) { case DG_HEAT_DISINFECT_ACITVE_COOL_MIX_DRAIN_R1_STATE: { // Valves setValveState( VPI, VALVE_STATE_OPEN ); setValveState( VBF, VALVE_STATE_CLOSED ); setValveState( VSP, VALVE_STATE_CLOSED ); setValveState( VPD, VALVE_STATE_DRAIN_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( VRO, VALVE_STATE_R1_C_TO_NO ); setValveState( VRD1, VALVE_STATE_OPEN ); setValveState( VRD2, VALVE_STATE_CLOSED ); setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); // UV reactors turnOnUVReactor( INLET_UV_REACTOR ); turnOffUVReactor( OUTLET_UV_REACTOR ); // Concentrate pumps requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID, NO_PARK_CONC_PUMPS ); requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB, NO_PARK_CONC_PUMPS ); // RO pump signalROPumpHardStop(); // Drain pump U32 rpm = ( TRUE == rsrvrsStatus.isThisInitialDrain ? ROF_ACTIVE_COOL_INITIAL_TARGET_DRAIN_RPM : ROF_ACTIVE_COOL_TARGET_DARIN_RPM ); setDrainPumpTargetRPM( rpm ); } break; case DG_HEAT_DISINFECT_ACTIVE_COOL_MIX_DRAIN_R2_STATE: { // Valves setValveState( VPI, VALVE_STATE_OPEN ); setValveState( VBF, VALVE_STATE_CLOSED ); setValveState( VSP, VALVE_STATE_CLOSED ); setValveState( VPD, VALVE_STATE_DRAIN_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( VRO, VALVE_STATE_R1_C_TO_NO ); setValveState( VRD1, VALVE_STATE_CLOSED ); setValveState( VRD2, VALVE_STATE_OPEN ); setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); // UV reactors turnOnUVReactor( INLET_UV_REACTOR ); turnOffUVReactor( OUTLET_UV_REACTOR ); // Concentrate pumps requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID, NO_PARK_CONC_PUMPS ); requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB, NO_PARK_CONC_PUMPS ); // RO pump signalROPumpHardStop(); // Drain pump U32 rpm = ( TRUE == rsrvrsStatus.isThisInitialDrain ? ROF_ACTIVE_COOL_INITIAL_TARGET_DRAIN_RPM : ROF_ACTIVE_COOL_TARGET_DARIN_RPM ); setDrainPumpTargetRPM( rpm ); } break; case DG_HEAT_DISINFECT_ACTIVE_COOL_FILL_R1_STATE: // Valves setValveState( VPI, VALVE_STATE_OPEN ); setValveState( VBF, VALVE_STATE_CLOSED ); setValveState( VSP, VALVE_STATE_CLOSED ); setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VRC, VALVE_STATE_RECIRC_C_TO_NC ); setValveState( VRO, VALVE_STATE_R2_C_TO_NC ); setValveState( VRD1, VALVE_STATE_CLOSED ); setValveState( VRD2, VALVE_STATE_CLOSED ); setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); // UV reactors turnOnUVReactor( INLET_UV_REACTOR ); turnOffUVReactor( OUTLET_UV_REACTOR ); // Concentrate pumps setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, ACID_PUMP_SPEED_ML_PER_MIN ); setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, BICARB_PUMP_SPEED_ML_PER_MIN ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); // RO pump setROPumpTargetFlowRateLPM( ROF_ACTIVE_COOL_TARGET_FLOW_LPM, CLEANING_MODE_HIGH_TEMP_MAX_RO_PRESSURE_PSI ); // Drain pump signalDrainPumpHardStop(); break; case DG_HEAT_DISINFECT_ACTIVE_COOL_FILL_R2_STATE: // Valves setValveState( VPI, VALVE_STATE_OPEN ); setValveState( VBF, VALVE_STATE_CLOSED ); setValveState( VSP, VALVE_STATE_CLOSED ); setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VRC, VALVE_STATE_RECIRC_C_TO_NC ); setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); setValveState( VRD1, VALVE_STATE_CLOSED ); setValveState( VRD2, VALVE_STATE_CLOSED ); setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); // UV reactors turnOnUVReactor( INLET_UV_REACTOR ); turnOffUVReactor( OUTLET_UV_REACTOR ); // Concentrate pumps setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, ACID_PUMP_SPEED_ML_PER_MIN ); setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, BICARB_PUMP_SPEED_ML_PER_MIN ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); // RO pump setROPumpTargetFlowRateLPM( ROF_ACTIVE_COOL_TARGET_FLOW_LPM, CLEANING_MODE_HIGH_TEMP_MAX_RO_PRESSURE_PSI ); // Drain pump signalDrainPumpHardStop(); break; case DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R2_FILL_R1_TO_R2_STATE: // Valves setValveState( VPI, VALVE_STATE_OPEN ); setValveState( VBF, VALVE_STATE_CLOSED ); setValveState( VSP, VALVE_STATE_CLOSED ); setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); 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 ); setValveState( VRD1, VALVE_STATE_CLOSED ); setValveState( VRD2, VALVE_STATE_OPEN ); setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); // UV reactors turnOnUVReactor( INLET_UV_REACTOR ); turnOffUVReactor( OUTLET_UV_REACTOR ); // Concentrate pumps setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, ACID_PUMP_SPEED_ML_PER_MIN ); setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, BICARB_PUMP_SPEED_ML_PER_MIN ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); // RO pump setROPumpTargetFlowRateLPM( RSRVR_ACTIVE_COOL_TARGET_FLOW_LPM, RSRVR_ACTIVE_COOL_MAX_ALLOWED_PRESSURE_PSI ); // Drain pump setDrainPumpTargetRPM( RSRVR_DRAIN_TARGET_RPM ); break; case DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R1_FILL_R2_TO_R1_STATE: // Valves setValveState( VPI, VALVE_STATE_OPEN ); setValveState( VBF, VALVE_STATE_CLOSED ); setValveState( VSP, VALVE_STATE_CLOSED ); setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); 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_R2_C_TO_NC ); setValveState( VRD1, VALVE_STATE_OPEN ); setValveState( VRD2, VALVE_STATE_CLOSED ); setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); // UV reactors turnOnUVReactor( INLET_UV_REACTOR ); turnOffUVReactor( OUTLET_UV_REACTOR ); // Concentrate pumps setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, ACID_PUMP_SPEED_ML_PER_MIN ); setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, BICARB_PUMP_SPEED_ML_PER_MIN ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); // RO pump setROPumpTargetFlowRateLPM( RSRVR_ACTIVE_COOL_TARGET_FLOW_LPM, RSRVR_ACTIVE_COOL_MAX_ALLOWED_PRESSURE_PSI ); // Drain pump setDrainPumpTargetRPM( RSRVR_DRAIN_TARGET_RPM ); break; case DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R1_STATE: // Valves setValveState( VPI, VALVE_STATE_CLOSED ); setValveState( VBF, VALVE_STATE_CLOSED ); setValveState( VSP, VALVE_STATE_CLOSED ); setValveState( VPD, VALVE_STATE_DRAIN_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( VRO, VALVE_STATE_R1_C_TO_NO ); setValveState( VRD1, VALVE_STATE_OPEN ); setValveState( VRD2, VALVE_STATE_CLOSED ); setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); // UV reactors turnOffUVReactor( INLET_UV_REACTOR ); turnOffUVReactor( OUTLET_UV_REACTOR ); // Concentrate pumps requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID , NO_PARK_CONC_PUMPS ); requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB , NO_PARK_CONC_PUMPS ); // RO pump signalROPumpHardStop(); // Drain pump setDrainPumpTargetRPM( RSRVR_DRAIN_TARGET_RPM ); break; case DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R2_STATE: // Valves setValveState( VPI, VALVE_STATE_CLOSED ); setValveState( VBF, VALVE_STATE_CLOSED ); setValveState( VSP, VALVE_STATE_CLOSED ); setValveState( VPD, VALVE_STATE_DRAIN_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( VRO, VALVE_STATE_R1_C_TO_NO ); setValveState( VRD1, VALVE_STATE_CLOSED ); setValveState( VRD2, VALVE_STATE_OPEN ); setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); // UV reactors turnOffUVReactor( INLET_UV_REACTOR ); turnOffUVReactor( OUTLET_UV_REACTOR ); // Concentrate pumps requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID, NO_PARK_CONC_PUMPS ); requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB, NO_PARK_CONC_PUMPS ); // RO pump signalROPumpHardStop(); // Drain pump setDrainPumpTargetRPM( RSRVR_DRAIN_TARGET_RPM ); break; case DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_START: case DG_HEAT_DISINFECT_ACTIVE_COOL_CANCEL_WATER_PATH_STATE: case DG_HEAT_DISINFECT_ACTIVE_COOL_CANCEL_BASIC_PATH_STATE: case DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_COMPLETE: // De-energize the actuators in the state upon transitioning to these states deenergizeActuators( NO_PARK_CONC_PUMPS ); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_HEAT_DISINFECT_ACTIVE_COOL_INVALID_EXEC_STATE, state ) break; } } /*********************************************************************//** * @brief * The writeDisinfectDataToNV function writes the disinfection data to the * non-volatile memory. * @details Inputs: disinfectNVOps * @details Outputs: disinfectNVOps * @param info the type disinfect data to write to the memory (i.e. heat * disinfect start time) * @return: none *************************************************************************/ static void writeDisinfectDataToNV( DG_USAGE_INFO_ITEMS_T info ) { if ( FALSE == disinfectNVOps.hasDisStatusBeenWrittenToNV ) { disinfectNVOps.hasDisStatusBeenWrittenToNV = setLastDisinfectDate( info, getRTCTimestamp() ); } } /*********************************************************************//** * @brief * The checkRsrvrMgmtTimeoutStatus function checks and manages the status * of reservoir management timeout status * @details Inputs: rsrvrsStatus * @details Outputs: heatDisinfectActiceCoolPrevState * @param rsrvrID the reservoir ID to check the status of the timeout * @param state the state of the heat disinfect active cool mode * @return: state of the heat disinfect active cool mode *************************************************************************/ static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T checkRsrvrMgmtTimeoutStatus( DG_RESERVOIR_ID_T rsrvrID, DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state ) { if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvrsStatus.rsrvr[ rsrvrID ].rStatus ) { switch ( state ) { case DG_HEAT_DISINFECT_ACTIVE_COOL_FILL_R1_STATE: case DG_HEAT_DISINFECT_ACTIVE_COOL_FILL_R2_STATE: case DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R2_FILL_R1_TO_R2_STATE: case DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R1_FILL_R2_TO_R1_STATE: alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_FILL_TIMEOUT; break; case DG_HEAT_DISINFECT_ACITVE_COOL_MIX_DRAIN_R1_STATE: case DG_HEAT_DISINFECT_ACTIVE_COOL_MIX_DRAIN_R2_STATE: case DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R1_STATE: case DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R2_STATE: alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_DRAIN_TIMEOUT; break; default: // Do nothing break; } heatDisinfectActiceCoolPrevState = state; state = DG_HEAT_DISINFECT_ACTIVE_COOL_CANCEL_WATER_PATH_STATE; setHeatDisinfectActiveCoolActuators( state ); } return state; } /*********************************************************************//** * @brief * The checkRsrvrPartialFillStatus function checks and manages the status * of the partial fill of a reservoir * @details Inputs: tempBelowTarget, rsrvrsStatus, stateStartTimeMS * @details Outputs: tempBelowTarget, rsrvrsStatus, stateStartTimeMS * @param rsrvrID the reservoir ID to check the status of the timeout * @param state the state of the heat disinfect active cool mode * @return: state of the heat disinfect active cool mode *************************************************************************/ static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T checkRsrvrPartialFillStatus( DG_RESERVOIR_ID_T rsrvrID, DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state ) { F32 THdTemperatureC = getTemperatureValue( TEMPSENSORS_HEAT_DISINFECT ); if ( THdTemperatureC <= ROF_ACTIVE_COOL_TARGET_TEMP_C ) { // If THd is below the target and the timer for stable temperature has not been set, set it if ( FALSE == tempBelowTarget.hasTempTargetTimeBeenSet ) { tempBelowTarget.tempBelowTargetStartTimeMS = getMSTimerCount(); tempBelowTarget.hasTempTargetTimeBeenSet = TRUE; } } else { // The temperature is still above the target so keep reseting the timer tempBelowTarget.tempBelowTargetStartTimeMS = getMSTimerCount(); } if ( DG_RESERVOIR_BELOW_TARGET == rsrvrsStatus.rsrvr[ rsrvrID ].rStatus ) { rsrvrsStatus.rsrvr[ rsrvrID ].rStatus = getDisinfectRsrvrFillStatus( rsrvrID, &rsrvrsStatus, ROF_ACTIVE_COOL_TARGET_RSRVR_FILL_ML, RSRVR_FILL_TIMEOUT_MS, stateStartTimeMS ); } else if ( DG_RESERVOIR_REACHED_TARGET == rsrvrsStatus.rsrvr[ rsrvrID ].rStatus ) { if ( TRUE == didTimeout( tempBelowTarget.tempBelowTargetStartTimeMS, ROF_ACTIVE_COOL_BELOW_TEMP_TIMEOUT_MS ) ) { stateStartTimeMS = getMSTimerCount(); tempBelowTarget.hasTempTargetTimeBeenSet = FALSE; // If THd temperature has been below the target temperature, transition to cooling the connection paths in between the two reservoirs if ( DG_HEAT_DISINFECT_ACTIVE_COOL_FILL_R1_STATE == state ) { rsrvrsStatus.rsrvr[ DG_RESERVOIR_1 ].rStatus = DG_RESERVOIR_BELOW_TARGET; rsrvrsStatus.rsrvr[ DG_RESERVOIR_2 ].rStatus = DG_RESERVOIR_ABOVE_TARGET; state = DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R2_FILL_R1_TO_R2_STATE; } else if ( DG_HEAT_DISINFECT_ACTIVE_COOL_FILL_R2_STATE == state ) { rsrvrsStatus.rsrvr[ DG_RESERVOIR_1 ].rStatus = DG_RESERVOIR_ABOVE_TARGET; rsrvrsStatus.rsrvr[ DG_RESERVOIR_2 ].rStatus = DG_RESERVOIR_BELOW_TARGET; state = DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R1_FILL_R2_TO_R1_STATE; } setHeatDisinfectActiveCoolActuators( state ); } else { deenergizeActuators( NO_PARK_CONC_PUMPS ); rsrvrsStatus.rsrvr[ rsrvrID ].rStatus = DG_RESERVOIR_ABOVE_TARGET; stateStartTimeMS = getMSTimerCount(); // If the temperature after fill is not below the target temperature, transition to mix drain since the fluid is considered // as hot if ( DG_HEAT_DISINFECT_ACTIVE_COOL_FILL_R1_STATE == state ) { state = DG_HEAT_DISINFECT_ACITVE_COOL_MIX_DRAIN_R1_STATE; } else if ( DG_HEAT_DISINFECT_ACTIVE_COOL_FILL_R2_STATE == state ) { state = DG_HEAT_DISINFECT_ACTIVE_COOL_MIX_DRAIN_R2_STATE; } } } state = checkRsrvrMgmtTimeoutStatus( rsrvrID, state ); return state; } /*********************************************************************//** * @brief * The checkRsrvrMixDrainStatus function checks and manages the status * of the mix drain of a reservoir * @details Inputs: rsrvrsStatus, stateStartTimeMS * @details Outputs: rsrvrsStatus, stateStartTimeMS * @param rsrvrID the reservoir ID to check the status of the timeout * @param state the state of the heat disinfect active cool mode * @return: state of the heat disinfect active cool mode *************************************************************************/ static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T checkRsrvrMixDrainStatus( DG_RESERVOIR_ID_T rsrvrID, DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state ) { if ( ( TRUE == didTimeout( stateStartTimeMS, ROF_ACTIVE_COOL_MIX_DRAIN_VALVE_ON_TIMEOUT_MS ) ) && ( FALSE == isDrainPumpOn() ) ) { setHeatDisinfectActiveCoolActuators( state ); } else if ( ( DG_RESERVOIR_ABOVE_TARGET == rsrvrsStatus.rsrvr[ rsrvrID ].rStatus ) && ( TRUE == isDrainPumpOn() ) ) { rsrvrsStatus.rsrvr[ rsrvrID ].rStatus = getDisinfectRsrvrDrainStatus( rsrvrID, &rsrvrsStatus, ROF_ACTIVE_COOL_MIX_DRAIN_STEADY_TIMEOUT_MS, ROF_ACTIVE_COOL_RSRVR_MIX_DRAIN_TIMEOUT_MS, stateStartTimeMS ); } else if ( DG_RESERVOIR_REACHED_TARGET == rsrvrsStatus.rsrvr[ rsrvrID ].rStatus ) { DG_RESERVOIR_ID_T switchRsrvrID = ( DG_RESERVOIR_1 == rsrvrID ? DG_RESERVOIR_2 : DG_RESERVOIR_1 ); if ( TRUE == rsrvrsStatus.isThisInitialDrain ) { // If this is the initial drain of the mode, tare the reservoirs and get ready for the mix drain if ( DG_HEAT_DISINFECT_ACITVE_COOL_MIX_DRAIN_R1_STATE == state ) { tareLoadCell( LOAD_CELL_RESERVOIR_1_PRIMARY ); tareLoadCell( LOAD_CELL_RESERVOIR_1_BACKUP ); deenergizeActuators( NO_PARK_CONC_PUMPS ); rsrvrsStatus.rsrvr[ switchRsrvrID ].rStatus = DG_RESERVOIR_ABOVE_TARGET; state = DG_HEAT_DISINFECT_ACTIVE_COOL_MIX_DRAIN_R2_STATE; } else if ( DG_HEAT_DISINFECT_ACTIVE_COOL_MIX_DRAIN_R2_STATE == state ) { tareLoadCell( LOAD_CELL_RESERVOIR_2_PRIMARY ); tareLoadCell( LOAD_CELL_RESERVOIR_2_BACKUP ); rsrvrsStatus.rsrvr[ switchRsrvrID ].rStatus = DG_RESERVOIR_BELOW_TARGET; rsrvrsStatus.isThisInitialDrain = FALSE; state = DG_HEAT_DISINFECT_ACTIVE_COOL_FILL_R1_STATE; setHeatDisinfectActiveCoolActuators( state ); } } else { rsrvrsStatus.rsrvr[ switchRsrvrID ].rStatus = DG_RESERVOIR_BELOW_TARGET; // If state are either of the mix drain reservoir states, transition the the fill state. // If the mix drain is R1 transition to fill R2 and vice versa if ( DG_HEAT_DISINFECT_ACITVE_COOL_MIX_DRAIN_R1_STATE == state ) { state = DG_HEAT_DISINFECT_ACTIVE_COOL_FILL_R2_STATE; } else if ( DG_HEAT_DISINFECT_ACTIVE_COOL_MIX_DRAIN_R2_STATE == state ) { state = DG_HEAT_DISINFECT_ACTIVE_COOL_FILL_R1_STATE; } setHeatDisinfectActiveCoolActuators( state ); } stateStartTimeMS = getMSTimerCount(); } state = checkRsrvrMgmtTimeoutStatus( rsrvrID, state ); return state; } /*********************************************************************//** * @brief * The checkRsrvrFillStatus function checks and manages the status * of the a reservoir's fill * @details Inputs: rsrvrsStatus, stateStartTimeMS, tempBelowTarget * @details Outputs: rsrvrsStatus, stateStartTimeMS, tempBelowTarget * @param rsrvrID the reservoir ID to check the status of the timeout * @param state the state of the heat disinfect active cool mode * @return: state of the heat disinfect active cool mode *************************************************************************/ static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T checkRsrvrFillStatus( DG_RESERVOIR_ID_T rsrvrID, DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state ) { if ( DG_RESERVOIR_BELOW_TARGET == rsrvrsStatus.rsrvr[ rsrvrID ].rStatus ) { rsrvrsStatus.rsrvr[ rsrvrID ].rStatus = getDisinfectRsrvrFillStatus( rsrvrID, &rsrvrsStatus, RSRVR_ACTIVE_COOL_FILL_TARGET_FILL_ML, RSRVR_FILL_TIMEOUT_MS, stateStartTimeMS ); } else if ( DG_RESERVOIR_REACHED_TARGET == rsrvrsStatus.rsrvr[ rsrvrID ].rStatus ) { // once the reservoir is filled check for the temperature below target F32 TDiTemperatureC = getTemperatureValue( TEMPSENSORS_INLET_DIALYSATE ); if ( ( TDiTemperatureC < RSRVR_ACTIVE_COOL_TARGET_TEMP_C ) && ( FALSE == tempBelowTarget.hasTempTargetTimeBeenSet ) ) { // Keep setting the time until the temperature is below the target tempBelowTarget.tempBelowTargetStartTimeMS = getMSTimerCount(); tempBelowTarget.hasTempTargetTimeBeenSet = TRUE; } // Keep waiting until the TDi temperature is below the target temperature for the specified time if ( ( TRUE == tempBelowTarget.hasTempTargetTimeBeenSet ) && ( TRUE == didTimeout( tempBelowTarget.tempBelowTargetStartTimeMS, RSRVR_ACTIVE_COOL_BELOW_TARGET_TEMP_TIMEOUT_MS ) ) ) { stateStartTimeMS = getMSTimerCount(); tempBelowTarget.hasTempTargetTimeBeenSet = FALSE; // Once the TDi has been below the specified temperature for the specified time, transition to the next state per the current state if ( DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R2_FILL_R1_TO_R2_STATE == state ) { rsrvrsStatus.rsrvr[ DG_RESERVOIR_1 ].rStatus = DG_RESERVOIR_REACHED_TARGET; rsrvrsStatus.rsrvr[ DG_RESERVOIR_2 ].rStatus = DG_RESERVOIR_BELOW_TARGET; state = DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R1_FILL_R2_TO_R1_STATE; } else if ( DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R1_FILL_R2_TO_R1_STATE == state ) { rsrvrsStatus.rsrvr[ DG_RESERVOIR_1 ].rStatus = DG_RESERVOIR_ABOVE_TARGET; state = DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R1_STATE; } } setHeatDisinfectActiveCoolActuators( state ); } state = checkRsrvrMgmtTimeoutStatus( rsrvrID, state ); return state; } /*********************************************************************//** * @brief * The checkRsrvrDrainStatus function checks and manages the status * of the a reservoir's drain * @details Inputs: rsrvrsStatus, stateStartTimeMS * @details Outputs: rsrvrsStatus, stateStartTimeMS * @param rsrvrID the reservoir ID to check the status of the timeout * @param state the state of the heat disinfect active cool mode * @return: state of the heat disinfect active cool mode *************************************************************************/ static DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T checkRsrvrDrainStatus( DG_RESERVOIR_ID_T rsrvrID, DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_T state ) { if ( DG_RESERVOIR_ABOVE_TARGET == rsrvrsStatus.rsrvr[ rsrvrID ].rStatus ) { rsrvrsStatus.rsrvr[ rsrvrID ].rStatus = getDisinfectRsrvrDrainStatus( rsrvrID, &rsrvrsStatus, RSRVR_DRAIN_STEADY_TIMEOUT_MS, RSRVR_DRAIN_TIMEOUT_MS, stateStartTimeMS ); } else if ( DG_RESERVOIR_REACHED_TARGET == rsrvrsStatus.rsrvr[ rsrvrID ].rStatus ) { if ( DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R1_STATE == state ) { DG_RESERVOIR_ID_T switchRsrvrID = ( DG_RESERVOIR_1 == rsrvrID ? DG_RESERVOIR_2 : DG_RESERVOIR_1 ); rsrvrsStatus.rsrvr[ switchRsrvrID ].rStatus = DG_RESERVOIR_ABOVE_TARGET; state = DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R2_STATE; setHeatDisinfectActiveCoolActuators( state ); } else if ( DG_HEAT_DISINFECT_ACTIVE_COOL_DRAIN_R2_STATE == state ) { deenergizeActuators( NO_PARK_CONC_PUMPS ); state = DG_HEAT_DISINFECT_ACTIVE_COOL_STATE_COMPLETE; } stateStartTimeMS = getMSTimerCount(); } state = checkRsrvrMgmtTimeoutStatus( rsrvrID, state ); return state; } /**@}*/