/************************************************************************** * * Copyright (c) 2020-2023 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file ModeChemicalDisinfect.c * * @author (last) Dara Navaei * @date (last) 15-Sep-2023 * * @author (original) Sean * @date (original) 04-Apr-2020 * ***************************************************************************/ #include // for memset() #include "ConcentratePumps.h" #include "ConductivitySensors.h" #include "CPLD.h" #include "DrainPump.h" #include "FlowSensors.h" #include "Heaters.h" #include "LoadCell.h" #include "MessageSupport.h" #include "ModeChemicalDisinfect.h" #include "ModeFault.h" #include "ModeStandby.h" #include "NVDataMgmt.h" #include "OperationModes.h" #include "PersistentAlarm.h" #include "Pressures.h" #include "Reservoirs.h" #include "ROPump.h" #include "PIControllers.h" #include "RTC.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "TemperatureSensors.h" #include "Timers.h" #include "UVReactors.h" #include "Valves.h" /** * @addtogroup DGChemicalDisinfectMode * @{ */ // ********** private definitions ********** // General defines #define CHEM_DISINFECT_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Mode chem disinfect data publish interval in counts. // Drain R1 & R2 states defines #define DRAIN_PUMP_TARGET_RPM 2400 ///< Drain pump target RPM during drain. #define DRAIN_WEIGHT_UNCHANGE_TIMEOUT_MS ( 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_TIMEOUT_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 ( 60 * MS_PER_SECOND ) ///< Flush Drain path wait time in milliseconds. // Deprime acid line state defines #define DEPRIME_ACID_LINE_TIMEOUT_MS ( 30 * MS_PER_SECOND ) ///< Deprime acid line timeout in milliseconds. #define DEPRIME_ACID_LINE_ACID_CONC_PUMP_SPEED_MLPM 40.0F ///< Deprime acid line acid concentrate pump speed in mL/min. // Flush circulation path state defines #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_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 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 // 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_MINIMUM_FULL_VOLUME_ML 1850.0F ///< When filling the reservoir, the volume reading must be at least this value before checking for the volume to level off. #define RSRVRS_STEADY_STATE_MAX_VOL_CHANGE_ML 5.0F ///< Reservoirs steady state maximum volume change in milliliters. #define RSRVRS_FULL_STATUS_CHECK_TIME_INTERVAL_MS ( 1 * MS_PER_SECOND ) ///< Reservoirs full status check time interval in milliseconds. #define RSRVRS_FILL_TO_FULL_STABLE_TASK_INT 4 ///< Reservoirs 1 & 2 full stable time in task intervals. // 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 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. #define PRIME_ACID_MINIMUM_PRIME_TIME_MS ( 60 * MS_PER_SECOND ) ///< Prime acid minimum prime time in milliseconds. // Stabilize disinfectant mixing state defines #define MAX_ALLOWED_DISINFECTANT_FLUSH_PERIODS 4 ///< 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_TIMEOUT_MS ( 30 * MS_PER_SECOND ) ///< Flush disinfectant temperature and conductivity to reach target in disinfectant flush state in milliseconds. // 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 DISINFECT_COND_OUT_OF_RANGE_TIMEOUT_MS ( 10 * MS_PER_SECOND ) ///< Chemical disinfect conductivity out of range in milliseconds. #define DISINFECT_TEMP_OUT_OF_RANGE_TIMEOUT_MS ( 1 * MS_PER_SECOND ) ///< Chemical disinfect temperature out of range in milliseconds. // Initial disinfectant fill of R1 and R2 #define RSRVRS_FULL_VOL_ML 1850.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. // 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 ( 5 * 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 #define ACID_MOVING_AVG_NUM_OF_SAMPLES 30 ///< Acid moving average number of samples. #define ACID_DATA_COLLECTION_TIME_MS ( 0.5F * MS_PER_SECOND ) ///< Acid data collection time in milliseconds. /// Non-volatile write structure typedef struct { BOOL hasDisStatusBeenWrittenToNV; ///< Boolean flag to indicate whether the disinfect status been written to NV or not. } DISINFECT_NV_OPS_T; /// Acid status data structure typedef struct { BOOL acidDataColHasTimerBeenSet; ///< Acid has data collection timer started boolean flag. U32 acidDataColStartTimeMS; ///< Acid data collection start time in milliseconds. F32 acidCondRunningSumUSPCM; ///< Acid conductivity running sum in uS/cm. OVERRIDE_F32_T acidCondAvgUSPCM; ///< Acid conductivity average in uS/cm. F32 acidCondSamplesUSPCM[ ACID_MOVING_AVG_NUM_OF_SAMPLES ]; ///< Acid conductivity samples array in uS/cm. U32 acidCondSamplesNextIndex; ///< Acid conductivity sample next index number. } ACID_DATA_STATUS_T; /// Reservoir full data structure typedef struct { F32 prevRsrvrVolumeML[ NUM_OF_DG_RESERVOIRS ]; ///< Previous reservoir volumes in milliliters. U32 startTimeStampMS; ///< Start time stamp in milliseconds. BOOL hasTimeStampBeenSet; ///< Boolean flag to indicate that the timer has been set or not. } RSRVR_FULL_STATUS_T; // ********** private data ********** static DG_CHEM_DISINFECT_STATE_T chemDisinfectState; ///< Currently active chemical disinfect state. static DG_CHEM_DISINFECT_STATE_T prevChemDisinfectState; ///< Previous active heat disinfect state before alarm. static U32 overallChemDisinfectTimer; ///< Chemical disinfect cycle total timer. static U32 stateTimer; ///< Chemical disinfect state timer to be used in different states. static U32 stateTrialCounter; ///< Chemical disinfect state trial counter to be used for retries in different states. static BOOL isThisLastDrain; ///< Boolean flag to check whether draining R1 and R2 is at the end of the chemical disinfect cycle or in the beginning. static DG_RESERVOIR_STATUS_T rsrvr1Status; ///< Reservoir 1 status. static DG_RESERVOIR_STATUS_T rsrvr2Status; ///< Reservoir 2 status. static BOOL isRsrvrLeaking; ///< Flag used to determine if reservoir is leaking. static U32 rsrvrsVolMonitorCounter; ///< Timer/counter used to determine if reservoir is leaking. static U32 chemDisinfectReservoirTime; ///< Chemical disinfect accumulated time in ms above the temperature target in one cycle for reservoir 1 or reservoir 2. static BOOL ischemDisinfectWarmupTargetReached; ///< Flag that indicates whether the disinfect warmup target has been reached. static BOOL isChemDisinfectTempAboveTarget; ///< Flag that indicates if the disinfect temperature is currently above the target. static U32 DisinfectCycleCounter; ///< Counter for disinfect cycles. static U32 drpControlTimerCounter; ///< Timer/counter for reservoir volume control of DRP. static U32 dataPublishCounter; ///< Chemical Disinfect data publish timer/counter. static CANCELLATION_MODE_T cancellationMode; ///< Cancellation mode. static ALARM_ID_T alarmDetectedPendingTrigger; ///< Chemical disinfect alarm to raise. static U32 flushCircWaitTime; ///< Variable time period in ms to wait in flush circulation state to check sensor values. static U32 primeAcidSteadyStateCounter; ///< Prime acid steady state counter. static U32 rsrvrFillStableTimeCounter; ///< Task interval timer/counter for determining if reservoir has reached fill target. static U32 rsrvrFillToFullStableTimeCounter; ///< Task interval timer/counter for determining if reservoir is full. static F32 R1FullVolume; ///< R1 volume determined by fill to full function. static F32 R2FullVolume; ///< R2 volume determined by fill to full function. static F32 disinfectantMixRatio ; ///< Current disinfectant mixing ratio. static DISINFECT_NV_OPS_T disinfectNVOps; ///< Disinfect non-volatile memory operations. static BOOL haveDrainParamsBeenInit[ NUM_OF_DG_RESERVOIRS ]; ///< Boolean flag to indicate whether the drain parameters have been reset or not. static U32 maxTemperatureOutOfRangeStartTimeMS; ///< Maximum temperature out of range start time in milliseconds. static U32 maxCondOutOfRangeStartTimeMS; ///< Maximum conductivity out of range start time in milliseconds. static ACID_DATA_STATUS_T acidDataStatus; ///< Acid data status. static RSRVR_FULL_STATUS_T rsrvrFullStatus; ///< Reservoir full status. #ifndef _RELEASE_ /* Nelson Labs is in charge of testing the efficacy of the disinfects (heat and chem). The codes that contain the name Nelson are used to * support the special conditions that are needed to be created to test the disinfects. The support codes are not compiled in a release * build. */ static NELSON_SUPPORT_T nelsonSupport; // Nelson support. #endif // ********** private function prototypes ********** static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectStartState( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDrainR1State( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDrainR2State( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFlushDrainState( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDeprimeAcidLine( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFlushCirculationState( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectPrimeDisinfectantState( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDisinfectantFlushState( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFillWithDisinfectantState( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDisinfectR1ToR2State( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectPartialDrainR1FillR2ToR1State( void ); static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDisinfectR2ToR1State( 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 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( DG_USAGE_INFO_ITEMS_T info ); static void processAcidConductivityData( void ); static BOOL isAcidCondOutOfRange( void ); #ifndef _RELEASE_ static void setNelsonSupportConditions( void ); #endif /*********************************************************************//** * @brief * The initChemicalDisinfectMode function initializes the chemical * disinfect mode module. * @details Inputs: none * @details Outputs: chemDisinfectState, prevChemDisinfectState * stateTimer, isThisLastDrain, stateTrialCounter * rsrvr1Status, rsrvr2Status, overallChemDisinfectTimer, cancellationMode, * rsrvrFillToFullStableTimeCounter, primeAcidSteadyStateCounter, isRsrvrLeaking, * chemDisinfectReservoirTime, ischemDisinfectWarmupTargetReached, * isChemDisinfectTempAboveTarget, isRsrvrLeaking, * disinfectNVOps.hasDisStatusBeenWrittenToNV, rsrvrsVolMonitorCounter, * DisinfectCycleCounter, drpControlTimerCounter, dataPublishCounter, * alarmDetectedPendingTrigger, flushCircWaitTime, acidDataStatus * primeAcidSteadyStateCounter, rsrvrFillStableTimeCounter, R1FullVolume, * rsrvrFillToFullStableTimeCounter, R2FullVolume, disinfectantMixRatio, * haveDrainParamsBeenInit[ DG_RESERVOIR_1 ], haveDrainParamsBeenInit[ DG_RESERVOIR_2 ], * maxTemperatureOutOfRangeStartTimeMS, maxCondOutOfRangeStartTimeMS, * rsrvrFullStatus * @return none *************************************************************************/ void initChemicalDisinfectMode( void ) { DG_RESERVOIR_ID_T rsrvr; for ( rsrvr = DG_RESERVOIR_1; rsrvr < NUM_OF_DG_RESERVOIRS; rsrvr++ ) { haveDrainParamsBeenInit[ rsrvr ] = FALSE; } chemDisinfectState = DG_CHEM_DISINFECT_STATE_START; prevChemDisinfectState = DG_CHEM_DISINFECT_STATE_START; stateTimer = getMSTimerCount(); isThisLastDrain = FALSE; stateTrialCounter = 0; rsrvr1Status = NUM_OF_DG_RESERVOIR_STATUS; rsrvr2Status = NUM_OF_DG_RESERVOIR_STATUS; overallChemDisinfectTimer = getMSTimerCount(); cancellationMode = CANCELLATION_MODE_NONE; rsrvrFillToFullStableTimeCounter = 0; primeAcidSteadyStateCounter = 0; disinfectantMixRatio = 0.0F; isRsrvrLeaking = FALSE; chemDisinfectReservoirTime = 0; ischemDisinfectWarmupTargetReached = FALSE; isChemDisinfectTempAboveTarget = FALSE; disinfectNVOps.hasDisStatusBeenWrittenToNV = FALSE; isRsrvrLeaking = FALSE; rsrvrsVolMonitorCounter = 0; DisinfectCycleCounter = 0; drpControlTimerCounter = 0; dataPublishCounter = 0; alarmDetectedPendingTrigger = ALARM_ID_NO_ALARM; flushCircWaitTime = 0; rsrvrFillStableTimeCounter = 0; rsrvrFillToFullStableTimeCounter = 0; R1FullVolume = 0.0F; R2FullVolume = 0.0F; disinfectantMixRatio = 0.0F; maxTemperatureOutOfRangeStartTimeMS = getMSTimerCount(); maxCondOutOfRangeStartTimeMS = getMSTimerCount(); acidDataStatus.acidDataColHasTimerBeenSet = FALSE; acidDataStatus.acidCondAvgUSPCM.data = 0.0F; acidDataStatus.acidCondAvgUSPCM.ovData = 0.0F; acidDataStatus.acidCondAvgUSPCM.ovInitData = 0.0F; acidDataStatus.acidCondAvgUSPCM.override = 0.0F; acidDataStatus.acidDataColHasTimerBeenSet = getMSTimerCount(); acidDataStatus.acidCondRunningSumUSPCM = 0.0F; acidDataStatus.acidCondSamplesNextIndex = 0; rsrvrFullStatus.hasTimeStampBeenSet = FALSE; rsrvrFullStatus.startTimeStampMS = getMSTimerCount(); memset( acidDataStatus.acidCondSamplesUSPCM, 0x0, sizeof( F32 ) * ACID_MOVING_AVG_NUM_OF_SAMPLES ); memset( rsrvrFullStatus.prevRsrvrVolumeML, 0x0, sizeof( F32 ) * NUM_OF_DG_RESERVOIRS ); #ifndef _RELEASE_ setNelsonSupportConditions(); #endif } /*********************************************************************//** * @brief * The transitionToChemicalDisinfectMode function prepares for transition to * chemical disinfect mode. * @details Inputs: none * @details Outputs: chemical disisnfect mode variables are reset * @return initial state *************************************************************************/ U32 transitionToChemicalDisinfectMode( void ) { // Set all the actuators to reset and de-energized state deenergizeActuators( NO_PARK_CONC_PUMPS ); initChemicalDisinfectMode(); setCurrentSubState( NO_SUB_STATE ); setCPLDCleanLEDColor( CPLD_CLEAN_LED_YELLOW ); // Set CD1 and CD2 conductivity sensors calibration table to be picked from the chemical disinfect calibration record setCondcutivitySensorCalTable( CONDUCTIVITYSENSORS_CD2_SENSOR, CAL_DATA_CD2_COND_SENSOR_CHEM_DISINFECT ); #ifndef _RELEASE_ if ( nelsonSupport != NELSON_CHEM_DISINFECT ) #endif { activateAlarmNoData( ALARM_ID_DG_CHEM_DISINFECT_INSERT_ACID ); } return chemDisinfectState; } /*********************************************************************//** * @brief * The execChemicalDisinfectMode function executes the chemical disinfect * mode state machine. * @details Inputs: none * @details Outputs: Chemical disinfect mode state machine executed * @return current state of chemical disinfect mode *************************************************************************/ U32 execChemicalDisinfectMode( void ) { processAcidConductivityData(); // The inlet pressure shall be checked all the time as long as VPi is open checkInletWaterPressure(); if ( chemDisinfectState != DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN ) { // Do not check on the inlet water temperature and conductivity until the inlet filters have been flushed // The initial states are drain reservoirs but in those states VPi is closed so these alarms are not checked checkInletWaterTemperature(); checkInletWaterConductivity(); } monitorModeChemicalDisinfect(); switch ( chemDisinfectState ) { case DG_CHEM_DISINFECT_STATE_START: chemDisinfectState = handleChemicalDisinfectStartState(); break; case DG_CHEM_DISINFECT_STATE_DRAIN_R1: chemDisinfectState = handleChemicalDisinfectDrainR1State(); break; case DG_CHEM_DISINFECT_STATE_DRAIN_R2: chemDisinfectState = handleChemicalDisinfectDrainR2State(); break; case DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN: chemDisinfectState = handleChemicalDisinfectFlushDrainState(); break; case DG_CHEM_DISINFECT_STATE_DEPRIME_ACID_LINE: chemDisinfectState = handleChemicalDisinfectDeprimeAcidLine(); break; case DG_CHEM_DISINFECT_STATE_FLUSH_CIRCULATION: chemDisinfectState = handleChemicalDisinfectFlushCirculationState(); break; case DG_CHEM_DISINFECT_STATE_PRIME_DISINFECTANT: chemDisinfectState = handleChemicalDisinfectPrimeDisinfectantState(); break; case DG_CHEM_DISINFECT_STATE_DISINFECTANT_FLUSH: chemDisinfectState = handleChemicalDisinfectDisinfectantFlushState(); break; case DG_CHEM_DISINFECT_STATE_FILL_WITH_DISINFECTANT: chemDisinfectState = handleChemicalDisinfectFillWithDisinfectantState(); break; case DG_CHEM_DISINFECT_STATE_DISINFECT_R1_TO_R2: chemDisinfectState = handleChemicalDisinfectDisinfectR1ToR2State(); break; 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_PARTIAL_DRAIN_R2_FILL_R1_TO_R2: chemDisinfectState = handleChemicalDisinfectPartialDrainR2FillR1ToR2State(); break; case DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH: chemDisinfectState = handleChemicalDisinfectCancelModeBasicPathState(); break; case DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH: chemDisinfectState = handleChemicalDisinfectCancelModeWaterPathState(); break; case DG_CHEM_DISINFECT_STATE_COMPLETE: chemDisinfectState = handleChemicalDisinfectCompleteState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_DG_CHEM_DISINFECT_INVALID_EXEC_STATE, chemDisinfectState ) chemDisinfectState = DG_CHEM_DISINFECT_STATE_START; break; } publishChemicalDisinfectData(); return chemDisinfectState; } /*********************************************************************//** * @brief * The getCurrentChemicalDisinfectState function returns the current state * of the chemical disinfect mode. * @details Inputs: chemState * @details Outputs: none * @return current state of chemical disinfect mode *************************************************************************/ DG_CHEM_DISINFECT_STATE_T getCurrentChemicalDisinfectState( void ) { return chemDisinfectState; } /*********************************************************************//** * @brief * The stopChemicalDisinfect function stops chemical disinfect mode. * @details Inputs: none * @details Outputs: none * @return TRUE is current operation mode is chemical disinfect, * otherwise FALSE *************************************************************************/ BOOL stopChemicalDisinfect( void ) { BOOL status = FALSE; // Check if the current operation mode is chemical disinfect if ( DG_MODE_CHEM == getCurrentOperationMode() ) { // Reset all the actuators deenergizeActuators( NO_PARK_CONC_PUMPS ); // Transition to mode standby requestNewOperationMode( DG_MODE_STAN ); status = TRUE; } return status; } // ********** private functions ********** /*********************************************************************//** * @brief * The handleChemicalDisinfectStartState function handles the chemical * disinfect start state. The state checks the inlet pressure and the * difference in between TDi and TRo sensors and if they are not in * range, it transitions to basic cancellation path. Otherwise, it * transitions to the next state. * @details Inputs: alarm, rsrvrFillStableTimeCounter, rsrvr1Status, * stateTimer * @details Outputs: alarm, rsrvrFillStableTimeCounter, rsrvr1Status, * stateTimer * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectStartState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_START; if ( FALSE == isAlarmActive( ALARM_ID_DG_CHEM_DISINFECT_INSERT_ACID ) ) { if ( ( STATE_CLOSED == getSwitchStatus( CONCENTRATE_CAP ) ) || ( STATE_OPEN == getSwitchStatus( DIALYSATE_CAP ) ) ) { activateAlarmNoData( ALARM_ID_DG_CHEM_DISINFECT_INSERT_ACID ); } else { // Set all the actuators to reset and de-energized state deenergizeActuators( NO_PARK_CONC_PUMPS ); setValveState( VPI, VALVE_STATE_CLOSED ); setValveState( VRD1, VALVE_STATE_OPEN ); setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); // Assume reservoir 1 is full and drain it rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; state = DG_CHEM_DISINFECT_STATE_DRAIN_R1; stateTimer = getMSTimerCount(); } } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectDrainR1State function handles the chemical * disinfect drain R1 state. The state drains reservoir 1. If the * transition is finished within the time, it transitions to the next state, * otherwise, it transitions to basic cancellation path. * @details Inputs: stateTimer, rsrvr1Status, rsrvr2Status, isThisLastDrain * @details Outputs: rsrvr1Status, rsrvr2Status * @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; if ( TRUE == isThisLastDrain ) { writeDisinfectDataToNV( USAGE_INFO_CHEM_DIS ); } if ( DG_RESERVOIR_ABOVE_TARGET == rsrvr1Status ) { rsrvr1Status = getRsrvrDrainStatus( DG_RESERVOIR_1, DRAIN_WEIGHT_UNCHANGE_TIMEOUT_MS, RSRVRS_INITIAL_DRAIN_TIMEOUT_MS ); } else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) { // Done with draining R1 if ( FALSE == isThisLastDrain ) { tareLoadCell( LOAD_CELL_RESERVOIR_1_PRIMARY ); tareLoadCell( LOAD_CELL_RESERVOIR_1_BACKUP ); } 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 ) { setValveState( VRD1, VALVE_STATE_CLOSED ); prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectDrainR2State function handles the chemical * disinfect drain R2 state. The state drains reservoir 2. If the * transition is finished within the time, it transitions to the next * state, otherwise, it transitions to basic cancellation path. * @details Inputs: stateTimer, rsrvr2Status, isThisLastDrain * @details Outputs: stateTimer, rsrvr2Status, stateTrialCounter * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDrainR2State( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_DRAIN_R2; if ( DG_RESERVOIR_ABOVE_TARGET == rsrvr2Status ) { rsrvr2Status = getRsrvrDrainStatus( DG_RESERVOIR_2, DRAIN_WEIGHT_UNCHANGE_TIMEOUT_MS, RSRVRS_INITIAL_DRAIN_TIMEOUT_MS ); } else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) { signalDrainPumpHardStop(); if ( TRUE == isThisLastDrain ) { // This is the final drain of chemical disinfect setValveState( VRD2, VALVE_STATE_CLOSED ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); state = DG_CHEM_DISINFECT_STATE_COMPLETE; } else { tareLoadCell( LOAD_CELL_RESERVOIR_2_PRIMARY ); tareLoadCell( LOAD_CELL_RESERVOIR_2_BACKUP ); setValveState( VPD, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); setValveState( VRD2, VALVE_STATE_CLOSED ); setValveState( VPI, VALVE_STATE_OPEN ); 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 ) { prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectFlushDrainState function handles the chemical * disinfect flush drain state. The state flushes the drain line for a * period of time and then measures the temperature and conductivity of * water. If they are not within the range, it transitions to basic * cancellation path, otherwise it transitions to the next state. * @details Inputs: stateTimer * @details Outputs: stateTimer * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFlushDrainState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_FLUSH_DRAIN; // Check if flush time has elapsed if ( TRUE == didTimeout( stateTimer, FLUSH_DRAIN_WAIT_TIME_MS ) ) { // set pumps and valves for next state, flush circulation setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); 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 ); setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, DEPRIME_ACID_LINE_ACID_CONC_PUMP_SPEED_MLPM ); // Turn on the concentrate pumps requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); stateTimer = getMSTimerCount(); state = DG_CHEM_DISINFECT_STATE_DEPRIME_ACID_LINE; } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectDeprimeAcidLine function handles the chemical * disinfect deprime acid line state. In this state the acid pump is run for * a couple of seconds to remove the residual fluid from the acid line to * prevent any leaks from the acid port. * @details Inputs: stateTimer * @details Outputs: stateTimer, flushCircWaitTime * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDeprimeAcidLine( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_DEPRIME_ACID_LINE; if ( TRUE == didTimeout( stateTimer, DEPRIME_ACID_LINE_TIMEOUT_MS ) ) { requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID, PARK_CONC_PUMPS ); flushCircWaitTime = FLUSH_CICRCULATION_INITIAL_WAIT_TIME_MS; stateTimer = getMSTimerCount(); state = DG_CHEM_DISINFECT_STATE_FLUSH_CIRCULATION; } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectFlushCirculationState function handles the * chemical disinfect flush circulation state. The state flushes the * circulation line for a period of time. After the flush if the temperature * sensors are not within a certain degrees from each other, the state * transitions to basic cancellation path, otherwise, it transitions to the * next state. * @details Inputs: stateTimer, stateTrialCounter, prevChemDisinfectState * @details Outputs: stateTimer, stateTrialCounter, prevChemDisinfectState, * 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, flushCircWaitTime ) ) { F32 TPoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); F32 TD2Temp = getTemperatureValue( TEMPSENSORS_CONDUCTIVITY_SENSOR_2 ); F32 avgTemp = ( TPoTemp + TD2Temp ) / NUM_OF_TEMP_SENSORS_TO_AVG; F32 cd2CondUSPCM = getConductivityValue( (U32)CONDUCTIVITYSENSORS_CD2_SENSOR ); F32 cpoCondUSPCM = getConductivityValue( (U32)CONDUCTIVITYSENSORS_CPO_SENSOR ); BOOL isTPoOut = FALSE; BOOL isTD2Out = FALSE; BOOL isCD2Out = FALSE; BOOL isCPoOut = FALSE; #ifndef _RELEASE_ if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_ENABLE_USING_TPO_FOR_PRIMARY_HEATER_CONTROL ) ) { TD2Temp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); } #endif #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 ); } isCD2Out = ( cd2CondUSPCM > MAX_FLUSH_CIRC_CONDUCTIVITY_US_PER_CM ? TRUE : FALSE ); isCPoOut = ( cpoCondUSPCM > MAX_FLUSH_CIRC_CONDUCTIVITY_US_PER_CM ? TRUE : FALSE ); // Check if any of the temperature sensors are out of tolerance if( ( TRUE == isTPoOut ) || ( TRUE == isTD2Out ) || ( TRUE == isCD2Out ) || ( TRUE == isCPoOut ) ) { // Check if we have exceeded the number of trials. If not, try another time ++stateTrialCounter; if ( stateTrialCounter < MAX_ALLOWED_FLUSH_CIRC_PERIODS ) { stateTimer = getMSTimerCount(); flushCircWaitTime = FLUSH_CICRCULATION_ADDITIONAL_WAIT_TIME_MS; } else { alarmDetectedPendingTrigger = ALARM_ID_DG_CLEANING_MODE_COND_SENSORS_OUT_OF_RANGE; // 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_CLEANING_MODE_TEMP_SENSORS_DIFF_OUT_OF_RANGE; } prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; } } else { 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; stateTrialCounter = 0; state = DG_CHEM_DISINFECT_STATE_PRIME_DISINFECTANT; stateTimer = getMSTimerCount(); } } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectPrimeDisinfectantState function handles the * chemical disinfect prime acid line state. The state primes the acid line * until a minimum conductivity is sensed for a period of time. The the * function transitions to another state. If the minimum conductivity was * not reached within the defined period of time, it transitions to a * cancellation path. * @details Inputs: stateTimer, primeAcidSteadyStateCounter * @details Outputs: stateTimer, prevChemDisinfectState, * primeAcidSteadyStateCounter, maxTemperatureOutOfRangeStartTimeMS, * maxCondOutOfRangeStartTimeMS * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectPrimeDisinfectantState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_PRIME_DISINFECTANT; handleDisinfectantMixing( getMeasuredFlowRateLPM( RO_FLOW_SENSOR ) * ML_PER_LITER, disinfectantMixRatio ); if ( TRUE == didTimeout( stateTimer, PRIME_ACID_MINIMUM_PRIME_TIME_MS ) ) { #ifndef _RELEASE_ if ( nelsonSupport != NELSON_CHEM_DISINFECT ) #endif { if ( getF32OverrideValue( &acidDataStatus.acidCondAvgUSPCM ) < MIN_PRIME_ACID_CONDUCTIVITY_US_PER_CM ) { primeAcidSteadyStateCounter = 0; } } // Check if the acid conductivity value has been above the threshold for the specified time period if ( ( ++primeAcidSteadyStateCounter >= PRIME_ACID_STEADY_CONDUCTIVITY_TASK_INT ) || ( TRUE == getTestConfigStatus( TEST_CONFIG_MIX_WITH_WATER ) ) ) { // 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( CHEM_DISINFECT_TARGET_RO_FLOW_LPM, MAX_RO_PUMP_FILL_DISINFECTANT_PRESSURE_PSI ); setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, DISINFECTANT_PUMP_FILL_SPEED_ML_PER_MIN ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); stateTrialCounter = 0; disinfectantMixRatio = DISINFECTANT_MIX_RATIO_FILL; stateTimer = getMSTimerCount(); maxTemperatureOutOfRangeStartTimeMS = getMSTimerCount(); maxCondOutOfRangeStartTimeMS = getMSTimerCount(); state = DG_CHEM_DISINFECT_STATE_DISINFECTANT_FLUSH; } else if ( TRUE == didTimeout( stateTimer, PRIME_ACID_LINE_TIMEOUT_MS ) ) { prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_PRIME_ACID_LINE_TIME_OUT; } } return state; } /*********************************************************************//** * @brief * 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, disinfectantMixRatio, stateTrialCounter * @details Outputs: stateTimer, stateTrialCounter, rsrvr1Status, rsrvr2Status, * chemDisinfectReservoirTime, rsrvrFillStableTimeCounter, isChemDisinfectTempAboveTarget, * disinfectNVOps * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectDisinfectantFlushState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_DISINFECTANT_FLUSH; writeDisinfectDataToNV( USAGE_INFO_CHEM_DIS_START ); handleDisinfectantMixing( getMeasuredFlowRateLPM( RO_FLOW_SENSOR ) * ML_PER_LITER, disinfectantMixRatio ); if ( TRUE == didTimeout( stateTimer, FLUSH_DISINFECTANT_TIMEOUT_MS ) ) { BOOL isCD2OutOfRange = isAcidCondOutOfRange(); if ( TRUE == getTestConfigStatus( TEST_CONFIG_MIX_WITH_WATER ) ) { isCD2OutOfRange = FALSE; } if ( TRUE == isCD2OutOfRange ) { stateTimer = getMSTimerCount(); if ( ++stateTrialCounter >= MAX_ALLOWED_DISINFECTANT_FLUSH_PERIODS ) { alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_TARGET_COND_OUT_OF_RANGE; prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; } } else { // Prepare for filling the reservoirs; fill reservoir 1 and overflow it into reservoir 2 setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); setValveState( VRD1, VALVE_STATE_CLOSED ); setValveState( VRD2, VALVE_STATE_CLOSED ); turnOffUVReactor( OUTLET_UV_REACTOR ); rsrvr1Status = DG_RESERVOIR_BELOW_TARGET; rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; chemDisinfectReservoirTime = 0; rsrvrFillStableTimeCounter = 0; isChemDisinfectTempAboveTarget = FALSE; stateTimer = getMSTimerCount(); disinfectNVOps.hasDisStatusBeenWrittenToNV = FALSE; state = DG_CHEM_DISINFECT_STATE_FILL_WITH_DISINFECTANT; } } return state; } /*********************************************************************//** * @brief * 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 * transitions to next state. The levels of the reservoirs are recorded to * be monitored during chemical disinfect. * @details Inputs: stateTimer, rsrvr1Status, rsrvr2Status, * rsrvrsVolMonitorTimer * @details Outputs: stateTimer, rsrvr1Status, rsrvr2Status, * R1ChemDisinfectVol, R2ChemDisinfectVol, rsrvrsVolMonitorTimer, * chemDisinfectReservoirTime * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectFillWithDisinfectantState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_FILL_WITH_DISINFECTANT; handleDisinfectantMixing( getMeasuredFlowRateLPM( RO_FLOW_SENSOR ) * ML_PER_LITER, disinfectantMixRatio ); // First reservoir 1 must be full if ( DG_RESERVOIR_BELOW_TARGET == rsrvr1Status ) { // 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 ) { // 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 R2 is full, transition to the next state if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) { // go to the next disinfect cycle, fill R1 and drain R2 setValveState( VRD2, VALVE_STATE_OPEN ); setROPumpTargetFlowRateLPM( CHEM_DISINFECT_TARGET_RO_FLOW_LPM, MAX_RO_PUMP_FILL_DISINFECTANT_PRESSURE_PSI ); // Set the drain pump to control mode controlDRPByReservoirVolume( DG_RESERVOIR_2, TRUE ); //Initialize the PI controller for DRP isRsrvrLeaking = FALSE; ischemDisinfectWarmupTargetReached = FALSE; chemDisinfectReservoirTime = 0; 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; } } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectDisinfectR1ToR2State function handles the * 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, * prevChemDisinfectState * @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; handleDisinfectantMixing( getMeasuredFlowRateLPM( RO_FLOW_SENSOR ) * ML_PER_LITER, disinfectantMixRatio ); chemicalDisinfectTimerUpdate(); if ( ( FALSE == ischemDisinfectWarmupTargetReached ) && ( TRUE == didTimeout( stateTimer, CHEM_DISINFECT_START_TEMP_TIMEOUT_MS ) ) ) { // Heating up to minimum temperature for chemical disinfect failed alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_TARGET_TEMP_OUT_OF_RANGE; prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; } if ( TRUE == didTimeout( stateTimer, MAX_DISINFECT_STATE_TIME_MS ) ) { // Heating up to minimum temperature for chemical disinfect failed alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_TARGET_TEMP_OUT_OF_RANGE; prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; } if ( chemDisinfectReservoirTime >= DISINFECT_CYCLE_PERIOD_MS ) { // 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; } else { controlDRPByReservoirVolume( DG_RESERVOIR_2, FALSE ); } return state; } /*********************************************************************//** * @brief * 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, disinfectantMixRatio * @details Outputs: stateTimer, isRsrvrLeaking, chemDisinfectReservoirTime, * isChemDisinfectTempAboveTarget, ischemDisinfectWarmupTargetReached, * prevChemDisinfectState * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectPartialDrainR1FillR2ToR1State( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_PARTIAL_DRAIN_R1_FILL_R2_TO_R1; handleDisinfectantMixing( getMeasuredFlowRateLPM( RO_FLOW_SENSOR ) * ML_PER_LITER, disinfectantMixRatio ); // First reservoir 2 must be full if ( DG_RESERVOIR_BELOW_TARGET == rsrvr2Status ) { // If R2 is not full, keep monitoring for R2 level and timeout rsrvr2Status = getRsrvrFillToFullStatus( DG_RESERVOIR_2, RSRVRS_ALMOST_FULL_FILL_UP_TIMEOUT_MS ); } if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) { isRsrvrLeaking = FALSE; chemDisinfectReservoirTime = 0; isChemDisinfectTempAboveTarget = FALSE; ischemDisinfectWarmupTargetReached = FALSE; stateTimer = getMSTimerCount(); 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 { controlDRPByReservoirVolume( DG_RESERVOIR_1, FALSE ); } return state; } /*********************************************************************//** * @brief * 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, * @return next state of the chemical 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; handleDisinfectantMixing( getMeasuredFlowRateLPM( RO_FLOW_SENSOR ) * ML_PER_LITER, disinfectantMixRatio ); chemicalDisinfectTimerUpdate(); if ( (TRUE != ischemDisinfectWarmupTargetReached ) && ( TRUE == didTimeout( stateTimer, CHEM_DISINFECT_START_TEMP_TIMEOUT_MS ) ) ) { // Heating up to minimum temperature for chemical disinfect failed alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_TARGET_TEMP_OUT_OF_RANGE; prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; } if ( TRUE == didTimeout( stateTimer, MAX_DISINFECT_STATE_TIME_MS ) ) { // Heating up to minimum temperature for chemical disinfect failed alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_TARGET_TEMP_OUT_OF_RANGE; prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; } if ( chemDisinfectReservoirTime >= DISINFECT_CYCLE_PERIOD_MS ) { if ( REQUIRED_DISINFECT_CYCLES == ++DisinfectCycleCounter ) { //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( VRD2, VALVE_STATE_CLOSED ); setValveState( VPD, VALVE_STATE_DRAIN_C_TO_NO ); 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 ); // 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; isThisLastDrain = TRUE; stateTimer = getMSTimerCount(); state = DG_CHEM_DISINFECT_STATE_DRAIN_R1; #ifndef _RELEASE_ if ( NELSON_CHEM_DISINFECT == nelsonSupport ) { deenergizeActuators( NO_PARK_CONC_PUMPS ); state = DG_CHEM_DISINFECT_STATE_COMPLETE; } #endif } else { // 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(); rsrvr1Status = DG_RESERVOIR_BELOW_TARGET; state = DG_CHEM_DISINFECT_STATE_PARTIAL_DRAIN_R2_FILL_R1_TO_R2; } } else { controlDRPByReservoirVolume( DG_RESERVOIR_1, FALSE ); } return state; } /*********************************************************************//** * @brief * 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: stateTimer, rsrvr1Status, disinfectantMixRatio * @details Outputs: stateTimer, chemDisinfectReservoirTime, isChemDisinfectTempAboveTarget, * ischemDisinfectWarmupTargetReached, isRsrvrLeaking, prevChemDisinfectState * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectPartialDrainR2FillR1ToR2State( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_PARTIAL_DRAIN_R2_FILL_R1_TO_R2; handleDisinfectantMixing( getMeasuredFlowRateLPM( RO_FLOW_SENSOR ) * ML_PER_LITER, disinfectantMixRatio ); // First reservoir 1 must be full if ( DG_RESERVOIR_BELOW_TARGET == rsrvr1Status ) { // If R1 is not full, keep monitoring for R1 level and timeout rsrvr1Status = getRsrvrFillToFullStatus( DG_RESERVOIR_1, RSRVRS_ALMOST_FULL_FILL_UP_TIMEOUT_MS ); } if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) { chemDisinfectReservoirTime = 0; isChemDisinfectTempAboveTarget = 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; } else { controlDRPByReservoirVolume( DG_RESERVOIR_2, FALSE ); } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectCancelModeBasicPathState function handles the * chemical disinfect cancel mode basic path state. The state sets the state * to complete and raises an alarm. * @details Inputs: none * @details Outputs: cancellationMode * @return next state of the heat disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectCancelModeBasicPathState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; cancellationMode = CANCELLATION_MODE_BASIC; failChemicalDisinfect(); return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectCancelModeWaterPathState function handles the * 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 * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectCancelModeWaterPathState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; if ( CANCELLATION_MODE_NONE == cancellationMode ) { // Stop all the actuators first then decide who should run next deenergizeActuators( NO_PARK_CONC_PUMPS ); cancellationMode = CANCELLATION_MODE_WATER; rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; rsrvr2Status = DG_RESERVOIR_ABOVE_TARGET; stateTimer = getMSTimerCount(); // The drain is set to start from reservoir 2 setValveState( VRD2, VALVE_STATE_OPEN ); setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); } // If reservoir 2 is empty, set to drain reservoir 1 if ( DG_RESERVOIR_ABOVE_TARGET == rsrvr2Status ) { // If the cancellation water path cannot be done, got to basic cancellation path rsrvr2Status = getRsrvrDrainStatus( DG_RESERVOIR_2, DRAIN_WEIGHT_UNCHANGE_TIMEOUT_MS, RSRVRS_INITIAL_DRAIN_TIMEOUT_MS ); if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) { // Set the drain valve to reservoir 1 setValveState( VRD1, VALVE_STATE_OPEN ); setValveState( VRD2, VALVE_STATE_CLOSED ); } } // Could not drain reservoir 2. Transition to basic cancellation path else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) { prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; } // If reservoir 2 has already been drained and reservoir 1 is empty, reset and switch to complete if ( ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) && ( DG_RESERVOIR_ABOVE_TARGET == rsrvr1Status ) ) { // If the cancellation water path cannot be done, got to basic cancellation path rsrvr1Status = getRsrvrDrainStatus( DG_RESERVOIR_1, DRAIN_WEIGHT_UNCHANGE_TIMEOUT_MS, RSRVRS_INITIAL_DRAIN_TIMEOUT_MS ); if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) { failChemicalDisinfect(); } // Could not drain reservoir 1. Transition to basic cancellation path else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { prevChemDisinfectState = state; state = DG_CHEM_DISINFECT_STATE_CANCEL_BASIC_PATH; } } return state; } /*********************************************************************//** * @brief * The handleChemicalDisinfectCompleteState function handles the chemical * disinfect complete state. The state stops chemical disinfect and * requests transition to mode standby. * @details Inputs: none * @details Outputs: none * @return next state of the chemical disinfect state machine *************************************************************************/ static DG_CHEM_DISINFECT_STATE_T handleChemicalDisinfectCompleteState( void ) { DG_CHEM_DISINFECT_STATE_T state = DG_CHEM_DISINFECT_STATE_COMPLETE; #ifndef _RELEASE_ if ( nelsonSupport != NELSON_NONE ) { nelsonSupport = NELSON_NONE; requestNewOperationMode( DG_MODE_STAN ); } else #endif { requestNewOperationMode( DG_MODE_CHFL ); } return state; } /*********************************************************************//** * @brief * The failChemicalDisinfect function sets the alarm that failed the * chemical disinfect mode. * @details Inputs: alarmDetectedPendingTrigger, prevChemDisinfectState * @details Outputs: none * @return none *************************************************************************/ static void failChemicalDisinfect( void ) { // If a fault alarm is active go to mode fault otherwise for cleaning mode alarms, transition to standby DG_OP_MODE_T nextOpMode = ( FALSE == isDGFaultAlarmActive() ? DG_MODE_STAN : DG_MODE_FAUL ); // In the cleaning modes the alarms are triggered but the mode is not transitioned to fault automatically // so transition to fault mode is done here if ( alarmDetectedPendingTrigger != ALARM_ID_NO_ALARM ) { SET_ALARM_WITH_1_U32_DATA( alarmDetectedPendingTrigger, prevChemDisinfectState ) } requestNewOperationMode( nextOpMode ); } /*********************************************************************//** * @brief * The getRsrvrFillStatus function checks whether the target reservoir * 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: 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 * @return the status of the reservoirs during filling *************************************************************************/ static DG_RESERVOIR_STATUS_T getRsrvrFillStatus( DG_RESERVOIR_ID_T r, F32 targetVol, U32 timeout ) { DG_RESERVOIR_STATUS_T status = DG_RESERVOIR_BELOW_TARGET; F32 volume = 0.0F; if ( DG_RESERVOIR_1 == r ) { volume = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); } else if ( DG_RESERVOIR_2 == r ) { volume = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); } else { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_DG_RESERVOIR_SELECTED, r ) } // Check the volume of the reservoir against the target volume if ( volume >= targetVol ) { if ( ++rsrvrFillStableTimeCounter >= RSRVRS_FULL_STABLE_TIME_TASK_INT ) { status = DG_RESERVOIR_REACHED_TARGET; rsrvrFillStableTimeCounter = 0; // Set the state timer in case it needs to be used for another timeout check stateTimer = getMSTimerCount(); } } else if ( TRUE == didTimeout( stateTimer, timeout ) ) { // Failed to fill ontime. Update the previous chemical disinfect state and transition to basic cancellation prevChemDisinfectState = chemDisinfectState; alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_FILL_TIMEOUT; status = DG_RESERVOIR_NOT_REACHED_TARGET; } return status; } /*********************************************************************//** * @brief * The getRsrvrDrainStatus function returns the status of draining a * reservoir. * @details Inputs: stateTimer, prevChemDisinfectState, chemDisinfectState * @details Outputs: stateTimer, chemDisinfectState, alarm * @param r is DG_RESERVOIR_1 or DG_RESERVOIR_2 * @param drainSteadyStateTimeout which is the time the reservoir's level * does not change and is steady state * @param timeout which is the timeout that a reservoir must be drained by * then * @return the status of the reservoirs during draining *************************************************************************/ static DG_RESERVOIR_STATUS_T getRsrvrDrainStatus( DG_RESERVOIR_ID_T r, U32 drainSteadyStateTimeout, U32 timeout ) { DG_RESERVOIR_STATUS_T status = DG_RESERVOIR_ABOVE_TARGET; BOOL isDrainComplete = FALSE; // If the drain parameters of the reservoir is not initialized, initialize them if ( FALSE == haveDrainParamsBeenInit[ r ] ) { initDrainParameters( r ); haveDrainParamsBeenInit[ r ] = TRUE; } // NOTE: the drain status should be checked once the reservoirs parameters are initialized. This is to make sure the // the timers for stable drain time are initialized prior to using them again isDrainComplete = hasTargetDrainToZeroBeenReached( r, drainSteadyStateTimeout ); if ( TRUE == isDrainComplete ) { 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; alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_DRAIN_TIMEOUT; haveDrainParamsBeenInit[ r ] = FALSE; status = DG_RESERVOIR_NOT_REACHED_TARGET; } return status; } /*********************************************************************//** * @brief * 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 DG_RESERVOIR_STATUS_T getRsrvrFillToFullStatus( DG_RESERVOIR_ID_T r, U32 timeout ) { DG_RESERVOIR_STATUS_T status = DG_RESERVOIR_BELOW_TARGET; F32 currentVolumeML = 0.0F; switch( r ) { case DG_RESERVOIR_1: currentVolumeML = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); break; case DG_RESERVOIR_2: currentVolumeML = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_DG_RESERVOIR_SELECTED, r ) break; } // Check the volume of the reservoir against the long term filtered volume if ( currentVolumeML > RESERVOIR_MINIMUM_FULL_VOLUME_ML ) { if ( FALSE == rsrvrFullStatus.hasTimeStampBeenSet ) { rsrvrFullStatus.startTimeStampMS = getMSTimerCount(); rsrvrFullStatus.hasTimeStampBeenSet = TRUE; } else if ( TRUE == didTimeout( rsrvrFullStatus.startTimeStampMS, RSRVRS_FULL_STATUS_CHECK_TIME_INTERVAL_MS ) ) { if ( fabs( currentVolumeML - rsrvrFullStatus.prevRsrvrVolumeML[ r ] ) < RSRVRS_STEADY_STATE_MAX_VOL_CHANGE_ML ) { rsrvrFillToFullStableTimeCounter++; } else { rsrvrFillToFullStableTimeCounter = 0; } rsrvrFullStatus.prevRsrvrVolumeML[ r ] = currentVolumeML; rsrvrFullStatus.hasTimeStampBeenSet = FALSE; } if ( rsrvrFillToFullStableTimeCounter > RSRVRS_FILL_TO_FULL_STABLE_TASK_INT ) { status = DG_RESERVOIR_REACHED_TARGET; rsrvrFillToFullStableTimeCounter = 0; // Set the state timer in case it needs to be used for another timeout check stateTimer = getMSTimerCount(); rsrvrFullStatus.prevRsrvrVolumeML[ r ] = 0.0F; // Record the full volumes if ( DG_RESERVOIR_1 == r ) { R1FullVolume = currentVolumeML; } else { // If the reservoir is neither 1 or 2, the current volume will be updated so the code will never get here R2FullVolume = currentVolumeML; } } } 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 { rsrvrFillToFullStableTimeCounter = 0; } 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 ) { // 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 ( DG_RESERVOIR_1 == r ) { actVolume = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); if ( R1FullVolume > 0.0F ) { // 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.0F ) { // 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(); } } /*********************************************************************//** * @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 ) { isChemDisinfectTempAboveTarget = TRUE; ischemDisinfectWarmupTargetReached = TRUE; } else if ( TdiTemp < CHEM_DISINFECT_MINIMUM_TEMPERATURE_C ) { isChemDisinfectTempAboveTarget = FALSE; } if ( TRUE == isChemDisinfectTempAboveTarget ) { chemDisinfectReservoirTime += TASK_GENERAL_INTERVAL; } } /*********************************************************************//** * @brief * The publishChemicalDisinfectData function publishes chemical disinfect * data at the set interval. * @details Inputs: dataPublishCounter * @details Outputs: dataPublishCounter * @return: none *************************************************************************/ static void publishChemicalDisinfectData( void ) { if ( ++dataPublishCounter >= CHEM_DISINFECT_DATA_PUB_INTERVAL ) { MODE_CHEMICAL_DISINFECT_DATA_T data; MODE_CHEMICAL_DISINFECT_UI_DATA_T uiData; data.chemDisinfectState = (U32)chemDisinfectState; data.overallElapsedTime = calcTimeSince( overallChemDisinfectTimer ); data.stateElapsedTime = calcTimeSince( stateTimer ); data.cancellationMode = (U32)cancellationMode; data.acidAvgCondUSPCM = getF32OverrideValue( &acidDataStatus.acidCondAvgUSPCM ); //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 = TARGET_CHEM_DISINFECT_TIME_MS; uiData.chemDisinfectCountdownTime = chemDisinfectReservoirTime; data.R1FillLevel = R1FullVolume; data.R2FillLevel = R2FullVolume; } else { uiData.chemDisinfectCountdownTime = 0.0F; data.R1FillLevel = 0.0F; data.R2FillLevel = 0.0F; } // General data publish channel broadcastData( MSG_ID_DG_CHEM_DISINFECT_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&data, sizeof( MODE_CHEMICAL_DISINFECT_DATA_T ) ); // Publish data to UI broadcastData( MSG_ID_DG_CHEM_DISINFECT_TIME_DATA, COMM_BUFFER_OUT_CAN_DG_2_UI, (U08*)&uiData, sizeof( MODE_CHEMICAL_DISINFECT_UI_DATA_T ) ); dataPublishCounter = 0; } } /*********************************************************************//** * @brief * The monitorModeChemicalDisinfect function monitors the status of the caps and * sets the state of the state machine to water cancellation path if the caps * are not closed during the run. 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, R1FullVolume, isRsrvrLeaking, * rsrvrsVolMonitorCounter, maxTemperatureOutOfRangeStartTimeMS, * maxCondOutOfRangeStartTimeMS * @details Outputs: chemDisinfectState, isRsrvrLeaking, rsrvrsVolMonitorCounter, * alarmDetectedPendingTrigger, prevChemDisinfectState, * maxTemperatureOutOfRangeStartTimeMS, maxCondOutOfRangeStartTimeMS * @return: none *************************************************************************/ static void monitorModeChemicalDisinfect( void ) { BOOL signalMaxTempOrMaxCondIsOutOfRange = FALSE; // Reservoir leak detection. if ( ( DG_CHEM_DISINFECT_STATE_DISINFECT_R1_TO_R2 == chemDisinfectState ) || ( DG_CHEM_DISINFECT_STATE_DISINFECT_R2_TO_R1 == chemDisinfectState ) ) { // Since the reservoir level is only checked if the mode is in R1 to R2 or it is in R2 to R1, it is assumed that the default is R2 to R1 so VectorCAST // can cover the cases F32 loadCellValueML = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); BOOL isRsrvrVolumeOutOfRange = ( fabs( loadCellValueML - R2FullVolume ) > RSRVRS_MAX_LEAK_VOL_CHANGE_ML ? TRUE : FALSE ); if ( DG_CHEM_DISINFECT_STATE_DISINFECT_R1_TO_R2 == chemDisinfectState ) { loadCellValueML = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); isRsrvrVolumeOutOfRange = ( fabs( loadCellValueML - R1FullVolume ) > RSRVRS_MAX_LEAK_VOL_CHANGE_ML ? TRUE : FALSE ); } isRsrvrLeaking = ( TRUE == isRsrvrLeaking ? isRsrvrLeaking : FALSE ); if ( TRUE == isRsrvrVolumeOutOfRange ) { // If the leak is the first time after a while, set the flag and start the timer if ( FALSE == isRsrvrLeaking ) { isRsrvrLeaking = TRUE; rsrvrsVolMonitorCounter = 0; } // If the volume is out of range and it has timed out, set the alarm else if ( ++rsrvrsVolMonitorCounter >= RSRVRS_LEAK_VOL_TIMEOUT_TASK_INT ) { isRsrvrLeaking = FALSE; alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_LEAK_TIMEOUT; chemDisinfectState = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; } } } #ifndef _RELEASE_ if ( ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_CAPS_MONITOR ) != SW_CONFIG_ENABLE_VALUE ) && ( NELSON_NONE == nelsonSupport ) ) #endif { // If the concentrate cap is closed during any state other the start state, alarm. In start state we are still looking for // the user to insert the acid into the concentrate port and hit ok if ( ( ( STATE_CLOSED == getSwitchStatus( CONCENTRATE_CAP ) || ( STATE_OPEN == getSwitchStatus( DIALYSATE_CAP ) ) ) ) && ( chemDisinfectState != DG_CHEM_DISINFECT_STATE_START ) ) { prevChemDisinfectState = chemDisinfectState; chemDisinfectState = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; if ( STATE_CLOSED == getSwitchStatus( CONCENTRATE_CAP ) ) { alarmDetectedPendingTrigger = ALARM_ID_DG_CONCENTRATE_CAP_NOT_IN_PROPER_POSITION; } if ( STATE_OPEN == getSwitchStatus( DIALYSATE_CAP ) ) { alarmDetectedPendingTrigger = ALARM_ID_DG_DIALYSATE_CAP_NOT_IN_PROPER_POSITION; } } } // Check the temperature and conductivity of the diluted disinfectant if ( ( chemDisinfectState >= DG_CHEM_DISINFECT_STATE_FILL_WITH_DISINFECTANT ) && ( chemDisinfectState <= DG_CHEM_DISINFECT_STATE_PARTIAL_DRAIN_R2_FILL_R1_TO_R2 ) ) { // Disinfect conditions check F32 TPoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); BOOL isCD2OutofRange = isAcidCondOutOfRange(); BOOL isTPoOutofRange = ( TPoTemp > MAX_DISINFECT_TPO_TEMPERATURE_C ? TRUE : FALSE ); maxTemperatureOutOfRangeStartTimeMS = ( TRUE == isTPoOutofRange ? maxTemperatureOutOfRangeStartTimeMS : getMSTimerCount() ); maxCondOutOfRangeStartTimeMS = ( TRUE == isCD2OutofRange ? maxCondOutOfRangeStartTimeMS : getMSTimerCount() ); if ( TRUE == getTestConfigStatus( TEST_CONFIG_MIX_WITH_WATER ) ) { // If the mix with water is enabled, keep reseting the time maxCondOutOfRangeStartTimeMS = getMSTimerCount(); } if ( TRUE == didTimeout( maxTemperatureOutOfRangeStartTimeMS, DISINFECT_TEMP_OUT_OF_RANGE_TIMEOUT_MS ) ) { signalMaxTempOrMaxCondIsOutOfRange = TRUE; alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_TARGET_TEMP_OUT_OF_RANGE; } if ( ( TRUE == didTimeout( maxCondOutOfRangeStartTimeMS, DISINFECT_COND_OUT_OF_RANGE_TIMEOUT_MS ) ) && ( getTestConfigStatus( TEST_CONFIG_MIX_WITH_WATER ) != TRUE ) ) { signalMaxTempOrMaxCondIsOutOfRange = TRUE; alarmDetectedPendingTrigger = ALARM_ID_DG_CHEM_DISINFECT_TARGET_COND_OUT_OF_RANGE; } } if ( ( TRUE == isDGFaultAlarmActive() ) || ( TRUE == isAnyCleaningModeInletWaterConditionActive() ) || ( TRUE == signalMaxTempOrMaxCondIsOutOfRange ) ) { if ( chemDisinfectState != DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH ) { prevChemDisinfectState = chemDisinfectState; chemDisinfectState = DG_CHEM_DISINFECT_STATE_CANCEL_WATER_PATH; } } } /*********************************************************************//** * @brief * The writeDisinfectDataToNV function writes the disinfection data to the * non-volatile memory. * @details Inputs: disinfectNVOps * @details Outputs: disinfectNVOps * @param info the type disinfect data to write to the memory (i.e. heat * disinfect start time) * @return: none *************************************************************************/ static void writeDisinfectDataToNV( DG_USAGE_INFO_ITEMS_T info ) { if ( FALSE == disinfectNVOps.hasDisStatusBeenWrittenToNV ) { disinfectNVOps.hasDisStatusBeenWrittenToNV = setLastDisinfectDate( info, getRTCTimestamp() ); } } /*********************************************************************//** * @brief * The processAcidConductivityData function processes the moving average * of the acid conductivity values. * @details Inputs: acidDataStatus * @details Outputs: acidDataStatus * @return: none *************************************************************************/ static void processAcidConductivityData( void ) { if ( FALSE == acidDataStatus.acidDataColHasTimerBeenSet ) { acidDataStatus.acidDataColStartTimeMS = getMSTimerCount(); acidDataStatus.acidDataColHasTimerBeenSet = TRUE; } else if ( TRUE == didTimeout( acidDataStatus.acidDataColStartTimeMS, ACID_DATA_COLLECTION_TIME_MS ) ) { F32 acidCondUSPCM = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); U32 currentIndex = acidDataStatus.acidCondSamplesNextIndex; F32 prevSampleToRemoveUSPCM = acidDataStatus.acidCondSamplesUSPCM[ currentIndex ]; acidDataStatus.acidDataColStartTimeMS = getMSTimerCount(); acidDataStatus.acidDataColHasTimerBeenSet = TRUE; acidDataStatus.acidCondSamplesUSPCM[ currentIndex ] = acidCondUSPCM; acidDataStatus.acidCondRunningSumUSPCM = acidDataStatus.acidCondRunningSumUSPCM + acidCondUSPCM - prevSampleToRemoveUSPCM; acidDataStatus.acidCondSamplesNextIndex = INC_WRAP( acidDataStatus.acidCondSamplesNextIndex, 0, ACID_MOVING_AVG_NUM_OF_SAMPLES - 1 ); acidDataStatus.acidCondAvgUSPCM.data = acidDataStatus.acidCondRunningSumUSPCM / (F32)ACID_MOVING_AVG_NUM_OF_SAMPLES; } } /*********************************************************************//** * @brief * The isAcidCondOutOfRange function checks whether the acid conductivity * is out of range or not. * @details Inputs: acidDataStatus * @details Outputs: acidDataStatus * @return: TRUE is acid conductivity is out of range, otherwise, FALSE *************************************************************************/ static BOOL isAcidCondOutOfRange( void ) { F32 acidCondUSPCM = getF32OverrideValue( &acidDataStatus.acidCondAvgUSPCM ); BOOL isCD2OutOfRange = ( ( acidCondUSPCM < MIN_DISINFECT_CONDUCTIVITY_US_PER_CM || acidCondUSPCM > MAX_DISINFECT_CONDUCTIVITY_US_PER_CM ) ? TRUE : FALSE ); return isCD2OutOfRange; } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetChemDisinfectionCD2AvgOverride function overrides the * CD2 acid value in chemical disinfect. * @details Inputs: none * @details Outputs: acidDataStatus.acidCondAvgUSPCM * @param value override concentrate pump data publish interval with (in ms) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetChemDisinfectionCD2AvgOverride( F32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; acidDataStatus.acidCondAvgUSPCM.ovData = value; acidDataStatus.acidCondAvgUSPCM.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetConcentratePumpDataPublishIntervalOverride function resets the * override of the CD2 acid value in chemical disinfect. * @details Inputs: none * @details Outputs: acidDataStatus.acidCondAvgUSPCM * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetChemDisinfectionCD2AvgOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; acidDataStatus.acidCondAvgUSPCM.override = OVERRIDE_RESET; acidDataStatus.acidCondAvgUSPCM.ovData = acidDataStatus.acidCondAvgUSPCM.ovInitData; } return result; } /**@}*/ // ********** Nelson Support Functions ********** #ifndef _RELEASE_ /*********************************************************************//** * @brief * The setNelsonSupportMode function sets the requested Nelson support * mode (i.e. inoculate, ...) * @details Inputs: none * @details Outputs: nelsonSupport * @param support the type Nelson support (i.e. inoculate, heat disinfect) * @return none *************************************************************************/ void setChemNelsonSupportMode( NELSON_SUPPORT_T support ) { nelsonSupport = support; } #endif #ifndef _RELEASE_ /*********************************************************************//** * @brief * The setNelsonSupportConditions function sets the disinfect variables for * Nelson support. * @details Inputs: nelsonSupport * @details Outputs: chemDisinfectState, flushCircWaitTime, stateTimer * @return: none *************************************************************************/ static void setNelsonSupportConditions( void ) { switch ( nelsonSupport ) { case NELSON_CHEM_DISINFECT: // Set the valves to start flush circulation setValveState( VPI, VALVE_STATE_OPEN ); setValveState( VBF, VALVE_STATE_CLOSED ); setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VRC, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); setValveState( VRD1, VALVE_STATE_CLOSED ); setValveState( VRD2, VALVE_STATE_CLOSED ); setROPumpTargetFlowRateLPM( RO_PUMP_TARGET_FLUSH_FLOW_RATE_LPM, MAX_RO_PUMP_FLUSH_FILL_PRESSURE_PSI ); setHeaterTargetTemperature( DG_PRIMARY_HEATER, CHEM_DISINFECT_HEATER_CONTROL_TEMPERATURE_C ); startHeater( DG_PRIMARY_HEATER ); turnOnUVReactor( INLET_UV_REACTOR ); turnOnUVReactor( OUTLET_UV_REACTOR ); flushCircWaitTime = FLUSH_CICRCULATION_INITIAL_WAIT_TIME_MS; stateTimer = getMSTimerCount(); chemDisinfectState = DG_CHEM_DISINFECT_STATE_FLUSH_CIRCULATION; break; case NELSON_NONE: default: // Do nothing for these cases. break; } } #endif