Index: firmware/App/Modes/ModeChemicalDisinfect.c =================================================================== diff -u -r19934ca6e36e2dcabd6a6083a9a1f15b1ed32189 -receb190a5d66fdbee779478ac8bb50a846ed9241 --- firmware/App/Modes/ModeChemicalDisinfect.c (.../ModeChemicalDisinfect.c) (revision 19934ca6e36e2dcabd6a6083a9a1f15b1ed32189) +++ firmware/App/Modes/ModeChemicalDisinfect.c (.../ModeChemicalDisinfect.c) (revision eceb190a5d66fdbee779478ac8bb50a846ed9241) @@ -7,8 +7,8 @@ * * @file ModeChemicalDisinfect.c * -* @author (last) Bill Bracken -* @date (last) 24-Oct-2022 +* @author (last) Steve Jarpe +* @date (last) 21-Dec-2022 * * @author (original) Sean * @date (original) 04-Apr-2020 @@ -31,6 +31,7 @@ #include "Pressures.h" #include "Reservoirs.h" #include "ROPump.h" +#include "PIControllers.h" #include "RTC.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" @@ -47,65 +48,96 @@ // ********** 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. +// Inlet water check defines +#define MIN_INLET_PRESSURE_PSI 14.0F ///< Minimum water inlet pressure in psi. +#define MAX_INLET_PRESSURE_PSI 80.0F ///< Maximum water inlet pressure in psi. +#define MIN_INLET_TEMPERATURE_C 18.0F ///< Minimum water inlet temperature in C. +#define MAX_INLET_TEMPERATURE_C 37.0F ///< Maximum water inlet temperature in C. +#define MAX_INLET_CONDUCTIVITY_US_PER_CM 2000.0F ///< Maximum water inlet conductivity in us/cm +#define MIN_INLET_CONDUCTIVITY_US_PER_CM 100.0F ///< Minimum water inlet conductivity in us/cm // 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. +#define RSRVRS_DRAIN_TIMEOUT_MS ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 drain timeout in ms. +#define RSRVRS_INITIAL_DRAIN_TIME_OUT_MS ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 initial drain time out in milliseconds. // 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 +#define FLUSH_DRAIN_WAIT_TIME_MS ( 60 * MS_PER_SECOND ) ///< Flush Drain path wait time in milliseconds. +#define MAX_ALLOWED_FLUSH_DRAIN_PERIODS 2 ///< Number of flush drain periods to wait for inlet water to come into range +#define INLET_WATER_CHECK_FAILURE_TASK_INT ( ( 10 * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ) ///< Task interval counter for inlet water check failures before alarm // 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 RO_PUMP_TARGET_FLUSH_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 FLUSH_CICRCULATION_INITIAL_WAIT_TIME_MS ( 30 * MS_PER_SECOND ) ///< Flush/rinse circulation path wait time in milliseconds. +#define FLUSH_CICRCULATION_ADDITIONAL_WAIT_TIME_MS ( 60 * MS_PER_SECOND ) ///< Flush/rinse circulation path additional wait time in milliseconds. #define MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C 3.0F ///< Maximum flush circulation temperature difference tolerance in C. -#define NUM_OF_TEMP_SENSORS_TO_AVG 4.0F ///< Number of temperature sensors to average to check the difference. +#define NUM_OF_TEMP_SENSORS_TO_AVG 2.0F ///< Number of temperature sensors to average to check the difference. +#define MAX_FLUSH_CIRC_CONDUCTIVITY_US_PER_CM 100.0F ///< Maximum allowed conductivity in flush circulation state in us/cm +#define MAX_ALLOWED_FLUSH_CIRC_PERIODS 2 ///< Number of flush circulation periods to wait for sensors to come into range -// 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. +// Fill R1 & R2 state defines +#define RSRVRS_FULL_STABLE_TIME_TASK_INT ( ( 4 * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ) ///< Reservoirs 1 & 2 full stable time in task interval counts. +#define RSRVRS_FILL_UP_TIMEOUT_MS ( 8 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 full fill up timeout in ms. +#define RSRVRS_PARTIAL_FILL_UP_TIMEOUT_MS ( 7 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 partial fill up timeout in ms. +#define RSRVRS_ALMOST_FULL_FILL_UP_TIMEOUT_MS ( 1 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 partial fill up timeout in ms. It is assumed the reservoir is nearly full to begin. +#define RESERVOIR_FULL_VOLUME_CHANGE_LIMIT_ML 5.0F ///< The maximum difference between the short-term and long-term filtered reservoir volumes in ml that determines the reservoir is full. +#define RESERVOIR_MINIMUM_FULL_VOLUME_ML 1700.0F ///< When filling the reservoir, the volume reading must be at least this value before checking for the volume to level off. +#define RSRVRS_FILL_TO_FULL_STABLE_TASK_INT ( ( 2 * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ) ///< Reservoirs 1 & 2 full stable time in task intervals. -// 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 +// Prime acid line state defines #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. +#define DISINFECTANT_PUMP_PRIME_SPEED_ML_PER_MIN 19.0F ///< Default Disinfectant pump prime speed in ml/min. +#define MIN_PRIME_ACID_CONDUCTIVITY_US_PER_CM 600.0F ///< Minimum conductivity that indicates acid is in the line in uS/cm +#define PRIME_ACID_STEADY_CONDUCTIVITY_TASK_INT ( ( 10 * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ) ///< Minimum time that a steady conductivity of acid must be read in task intervals. +#define RO_PUMP_PRIME_DISINECTANT_FLOW_RATE_LPM 0.8F ///< ROP flow rate for disinfectant prime +#define MAX_RO_PUMP_PRIME_DISINFECTANT_PRESSURE_PSI 130.0F ///< ROP max pressure for disinfectant prime +#define DISINFECTANT_MIX_RATIO_PRIME 19.0F ///< Disinfectant mixing ratio during prime, water/disinfectant. -// 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. +// Stabilize disinfectant mixing state defines +#define MAX_ALLOWED_DISINFECTANT_FLUSH_PERIODS 2 ///< Number of allowed periods to check conductivity and temperature in disinfectant flush state +#define DISINFECTANT_PUMP_FILL_SPEED_ML_PER_MIN 7.61F ///< Default concentrate pump speed for mixing disinfectant. +#define DISINFECTANT_MIX_RATIO_FILL 65.67F ///< Disinfectant mixing ratio during fill, water/disinfectant. +#define RO_PUMP_FILL_DISINECTANT_FLOW_RATE_LPM 0.4F ///< ROP flow rate for disinfectant prime +#define MAX_RO_PUMP_FILL_DISINFECTANT_PRESSURE_PSI 130.0F ///< ROP max pressure for disinfectant fill +#define FLUSH_DISINFECTANT_INITIAL_WAIT_TIME ( 30 * MS_PER_SECOND ) ///< Initial time to wait for temperature and conductivity to reach target in disinfectant flush state +#define FLUSH_DISINFECTANT_ADDITIONAL_WAIT_TIME ( 60 * MS_PER_SECOND ) ///< Additional time to wait for temperature and conductivity to reach target in disinfectant flush state -// Fill heat up -#define CHEM_DISINFECT_TARGET_TEMPERATURE_C 21.0F ///< Chemical disinfect target water temperature in C. // TODO this used to 30.0 +// Parameters during disinfect defines +#define CHEM_DISINFECT_TARGET_RO_FLOW_LPM 0.5F ///< Chemical disinfect target RO flow rate in L/min. +#define CHEM_DISINFECT_MAX_RO_PRESSURE_PSI 130 ///< Chemical disinfect maximum RO pressure in psi. +#define MIN_DISINFECT_CONDUCTIVITY_US_PER_CM 150.0F ///< Minimum conductivity for mixed disinfect fluid in us/cm +#define MAX_DISINFECT_CONDUCTIVITY_US_PER_CM 650.0F ///< Maximum conductivity for mixed disinfect fluid in us/cm +#define MAX_DISINFECT_TPO_TEMPERATURE_C 55.0F ///< Maximum temperature for mixed disinfect fluid at outlet of heater in dec C +#define MIN_MAX_CONDUCTIVITY_WAIT_TASK_INT ( ( 10 * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ) ///< Number of task intervals to wait for conductivity and temperature to stabilize -// Post disinfect rinses -#define NUM_OF_POST_DISINFECT_RINSES 12 ///< Number of rinses after a chemical disinfect. +// Initial disinfectant fill of R1 and R2 +#define RSRVRS_FULL_VOL_ML 1800.0F ///< Reservoirs 1 & 2 full volume in mL. +#define RSRVRS_LEAK_VOL_TIMEOUT_TASK_INT ( ( 30 * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ) ///< Time delay for declaring that reservoir is leaking due to volume depletion. +#define RSRVRS_MAX_LEAK_VOL_CHANGE_ML 100.0F; ///< Volume loss that is necessary to declare a reservoir leak. -static const F32 ACID_TO_WATER_MIXING_RATIO = ( 1.0F / 70.0F ); ///< Acid to water mixing ratio for chemical disinfect. +// Parameters controlling chemical disinfect +#define TARGET_CHEM_DISINFECT_TIME_MS ( 12 * SEC_PER_MIN * MS_PER_SECOND ) ///< Expected chemical disinfect time in ms. +#define CHEM_DISINFECT_START_TEMP_TIMEOUT_MS ( 6 * SEC_PER_MIN * MS_PER_SECOND ) ///< Chemical disinfect reaching to minimum temperature timeout in milliseconds. +#define CHEM_DISINFECT_TARGET_TEMPERATURE_C 37.0F ///< Chemical disinfect target water temperature in C. +#define CHEM_DISINFECT_MINIMUM_TEMPERATURE_C 35.0F ///< Chemical disinfect target water temperature in C. +#define CHEM_DISINFECT_HEATER_CONTROL_TEMPERATURE_C 45.0F ///< Chemical disinfect heater control water temperature in C. +#define DISINFECT_CYCLE_PERIOD_MS ( 3 * SEC_PER_MIN * MS_PER_SECOND ) ///< Time for each disinfectant filling and partial draining cycle. This is the time that the reservoir is full and the temperature is above the target. +#define REQUIRED_DISINFECT_CYCLES 2 ///< Number of times each reservoir is to be filled with disinfectant. +#define MAX_DISINFECT_STATE_TIME_MS ( 10 * SEC_PER_MIN * MS_PER_SECOND ) ///< Maximum time in each reservoir disinfect cycle. +// A PI control loop is used control the DRP speed while the reservoir is filling and draining, to maintain a constant level of disinfectant in the reservoir +#define RESERVOIR_VOLUME_BELOW_FULL_ML 150.0F ///< Volume to maintain in reservoir relative to full volume +#define DRP_VOLUME_CONTROL_TARGET_VOLUME_ML 1800.0F ///< Reservoir level to maintain using DRP volume control, default value +#define DRP_VOLUME_CONTROL_INTERVAL_TASK_INT ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Update rate for DRP volume control in ms. +#define DRP_VOLUME_CONTROL_P_COEFFICIENT -2.5F ///< P term for DRP volume control. +#define DRP_VOLUME_CONTROL_I_COEFFICIENT -0.2F ///< I term for DRP volume control. +#define MIN_DRP_VOLUME_CONTROL_RPM 350.0F ///< Minimum DRP RPM for volume control. The minimum value allowed by the pump driver is 300 RPM +#define MAX_DRP_VOLUME_CONTROL_RPM 1500.0F ///< Maximum DRP RPM for volume control. The maximum recommended value is 2400 RPM + /// Cancellation paths typedef enum Cancellation_modes { @@ -116,21 +148,10 @@ 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; - /// Non-volatile write structure typedef struct { BOOL hasDisStatusBeenWrittenToNV; ///< Boolean flag to indicate whether the disinfect status been written to NV or not. - BOOL hasDisCompleteDateBeenWrittenToNV; ///< Boolean flag to indicate whether the disinfect complete date been written to NV or not. } DISINFECT_NV_OPS_T; // ********** private data ********** @@ -141,24 +162,30 @@ static U32 overallChemDisinfectTimer = 0; ///< Chemical disinfect cycle total timer. static U32 stateTimer = 0; ///< Chemical disinfect state timer to be used in different states. static U32 stateTrialCounter = 0; ///< Chemical disinfect state trial counter to be used for retries in different states. -static BOOL areTempSensorsInRange = FALSE; ///< Chemical disinfect temperature sensors in/out range flag. -/// Boolean flag to check whether draining R1 and R2 is at the end of the chemical disinfect cycle or in the beginning. So the drain states can be reused. -static BOOL isThisLastDrain = FALSE; +static BOOL isThisLastDrain = FALSE; ///< Boolean flag to check whether draining R1 and R2 is at the end of the chemical disinfect cycle or in the beginning. +static BOOL haveInletWaterChecksPassed = FALSE; ///< Chemical disinfect inlet water checks flag. +static U32 inletWaterChecksFailCounter = 0; ///< Timer/counter for inlet water check failures. static DG_RESERVOIR_STATUS_T rsrvr1Status = NUM_OF_DG_RESERVOIR_STATUS; ///< Reservoir 1 status. static DG_RESERVOIR_STATUS_T rsrvr2Status = NUM_OF_DG_RESERVOIR_STATUS; ///< Reservoir 2 status. -static F32 R1ChemDisinfectVol = 0.0; ///< Reservoir 1 full volume during chemical disinfect. -static F32 R2ChemDisinfectVol = 0.0; ///< Reservoir 2 full volume during chemical disinfect. -static U32 chemDisinfectTimer = 0; ///< Chemical disinfect timer. -static 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 BOOL isRsrvrLeaking = FALSE; ///< Flag used to determine if reservoir is leaking. +static U32 rsrvrsVolMonitorCounter = 0; ///< Timer/counter used to determine if reservoir is leaking. +static U32 chemDisinfectReservoirTime = 0; ///< Chemical disinfect accumulated time in ms above the temperature target in one cycle for reservoir 1 or reservoir 2. +static BOOL ischemDisinfectWarmupTargetReached = FALSE; ///< Flag that indicates whether the disinfect warmup target has been reached. +static BOOL isChemDisinfectTemperatureAboveTarget = FALSE; ///< Flag that indicates if the disinfect temperature is currently above the target. +static U32 DisinfectCycleCounter = 0; ///< Counter for disinfect cycles. +static U32 drpControlTimerCounter = 0; ///< Timer/counter for reservoir volume control of DRP. +static U32 dataPublishCounter = 0; ///< Chemical Disinfect data publish timer/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 flushCircWaitTime; ///< Variable time period in ms to wait in flush circulation state to check sensor values. +static U32 flushDisinfectantWaitTime; ///< Variable time period in ms to wait in disinfectant flush state to check sensor values. static U32 primeAcidSteadyStateCounter = 0; ///< Prime acid steady state counter. -static BOOL haveDrainParamsBeenInit[ NUM_OF_DG_RESERVOIRS ]; ///< Boolean flag to indicate whether the drain parameters have been reset or not. +static U32 minMaxConductivityWaitCounter = 0; ///< Timer/counter for the disinfect conductivity check. +static U32 rsrvrFillStableTimeCounter = 0; ///< Task interval timer/counter for determining if reservoir has reached fill target. +static U32 rsrvrFillToFullStableTimeCounter = 0; ///< Task interval timer/counter for determining if reservoir is full. +static F32 R1FullVolume = 0.0; ///< R1 volume determined by fill to full function. +static F32 R2FullVolume = 0.0; ///< R2 volume determined by fill to full function. +static F32 disinfectantMixRatio = 0.0; ///< Current disinfectant mixing ratio. static DISINFECT_NV_OPS_T disinfectNVOps; ///< Disinfect non-volatile memory operations. // ********** private function prototypes ********** @@ -168,31 +195,24 @@ 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 handleChemicalDisinfectPrimeDisinfectantState( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDisinfectantFlushState( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFillWithDisinfectantState( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDisinfectR1ToR2State( void ); -static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFillR2WithDisinfectantState( void ); +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectPartialDrainR1FillR2ToR1State( 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 DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectPartialDrainR2FillR1ToR2State( 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 DG_RESERVOIR_STATUS_T getRsrvrFillToFullStatus( DG_RESERVOIR_ID_T r, U32 timeout ); +static void controlDRPByReservoirVolume( DG_RESERVOIR_ID_T r, BOOL initialize ); +static void handleDisinfectantMixing( F32 measuredROFlowRate_mL_min, F32 disinfectantMixingRatio ); +static void chemicalDisinfectTimerUpdate( void ); static void publishChemicalDisinfectData( void ); static void monitorModeChemicalDisinfect( void ); static void writeDisinfectDataToNV( void ); @@ -204,7 +224,7 @@ * @details Inputs: none * @details Outputs: chemDisinfectState, stateTimer, isThisLastDrain, * stateTrialCounter, areTempSensorsInRange, rsrvr1Status, rsrvr2Status, - * R1ChemDisinfectVol, R2ChemDisinfectVol, overallChemDisinfectTimer, + * overallChemDisinfectTimer, * cancellationMode, rsrvrFillStableTimeCounter, prevChemDisinfectState * isPartialDisinfectInProgress, numberOfPostDisinfectRinses, * primeAcidSteadyStateCounter, chemDisinfectUIState, haveDrainParamsBeenInit, @@ -218,21 +238,18 @@ 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; + rsrvrFillToFullStableTimeCounter = 0; primeAcidSteadyStateCounter = 0; + disinfectantMixRatio = 0.0; + isRsrvrLeaking = FALSE; + chemDisinfectReservoirTime = 0; + ischemDisinfectWarmupTargetReached = FALSE; + isChemDisinfectTemperatureAboveTarget = FALSE; chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_NOT_RUNNING; - haveDrainParamsBeenInit[ DG_RESERVOIR_1 ] = FALSE; - haveDrainParamsBeenInit[ DG_RESERVOIR_2 ] = FALSE; - disinfectNVOps.hasDisCompleteDateBeenWrittenToNV = FALSE; disinfectNVOps.hasDisStatusBeenWrittenToNV = FALSE; } @@ -246,7 +263,8 @@ *************************************************************************/ U32 transitionToChemicalDisinfectMode( void ) { - deenergizeActuators(); + // Set all the actuators to reset and de-energized state + deenergizeActuators( NO_PARK_CONC_PUMPS ); initChemicalDisinfectMode(); @@ -289,74 +307,34 @@ chemDisinfectState = handleChemicalDisinfectFlushCirculationState(); break; - case DG_CHEM_DISINFECT_STATE_FLUSH_R1_AND_R2: - chemDisinfectState = handleChemicalDisinfectFlushR1AndR2State(); + case DG_CHEM_DISINFECT_STATE_PRIME_DISINFECTANT: + chemDisinfectState = handleChemicalDisinfectPrimeDisinfectantState(); break; - case DG_CHEM_DISINFECT_STATE_FLUSH_R2_AND_DRAIN_R1: - chemDisinfectState = handleChemicalDisinfectFlushR2AndDrainR1State(); + case DG_CHEM_DISINFECT_STATE_DISINFECTANT_FLUSH: + chemDisinfectState = handleChemicalDisinfectDisinfectantFlushState(); break; - case DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN_R2: - chemDisinfectState = handleChemicalDisinfectFlushDrainR2State(); + case DG_CHEM_DISINFECT_STATE_FILL_WITH_DISINFECTANT: + chemDisinfectState = handleChemicalDisinfectFillWithDisinfectantState(); 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(); + case DG_CHEM_DISINFECT_STATE_PARTIAL_DRAIN_R1_FILL_R2_TO_R1: + chemDisinfectState = handleChemicalDisinfectPartialDrainR1FillR2ToR1State(); break; case DG_CHEM_DISINFECT_STATE_DISINFECT_R2_TO_R1: chemDisinfectState = handleChemicalDisinfectDisinfectR2ToR1State(); break; - case DG_CHEM_DISINFECT_STATE_COOL_DOWN_HEATERS: - chemDisinfectState = handleChemicalDisinfectCoolDownHeatersState(); + case DG_CHEM_DISINFECT_STATE_PARTIAL_DRAIN_R2_FILL_R1_TO_R2: + chemDisinfectState = handleChemicalDisinfectPartialDrainR2FillR1ToR2State(); 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; @@ -436,41 +414,26 @@ { 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; + // Set the chemical disinfect state that is published on the UI + chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_FLUSH_BEFORE_DISINFECT; // Start overall chemical disinfect timer overallChemDisinfectTimer = getMSTimerCount(); // Set all the actuators to reset and de-energized state - deenergizeActuators(); + deenergizeActuators( NO_PARK_CONC_PUMPS ); - F32 ppiPressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); - F32 THdTemp = getTemperatureValue( TEMPSENSORS_HEAT_DISINFECT ); - F32 TPoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); + setValveState( VPI, VALVE_STATE_CLOSED ); + // Set the actuators to drain R1 + setValveState( VRD1, VALVE_STATE_OPEN ); + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); - // 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 ); + // Assume reservoir 1 is full and drain it + rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; + state = DG_CHEM_DISINFECT_STATE_DRAIN_R1; + stateTimer = getMSTimerCount(); - // 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; } @@ -481,64 +444,30 @@ * 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 + * @details Outputs: 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 ) + { // Done with draining R1 + if ( FALSE == 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(); + setValveState( VRD1, VALVE_STATE_CLOSED ); + setValveState( VRD2, VALVE_STATE_OPEN ); + // Assume reservoir 2 is full and drain it + rsrvr2Status = DG_RESERVOIR_ABOVE_TARGET; + stateTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_DRAIN_R2; } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { @@ -555,8 +484,7 @@ * 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 Inputs: stateTimer, rsrvr2Status, isThisLastDrain * @details Outputs: stateTimer, rsrvr2Status, stateTrialCounter, * chemDisinfectUIState * @return next state of the chemical disinfect state machine @@ -571,29 +499,28 @@ } else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) { + signalDrainPumpHardStop(); if ( TRUE == isThisLastDrain ) { - // Set the chemical disinfect that is published on the UI // This is the final drain of chemical disinfect - chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_FLUSH_AFTER_DISINFECT; - - setValveState( VRD1, VALVE_STATE_OPEN ); setValveState( VRD2, VALVE_STATE_CLOSED ); - rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; - state = DG_CHEM_DISINFECT_STATE_DRAIN_R1; + setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); + state = DG_CHEM_DISINFECT_STATE_COMPLETE; } else { tareLoadCell( LOAD_CELL_RESERVOIR_2_PRIMARY ); tareLoadCell( LOAD_CELL_RESERVOIR_2_BACKUP ); - signalDrainPumpHardStop(); + setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); + setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); setValveState( VRD2, VALVE_STATE_CLOSED ); setValveState( VPI, VALVE_STATE_OPEN ); - - stateTrialCounter = 0; - stateTimer = getMSTimerCount(); - state = DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN; + setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); + turnOnUVReactor( INLET_UV_REACTOR ); + stateTrialCounter = 0; + stateTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN; } } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) @@ -625,44 +552,35 @@ // 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 ) ) + // haveInletWaterChecksPassed is set in the monitor function + if ( TRUE == haveInletWaterChecksPassed ) { - hasConductivityPassed = TRUE; - } - -#ifndef _RELEASE_ - if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_DISABLE_DISINFECT_CONDUCTIVITY_CHECK ) ) - { - hasConductivityPassed = TRUE; - } -#endif - - if ( TRUE == hasConductivityPassed ) - { + // set pumps and valves for next state, flush circulation setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); - setROPumpTargetFlowRateLPM( RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM, MAX_RO_PUMP_FLUSH_FILL_PRESSURE_PSI ); - + setROPumpTargetFlowRateLPM( RO_PUMP_TARGET_FLUSH_FLOW_RATE_LPM, MAX_RO_PUMP_FLUSH_FILL_PRESSURE_PSI ); + // Start heating the water while we are flushing + setHeaterTargetTemperature( DG_PRIMARY_HEATER, CHEM_DISINFECT_HEATER_CONTROL_TEMPERATURE_C ); + startHeater( DG_PRIMARY_HEATER ); + // The UV reactors will be on for the entire disinfect cycle + turnOnUVReactor( OUTLET_UV_REACTOR ); + flushCircWaitTime = FLUSH_CICRCULATION_INITIAL_WAIT_TIME_MS; 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(); + else + { // The inlet water checks failed + // If the number of failures have not exceeded the limit, try again. + if ( ++stateTrialCounter < MAX_ALLOWED_FLUSH_DRAIN_PERIODS ) + { + stateTimer = getMSTimerCount(); + } + else + { // Couldn't get a good water sample after a couple of trials so the disinfect flush drain cycle failed + alarmDetectedPendingTrigger = ALARM_ID_INLET_WATER_TEMPERATURE_IN_LOW_RANGE; // TODO: ALARM_ID_DG_NEW_WAT; + prevChemDisinfectState = state; + state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; + } } - // 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; @@ -677,398 +595,220 @@ * 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 + * alarm, * @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 ) ) + if ( TRUE == didTimeout( stateTimer, flushCircWaitTime ) ) { - 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; + F32 TPoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); + F32 TD2Temp = getTemperatureValue( TEMPSENSORS_CONDUCTIVITY_SENSOR_2 ); + F32 avgTemp = ( TPoTemp + TD2Temp ) / NUM_OF_TEMP_SENSORS_TO_AVG; + F32 cd2Conductivity = getConductivityValue( (U32)CONDUCTIVITYSENSORS_CD2_SENSOR ); + F32 cpoConductivity = getConductivityValue( (U32)CONDUCTIVITYSENSORS_CPO_SENSOR ); // Check if any of the temperature sensors deviate for more than the defined value from the average of all // of the temperature sensors - BOOL 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 ); + BOOL isTPoOut = FALSE; + BOOL isTD2Out = FALSE; + BOOL isCD2Out = ( cd2Conductivity > MAX_FLUSH_CIRC_CONDUCTIVITY_US_PER_CM ? TRUE : FALSE ); + BOOL isCPoOut = ( cpoConductivity > MAX_FLUSH_CIRC_CONDUCTIVITY_US_PER_CM ? TRUE : FALSE ); +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_TEMPERATURE_SENSORS_ALARM ) != SW_CONFIG_ENABLE_VALUE ) +#endif + { + isTPoOut = ( fabs( TPoTemp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C ? TRUE : FALSE ); + 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 ) ) + if( ( TRUE == isTPoOut ) || ( TRUE == isTD2Out ) || ( TRUE == isCD2Out ) || ( TRUE == isCPoOut) ) { // Check if we have exceeded the number of trials. If not, try another time - if ( ++stateTrialCounter < MAX_ALLOWED_STATE_TRIALS ) + ++stateTrialCounter; + if ( stateTrialCounter < MAX_ALLOWED_FLUSH_CIRC_PERIODS ) { - stateTimer = getMSTimerCount(); + stateTimer = getMSTimerCount(); + flushCircWaitTime = FLUSH_CICRCULATION_ADDITIONAL_WAIT_TIME_MS; } - // 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; + { // State failed. Cancel chemical disinfect mode + // determine which alarm it is, temperature or conductivity, one of them has to take precedence + if ( ( TRUE == isTPoOut ) || ( TRUE == isTD2Out ) ) + { + alarmDetectedPendingTrigger = ALARM_ID_DG_TEMP_SENSORS_DIFF_OUT_OF_RANGE; + } + else + { + alarmDetectedPendingTrigger = ALARM_ID_INLET_WATER_CONDUCTIVITY_IN_HIGH_RANGE; //TODO: ALARM_ID_DG_NEW_CON; + } + 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 ); + setROPumpTargetFlowRateLPM( RO_PUMP_PRIME_DISINECTANT_FLOW_RATE_LPM, MAX_RO_PUMP_PRIME_DISINFECTANT_PRESSURE_PSI ); + // Set the acid dispense pump speed at the priming rate + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, DISINFECTANT_PUMP_PRIME_SPEED_ML_PER_MIN ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); + disinfectantMixRatio = DISINFECTANT_MIX_RATIO_PRIME; + primeAcidSteadyStateCounter = 0; + state = DG_CHEM_DISINFECT_STATE_PRIME_DISINFECTANT; + stateTimer = getMSTimerCount(); } } - // 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 + * The handleChemicalDisinfectPrimeDisinfectantState function handles the + * chemical disinfect prime acid line state. The state primes the acid line + * until a minimum conductivity is sensed for a period of time. The the + * function transitions to another state. If the minimum conductivity was + * not reached within the defined period of time, it transitions to a + * cancellation path. + * @details Inputs: stateTimer, primeAcidSteadyStateCounter + * @details Outputs: stateTimer, prevChemDisinfectState, + * primeAcidSteadyStateCounter, chemDisinfectUIState * @return next state of the chemical disinfect state machine *************************************************************************/ -static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFlushR1AndR2State( void ) +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectPrimeDisinfectantState( void ) { - DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_FLUSH_R1_AND_R2; + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_PRIME_DISINFECTANT; + F32 cd2Conductivity = getConductivityValue( (U32)CONDUCTIVITYSENSORS_CD2_SENSOR ); - // 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 ); + // Set the chemical disinfect state that is published on the UI + chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_MIX_WATER_AND_ACID; - // 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 ) + handleDisinfectantMixing( getMeasuredFlowRateLPM( RO_FLOW_SENSOR ) * ML_PER_LITER, disinfectantMixRatio ); + +#ifndef _RELEASE_ + if ( SW_CONFIG_ENABLE_VALUE != getSoftwareConfigStatus( SW_CONFIG_DISABLE_DISINFECT_CONDUCTIVITY_CHECK ) ) + { +#endif + if ( cd2Conductivity < MIN_PRIME_ACID_CONDUCTIVITY_US_PER_CM ) { - prevChemDisinfectState = state; - alarmDetectedPendingTrigger = ALARM_ID_DG_INVALID_LOAD_CELL_VALUE; - state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + primeAcidSteadyStateCounter = 0; } } - // Once R1 is full, keep monitoring for R2 level and timeout - else if( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) + // Check if the acid conductivity value has been above the threshold for the specified time period + if ( ++primeAcidSteadyStateCounter >= PRIME_ACID_STEADY_CONDUCTIVITY_TASK_INT ) { - 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; - } + // Turn off the concentrate pump + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB, NO_PARK_CONC_PUMPS ); + // Turn on the RO pump and concentrate pump to the disinfectant fill rates + setROPumpTargetFlowRateLPM( RO_PUMP_FILL_DISINECTANT_FLOW_RATE_LPM, MAX_RO_PUMP_FILL_DISINFECTANT_PRESSURE_PSI ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, DISINFECTANT_PUMP_FILL_SPEED_ML_PER_MIN ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); + disinfectantMixRatio = DISINFECTANT_MIX_RATIO_FILL; + flushDisinfectantWaitTime = FLUSH_DISINFECTANT_INITIAL_WAIT_TIME; + stateTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_DISINFECTANT_FLUSH; } - else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) + if ( TRUE == didTimeout( stateTimer, PRIME_ACID_LINE_TIMEOUT_MS ) ) { - prevChemDisinfectState = state; - state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + 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 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 + * The handleChemicalDisinfectDisinfectantFlushState function handles the + * chemical disinfect disinfectant flush state. The state adds disinfectant + * to the heated RO product water and flushes it to drain while monitoring the + * conductivity and temperature. If the conductivity and temperature are + * within the specified range within the specified time, the function transitions + * to another state. If the minimum conductivity and temperature are not + * reached within the defined period of time, it transition to a cancellation path. + * @details Inputs: stateTimer, primeAcidSteadyStateCounter, + * temperature, conductivity + * @details Outputs: stateTimer, prevChemDisinfectState, chemDisinfectUIState, + * alarm * @return next state of the chemical disinfect state machine *************************************************************************/ -static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFlushR2AndDrainR1State( void ) +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDisinfectantFlushState( void ) { - DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_FLUSH_R2_AND_DRAIN_R1; + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_DISINFECTANT_FLUSH; - // 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(); + // Set the chemical disinfect state that is published on the UI + chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_MIX_WATER_AND_ACID; - setValveState( VRD1, VALVE_STATE_CLOSED ); - } - else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) - { - prevChemDisinfectState = state; - state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; - } + handleDisinfectantMixing( getMeasuredFlowRateLPM( RO_FLOW_SENSOR ) * ML_PER_LITER, disinfectantMixRatio ); - // First reservoir 2 must be completely full - if ( DG_RESERVOIR_BELOW_TARGET == rsrvr2Status ) + if ( TRUE == didTimeout( stateTimer, flushDisinfectantWaitTime ) ) { - rsrvr2Status = getRsrvrFillStatus( DG_RESERVOIR_2, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS ); + F32 TdiTemp = getTemperatureValue( TEMPSENSORS_INLET_DIALYSATE ); + F32 cd2Conductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); + F32 TPoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); + BOOL isCD2OutofRange = ( cd2Conductivity < MIN_DISINFECT_CONDUCTIVITY_US_PER_CM || cd2Conductivity > MAX_DISINFECT_CONDUCTIVITY_US_PER_CM ); + BOOL isTPoOutofRange = ( TPoTemp > MAX_DISINFECT_TPO_TEMPERATURE_C ); - 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 ) ) +#ifndef _RELEASE_ + if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_DISABLE_DISINFECT_CONDUCTIVITY_CHECK ) ) { - prevChemDisinfectState = state; - alarmDetectedPendingTrigger = ALARM_ID_DG_INVALID_LOAD_CELL_VALUE; - state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + isCD2OutofRange = FALSE; } - } - // 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 ); +#endif - // 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; + if ( ( TRUE == isCD2OutofRange ) || ( TRUE == isTPoOutofRange ) ) + { // The conditions have not been met + // Check if we have exceeded the number of trials. If not, try another time + if (++stateTrialCounter < MAX_ALLOWED_DISINFECTANT_FLUSH_PERIODS ) + { + stateTimer = getMSTimerCount(); + flushDisinfectantWaitTime = FLUSH_DISINFECTANT_ADDITIONAL_WAIT_TIME; + } + else + { + if ( ( TRUE == isTPoOutofRange ) ) + { + alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_TARGET_TEMP_TIMEOUT; + } + else + { + alarmDetectedPendingTrigger = ALARM_ID_INLET_WATER_CONDUCTIVITY_IN_HIGH_RANGE; //TODO: ALARM_ID_DG_NEW_CON + } + state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + } } - else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) + else { - 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 ); + // Prepare for filling the reservoirs; fill reservoir 1 and overflow it into reservoir 2 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; + setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); + setValveState( VRD1, VALVE_STATE_CLOSED ); + setValveState( VRD2, VALVE_STATE_CLOSED ); + rsrvr1Status = DG_RESERVOIR_BELOW_TARGET; + rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; + chemDisinfectReservoirTime = 0; + rsrvrFillStableTimeCounter = 0; + isChemDisinfectTemperatureAboveTarget = FALSE; + stateTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_FILL_WITH_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 + * The handleChemicalDisinfectFillWithDisinfectantState 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 @@ -1080,667 +820,291 @@ * R1ChemDisinfectVol, R2ChemDisinfectVol, rsrvrsVolMonitorTimer * @return next state of the chemical disinfect state machine *************************************************************************/ -static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFillWithWaterAndDisinfectantState( void ) +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFillWithDisinfectantState( void ) { - DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_FILL_WITH_WATER_AND_DISINFECTANT; + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_FILL_WITH_DISINFECTANT; - // Get the flow rate that is used for mixing - F32 measuredROFlowRate = getMeasuredFlowRateLPM( RO_FLOW_SENSOR ); + // Set the chemical disinfect state that is published on the UI + chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_DISINFECT_DEVICE; - // 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; + handleDisinfectantMixing( getMeasuredFlowRateLPM( RO_FLOW_SENSOR ) * ML_PER_LITER, disinfectantMixRatio ); - 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 ); + // If R1 is not full, keep monitoring for R1 level and timeout + rsrvr1Status = getRsrvrFillToFullStatus( DG_RESERVOIR_1, 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 R1 is full, keep monitoring for R2 level and timeout + rsrvr2Status = getRsrvrFillStatus( DG_RESERVOIR_2, RSRVRS_FULL_VOL_ML, RSRVRS_PARTIAL_FILL_UP_TIMEOUT_MS ); + chemicalDisinfectTimerUpdate( ); - // Once reservoir 2 is full, set the actuators for recirculation + // Once R2 is full, transition to the next state 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 ); + // go to the next disinfect cycle, fill R1 and drain R2 setValveState( VRD2, VALVE_STATE_OPEN ); - setValveState( VDR, VALVE_STATE_RECIRC_C_TO_NC ); - setValveState( VRC, VALVE_STATE_RECIRC_C_TO_NC ); + setROPumpTargetFlowRateLPM( CHEM_DISINFECT_TARGET_RO_FLOW_LPM, MAX_RO_PUMP_FILL_DISINFECTANT_PRESSURE_PSI ); - // 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; + controlDRPByReservoirVolume( DG_RESERVOIR_2, TRUE ); //Initialize the PI controller for DRP + isRsrvrLeaking = FALSE; + ischemDisinfectWarmupTargetReached = FALSE; + stateTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_DISINFECT_R1_TO_R2; } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) { - prevChemDisinfectState = state; - state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + 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; + 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 + * chemical disinfect disinfect R1 to R2 state. The state fills reservoir 1 + * to reservoir 2. 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 + * 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; + handleDisinfectantMixing( getMeasuredFlowRateLPM( RO_FLOW_SENSOR ) * ML_PER_LITER, disinfectantMixRatio ); + chemicalDisinfectTimerUpdate( ); - 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 ) + if ( (TRUE != ischemDisinfectWarmupTargetReached ) && ( TRUE == didTimeout( stateTimer, CHEM_DISINFECT_START_TEMP_TIMEOUT_MS ) ) ) { - rsrvr1Status = getRsrvrFillStatus( DG_RESERVOIR_1, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_500ML_FILL_UP_TIMEOUT_MS ); + // Heating up to minimum temperature for chemical disinfect failed + alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_TARGET_TEMP_TIMEOUT; + chemDisinfectState = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; } - 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 ) + if ( TRUE == didTimeout( stateTimer, MAX_DISINFECT_STATE_TIME_MS ) ) { - prevChemDisinfectState = state; - state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + // Heating up to minimum temperature for chemical disinfect failed + alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_TARGET_TEMP_TIMEOUT; + chemDisinfectState = 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 ) + if ( chemDisinfectReservoirTime >= DISINFECT_CYCLE_PERIOD_MS ) { - 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; - break; + // Set the actuators to fill R2 and drain R1 state + setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); + setValveState( VRO, VALVE_STATE_R2_C_TO_NC ); + setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); + setValveState( VRD1, VALVE_STATE_OPEN ); + setValveState( VRD2, VALVE_STATE_CLOSED ); + rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; + stateTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_PARTIAL_DRAIN_R1_FILL_R2_TO_R1; } - - 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; - - // Done with chemical disinfect, write the data to NV memory - writeDisinfectDataToNV(); - - if ( TRUE == didTimeout( stateTimer, POST_CHEM_DISINFECT_WAIT_TIME_MS ) ) + else { - // 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; - } + controlDRPByReservoirVolume( DG_RESERVOIR_2, FALSE ); } 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 + * The handleChemicalDisinfectPartialDrainR1FillR2ToR1State function handles the + * chemical disinfect partial drain R1 fill R2 to R1 state. + * The state drains reservoir 1 to a specified volume, fills reservoir 2, and + * fills reservoir 2 to reservoir 1, If reservoir 2 cannot be filled + * within a certain period of time, it transitions to water cancellation. + * If reservoir 2 is filled within the specified time, it + * transitions to the next state. + * @details Inputs: stateTimer, rsrvr2Status + * @details Outputs: stateTimer, chemDisinfectUIState * @return next state of the chemical disinfect state machine *************************************************************************/ -static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDisinfectantDrainR1State( void ) +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectPartialDrainR1FillR2ToR1State( void ) { - DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_DISINFECTANT_DRAIN_R1; + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_PARTIAL_DRAIN_R1_FILL_R2_TO_R1; - // Set the chemical disinfect that is published on the UI - chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_FLUSH_AFTER_DISINFECT; + handleDisinfectantMixing( getMeasuredFlowRateLPM( RO_FLOW_SENSOR ) * ML_PER_LITER, disinfectantMixRatio ); - if ( DG_RESERVOIR_ABOVE_TARGET == rsrvr1Status ) + // First reservoir 2 must be full + if ( DG_RESERVOIR_BELOW_TARGET == rsrvr2Status ) { - rsrvr1Status = getRsrvrDrainStatus( DG_RESERVOIR_1, DRAIN_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS ); + // If R2 is not full, keep monitoring for R2 level and timeout + rsrvr2Status = getRsrvrFillToFullStatus( DG_RESERVOIR_2, RSRVRS_ALMOST_FULL_FILL_UP_TIMEOUT_MS ); } - else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) + if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) { - // 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; + isRsrvrLeaking = FALSE; + chemDisinfectReservoirTime = 0; + isChemDisinfectTemperatureAboveTarget = FALSE; + ischemDisinfectWarmupTargetReached = FALSE; + stateTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_DISINFECT_R2_TO_R1; } - else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) + else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) { prevChemDisinfectState = state; - state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; + state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_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 ) + else { - rsrvr2Status = getRsrvrDrainStatus( DG_RESERVOIR_2, DRAIN_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS ); + controlDRPByReservoirVolume( DG_RESERVOIR_1, FALSE ); } - 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 + * The handleChemicalDisinfectDisinfectR2ToR1State function handles the + * chemical disinfect disinfect R2 to R1 state. The state fills reservoir 1 + * to reservoir 2. 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 2 to reservoir 1 is completed, it + * transitions to the next state. + * @details Inputs: stateTimer, rsrvr1Status, rsrvr2Status * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, - * prevChemDisinfectState, alarmDetectedPendingTrigger + * chemDisinfectUIState * @return next state of the chemical disinfect state machine *************************************************************************/ -static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectRinseR1ToR2State( void ) +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDisinfectR2ToR1State( void ) { - DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_RINSE_R1_TO_R2; + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_DISINFECT_R2_TO_R1; - if ( DG_RESERVOIR_BELOW_TARGET == rsrvr1Status ) - { - rsrvr1Status = getRsrvrFillStatus( DG_RESERVOIR_1, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS ); + // Set the chemical disinfect that is published on the UI + chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_DISINFECT_DEVICE; - // 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 ); + handleDisinfectantMixing( getMeasuredFlowRateLPM( RO_FLOW_SENSOR ) * ML_PER_LITER, disinfectantMixRatio ); + chemicalDisinfectTimerUpdate( ); - 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 ) + if ( (TRUE != ischemDisinfectWarmupTargetReached ) && ( TRUE == didTimeout( stateTimer, CHEM_DISINFECT_START_TEMP_TIMEOUT_MS ) ) ) { - prevChemDisinfectState = state; - state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + // Heating up to minimum temperature for chemical disinfect failed + alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_TARGET_TEMP_TIMEOUT; + chemDisinfectState = 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 ) + if ( TRUE == didTimeout( stateTimer, MAX_DISINFECT_STATE_TIME_MS ) ) { - rsrvr1Status = getRsrvrDrainStatus( DG_RESERVOIR_1, DRAIN_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_INITIAL_DRAIN_TIME_OUT_MS ); + // Heating up to minimum temperature for chemical disinfect failed + alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_TARGET_TEMP_TIMEOUT; + chemDisinfectState = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; } - 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 ) + if ( chemDisinfectReservoirTime >= DISINFECT_CYCLE_PERIOD_MS ) { - 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 ) ) + if ( REQUIRED_DISINFECT_CYCLES == ++DisinfectCycleCounter ) { - 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 ); + //Disinfect is complete + // turn pumps and valves off + stopHeater( DG_PRIMARY_HEATER ); + setValveState( VPI, VALVE_STATE_CLOSED ); + 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( VRI, VALVE_STATE_R2_C_TO_NC ); - setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); - - // Turn on the drain pump to drain R2 + setValveState( VRD2, VALVE_STATE_CLOSED ); + turnOffUVReactor( INLET_UV_REACTOR ); + turnOffUVReactor( OUTLET_UV_REACTOR ); + signalROPumpHardStop(); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB, NO_PARK_CONC_PUMPS ); + // Prepare to drain reservoir 1 + setValveState( VRD1, VALVE_STATE_OPEN ); + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); - - // Set the reservoir status - rsrvr1Status = DG_RESERVOIR_BELOW_TARGET; + // Because this is the second reservoir drain in this mode, the drain parameters must be reset + initDrainParameters( DG_RESERVOIR_1 ); + initDrainParameters( DG_RESERVOIR_2 ); + rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; rsrvr2Status = DG_RESERVOIR_ABOVE_TARGET; - state = DG_CHEM_DISINFECT_STATE_RINSE_R1_TO_R2_AND_DRAIN_R2; - stateTimer = getMSTimerCount(); + isThisLastDrain = TRUE; + // Set the chemical disinfect state that is published on the UI + chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_FLUSH_AFTER_DISINFECT; + writeDisinfectDataToNV(); + stateTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_DRAIN_R1; } - else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) + else { - prevChemDisinfectState = state; - state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + // go to the next disinfect configuration cycle, fill R1 and drain R2 + setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); + setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); + setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); + setValveState( VRD1, VALVE_STATE_CLOSED ); + setValveState( VRD2, VALVE_STATE_OPEN ); + stateTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_PARTIAL_DRAIN_R2_FILL_R1_TO_R2; } } - else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) + else { - prevChemDisinfectState = state; - state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + controlDRPByReservoirVolume( DG_RESERVOIR_1, FALSE ); } 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 + * The handleChemicalDisinfectPartialDrainR2FillR1ToR2State function handles the + * chemical disinfect partial drain R2 fill R1 to R2 state. + * The state drains reservoir 2 to a specified volume, fills reservoir 1, and + * fills reservoir 1 to reservoir 2. If reservoir 1 cannot be filled + * within a certain period of time, it transitions to water cancellation. + * If reservoir 1 is filled within the specified time, it * transitions to the next state. - * @details Inputs: rsrvr1Status, rsrvr2Status, numberOfPostDisinfectRinses - * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, - * prevChemDisinfectState, alarmDetectedPendingTrigger, isThisLastDrain - * numberOfPostDisinfectRinses + * @details Inputs: stateTimer, rsrvr1Status + * @details Outputs: stateTimer, chemDisinfectUIState * @return next state of the chemical disinfect state machine *************************************************************************/ -static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectRinseR1ToR2AndDrainR2State( void ) +static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectPartialDrainR2FillR1ToR2State( void ) { - DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_RINSE_R1_TO_R2_AND_DRAIN_R2; + DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_PARTIAL_DRAIN_R2_FILL_R1_TO_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; - } + handleDisinfectantMixing( getMeasuredFlowRateLPM( RO_FLOW_SENSOR ) * ML_PER_LITER, disinfectantMixRatio ); - // First reservoir 1 must be completely full + // 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 ); - - 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; - } + // If R1 is not full, keep monitoring for R1 level and timeout + rsrvr1Status = getRsrvrFillToFullStatus( DG_RESERVOIR_1, RSRVRS_ALMOST_FULL_FILL_UP_TIMEOUT_MS ); } - // Once reservoir 1 is completely full, monitor reservoir 2 - else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) + 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; - } + chemDisinfectReservoirTime = 0; + isChemDisinfectTemperatureAboveTarget = FALSE; + ischemDisinfectWarmupTargetReached = FALSE; + isRsrvrLeaking = FALSE; + stateTimer = getMSTimerCount(); + state = DG_CHEM_DISINFECT_STATE_DISINFECT_R1_TO_R2; } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { - prevChemDisinfectState = state; - state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + 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 ) ) + else { - state = DG_CHEM_DISINFECT_STATE_COMPLETE; + controlDRPByReservoirVolume( DG_RESERVOIR_2, FALSE ); } return state; @@ -1763,7 +1127,7 @@ chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_CANCEL_DISINFECT; // Set the cancellation mode - cancellationMode = CANCELLATION_MODE_BASIC; + cancellationMode = CANCELLATION_MODE_BASIC; failChemicalDisinfect(); @@ -1773,7 +1137,8 @@ /*********************************************************************//** * @brief * The handleChemicalDisinfectCancelModeWaterPathState function handles the - * chemical disinfect cancel mode cold water path state. + * chemical disinfect cancel mode cold water path state. It drains the reservoirs + * before the cancel basic path mode is entered. * @details Inputs: rsrvr1Status, rsrvr2Status, cancellationMode, stateTimer * @details Outputs: rsrvr1Status, rsrvr2Status, cancellationMode, stateTimer, * chemDisinfectUIState @@ -1789,12 +1154,11 @@ if ( CANCELLATION_MODE_NONE == cancellationMode ) { // Stop all the actuators first then decide who should run next - deenergizeActuators(); + deenergizeActuators( NO_PARK_CONC_PUMPS ); 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 ); @@ -1858,7 +1222,7 @@ 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; + chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_COMPLETE; stopChemicalDisinfect(); @@ -1881,10 +1245,10 @@ /*********************************************************************//** * @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. + * is full or not. If the fill times out, it sets the alarm for the + * reservoir fill time out, and returns the time out status. * @details Inputs: rsrvrFillStableTimeCounter, alarm, stateTimer - * @details Outputs: none + * @details Outputs: alarm * @param r is DG_RESERVOIR_1 or DG_RESERVOIR_2 * @param targetVol is the target fill volume * @param timeout is the fill up timeout @@ -1893,7 +1257,7 @@ 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; + F32 volume = 0.0; if ( DG_RESERVOIR_1 == r ) { @@ -1911,7 +1275,7 @@ // Check the volume of the reservoir against the target volume if ( volume >= targetVol ) { - if ( ++rsrvrFillStableTimeCounter >= RSRVRS_FULL_STABLE_TIME_COUNT ) + if ( ++rsrvrFillStableTimeCounter >= RSRVRS_FULL_STABLE_TIME_TASK_INT ) { status = DG_RESERVOIR_REACHED_TARGET; rsrvrFillStableTimeCounter = 0; @@ -1922,9 +1286,9 @@ 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; + prevChemDisinfectState = chemDisinfectState; + alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_FILL_TIMEOUT; + status = DG_RESERVOIR_NOT_REACHED_TARGET; } return status; @@ -1934,8 +1298,7 @@ * @brief * The getRsrvrDrainStatus function returns the status of draining a * reservoir. - * @details Inputs: stateTimer, prevChemDisinfectState, chemDisinfectState, - * alarm + * @details Inputs: stateTimer, prevChemDisinfectState, chemDisinfectState * @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 @@ -1948,108 +1311,206 @@ { 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; + prevChemDisinfectState = chemDisinfectState; + 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) + * The getRsrvrFillToFullStatus function checks whether the target reservoir + * is full or not based on a plateau in the reservoir readings. + * If the fill times out, it sets an alarm. + * @details Inputs: rsrvrFillToFullStableTimeCounter, stateTimer, reservoir volume + * @details Outputs: alarm, reservoir fill status + * @param r is DG_RESERVOIR_1 or DG_RESERVOIR_2 + * @param timeout is the fill up timeout + * @return the status of the reservoir during filling *************************************************************************/ -static CHEM_DISINFECT_STATUS_T getChemicalDisinfectStatus( void ) +static DG_RESERVOIR_STATUS_T getRsrvrFillToFullStatus( DG_RESERVOIR_ID_T r, U32 timeout ) { - CHEM_DISINFECT_STATUS_T status = CHEM_DISINFECT_IN_PROGRESS; + DG_RESERVOIR_STATUS_T status = DG_RESERVOIR_BELOW_TARGET; + F32 currentVolume = 0.0; + F32 filteredVolume = 0.0; - // 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; + if ( DG_RESERVOIR_1 == r ) + { + currentVolume = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); + filteredVolume = getLoadCellLargeFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); + } + else if ( DG_RESERVOIR_2 == r ) + { + currentVolume = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); + filteredVolume = getLoadCellLargeFilteredWeight( 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 if either reservoir 1 or reservoir 2 are losing volume more than allowed volume - if ( ( TRUE == isR1OutOfRange ) || ( TRUE == isR2OutOfRange ) ) + // Check the volume of the reservoir against the long term filtered volume + if ( ( currentVolume > RESERVOIR_MINIMUM_FULL_VOLUME_ML ) && ( fabs( currentVolume - filteredVolume ) < RESERVOIR_FULL_VOLUME_CHANGE_LIMIT_ML ) ) { - // If the leak is the first time after a while, set the flag and start the timer - if ( FALSE == areRsrvrsLeaking ) + if ( ++rsrvrFillToFullStableTimeCounter >= RSRVRS_FILL_TO_FULL_STABLE_TASK_INT ) { - areRsrvrsLeaking = TRUE; - rsrvrsVolMonitorTimer = getMSTimerCount(); + status = DG_RESERVOIR_REACHED_TARGET; + rsrvrFillToFullStableTimeCounter = 0; + // Record the full volumes + if ( DG_RESERVOIR_1 == r ) + { + R1FullVolume = currentVolume; + } + else if ( DG_RESERVOIR_2 == r ) + { + R2FullVolume = currentVolume; + } + // Set the state timer in case it needs to be used for another timeout check + stateTimer = 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 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; + } else { - areRsrvrsLeaking = FALSE; + rsrvrFillToFullStableTimeCounter = 0; } - // 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 ) + return status; +} + +/*********************************************************************//** + * @brief + * The handleDisinfectantMixing function handles the disinfectant mixing by setting + * the concentrate pump speed relative to the RO pump flow rate. + * @details Inputs: none + * @details Outputs: Set concentrate pump speed relative to RO pump flow rate + * @param measuredROFlowRate_mL_min measured RO flow rate in mL/min + * @param disinfectantMixingRatio is the ratio of RO flow rate to disinfectant flow rate + * @return none + *************************************************************************/ +static void handleDisinfectantMixing( F32 measuredROFlowRate_mL_min, F32 disinfectantMixingRatio ) +{ + // Set concentrate pumps speed based off RO pump flow rate. The ratio is water/disinfectant + F32 disinfectantCP2PumpFlowRate = measuredROFlowRate_mL_min / disinfectantMixingRatio; + + // Cap to the maximum allowed concentrate pumps rate + disinfectantCP2PumpFlowRate = MIN( disinfectantCP2PumpFlowRate, CONCENTRATE_PUMP_MAX_SPEED ); + + disinfectantCP2PumpFlowRate = MAX( disinfectantCP2PumpFlowRate, 0.0F ); + + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, disinfectantCP2PumpFlowRate ); +} + +/*********************************************************************//** + * @brief + * The controlDRPByReservoirVolume function implements the volume control of the + * drain pump to maintain a specified volume in one of the reservoirs. It uses + * a PI control loop to accomplish this. + * @details Inputs: reservoir volume, PI coefficients, full volume, + * volume below full to maintain, min and max drain pump control RPM + * @details Outputs: Sets drain pump RPM + * @param r is DG_RESERVOIR_1 or DG_RESERVOIR_2 + * @param initialize determines whether the function initializes the controller (=TRUE) or + * runs it (=FALSE). + * @return none + *************************************************************************/ +static void controlDRPByReservoirVolume( DG_RESERVOIR_ID_T r, BOOL initialize ) +{ + if ( TRUE == initialize ) { - // Keep reseting the disinfect timer so the elapsed time is always 0 until disinfect truly starts - chemDisinfectTimer = getMSTimerCount(); - isPartialDisinfectInProgress = FALSE; + // Initialize drain pump volume control PI controller + initializePIController( PI_CONTROLLER_ID_DRAIN_PUMP_VOLUME, MIN_DRP_VOLUME_CONTROL_RPM, + DRP_VOLUME_CONTROL_P_COEFFICIENT, DRP_VOLUME_CONTROL_I_COEFFICIENT, + MIN_DRP_VOLUME_CONTROL_RPM, MAX_DRP_VOLUME_CONTROL_RPM ); + drpControlTimerCounter = getMSTimerCount(); + resetPIController( PI_CONTROLLER_ID_DRAIN_PUMP_VOLUME, MIN_DRP_VOLUME_CONTROL_RPM ); + setDrainPumpTargetRPM( MIN_DRP_VOLUME_CONTROL_RPM ); //set the initial pump speed to the minimum + } + else if ( TRUE == didTimeout( drpControlTimerCounter, DRP_VOLUME_CONTROL_INTERVAL_TASK_INT) ) + { // Control at set interval + F32 tgtVolume = (F32)DRP_VOLUME_CONTROL_TARGET_VOLUME_ML; // this is the default if no full volume is available + F32 actVolume; + U32 newRPM; - if ( TRUE == didTimeout( stateTimer, CHEM_DISINFECT_START_TEMP_TIMOUT_MS ) ) + if ( DG_RESERVOIR_1 == r ) { - // Heating up to minimum temperature for chemical disinfect failed - alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_TARGET_TEMP_TIMEOUT; - status = CHEM_DISINFECT_HEAT_UP_TIMEOUT; + actVolume = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); + if ( R1FullVolume > 0.0 ) + { // If a reservoir full volume is known, use a set volume below full. This compensates for variations in + // the reservoir and load cell that may affect the volume read when the reservoir is full. + tgtVolume = R1FullVolume - RESERVOIR_VOLUME_BELOW_FULL_ML; + } } + else if ( DG_RESERVOIR_2 == r ) + { + actVolume = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); + if ( R2FullVolume > 0.0 ) + { // If a reservoir full volume is known, use a set volume below full. This compensates for variations in + // the reservoir and load cell that may affect the volume read when the reservoir is full. + tgtVolume = R2FullVolume - RESERVOIR_VOLUME_BELOW_FULL_ML; + } + } + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_DG_RESERVOIR_SELECTED, r ) + } + + newRPM = runPIController( PI_CONTROLLER_ID_DRAIN_PUMP_VOLUME, tgtVolume, actVolume ); + setDrainPumpTargetRPM( newRPM ); + drpControlTimerCounter = getMSTimerCount(); } - else if ( ( isPartialDisinfectInProgress != TRUE ) && ( ThdTemp > CHEM_DISINFECT_TARGET_TEMPERATURE_C ) ) +} + +/*********************************************************************//** + * @brief + * The chemicalDisinfectTimerUpdate function monitors the disinfect temperature + * and returns the current + * stage of chemical disinfect cycle. 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 void chemicalDisinfectTimerUpdate( void ) +{ + // Update the disinfect temperature + F32 TdiTemp = getTemperatureValue( TEMPSENSORS_INLET_DIALYSATE ); + + // If the coldest spot which is TDi is less than minimum chemical disinfect temperature, + // reset the chemical disinfect timers and check whether heating up has timed out + if ( TdiTemp >= CHEM_DISINFECT_TARGET_TEMPERATURE_C ) { - // The temperature of the coldest spot is in range to start the disinfect timer - chemDisinfectTimer = getMSTimerCount(); - isPartialDisinfectInProgress = TRUE; + isChemDisinfectTemperatureAboveTarget = TRUE; + ischemDisinfectWarmupTargetReached = 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 ) ) ) + else if ( TdiTemp < CHEM_DISINFECT_MINIMUM_TEMPERATURE_C ) { - // Done with this stage of chemical disnfect. Reset the variables - status = CHEM_DISINFECT_COMPLETE; - isPartialDisinfectInProgress = FALSE; + isChemDisinfectTemperatureAboveTarget = FALSE; } - return status; + if ( TRUE == isChemDisinfectTemperatureAboveTarget ) + { + chemDisinfectReservoirTime += TASK_GENERAL_INTERVAL; + } } /*********************************************************************//** @@ -2072,14 +1533,13 @@ 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 ) ) + //If the mode is in the actual chemical disinfect states, publish the elapsed time, otherwise publish 0 to avoid confusion + if ( chemDisinfectState > DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN ) { - uiData.chemDisinfectTargetTime = CHEM_DISINFECT_TIME_MS; - uiData.chemDisinfectCountdownTime = CHEM_DISINFECT_TIME_MS - calcTimeSince( chemDisinfectTimer ); - data.R1FillLevel = R1ChemDisinfectVol; - data.R2FillLevel = R2ChemDisinfectVol; + uiData.chemDisinfectTargetTime = TARGET_CHEM_DISINFECT_TIME_MS; + uiData.chemDisinfectCountdownTime = chemDisinfectReservoirTime; + data.R1FillLevel = R1FullVolume; + data.R2FillLevel = R2FullVolume; } else { @@ -2088,9 +1548,7 @@ data.R2FillLevel = 0.0; } - data.postDisinfectTargetRinseCount = NUM_OF_POST_DISINFECT_RINSES; - data.postDisinfectCurrentRinseCount = numberOfPostDisinfectRinses; - data.chemDisinfectUIState = chemDisinfectUIState; + 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 ) ); @@ -2106,63 +1564,67 @@ * @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, + * are not closed during the run. If the level of the reservoirs has drifted + * consecutively for the defined period of time, it sets the reservoir leak + * time out alarm and cancels the chemical disinfect mode. + * If the inlet water is not within the specified limits, an alarm is set and + * chemical disinfect is cancelled. If the conductivity of the diluted disinfectant + * mix is not within the specified limits, an alarm is set and disinfect is + * cancelled. + * @details Inputs: chemDisinfectState, reservoir volumes, reservoir volume monitor timer, + * inlet water temperature, pressure, and conductivity, disinfectant conductivity. + * @details Outputs: prevChemDisinfectState, chemDisinfectState, cancel state, * alarmDetectedPendingTrigger * @return: none *************************************************************************/ static void monitorModeChemicalDisinfect( void ) { - BOOL isAlarmNeeded = FALSE; + BOOL hasConductivityFailed = TRUE; + BOOL hasInletPressureFailed = TRUE; + BOOL hasInletTemperatureFailed = TRUE; - switch( chemDisinfectState ) + // Reservoir leak detection. + if ( ( DG_CHEM_DISINFECT_STATE_DISINFECT_R1_TO_R2 == chemDisinfectState ) || ( DG_CHEM_DISINFECT_STATE_DISINFECT_R2_TO_R1 == 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; + BOOL isRsrvrVolumeOutOfRange = FALSE; - 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 ) ) ) + if ( DG_CHEM_DISINFECT_STATE_DISINFECT_R1_TO_R2 == chemDisinfectState ) + { + isRsrvrVolumeOutOfRange = fabs( getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ) - R1FullVolume ) > RSRVRS_MAX_LEAK_VOL_CHANGE_ML; + } + else if ( DG_CHEM_DISINFECT_STATE_DISINFECT_R2_TO_R1 == chemDisinfectState ) + { + isRsrvrVolumeOutOfRange = fabs( getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ) - R2FullVolume ) > RSRVRS_MAX_LEAK_VOL_CHANGE_ML; + } + if ( ( TRUE == isRsrvrVolumeOutOfRange ) ) + { + // If the leak is the first time after a while, set the flag and start the timer + if ( FALSE == isRsrvrLeaking ) { - isAlarmNeeded = TRUE; + isRsrvrLeaking = TRUE; + rsrvrsVolMonitorCounter = 0; } - 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 ) ) ) + // If the volume is out of range and it has timed out, set the alarm + else if ( ++rsrvrsVolMonitorCounter >= RSRVRS_LEAK_VOL_TIMEOUT_TASK_INT ) { - isAlarmNeeded = TRUE; + isRsrvrLeaking = FALSE; + alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_LEAK_TIMEOUT; + chemDisinfectState = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; } - break; + } + // Reservoirs are in range + else + { + isRsrvrLeaking = FALSE; + } } #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_CAPS_MONITOR ) != SW_CONFIG_ENABLE_VALUE ) #endif { - if ( TRUE == isAlarmNeeded ) + // If the dialysate cap is open during any state, alarm + if ( STATE_OPEN == getSwitchStatus( DIALYSATE_CAP ) ) { // Set the variables to fail and go to cancel water path. Set the pending alarm to no alarm so the cancel water path // will not be raising the alarm at end of the cancel water path. The recoverable alarm is raised here in this function @@ -2171,8 +1633,95 @@ prevChemDisinfectState = chemDisinfectState; chemDisinfectState = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; alarmDetectedPendingTrigger = ALARM_ID_DG_DIALYSATE_OR_CONC_CAP_NOT_IN_PROPER_POSITION; - } + } } + + // In all states, check inlet temperature, inlet pressure, and inlet conductivity. + haveInletWaterChecksPassed= TRUE; + + hasConductivityFailed = ( ( getConductivityValue( CONDUCTIVITYSENSORS_CPI_SENSOR ) > MAX_INLET_CONDUCTIVITY_US_PER_CM ) || + ( getConductivityValue( CONDUCTIVITYSENSORS_CPI_SENSOR ) < MIN_INLET_CONDUCTIVITY_US_PER_CM ) ); + hasInletPressureFailed = ( ( getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ) < MIN_INLET_PRESSURE_PSI ) || + ( getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ) > MAX_INLET_PRESSURE_PSI ) ); + hasInletTemperatureFailed = ( ( getTemperatureValue( TEMPSENSORS_INLET_PRIMARY_HEATER ) < MIN_INLET_TEMPERATURE_C ) || + ( getTemperatureValue( TEMPSENSORS_INLET_PRIMARY_HEATER ) > MAX_INLET_TEMPERATURE_C ) ); + +#ifndef _RELEASE_ + if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_DISABLE_WATER_QUALITY_CHECK ) ) + { + hasConductivityFailed = FALSE; + } +#endif + if ( hasInletTemperatureFailed || hasConductivityFailed || hasInletPressureFailed ) + { // Inlet check failed, + haveInletWaterChecksPassed= FALSE; // set flag for flush drain state + + // increment timer/counter and check for timeout + if ( ++inletWaterChecksFailCounter >= INLET_WATER_CHECK_FAILURE_TASK_INT ) + { + // alarm unless in the start, drain, or flush drain states + switch( chemDisinfectState ) + { + case DG_CHEM_DISINFECT_STATE_FLUSH_CIRCULATION: + case DG_CHEM_DISINFECT_STATE_PRIME_DISINFECTANT: + case DG_CHEM_DISINFECT_STATE_DISINFECTANT_FLUSH: + prevChemDisinfectState = chemDisinfectState; + alarmDetectedPendingTrigger = ALARM_ID_INLET_WATER_TEMPERATURE_IN_LOW_RANGE; //ALARM_ID_NEW_WAT; + chemDisinfectState = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; + break; + + case DG_CHEM_DISINFECT_STATE_FILL_WITH_DISINFECTANT: + case DG_CHEM_DISINFECT_STATE_DISINFECT_R1_TO_R2: + case DG_CHEM_DISINFECT_STATE_PARTIAL_DRAIN_R1_FILL_R2_TO_R1: + case DG_CHEM_DISINFECT_STATE_DISINFECT_R2_TO_R1: + case DG_CHEM_DISINFECT_STATE_PARTIAL_DRAIN_R2_FILL_R1_TO_R2: + prevChemDisinfectState = chemDisinfectState; + alarmDetectedPendingTrigger = ALARM_ID_INLET_WATER_TEMPERATURE_IN_LOW_RANGE; //ALARM_ID_NEW_WAT; + chemDisinfectState = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + break; + + default: + // In all other states, it is not necessary to monitor inlet water conditions + break; + } + } + } + else + { // inlet water checks passed, reset water check fail timer + inletWaterChecksFailCounter = 0; + } + + // Check the temperature and conductivity of the diluted disinfectant + if ( chemDisinfectState > DG_CHEM_DISINFECT_STATE_PRIME_DISINFECTANT ) + { + // Disinfect conditions check + F32 cd2Conductivity = getConductivityValue( (U32)CONDUCTIVITYSENSORS_CD2_SENSOR ); + F32 TPoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); + BOOL isCD2OutofRange = ( cd2Conductivity < MIN_DISINFECT_CONDUCTIVITY_US_PER_CM || cd2Conductivity > MAX_DISINFECT_CONDUCTIVITY_US_PER_CM ); + +#ifndef _RELEASE_ + if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_DISABLE_DISINFECT_CONDUCTIVITY_CHECK ) ) + { + isCD2OutofRange = FALSE; + } +#endif + BOOL isTPoOutofRange = ( TPoTemp > MAX_DISINFECT_TPO_TEMPERATURE_C ); + + if ( TRUE != isCD2OutofRange ) + { + minMaxConductivityWaitCounter = 0; + } + else if ( ++minMaxConductivityWaitCounter >= MIN_MAX_CONDUCTIVITY_WAIT_TASK_INT ) + { + alarmDetectedPendingTrigger = ALARM_ID_INLET_WATER_CONDUCTIVITY_IN_HIGH_RANGE; //ALARM_ID_DG_NEW_CON; TODO + chemDisinfectState = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + } + else if ( ( TRUE == isTPoOutofRange ) ) + { + alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_TARGET_TEMP_TIMEOUT; + chemDisinfectState = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; + } + } } /*********************************************************************//** @@ -2183,16 +1732,12 @@ * @details Outputs: disinfectNVOps * @return: none *************************************************************************/ +// TODO: this may conflict with heat disinfect? static void writeDisinfectDataToNV( void ) { - if ( FALSE == disinfectNVOps.hasDisCompleteDateBeenWrittenToNV ) - { - disinfectNVOps.hasDisCompleteDateBeenWrittenToNV = setDisinfectStatus( TRUE ); - } - if ( FALSE == disinfectNVOps.hasDisStatusBeenWrittenToNV ) { - disinfectNVOps.hasDisStatusBeenWrittenToNV = setLastDisinfectDate( USAGE_INFO_CHEMICAL_DISINFECT, getRTCTimestamp() ); + disinfectNVOps.hasDisStatusBeenWrittenToNV = setLastDisinfectDate( USAGE_INFO_CHEM_DIS, getRTCTimestamp() ); } }