Index: firmware/App/Modes/ModeChemicalDisinfect.c =================================================================== diff -u -rcc398b14ccf518f350b57fb5cb8728e5c908bd1e -rd7be59e36db5e9899b02dd0bfadadc50fed934c0 --- firmware/App/Modes/ModeChemicalDisinfect.c (.../ModeChemicalDisinfect.c) (revision cc398b14ccf518f350b57fb5cb8728e5c908bd1e) +++ firmware/App/Modes/ModeChemicalDisinfect.c (.../ModeChemicalDisinfect.c) (revision d7be59e36db5e9899b02dd0bfadadc50fed934c0) @@ -1,22 +1,41 @@ /************************************************************************** * -* Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. +* 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 +* @file ModeChemicalDisinfect.c * -* @author (last) Sean Nash -* @date (last) 26-May-2020 +* @author (last) Bill Bracken +* @date (last) 22-Aug-2022 * -* @author (original) Sean -* @date (original) 04-Apr-2020 +* @author (original) Sean +* @date (original) 04-Apr-2020 * ***************************************************************************/ +#include "ConcentratePumps.h" +#include "ConductivitySensors.h" +#include "DrainPump.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 "RTC.h" +#include "SystemCommMessages.h" +#include "TaskGeneral.h" +#include "TemperatureSensors.h" +#include "Timers.h" +#include "UVReactors.h" +#include "Valves.h" /** * @addtogroup DGChemicalDisinfectMode @@ -25,76 +44,2120 @@ // ********** private definitions ********** +// General defines +#define MAX_ALLOWED_STATE_TRIALS 1 ///< Max allowed trials on a state. This is general among all the states. +#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 30.0F ///< Minimum water inlet pressure in psi. +#define MAX_START_STATE_TEMP_SENSORS_DIFF_C 3.0F ///< Max start state TDi and TRo difference tolerance in C. + +// Drain R1 & R2 states defines +#define DRAIN_PUMP_TARGET_RPM 2400 ///< Drain pump target RPM during drain. +#define RSRVRS_INITIAL_DRAIN_TIME_OUT_MS ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 initial drain time out in milliseconds. +#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 ( SEC_PER_MIN * MS_PER_SECOND ) ///< Flush Drain path wait time in milliseconds. +#define MIN_INLET_TEMPERATURE_C 15.0F ///< Minimum water inlet temperature in C. TODO original temperature was 25 C +#define MAX_INLET_CONDUCTIVITY_US_PER_CM 2000.0F ///< Maximum water inlet conductivity in us/cm + +// Flush circulation path state defines +#define RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM 0.8F ///< 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 MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C 3.0F ///< Maximum flush circulation temperature difference tolerance in C. +#define NUM_OF_TEMP_SENSORS_TO_AVG 4.0F ///< Number of temperature sensors to average to check the difference. + +// Flush and drain R1 and R2 +#define RSRVRS_FULL_VOL_ML 1850.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 ( 5 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 full fill up timeout in ms. +#define RSRVRS_500ML_FILL_UP_TIMEOUT_MS ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 partial fill up timeout in ms. +#define RSRVRS_DRAIN_TIMEOUT_MS ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 drain timeout in ms. + +// R1 to R2 & R2 to R1 chemical disinfect circulation +#define CHEM_DISINFECT_TARGET_RO_FLOW_LPM 0.8F ///< Chemical disinfect target RO flow rate in L/min. +#define CHEM_DISINFECT_MAX_RO_PRESSURE_PSI 130 ///< Chemical disinfect maximum RO pressure in psi. +#define CHEM_DISINFECT_TARGET_DRAIN_PRES_PSI 12.0 ///< Chemical disinfect target drain outlet pressure in psi. +#define CHEM_DISINFECT_TIME_MS ( 36 * SEC_PER_MIN * MS_PER_SECOND ) ///< Chemical disinfect time for each section in milliseconds. TODO original time was 36 minutes +#define CHEM_DISINFECT_START_TEMP_TIMOUT_MS ( 4 * MIN_PER_HOUR * SEC_PER_MIN * MS_PER_SECOND ) ///< Chemical disinfect reaching to minimum temperature timeout in milliseconds. +#define RSRVRS_TARGET_VOL_OUT_TIMEOUT_MS ( 0.5F * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 maximum volume out of range timeout during chemical disinfect. TODO change this to 5 seconds +#define RSRVRS_MAX_TARGET_VOL_CHANGE_ML 600.0F ///< Reservoirs 1 & 2 maximum allowed volume change when full during chemical disinfect. TODO original value is 100 mL +#define POST_CHEM_DISINFECT_WAIT_TIME_MS ( 1 * SEC_PER_MIN * MS_PER_SECOND ) ///< Chemical disinfect final wait time before flushing the system in milliseconds. + +// Prime acid line +#define PRIME_ACID_LINE_TIMEOUT_MS ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Priming acid line timeout in milliseconds. +#define CONC_PUMP_PRIME_SPEED_ML_PER_MIN 40.0F ///< Concentrate pump prime speed in ml/min. +#define MIN_ACID_CONDUCTIVITY_US_PER_CM 2000.0F ///< Minimum conductivity that indicates acid is in the line in uS/cm. +#define PRIME_ACID_STEADY_CONDUCTIVITY_TIME_MS ( ( 2 * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ) ///< Minimum time that a steady conductivity of acid must be read in milliseconds. + +// Fill with disinfectant and water +#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. + +// Fill heat up +#define CHEM_DISINFECT_TARGET_TEMPERATURE_C 21.0F ///< Chemical disinfect target water temperature in C. // TODO this used to 30.0 + +// Post disinfect rinses +#define NUM_OF_POST_DISINFECT_RINSES 12 ///< Number of rinses after a chemical disinfect. + +static const F32 ACID_TO_WATER_MIXING_RATIO = ( 1.0F / 70.0F ); ///< Acid to water mixing ratio for chemical disinfect. + +/// 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_COMPLETE, ///< Chemical disinfect complete. + NUM_OF_CHEM_DISINFECT_STATUS ///< Number of chemical disinfect status. +} CHEM_DISINFECT_STATUS_T; + // ********** private data ********** -static DG_CHEM_DISINFECT_STATE_T chemState = DG_CHEM_DISINFECT_STATE_START; ///< Currently active chemical disinfect state. +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 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 BOOL isPartialDisinfectInProgress = FALSE; ///< Chemical disinfect partial complete/in progess flag. +static U32 rsrvrsVolMonitorTimer = 0; ///< Reservoir 1 & 2 volume monitor timers during chemical disinfect. +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 numberOfPostDisinfectRinses; ///< Number of times to rinse the fluid path after chemical disinfect. +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. // ********** 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 handleChemicalDisinfectFlushR1AndR2State( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFlushR2AndDrainR1State( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFlushDrainR2State( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFlushDrainR1State( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectPrimeAcidLineState( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFillWithWaterAndDisinfectantState( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectRemoveAcidBottleFromUIState( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDisinfectR1ToR2State( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFillR2WithDisinfectantState( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDisinfectR2ToR1State( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectCoolDownHeatersState( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDisinfectantDrainR1State( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDisinfectantDrainR2State( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectRinseR1ToR2State( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectRinseR2ToR1AndDrainR1State( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectRinseR1ToR2AndDrainR2State( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectRinseCirculationState( 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 CHEM_DISINFECT_STATUS_T getChemicalDisinfectStatus( void ); +static void publishChemicalDisinfectData( void ); +static void monitorModeChemicalDisinfect( void ); + /*********************************************************************//** * @brief - * The initChemicalDisinfectMode function initializes the chemical disinfect mode module. - * @details - * Inputs : none - * Outputs : Module initialized. + * 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 * @return none *************************************************************************/ void initChemicalDisinfectMode( void ) { - chemState = DG_CHEM_DISINFECT_STATE_START; + 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; + 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; } /*********************************************************************//** * @brief * The transitionToChemicalDisinfectMode function prepares for transition to * chemical disinfect mode. - * @details - * Inputs : none - * Outputs : none - * @return none + * @details Inputs: none + * @details Outputs: chemical disisnfect mode variables are reset + * @return initial state *************************************************************************/ -void transitionToChemicalDisinfectMode( void ) +U32 transitionToChemicalDisinfectMode( void ) { + deenergizeActuators(); + initChemicalDisinfectMode(); + + return chemDisinfectState; } /*********************************************************************//** * @brief - * The execChemicalDisinfectMode function executes the chemical disinfect mode state machine. - * @details - * Inputs : none - * Outputs : Chemical disinfect mode state machine executed + * 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 ) { - // execute current chemical disinfect state - switch ( chemState ) + 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_FLUSH_R1_AND_R2: + chemDisinfectState = handleChemicalDisinfectFlushR1AndR2State(); + break; + + case DG_CHEM_DISINFECT_STATE_FLUSH_R2_AND_DRAIN_R1: + chemDisinfectState = handleChemicalDisinfectFlushR2AndDrainR1State(); + break; + + case DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN_R2: + chemDisinfectState = handleChemicalDisinfectFlushDrainR2State(); + break; + + case DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN_R1: + chemDisinfectState = handleChemicalDisinfectFlushDrainR1State(); + break; + + case DG_CHEM_DISINFECT_STATE_PRIME_ACID_LINE: + chemDisinfectState = handleChemicalDisinfectPrimeAcidLineState(); + break; + + case DG_CHEM_DISINFECT_STATE_FILL_WITH_WATER_AND_DISINFECTANT: + chemDisinfectState = handleChemicalDisinfectFillWithWaterAndDisinfectantState(); + break; + + case DG_CHEM_DISINFECT_STATE_REMOVE_ACID_BOTTLE_FROM_UI: + chemDisinfectState = handleChemicalDisinfectRemoveAcidBottleFromUIState(); + break; + + case DG_CHEM_DISINFECT_STATE_DISINFECT_R1_TO_R2: + chemDisinfectState = handleChemicalDisinfectDisinfectR1ToR2State(); + break; + + case DG_CHEM_DISINFECT_STATE_FILL_R2_WITH_DISINFECTANT: + chemDisinfectState = handleChemicalDisinfectFillR2WithDisinfectantState(); + break; + + case DG_CHEM_DISINFECT_STATE_DISINFECT_R2_TO_R1: + chemDisinfectState = handleChemicalDisinfectDisinfectR2ToR1State(); + break; + + case DG_CHEM_DISINFECT_STATE_COOL_DOWN_HEATERS: + chemDisinfectState = handleChemicalDisinfectCoolDownHeatersState(); + break; + + case DG_CHEM_DISINFECT_STATE_DISINFECTANT_DRAIN_R1: + chemDisinfectState = handleChemicalDisinfectDisinfectantDrainR1State(); + break; + + case DG_CHEM_DISINFECT_STATE_DISINFECTANT_DRAIN_R2: + chemDisinfectState = handleChemicalDisinfectDisinfectantDrainR2State(); + break; + + case DG_CHEM_DISINFECT_STATE_RINSE_R1_TO_R2: + chemDisinfectState = handleChemicalDisinfectRinseR1ToR2State(); + break; + + case DG_CHEM_DISINFECT_STATE_RINSE_R2_TO_R1_AND_DRAIN_R1: + chemDisinfectState = handleChemicalDisinfectRinseR2ToR1AndDrainR1State(); + break; + + case DG_CHEM_DISINFECT_STATE_RINSE_R1_TO_R2_AND_DRAIN_R2: + chemDisinfectState = handleChemicalDisinfectRinseR1ToR2AndDrainR2State(); + break; + + case DG_CHEM_DISINFECT_STATE_RINSE_CIRCULATION: + chemDisinfectState = handleChemicalDisinfectRinseCirculationState(); + 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: - // TODO - s/w fault - chemState = DG_CHEM_DISINFECT_STATE_START; + 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; } - return chemState; + publishChemicalDisinfectData(); + + return chemDisinfectState; } /*********************************************************************//** * @brief - * The getCurrentChemicalDisinfectState function returns the current state of the - * chemical disinfect mode. - * @details - * Inputs : chemState - * Outputs : none + * 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 chemState; + 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(); + + F32 ppiPressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); + F32 THdTemp = getTemperatureValue( TEMPSENSORS_HEAT_DISINFECT ); + F32 TPoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); + + // If the inlet pressure is less than the threshold and TDi and TRo difference is greater than 3 C, the cycle should be canceled + if ( ( ppiPressure < MIN_INLET_PRESSURE_PSI ) && ( fabs( THdTemp - TPoTemp ) > MAX_START_STATE_TEMP_SENSORS_DIFF_C ) ) + { + prevChemDisinfectState = state; + alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_INLET_PRES_AND_TEMP_SNSRS_OUT; + state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; + } + else + { + // Close VPi to prevent wasting water + setValveState( VPI, VALVE_STATE_CLOSED ); + + // Set the actuators to drain R1 + setValveState( VRD1, VALVE_STATE_OPEN ); + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + + rsrvrFillStableTimeCounter = 0; + // Assume reservoir 1 is full and drain it + rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; + 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 ) + { + 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; + + // Done with draining + signalDrainPumpHardStop(); + + // Set the valves to flush the recirculation line + setValveState( VPI, VALVE_STATE_OPEN ); + setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); + // Done with draining R1 + setValveState( VRD1, VALVE_STATE_CLOSED ); + setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); + setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); + setValveState( VRC, VALVE_STATE_DRAIN_C_TO_NO ); + + // Set the RO pump to run at full pressure + setROPumpTargetFlowRateLPM( RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM, MAX_RO_PUMP_FLUSH_FILL_PRESSURE_PSI ); + + // Done with final draining + isThisLastDrain = FALSE; + state = DG_CHEM_DISINFECT_STATE_RINSE_CIRCULATION; + } + else + { + // Assume reservoir 2 is full and drain it + rsrvr2Status = DG_RESERVOIR_ABOVE_TARGET; + + 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 ); + state = DG_CHEM_DISINFECT_STATE_DRAIN_R2; + } + + // Start the timer + stateTimer = getMSTimerCount(); + } + 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 ) + { + 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; + + setValveState( VRD1, VALVE_STATE_OPEN ); + setValveState( VRD2, VALVE_STATE_CLOSED ); + rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; + state = DG_CHEM_DISINFECT_STATE_DRAIN_R1; + } + else + { + tareLoadCell( LOAD_CELL_RESERVOIR_2_PRIMARY ); + tareLoadCell( LOAD_CELL_RESERVOIR_2_BACKUP ); + + signalDrainPumpHardStop(); + setValveState( VRD2, VALVE_STATE_CLOSED ); + setValveState( VPI, VALVE_STATE_OPEN ); + + 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; + + // Check if flush time has elapsed + if ( TRUE == didTimeout( stateTimer, FLUSH_DRAIN_WAIT_TIME_MS ) ) + { + BOOL hasConductivityPassed = FALSE; + + // If the inlet temperature and conductivity are in range, move onto the next state + if ( ( getTemperatureValue( TEMPSENSORS_INLET_PRIMARY_HEATER ) > MIN_INLET_TEMPERATURE_C ) && + ( getConductivityValue( CONDUCTIVITYSENSORS_CPI_SENSOR ) <= MAX_INLET_CONDUCTIVITY_US_PER_CM ) ) + { + hasConductivityPassed = TRUE; + } + +#ifndef _RELEASE_ + if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_DISABLE_DISINFECT_CONDUCTIVITY_CHECK ) ) + { + hasConductivityPassed = TRUE; + } +#endif + + if ( TRUE == hasConductivityPassed ) + { + setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); + setROPumpTargetFlowRateLPM( RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM, MAX_RO_PUMP_FLUSH_FILL_PRESSURE_PSI ); + + stateTimer = getMSTimerCount(); + stateTrialCounter = 0; + state = DG_CHEM_DISINFECT_STATE_FLUSH_CIRCULATION; + } + // If the number of failures have not exceeded the limit, try again. + else if ( stateTrialCounter < MAX_ALLOWED_STATE_TRIALS ) + { + stateTrialCounter++; + stateTimer = getMSTimerCount(); + } + // Couldn't get a good water sample after a couple of trials and the disinfect cycle failed + else + { + alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_INLET_COND_AND_TEMP_OUT; + 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, FLUSH_CICRCULATION_WAIT_TIME_MS ) ) && ( FALSE == areTempSensorsInRange ) ) + { + F32 ThdTemp = getTemperatureValue( TEMPSENSORS_HEAT_DISINFECT ); + F32 TPoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); + F32 TD1Temp = getTemperatureValue( TEMPSENSORS_CONDUCTIVITY_SENSOR_1 ); + F32 TD2Temp = getTemperatureValue( TEMPSENSORS_CONDUCTIVITY_SENSOR_2 ); + F32 avgTemp = ( ThdTemp + TPoTemp + TD1Temp + TD2Temp ) / NUM_OF_TEMP_SENSORS_TO_AVG; + + // Check if any of the temperature sensors deviate for more than the defined value from the average of all + // of the temperature sensors + BOOL isThdOut = ( fabs( ThdTemp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C ? TRUE : FALSE ); + BOOL isTPoOut = ( fabs( TPoTemp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C ? TRUE : FALSE ); + BOOL isTD1Out = ( fabs( TD1Temp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C ? TRUE : FALSE ); + BOOL isTD2Out = ( fabs( TD2Temp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C ? TRUE : FALSE ); + + // Check if any of the temperature sensors are out of tolerance + if( ( TRUE == isThdOut ) || ( TRUE == isTPoOut ) || ( TRUE == isTD1Out ) || ( TRUE == isTD2Out ) ) + { + // Check if we have exceeded the number of trials. If not, try another time + if ( ++stateTrialCounter < MAX_ALLOWED_STATE_TRIALS ) + { + stateTimer = getMSTimerCount(); + } + // State failed. Cancel chemical disinfect mode + else + { + alarmDetectedPendingTrigger = ALARM_ID_DG_TEMP_SENSORS_DIFF_OUT_OF_RANGE; + prevChemDisinfectState = state; + state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; + } + } + else + { + areTempSensorsInRange = TRUE; + stateTimer = getMSTimerCount(); + + // Set the concentrate pump to run at a constant speed during priming in reverse + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, -1.0F * CONC_PUMP_PRIME_SPEED_ML_PER_MIN ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); + } + } + + // Only start the concentrate pumps if the temperature sensors are in range + if ( ( TRUE == areTempSensorsInRange ) && ( TRUE == didTimeout( stateTimer, FLUSH_CICRCULATION_WAIT_TIME_MS ) ) ) + { + rsrvr1Status = DG_RESERVOIR_BELOW_TARGET; + rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; + + // Turn off the concentrate pumps + requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); + + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); + setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); + setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); + + // Turn on the bicarb line with forward direction, to dispense the chemical and mix + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, CONC_PUMP_PRIME_SPEED_ML_PER_MIN ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); + + stateTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_FLUSH_R1_AND_R2; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleChemicalDisinfectFlushR1AndR2State function handles the + * chemical disinfect flush reservoir 1 and reservoir 2 state. If the + * reservoirs did not flush within a period time, the state transitions + * to water cancellation path. + * @details Inputs: stateTimer, rsrvr1Status, rsrvr2Status, + * prevChemDisinfectState + * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, + * prevChemDisinfectState + * @return next state of the chemical disinfect state machine + *************************************************************************/ +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFlushR1AndR2State( void ) +{ + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_FLUSH_R1_AND_R2; + + // If R1 is not full, keep monitoring for R1 level and timeout + if ( DG_RESERVOIR_BELOW_TARGET == rsrvr1Status ) + { + rsrvr1Status = getRsrvrFillStatus( DG_RESERVOIR_1, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS ); + + // Keep monitoring the status of reservoir 2 at the same time + rsrvr2Status = getRsrvrFillStatus( DG_RESERVOIR_2, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_500ML_FILL_UP_TIMEOUT_MS ); + // Reservoir 2 cannot be filled before reservoir 1 is filled and is overflowing to reservoir 2. If reservoir 2 has already + // reached to target volume, it means reservoir 1's load cell might be reading incorrect values. This situation might continue + // until reservoir 2 is filled up and the tubing might expand or leak. + if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) + { + prevChemDisinfectState = state; + alarmDetectedPendingTrigger = ALARM_ID_DG_INVALID_LOAD_CELL_VALUE; + state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + } + } + // Once R1 is full, keep monitoring for R2 level and timeout + else if( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) + { + rsrvr2Status = getRsrvrFillStatus( DG_RESERVOIR_2, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_500ML_FILL_UP_TIMEOUT_MS ); + + // Once R2 is full (to 500mL in this case), transition to the next state + if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) + { + // Set the actuators to flush R2 and drain R1 state + setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); + setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); + setValveState( VRO, VALVE_STATE_R2_C_TO_NC ); + setValveState( VRD1, VALVE_STATE_OPEN ); + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + stateTimer = getMSTimerCount(); + + // Set both reservoirs status + rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; + rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; + state = DG_CHEM_DISINFECT_STATE_FLUSH_R2_AND_DRAIN_R1; + } + 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 handleChemicalDisinfectFlushR2AndDrainR1State function handles the + * chemical disinfect flush reservoir 2 and drain reservoir 1 state. The + * state drains reservoir 1 and flushes reservoir 2 at the same time until + * the water in reservoir 2 overflows to reservoir 1. If this process is + * done within a certain period of time, it transitions to the next state. + * If the drain process times out, it transitions to basic cancellation and + * if the flush process times out, it transitions to water cancellation. + * @details Inputs: stateTimer, rsrvr1Status, rsrvr2Status + * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, + * prevChemDisinfectState + * @return next state of the chemical disinfect state machine + *************************************************************************/ +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFlushR2AndDrainR1State( void ) +{ + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_FLUSH_R2_AND_DRAIN_R1; + + // If reservoir 1 is empty, turn off the drain pump + 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 ) + { + // Done with draining R1 + signalDrainPumpHardStop(); + + setValveState( VRD1, VALVE_STATE_CLOSED ); + } + else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) + { + prevChemDisinfectState = state; + state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; + } + + // First reservoir 2 must be completely full + if ( DG_RESERVOIR_BELOW_TARGET == rsrvr2Status ) + { + rsrvr2Status = getRsrvrFillStatus( DG_RESERVOIR_2, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS ); + + U32 drainPumpRPM = getDrainPumpTargetRPM(); + // Keep monitoring the status of reservoir 1 as the same time + F32 volume = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); + // Reservoir 1 cannot be filled before reservoir 2 is filled and is overflowing to reservoir 1. If reservoir 1 has already + // reached to target volume, it means reservoir 2's load cell might be reading incorrect values. This situation might continue + // until reservoir 1 is filled up and the tubing might expand or leak. + // Before checking whether reservoir 1 is filled pre-maturely, we have to make sure reservoir 1 is drained completely to make + // sure the extra volume that is read is not because of previous water that is being drained currently and it is above 500 mL + if ( ( volume >= RSRVRS_PARTIAL_FILL_VOL_ML ) && ( 0 == drainPumpRPM ) ) + { + prevChemDisinfectState = state; + alarmDetectedPendingTrigger = ALARM_ID_DG_INVALID_LOAD_CELL_VALUE; + state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + } + } + // Once R2 is full, R1 must be partially full + else if( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) + { + rsrvr1Status = getRsrvrFillStatus( DG_RESERVOIR_1, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_500ML_FILL_UP_TIMEOUT_MS ); + + // Once R1 is partially full, transition to the next state + if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) + { + // Done with filing turn off the RO pump + signalROPumpHardStop(); + + // Set the valves to drain R2 and no fill + setValveState( VPI, VALVE_STATE_CLOSED ); + setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); + setValveState( VRD2, VALVE_STATE_OPEN ); + setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); + setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + // Start the timer for drain timeout + stateTimer = getMSTimerCount(); + rsrvr2Status = DG_RESERVOIR_ABOVE_TARGET; + state = DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN_R2; + } + else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) + { + prevChemDisinfectState = state; + state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + } + } + else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) + { + prevChemDisinfectState = state; + state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleChemicalDisinfectFlushDrainR2State function handles the + * chemical disinfect flush drain reservoir 2 state. The state drains + * reservoir 2 and if the drain times out, it transitions to basic + * cancellation. If the drain is finished within a certain period of time, + * it transitions to the next state. + * @details Inputs: stateTimer, rsrvr1Status, rsrvr2Status + * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, + * prevChemDisinfectState + * @return next state of the chemical disinfect state machine + *************************************************************************/ +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFlushDrainR2State( void ) +{ + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN_R2; + + // If reservoir 2 is empty, set the drain valve to drain R1 + 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 ) + { + setValveState( VRD1, VALVE_STATE_OPEN ); + setValveState( VRD2, VALVE_STATE_CLOSED ); + // Start the timer for drain timeout + stateTimer = getMSTimerCount(); + rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; + state = DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN_R1; + } + else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) + { + prevChemDisinfectState = state; + state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleChemicalDisinfectFlushDrainR1State function handles the + * chemical disinfect flush drain reservoir 1 state. The state drains + * reservoir 1 and if the drain times out, it transitions to basic + * cancellation. If the drain is finished within a certain period of time, + * it transitions to the next state. + * @details Inputs: stateTimer, rsrvr1Status, rsrvr2Status + * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, + * prevChemDisinfectState + * @return next state of the chemical disinfect state machine + *************************************************************************/ +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFlushDrainR1State( void ) +{ + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN_R1; + + // If reservoir 1 is empty, set the state to fill water state + 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 ) + { + // Done with draining the reservoirs + signalDrainPumpHardStop(); + + // Set the concentrate pump to run at a constant speed during priming + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, CONC_PUMP_PRIME_SPEED_ML_PER_MIN ); + + // The bicarb line is used to inject the acid into the fluid path during chemical disinfect + requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); // TODO why do we need to turn off CP2 while we are going to turn it on immediately? + + // Start the timer for drain timeout + stateTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_PRIME_ACID_LINE; + } + else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) + { + prevChemDisinfectState = state; + state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; + } + + 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 handleChemicalDisinfectPrimeAcidLineState( void ) +{ + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_PRIME_ACID_LINE; + F32 cd2Conductivity = getConductivityValue( (U32)CONDUCTIVITYSENSORS_CD2_SENSOR ); + + // Set the chemical disinfect that is published on the UI + chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_MIX_WATER_AND_ACID; + + if ( cd2Conductivity <= MIN_ACID_CONDUCTIVITY_US_PER_CM ) + { + primeAcidSteadyStateCounter = 0; + } + else + { + // Check if the acid conductivity value has been + if ( ++primeAcidSteadyStateCounter >= PRIME_ACID_STEADY_CONDUCTIVITY_TIME_MS ) + { + // Turn off the concentrate pump for now until there is sufficient RO flow to turn it + // back for mixing + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); + + // Prepare for filling the reservoirs and heating the water + setValveState( VPI, VALVE_STATE_OPEN ); + setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); + setValveState( VRD1, VALVE_STATE_CLOSED ); + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); + setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); + setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); + setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); + + // Turn on the RO pump + setROPumpTargetFlowRateLPM( RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM, MAX_RO_PUMP_FLUSH_FILL_PRESSURE_PSI ); + + // Start heating the water while we are filling up the reservoirs + setHeaterTargetTemperature( DG_PRIMARY_HEATER, CHEM_DISINFECT_TARGET_TEMPERATURE_C ); + startHeater( DG_PRIMARY_HEATER ); + + rsrvr1Status = DG_RESERVOIR_BELOW_TARGET; + rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; + state = DG_CHEM_DISINFECT_STATE_FILL_WITH_WATER_AND_DISINFECTANT; + } + } + + 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 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 handleChemicalDisinfectFillWithWaterAndDisinfectantState( void ) +{ + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_FILL_WITH_WATER_AND_DISINFECTANT; + + // Get the flow rate that is used for mixing + F32 measuredROFlowRate = getMeasuredROFlowRateLPM(); + + // If the flow is less than the minimum, request the concentrate pump to be on but do + // control it. + if ( measuredROFlowRate < MIN_RO_FLOW_FOR_CONC_PUMP_MIXING_LPM ) + { + requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); + } + else + { + F32 acidCP2PumpFlowRate = ACID_TO_WATER_MIXING_RATIO * measuredROFlowRate * ML_PER_LITER; + + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, acidCP2PumpFlowRate ); + + requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); + } + + // 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_PARTIAL_FILL_VOL_ML, RSRVRS_500ML_FILL_UP_TIMEOUT_MS ); + + // Once reservoir 2 is full, set the actuators for recirculation + if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) + { + // Set the valves to drain R2 and no fill + setValveState( VPI, VALVE_STATE_CLOSED ); + setValveState( VBF, VALVE_STATE_OPEN ); + setValveState( VPD, VALVE_STATE_DRAIN_C_TO_NO ); + setValveState( VRD2, VALVE_STATE_OPEN ); + setValveState( VDR, VALVE_STATE_RECIRC_C_TO_NC ); + setValveState( VRC, VALVE_STATE_RECIRC_C_TO_NC ); + + // Done with mixing acid + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); + + // Set the drain pump to control mode + setDrainPumpTargetOutletPressure( CHEM_DISINFECT_TARGET_DRAIN_PRES_PSI ); + + setROPumpTargetFlowRateLPM( CHEM_DISINFECT_TARGET_RO_FLOW_LPM, CHEM_DISINFECT_MAX_RO_PRESSURE_PSI ); + + // 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 + R1ChemDisinfectVol = getLoadCellLargeFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); + R2ChemDisinfectVol = getLoadCellLargeFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); + + stateTimer = getMSTimerCount(); + rsrvrsVolMonitorTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_REMOVE_ACID_BOTTLE_FROM_UI; + } + 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 handleChemicalDisinfectRemoveAcidBottleFromUIState function + * handles the chemical disinfect remove acid bottle from UI state. + * The state fills sends a command to the UI to prompt the user to remove + * the acid bottle and shut the acid and bicarb line. The state continues + * waiting in this state until the user confirms that the bottle has been + * removed and acid and bicarb lines are closed. + * @details Inputs: TODO fill up + * @details Outputs: chemDisinfectUIState TODO fill up + * @return next state of the chemical disinfect state machine + *************************************************************************/ +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectRemoveAcidBottleFromUIState( void ) +{ + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_REMOVE_ACID_BOTTLE_FROM_UI; + + // Set the chemical disinfect that is published on the UI + chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_REMOVE_ACID; + + // TODO fill up. We should wait for the user until the acid bottle is removed and it is confirmed by the user + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, -1.0F * CONC_PUMP_PRIME_SPEED_ML_PER_MIN ); + state = DG_CHEM_DISINFECT_STATE_DISINFECT_R1_TO_R2; + + return state; +} + +/*********************************************************************//** + * @brief + * The handleChemicalDisinfectDisinfectR1ToR2State 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 handleChemicalDisinfectDisinfectR1ToR2State( void ) +{ + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_DISINFECT_R1_TO_R2; + CHEM_DISINFECT_STATUS_T status = getChemicalDisinfectStatus(); + + // Set the chemical disinfect that is published on the UI + chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_DISINFECT_DEVICE; + + switch ( status ) + { + case CHEM_DISINFECT_RSRVRS_LEAK_TIMEOUT: + case CHEM_DISINFECT_HEAT_UP_TIMEOUT: + state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + break; + + case CHEM_DISINFECT_COMPLETE: + // Turn off the concentrate pumps + requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); + + // Set the valves to transfer hot water from R1 to R2 and fill up R2. + 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 ); + // Although there is fluid in both reservoirs, but they are set to empty + // to begin the transition of disinfectant water from R1 to R2. + rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; + rsrvr1Status = DG_RESERVOIR_BELOW_TARGET; + stateTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_FILL_R2_WITH_DISINFECTANT; + break; + + default: + // Do nothing, chemical disinfect is in progress + break; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleChemicalDisinfectFillR2WithDisinfectantState function + * handles fill R2 with water state. The state transfers disinfectant from + * reservoir 1 to reservoir 2 until disinfectant overflows from reservoir 2 + * to reservoir 1. + * If the fill times out, it transitions to water cancellation state, + * otherwise, it transitions to the next state. + * @details Inputs: rsrvr1Status, rsrvr2Status + * @details Outputs: rsrvr1Status, rsrvr2Status, R1ChemDisinfectVol, + * R2ChemDisinfectVol, prevChemDisinfectState + * @return next state of the chemical disinfect state machine + *************************************************************************/ +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFillR2WithDisinfectantState( void ) +{ + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_FILL_R2_WITH_DISINFECTANT; + + // First reservoir 1 must be partially full + if ( DG_RESERVOIR_BELOW_TARGET == rsrvr1Status ) + { + rsrvr1Status = getRsrvrFillStatus( DG_RESERVOIR_1, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_500ML_FILL_UP_TIMEOUT_MS ); + } + else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) + { + rsrvr2Status = getRsrvrFillStatus( DG_RESERVOIR_2, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS ); + + if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) + { + // Get the current volumes to be monitored during R2 to R1 chemical disinfect state + R1ChemDisinfectVol = getLoadCellLargeFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); + R2ChemDisinfectVol = getLoadCellLargeFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); + + state = DG_CHEM_DISINFECT_STATE_DISINFECT_R2_TO_R1; + } + 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 handleChemicalDisinfectDisinfectR2ToR1State function handles the + * chemical disinfect R2 to R1 state. If the reservoirs leak or it cannot + * reach to temperature within a certain period of time, it transitions to + * water cancellation state. If heat disinfect reservoir 1 to reservoir 2 is + * completed, it transitions to the next state. + * @details Inputs: stateTimer, rsrvr1Status + * @details Outputs: stateTimer, rsrvr1Status + * @return next state of the heat disinfect state machine + *************************************************************************/ +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDisinfectR2ToR1State( void ) +{ + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_DISINFECT_R2_TO_R1; + CHEM_DISINFECT_STATUS_T status = getChemicalDisinfectStatus(); + + switch ( status ) + { + case CHEM_DISINFECT_RSRVRS_LEAK_TIMEOUT: + case CHEM_DISINFECT_HEAT_UP_TIMEOUT: + state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + break; + + case CHEM_DISINFECT_COMPLETE: + // Turn off the heaters + stopHeater( DG_PRIMARY_HEATER ); + stateTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_COOL_DOWN_HEATERS; + + // Set the disinfect flags + setDisinfectStatus( TRUE ); + setLastDisinfectDate( getRTCTimestamp() ); + break; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleChemicalDisinfectCoolDownHeatersState function handles the + * chemical disinfect cool down heaters state. The state continues running + * the fluid while the heaters are off for a certain period of time. + * @details Inputs: stateTimer + * @details Outputs: stateTimer + * @return next state of the heat disinfect state machine + *************************************************************************/ +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectCoolDownHeatersState( void ) +{ + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_COOL_DOWN_HEATERS; + + if ( TRUE == didTimeout( stateTimer, POST_CHEM_DISINFECT_WAIT_TIME_MS ) ) + { + // Stop the drain pump and the RO pump to exit the closed loop + signalDrainPumpHardStop(); + signalROPumpHardStop(); + + if ( 0 == getDrainPumpTargetRPM() ) + { + // De-energize all the valves that are not in the path anymore + // and wait for the RO membrane to be cooled down. + setValveState( VPI, VALVE_STATE_CLOSED ); + setValveState( VBF, VALVE_STATE_CLOSED ); + setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); + setValveState( VRC, VALVE_STATE_DRAIN_C_TO_NO ); + setValveState( VPD, VALVE_STATE_DRAIN_C_TO_NO ); + setValveState( VRD1, VALVE_STATE_OPEN ); + rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + + stateTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_DISINFECTANT_DRAIN_R1; + } + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleChemicalDisinfectDisinfectantDrainR1State function handles + * the chemical disinfect disinfectant drain R1 state. The state drains + * reservoir 1 and if it times out, it transitions to basic cancellation + * state. Otherwise, it transitions to the next state. + * @details Inputs: rsrvr1Status, rsrvr2Status + * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, + * prevChemDisinfectState, chemDisinfectUIState + * @return next state of the chemical disinfect state machine + *************************************************************************/ +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDisinfectantDrainR1State( void ) +{ + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_DISINFECTANT_DRAIN_R1; + + // Set the chemical disinfect that is published on the UI + chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_FLUSH_AFTER_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 ) + { + // Done with draining reservoir 1 + setValveState( VRD1, VALVE_STATE_CLOSED ); + // Set the drain valve to reservoir 2 + setValveState( VRD2, VALVE_STATE_OPEN ); + + rsrvr2Status = DG_RESERVOIR_ABOVE_TARGET; + stateTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_DISINFECTANT_DRAIN_R2; + } + else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) + { + prevChemDisinfectState = state; + state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleChemicalDisinfectDisinfectantDrainR1State function handles + * the chemical disinfect disinfectant drain R2 state. The state drains + * reservoir 1 and if it times out, it transitions to basic cancellation + * state. Otherwise, it transitions to the next state. + * @details Inputs: rsrvr1Status, rsrvr2Status + * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, + * prevChemDisinfectState + * @return next state of the chemical disinfect state machine + *************************************************************************/ +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDisinfectantDrainR2State( void ) +{ + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_DISINFECTANT_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 ) + { + // Done with draining the reservoirs + signalDrainPumpHardStop(); + + // Set the valves to fill up R1 and overflow to R2 + setValveState( VPI, VALVE_STATE_OPEN ); + setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); + // Done with draining reservoir 2 + setValveState( VRD2, VALVE_STATE_CLOSED ); + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); + setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); + setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); + setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); + setROPumpTargetFlowRateLPM( RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM, MAX_RO_PUMP_FLUSH_FILL_PRESSURE_PSI ); + + rsrvr1Status = DG_RESERVOIR_BELOW_TARGET; + rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; + + stateTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_RINSE_R1_TO_R2; + } + else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) + { + prevChemDisinfectState = state; + state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleChemicalDisinfectRinseR1ToR2State function handles the chemical + * disinfect rinse R1 to R2 state. The state rinses reservoir 1 to reservoir + * 2. If the rinse process times out, it transitions to water cancellation + * state, otherwise, it transitions to the next state. + * @details Inputs: rsrvr1Status, rsrvr2Status + * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, + * prevChemDisinfectState, alarmDetectedPendingTrigger + * @return next state of the chemical disinfect state machine + *************************************************************************/ +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectRinseR1ToR2State( void ) +{ + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_RINSE_R1_TO_R2; + + if ( DG_RESERVOIR_BELOW_TARGET == rsrvr1Status ) + { + rsrvr1Status = getRsrvrFillStatus( DG_RESERVOIR_1, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS ); + + // Keep monitoring the status of reservoir 2 at the same time + rsrvr2Status = getRsrvrFillStatus( DG_RESERVOIR_2, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_500ML_FILL_UP_TIMEOUT_MS ); + // Reservoir 2 cannot be filled before reservoir 1 is filled and is overflowing to reservoir 2. If reservoir 2 has already + // reached to target volume, it means reservoir 1's load cell might be reading incorrect values. This situation might continue + // until reservoir 2 is filled up and the tubing might expand or leak. + if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) + { + prevChemDisinfectState = state; + alarmDetectedPendingTrigger = ALARM_ID_DG_INVALID_LOAD_CELL_VALUE; + state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + } + } + else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) + { + rsrvr2Status = getRsrvrFillStatus( DG_RESERVOIR_2, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_500ML_FILL_UP_TIMEOUT_MS ); + + if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) + { + // Set the valves to rinse R2 to R1 and drain R1 + setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); + setValveState( VRO, VALVE_STATE_R2_C_TO_NC ); + setValveState( VRD1, VALVE_STATE_OPEN ); + setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + + rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; + rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; + stateTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_RINSE_R2_TO_R1_AND_DRAIN_R1; + } + 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 handleChemicalDisinfectRinseR2ToR1AndDrainR1State function handles + * the chemical disinfect rinse R2 to R1 and drain R1 state. The state + * rinses reservoir 2 and drains reservoir 1 at the same time. If the drain + * process times out, it transitions to basic cancellation state, and + * if the rinse times out, it transitions to water cancellation state. + * If the drain and rinse are completed within the define time, it + * transitions to the next state. + * @details Inputs: rsrvr1Status, rsrvr2Status + * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, + * prevChemDisinfectState, alarmDetectedPendingTrigger + * @return next state of the chemical disinfect state machine + *************************************************************************/ +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectRinseR2ToR1AndDrainR1State( void ) +{ + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_RINSE_R2_TO_R1_AND_DRAIN_R1; + + 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 ) + { + // Done with draining R1 + signalDrainPumpHardStop(); + setValveState( VRD1, VALVE_STATE_CLOSED ); + } + else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) + { + prevChemDisinfectState = state; + state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; + } + + // First reservoir 2 must be completely full + if ( DG_RESERVOIR_BELOW_TARGET == rsrvr2Status ) + { + rsrvr2Status = getRsrvrFillStatus( DG_RESERVOIR_2, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS ); + + U32 drainPumpRPM = getDrainPumpTargetRPM(); + // Keep monitoring the status of reservoir 1 as the same time + F32 volume = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); + // Reservoir 1 cannot be filled before reservoir 2 is filled and is overflowing to reservoir 1. If reservoir 1 has already + // reached to target volume, it means reservoir 2's load cell might be reading incorrect values. This situation might continue + // until reservoir 1 is filled up and the tubing might expand or leak. + // Before checking whether reservoir 1 is filled pre-maturely, we have to make sure reservoir 1 is drained completely to make + // sure the extra volume that is read is not because of previous water that is being drained currently and it is above 500 mL + if ( ( volume >= RSRVRS_PARTIAL_FILL_VOL_ML ) && ( 0 == drainPumpRPM ) ) + { + prevChemDisinfectState = state; + alarmDetectedPendingTrigger = ALARM_ID_DG_INVALID_LOAD_CELL_VALUE; + state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + } + } + // Once reservoir 2 is completely full, monitor reservoir 1 + else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) + { + rsrvr1Status = getRsrvrFillStatus( DG_RESERVOIR_1, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_500ML_FILL_UP_TIMEOUT_MS ); + + if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) + { + // Set the valves to rinse R2 to R1 and drain R1 + setValveState( VRD2, VALVE_STATE_OPEN ); + setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); + setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); + setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); + + // Turn on the drain pump to drain R2 + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + + // Set the reservoir status + rsrvr1Status = DG_RESERVOIR_BELOW_TARGET; + rsrvr2Status = DG_RESERVOIR_ABOVE_TARGET; + state = DG_CHEM_DISINFECT_STATE_RINSE_R1_TO_R2_AND_DRAIN_R2; + stateTimer = getMSTimerCount(); + } + else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) + { + prevChemDisinfectState = state; + state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + } + } + else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) + { + prevChemDisinfectState = state; + state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleChemicalDisinfectRinseR1ToR2AndDrainR2State function handles + * the chemical disinfect rinse R1 to R2 and drain R2 state. The state + * rinses reservoir 1 and drains reservoir 2 at the same time. If the drain + * process times out, it transitions to basic cancellation state, and + * if the rinse times out, it transitions to water cancellation state. + * If the drain and rinse are completed within the define time, it + * transitions to the next state. + * @details Inputs: rsrvr1Status, rsrvr2Status, numberOfPostDisinfectRinses + * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, + * prevChemDisinfectState, alarmDetectedPendingTrigger, isThisLastDrain + * numberOfPostDisinfectRinses + * @return next state of the chemical disinfect state machine + *************************************************************************/ +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectRinseR1ToR2AndDrainR2State( void ) +{ + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_RINSE_R1_TO_R2_AND_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 ) + { + // Done with draining R2 + signalDrainPumpHardStop(); + setValveState( VRD2, VALVE_STATE_CLOSED ); + } + else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) + { + prevChemDisinfectState = state; + state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; + } + + // First reservoir 1 must be completely full + if ( DG_RESERVOIR_BELOW_TARGET == rsrvr1Status ) + { + rsrvr1Status = getRsrvrFillStatus( DG_RESERVOIR_1, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS ); + + U32 drainPumpRPM = getDrainPumpTargetRPM(); + // Keep monitoring the status of reservoir 2 as the same time + F32 volume = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); + // Reservoir 1 cannot be filled before reservoir 2 is filled and is overflowing to reservoir 1. If reservoir 1 has already + // reached to target volume, it means reservoir 2's load cell might be reading incorrect values. This situation might continue + // until reservoir 1 is filled up and the tubing might expand or leak. + // Before checking whether reservoir 1 is filled pre-maturely, we have to make sure reservoir 1 is drained completely to make + // sure the extra volume that is read is not because of previous water that is being drained currently and it is above 500 mL + if ( ( volume >= RSRVRS_PARTIAL_FILL_VOL_ML ) && ( 0 == drainPumpRPM ) ) + { + prevChemDisinfectState = state; + alarmDetectedPendingTrigger = ALARM_ID_DG_INVALID_LOAD_CELL_VALUE; + state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + } + } + // Once reservoir 1 is completely full, monitor reservoir 2 + else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) + { + rsrvr2Status = getRsrvrFillStatus( DG_RESERVOIR_2, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_500ML_FILL_UP_TIMEOUT_MS ); + + if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) + { + // If the number of post chemical disinfect rinses have finished, + // transition to drain R1 state. Otherwise, start the next rinse. + if ( ++numberOfPostDisinfectRinses >= NUM_OF_POST_DISINFECT_RINSES ) + { + // Done with filling, turn off the RO pump + signalROPumpHardStop(); + + // Set the rest of the valve to be in the de-enrgize mode + 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_OPEN ); + + // Turn on the drain pump to drain R2 + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + + // Set the reservoir status + rsrvr2Status = DG_RESERVOIR_ABOVE_TARGET; + // This is last drain + isThisLastDrain = TRUE; + state = DG_CHEM_DISINFECT_STATE_DRAIN_R2; + } + else + { + // Set the reservoirs' status + setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); + setValveState( VRO, VALVE_STATE_R2_C_TO_NC ); + setValveState( VRD1, VALVE_STATE_OPEN ); + setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + + rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; + rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; + state = DG_CHEM_DISINFECT_STATE_RINSE_R2_TO_R1_AND_DRAIN_R1; + } + + stateTimer = getMSTimerCount(); + } + 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 handleChemicalDisinfectRinseCirculationState function handles the + * chemical disinfect rinse RO circulation and concentrate pumps state. + * Once the defined flush circulation time has elapsed, it transitions to + * the next state. + * @details Inputs: stateTimer + * @details Outputs: none + * @return next state of the chemical disinfect state machine + *************************************************************************/ +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectRinseCirculationState( void ) +{ + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_RINSE_CIRCULATION; + + if ( TRUE == didTimeout( stateTimer, FLUSH_CICRCULATION_WAIT_TIME_MS ) ) + { + state = DG_CHEM_DISINFECT_STATE_COMPLETE; + } + + 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 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 ThdTemp = getTemperatureValue( TEMPSENSORS_HEAT_DISINFECT ); + 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; + } + + // If the coldest spot which is THd is less than minimum chemical disinfect temperature, + // reset the chemical disinfect timers and check whether heating up has timed out + if ( ThdTemp < CHEM_DISINFECT_TARGET_TEMPERATURE_C ) + { + // Keep reseting the disinfect timer so the elapsed time is always 0 until disinfect truly starts + chemDisinfectTimer = getMSTimerCount(); + isPartialDisinfectInProgress = FALSE; + + if ( TRUE == didTimeout( stateTimer, CHEM_DISINFECT_START_TEMP_TIMOUT_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 if ( ( isPartialDisinfectInProgress != TRUE ) && ( ThdTemp > CHEM_DISINFECT_TARGET_TEMPERATURE_C ) ) + { + // 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 disnfect. 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_DISINFECT_R1_TO_R2 == chemDisinfectState ) || + ( DG_CHEM_DISINFECT_STATE_DISINFECT_R2_TO_R1 == chemDisinfectState ) ) + { + uiData.chemDisinfectTargetTime = CHEM_DISINFECT_TIME_MS; + uiData.chemDisinfectCountdownTime = CHEM_DISINFECT_TIME_MS - calcTimeSince( chemDisinfectTimer ); + data.R1FillLevel = R1ChemDisinfectVol; + data.R2FillLevel = R2ChemDisinfectVol; + } + else + { + uiData.chemDisinfectCountdownTime = 0.0; + data.R1FillLevel = 0.0; + data.R2FillLevel = 0.0; + } + + data.postDisinfectTargetRinseCount = NUM_OF_POST_DISINFECT_RINSES; + data.postDisinfectCurrentRinseCount = numberOfPostDisinfectRinses; + 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 isAlarmNeeded = FALSE; + + 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: + case DG_CHEM_DISINFECT_STATE_FLUSH_CIRCULATION: + case DG_CHEM_DISINFECT_STATE_FLUSH_R1_AND_R2: + case DG_CHEM_DISINFECT_STATE_FLUSH_R2_AND_DRAIN_R1: + case DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN_R2: + case DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN_R1: + if ( ( STATE_OPEN == getSwitchStatus( CONCENTRATE_CAP ) ) || ( STATE_OPEN == getSwitchStatus( DIALYSATE_CAP ) ) ) + { + isAlarmNeeded = TRUE; + } + break; + + case DG_CHEM_DISINFECT_STATE_PRIME_ACID_LINE: + case DG_CHEM_DISINFECT_STATE_FILL_WITH_WATER_AND_DISINFECTANT: + if ( ( STATE_CLOSED == getSwitchStatus( CONCENTRATE_CAP ) ) || ( STATE_OPEN == getSwitchStatus( DIALYSATE_CAP ) ) ) + { + isAlarmNeeded = TRUE; + } + break; + + case DG_CHEM_DISINFECT_STATE_DISINFECT_R1_TO_R2: + case DG_CHEM_DISINFECT_STATE_FILL_R2_WITH_DISINFECTANT: + case DG_CHEM_DISINFECT_STATE_DISINFECT_R2_TO_R1: + case DG_CHEM_DISINFECT_STATE_DISINFECTANT_DRAIN_R1: + case DG_CHEM_DISINFECT_STATE_DISINFECTANT_DRAIN_R2: + case DG_CHEM_DISINFECT_STATE_RINSE_R1_TO_R2: + case DG_CHEM_DISINFECT_STATE_RINSE_R2_TO_R1_AND_DRAIN_R1: + case DG_CHEM_DISINFECT_STATE_RINSE_R1_TO_R2_AND_DRAIN_R2: + case DG_CHEM_DISINFECT_STATE_RINSE_CIRCULATION: + case DG_CHEM_DISINFECT_STATE_COMPLETE: + if ( ( STATE_OPEN == getSwitchStatus( CONCENTRATE_CAP ) ) || ( STATE_OPEN == getSwitchStatus( DIALYSATE_CAP ) ) ) + { + isAlarmNeeded = TRUE; + } + break; + } + +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_CAPS_MONITOR ) != SW_CONFIG_ENABLE_VALUE ) +#endif + { + if ( TRUE == isAlarmNeeded ) + { + // 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; + } + } +} + /**@}*/