/************************************************************************** * * Copyright (c) 2020-2022 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 ModeChemicalDisinfect.c * * @author (last) Bill Bracken * @date (last) 24-Oct-2022 * * @author (original) Sean * @date (original) 04-Apr-2020 * ***************************************************************************/ #include "ConcentratePumps.h" #include "ConductivitySensors.h" #include "CPLD.h" #include "DrainPump.h" #include "FlowSensors.h" #include "Heaters.h" #include "LoadCell.h" #include "MessageSupport.h" #include "ModeChemicalDisinfect.h" #include "ModeFault.h" #include "ModeStandby.h" #include "NVDataMgmt.h" #include "OperationModes.h" #include "Pressures.h" #include "Reservoirs.h" #include "ROPump.h" #include "PIControllers.h" #include "RTC.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "TemperatureSensors.h" #include "Timers.h" #include "UVReactors.h" #include "Valves.h" /** * @addtogroup DGChemicalDisinfectMode * @{ */ // ********** private definitions ********** // General defines #define MAX_ALLOWED_STATE_TRIALS 1 ///< Max allowed trials on a state. This is general among all the states. TODO: not used #define CHEM_DISINFECT_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Mode chem disinfect data publish interval in counts. // Start state defines #define MIN_INLET_PRESSURE_PSI 20.0F ///< Minimum water inlet pressure in psi. TODO: set to 30.0 // Drain R1 & R2 states defines #define DRAIN_PUMP_TARGET_RPM 2400 ///< Drain pump target RPM during drain. #define DRAIN_WEIGHT_UNCHANGE_TIMEOUT ( 6 * MS_PER_SECOND ) ///< Time period of unchanged weight during draining before timeout. // Flush drain path state defines #define FLUSH_DRAIN_WAIT_TIME_MS ( 30 * MS_PER_SECOND ) ///< Flush Drain path wait time in milliseconds. #define MIN_INLET_TEMPERATURE_C 20.0F ///< Minimum water inlet temperature in C. TODO: set to 24.0 #define MAX_INLET_TEMPERATURE_C 35.0F ///< Maximum water inlet temperature in C. #define MAX_INLET_CONDUCTIVITY_US_PER_CM 2000.0F ///< Maximum water inlet conductivity in us/cm #define MIN_INLET_CONDUCTIVITY_US_PER_CM 0.0F ///< Minimum water inlet conductivity in us/cm #define MAX_ALLOWED_FLUSH_DRAIN_PERIODS 2 ///< Number of flush drain periods to wait for inlet water to come into range #define INLET_WATER_CHECK_FAILURE_TIMEOUT_MS ( 10 * MS_PER_SECOND ) ///< Timer for inlet water check failures before alarm // Flush circulation path state defines #define RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM 0.5F ///< RO pump target flow rate during flush/fill in L/min. #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. #define FLUSH_CICRCULATION_ADDITIONAL_WAIT_TIME_MS ( 60 * MS_PER_SECOND ) ///< Flush/rinse circulation path additional wait time in milliseconds. #define MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C 3.0F ///< Maximum flush circulation temperature difference tolerance in C. #define NUM_OF_TEMP_SENSORS_TO_AVG 2.0F ///< Number of temperature sensors to average to check the difference. #define MAX_FLUSH_CIRC_CONDUCTIVITY_US_PER_CM 1000.0F ///< Maximum allowed conductivity in flush circulation state in us/cm TODO: set to 100.0 #define MAX_ALLOWED_FLUSH_CIRC_PERIODS 3 ///< Number of flush circulation periods to wait for sensors to come into range // RO pump speed and pressure during entire disinfect cycle #define CHEM_DISINFECT_TARGET_RO_FLOW_LPM 0.5F ///< Chemical disinfect target RO flow rate in L/min. #define CHEM_DISINFECT_MAX_RO_PRESSURE_PSI 130 ///< Chemical disinfect maximum RO pressure in psi. // Prime acid line #define PRIME_ACID_LINE_TIMEOUT_MS ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Priming acid line timeout in milliseconds. #define DISINFECTANT_PUMP_PRIME_SPEED_ML_PER_MIN 19.0F ///< Disinfectant pump prime speed in ml/min. #define MIN_PRIME_ACID_CONDUCTIVITY_US_PER_CM 2.0F ///< Minimum conductivity that indicates acid is in the line in uS/cm. TODO: set to 600.0 #define PRIME_ACID_STEADY_CONDUCTIVITY_TIME_MS ( ( 10 * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ) ///< Minimum time that a steady conductivity of acid must be read in milliseconds. TODO: set to 10 sec #define RO_PUMP_TARGET_PRIME_DISINECTANT_FLOW_RATE_LPM 0.8F ///< ROP flow rate for disinfectant prime #define MAX_RO_PUMP_PRIME_DISINFECTANT_PRESSURE_PSI 130.0F ///< ROP max pressure for disinfectant prime // Stabilize disinfectant mixing and begin to fill tanks with disinfectant #define MAX_ALLOWED_DISINFECTANT_FLUSH_PERIODS 2 #define MIN_RO_FLOW_FOR_CONC_PUMP_MIXING_LPM 0.3F ///< Minimum RO flow rate that is need to be able to turn on the concentrate pump for mixing. #define DISINFECTANT_PUMP_FILL_SPEED_ML_PER_MIN 7.5F ///< Disinfectant pump fill speed in ml/min. #define RO_PUMP_TARGET_FILL_DISINECTANT_FLOW_RATE_LPM 0.5F ///< ROP flow rate for disinfectant prime #define MAX_RO_PUMP_FILL_DISINFECTANT_PRESSURE_PSI 130.0F ///< ROP max pressure for disinfectant fill #define FLUSH_DISINFECTANT_INITIAL_WAIT_TIME ( 30 * MS_PER_SECOND ) ///< Initial time to wait for temperature and conductivity to reach target in disinfectant flush state #define FLUSH_DISINFECTANT_ADDITIONAL_WAIT_TIME ( 60 * MS_PER_SECOND ) ///< Additional time to wait for temperature and conductivity to reach target in disinfectant flush state #define MAX_DISINFECT_FLUSH_CYCLE_PERIODS 2 ///< Number of allowed periods to check conductivity and temperature in disinfectant flush state // Contuctivity and temperature absolute limits during disinfection #define MIN_DISINFECT_CONDUCTIVITY_US_PER_CM 5.0F ///< Minimum conductivity for mixed disinfect fluid in us/cm TODO: set to 150.0 #define MAX_DISINFECT_CONDUCTIVITY_US_PER_CM 5000.0F ///< Minimum conductivity for mixed disinfect fluid in us/cm TODO: set to 650.0 #define MIN_DISINFECT_TD2_TEMPERATURE_C 25.0F ///< Minimum temperature for mixed disinfect fluid at outlet of heater in dec C TODO: set to 35.0 #define MAX_DISINFECT_TD2_TEMPERATURE_C 45.0F ///< Maximum temperature for mixed disinfect fluid at outlet of heater in dec C TODO: set to 45.0 // Initial disinfectant fill of R1 and R2 #define RSRVRS_FULL_VOL_ML 1500.0F ///< Reservoirs 1 & 2 full volume in mL. TODo original value was 1900 #define RSRVRS_PARTIAL_FILL_VOL_ML 500.0F ///< Reservoirs 1 & 2 partial volume in mL. #define RSRVRS_FULL_STABLE_TIME_COUNT ( ( 4 * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ) ///< Reservoirs 1 & 2 full stable time in counts. #define RSRVRS_FILL_UP_TIMEOUT_MS ( 6 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 full fill up timeout in ms. #define RSRVRS_BOTH_FILL_UP_TIMEOUT_MS ( 12 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 partial fill up timeout in ms. #define RSRVRS_PARTIAL_FILL_UP_TIMEOUT_MS ( 5 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 partial fill up timeout in ms. #define RSRVRS_INITIAL_DRAIN_TIME_OUT_MS ( 3 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 initial drain time out in milliseconds. #define RSRVRS_DRAIN_TIMEOUT_MS ( 3 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 drain timeout in ms. // Parameters controlling chemical disinfect #define CHEM_DISINFECT_TIME_MS ( 12 * SEC_PER_MIN * MS_PER_SECOND ) ///< Chemical disinfect time in ms. #define CHEM_DISINFECT_START_TEMP_TIMEOUT_MS ( 6 * SEC_PER_MIN * MS_PER_SECOND ) ///< Chemical disinfect reaching to minimum temperature timeout in milliseconds. #define CHEM_DISINFECT_TARGET_TEMPERATURE_C 28.0F ///< Chemical disinfect target water temperature in C. // TODO this has not been decided #define CHEM_DISINFECT_HEATER_CONTROL_TEMPERATURE_C 40.0F ///< Chemical disinfect heater control water temperature in C. // TODO this used to 30.0 #define DISINFECT_CYCLE_PERIOD_MS ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Time for each disinfectant filling and partial draining cycle // A PI control loop is used control the DRP speed while the tank is filling and draining, to maintain a constant level of disinfectant in the tank #define DRP_VOLUME_CONTROL_TARGET_VOLUME_ML 1500.0F ///< Tank level to maintain using DRP volume control #define DRP_VOLUME_CONTROL_INTERVAL_MS ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Update rate for DRP volume control in ms. #define DRP_VOLUME_CONTROL_P_COEFFICIENT -0.8F ///< P term for DRP volume control. #define DRP_VOLUME_CONTROL_I_COEFFICIENT 0.02F ///< I term for DRP volume control. #define MIN_DRP_VOLUME_CONTROL_RPM 350.0F ///< Minimum DRP RPM for volume control. The minimum value allowed by the pump driver is 300 RPM #define MAX_DRP_VOLUME_CONTROL_RPM 1500.0F ///< Maximum DRP RPM for volume control. The maximum recommended value is 2400 RPM /// Cancellation paths typedef enum Cancellation_modes { CANCELLATION_MODE_NONE = 0, ///< Cancellation mode none. CANCELLATION_MODE_BASIC, ///< Cancellation mode basic. CANCELLATION_MODE_WATER, ///< Cancellation mode water. CANCELLATION_MODE_CHEMICAL, ///< Cancellation mode chemical. NUM_OF_CANCELLATION_MODES ///< Number of cancellation modes. } CANCELLATION_MODE_T; /// Chemical disinfect status typedef enum Chemical_disinfect_status { CHEM_DISINFECT_IN_PROGRESS = 0, ///< Chemical disinfect in progress. CHEM_DISINFECT_RSRVRS_LEAK_TIMEOUT, ///< Chemical disinfect reservoirs leak timeout. CHEM_DISINFECT_HEAT_UP_TIMEOUT, ///< Chemical disinfect heat up timeout. CHEM_DISINFECT_HEATER_OUTLET_TEMPERATURE_OUT_OF_RANGE, CHEM_DISINFECT_DISINFECTANT_CONDUCTIVITY_OUT_OF_RANGE, CHEM_DISINFECT_DISINFECT_TEMPERATURE_LOW, CHEM_DISINFECT_COMPLETE, ///< Chemical disinfect complete. NUM_OF_CHEM_DISINFECT_STATUS ///< Number of chemical disinfect status. } CHEM_DISINFECT_STATUS_T; /// Non-volatile write structure typedef struct { BOOL hasDisStatusBeenWrittenToNV; ///< Boolean flag to indicate whether the disinfect status been written to NV or not. BOOL hasDisCompleteDateBeenWrittenToNV; ///< Boolean flag to indicate whether the disinfect complete date been written to NV or not. } DISINFECT_NV_OPS_T; // ********** private data ********** static DG_CHEM_DISINFECT_STATE_T chemDisinfectState = DG_CHEM_DISINFECT_STATE_START; ///< Currently active chemical disinfect state. static DG_CHEM_DISINFECT_STATE_T prevChemDisinfectState = DG_CHEM_DISINFECT_STATE_START; ///< Previous active heat disinfect state before alarm. static DG_CHEM_DISINFECT_UI_STATE_T chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_NOT_RUNNING; ///< Currently active chemical disinfect UI state. static U32 overallChemDisinfectTimer = 0; ///< Chemical disinfect cycle total timer. static U32 stateTimer = 0; ///< Chemical disinfect state timer to be used in different states. static U32 stateTrialCounter = 0; ///< Chemical disinfect state trial counter to be used for retries in different states. static BOOL areTempSensorsInRange = FALSE; ///< Chemical disinfect temperature sensors in/out range flag. /// Boolean flag to check whether draining R1 and R2 is at the end of the chemical disinfect cycle or in the beginning. So the drain states can be reused. static BOOL isThisLastDrain = FALSE; static BOOL haveInletWaterChecksPassed = FALSE; ///< Chemical disinfect inlet water checks flag static U32 inletWaterChecksFailTimer = 0; ///< Timer for inlet water check failures static DG_RESERVOIR_STATUS_T rsrvr1Status = NUM_OF_DG_RESERVOIR_STATUS; ///< Reservoir 1 status. static DG_RESERVOIR_STATUS_T rsrvr2Status = NUM_OF_DG_RESERVOIR_STATUS; ///< Reservoir 2 status. static F32 R1ChemDisinfectVol = 0.0; ///< Reservoir 1 full volume during chemical disinfect. static F32 R2ChemDisinfectVol = 0.0; ///< Reservoir 2 full volume during chemical disinfect. static U32 chemDisinfectTimer = 0; ///< Chemical disinfect timer. static U32 chemDisinfectWarmupTimer = 0; ///< Chemical disinfect warmup timer. static BOOL isPartialDisinfectInProgress = FALSE; ///< Chemical disinfect partial complete/in progess flag. static U32 rsrvrsVolMonitorTimer = 0; static U32 drpControlTimerCounter = 0; static BOOL areRsrvrsLeaking = FALSE; ///< Reservoir 1 & 2 leak check flag during chemical disinfect. static U32 dataPublishCounter = 0; ///< Chemical Disinfect data publish counter. static CANCELLATION_MODE_T cancellationMode = CANCELLATION_MODE_NONE; ///< Cancellation mode. static U32 rsrvrFillStableTimeCounter; ///< Reservoirs fill stable time counter. static ALARM_ID_T alarmDetectedPendingTrigger; ///< Chemical disinfect alarm to raise. static U32 flushCircWaitTime; ///< Variable time period to wait in flush circulation state to check sensor values static U32 flushDisinfectantWaitTime; ///< Variable time period to wait in disinfectant flush state to check sensor values static U32 primeAcidSteadyStateCounter = 0; ///< Prime acid steady state counter. static BOOL haveDrainParamsBeenInit[ NUM_OF_DG_RESERVOIRS ]; ///< Boolean flag to indicate whether the drain parameters have been reset or not. static DISINFECT_NV_OPS_T disinfectNVOps; ///< Disinfect non-volatile memory operations. // ********** private function prototypes ********** static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectStartState( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDrainR1State( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDrainR2State( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFlushDrainState( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFlushCirculationState( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectPrimeDisinfectantState( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDisinfectantFlushState( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFillWithDisinfectantState( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectR1DisinfectantFillR2DrainState( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectR2DisinfectantFillR1DrainState( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectCancelModeBasicPathState(void); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectCancelModeWaterPathState(void); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectCompleteState(void); static void failChemicalDisinfect( void ); static DG_RESERVOIR_STATUS_T getRsrvrFillStatus( DG_RESERVOIR_ID_T r, F32 targetVol, U32 timeout ); static DG_RESERVOIR_STATUS_T getRsrvrDrainStatus( DG_RESERVOIR_ID_T r, U32 drainSteadyStateTimeout, U32 timeout ); static void controlDRPByReservoirVolume( DG_RESERVOIR_ID_T r, BOOL initialize ); static CHEM_DISINFECT_STATUS_T getChemicalDisinfectStatus( void ); static void publishChemicalDisinfectData( void ); static void monitorModeChemicalDisinfect( void ); static void writeDisinfectDataToNV( void ); /*********************************************************************//** * @brief * The initChemicalDisinfectMode function initializes the chemical * disinfect mode module. * @details Inputs: none * @details Outputs: chemDisinfectState, stateTimer, isThisLastDrain, * stateTrialCounter, areTempSensorsInRange, rsrvr1Status, rsrvr2Status, * R1ChemDisinfectVol, R2ChemDisinfectVol, overallChemDisinfectTimer, * cancellationMode, rsrvrFillStableTimeCounter, prevChemDisinfectState * isPartialDisinfectInProgress, numberOfPostDisinfectRinses, * primeAcidSteadyStateCounter, chemDisinfectUIState, haveDrainParamsBeenInit, * disinfectNVOps * @return none *************************************************************************/ void initChemicalDisinfectMode( void ) { chemDisinfectState = DG_CHEM_DISINFECT_STATE_START; prevChemDisinfectState = DG_CHEM_DISINFECT_STATE_START; stateTimer = 0; isThisLastDrain = FALSE; stateTrialCounter = 0; areTempSensorsInRange = FALSE; rsrvr1Status = NUM_OF_DG_RESERVOIR_STATUS; rsrvr2Status = NUM_OF_DG_RESERVOIR_STATUS; R1ChemDisinfectVol = 0.0; R2ChemDisinfectVol = 0.0; overallChemDisinfectTimer = 0; chemDisinfectWarmupTimer = 0; chemDisinfectTimer = 0; cancellationMode = CANCELLATION_MODE_NONE; rsrvrFillStableTimeCounter = 0; isPartialDisinfectInProgress = FALSE; // numberOfPostDisinfectRinses = 0; primeAcidSteadyStateCounter = 0; chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_NOT_RUNNING; haveDrainParamsBeenInit[ DG_RESERVOIR_1 ] = FALSE; haveDrainParamsBeenInit[ DG_RESERVOIR_2 ] = FALSE; disinfectNVOps.hasDisCompleteDateBeenWrittenToNV = FALSE; disinfectNVOps.hasDisStatusBeenWrittenToNV = FALSE; } /*********************************************************************//** * @brief * The transitionToChemicalDisinfectMode function prepares for transition to * chemical disinfect mode. * @details Inputs: none * @details Outputs: chemical disisnfect mode variables are reset * @return initial state *************************************************************************/ U32 transitionToChemicalDisinfectMode( void ) { deenergizeActuators(); initChemicalDisinfectMode(); setCPLDCleanLEDColor( CPLD_CLEAN_LED_YELLOW ); return chemDisinfectState; } /*********************************************************************//** * @brief * The execChemicalDisinfectMode function executes the chemical disinfect * mode state machine. * @details Inputs: none * @details Outputs: Chemical disinfect mode state machine executed * @return current state of chemical disinfect mode *************************************************************************/ U32 execChemicalDisinfectMode( void ) { monitorModeChemicalDisinfect(); switch ( chemDisinfectState ) { case DG_CHEM_DISINFECT_STATE_START: chemDisinfectState = handleChemicalDisinfectStartState(); break; case DG_CHEM_DISINFECT_STATE_DRAIN_R1: chemDisinfectState = handleChemicalDisinfectDrainR1State(); break; case DG_CHEM_DISINFECT_STATE_DRAIN_R2: chemDisinfectState = handleChemicalDisinfectDrainR2State(); break; case DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN: chemDisinfectState = handleChemicalDisinfectFlushDrainState(); break; case DG_CHEM_DISINFECT_STATE_FLUSH_CIRCULATION: chemDisinfectState = handleChemicalDisinfectFlushCirculationState(); break; case DG_CHEM_DISINFECT_STATE_PRIME_DISINFECTANT: chemDisinfectState = handleChemicalDisinfectPrimeDisinfectantState(); break; case DG_CHEM_DISINFECT_STATE_DISINFECTANT_FLUSH: chemDisinfectState = handleChemicalDisinfectDisinfectantFlushState(); break; case DG_CHEM_DISINFECT_STATE_FILL_WITH_DISINFECTANT: chemDisinfectState = handleChemicalDisinfectFillWithDisinfectantState(); break; case DG_CHEM_DISINFECT_STATE_R2_DISINFECTANT_FILL_R1_DRAIN: chemDisinfectState = handleChemicalDisinfectR2DisinfectantFillR1DrainState(); break; case DG_CHEM_DISINFECT_STATE_R1_DISINFECTANT_FILL_R2_DRAIN: chemDisinfectState = handleChemicalDisinfectR1DisinfectantFillR2DrainState(); break; case DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH: chemDisinfectState = handleChemicalDisinfectCancelModeBasicPathState(); break; case DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH: chemDisinfectState = handleChemicalDisinfectCancelModeWaterPathState(); break; case DG_CHEM_DISINFECT_STATE_COMPLETE: chemDisinfectState = handleChemicalDisinfectCompleteState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_DG_CHEM_DISINFECT_INVALID_EXEC_STATE, chemDisinfectState ) chemDisinfectState = DG_CHEM_DISINFECT_STATE_START; break; } publishChemicalDisinfectData(); return chemDisinfectState; } /*********************************************************************//** * @brief * The getCurrentChemicalDisinfectState function returns the current state * of the chemical disinfect mode. * @details Inputs: chemState * @details Outputs: none * @return current state of chemical disinfect mode *************************************************************************/ DG_CHEM_DISINFECT_STATE_T getCurrentChemicalDisinfectState( void ) { return chemDisinfectState; } /*********************************************************************//** * @brief * The stopChemicalDisinfect function stops chemical disinfect mode. * @details Inputs: none * @details Outputs: none * @return TRUE is current operation mode is chemical disinfect, * otherwise FALSE *************************************************************************/ BOOL stopChemicalDisinfect( void ) { BOOL status = FALSE; // Check if the current operation mode is chemical disinfect if ( DG_MODE_CHEM == getCurrentOperationMode() ) { // Transition to mode standby requestNewOperationMode( DG_MODE_STAN ); status = TRUE; } return status; } // ********** private functions ********** /*********************************************************************//** * @brief * The handleChemicalDisinfectStartState function handles the chemical * disinfect start state. The state checks the inlet pressure and the * difference in between TDi and TRo sensors and if they are not in * range, it transitions to basic cancellation path. Otherwise, it * transitions to the next state. * @details Inputs: alarm, rsrvrFillStableTimeCounter, rsrvr1Status, * stateTimer * @details Outputs: alarm, rsrvrFillStableTimeCounter, rsrvr1Status, * stateTimer, chemDisinfectUIState * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectStartState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_DRAIN_R1; // Set the chemical disinfect that is published on the UI chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_NOT_RUNNING; // Start overall chemical disinfect timer overallChemDisinfectTimer = getMSTimerCount(); // Set all the actuators to reset and de-energized state deenergizeActuators(); // Set the actuators to drain R1 setValveState( VRD1, VALVE_STATE_OPEN ); setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); rsrvrFillStableTimeCounter = 0; // Assume reservoir 1 is full and drain it rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; state = DG_CHEM_DISINFECT_STATE_DRAIN_R1; stateTimer = getMSTimerCount(); return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectDrainR1State function handles the chemical * disinfect drain R1 state. The state drains reservoir 1. If the * transition is finished within the time, it transitions to the next state, * otherwise, it transitions to basic cancellation path. * @details Inputs: stateTimer, rsrvr1Status, rsrvr2Status, isThisLastDrain * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, * chemDisinfectUIState * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDrainR1State( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_DRAIN_R1; // Set the chemical disinfect that is published on the UI chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_FLUSH_BEFORE_DISINFECT; if ( DG_RESERVOIR_ABOVE_TARGET == rsrvr1Status ) { rsrvr1Status = getRsrvrDrainStatus( DG_RESERVOIR_1, DRAIN_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS ); } else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) { tareLoadCell( LOAD_CELL_RESERVOIR_1_PRIMARY ); tareLoadCell( LOAD_CELL_RESERVOIR_1_BACKUP ); // Done with draining R1 setValveState( VRD1, VALVE_STATE_CLOSED ); setValveState( VRD2, VALVE_STATE_OPEN ); // Assume reservoir 2 is full and drain it rsrvr2Status = DG_RESERVOIR_ABOVE_TARGET; state = DG_CHEM_DISINFECT_STATE_DRAIN_R2; } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectDrainR2State function handles the chemical * disinfect drain R2 state. The state drains reservoir 2. If the * transition is finished within the time, it transitions to the next * state, otherwise, it transitions to basic cancellation path. * @details Inputs: stateTimer, rsrvr2Status, isThisLastDrain, * stateTrialCounter * @details Outputs: stateTimer, rsrvr2Status, stateTrialCounter, * chemDisinfectUIState * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDrainR2State( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_DRAIN_R2; if ( DG_RESERVOIR_ABOVE_TARGET == rsrvr2Status ) { rsrvr2Status = getRsrvrDrainStatus( DG_RESERVOIR_2, DRAIN_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS ); } else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) { signalDrainPumpHardStop(); if ( TRUE == isThisLastDrain ) { // Set the chemical disinfect that is published on the UI // This is the final drain of chemical disinfect chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_FLUSH_AFTER_DISINFECT; // ??? this may not be correct setValveState( VRD2, VALVE_STATE_CLOSED ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); state = DG_CHEM_DISINFECT_STATE_COMPLETE; } else { tareLoadCell( LOAD_CELL_RESERVOIR_2_PRIMARY ); tareLoadCell( LOAD_CELL_RESERVOIR_2_BACKUP ); setValveState( VRD2, VALVE_STATE_CLOSED ); setValveState( VPI, VALVE_STATE_OPEN ); setValveState( VRD2, VALVE_STATE_CLOSED ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); stateTrialCounter = 0; stateTimer = getMSTimerCount(); state = DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN; } } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) { prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectFlushDrainState function handles the chemical * disinfect flush drain state. The state flushes the drain line for a * period of time and then measures the temperature and conductivity of * water. If they are not within the range, it transitions to basic * cancellation path, otherwise it transitions to the next state. * @details Inputs: stateTimer, stateTrialCounter, alarm, * prevChemDisinfectState * @details Outputs: stateTimer, stateTrialCounter, alarm, * prevChemDisinfectState * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFlushDrainState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN; // TODO: check whether flush has already been completed within some time period, skip this state if it has // Check if flush time has elapsed if ( TRUE == didTimeout( stateTimer, FLUSH_DRAIN_WAIT_TIME_MS ) ) { if ( TRUE == haveInletWaterChecksPassed ) { // set pumps and valves for next state, flush disinfectant line setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); setROPumpTargetFlowRateLPM( RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM, MAX_RO_PUMP_FLUSH_FILL_PRESSURE_PSI ); turnOnUVReactor( INLET_UV_REACTOR ); turnOnUVReactor( OUTLET_UV_REACTOR ); flushCircWaitTime = FLUSH_CICRCULATION_WAIT_TIME_MS; stateTimer = getMSTimerCount(); state = DG_CHEM_DISINFECT_STATE_FLUSH_CIRCULATION; } else { // If the number of failures have not exceeded the limit, try again. ++stateTrialCounter; if ( stateTrialCounter < MAX_ALLOWED_FLUSH_DRAIN_PERIODS ) { stateTimer = getMSTimerCount(); } // Couldn't get a good water sample after a couple of trials and the disinfect flush cycle failed else { //alarmDetectedPendingTrigger = ALARM_ID_DG_HEAT_DISINFECT_INLET_COND_AND_TEMP_OUT; // Do we need an alarm like this? alarmDetectedPendingTrigger = ALARM_ID_INLET_WATER_TEMPERATURE_IN_LOW_RANGE; // Or a generic alarm? ALARM_ID_DG_NEW_WAT; prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; } } } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectFlushCirculationState function handles the * chemical disinfect flush circulation state. The state flushes the * circulation line for a period of time. After the flush if the temperature * sensors are not within a certain degrees from each other, the state * transitions to basic cancellation path, otherwise, it transitions to the * next state. * @details Inputs: stateTimer, stateTrialCounter, prevChemDisinfectState * alarm, areTempSensorsInRange, rsrvr1Status, rsrvr2Status * @details Outputs: stateTimer, stateTrialCounter, prevChemDisinfectState, * alarm, areTempSensorsInRange, rsrvr1Status, rsrvr2Status * @return next state of the chemical disinfect state machine ************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFlushCirculationState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_FLUSH_CIRCULATION; // Check if the flush circulation time has elapsed and the temperature sensors are not in range yet if ( TRUE == didTimeout( stateTimer, flushCircWaitTime ) ) { F32 TPoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); F32 TD2Temp = getTemperatureValue( TEMPSENSORS_CONDUCTIVITY_SENSOR_2 ); F32 avgTemp = ( TPoTemp + TD2Temp ) / NUM_OF_TEMP_SENSORS_TO_AVG; F32 cd2Conductivity = getConductivityValue( (U32)CONDUCTIVITYSENSORS_CD2_SENSOR ); F32 cpoConductivity = getConductivityValue( (U32)CONDUCTIVITYSENSORS_CPO_SENSOR ); // Check if any of the temperature sensors deviate for more than the defined value from the average of all // of the temperature sensors BOOL isTPoOut = ( fabs( TPoTemp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C ? TRUE : FALSE ); BOOL isTD2Out = ( fabs( TD2Temp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C ? TRUE : FALSE ); BOOL isCD2Out = ( cd2Conductivity > MAX_FLUSH_CIRC_CONDUCTIVITY_US_PER_CM ? TRUE : FALSE ); BOOL isCPoOut = ( cpoConductivity > MAX_FLUSH_CIRC_CONDUCTIVITY_US_PER_CM ? TRUE : FALSE ); // Check if any of the temperature sensors are out of tolerance if( ( TRUE == isTPoOut ) || ( TRUE == isTD2Out ) || ( TRUE == isCD2Out ) || ( TRUE == isCPoOut) ) { // Check if we have exceeded the number of trials. If not, try another time ++stateTrialCounter; if ( stateTrialCounter < MAX_ALLOWED_FLUSH_CIRC_PERIODS ) { stateTimer = getMSTimerCount(); flushCircWaitTime = FLUSH_CICRCULATION_ADDITIONAL_WAIT_TIME_MS; } else // State failed. Cancel chemical disinfect mode // determine which alarm it is, temperature or conductivity, one of them has to take precedence { if ( ( TRUE == isTPoOut ) || ( TRUE == isTD2Out ) ) { alarmDetectedPendingTrigger = ALARM_ID_DG_TEMP_SENSORS_DIFF_OUT_OF_RANGE; } else { alarmDetectedPendingTrigger = ALARM_ID_INLET_WATER_CONDUCTIVITY_IN_HIGH_RANGE; //ALARM_ID_DG_NEW_CON; } prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; } } else { // Set the valves for the prime disinfectant state setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); // Set the acid dispense pump speed setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, DISINFECTANT_PUMP_PRIME_SPEED_ML_PER_MIN ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); state = DG_CHEM_DISINFECT_STATE_PRIME_DISINFECTANT; stateTimer = getMSTimerCount(); } } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectPrimeDisinfectantState function handles the * chemical disinfect prime acid line state. The state primes the acid line * until a minimum conductivity is sensed for a period of time. The the * function transitions to another state. If the minimum conductivity was * not reached within the defined period of time, it transition to a * cancellation path. * @details Inputs: stateTimer, primeAcidSteadyStateCounter * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, * prevChemDisinfectState, primeAcidSteadyStateCounter, chemDisinfectUIState * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectPrimeDisinfectantState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_PRIME_DISINFECTANT; F32 cd2Conductivity = getConductivityValue( (U32)CONDUCTIVITYSENSORS_CD2_SENSOR ); // Set the chemical disinfect state that is published on the UI chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_MIX_WATER_AND_ACID; if ( cd2Conductivity <= MIN_PRIME_ACID_CONDUCTIVITY_US_PER_CM ) { primeAcidSteadyStateCounter = 0; } // Check if the acid conductivity value has been above the threshold for the specified time period else if ( ++primeAcidSteadyStateCounter >= PRIME_ACID_STEADY_CONDUCTIVITY_TIME_MS ) { // Turn off the concentrate pump requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); // Prepare for filling the reservoirs and heating the water setValveState( VRD1, VALVE_STATE_CLOSED ); setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); // Turn on the RO pump and concentrate pump setROPumpTargetFlowRateLPM( RO_PUMP_TARGET_FILL_DISINECTANT_FLOW_RATE_LPM, MAX_RO_PUMP_FILL_DISINFECTANT_PRESSURE_PSI ); setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, DISINFECTANT_PUMP_FILL_SPEED_ML_PER_MIN ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); // Start heating the water while we are filling up the reservoirs setHeaterTargetTemperature( DG_PRIMARY_HEATER, CHEM_DISINFECT_HEATER_CONTROL_TEMPERATURE_C ); startHeater( DG_PRIMARY_HEATER ); flushDisinfectantWaitTime = FLUSH_DISINFECTANT_INITIAL_WAIT_TIME; stateTrialCounter = 0; state = DG_CHEM_DISINFECT_STATE_DISINFECTANT_FLUSH; stateTimer = getMSTimerCount(); } if ( TRUE == didTimeout( stateTimer, PRIME_ACID_LINE_TIMEOUT_MS ) ) { prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_PRIME_ACID_LINE_TIME_OUT; } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectPrimeAcidLineState function handles the * chemical disinfect prime acid line state. The state primes the acid line * until a minimum conductivity is sensed for a period of time. The the * function transitions to another state. If the minimum conductivity was * not reached within the defined period of time, it transition to a * cancellation path. * @details Inputs: stateTimer, primeAcidSteadyStateCounter * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, * prevChemDisinfectState, primeAcidSteadyStateCounter, chemDisinfectUIState * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDisinfectantFlushState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_DISINFECTANT_FLUSH; // Set the chemical disinfect that is published on the UI chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_MIX_WATER_AND_ACID; if ( TRUE == didTimeout( stateTimer, flushDisinfectantWaitTime ) ) { CHEM_DISINFECT_STATUS_T status = getChemicalDisinfectStatus(); if ( ( CHEM_DISINFECT_HEATER_OUTLET_TEMPERATURE_OUT_OF_RANGE == status ) || ( CHEM_DISINFECT_DISINFECTANT_CONDUCTIVITY_OUT_OF_RANGE == status ) ) { // Check if we have exceeded the number of trials. If not, try another time ++stateTrialCounter; if (stateTrialCounter < MAX_ALLOWED_DISINFECTANT_FLUSH_PERIODS ) { stateTimer = getMSTimerCount(); flushDisinfectantWaitTime = FLUSH_DISINFECTANT_ADDITIONAL_WAIT_TIME; } else { // fail, alarm has already been set prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; } } else { // No changes in valves and pumps for next state rsrvr1Status = DG_RESERVOIR_BELOW_TARGET; rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; stateTimer = getMSTimerCount(); state = DG_CHEM_DISINFECT_STATE_FILL_WITH_DISINFECTANT; } } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectFillWithWaterAndDisinfectantState function * handles the chemical disinfect fill with water state. The state fills * reservoir 1 until it overflows to reservoir 2. If the filling process * times out, it transitions to water cancellation state, otherwise, it * transitions to next state. The levels of the reservoirs are recorded to * be monitored during chemical disinfect. * @details Inputs: stateTimer, rsrvr1Status, rsrvr2Status, * rsrvrsVolMonitorTimer * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, * R1ChemDisinfectVol, R2ChemDisinfectVol, rsrvrsVolMonitorTimer * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFillWithDisinfectantState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_FILL_WITH_DISINFECTANT; // First reservoir 1 must be full if ( DG_RESERVOIR_BELOW_TARGET == rsrvr1Status ) { rsrvr1Status = getRsrvrFillStatus( DG_RESERVOIR_1, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS ); } // Once reservoir 1 is full, check the status of reservoir 2 since the water overflows to reservoir 2 else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) { rsrvr2Status = getRsrvrFillStatus( DG_RESERVOIR_2, RSRVRS_FULL_VOL_ML, RSRVRS_BOTH_FILL_UP_TIMEOUT_MS ); // If R1 is not full, keep monitoring for R1 level and timeout // Once R1 is full, keep monitoring for R2 level and timeout // Once R2 is full, transition to the next state if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) { // Set the actuators to flush R2 and drain R1 state // Set the valves to drain R1 and fill R2 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 ); // Set both reservoirs status rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; rsrvr2Status = DG_RESERVOIR_ABOVE_TARGET; // Set the drain pump to control mode controlDRPByReservoirVolume( DG_RESERVOIR_1, TRUE ); //Initialize the PI controller for DRP /* ??? I am not sure whether we should monitor the reservoir volumes for leaks // 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 chemical disinfect cycle rsrvrsVolMonitorTimer = getMSTimerCount(); */ R1ChemDisinfectVol = getLoadCellLargeFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); R2ChemDisinfectVol = getLoadCellLargeFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); chemDisinfectWarmupTimer = getMSTimerCount(); isPartialDisinfectInProgress = FALSE; stateTimer = getMSTimerCount(); state = DG_CHEM_DISINFECT_STATE_R2_DISINFECTANT_FILL_R1_DRAIN; } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) { prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; } } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectR2DisinfectantFillR1DrainState function handles the * chemical disinfect R1 to R2 state. The state runs reservoir 1 to reservoir 2 * chemical disinfect. If the reservoirs leak or it cannot reach to temperature * within a certain period of time, it transitions to water cancellation. * If chemical disinfect reservoir 1 to reservoir 2 is completed, it * transitions to the next state. * @details Inputs: stateTimer, rsrvr1Status, rsrvr2Status * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, * chemDisinfectUIState * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectR2DisinfectantFillR1DrainState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_R2_DISINFECTANT_FILL_R1_DRAIN; CHEM_DISINFECT_STATUS_T status = getChemicalDisinfectStatus(); // Set the chemical disinfect that is published on the UI chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_DISINFECT_DEVICE; if ( CHEM_DISINFECT_COMPLETE == status) { // turn pumps and valves off stopHeater( DG_PRIMARY_HEATER ); setValveState( VPI, VALVE_STATE_CLOSED ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); setValveState( VRD1, VALVE_STATE_CLOSED ); signalROPumpHardStop(); signalDrainPumpHardStop(); requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); state = DG_CHEM_DISINFECT_STATE_COMPLETE; } if ( TRUE == didTimeout( stateTimer, DISINFECT_CYCLE_PERIOD_MS ) ) { // go to the next disinfect configuration cycle setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); setValveState( VRD1, VALVE_STATE_CLOSED ); setValveState( VRD2, VALVE_STATE_OPEN ); stateTimer = getMSTimerCount(); state = DG_CHEM_DISINFECT_STATE_R1_DISINFECTANT_FILL_R2_DRAIN; } else { controlDRPByReservoirVolume( DG_RESERVOIR_1, FALSE ); } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectR1DisinfectantFillR2DrainState function handles the * chemical disinfect R1 to R2 state. The state runs reservoir 1 to reservoir 2 * chemical disinfect. If the reservoirs leak or it cannot reach to temperature * within a certain period of time, it transitions to water cancellation. * If chemical disinfect reservoir 1 to reservoir 2 is completed, it * transitions to the next state. * @details Inputs: stateTimer, rsrvr1Status, rsrvr2Status * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, * chemDisinfectUIState * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectR1DisinfectantFillR2DrainState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_R1_DISINFECTANT_FILL_R2_DRAIN; CHEM_DISINFECT_STATUS_T status = getChemicalDisinfectStatus(); // Set the chemical disinfect that is published on the UI chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_DISINFECT_DEVICE; if ( CHEM_DISINFECT_COMPLETE == status) { // turn pumps and valves off stopHeater( DG_PRIMARY_HEATER ); setValveState( VPI, VALVE_STATE_CLOSED ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); setValveState( VRD2, VALVE_STATE_CLOSED ); signalROPumpHardStop(); signalDrainPumpHardStop(); requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); state = DG_CHEM_DISINFECT_STATE_COMPLETE; } else if ( TRUE == didTimeout( stateTimer, DISINFECT_CYCLE_PERIOD_MS ) ) { // go to the next disinfect configuration cycle 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 ); stateTimer = getMSTimerCount(); state = DG_CHEM_DISINFECT_STATE_R2_DISINFECTANT_FILL_R1_DRAIN; } else { controlDRPByReservoirVolume( DG_RESERVOIR_2, FALSE ); } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectCancelModeBasicPathState function handles the * chemical disinfect cancel mode basic path state. The state sets the state * to complete and raises an alarm. * @details Inputs: none * @details Outputs: cancellationMode, chemDisinfectUIState * @return next state of the heat disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectCancelModeBasicPathState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; // Set the chemical disinfect that is published on the UI chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_CANCEL_DISINFECT; // Set the cancellation mode cancellationMode = CANCELLATION_MODE_BASIC; failChemicalDisinfect(); return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectCancelModeWaterPathState function handles the * chemical disinfect cancel mode cold water path state. * @details Inputs: rsrvr1Status, rsrvr2Status, cancellationMode, stateTimer * @details Outputs: rsrvr1Status, rsrvr2Status, cancellationMode, stateTimer, * chemDisinfectUIState * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectCancelModeWaterPathState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; // Set the chemical disinfect that is published on the UI chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_CANCEL_DISINFECT; if ( CANCELLATION_MODE_NONE == cancellationMode ) { // Stop all the actuators first then decide who should run next deenergizeActuators(); cancellationMode = CANCELLATION_MODE_WATER; rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; rsrvr2Status = DG_RESERVOIR_ABOVE_TARGET; // The drain is set to start from reservoir 2 setValveState( VRD2, VALVE_STATE_OPEN ); setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); // Start the timer for drain timeout stateTimer = getMSTimerCount(); } // 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, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS ); if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) { // Set the drain valve to reservoir 1 setValveState( VRD1, VALVE_STATE_OPEN ); setValveState( VRD2, VALVE_STATE_CLOSED ); } } // Could not drain reservoir 2. Transition to basic cancellation path else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) { prevChemDisinfectState = state; state = DG_CHEM_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, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS ); if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) { failChemicalDisinfect(); } // Could not drain reservoir 1. Transition to basic cancellation path else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; } } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectCompleteState function handles the chemical * disinfect complete state. The state stops chemical disinfect and * requests transition to mode standby. * @details Inputs: none * @details Outputs: chemDisinfectUIState * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectCompleteState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_COMPLETE; // Set the chemical disinfect that is published on the UI chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_COMPLETE; stopChemicalDisinfect(); return state; } /*********************************************************************//** * @brief * The failChemicalDisinfect function sets the alarm that failed the * chemical disinfect mode. * @details Inputs: alarmDetectedPendingTrigger, prevChemDisinfectState * @details Outputs: none * @return none *************************************************************************/ static void failChemicalDisinfect( void ) { SET_ALARM_WITH_1_U32_DATA( alarmDetectedPendingTrigger, prevChemDisinfectState ) } /*********************************************************************//** * @brief * The getRsrvrFillStatus 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 chemical disinfect mode. * @details Inputs: rsrvrFillStableTimeCounter, alarm, stateTimer * @details Outputs: none * @param r is DG_RESERVOIR_1 or DG_RESERVOIR_2 * @param targetVol is the target fill volume * @param timeout is the fill up timeout * @return the status of the reservoirs during filling *************************************************************************/ static DG_RESERVOIR_STATUS_T getRsrvrFillStatus( DG_RESERVOIR_ID_T r, F32 targetVol, U32 timeout ) { DG_RESERVOIR_STATUS_T status = DG_RESERVOIR_BELOW_TARGET; F32 volume = 0.0; if ( DG_RESERVOIR_1 == r ) { volume = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); } else if ( DG_RESERVOIR_2 == r ) { volume = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_DG_RESERVOIR_SELECTED, r ) } // Check the volume of the reservoir against the target volume if ( volume >= targetVol ) { if ( ++rsrvrFillStableTimeCounter >= RSRVRS_FULL_STABLE_TIME_COUNT ) { status = DG_RESERVOIR_REACHED_TARGET; rsrvrFillStableTimeCounter = 0; // Set the state timer in case it needs to be used for another timeout check stateTimer = getMSTimerCount(); } } else if ( TRUE == didTimeout( stateTimer, timeout ) ) { // Failed to fill ontime. Update the previous chemical disinfect state and transition to basic cancellation prevChemDisinfectState = chemDisinfectState; alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_FILL_TIMEOUT; status = DG_RESERVOIR_NOT_REACHED_TARGET; } return status; } /*********************************************************************//** * @brief * The getRsrvrDrainStatus function returns the status of draining a * reservoir. * @details Inputs: stateTimer, prevChemDisinfectState, chemDisinfectState, * alarm * @details Outputs: stateTimer, chemDisinfectState, alarm * @param r is DG_RESERVOIR_1 or DG_RESERVOIR_2 * @param drainSteadyStateTimeout which is the time the reservoir's level * does not change and is steady state * @param timeout which is the timeout that a reservoir must be drained by * then * @return the status of the reservoirs during draining *************************************************************************/ static DG_RESERVOIR_STATUS_T getRsrvrDrainStatus( DG_RESERVOIR_ID_T r, U32 drainSteadyStateTimeout, U32 timeout ) { DG_RESERVOIR_STATUS_T status = DG_RESERVOIR_ABOVE_TARGET; // If the drain parameters of the reservoir is not initialized, initialize them if ( FALSE == haveDrainParamsBeenInit[ r ] ) { initDrainParameters( r ); haveDrainParamsBeenInit[ r ] = TRUE; } BOOL isDrainComplete = hasTargetDrainVolumeBeenReached( r, drainSteadyStateTimeout ); if ( TRUE == isDrainComplete ) { // Set the state timer in case it needs to be used for another timeout check stateTimer = getMSTimerCount(); haveDrainParamsBeenInit[ r ] = FALSE; status = DG_RESERVOIR_REACHED_TARGET; } else if ( TRUE == didTimeout( stateTimer, timeout ) ) { // Failed to drain on time. Update the previous chemical disinfect state and transition to basic cancellation prevChemDisinfectState = chemDisinfectState; haveDrainParamsBeenInit[ r ] = FALSE; alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_DRAIN_TIMEOUT; status = DG_RESERVOIR_NOT_REACHED_TARGET; } return status; } /*********************************************************************//** * @brief * The controlDRPByReservoirVolume function implements the volume control of the * drain pump to maintain a specified volume in one of the reservoirs. * @details Inputs: stateTimer, reservoir, initialize flag, control volume, * tank volume, PI coefficients, min and max drain pump control RPM * @details Outputs: stateTimer, chemDisinfectState, alarm * @param r is DG_RESERVOIR_1 or DG_RESERVOIR_2 * @param initialize determines whether the function initializes the controller or * runs it *************************************************************************/ static void controlDRPByReservoirVolume( DG_RESERVOIR_ID_T r, BOOL initialize ) { if ( TRUE == initialize ) { // Initialize drain pump volume control PI controller initializePIController( PI_CONTROLLER_ID_DRAIN_PUMP_VOLUME, MIN_DRP_VOLUME_CONTROL_RPM, DRP_VOLUME_CONTROL_P_COEFFICIENT, DRP_VOLUME_CONTROL_I_COEFFICIENT, MIN_DRP_VOLUME_CONTROL_RPM, MAX_DRP_VOLUME_CONTROL_RPM ); drpControlTimerCounter = getMSTimerCount(); resetPIController( PI_CONTROLLER_ID_DRAIN_PUMP_VOLUME, MIN_DRP_VOLUME_CONTROL_RPM ); setDrainPumpTargetRPM( MIN_DRP_VOLUME_CONTROL_RPM ); } else if ( TRUE == didTimeout( drpControlTimerCounter, DRP_VOLUME_CONTROL_INTERVAL_MS ) ) { // Control at set interval F32 tgtVolume = (F32)DRP_VOLUME_CONTROL_TARGET_VOLUME_ML; F32 actVolume; U32 newRPM; if ( DG_RESERVOIR_1 == r ) { actVolume = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); } else if ( DG_RESERVOIR_2 == r ) { actVolume = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_DG_RESERVOIR_SELECTED, r ) } newRPM = runPIController( PI_CONTROLLER_ID_DRAIN_PUMP_VOLUME, tgtVolume, actVolume ); setDrainPumpTargetRPM( newRPM ); drpControlTimerCounter = getMSTimerCount(); } } /*********************************************************************//** * @brief * The getChemicalDisinfectStatus function monitors and returns the current * stage of chemical disinfect cycle. If the level of the reservoirs is drifted * consecutively for the define period of time, it sets the reservoir leak * time out alarm. If the chemical disinfect has started or has elapsed, it * set the status of chemical disinfect accordingly. * @details Inputs: areRsrvrsLeaking, areRsrvrsLeaking * @details Outputs: areRsrvrsLeaking, areRsrvrsLeaking * @return status of the chemical disinfect (i.e in progress, complete) *************************************************************************/ static CHEM_DISINFECT_STATUS_T getChemicalDisinfectStatus( void ) { CHEM_DISINFECT_STATUS_T status = CHEM_DISINFECT_IN_PROGRESS; // Update the variables F32 TdiTemp = getTemperatureValue( TEMPSENSORS_INLET_DIALYSATE ); F32 cd2Conductivity = getConductivityValue( (U32)CONDUCTIVITYSENSORS_CD2_SENSOR ); F32 TD2Temp = getTemperatureValue( TEMPSENSORS_CONDUCTIVITY_SENSOR_2 ); BOOL isCD2OutofRange = ( cd2Conductivity < MIN_DISINFECT_CONDUCTIVITY_US_PER_CM || cd2Conductivity > MAX_DISINFECT_CONDUCTIVITY_US_PER_CM ); BOOL isTD2OutofRange = ( TD2Temp < MIN_DISINFECT_TD2_TEMPERATURE_C || TD2Temp > MAX_DISINFECT_TD2_TEMPERATURE_C ); /* this section is for monitoring the tank volumes for possible leaks. It is currently disabled pending decision whether to use it BOOL isR1OutOfRange = fabs( getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ) - R1ChemDisinfectVol ) > RSRVRS_MAX_TARGET_VOL_CHANGE_ML; BOOL isR2OutOfRange = fabs( getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ) - R2ChemDisinfectVol ) > RSRVRS_MAX_TARGET_VOL_CHANGE_ML; // Check if either reservoir 1 or reservoir 2 are losing volume more than allowed volume if ( ( TRUE == isR1OutOfRange ) || ( TRUE == 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 ( TRUE == didTimeout( rsrvrsVolMonitorTimer, RSRVRS_TARGET_VOL_OUT_TIMEOUT_MS ) ) { areRsrvrsLeaking = FALSE; alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_LEAK_TIMEOUT; status = CHEM_DISINFECT_RSRVRS_LEAK_TIMEOUT; } } // Reservoirs are in range else { areRsrvrsLeaking = FALSE; } */ // set the tank volumes for logging R1ChemDisinfectVol = getLoadCellLargeFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); R2ChemDisinfectVol = getLoadCellLargeFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); // For the following checks, if multiple failures occur at the same time, the last one will take precedence // Check if the acid conductivity and temperature are within range if ( TRUE == isCD2OutofRange ) { alarmDetectedPendingTrigger = ALARM_ID_INLET_WATER_CONDUCTIVITY_IN_HIGH_RANGE; //ALARM_ID_DG_NEW_CON; TODO status = CHEM_DISINFECT_DISINFECTANT_CONDUCTIVITY_OUT_OF_RANGE; } if ( ( TRUE == isTD2OutofRange ) ) { alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_TARGET_TEMP_TIMEOUT; status = CHEM_DISINFECT_HEATER_OUTLET_TEMPERATURE_OUT_OF_RANGE; } // If the coldest spot which is TDi is less than minimum chemical disinfect temperature, // reset the chemical disinfect timers and check whether heating up has timed out if ( TdiTemp < CHEM_DISINFECT_TARGET_TEMPERATURE_C ) { if ( TRUE == didTimeout( chemDisinfectWarmupTimer, CHEM_DISINFECT_START_TEMP_TIMEOUT_MS ) ) { // Heating up to minimum temperature for chemical disinfect failed alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_TARGET_TEMP_TIMEOUT; status = CHEM_DISINFECT_HEAT_UP_TIMEOUT; } else { // Keep reseting the disinfect timer so the elapsed time is always 0 until disinfect truly starts chemDisinfectTimer = getMSTimerCount(); isPartialDisinfectInProgress = FALSE; status = CHEM_DISINFECT_DISINFECT_TEMPERATURE_LOW; } } else if ( isPartialDisinfectInProgress != TRUE ) { // The temperature of the coldest spot is in range to start the disinfect timer chemDisinfectTimer = getMSTimerCount(); isPartialDisinfectInProgress = TRUE; } // If chemical disinfect temperature has been reached, check if this stage of chemical disinfect is done if ( ( TRUE == isPartialDisinfectInProgress ) && ( TRUE == didTimeout( chemDisinfectTimer, CHEM_DISINFECT_TIME_MS ) ) ) { // Done with this stage of chemical disinfect. Reset the variables status = CHEM_DISINFECT_COMPLETE; isPartialDisinfectInProgress = FALSE; } return status; } /*********************************************************************//** * @brief * The publishChemicalDisinfectData function publishes chemical disinfect * data at the set interval. * @details Inputs: dataPublishCounter * @details Outputs: dataPublishCounter * @return: none *************************************************************************/ static void publishChemicalDisinfectData( void ) { if ( ++dataPublishCounter >= CHEM_DISINFECT_DATA_PUB_INTERVAL ) { MODE_CHEMICAL_DISINFECT_DATA_T data; MODE_CHEMICAL_DISINFECT_UI_DATA_T uiData; data.chemDisinfectState = (U32)chemDisinfectState; data.overallElapsedTime = calcTimeSince( overallChemDisinfectTimer ); data.stateElapsedTime = calcTimeSince( stateTimer ); data.cancellationMode = (U32)cancellationMode; //If the mode is in the actual chemical disinfect states, publish the elapsed time, otherwise publish 0 to avoid confusion if ( ( DG_CHEM_DISINFECT_STATE_R2_DISINFECTANT_FILL_R1_DRAIN == chemDisinfectState ) || ( DG_CHEM_DISINFECT_STATE_R1_DISINFECTANT_FILL_R2_DRAIN == chemDisinfectState ) ) { uiData.chemDisinfectTargetTime = CHEM_DISINFECT_TIME_MS; uiData.chemDisinfectCountdownTime = calcTimeSince( chemDisinfectTimer ); data.R1FillLevel = R1ChemDisinfectVol; data.R2FillLevel = R2ChemDisinfectVol; } else { uiData.chemDisinfectCountdownTime = 0.0; data.R1FillLevel = 0.0; data.R2FillLevel = 0.0; } data.postDisinfectTargetRinseCount = 0; data.postDisinfectCurrentRinseCount = 0; data.chemDisinfectUIState = chemDisinfectUIState; // General data publish channel broadcastData( MSG_ID_DG_CHEM_DISINFECT_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&data, sizeof( MODE_CHEMICAL_DISINFECT_DATA_T ) ); // Publish data to UI broadcastData( MSG_ID_DG_CHEM_DISINFECT_TIME_DATA, COMM_BUFFER_OUT_CAN_DG_2_UI, (U08*)&uiData, sizeof( MODE_CHEMICAL_DISINFECT_UI_DATA_T ) ); dataPublishCounter = 0; } } /*********************************************************************//** * @brief * The monitorModeChemicalDisinfect 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: chemDisinfectState * @details Outputs: prevChemDisinfectState, chemDisinfectState, * alarmDetectedPendingTrigger * @return: none *************************************************************************/ static void monitorModeChemicalDisinfect( void ) { BOOL hasConductivityFailed = TRUE; #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_CAPS_MONITOR ) != SW_CONFIG_ENABLE_VALUE ) #endif { // If the dialysate cap is open during any state, alarm if ( 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 U32 ConcCap = (U32)getSwitchStatus( CONCENTRATE_CAP ); U32 DialysateCap = (U32)getSwitchStatus( DIALYSATE_CAP ); prevChemDisinfectState = chemDisinfectState; chemDisinfectState = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; alarmDetectedPendingTrigger = ALARM_ID_DG_DIALYSATE_OR_CONC_CAP_NOT_IN_PROPER_POSITION; } } // In all states, check inlet temperature, inlet pressure, and inlet conductivity. haveInletWaterChecksPassed= TRUE; hasConductivityFailed = ( ( getConductivityValue( CONDUCTIVITYSENSORS_CPI_SENSOR ) > MAX_INLET_CONDUCTIVITY_US_PER_CM ) || ( getConductivityValue( CONDUCTIVITYSENSORS_CPI_SENSOR ) < MIN_INLET_CONDUCTIVITY_US_PER_CM ) ); #ifndef _RELEASE_ if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_DISABLE_DISINFECT_CONDUCTIVITY_CHECK ) ) { hasConductivityFailed = FALSE; } #endif if ( getTemperatureValue( TEMPSENSORS_INLET_PRIMARY_HEATER ) < MIN_INLET_TEMPERATURE_C || getTemperatureValue( TEMPSENSORS_INLET_PRIMARY_HEATER ) > MAX_INLET_TEMPERATURE_C || hasConductivityFailed || getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ) < MIN_INLET_PRESSURE_PSI) { // Inlet check failed, set timer if not already set if ( 0 == inletWaterChecksFailTimer ) { inletWaterChecksFailTimer = getMSTimerCount(); } haveInletWaterChecksPassed= FALSE; // set flag for flush drain state if ( TRUE == didTimeout( inletWaterChecksFailTimer, INLET_WATER_CHECK_FAILURE_TIMEOUT_MS ) ) { // alarm unless in the start, drain, or flush drain states switch( chemDisinfectState ) { case DG_CHEM_DISINFECT_STATE_START: case DG_CHEM_DISINFECT_STATE_DRAIN_R1: case DG_CHEM_DISINFECT_STATE_DRAIN_R2: case DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN: break; case DG_CHEM_DISINFECT_STATE_FLUSH_CIRCULATION: case DG_CHEM_DISINFECT_STATE_PRIME_DISINFECTANT: case DG_CHEM_DISINFECT_STATE_DISINFECTANT_FLUSH: prevChemDisinfectState = chemDisinfectState; alarmDetectedPendingTrigger = ALARM_ID_INLET_WATER_TEMPERATURE_IN_LOW_RANGE; //ALARM_ID_NEW_WAT; chemDisinfectState = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; break; case DG_CHEM_DISINFECT_STATE_FILL_WITH_DISINFECTANT: case DG_CHEM_DISINFECT_STATE_R2_DISINFECTANT_FILL_R1_DRAIN: case DG_CHEM_DISINFECT_STATE_R1_DISINFECTANT_FILL_R2_DRAIN: prevChemDisinfectState = chemDisinfectState; alarmDetectedPendingTrigger = ALARM_ID_INLET_WATER_TEMPERATURE_IN_LOW_RANGE; //ALARM_ID_NEW_WAT; chemDisinfectState = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; break; } } } else { // reset water check fail timer inletWaterChecksFailTimer = 0; } } /*********************************************************************//** * @brief * The writeDisinfectDataToNV function writes the disinfection data to the * non-volatile memory. * @details Inputs: disinfectNVOps * @details Outputs: disinfectNVOps * @return: none *************************************************************************/ static void writeDisinfectDataToNV( void ) { if ( FALSE == disinfectNVOps.hasDisCompleteDateBeenWrittenToNV ) { disinfectNVOps.hasDisCompleteDateBeenWrittenToNV = setDisinfectStatus( TRUE ); } if ( FALSE == disinfectNVOps.hasDisStatusBeenWrittenToNV ) { disinfectNVOps.hasDisStatusBeenWrittenToNV = setLastDisinfectDate( USAGE_INFO_CHEMICAL_DISINFECT, getRTCTimestamp() ); } } /**@}*/