Index: firmware/App/Controllers/Pressures.c =================================================================== diff -u -rc6f6a1a8aaddcdd0a796003f8b0c804e79359d68 -receb190a5d66fdbee779478ac8bb50a846ed9241 --- firmware/App/Controllers/Pressures.c (.../Pressures.c) (revision c6f6a1a8aaddcdd0a796003f8b0c804e79359d68) +++ firmware/App/Controllers/Pressures.c (.../Pressures.c) (revision eceb190a5d66fdbee779478ac8bb50a846ed9241) @@ -29,7 +29,8 @@ #include "TaskPriority.h" #include "TemperatureSensors.h" #include "Timers.h" -#include "Utilities.h" +#include "Utilities.h" +#include "Valves.h" /** * @addtogroup DGPressures @@ -55,8 +56,12 @@ #define MIN_VALID_BARO_PRESSURE_PSIA 10.1F ///< Minimum valid barometric pressure in psia. #define MAX_VALID_BARO_PRESSURE_PSIA 15.4F ///< Maximum valid barometric pressure in psia. -#define MIN_INLET_WATER_PRESSURE_WARNING_LOW 14.0F ///< Minimum allowed low pressure value. +#define MAX_INLET_WATER_PRESSURE_WARNING_LOW_PSIG 14.0F ///< Maximum allowed low pressure value in psig. +#define MIN_INLET_WATER_PRESSURE_WARNING_LOW_PSIG 30.0F ///< Minimum allowed low pressure value in psig. +#define MAX_INLET_WATER_PRESSURE_WARNING_HIGH_PSIG 80.0F ///< Maximum allowed high pressure value in psig. +#define MIN_INLET_WATER_PRESSURE_WARNING_HIGH_PSIG 78.0F ///< Minimum allowed high pressure value in psig. + #define INLET_WATER_PRESSURE_PERSISTENCE_PERIOD ( 5 * MS_PER_SECOND ) ///< Persistence period for pressure out of range error. #define PRESSURE_OUT_OF_RANGE_TIMEOUT_MS ( 5 * MS_PER_SECOND ) ///< Pressure out of range persistence period in milliseconds. #define PRESSURES_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Interval (ms/task time) at which the pressures data is published on the CAN bus. @@ -146,8 +151,11 @@ } initPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_IN_LOW_RANGE, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD ); + initPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_IN_HIGH_RANGE, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD ); initPersistentAlarm( ALARM_ID_DG_PRESSURE_OUT_OF_RANGE, PRESSURE_OUT_OF_RANGE_TIMEOUT_MS, PRESSURE_OUT_OF_RANGE_TIMEOUT_MS ); initPersistentAlarm( ALARM_ID_DG_BARO_PRESSURE_OUT_OF_RANGE, PRESSURE_OUT_OF_RANGE_TIMEOUT_MS, PRESSURE_OUT_OF_RANGE_TIMEOUT_MS ); + initPersistentAlarm( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_PRESSURE_TOO_HIGH, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD ); + initPersistentAlarm( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_PRESSURE_TOO_LOW, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD, INLET_WATER_PRESSURE_PERSISTENCE_PERIOD ); } /*********************************************************************//** @@ -164,18 +172,43 @@ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_WATER_QUALITY_CHECK ) != SW_CONFIG_ENABLE_VALUE ) #endif { - F32 pressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); - BOOL isPressureTooLow = ( pressure < MIN_INLET_WATER_PRESSURE_WARNING_LOW ? TRUE : FALSE ); + DG_OP_MODE_T opMode = getCurrentOperationMode(); + F32 pressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); + BOOL isPressureTooLow = ( pressure < MAX_INLET_WATER_PRESSURE_WARNING_LOW_PSIG ? TRUE : FALSE ); + BOOL isPressureTooHigh = ( pressure > MAX_INLET_WATER_PRESSURE_WARNING_HIGH_PSIG ? TRUE : FALSE ); - // Alarm per PRS 401 - if ( TRUE == isPressureTooLow ) + switch( opMode ) { - checkPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_IN_LOW_RANGE, isPressureTooLow, pressure, MIN_INLET_WATER_PRESSURE_WARNING_LOW ); + case DG_MODE_GENE: + case DG_MODE_FILL: + case DG_MODE_DRAI: + isPressureTooHigh = ( pressure <= MIN_INLET_WATER_PRESSURE_WARNING_HIGH_PSIG ? FALSE : TRUE ); + isPressureTooLow = ( pressure >= MIN_INLET_WATER_PRESSURE_WARNING_LOW_PSIG ? FALSE : TRUE ); + // Per PRS 841 + checkPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_IN_HIGH_RANGE, isPressureTooHigh, pressure, MAX_INLET_WATER_PRESSURE_WARNING_HIGH_PSIG ); + // Per PRS 401 + checkPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_IN_LOW_RANGE, isPressureTooLow, pressure, MAX_INLET_WATER_PRESSURE_WARNING_LOW_PSIG ); + break; + + // TODO add chem flush and heat disinfect cooling modes once they are created + case DG_MODE_INIT: + case DG_MODE_STAN: + case DG_MODE_FLUS: + case DG_MODE_HEAT: + case DG_MODE_CHEM: + if ( VALVE_STATE_OPEN == getValveStateName( VPI ) ) + { + // TODO define the PRS + checkPersistentAlarm( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_PRESSURE_TOO_HIGH, isPressureTooHigh, pressure, MIN_INLET_WATER_PRESSURE_WARNING_HIGH_PSIG ); + // TODO define the PRS + checkPersistentAlarm( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_PRESSURE_TOO_LOW, isPressureTooLow, pressure, MIN_INLET_WATER_PRESSURE_WARNING_LOW_PSIG ); + } + break; + + default: + // NOTE: Do nothing for the rest of the modes + break; } - else - { - checkPersistentAlarm( ALARM_ID_INLET_WATER_PRESSURE_IN_LOW_RANGE, FALSE, pressure, MIN_INLET_WATER_PRESSURE_WARNING_LOW ); - } } } @@ -557,7 +590,7 @@ } // Once the sensors were all checked to be out of range, check the persistent alarm - //checkPersistentAlarm( ALARM_ID_DG_PRESSURE_OUT_OF_RANGE, isPressureOutOfRange, pressureOutID, pressureReading ); + checkPersistentAlarm( ALARM_ID_DG_PRESSURE_OUT_OF_RANGE, isPressureOutOfRange, pressureOutID, pressureReading ); } Index: firmware/App/Controllers/ROPump.c =================================================================== diff -u -reea0132d751dfd21ef8ae0212ebed184fb2528ff -receb190a5d66fdbee779478ac8bb50a846ed9241 --- firmware/App/Controllers/ROPump.c (.../ROPump.c) (revision eea0132d751dfd21ef8ae0212ebed184fb2528ff) +++ firmware/App/Controllers/ROPump.c (.../ROPump.c) (revision eceb190a5d66fdbee779478ac8bb50a846ed9241) @@ -7,8 +7,8 @@ * * @file ROPump.c * -* @author (last) Dara Navaei -* @date (last) 03-Nov-2022 +* @author (last) Michael Garthwaite +* @date (last) 21-Nov-2022 * * @author (original) Sean * @date (original) 04-Apr-2020 @@ -58,7 +58,7 @@ #define ROP_FLOW_CONTROL_I_COEFFICIENT 0.65F ///< I term for RO pump flow control. #define ROP_MAX_PRESSURE_P_COEFFICIENT 0.01F ///< P term for RO pump max pressure control. #define ROP_MAX_PRESSURE_I_COEFFICIENT 0.01F ///< I term for RO pump max pressure control. - +#define ROP_PWM_STEP_LIMIT 0.50F ///< Current maximum PWM step limit used in RO Profiles. #define ROP_FLOW_TARGET_TOLERANCE 0.03F ///< Tolerance in between the target flow rate and the actual flow rate in percentage. #define ROP_RAMP_DOWN_DUTY_CYCLE_RATIO 0.03F ///< Pump ramp down duty cycle ratio when the pressure higher than max defined. @@ -71,7 +71,7 @@ // 110000 pulses/liter // For 2 LPM => 2LPM x 110000 pulses/liter * 1 edges/pulse * 1 min/60 seconds = 3666.66 counts/sec => 272.72 microseconds => for 1 LPM = 136.36 counts #define RO_FLOW_ADC_TO_LPM_FACTOR 272.72F ///< Conversion factor from ADC counts to LPM (liters/min) for RO flow rate (multiply this by inverse of FPGA reading). -#define ROP_FLOW_TO_PWM_SLOPE 0.1F ///< Slope of flow to PWM line equation. +#define ROP_FLOW_TO_PWM_SLOPE 0.5F ///< Slope of flow to PWM line equation. #define ROP_FLOW_TO_PWM_INTERCEPT 0.0F ///< Intercept of flow to PWM line equation. /// Initial conversion factor from target flow rate to PWM duty cycle estimate. @@ -118,7 +118,7 @@ static BOOL isROPumpOn; ///< RO pump is currently running. static F32 roPumpPWMDutyCyclePct; ///< Initial RO pump PWM duty cycle. static F32 roPumpDutyCyclePctSet; ///< Currently set RO pump PWM duty cycle. -static F32 roPumpFeedbackDutyCyclePct; ///< RO pump feedback duty cycle in percent. +static OVERRIDE_F32_T roPumpFeedbackDutyCyclePct = { 0, 0, 0, 0 }; ///< RO pump feedback duty cycle in percent. static PUMP_CONTROL_MODE_T roPumpControlMode; ///< Requested RO pump control mode. static F32 pendingROPumpCmdMaxPressure; ///< Delayed (pending) RO pump max pressure (in PSI) setting. static F32 pendingROPumpCmdTargetFlow; ///< Delayed (pending) RO pump target flow rate (in mL/min) setting. @@ -130,7 +130,22 @@ static U32 roControlTimerCounter; ///< Determines when to perform control on RO pump. static F32 roPumpOpenLoopTargetDutyCycle; ///< Target RO pump open loop PWM. static F32 roVolumeL; ///< RO water generated in liters. +static U32 roPumpControlInterval; ///< RO pump Control interval. +static RO_PI_FLOW_PROFILES_T currentROPumpProfile; +///< RO Pump flow profile table. +///< Most values are currently the same until future efforts into tuning op modes. +///< TODO: Fine tune each op mode. +static PI_CONTROLLER_PROFILE_DATA_T roPIFlowProfiles[ NUM_OF_RO_PI_FLOW_PROFILES ] = +{ // Kp Ki uMin uMax maxErrorSumStep Control Interval + { ROP_FLOW_CONTROL_P_COEFFICIENT, ROP_FLOW_CONTROL_I_COEFFICIENT, MIN_RO_PUMP_DUTY_CYCLE, MAX_RO_PUMP_DUTY_CYCLE, ROP_PWM_STEP_LIMIT, ROP_CONTROL_INTERVAL }, ///< RO_PI_FLOW_PROFILE_FLUSH + { ROP_FLOW_CONTROL_P_COEFFICIENT, ROP_FLOW_CONTROL_I_COEFFICIENT, MIN_RO_PUMP_DUTY_CYCLE, MAX_RO_PUMP_DUTY_CYCLE, ROP_PWM_STEP_LIMIT, ROP_CONTROL_INTERVAL }, ///< RO_PI_FLOW_PROFILE_GEN_IDLE + { ROP_FLOW_CONTROL_P_COEFFICIENT, ROP_FLOW_CONTROL_I_COEFFICIENT, MIN_RO_PUMP_DUTY_CYCLE, MAX_RO_PUMP_DUTY_CYCLE, ROP_PWM_STEP_LIMIT, ROP_CONTROL_INTERVAL }, ///< RO_PI_FLOW_PROFILE_FILL + { ROP_FLOW_CONTROL_P_COEFFICIENT, ROP_FLOW_CONTROL_I_COEFFICIENT, MIN_RO_PUMP_DUTY_CYCLE, MAX_RO_PUMP_DUTY_CYCLE, ROP_PWM_STEP_LIMIT, ROP_CONTROL_INTERVAL }, ///< RO_PI_FLOW_PROFILE_DRAIN + { ROP_FLOW_CONTROL_P_COEFFICIENT, ROP_FLOW_CONTROL_I_COEFFICIENT, MIN_RO_PUMP_DUTY_CYCLE, MAX_RO_PUMP_DUTY_CYCLE, ROP_PWM_STEP_LIMIT, ROP_CONTROL_INTERVAL }, ///< RO_PI_FLOW_PROFILE_HEAT + { ROP_FLOW_CONTROL_P_COEFFICIENT, ROP_FLOW_CONTROL_I_COEFFICIENT, MIN_RO_PUMP_DUTY_CYCLE, MAX_RO_PUMP_DUTY_CYCLE, ROP_PWM_STEP_LIMIT, ROP_CONTROL_INTERVAL }, ///< RO_PI_FLOW_PROFILE_OPEN_LOOP +};// Kp Ki uMin uMax maxErrorSumStep Control Interval + // ********** private function prototypes ********** static RO_PUMP_STATE_T handleROPumpOffState( void ); @@ -139,10 +154,12 @@ static RO_PUMP_STATE_T handleROPumpControlToMaxPressureState( void ); static RO_PUMP_STATE_T handleROPumpOpenLoopState( void ); +static F32 roPumpFlowToPWM( RO_PI_FLOW_PROFILES_T profileID, F32 targetFlow ); static void setROPumpTargetDutyCycle( F32 duty ); static void setROPumpControlSignalDutyCycle( F32 dutyCycle ); static void stopROPump( void ); static void publishROPumpData( void ); +static F32 getROFeedbackDutyCycle( void ); /*********************************************************************//** * @brief @@ -185,7 +202,7 @@ roPumpState = RO_PUMP_OFF_STATE; roPumpControlMode = NUM_OF_PUMP_CONTROL_MODES; isROPumpOn = FALSE; - roPumpFeedbackDutyCyclePct = 0.0F; + roPumpFeedbackDutyCyclePct.data = 0.0F; roVolumeL = 0.0F; targetROPumpFlowRateLPM = 0.0F; roPumpPWMDutyCyclePct = 0.0F; @@ -194,16 +211,18 @@ pendingROPumpCmdTargetFlow = 0.0F; pendingROPumpCmdCountDown = 0; targetROPumpMaxPressure = 0.0F; + roPumpControlInterval = ROP_CONTROL_INTERVAL; + currentROPumpProfile = RO_PI_FLOW_PROFILE_GEN_IDLE; } /*********************************************************************//** * @brief * The setROPumpTargetFlowRate function sets a new target flow rate for the * RO pump. * @details Inputs: targetROPumpPressure, targetROPumpFlowRate, - * roPumpControlMode, rampUp2FlowTimeoutCounter + * roPumpControlMode, rampUp2FlowTimeoutCounter, currentROPumpProfile * @details Outputs: targetROPumpPressure, targetROPumpFlowRate, - * roPumpControlMode, rampUp2FlowTimeoutCounter + * roPumpControlMode, rampUp2FlowTimeoutCounter, roPumpPWMDutyCyclePct * @param roFlowRate which is target RO flow rate * @param maxPressure which is the maximum allowed pressure that the RO pump * can reach @@ -224,9 +243,8 @@ targetROPumpMaxPressure = maxPressure; targetROPumpFlowRateLPM = roFlowRate; roPumpControlMode = PUMP_CONTROL_MODE_CLOSED_LOOP; - roPumpState = RO_PUMP_RAMP_UP_TO_TARGET_FLOW_STATE; // Get the initial guess of the duty cycle - roPumpPWMDutyCyclePct = ROP_FLOW_TO_PWM_DC( roFlowRate ); + roPumpPWMDutyCyclePct = roPumpFlowToPWM( currentROPumpProfile, targetROPumpFlowRateLPM ); roControlTimerCounter = 0; isROPumpOn = TRUE; result = TRUE; @@ -322,10 +340,10 @@ * The execROPumpMonitor function executes the RO pump monitor. The RO flow * sensor is read, filtered, converted to L/min and calibrated. * @details Inputs: measuredFlowReadingsSum, flowFilterCounter, - * measuredROFlowRateLPM, measuredROFlowRateLPM, roPumpState, - * flowOutOfRangeCounter, roPumpControlMode + * measuredROFlowRateLPM, roPumpState, roPumpFeedbackDutyCyclePct, + * flowOutOfRangeCounter, roPumpControlMode, Ppo * @details Outputs: measuredFlowReadingsSum, flowFilterCounter, - * measuredROFlowRateLPM, measuredROFlowRateLPM + * measuredROFlowRateLPM, roVolumeL, roPumpFeedbackDutyCyclePct * @return none *************************************************************************/ void execROPumpMonitor( void ) @@ -379,10 +397,10 @@ { // The feedback voltage is on the 0V line so when the duty cycle is 0, the feedback is 2.5V // The duty cycle is calculated by getting the 1 - (ratio of feedback / to the voltage at 0 percent duty cycle). - roPumpFeedbackDutyCyclePct = 1.0F - ( roFeedbackVoltage / ROP_FEEDBACK_0_PCT_DUTY_CYCLE_VOLTAGE ); - isDutyCylceOutOfRange = ( fabs( roPumpFeedbackDutyCyclePct - roPumpDutyCyclePctSet ) > ROP_DUTY_CYCLE_OUT_OF_RANGE_TOLERANCE ? TRUE : FALSE ); + roPumpFeedbackDutyCyclePct.data = 1.0F - ( roFeedbackVoltage / ROP_FEEDBACK_0_PCT_DUTY_CYCLE_VOLTAGE ); + isDutyCylceOutOfRange = ( fabs( getROFeedbackDutyCycle() - roPumpDutyCyclePctSet ) > ROP_DUTY_CYCLE_OUT_OF_RANGE_TOLERANCE ? TRUE : FALSE ); - checkPersistentAlarm( ALARM_ID_RO_PUMP_DUTY_CYCLE_OUT_OF_RANGE, isDutyCylceOutOfRange, roPumpFeedbackDutyCyclePct, roPumpDutyCyclePctSet ); + checkPersistentAlarm( ALARM_ID_RO_PUMP_DUTY_CYCLE_OUT_OF_RANGE, isDutyCylceOutOfRange, getROFeedbackDutyCycle(), roPumpDutyCyclePctSet ); // Check if it the alarm has timed out and if the pump is supposed to be off but it is still on, activate the safety shutdown if ( ( TRUE == isAlarmActive( ALARM_ID_RO_PUMP_DUTY_CYCLE_OUT_OF_RANGE ) ) && ( FALSE == isROPumpOn ) ) @@ -416,9 +434,8 @@ pendingROPumpCmdMaxPressure = 0.0F; pendingROPumpCmdTargetFlow = 0.0F; roPumpControlMode = PUMP_CONTROL_MODE_CLOSED_LOOP; - roPumpState = RO_PUMP_RAMP_UP_TO_TARGET_FLOW_STATE; // Get the initial guess of the duty cycle - roPumpPWMDutyCyclePct = ROP_FLOW_TO_PWM_DC( targetROPumpFlowRateLPM ); + roPumpPWMDutyCyclePct = roPumpFlowToPWM( currentROPumpProfile, targetROPumpFlowRateLPM ); roControlTimerCounter = 0; isROPumpOn = TRUE; } @@ -523,6 +540,21 @@ /*********************************************************************//** * @brief + * The getROFeedbackDutyCyle function returns the RO pump feedback + * duty cycle. + * @details Inputs: roPumpFeedbackDutyCyclePct + * @details Outputs: none + * @return ro pump feedback duty cycle + *************************************************************************/ +static F32 getROFeedbackDutyCycle( void ) +{ + F32 feedbackdutyCycle = getF32OverrideValue( &roPumpFeedbackDutyCyclePct ); + + return feedbackdutyCycle; +} + +/*********************************************************************//** + * @brief * The resetROGenerateVolumeL function resets the RO generated volume in liters. * @details Inputs: none * @details Outputs: roVolumeL @@ -547,17 +579,17 @@ RO_PUMP_STATE_T state = RO_PUMP_OFF_STATE; // If there is a flow, transition to the PI controller to get the corresponding pressure of that flow - if ( getTargetROPumpFlowRateLPM() > 0.0F && roPumpControlMode == PUMP_CONTROL_MODE_CLOSED_LOOP ) + if ( ( getTargetROPumpFlowRateLPM() > 0.0F ) && ( PUMP_CONTROL_MODE_CLOSED_LOOP == roPumpControlMode ) ) { // Set pump to on isROPumpOn = TRUE; - roPumpDutyCyclePctSet = ROP_FLOW_TO_PWM_DC( getTargetROPumpFlowRateLPM() ); + roPumpDutyCyclePctSet = roPumpFlowToPWM( currentROPumpProfile, getTargetROPumpFlowRateLPM() ); setROPumpControlSignalDutyCycle( roPumpDutyCyclePctSet ); - state = RO_PUMP_RAMP_UP_TO_TARGET_FLOW_STATE; + state = RO_PUMP_CONTROL_TO_TARGET_FLOW_STATE; } // If the target duty cycle is greater than zero (minimum is 10%) and the mode has been set to open // loop, set the duty cycle - if ( roPumpOpenLoopTargetDutyCycle > 0.0F && roPumpControlMode == PUMP_CONTROL_MODE_OPEN_LOOP ) + if ( ( roPumpOpenLoopTargetDutyCycle > 0.0F ) && ( PUMP_CONTROL_MODE_OPEN_LOOP == roPumpControlMode ) ) { setROPumpControlSignalDutyCycle( roPumpOpenLoopTargetDutyCycle ); isROPumpOn = TRUE; @@ -571,55 +603,16 @@ * @brief * The handleROPumpRampUpToTargetFlowState function handles the RO pump * ramp up to flow state of the controller state machine. - * @details Inputs: roControlTimerCounter, roPumpPWMDutyCyclePctSet, - * rampUp2FlowTimeoutCounter - * @details Outputs: roControlTimerCounter, roPumpPWMDutyCyclePctSet, - * rampUp2FlowTimeoutCounter + * @details Inputs: none + * @details Outputs: none * @return next state of the controller state machine *************************************************************************/ static RO_PUMP_STATE_T handleROPumpRampUpToTargetFlowState( void ) { - RO_PUMP_STATE_T state = RO_PUMP_RAMP_UP_TO_TARGET_FLOW_STATE; + RO_PUMP_STATE_T state = RO_PUMP_CONTROL_TO_TARGET_FLOW_STATE; - // Get the current pressure from the sensor - F32 actualPressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_OUTLET ); - F32 targetFlowRate = getTargetROPumpFlowRateLPM(); - F32 actualFlowRate = getMeasuredFlowRateLPM( RO_FLOW_SENSOR ); - F32 flowRateDeviation = fabs( targetFlowRate - actualFlowRate ) / targetFlowRate; - BOOL isFlowOutOfRange = flowRateDeviation > ROP_FLOW_TARGET_TOLERANCE; - F32 targetPressure = getTargetROPumpPressurePSI(); + // TODO - remove function and state - obsolete - if ( ++roControlTimerCounter >= ROP_RAMP_UP_CONTROL_INTERVAL ) - { - // If the actual pressure is greater than the target pressure or it is within the tolerance of the maximum pressure, move to set - // to target pressure straight. At the beginning the maximum pressure is set in the targetROPumpPressure override variable. - // If the flow rate was reached without reaching to maximum pressure, the pressure that was set to targetROPumpPressure override will - // be reset to the corresponding pressure of the target flow rate. - if ( ( actualPressure > targetPressure ) || ( ( targetPressure - actualPressure ) < MAX_PRESSURE_TARGET_TOLERANCE ) ) - { - resetPIController( PI_CONTROLLER_ID_RO_PUMP_MAX_PRES, roPumpDutyCyclePctSet ); - state = RO_PUMP_CONTROL_TO_MAX_PRESSURE_STATE; - } - // If the actual flow is still far from target flow, update the duty cycle using the I controller and stay in this state - else if ( TRUE == isFlowOutOfRange ) - { - roPumpDutyCyclePctSet += ( targetFlowRate - actualFlowRate ) * ROP_RAMP_UP_P_COEFFICIENT; - // If the duty cycle is out of the define range for the RO pump, set the boundaries - roPumpDutyCyclePctSet = roPumpDutyCyclePctSet < MIN_RO_PUMP_DUTY_CYCLE ? MIN_RO_PUMP_DUTY_CYCLE : roPumpDutyCyclePctSet; - roPumpDutyCyclePctSet = roPumpDutyCyclePctSet > MAX_RO_PUMP_DUTY_CYCLE ? MAX_RO_PUMP_DUTY_CYCLE : roPumpDutyCyclePctSet; - - setROPumpControlSignalDutyCycle( roPumpDutyCyclePctSet ); - } - // Reached to the target flow go to the next state - else - { - resetPIController( PI_CONTROLLER_ID_RO_PUMP_FLOW, roPumpDutyCyclePctSet ); - state = RO_PUMP_CONTROL_TO_TARGET_FLOW_STATE; - } - - roControlTimerCounter = 0; - } - return state; } @@ -636,7 +629,7 @@ RO_PUMP_STATE_T state = RO_PUMP_CONTROL_TO_TARGET_FLOW_STATE; // Control at set interval - if ( ++roControlTimerCounter >= ROP_CONTROL_INTERVAL && roPumpControlMode == PUMP_CONTROL_MODE_CLOSED_LOOP ) + if ( ( ++roControlTimerCounter >= roPumpControlInterval ) && ( PUMP_CONTROL_MODE_CLOSED_LOOP == roPumpControlMode ) ) { // Get the pressure to use it for setting the control F32 actualPressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_OUTLET ); @@ -787,14 +780,89 @@ pumpData.roPumpDutyCycle = roPumpDutyCyclePctSet * FRACTION_TO_PERCENT_FACTOR; pumpData.roMeasFlowRateLPM = getMeasuredFlowRateLPM( RO_FLOW_SENSOR ); pumpData.roPumpState = (U32)roPumpState; - pumpData.roPumpFBDutyCycle = roPumpFeedbackDutyCyclePct * FRACTION_TO_PERCENT_FACTOR; + pumpData.roPumpFBDutyCycle = getROFeedbackDutyCycle() * FRACTION_TO_PERCENT_FACTOR; broadcastData( MSG_ID_RO_PUMP_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&pumpData, sizeof( RO_PUMP_DATA_T ) ); roPumpDataPublicationTimerCounter = 0; } } +/*********************************************************************//** + * @brief + * The setROPIFlowProfile function sets the RO flow PI controller to new coefficients + * and calculates the initial duty cycle. + * @details Inputs: targetROPumpFlowRateLPM + * @details Outputs: roPumpControlInterval, currentROPumpProfile + * @param profileID the ID for which flow profile to be used + * @return none + *************************************************************************/ +void setROPIFlowProfile( RO_PI_FLOW_PROFILES_T profileID ) +{ + F32 initialControlDutyCycle; + if ( profileID < NUM_OF_RO_PI_FLOW_PROFILES ) + { + roPumpControlInterval = roPIFlowProfiles[ profileID ].controlInterval; + initialControlDutyCycle = roPumpFlowToPWM( profileID, (getTargetROPumpFlowRateLPM()) ); + + initializePIController( PI_CONTROLLER_ID_RO_PUMP_FLOW, initialControlDutyCycle, + roPIFlowProfiles[ profileID ].Kp, roPIFlowProfiles[ profileID ].Ki, + roPIFlowProfiles[ profileID ].uMin, roPIFlowProfiles[ profileID ].uMax ); + + setPIControllerStepLimit( PI_CONTROLLER_ID_RO_PUMP_FLOW, roPIFlowProfiles[ profileID ].maxErrorSumStep ); + currentROPumpProfile = profileID; + } + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_PI_PROFILE_SELECTED, profileID ) + } +} + +/*********************************************************************//** + * @brief + * The roPumpFlowToPWM function calculates the duty cycle for the given target + * flow in relation to which PI profile has been selected. + * @details Inputs: none + * @details Outputs: dutyCyclePct + * @param profileID the ID for which flow profile to be used + * targetFlow the flow as a parameter for the conversion calculation + * @return dutyCyclePct, the duty cycle for the given flow + *************************************************************************/ +static F32 roPumpFlowToPWM( RO_PI_FLOW_PROFILES_T profileID, F32 targetFlow ) +{ + F32 dutyCyclePct = 0; + if ( profileID < NUM_OF_RO_PI_FLOW_PROFILES ) + { + switch( profileID ) + { + case RO_PI_FLOW_PROFILE_FLUSH: + dutyCyclePct = ROP_FLOW_TO_PWM_DC( targetFlow ); + break; + case RO_PI_FLOW_PROFILE_GEN_IDLE: + dutyCyclePct = ROP_FLOW_TO_PWM_DC( targetFlow ); + break; + case RO_PI_FLOW_PROFILE_FILL: + dutyCyclePct = ROP_FLOW_TO_PWM_DC( targetFlow ); + break; + case RO_PI_FLOW_PROFILE_DRAIN: + dutyCyclePct = ROP_FLOW_TO_PWM_DC( targetFlow ); + break; + case RO_PI_FLOW_PROFILE_HEAT: + dutyCyclePct = ROP_FLOW_TO_PWM_DC( targetFlow ); + break; + default: + dutyCyclePct = ROP_FLOW_TO_PWM_DC( targetFlow ); + break; + } + } + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_PI_PROFILE_SELECTED, profileID ) + } + return dutyCyclePct; +} + + /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ @@ -930,4 +998,55 @@ return result; } +/*********************************************************************//** + * @brief + * The testSetROPumpMeasuredFeedbackDutyCycleOverride function overrides the + * feedback duty cycle. + * @details Inputs: none + * @details Outputs: roPumpFeedbackDutyCyclePct + * @param value override feedback duty cycle. + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetROPumpMeasuredFeedbackDutyCycleOverride( F32 value ) +{ + BOOL status = FALSE; + + // Check if the requested drain pump RPM is within range + if ( ( value >= MIN_RO_PUMP_DUTY_CYCLE ) && ( value <= MAX_RO_PUMP_DUTY_CYCLE ) ) + { + // Check if the user is logged in + if ( TRUE == isTestingActivated() ) + { + roPumpFeedbackDutyCyclePct.ovData = value; + roPumpFeedbackDutyCyclePct.override = OVERRIDE_KEY; + status = TRUE; + } + } + + return status; +} + +/*********************************************************************//** + * @brief + * The testResetROPumpMeasuredFeedbackDutyCycleOverride function resets the + * feedback duty cycle. + * @details Inputs: none + * @details Outputs: roPumpFeedbackDutyCyclePct + * @return TRUE if override reset successful, FALSE if not + *************************************************************************/ +BOOL testResetROPumpMeasuredFeedbackDutyCycleOverride( void ) +{ + BOOL status = FALSE; + + // Check if the user is logged in + if ( TRUE == isTestingActivated() ) + { + roPumpFeedbackDutyCyclePct.ovData = roPumpFeedbackDutyCyclePct.ovInitData; + roPumpFeedbackDutyCyclePct.override = OVERRIDE_RESET; + status = TRUE; + } + + return status; +} + /**@}*/ Index: firmware/App/Modes/ModeChemicalDisinfect.c =================================================================== diff -u -r335d28f954aed9522d5cc71863cd8dc253070758 -receb190a5d66fdbee779478ac8bb50a846ed9241 --- firmware/App/Modes/ModeChemicalDisinfect.c (.../ModeChemicalDisinfect.c) (revision 335d28f954aed9522d5cc71863cd8dc253070758) +++ 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,16 +148,6 @@ 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 { @@ -140,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 ********** @@ -167,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 ); @@ -203,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, @@ -212,26 +233,24 @@ *************************************************************************/ void initChemicalDisinfectMode( void ) { - chemDisinfectState = DG_CHEM_DISINFECT_STATE_START; - prevChemDisinfectState = DG_CHEM_DISINFECT_STATE_START; - stateTimer = 0; - isThisLastDrain = FALSE; - stateTrialCounter = 0; - areTempSensorsInRange = FALSE; - rsrvr1Status = NUM_OF_DG_RESERVOIR_STATUS; - rsrvr2Status = NUM_OF_DG_RESERVOIR_STATUS; - R1ChemDisinfectVol = 0.0; - R2ChemDisinfectVol = 0.0; - overallChemDisinfectTimer = 0; - cancellationMode = CANCELLATION_MODE_NONE; - rsrvrFillStableTimeCounter = 0; - isPartialDisinfectInProgress = FALSE; - numberOfPostDisinfectRinses = 0; - primeAcidSteadyStateCounter = 0; - chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_NOT_RUNNING; - haveDrainParamsBeenInit[ DG_RESERVOIR_1 ] = FALSE; - haveDrainParamsBeenInit[ DG_RESERVOIR_2 ] = FALSE; - disinfectNVOps.hasDisStatusBeenWrittenToNV = FALSE; + chemDisinfectState = DG_CHEM_DISINFECT_STATE_START; + prevChemDisinfectState = DG_CHEM_DISINFECT_STATE_START; + stateTimer = 0; + isThisLastDrain = FALSE; + stateTrialCounter = 0; + rsrvr1Status = NUM_OF_DG_RESERVOIR_STATUS; + rsrvr2Status = NUM_OF_DG_RESERVOIR_STATUS; + overallChemDisinfectTimer = 0; + cancellationMode = CANCELLATION_MODE_NONE; + rsrvrFillToFullStableTimeCounter = 0; + primeAcidSteadyStateCounter = 0; + disinfectantMixRatio = 0.0; + isRsrvrLeaking = FALSE; + chemDisinfectReservoirTime = 0; + ischemDisinfectWarmupTargetReached = FALSE; + isChemDisinfectTemperatureAboveTarget = FALSE; + chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_NOT_RUNNING; + disinfectNVOps.hasDisStatusBeenWrittenToNV = FALSE; } /*********************************************************************//** @@ -288,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; @@ -435,38 +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(); - F32 ppiPressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); - F32 THdTemp = getTemperatureValue( TEMPSENSORS_HEAT_DISINFECT ); - F32 TPoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); + // Set all the actuators to reset and de-energized state + deenergizeActuators( NO_PARK_CONC_PUMPS ); - // 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 ); + 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 ); - // Set the actuators to drain R1 - setValveState( VRD1, VALVE_STATE_OPEN ); - 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(); - rsrvrFillStableTimeCounter = 0; - // Assume reservoir 1 is full and drain it - rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; - stateTimer = getMSTimerCount(); - } - return state; } @@ -477,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 ) { @@ -551,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 @@ -567,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 ) @@ -621,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; @@ -673,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, NO_PARK_CONC_PUMPS ); - - 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, NO_PARK_CONC_PUMPS ); - - // 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 @@ -1076,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, NO_PARK_CONC_PUMPS ); - // 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, NO_PARK_CONC_PUMPS ); - - // 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; @@ -1759,7 +1127,7 @@ chemDisinfectUIState = CHEM_DISINFECT_UI_STATE_CANCEL_DISINFECT; // Set the cancellation mode - cancellationMode = CANCELLATION_MODE_BASIC; + cancellationMode = CANCELLATION_MODE_BASIC; failChemicalDisinfect(); @@ -1769,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 @@ -1790,7 +1159,6 @@ 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 ); @@ -1854,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(); @@ -1877,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 @@ -1889,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 ) { @@ -1907,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; @@ -1918,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; @@ -1930,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 @@ -1944,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; + } } /*********************************************************************//** @@ -2068,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 { @@ -2084,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 ) ); @@ -2102,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 @@ -2167,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; + } + } } /*********************************************************************//** @@ -2179,6 +1732,7 @@ * @details Outputs: disinfectNVOps * @return: none *************************************************************************/ +// TODO: this may conflict with heat disinfect? static void writeDisinfectDataToNV( void ) { if ( FALSE == disinfectNVOps.hasDisStatusBeenWrittenToNV ) Index: firmware/App/Modes/ModeChemicalDisinfectFlush.c =================================================================== diff -u -r3d977e037c170a3ad8870738215830d41713b00b -receb190a5d66fdbee779478ac8bb50a846ed9241 --- firmware/App/Modes/ModeChemicalDisinfectFlush.c (.../ModeChemicalDisinfectFlush.c) (revision 3d977e037c170a3ad8870738215830d41713b00b) +++ firmware/App/Modes/ModeChemicalDisinfectFlush.c (.../ModeChemicalDisinfectFlush.c) (revision eceb190a5d66fdbee779478ac8bb50a846ed9241) @@ -194,7 +194,7 @@ *************************************************************************/ U32 transitionToChemicalDisinfectFlushMode( void ) { - deenergizeActuators(); + deenergizeActuators( NO_PARK_CONC_PUMPS ); initChemicalDisinfectFlushMode(); @@ -705,8 +705,8 @@ rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; // Turn off CP1 and CP2 and ROP signalROPumpHardStop(); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID, NO_PARK_CONC_PUMPS ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB, NO_PARK_CONC_PUMPS ); //Turn off UV reactors turnOffUVReactor( INLET_UV_REACTOR ); turnOffUVReactor( OUTLET_UV_REACTOR ); @@ -762,7 +762,7 @@ 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; @@ -1056,18 +1056,18 @@ * @details Outputs: disinfectFlushNVOps * @return: none *************************************************************************/ -// TODO: is this necessary? static void writeDisinfectFlushDataToNV( void ) { - if ( FALSE == disinfectFlushNVOps.hasDisFlushCompleteDateBeenWrittenToNV ) + // TODO update in the next branch + /*if ( FALSE == disinfectFlushNVOps.hasDisFlushCompleteDateBeenWrittenToNV ) { disinfectFlushNVOps.hasDisFlushCompleteDateBeenWrittenToNV = setDisinfectStatus( TRUE ); } if ( FALSE == disinfectFlushNVOps.hasDisFlushStatusBeenWrittenToNV ) { disinfectFlushNVOps.hasDisFlushStatusBeenWrittenToNV = setLastDisinfectDate( USAGE_INFO_CHEMICAL_DISINFECT, getRTCTimestamp() ); - } + }*/ } /**@}*/ Index: firmware/App/Modes/ModeDrain.c =================================================================== diff -u -r3a371ac06b79d024d7fe1607f003031eb9322ba4 -receb190a5d66fdbee779478ac8bb50a846ed9241 --- firmware/App/Modes/ModeDrain.c (.../ModeDrain.c) (revision 3a371ac06b79d024d7fe1607f003031eb9322ba4) +++ firmware/App/Modes/ModeDrain.c (.../ModeDrain.c) (revision eceb190a5d66fdbee779478ac8bb50a846ed9241) @@ -346,8 +346,8 @@ if ( ++rinseConcentrateLinesTimerCtr > RINSE_CONCENTRATE_LINES_WAIT ) { rinseConcentrateLinesTimerCtr = 0; - requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID, NO_PARK_CONC_PUMPS ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB, NO_PARK_CONC_PUMPS ); requestNewOperationMode( DG_MODE_GENE ); } } Index: firmware/App/Modes/ModeFill.c =================================================================== diff -u -r5e8f96e11c797bddeddfc009c87f20df3b7a8664 -receb190a5d66fdbee779478ac8bb50a846ed9241 --- firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision 5e8f96e11c797bddeddfc009c87f20df3b7a8664) +++ firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision eceb190a5d66fdbee779478ac8bb50a846ed9241) @@ -79,8 +79,6 @@ #define PRIME_CONCENTRATE_LINES_TIME_OUT_MS ( 95 * MS_PER_SECOND ) ///< Time required to prime the concentrate lines. #define FLUSH_BUBBLES_PUMP_TIME_OUT_MS ( 2 * MS_PER_SECOND ) ///< RO pump on during flush bubble interval in ms. #define DIALYSATE_TEMPERATURE_TOLERANCE_C 2.0F ///< Dialysate temperature tolerance in degree C. -#define DIALYSATE_TEMPERATURE_SENSORS_MAX_DEVIATION_C 1.0F ///< Dialysate temperature sensors maximum allowed deviation in C. -#define DIALYSATE_TEMP_SNSRS_OUT_OF_RANGE_TIMEOUT_MS ( 10 * MS_PER_SECOND ) ///< Dialysate temperature sensors drift timeout in milliseconds. #define DATA_PUBLISH_COUNTER_START_COUNT 63 ///< Data publish counter start count. /// Multiplier to convert flow (mL/min) into volume (mL) for period of general task interval. @@ -151,7 +149,6 @@ static DG_FILL_MODE_STATE_T handlePausedState( void ); static BOOL areInletWaterConditionsAlarmsActive( void ); -static void checkDialysateTemperatureSensors( void ); static void handleDialysateMixing( F32 measuredROFlowRate_mL_min, F32 acidMixingRatio, F32 bicarbMixingRatio ); static void setFillInfoToRTCRAM( void ); static BOOL isValueWithinPercentRange( F32 testValue, F32 baseValue, F32 percentFactor ); @@ -189,8 +186,6 @@ totalBicarbConductivity = 0.0F; totalAcidConductivity = 0.0F; havePauseActuatorsBeenSet = FALSE; - - initPersistentAlarm( ALARM_ID_DG_DIALYSATE_TEMPERATURE_SENSORS_OUT_OF_RANGE, 0, DIALYSATE_TEMP_SNSRS_OUT_OF_RANGE_TIMEOUT_MS ); } /*********************************************************************//** @@ -505,8 +500,8 @@ if ( TRUE == didTimeout( concentratePrimingStartTime, PRIME_CONCENTRATE_LINES_TIME_OUT_MS ) ) { - requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID, NO_PARK_CONC_PUMPS ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB, NO_PARK_CONC_PUMPS ); // Set the RO pump flow rate in according to the roPumpFlushBubblesSpeed table to flush bubbles pumpSpeedIndex = 0; @@ -599,7 +594,7 @@ if ( TRUE == isConductivityInRange ) { // Initialization - requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB, NO_PARK_CONC_PUMPS ); totalBicarbConductivity = 0.0F; totalAcidConductivity = 0.0F; bicarbConductivitySampleCount = 0; @@ -753,11 +748,12 @@ DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; DG_RESERVOIR_ID_T inactiveRsrvr = getInactiveReservoir(); F32 acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); - F32 bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); // TODO the systems team will let us know when we can use TD2 in DVT. Until then it is disabled - F32 inletTemperature = getTemperatureValue( (U32)TEMPSENSORS_OUTLET_PRIMARY_HEATER ); //getTemperatureValue( (U32)TEMPSENSORS_CONDUCTIVITY_SENSOR_2 ); + F32 bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); + F32 inletTemperature = getTemperatureValue( (U32)TEMPSENSORS_CONDUCTIVITY_SENSOR_2 ); #ifndef _RELEASE_ - if ( HW_CONFIG_BETA == getHardwareConfigStatus() ) + if ( ( HW_CONFIG_BETA == getHardwareConfigStatus() ) || + ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_ENABLE_USING_TPO_FOR_PRIMARY_HEATER_CONTROL ) ) ) { inletTemperature = getTemperatureValue( (U32)TEMPSENSORS_OUTLET_PRIMARY_HEATER ); } @@ -879,8 +875,8 @@ { setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); setROPumpTargetFlowRateLPM( TARGET_RO_FLOW_RATE_IN_PAUSE_L, TARGET_RO_PRESSURE_PSI ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID, NO_PARK_CONC_PUMPS ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB, NO_PARK_CONC_PUMPS ); havePauseActuatorsBeenSet = TRUE; } @@ -925,30 +921,6 @@ /*********************************************************************//** * @brief - * The checkDialysateTemperatureSensors function checks whether the - * dialysate temperature sensors have drifted. If they are drifted, it raises - * an alarm. - * @details Inputs: none - * @details Outputs: None - * @return none - *************************************************************************/ -static void checkDialysateTemperatureSensors( void ) -{ - F32 TDi = getTemperatureValue( TEMPSENSORS_INLET_DIALYSATE ); - F32 TRo = getTemperatureValue( TEMPSENSORS_OUTLET_REDUNDANT ); - BOOL isDriftOut = ( fabs( TDi - TRo ) >= DIALYSATE_TEMPERATURE_SENSORS_MAX_DEVIATION_C ? TRUE : FALSE ); - -#ifndef _RELEASE_ - if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_TEMPERATURE_SENSORS_ALARM ) != SW_CONFIG_ENABLE_VALUE ) -#endif - { - checkPersistentAlarm( ALARM_ID_DG_DIALYSATE_TEMPERATURE_SENSORS_OUT_OF_RANGE, isDriftOut, fabs( TDi - TRo ), - DIALYSATE_TEMPERATURE_SENSORS_MAX_DEVIATION_C ); - } -} - -/*********************************************************************//** - * @brief * The isValueWithinPercentRange function validates if the test value * is within the percentage range specified by the baseValue and * percentFactor. @@ -1071,7 +1043,7 @@ usedAcidVolumeML.ovInitData = usedAcidVolumeML.data; usedAcidVolumeML.ovData = value; usedAcidVolumeML.override = OVERRIDE_KEY; - result = TRUE; + result = TRUE; } return result; @@ -1093,9 +1065,9 @@ { usedAcidVolumeML.data = usedAcidVolumeML.ovInitData; usedAcidVolumeML.override = OVERRIDE_RESET; - usedAcidVolumeML.ovInitData = 0.0; - usedAcidVolumeML.ovData = 0.0; - result = TRUE; + usedAcidVolumeML.ovInitData = 0.0F; + usedAcidVolumeML.ovData = 0.0F; + result = TRUE; } return result; @@ -1119,7 +1091,7 @@ usedBicarbVolumeML.ovInitData = usedBicarbVolumeML.data; usedBicarbVolumeML.ovData = value; usedBicarbVolumeML.override = OVERRIDE_KEY; - result = TRUE; + result = TRUE; } return result; @@ -1141,9 +1113,9 @@ { usedBicarbVolumeML.data = usedBicarbVolumeML.ovInitData; usedBicarbVolumeML.override = OVERRIDE_RESET; - usedBicarbVolumeML.ovInitData = 0.0; - usedBicarbVolumeML.ovData = 0.0; - result = TRUE; + usedBicarbVolumeML.ovInitData = 0.0F; + usedBicarbVolumeML.ovData = 0.0F; + result = TRUE; } return result; @@ -1164,10 +1136,10 @@ if ( TRUE == isTestingActivated() ) { - U32 intvl = value / TASK_GENERAL_INTERVAL; + U32 intvl = value / TASK_GENERAL_INTERVAL; fillModeDataPublishInterval.ovData = intvl; fillModeDataPublishInterval.override = OVERRIDE_KEY; - result = TRUE; + result = TRUE; } return result; @@ -1189,7 +1161,7 @@ { fillModeDataPublishInterval.override = OVERRIDE_RESET; fillModeDataPublishInterval.ovData = fillModeDataPublishInterval.ovInitData; - result = TRUE; + result = TRUE; } return result; Index: firmware/App/Modes/ModeFlush.c =================================================================== diff -u -r35cbd8ed1613bbade5e2ebf3b7708b710e23050b -receb190a5d66fdbee779478ac8bb50a846ed9241 --- firmware/App/Modes/ModeFlush.c (.../ModeFlush.c) (revision 35cbd8ed1613bbade5e2ebf3b7708b710e23050b) +++ firmware/App/Modes/ModeFlush.c (.../ModeFlush.c) (revision eceb190a5d66fdbee779478ac8bb50a846ed9241) @@ -7,8 +7,8 @@ * * @file ModeFlush.c * -* @author (last) Bill Bracken -* @date (last) 24-Oct-2022 +* @author (last) Steve Jarpe +* @date (last) 06-Dec-2022 * * @author (original) Leonardo Baloa * @date (original) 20-Dec-2019 Index: firmware/App/Modes/ModeGenIdle.c =================================================================== diff -u -r5e8f96e11c797bddeddfc009c87f20df3b7a8664 -receb190a5d66fdbee779478ac8bb50a846ed9241 --- firmware/App/Modes/ModeGenIdle.c (.../ModeGenIdle.c) (revision 5e8f96e11c797bddeddfc009c87f20df3b7a8664) +++ firmware/App/Modes/ModeGenIdle.c (.../ModeGenIdle.c) (revision eceb190a5d66fdbee779478ac8bb50a846ed9241) @@ -115,8 +115,8 @@ setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); signalDrainPumpHardStop(); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID, NO_PARK_CONC_PUMPS ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB, NO_PARK_CONC_PUMPS ); // UV reactors on turnOnUVReactor( INLET_UV_REACTOR ); Index: firmware/App/Modes/ModeHeatDisinfect.c =================================================================== diff -u -r570c501cb2f05c376551073d66d7dfc5ec444ee0 -receb190a5d66fdbee779478ac8bb50a846ed9241 --- firmware/App/Modes/ModeHeatDisinfect.c (.../ModeHeatDisinfect.c) (revision 570c501cb2f05c376551073d66d7dfc5ec444ee0) +++ firmware/App/Modes/ModeHeatDisinfect.c (.../ModeHeatDisinfect.c) (revision eceb190a5d66fdbee779478ac8bb50a846ed9241) @@ -51,41 +51,38 @@ #define MAX_ALLOWED_STATE_TRIALS 1 ///< Max allowed trials on a state. This is general among all the states. #define HEAT_DISINFECT_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Mode heat disinfect data publish interval in counts. -// Start state defines -#define MIN_INLET_PRESSURE_PSI 30.0F ///< Minimum water inlet pressure in psi. -#define MAX_START_STATE_TEMP_SENSORS_DIFF_C 3.0F ///< Max start state TDi and TRo difference tolerance in C. - // Drain R1 & R2 states defines #define DRAIN_PUMP_TARGET_RPM 2400 ///< Drain pump target RPM during drain. #define RSRVRS_INITIAL_DRAIN_TIME_OUT_MS ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 initial drain time out in milliseconds. #define DRAIN_WEIGHT_UNCHANGE_TIMEOUT ( 6 * MS_PER_SECOND ) ///< Time period of unchanged weight during draining before timeout. // Flush drain path state defines -#define FLUSH_DRAIN_WAIT_TIME_MS ( SEC_PER_MIN * MS_PER_SECOND ) ///< Flush Drain path wait time in milliseconds. -#define MIN_INLET_TEMPERATURE_C 15.0F ///< Minimum water inlet temperature in C. TODO original temperature was 25 C +#define FLUSH_DRAIN_WAIT_TIME_MS ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Flush Drain path wait time in milliseconds. +#define MIN_INLET_TEMPERATURE_C 24.0F ///< Minimum water inlet temperature in C. #define MAX_INLET_CONDUCTIVITY_US_PER_CM 2000.0F ///< Maximum water inlet conductivity in us/cm // Flush circulation path state defines #define RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM 0.8F ///< RO pump target flow rate during flush/fill in L/min. #define MAX_RO_PUMP_FLUSH_FILL_PRESSURE_PSI 130 ///< Maximum RO pump pressure during flush/fill states in psi. -#define FLUSH_CICRCULATION_WAIT_TIME_MS ( 30 * MS_PER_SECOND ) ///< Flush/rinse circulation path wait time in milliseconds. TODO original time was 30 seconds -#define MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C 50.0F ///< Maximum flush circulation temperature difference tolerance in C. TODO original difference was 3.0 degrees -#define NUM_OF_TEMP_SENSORS_TO_AVG 4.0F ///< Number of temperature sensors to average to check the difference. -#define ACID_PUMP_SPEED_ML_PER_MIN -30.0F ///< Acid concentrate pump speed in mL/min. +#define FLUSH_CICRCULATION_WAIT_TIME_MS ( 60 * MS_PER_SECOND ) ///< Flush circulation path wait time in milliseconds. +#define FLUSH_CIRCULATION_CONC_PUMPS_WAIT_TIME_MS ( 3 * SEC_PER_MIN * MS_PER_SECOND ) ///< Flush circulation concentrate pumps on 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 3.0F ///< Number of temperature sensors to average to check the difference. +#define ACID_PUMP_SPEED_ML_PER_MIN 30.6F ///< Acid concentrate pump speed in mL/min. // The bicarb pump is 2% faster than the acid pump to create a flow from acid to bicarb line during heat disinfect -#define BICARB_PUMP_SPEED_ML_PER_MIN 30.6F ///< Bicarb concentrate pump speed in mL/min. +#define BICARB_PUMP_SPEED_ML_PER_MIN -30.0F ///< Bicarb concentrate pump speed in mL/min. // 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_FULL_VOL_ML 1900.0F ///< Reservoirs 1 & 2 full volume in mL. #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. TODO original value was 5 mins -#define RSRVRS_500ML_FILL_UP_TIMEOUT_MS ( 4 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 partial fill up timeout in ms. TODO original value was 2 mins +#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 ( 4 * 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 and heat water #define HEAT_DISINFECT_TARGET_TEMPERATURE_C 82.0F ///< Heat disinfect target water temperature in C. -#define HEAT_DISINFECT_START_TEMPERATURE_C 81.0F ///< Heat disinfect minimum acceptable temperature in C. +#define HEAT_DISINFECT_START_TEMPERATURE_C 81.00F ///< Heat disinfect minimum acceptable temperature in C. // R1 to R2 & R2 to R1 heat disinfect circulation #define HEAT_DISINFECT_TARGET_RO_FLOW_LPM 1.3F ///< Heat disinfect target RO flow rate in L/min. @@ -101,22 +98,11 @@ #define HEAT_DISINFECT_MAX_TEMP_GRADIENT_C 15.0F ///< Heat disinfect maximum allowed temperature gradient in between hottest and coldest sensors. #define HEAT_DISINFECT_TEMP_GRAD_OUT_RANGE_TIME_MS ( 0.16 * SEC_PER_MIN * MS_PER_SECOND ) ///< Heat disinfect temperature gradient out of range timeout in milliseconds. -// Cool down RO filter -#define THD_REACH_BELOW_45_AFTER_CIRC_TIME_MS ( 5 * MS_PER_SECOND ) ///< Number of circulations that are needed to make the RO filter is below 45 C. -#define ROF_COOL_DOWN_TARGET_FLOW_LPM 0.3F ///< RO filter cool down target flow in L/min. -#define ROF_COOL_DOWN_CIRCULATION_TIME_MS ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< RO filter cool down circulation timer in milliseconds. -#define ROF_COOL_DOWN_MAX_TIME_MS ( 4 * MIN_PER_HOUR * SEC_PER_MIN * MS_PER_SECOND ) ///< RO filter cool down maximum state time in milliseconds. -#define TARGET_THD_SENSOR_FOR_RINSING_C 44.0F ///< Target THd temperature sensor value before rinsing in C. - // Mix drain R1 and R2 #define RSRVRS_MIX_DRAIN_TIMEOUT_MS ( 5 * SEC_PER_MIN * MS_PER_SECOND ) ///< Reservoirs 1 & 2 mix drain timeout in ms. #define DRAIN_PUMP_START_TIME_IN_MIX_DRAIN_MS ( 5 * MS_PER_SECOND ) ///< Time to start the drain pump at mix drain after directing the flow to drain in ms. #define DRAIN_PUMP_RPM_IN_MIX_DRAIN 600 ///< The RPM that the drain pump should be run during mix drain. #define MIX_DRAIN_WEIGHT_UNCHANGE_TIMEOUT ( 15 * MS_PER_SECOND ) ///< Time period of unchanged weight during mix draining before timeout. - -// Rinse R1 to R2 -#define ROF_MIN_LOW_PRESSURE_TEMPERATURE_C 45.0F ///< RO filter minimum temperature that the pressure must be no more than 30psi in C. - #define MIX_DRAIN_TEMPERATURE_THRESHOLD_C 60.0F ///< Temperature threshold for performing mix drain or normal drain. /// Cancellation paths @@ -145,20 +131,18 @@ typedef struct { BOOL hasDisStatusBeenWrittenToNV; ///< Boolean flag to indicate whether the disinfect status been written to NV or not. - BOOL hasDisCompleteDateBeenWrittenToNV; ///< Boolean flag to indicate whether the disinfect complete date been written to NV or not. } DISINFECT_NV_OPS_T; // ********** private data ********** -static DG_HEAT_DISINFECT_STATE_T heatDisinfectState; ///< Current active heat disinfect state. -static DG_HEAT_DISINFECT_STATE_T prevHeatDisinfectState; ///< Previous active heat disinfect state before alarm. -static DG_HEAT_DISINFECT_UI_STATE_T heatDisinfectUIState; ///< Current active heat disinfect UI state. -static U32 overallHeatDisinfectTimer; ///< Heat disinfect cycle total timer. -static U32 stateTimer; ///< Heat disinfect state timer to be used in different states. -static U32 stateTrialCounter; ///< Heat disinfect state trial counter to be used for retries in different states. -static BOOL areTempSensorsInRange; ///< Heat disinfect temperature sensors in/out range flag. -static U32 concentratePumpsPrimeTimer; ///< Concentrate pumps prime timer. -/// Boolean flag to check whether draining R1 and R2 is at the end of the heat disinfect cycle or in the beginning. So the drain states can be reused. +static DG_HEAT_DISINFECT_STATE_T heatDisinfectState; ///< Current active heat disinfect state. +static DG_HEAT_DISINFECT_STATE_T prevHeatDisinfectState; ///< Previous active heat disinfect state before alarm. +static DG_HEAT_DISINFECT_UI_STATE_T heatDisinfectUIState; ///< Current active heat disinfect UI state. +static U32 overallHeatDisinfectTimer; ///< Heat disinfect cycle total timer. +static U32 stateTimer; ///< Heat disinfect state timer to be used in different states. +static U32 stateTrialCounter; ///< Heat disinfect state trial counter to be used for retries in different states. +static BOOL areTempSensorsInRange; ///< Heat disinfect temperature sensors in/out range flag. +static U32 concentratePumpsPrimeTimer; ///< Concentrate pumps prime timer. static DG_RESERVOIR_STATUS_T rsrvr1Status; ///< Reservoir 1 status. static DG_RESERVOIR_STATUS_T rsrvr2Status; ///< Reservoir 2 status. static F32 R1HeatDisinfectVol; ///< Reservoir 1 full volume during heat disinfect. @@ -172,7 +156,6 @@ static U32 rsrvrFillStableTimeCounter; ///< Reservoirs fill stable time counter. static ALARM_ID_T alarmDetectedPendingTrigger; ///< Heat disinfect alarm to raise. static BOOL isDrainPumpInMixDrainOn; ///< Flag to indicate the drain pump is on during mix drain. -static U32 ROFCoolingTimer; ///< RO filter cooling timer. static U32 targetDisinfectTime; ///< Target disinfect time. static BOOL haveDrainParamsBeenInit[ NUM_OF_DG_RESERVOIRS ]; ///< Boolean flag to indicate whether the drain parameters have been reset or not. static U32 tempGradOutOfRangeTimer; ///< Temperature gradient out of range start timer. @@ -194,7 +177,6 @@ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFillR2WithHotWaterState( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectDisinfectR2ToR1State( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectCoolDownHeatersState( void ); -static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectCoolDownROFilterState( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectMixDrainR1State( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectMixDrainR2State( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectCancelModeBasicPathState( void ); @@ -207,7 +189,7 @@ static HEAT_DISINFECT_STATUS_T getHeatDisinfectStatus( void ); static void publishHeatDisinfectData( void ); static void monitorModeHeatDisinfect( void ); -static void writeDisinfectDataToNV( void ); +static void writeDisinfectDataToNV( DG_USAGE_INFO_ITEMS_T info ); /*********************************************************************//** * @brief @@ -227,34 +209,32 @@ *************************************************************************/ void initHeatDisinfectMode( void ) { - heatDisinfectState = DG_HEAT_DISINFECT_STATE_START; - prevHeatDisinfectState = DG_HEAT_DISINFECT_STATE_START; - heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_NOT_RUNNING; - stateTimer = 0; - stateTrialCounter = 0; - areTempSensorsInRange = FALSE; - rsrvr1Status = NUM_OF_DG_RESERVOIR_STATUS; - rsrvr2Status = NUM_OF_DG_RESERVOIR_STATUS; - R1HeatDisinfectVol = 0.0F; - R2HeatDisinfectVol = 0.0F; - overallHeatDisinfectTimer = 0; - cancellationMode = CANCELLATION_MODE_NONE; - rsrvrFillStableTimeCounter = 0; - isPartialDisinfectInProgress = FALSE; - isDrainPumpInMixDrainOn = FALSE; - ROFCoolingTimer = 0; - concentratePumpsPrimeTimer = 0; - targetDisinfectTime = 0; - haveDrainParamsBeenInit[ DG_RESERVOIR_1 ] = FALSE; - haveDrainParamsBeenInit[ DG_RESERVOIR_2 ] = FALSE; - tempGradOutOfRangeTimer = 0; - disinfectNVOps.hasDisCompleteDateBeenWrittenToNV = FALSE; - disinfectNVOps.hasDisStatusBeenWrittenToNV = FALSE; - alarmDetectedPendingTrigger = ALARM_ID_NO_ALARM; - heatDisinfectTimer = 0; - rsrvrsVolMonitorTimer = 0; - areRsrvrsLeaking = FALSE; - dataPublishCounter = 0; + heatDisinfectState = DG_HEAT_DISINFECT_STATE_START; + prevHeatDisinfectState = DG_HEAT_DISINFECT_STATE_START; + heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_NOT_RUNNING; + stateTimer = 0; + stateTrialCounter = 0; + areTempSensorsInRange = FALSE; + rsrvr1Status = NUM_OF_DG_RESERVOIR_STATUS; + rsrvr2Status = NUM_OF_DG_RESERVOIR_STATUS; + R1HeatDisinfectVol = 0.0F; + R2HeatDisinfectVol = 0.0F; + overallHeatDisinfectTimer = 0; + cancellationMode = CANCELLATION_MODE_NONE; + rsrvrFillStableTimeCounter = 0; + isPartialDisinfectInProgress = FALSE; + isDrainPumpInMixDrainOn = FALSE; + concentratePumpsPrimeTimer = 0; + targetDisinfectTime = 0; + haveDrainParamsBeenInit[ DG_RESERVOIR_1 ] = FALSE; + haveDrainParamsBeenInit[ DG_RESERVOIR_2 ] = FALSE; + tempGradOutOfRangeTimer = 0; + disinfectNVOps.hasDisStatusBeenWrittenToNV = FALSE; + alarmDetectedPendingTrigger = ALARM_ID_NO_ALARM; + heatDisinfectTimer = 0; + rsrvrsVolMonitorTimer = 0; + areRsrvrsLeaking = FALSE; + dataPublishCounter = 0; } /*********************************************************************//** @@ -267,7 +247,8 @@ *************************************************************************/ U32 transitionToHeatDisinfectMode( void ) { - deenergizeActuators(); + // Set all the actuators to reset and de-energized state + deenergizeActuators( NO_PARK_CONC_PUMPS ); initHeatDisinfectMode(); @@ -286,6 +267,17 @@ *************************************************************************/ U32 execHeatDisinfectMode( void ) { + // The inlet pressure shall be checked all the time as long as VPi is open + checkInletWaterPressure(); + + if ( heatDisinfectState != DG_HEAT_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(); + } + monitorModeHeatDisinfect(); switch ( heatDisinfectState ) @@ -346,10 +338,6 @@ heatDisinfectState = handleHeatDisinfectCoolDownHeatersState(); break; - case DG_HEAT_DISINFECT_STATE_COOL_DOWN_RO_FILTER: - heatDisinfectState = handleHeatDisinfectCoolDownROFilterState(); - break; - case DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R1: heatDisinfectState = handleHeatDisinfectMixDrainR1State(); break; @@ -409,7 +397,7 @@ if ( DG_MODE_HEAT == getCurrentOperationMode() ) { // Reset all the actuators - deenergizeActuators(); + deenergizeActuators( NO_PARK_CONC_PUMPS ); // Transition to mode standby requestNewOperationMode( DG_MODE_STAN ); @@ -438,43 +426,18 @@ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectStartState( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_DRAIN_R1; - F32 ppiPressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); - F32 TDiTemp = getTemperatureValue( TEMPSENSORS_INLET_DIALYSATE ); - F32 TRoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_REDUNDANT ); + overallHeatDisinfectTimer = getMSTimerCount(); + heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_FLUSH_BEFORE_DISINFECT; + rsrvrFillStableTimeCounter = 0; + rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; + stateTimer = getMSTimerCount(); - // Start overall heat disinfect timer - overallHeatDisinfectTimer = getMSTimerCount(); + // Close VPi to prevent wasting water + setValveState( VPI, VALVE_STATE_CLOSED ); + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC); + setValveState( VRD1, VALVE_STATE_OPEN ); + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); - // Set all the actuators to reset and de-energized state - deenergizeActuators(); - - // If the inlet pressure is less than or equal to the threshold or TDi and TRo difference is greater than 3 C, the cycle - // should be canceled - if ( ( ppiPressure <= MIN_INLET_PRESSURE_PSI ) || ( fabs( TDiTemp - TRoTemp ) > MAX_START_STATE_TEMP_SENSORS_DIFF_C ) ) - { - prevHeatDisinfectState = state; - alarmDetectedPendingTrigger = ALARM_ID_DG_HEAT_DISINFECT_INLET_PRES_AND_TEMP_SNSRS_OUT; - state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; - } - else - { - // Set the heat disinfect UI state - heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_FLUSH_BEFORE_DISINFECT; - - // Close VPi to prevent wasting water - setValveState( VPI, VALVE_STATE_CLOSED ); - setValveState( VPO, VALVE_STATE_FILL_C_TO_NC); - - // 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; } @@ -589,43 +552,13 @@ // Check if flush time has elapsed if ( TRUE == didTimeout( stateTimer, FLUSH_DRAIN_WAIT_TIME_MS ) ) { - BOOL hasConductivityPassed = FALSE; + turnOnUVReactor( INLET_UV_REACTOR ); + setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); + setROPumpTargetFlowRateLPM( RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM, MAX_RO_PUMP_FLUSH_FILL_PRESSURE_PSI ); + stateTimer = getMSTimerCount(); + stateTrialCounter = 0; + state = DG_HEAT_DISINFECT_STATE_FLUSH_CIRCULATION; - // If the inlet temperature and conductivity are in range, move onto the next state - if ( ( getTemperatureValue( TEMPSENSORS_INLET_PRIMARY_HEATER ) >= MIN_INLET_TEMPERATURE_C ) && - ( getConductivityValue( CONDUCTIVITYSENSORS_CPI_SENSOR ) <= MAX_INLET_CONDUCTIVITY_US_PER_CM ) ) - { - hasConductivityPassed = TRUE; - } - -#ifndef _RELEASE_ - if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_DISABLE_DISINFECT_CONDUCTIVITY_CHECK ) ) - { - hasConductivityPassed = TRUE; - } -#endif - - if ( TRUE == hasConductivityPassed ) - { - setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); - setROPumpTargetFlowRateLPM( RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM, MAX_RO_PUMP_FLUSH_FILL_PRESSURE_PSI ); - stateTimer = getMSTimerCount(); - stateTrialCounter = 0; - state = DG_HEAT_DISINFECT_STATE_FLUSH_CIRCULATION; - } - // If the number of failures have not exceeded the limit, try again. - else if ( stateTrialCounter < MAX_ALLOWED_STATE_TRIALS ) - { - stateTrialCounter++; - stateTimer = getMSTimerCount(); - } - // Couldn't get a good water sample after a couple of trials and the disinfect cycle failed - else - { - alarmDetectedPendingTrigger = ALARM_ID_DG_HEAT_DISINFECT_INLET_COND_AND_TEMP_OUT; - prevHeatDisinfectState = state; - state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; - } } return state; @@ -648,24 +581,24 @@ { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_FLUSH_CIRCULATION; + writeDisinfectDataToNV( USAGE_INFO_FILTER_FLUSH ); + // Check if the flush circulation time has elapsed and the temperature sensors are not in range yet if ( ( TRUE == didTimeout( stateTimer, FLUSH_CICRCULATION_WAIT_TIME_MS ) ) && ( FALSE == areTempSensorsInRange ) ) { F32 ThdTemp = getTemperatureValue( TEMPSENSORS_HEAT_DISINFECT ); F32 TPoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); - F32 TD1Temp = getTemperatureValue( TEMPSENSORS_CONDUCTIVITY_SENSOR_1 ); F32 TD2Temp = getTemperatureValue( TEMPSENSORS_CONDUCTIVITY_SENSOR_2 ); - F32 avgTemp = ( ThdTemp + TPoTemp + TD1Temp + TD2Temp ) / NUM_OF_TEMP_SENSORS_TO_AVG; + F32 avgTemp = ( ThdTemp + TPoTemp + TD2Temp ) / NUM_OF_TEMP_SENSORS_TO_AVG; // Check if any of the temperature sensors deviate for more than the defined value from the average of all // of the temperature sensors BOOL isThdOut = ( fabs( ThdTemp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C ? TRUE : FALSE ); BOOL isTPoOut = ( fabs( TPoTemp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C ? TRUE : FALSE ); - BOOL isTD1Out = ( fabs( TD1Temp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C ? TRUE : FALSE ); BOOL isTD2Out = ( fabs( TD2Temp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C ? TRUE : FALSE ); // Check if any of the temperature sensors are out of tolerance - if ( ( TRUE == isThdOut ) || ( TRUE == isTPoOut ) || ( TRUE == isTD1Out ) || ( TRUE == isTD2Out ) ) + if ( ( TRUE == isThdOut ) || ( TRUE == isTPoOut ) || ( TRUE == isTD2Out ) ) { // Check if we have exceeded the number of trials. If not, try another time if ( ++stateTrialCounter < MAX_ALLOWED_STATE_TRIALS ) @@ -676,8 +609,8 @@ else { alarmDetectedPendingTrigger = ALARM_ID_DG_TEMP_SENSORS_DIFF_OUT_OF_RANGE; - prevHeatDisinfectState = state; - state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; + prevHeatDisinfectState = state; + state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; } } else @@ -697,20 +630,25 @@ // Only start the concentrate pumps if the temperature sensors are in range if ( TRUE == areTempSensorsInRange ) { - if ( TRUE == didTimeout( concentratePumpsPrimeTimer, FLUSH_CICRCULATION_WAIT_TIME_MS ) ) + if ( TRUE == didTimeout( concentratePumpsPrimeTimer, FLUSH_CIRCULATION_CONC_PUMPS_WAIT_TIME_MS ) ) { rsrvr1Status = DG_RESERVOIR_BELOW_TARGET; rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; // Done with flushing the concentrate pumps line - requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID, NO_PARK_CONC_PUMPS ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB, NO_PARK_CONC_PUMPS ); setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); - stateTimer = getMSTimerCount(); - state = DG_HEAT_DISINFECT_STATE_FLUSH_R1_AND_R2; + setHeaterTargetTemperature( DG_PRIMARY_HEATER, HEAT_DISINFECT_TARGET_TEMPERATURE_C ); + startHeater( DG_PRIMARY_HEATER ); + + // Set the NV data to FLASE to be able to write another NV data ops + disinfectNVOps.hasDisStatusBeenWrittenToNV = FALSE; + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_FLUSH_R1_AND_R2; } } @@ -745,9 +683,9 @@ // until reservoir 2 is filled up and the tubing might expand or leak. if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) { - prevHeatDisinfectState = state; + prevHeatDisinfectState = state; alarmDetectedPendingTrigger = ALARM_ID_DG_INVALID_LOAD_CELL_VALUE; - state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; + state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; } } // Once R1 is full, keep monitoring for R2 level and timeout @@ -769,18 +707,18 @@ // Set both reservoirs status rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; - state = DG_HEAT_DISINFECT_STATE_FLUSH_R2_AND_DRAIN_R1; + state = DG_HEAT_DISINFECT_STATE_FLUSH_R2_AND_DRAIN_R1; } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) { prevHeatDisinfectState = state; - state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; + state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; } } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { prevHeatDisinfectState = state; - state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; + state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; } return state; @@ -818,7 +756,7 @@ else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { prevHeatDisinfectState = state; - state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; + state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; } // First reservoir 2 must be completely full @@ -836,9 +774,9 @@ // 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 ) ) { - prevHeatDisinfectState = state; + prevHeatDisinfectState = state; alarmDetectedPendingTrigger = ALARM_ID_DG_INVALID_LOAD_CELL_VALUE; - state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; + state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; } } // Once R2 is full, R1 must be partially full @@ -851,7 +789,8 @@ { // Done with filing turn off the RO pump signalROPumpHardStop(); - + stopHeater( DG_PRIMARY_HEATER ); + turnOffUVReactor( INLET_UV_REACTOR ); // Set the valves to drain R2 and no fill setValveState( VPI, VALVE_STATE_CLOSED ); setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); @@ -860,20 +799,20 @@ setValveState( VRD2, VALVE_STATE_OPEN ); setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); // Start the timer for drain timeout - stateTimer = getMSTimerCount(); + stateTimer = getMSTimerCount(); rsrvr2Status = DG_RESERVOIR_ABOVE_TARGET; - state = DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN_R2; + state = DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN_R2; } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { prevHeatDisinfectState = state; - state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; + state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; } } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) { prevHeatDisinfectState = state; - state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; + state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; } return state; @@ -904,14 +843,14 @@ setValveState( VRD1, VALVE_STATE_OPEN ); setValveState( VRD2, VALVE_STATE_CLOSED ); // Start the timer for drain timeout - stateTimer = getMSTimerCount(); + stateTimer = getMSTimerCount(); rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; - state = DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN_R1; + state = DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN_R1; } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) { prevHeatDisinfectState = state; - state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; + state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; } return state; @@ -951,7 +890,7 @@ setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); setValveState( VRD1, VALVE_STATE_CLOSED ); setValveState( VRD2, VALVE_STATE_OPEN ); - + turnOnUVReactor( INLET_UV_REACTOR ); // Turn on the RO pump setROPumpTargetFlowRateLPM( RO_PUMP_TARGET_FLUSH_FILL_FLOW_RATE_LPM, MAX_RO_PUMP_FLUSH_FILL_PRESSURE_PSI ); @@ -961,15 +900,13 @@ rsrvr1Status = DG_RESERVOIR_BELOW_TARGET; rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; - - // Start the timer for drain timeout - stateTimer = getMSTimerCount(); - state = DG_HEAT_DISINFECT_STATE_FILL_WITH_WATER; + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_FILL_WITH_WATER; } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { prevHeatDisinfectState = state; - state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; + state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; } return state; @@ -1009,6 +946,7 @@ // Once reservoir 2 is full, set the actuators for recirculation if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) { + turnOffUVReactor( INLET_UV_REACTOR ); // Set the valves to drain R2 and no fill setValveState( VPI, VALVE_STATE_CLOSED ); setValveState( VBF, VALVE_STATE_OPEN ); @@ -1081,9 +1019,8 @@ break; case HEAT_DISINFECT_COMPLETE: - - requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID, NO_PARK_CONC_PUMPS ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB, NO_PARK_CONC_PUMPS ); // 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 ); @@ -1099,7 +1036,7 @@ setDrainPumpTargetOutletPressure( HEAT_DISINFECT_TARGET_DRAIN_FILL_R2_PSI ); // Turn off trimmer heater for transition - stopHeater(DG_TRIMMER_HEATER); + stopHeater( DG_TRIMMER_HEATER ); // Although there is fluid in both reservoirs, but they are set to empty // to begin the transition of hot water from R1 to R2. @@ -1209,15 +1146,13 @@ break; case HEAT_DISINFECT_COMPLETE: + requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID, NO_PARK_CONC_PUMPS ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB, NO_PARK_CONC_PUMPS ); // Turn off the heaters stopHeater( DG_PRIMARY_HEATER ); stopHeater( DG_TRIMMER_HEATER ); stateTimer = getMSTimerCount(); state = DG_HEAT_DISINFECT_STATE_COOL_DOWN_HEATERS; - - // Set the disinfect flags - setDisinfectStatus( TRUE ); - setLastDisinfectDate( USAGE_INFO_HEAT_DISINFECT, getRTCTimestamp() ); break; case HEAT_DISINFECT_HEAT_UP_IN_PROGRESS: @@ -1241,99 +1176,33 @@ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectCoolDownHeatersState( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_COOL_DOWN_HEATERS; + heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_COOL_DOWN_DEVICE; - // Set the heat disinfect UI state - heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_COOL_DOWN_DEVICE; + writeDisinfectDataToNV( USAGE_INFO_HEAT_DIS ); - writeDisinfectDataToNV(); - if ( TRUE == didTimeout( stateTimer, POST_HEAT_DISINFECT_WAIT_TIME_MS ) ) { - // Stop the drain pump and the RO pump to exit the closed loop - signalDrainPumpHardStop(); signalROPumpHardStop(); - // 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( VRO, VALVE_STATE_R1_C_TO_NO ); - setValveState( VRD1, VALVE_STATE_CLOSED ); - - setValveState( VBF, VALVE_STATE_OPEN ); - setValveState( VDR, VALVE_STATE_RECIRC_C_TO_NC ); - setValveState( VRC, VALVE_STATE_RECIRC_C_TO_NC ); - setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); - - setROPumpTargetFlowRateLPM( ROF_COOL_DOWN_TARGET_FLOW_LPM, HEAT_DISINFECT_MAX_RO_PRESSURE_PSI ); - - ROFCoolingTimer = 0; - stateTimer = getMSTimerCount(); - state = DG_HEAT_DISINFECT_STATE_COOL_DOWN_RO_FILTER; + if ( TRUE == isDrainPumpOn() ) + { + // Stop the drain pump and the RO pump to exit the closed loop + signalDrainPumpHardStop(); + setValveState( VRD1, VALVE_STATE_CLOSED ); + } + else + { + rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R1; + } } return state; } /*********************************************************************//** * @brief - * The handleHeatDisinfectCoolDownROFilterState function handles the heat - * disinfect cool down RO filter state. The state monitors the temperature - * at THd and if it is less than 45 C, it transitions to the next state. - * @details Inputs: stateTimer, ROFCoolingTimer - * @details Outputs: stateTimer, ROFcoolingTimer, heatDisinfectUIState - * @return next state of the heat disinfect state machine - *************************************************************************/ -static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectCoolDownROFilterState( void ) -{ - DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_COOL_DOWN_RO_FILTER; - // THd is the closet sensor to the RO filter and this temperature is monitored - // until it is dropped below 45 C to be able to run fluid through the RO filter - F32 THdTemp = getTemperatureValue( TEMPSENSORS_HEAT_DISINFECT ); - - // Set the heat disinfect UI state - heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_COOL_DOWN_DEVICE; - - if ( ( 0 == ROFCoolingTimer ) && ( THdTemp < TARGET_THD_SENSOR_FOR_RINSING_C ) ) - { - // Temperature is below target - perform mandatory cool down - ROFCoolingTimer = getMSTimerCount(); - } - else if ( THdTemp >= TARGET_THD_SENSOR_FOR_RINSING_C ) - { - // Temperature is not below target - reset the timer and keep looking - ROFCoolingTimer = 0; - } - - if ( ( 0 != ROFCoolingTimer ) && ( TRUE == didTimeout( ROFCoolingTimer, ROF_COOL_DOWN_CIRCULATION_TIME_MS ) ) ) - { - // Temperature is below target, transition to next state - setValveState( VPI, VALVE_STATE_OPEN ); - setValveState( VBF, VALVE_STATE_CLOSED ); - setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); - setValveState( VRC, VALVE_STATE_RECIRC_C_TO_NC ); - setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); - setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); - setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); - setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); - - signalROPumpHardStop(); - - rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; - stateTimer = getMSTimerCount(); - state = DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R1; - } - else if ( ( TRUE == didTimeout( stateTimer, ROF_COOL_DOWN_MAX_TIME_MS ) ) ) - { - prevHeatDisinfectState = state; - alarmDetectedPendingTrigger = ALARM_ID_DG_HEAT_DISINFECT_TARGET_TEMP_TIMEOUT; // TODO - cool down alarm? - state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; - } - - return state; -} - -/*********************************************************************//** - * @brief * The handleHeatDisinfectMixDrainR1State function handles the heat * disinfect mix drain R1 state. The state drains reservoir 1 and if it * times out, it transitions to basic cancellation state. Otherwise, it @@ -1347,15 +1216,19 @@ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectMixDrainR1State( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R1; + heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_FLUSH_AFTER_DISINFECT; - // Set the heat disinfect UI state - heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_FLUSH_AFTER_DISINFECT; - if ( ( TRUE == didTimeout( stateTimer, DRAIN_PUMP_START_TIME_IN_MIX_DRAIN_MS ) ) && ( FALSE == isDrainPumpInMixDrainOn ) ) { isDrainPumpInMixDrainOn = TRUE; + setValveState( VPI, VALVE_STATE_OPEN ); setValveState( VRD1, VALVE_STATE_OPEN ); + setValveState( VBF, 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 ); + setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); // Turn on the drain pump to drain the reservoirs in open loop mode setDrainPumpTargetRPM( DRAIN_PUMP_RPM_IN_MIX_DRAIN ); } @@ -1373,13 +1246,13 @@ setValveState( VRD2, VALVE_STATE_OPEN ); rsrvr2Status = DG_RESERVOIR_ABOVE_TARGET; - stateTimer = getMSTimerCount(); - state = DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R2; + stateTimer = getMSTimerCount(); + state = DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R2; } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { prevHeatDisinfectState = state; - state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; + state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; } } @@ -1411,7 +1284,7 @@ else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) { prevHeatDisinfectState = state; - state = DG_HEAT_DISINFECT_STATE_CANCEL_BASIC_PATH; + state = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; } return state; @@ -1470,7 +1343,7 @@ F32 TRo = getTemperatureValue( TEMPSENSORS_OUTLET_REDUNDANT ); // Stop all the actuators first then decide who should run next - deenergizeActuators(); + deenergizeActuators( NO_PARK_CONC_PUMPS ); // The two sensors must be less than a threshold to decide if mix drain is needed to normal drain if ( ( TDi < MIX_DRAIN_TEMPERATURE_THRESHOLD_C ) && ( TRo < MIX_DRAIN_TEMPERATURE_THRESHOLD_C ) ) @@ -1569,7 +1442,7 @@ // Set the heat disinfect UI state heatDisinfectUIState = HEAT_DISINFECT_UI_STATE_COMPLETE; - stopDGHeatDisinfect(); + requestNewOperationMode( DG_MODE_HCOL ); return state; } @@ -1584,7 +1457,13 @@ *************************************************************************/ static void failHeatDisinfect( void ) { - SET_ALARM_WITH_1_U32_DATA( alarmDetectedPendingTrigger, prevHeatDisinfectState ) + // 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, prevHeatDisinfectState ) + } + requestNewOperationMode( DG_MODE_FAUL ); } /*********************************************************************//** @@ -1678,9 +1557,9 @@ if ( TRUE == isDrainComplete ) { - if ( ( DG_HEAT_DISINFECT_STATE_FLUSH_R2_AND_DRAIN_R1 == heatDisinfectState) && ( DG_RESERVOIR_1 == r) ) + if ( ( DG_HEAT_DISINFECT_STATE_FLUSH_R2_AND_DRAIN_R1 == heatDisinfectState) && ( DG_RESERVOIR_1 == r ) ) { - if ( ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status) && ( 0 == getDrainPumpTargetRPM() ) ) + if ( ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) && ( 0 == getDrainPumpTargetRPM() ) ) { stateTimer = getMSTimerCount(); } @@ -1696,10 +1575,10 @@ else if ( TRUE == didTimeout( stateTimer, timeout ) ) { // Failed to drain on time. Update the previous heat disinfect state and transition to basic cancellation - prevHeatDisinfectState = heatDisinfectState; - alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_DRAIN_TIMEOUT; - haveDrainParamsBeenInit[ r ] = FALSE; - status = DG_RESERVOIR_NOT_REACHED_TARGET; + prevHeatDisinfectState = heatDisinfectState; + alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_DRAIN_TIMEOUT; + haveDrainParamsBeenInit[ r ] = FALSE; + status = DG_RESERVOIR_NOT_REACHED_TARGET; } return status; @@ -1726,21 +1605,18 @@ static HEAT_DISINFECT_STATUS_T getHeatDisinfectStatus( void ) { HEAT_DISINFECT_STATUS_T status = HEAT_DISINFECT_HEAT_UP_IN_PROGRESS; + F32 TPoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); + F32 ThdTemp = getTemperatureValue( TEMPSENSORS_HEAT_DISINFECT ); + F32 loadCellA1 = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); + F32 loadCellB1 = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); + BOOL isR1OutOfRange = ( fabs( loadCellA1 - R1HeatDisinfectVol ) > RSRVRS_MAX_TARGET_VOL_CHANGE_ML ? TRUE : FALSE ); + BOOL isR2OutOfRange = ( fabs( loadCellB1 - R2HeatDisinfectVol ) > RSRVRS_MAX_TARGET_VOL_CHANGE_ML ? TRUE : FALSE ); + BOOL isGradientOutOfRange = ( fabs( TPoTemp - ThdTemp ) > HEAT_DISINFECT_MAX_TEMP_GRADIENT_C ? TRUE : FALSE ); - F32 TPoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); - F32 ThdTemp = getTemperatureValue( TEMPSENSORS_HEAT_DISINFECT ); - - BOOL isR1OutOfRange = fabs( getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ) - R1HeatDisinfectVol ) > RSRVRS_MAX_TARGET_VOL_CHANGE_ML; - BOOL isR2OutOfRange = fabs( getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ) - R2HeatDisinfectVol ) > RSRVRS_MAX_TARGET_VOL_CHANGE_ML; - - // Check if the temperature gradient in between the coldest and the hottest spot is more than the specified temperature and - // the timer has not started yet, start it - BOOL gradientOutOfRange = ( fabs( TPoTemp - ThdTemp ) > HEAT_DISINFECT_MAX_TEMP_GRADIENT_C ) ? TRUE : FALSE; - // Perform check if no pending alarm - if (ALARM_ID_NO_ALARM == alarmDetectedPendingTrigger) + if ( ALARM_ID_NO_ALARM == alarmDetectedPendingTrigger ) { - if ( TRUE != gradientOutOfRange ) + if ( FALSE == isGradientOutOfRange ) { tempGradOutOfRangeTimer = 0; } @@ -1756,7 +1632,7 @@ } // Perform check if no pending alarm - if (ALARM_ID_NO_ALARM == alarmDetectedPendingTrigger) + if ( ALARM_ID_NO_ALARM == alarmDetectedPendingTrigger ) { // Check if either reservoir 1 or reservoir 2 are losing volume more than allowed volume if ( ( TRUE == isR1OutOfRange ) || ( TRUE == isR2OutOfRange ) ) @@ -1783,7 +1659,7 @@ } // Perform check if no pending alarm - if (ALARM_ID_NO_ALARM == alarmDetectedPendingTrigger) + if ( ALARM_ID_NO_ALARM == alarmDetectedPendingTrigger ) { // If the coldest spot which is THd is less than minimum heat disinfect temperature, // reset the heat disinfect timers and check whether heating up has timed out @@ -1807,24 +1683,20 @@ heatDisinfectTimer = getMSTimerCount(); isPartialDisinfectInProgress = TRUE; targetDisinfectTime = HEAT_DISINFECT_TIME_MS; - status = HEAT_DISINFECT_DISINFECT_IN_PROGRESS; + status = HEAT_DISINFECT_DISINFECT_IN_PROGRESS; - // In disinfect R1 to R2, concentrate pumps are also run - if ( DG_HEAT_DISINFECT_STATE_DISINFECT_R1_TO_R2 == heatDisinfectState ) - { - // Turn the pumps on in reverse - setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, ACID_PUMP_SPEED_ML_PER_MIN ); - setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, BICARB_PUMP_SPEED_ML_PER_MIN ); + // Turn the pumps on in reverse + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, ACID_PUMP_SPEED_ML_PER_MIN ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, BICARB_PUMP_SPEED_ML_PER_MIN ); - // During R1 to R2 disinfect, the concentrate pumps turn on - requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); - } + // During R1 to R2 disinfect, the concentrate pumps turn on + requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); } } // Perform check if no pending alarm - if (ALARM_ID_NO_ALARM == alarmDetectedPendingTrigger) + if ( ALARM_ID_NO_ALARM == alarmDetectedPendingTrigger ) { // If heat disinfect temperature has been reached, check if this stage of heat disinfect is done if ( ( TRUE == isPartialDisinfectInProgress ) && ( TRUE == didTimeout( heatDisinfectTimer, HEAT_DISINFECT_TIME_MS ) ) ) @@ -1905,6 +1777,8 @@ *************************************************************************/ static void monitorModeHeatDisinfect( void ) { + BOOL areInletWaterAlarmsActive = FALSE; + #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_CAPS_MONITOR ) != SW_CONFIG_ENABLE_VALUE ) #endif @@ -1919,6 +1793,20 @@ alarmDetectedPendingTrigger = ALARM_ID_DG_DIALYSATE_OR_CONC_CAP_NOT_IN_PROPER_POSITION; } } + + areInletWaterAlarmsActive |= isAlarmActive( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_TEMP_TOO_HIGH ); + areInletWaterAlarmsActive |= isAlarmActive( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_TEMP_TOO_LOW ); + areInletWaterAlarmsActive |= isAlarmActive( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_PRESSURE_TOO_HIGH ); + areInletWaterAlarmsActive |= isAlarmActive( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_PRESSURE_TOO_LOW ); + areInletWaterAlarmsActive |= isAlarmActive( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_COND_TOO_HIGH ); + areInletWaterAlarmsActive |= isAlarmActive( ALARM_ID_DG_CLEANING_MODE_INLET_WATER_COND_TOO_LOW ); + areInletWaterAlarmsActive |= isAlarmActive( ALARM_ID_DG_DIALYSATE_TEMPERATURE_SENSORS_OUT_OF_RANGE ); + + if ( ( TRUE == areInletWaterAlarmsActive ) ) + { + prevHeatDisinfectState = heatDisinfectState; + heatDisinfectState = DG_HEAT_DISINFECT_STATE_CANCEL_WATER_PATH; + } } /*********************************************************************//** @@ -1929,16 +1817,11 @@ * @details Outputs: disinfectNVOps * @return: none *************************************************************************/ -static void writeDisinfectDataToNV( void ) +static void writeDisinfectDataToNV( DG_USAGE_INFO_ITEMS_T info ) { - if ( FALSE == disinfectNVOps.hasDisCompleteDateBeenWrittenToNV ) - { - disinfectNVOps.hasDisCompleteDateBeenWrittenToNV = setDisinfectStatus( TRUE ); - } - if ( FALSE == disinfectNVOps.hasDisStatusBeenWrittenToNV ) { - disinfectNVOps.hasDisStatusBeenWrittenToNV = setLastDisinfectDate( USAGE_INFO_HEAT_DISINFECT, getRTCTimestamp() ); + disinfectNVOps.hasDisStatusBeenWrittenToNV = setLastDisinfectDate( info, getRTCTimestamp() ); } } Index: firmware/App/Modes/ModeStandby.c =================================================================== diff -u -r5d3e28c19ae10a99a5ce1fc5c010ac944975a6a1 -receb190a5d66fdbee779478ac8bb50a846ed9241 --- firmware/App/Modes/ModeStandby.c (.../ModeStandby.c) (revision 5d3e28c19ae10a99a5ce1fc5c010ac944975a6a1) +++ firmware/App/Modes/ModeStandby.c (.../ModeStandby.c) (revision eceb190a5d66fdbee779478ac8bb50a846ed9241) @@ -8,7 +8,7 @@ * @file ModeStandby.c * * @author (last) Dara Navaei -* @date (last) 27-Sep-2022 +* @date (last) 21-Dec-2022 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 @@ -62,6 +62,7 @@ static BOOL pendingStartDGHeatDisinfectRequest; ///< Flag indicating HD has requested DG start heat disinfect. static BOOL pendingStartDGChemicalDisinfectRequest; ///< Flag indicating HD has requested DG start chemical disinfect. static BOOL pendingStartDGHeatDisinfectActiveCoolRequest; ///< Flag indicating HD has requested DG start heat disinfect active cool. +static BOOL pendingStartDGChemicalDisinfectFlushRequest; ///< Flag indicating HD has requested DG start chemical disinfect flush. static OVERRIDE_U32_T filterFlushTimePeriod = { FILTER_FLUSH_TIME_MS, FILTER_FLUSH_TIME_MS, 0, 0 }; ///< Filter flush time period in ms. @@ -203,19 +204,16 @@ else if ( TRUE == pendingStartDGFlushRequest ) { pendingStartDGFlushRequest = FALSE; - requestNewOperationMode( DG_MODE_FLUS ); } else if ( TRUE == pendingStartDGHeatDisinfectRequest ) { pendingStartDGHeatDisinfectRequest = FALSE; - requestNewOperationMode( DG_MODE_HEAT ); } else if ( TRUE == pendingStartDGChemicalDisinfectRequest ) { pendingStartDGChemicalDisinfectRequest = FALSE; - requestNewOperationMode( DG_MODE_CHEM ); } else if ( TRUE == pendingStartDGHeatDisinfectActiveCoolRequest ) @@ -224,6 +222,11 @@ requestNewOperationMode( DG_MODE_HCOL ); } + else if ( TRUE == pendingStartDGChemicalDisinfectFlushRequest ) + { + pendingStartDGChemicalDisinfectFlushRequest = FALSE; + requestNewOperationMode( DG_MODE_CHFL ); + } return state; } @@ -319,6 +322,7 @@ { stopSampleWaterRequest = FALSE; setValveState( VSP, VALVE_STATE_CLOSED ); + setValveState( VPI, VALVE_STATE_CLOSED ); state = DG_STANDBY_MODE_STATE_FLUSH_FILTER_IDLE; } @@ -639,6 +643,61 @@ /*********************************************************************//** * @brief + * The startDGChemicalDisinfectFlush function starts chemical disinfect flush mode. + * @details Inputs: standbyState + * @details Outputs: none + * @return: TRUE if the switch was successful + *************************************************************************/ +BOOL startDGChemicalDisinfectFlush( void ) +{ + BOOL status = FALSE; + DG_CMD_RESPONSE_T cmdResponse; + + cmdResponse.commandID = DG_CMD_START_CHEM_DISINFECT_FLUSH; + cmdResponse.rejected = FALSE; + cmdResponse.rejectCode = DG_CMD_REQUEST_REJECT_REASON_NONE; + + // If DG is in standby mode and the standby mode is in Idle, request chemical disinfect flush + // Chemical disinfect flush cannot be run in solo mode because the user has to confirm that the acid is inserted or removed + if ( ( DG_MODE_STAN == getCurrentOperationMode() ) && ( DG_STANDBY_MODE_STATE_IDLE == standbyState ) ) + { + OPN_CLS_STATE_T concCap = getSwitchStatus( CONCENTRATE_CAP ); + OPN_CLS_STATE_T diaCap = getSwitchStatus( DIALYSATE_CAP ); + +#ifndef _RELEASE_ + if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_DISABLE_CAPS_MONITOR ) ) + { + concCap = STATE_CLOSED; + diaCap = STATE_CLOSED; + } +#endif + + // When chemical disinfect flush is about to be started, both caps must be closed + if ( ( STATE_OPEN == concCap ) || ( STATE_OPEN == diaCap ) ) + { + cmdResponse.rejected = TRUE; + cmdResponse.rejectCode = ( STATE_OPEN == getSwitchStatus( DIALYSATE_CAP ) ? REQUEST_REJECT_REASON_DG_DIALYSATE_CAP_OPEN : + REQUEST_REJECT_REASON_DG_CONCENTRATE_CAP_OPEN ); + } + else + { + pendingStartDGChemicalDisinfectFlushRequest = TRUE; + status = TRUE; + } + } + else + { + cmdResponse.rejected = TRUE; + cmdResponse.rejectCode = REQUEST_REJECT_REASON_DG_NOT_IN_STANDBY_IDLE_STATE; + } + + sendCommandResponseMsg( &cmdResponse ); + + return status; +} + +/*********************************************************************//** + * @brief * The getCurrentStandbyState function returns the current state of standby mode. * @details Inputs: standbyState * @details Outputs: none Index: firmware/App/Modes/ModeStandby.h =================================================================== diff -u -r0b121a7c92a8d86f8369b7094b0bce21389f3747 -receb190a5d66fdbee779478ac8bb50a846ed9241 --- firmware/App/Modes/ModeStandby.h (.../ModeStandby.h) (revision 0b121a7c92a8d86f8369b7094b0bce21389f3747) +++ firmware/App/Modes/ModeStandby.h (.../ModeStandby.h) (revision eceb190a5d66fdbee779478ac8bb50a846ed9241) @@ -7,8 +7,8 @@ * * @file ModeStandby.h * -* @author (last) Dara Navaei -* @date (last) 06-Nov-2021 +* @author (last) Steve Jarpe +* @date (last) 30-Nov-2022 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 @@ -56,7 +56,8 @@ BOOL testSetFilterFlushTimePeriodOverride( U32 value ); BOOL testResetFilterFlushTimePeriodOverride( void ); -BOOL startDGChemicalDisinfect( void ); // HD start chemical disinfect mode +BOOL startDGChemicalDisinfect( void ); // HD start chemical disinfect mode +BOOL startDGChemicalDisinfectFlush( void ); // HD start chemical disinfect flush mode /**@}*/ Index: firmware/App/Modes/OperationModes.c =================================================================== diff -u -r5e3a46112ebab361a33b9f7cadb619eb12b44c0f -receb190a5d66fdbee779478ac8bb50a846ed9241 --- firmware/App/Modes/OperationModes.c (.../OperationModes.c) (revision 5e3a46112ebab361a33b9f7cadb619eb12b44c0f) +++ firmware/App/Modes/OperationModes.c (.../OperationModes.c) (revision eceb190a5d66fdbee779478ac8bb50a846ed9241) @@ -7,8 +7,8 @@ * * @file OperationModes.c * -* @author (last) Dara Navaei -* @date (last) 12-Oct-2022 +* @author (last) Steve Jarpe +* @date (last) 20-Dec-2022 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 @@ -19,6 +19,7 @@ #include "MessageSupport.h" #include "ModeChemicalDisinfect.h" +#include "ModeChemicalDisinfectFlush.h" #include "ModeDrain.h" #include "ModeFault.h" #include "ModeFill.h" @@ -122,6 +123,7 @@ initHeatDisinfectMode(); initChemicalDisinfectMode(); initHeatDisinfectActiveCoolMode(); + initChemicalDisinfectFlushMode(); } /*********************************************************************//** @@ -210,6 +212,10 @@ currentSubMode = execHeatDisinfectActiveCoolMode(); break; + case DG_MODE_CHFL: + currentSubMode = execChemicalDisinfectFlushMode(); + break; + default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_OP_MODES_INVALID_MODE_STATE, (U32)currentMode ) currentMode = DG_MODE_FAUL; @@ -365,6 +371,9 @@ case DG_MODE_HCOL: currentSubMode = transitionToHeatDisinfectActiveCoolMode(); break; + case DG_MODE_CHFL: + currentSubMode = transitionToChemicalDisinfectFlushMode(); + break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_OP_MODES_INVALID_MODE_TO_TRANSITION_TO, (U32)newMode ) break; Index: firmware/App/Services/AlarmMgmt.c =================================================================== diff -u -rc9b807ca8f5a4c3d8a5b390d54c43afd217b39d3 -receb190a5d66fdbee779478ac8bb50a846ed9241 --- firmware/App/Services/AlarmMgmt.c (.../AlarmMgmt.c) (revision c9b807ca8f5a4c3d8a5b390d54c43afd217b39d3) +++ firmware/App/Services/AlarmMgmt.c (.../AlarmMgmt.c) (revision eceb190a5d66fdbee779478ac8bb50a846ed9241) @@ -65,6 +65,7 @@ static void activateAlarm( ALARM_ID_T alarm ); static void publishAlarmInfo( void ); static void alarmUserNotify( void ); +static BOOL isTransitionToFaultRequired( void ); /*********************************************************************//** * @brief @@ -125,7 +126,7 @@ alarmConditionIsActive[ alarm ] = TRUE; // If alarm is a DG fault, request transition to fault mode - if ( ( TRUE == ALARM_TABLE[ alarm ].alarmIsDGFault ) && ( getCurrentOperationMode() != DG_MODE_SERV ) ) + if ( ( TRUE == ALARM_TABLE[ alarm ].alarmIsDGFault ) && ( TRUE == isTransitionToFaultRequired() ) ) { requestNewOperationMode( DG_MODE_FAUL ); } @@ -357,6 +358,36 @@ } } +/************************************************************************* + * @brief + * The isTransitionToFaultRequired function checks whether the alarm management + * should request a transition to fault mode immediately or it should be deferred + * @details Inputs: none + * @details Outputs: none + * @param none + * @return TRUE if transition to fault is required otherwise, FALSE + *************************************************************************/ +static BOOL isTransitionToFaultRequired( void ) +{ + BOOL status = TRUE; + DG_OP_MODE_T opMode = getCurrentOperationMode(); + + switch( opMode ) + { + case DG_MODE_FLUS: + case DG_MODE_HEAT: + case DG_MODE_CHEM: + case DG_MODE_SERV: + status = FALSE; + break; + + default: + // NOTE: Do nothing for the other modes + break; + } + + return status; +} /************************************************************************* * TEST SUPPORT FUNCTIONS Index: firmware/App/Services/AlarmMgmtSWFaults.h =================================================================== diff -u -r73c6b6fb7bbb934277157ce1e218358c573a6af1 -receb190a5d66fdbee779478ac8bb50a846ed9241 --- firmware/App/Services/AlarmMgmtSWFaults.h (.../AlarmMgmtSWFaults.h) (revision 73c6b6fb7bbb934277157ce1e218358c573a6af1) +++ firmware/App/Services/AlarmMgmtSWFaults.h (.../AlarmMgmtSWFaults.h) (revision eceb190a5d66fdbee779478ac8bb50a846ed9241) @@ -8,7 +8,7 @@ * @file AlarmMgmtSWFaults.h * * @author (last) Dara Navaei -* @date (last) 12-Oct-2022 +* @date (last) 21-Dec-2022 * * @author (original) Quang Nguyen * @date (original) 20-May-2021 @@ -144,6 +144,9 @@ SW_FAULT_ID_NVDATA_MANAGEMENT_OPS_TIMEOUT, SW_FAULT_ID_NVDATA_RTC_RAM_OPS_FAILURE, SW_FAULT_ID_HEAT_DISINFECT_ACTIVE_COOL_INVALID_EXEC_STATE, // 115 + SW_FAULT_ID_DG_CHEM_DISINFECT_FLUSH_INVALID_EXEC_STATE, + SW_FAULT_ID_INVALID_PI_PROFILE_SELECTED, + SW_FAULT_ID_PI_CTRL_INVALID_STEP_LIMIT, NUM_OF_SW_FAULT_IDS } SW_FAULT_ID_T; Index: firmware/App/Services/SystemComm.c =================================================================== diff -u -r0b121a7c92a8d86f8369b7094b0bce21389f3747 -receb190a5d66fdbee779478ac8bb50a846ed9241 --- firmware/App/Services/SystemComm.c (.../SystemComm.c) (revision 0b121a7c92a8d86f8369b7094b0bce21389f3747) +++ firmware/App/Services/SystemComm.c (.../SystemComm.c) (revision eceb190a5d66fdbee779478ac8bb50a846ed9241) @@ -8,7 +8,7 @@ * @file SystemComm.c * * @author (last) Dara Navaei -* @date (last) 28-Oct-2022 +* @date (last) 21-Dec-2022 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 @@ -877,6 +877,10 @@ handleStartStopDGChemicalDisinfect( message ); break; + case MSG_ID_DG_START_STOP_CHEM_DISINFECT_FLUSH: + handleStartStopDGChemicalDisinfectFlush( message ); + break; + case MSG_ID_UI_DG_SET_RTC_REQUEST: handleUIClockSyncRequest( message ); break; @@ -909,6 +913,14 @@ handleStartStopDGHeatDisinfectActiveCool( message ); break; + case MSG_ID_REQUEST_CPLD_STATUS: + handleCpldStatusRequest( message ); + break; + + case MSG_ID_HD_REQUEST_DG_ALARMS: + handleResendAllAlarmsCommand( message ); + break; + // NOTE: This case must be last case MSG_ID_DG_TESTER_LOGIN_REQUEST: handleTesterLogInRequest( message ); @@ -1204,10 +1216,6 @@ handleSetDGUsageInfoRecord( message ); break; - case MSG_ID_HD_REQUEST_DG_ALARMS: - handleResendAllAlarmsCommand( message ); - break; - case MSG_ID_DG_SET_OP_MODE_REQUEST: handleTestSetOpModeRequest( message ); break; @@ -1240,6 +1248,18 @@ handleTestDGNVRecordCRCOverride( message ); break; + case MSG_ID_DG_RO_FEEDBACK_VOLTAGE_OVERRIDE: + handleTestDGROPumpFeedbackVoltageOverrideRequest( message ); + break; + + case MSG_ID_DG_DIALYSATE_FILL_INTEGRATED_VOLUME_OVERRIDE: + handleTestDGFillIntegratedVolumeOverrideRequest( message ); + break; + + case MSG_ID_FILL_MODE_DATA_PUBLISH_INTERVAL_OVERRIDE: + handleTestDGFillModeBroadcastOverrideRequest( message ); + break; + default: // TODO - unrecognized message ID received - ignore break; Index: firmware/App/Services/SystemCommMessages.c =================================================================== diff -u -rce659127f12060b21cb725240ff9ed919ab5f94d -receb190a5d66fdbee779478ac8bb50a846ed9241 --- firmware/App/Services/SystemCommMessages.c (.../SystemCommMessages.c) (revision ce659127f12060b21cb725240ff9ed919ab5f94d) +++ firmware/App/Services/SystemCommMessages.c (.../SystemCommMessages.c) (revision eceb190a5d66fdbee779478ac8bb50a846ed9241) @@ -8,21 +8,22 @@ * @file SystemCommMessages.c * * @author (last) Dara Navaei -* @date (last) 03-Nov-2022 +* @date (last) 21-Dec-2022 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 * ***************************************************************************/ -#include // for memcpy() +#include // for memcpy() #include "reg_system.h" #include "Accel.h" #include "Compatible.h" #include "ConcentratePumps.h" #include "ConductivitySensors.h" +#include "CPLD.h" #include "Fans.h" #include "FlowSensors.h" #include "FPGA.h" @@ -831,6 +832,35 @@ } /*********************************************************************//** + * @brief + * The handleDGScheduledRunsRequest function handles a request for DG + * scheduled runs information. + * @details Inputs: none + * @details Outputs: message handled, response constructed and queued for + * transmit. + * @return none + *************************************************************************/ +void handleDGScheduledRunsRequest( MESSAGE_T *message ) +{ + MESSAGE_T msg; + DG_SCHEDULED_RUN_RECORD_T scheduledService; + + // Get the service record. There are no arrays of service to check and also, raise no alarm since the service record + // has been already checked in POST + getNVRecord2Driver( GET_SRR_RECORD, (U08*)&scheduledService, sizeof( DG_SCHEDULED_RUN_RECORD_T ), 0, ALARM_ID_NO_ALARM ); + + // Create a message record + blankMessage( &msg ); + msg.hdr.msgID = MSG_ID_DG_SCHEDULED_RUNS_DATA; + msg.hdr.payloadLen = sizeof( U32 ) + sizeof( U32 ); + + // TODO this message is for Phase 1B. + + // Serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer + serializeMessage( msg, COMM_BUFFER_OUT_CAN_DG_2_UI, ACK_REQUIRED ); +} + +/*********************************************************************//** * @brief * The handleStartStopDGFlush function handles a request to start or stop * DG flush mode. @@ -1356,6 +1386,41 @@ } /*********************************************************************//** +* @brief +* The handleStartStopDGChemicalDisinfectFlush function handles a request to start +* or stop DG chemical disinfect flush mode. +* @details Inputs: none +* @details Outputs: message handled +* @param message a pointer to the message to handle +* @return none +*************************************************************************/ +BOOL handleStartStopDGChemicalDisinfectFlush( MESSAGE_T *message ) +{ + BOOL result = FALSE; + + if ( message->hdr.payloadLen == sizeof(BOOL) ) + { + BOOL startingDGChemicalDisinfectFlush; + + memcpy( &startingDGChemicalDisinfectFlush, message->payload, sizeof(BOOL) ); + + if ( TRUE == startingDGChemicalDisinfectFlush ) + { + result = startDGChemicalDisinfectFlush(); + } + else + { + result = stopChemicalDisinfectFlush(); + } + } + + // Respond to request + sendAckResponseMsg( (MSG_ID_T)message->hdr.msgID, COMM_BUFFER_OUT_CAN_DG_2_HD, result ); + + return result; +} + +/*********************************************************************//** * @brief * The handleTestSetOpModeRequest function handles a request to set the * DG operation mode. @@ -1437,40 +1502,6 @@ } /*********************************************************************//** - * @brief - * The handleHDRequestDGUsageInfo function handles a request for DG - * usage information. - * @details Inputs: none - * @details Outputs: message handled, response constructed and queued for - * transmit. - * @return none - *************************************************************************/ -void handleHDRequestDGUsageInfo( MESSAGE_T * message ) -{ - MESSAGE_T msg; - DG_USAGE_INFO_RECORD_T usageInfo; - U08 *payloadPtr = msg.payload; - - // Create a message record - blankMessage( &msg ); - msg.hdr.msgID = MSG_ID_DG_USAGE_DATA; - msg.hdr.payloadLen = sizeof( DG_USAGE_INFO_RECORD_T ); - - // Get the service record. There are no arrays of service to check and also, raise no alarm since the service record - // has been already checked in POST - getNVRecord2Driver( GET_USAGE_RECORD, (U08*)&usageInfo, sizeof( DG_USAGE_INFO_RECORD_T ), 0, ALARM_ID_NO_ALARM ); - - if ( 0 == message->hdr.payloadLen ) - { - // Fill message payload - memcpy( payloadPtr, &usageInfo, sizeof( DG_USAGE_INFO_RECORD_T ) ); - } - - // Serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer - serializeMessage( msg, COMM_BUFFER_OUT_CAN_DG_2_HD, ACK_REQUIRED ); -} - -/*********************************************************************//** * @brief * The handleServiceModeRequest function handles a request to enter service * mode. @@ -1510,6 +1541,40 @@ /*********************************************************************//** * @brief + * The handleHDRequestDGUsageInfo function handles a request for DG + * usage information. + * @details Inputs: none + * @details Outputs: message handled, response constructed and queued for + * transmit. + * @return none + *************************************************************************/ +void handleHDRequestDGUsageInfo( MESSAGE_T * message ) +{ + MESSAGE_T msg; + DG_USAGE_INFO_RECORD_T usageInfo; + U08 *payloadPtr = msg.payload; + + // Create a message record + blankMessage( &msg ); + msg.hdr.msgID = MSG_ID_DG_USAGE_DATA; + msg.hdr.payloadLen = sizeof( DG_USAGE_INFO_RECORD_T ); + + // Get the service record. There are no arrays of service to check and also, raise no alarm since the service record + // has been already checked in POST + getNVRecord2Driver( GET_USAGE_RECORD, (U08*)&usageInfo, sizeof( DG_USAGE_INFO_RECORD_T ), 0, ALARM_ID_NO_ALARM ); + + if ( 0 == message->hdr.payloadLen ) + { + // Fill message payload + memcpy( payloadPtr, &usageInfo, sizeof( DG_USAGE_INFO_RECORD_T ) ); + } + + // Serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer + serializeMessage( msg, COMM_BUFFER_OUT_CAN_DG_2_HD, ACK_REQUIRED ); +} + +/*********************************************************************//** + * @brief * The sendServiceModeResponse function sends out the DG response to a * UI request to go to service mode. * @details Inputs: none @@ -1592,7 +1657,35 @@ sendAckResponseMsg( (MSG_ID_T)message->hdr.msgID, COMM_BUFFER_OUT_CAN_DG_2_HD, status ); } +/*********************************************************************//** + * @brief + * The handleCpldStatusRequest function handles a CPLD Status request message. + * @details Inputs: none + * @details Outputs: message handled + * @param message a pointer to the message to handle + * @return none + *************************************************************************/ +void handleCpldStatusRequest( MESSAGE_T *message ) +{ + MESSAGE_T msg; + CPLD_STATUS_T payload; + U08 *payloadPtr = msg.payload; + // populate payload + getCPLDStatus( &payload ); + // create a message record + blankMessage( &msg ); + msg.hdr.msgID = MSG_ID_DG_CPLD_STATUS; + msg.hdr.payloadLen = sizeof( CPLD_STATUS_T ); + + // fill message payload + memcpy( payloadPtr, &payload, sizeof( CPLD_STATUS_T ) ); + + // serialize the message (w/ sync, CRC, and appropriate CAN padding) and add serialized message data to appropriate comm buffer + serializeMessage( msg, COMM_BUFFER_OUT_CAN_DG_BROADCAST, ACK_NOT_REQUIRED ); +} + + // *********************************************************************** // **************** Message Handling Helper Functions ******************** // *********************************************************************** @@ -4026,4 +4119,163 @@ sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); } +/*********************************************************************//** + * @brief + * The handleTestDrainPumpMeasuredCurrentOverride function handles a request + * to override the drain pump measured current + * @details Inputs: none + * @details Outputs: message handled + * @param message a pointer to the message to handle + * @return none + *************************************************************************/ +void handleTestDrainPumpMeasuredCurrentOverride( MESSAGE_T *message ) +{ + TEST_OVERRIDE_PAYLOAD_T payload; + BOOL result = FALSE; + + // verify payload length + if ( sizeof( TEST_OVERRIDE_PAYLOAD_T ) == message->hdr.payloadLen ) + { + memcpy( &payload, message->payload, sizeof( TEST_OVERRIDE_PAYLOAD_T ) ); + if ( FALSE == payload.reset ) + { + result = testSetDrainPumpMeasuredCurrentOverride( payload.state.f32 ); + } + else + { + result = testResetDrainPumpMeasuredCurrentOverride(); + } + } + + // respond to request + sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); +} + +/*********************************************************************//** + * @brief + * The handleTestGenIdlePublishIntervalOverride function handles a request + * to override the gen idle state publish interval + * @details Inputs: none + * @details Outputs: message handled + * @param message a pointer to the message to handle + * @return none + *************************************************************************/ +void handleTestGenIdlePublishIntervalOverride( MESSAGE_T * message ) +{ + TEST_OVERRIDE_PAYLOAD_T payload; + BOOL result = FALSE; + + // verify payload length + if ( sizeof( TEST_OVERRIDE_PAYLOAD_T ) == message->hdr.payloadLen ) + { + memcpy( &payload, message->payload, sizeof( TEST_OVERRIDE_PAYLOAD_T ) ); + if ( FALSE == payload.reset ) + { + result = testSetGenIdleSubstatesPublishIntervalOverride( payload.state.u32 ); + } + else + { + result = testResetGenIdleSubstatesPublishIntervalOverride(); + } + } + + // respond to request + sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); +} + +/*********************************************************************//** + * @brief + * The handleTestDGROPumpFeedbackVoltageOverrideRequest function handles a request + * to override the gen idle state publish interval + * @details Inputs: none + * @details Outputs: message handled + * @param message a pointer to the message to handle + * @return none + *************************************************************************/ +void handleTestDGROPumpFeedbackVoltageOverrideRequest( MESSAGE_T * message ) +{ + TEST_OVERRIDE_PAYLOAD_T payload; + BOOL result = FALSE; + + // verify payload length + if ( sizeof( TEST_OVERRIDE_PAYLOAD_T ) == message->hdr.payloadLen ) + { + memcpy( &payload, message->payload, sizeof( TEST_OVERRIDE_PAYLOAD_T ) ); + if ( FALSE == payload.reset ) + { + result = testSetROPumpMeasuredFeedbackDutyCycleOverride( payload.state.f32 ); + } + else + { + result = testResetROPumpMeasuredFeedbackDutyCycleOverride(); + } + } + + // respond to request + sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); +} + +/*********************************************************************//** + * @brief + * The handleTestDGFillModeBroadcastOverrideRequest function handles a request + * to override the gen idle state publish interval + * @details Inputs: none + * @details Outputs: message handled + * @param message a pointer to the message to handle + * @return none + *************************************************************************/ +void handleTestDGFillModeBroadcastOverrideRequest( MESSAGE_T * message ) +{ + TEST_OVERRIDE_PAYLOAD_T payload; + BOOL result = FALSE; + + // verify payload length + if ( sizeof( TEST_OVERRIDE_PAYLOAD_T ) == message->hdr.payloadLen ) + { + memcpy( &payload, message->payload, sizeof( TEST_OVERRIDE_PAYLOAD_T ) ); + if ( FALSE == payload.reset ) + { + result = testSetFillModeDataPublishIntervalOverride( payload.state.u32 ); + } + else + { + result = testResetFillModeDataPublishIntervalOverride(); + } + } + + // respond to request + sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); +} +/*********************************************************************//** + * @brief + * The handleTestDGFillIntegratedVolumeOverrideRequest function handles a request + * to override the gen idle state publish interval + * @details Inputs: none + * @details Outputs: message handled + * @param message a pointer to the message to handle + * @return none + *************************************************************************/ +void handleTestDGFillIntegratedVolumeOverrideRequest( MESSAGE_T * message ) +{ + TEST_OVERRIDE_PAYLOAD_T payload; + BOOL result = FALSE; + + // verify payload length + if ( sizeof( TEST_OVERRIDE_PAYLOAD_T ) == message->hdr.payloadLen ) + { + memcpy( &payload, message->payload, sizeof( TEST_OVERRIDE_PAYLOAD_T ) ); + if ( FALSE == payload.reset ) + { + result = testSetIntegratedVolumeOverride( payload.state.u32 ); + } + else + { + result = testResetIntegratedVolumeOverride(); + } + } + + // respond to request + sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); +} + /**@}*/ Index: firmware/App/Services/SystemCommMessages.h =================================================================== diff -u -r0b121a7c92a8d86f8369b7094b0bce21389f3747 -receb190a5d66fdbee779478ac8bb50a846ed9241 --- firmware/App/Services/SystemCommMessages.h (.../SystemCommMessages.h) (revision 0b121a7c92a8d86f8369b7094b0bce21389f3747) +++ firmware/App/Services/SystemCommMessages.h (.../SystemCommMessages.h) (revision eceb190a5d66fdbee779478ac8bb50a846ed9241) @@ -8,7 +8,7 @@ * @file SystemCommMessages.h * * @author (last) Dara Navaei -* @date (last) 28-Oct-2022 +* @date (last) 21-Dec-2022 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 @@ -166,6 +166,9 @@ // MSG_ID_DG_START_STOP_HEAT_DISINFECT_ACTIVE_COOL void handleStartStopDGHeatDisinfectActiveCool( MESSAGE_T* message ); +// MSG_ID_REQUEST_CPLD_STATUS +void handleCpldStatusRequest( MESSAGE_T *message ); + // *********** public test support message functions ********** // MSG_TESTER_LOG_IN @@ -340,6 +343,9 @@ // MSG_ID_DG_START_STOP_CHEM_DSINFECT BOOL handleStartStopDGChemicalDisinfect( MESSAGE_T *message ); +// MSG_ID_DG_START_STOP_CHEM_DISINFECT_FLUSH +BOOL handleStartStopDGChemicalDisinfectFlush( MESSAGE_T *message ); + // MSG_ID_DG_OP_MODE_PUBLISH_INTERVAL_OVERRIDE void handleSetDGOpModeBroadcastIntervalOverrideRequest( MESSAGE_T *message ); @@ -407,6 +413,12 @@ BOOL handleSetHDOperationMode( MESSAGE_T *message ); // MSG_ID_DG_DRAIN_PUMP_CURRENT_OVERRIDE +void handleTestDrainPumpMeasuredCurrentOverride( MESSAGE_T *message ); + +// MSG_ID_DG_GEN_IDLE_PUBLISH_INTERVAL_OVERRIDE +void handleTestGenIdlePublishIntervalOverride( MESSAGE_T * message ); + +// MSG_ID_DG_DRAIN_PUMP_CURRENT_OVERRIDE void handleTestDGDrainPumpCurrentOverrideRequest( MESSAGE_T *message ); // MSG_ID_DG_DRAIN_PUMP_DIRECTION_OVERRIDE @@ -427,7 +439,15 @@ // MSG_ID_DG_CONC_PUMP_PARK_COMMAND void handleTestDGConcPumpParkCommandRequest( MESSAGE_T *message ); +// MSG_ID_FILL_MODE_DATA_PUBLISH_INTERVAL_OVERRIDE +void handleTestDGFillModeBroadcastOverrideRequest( MESSAGE_T *message ); +// MSG_ID_DG_RO_FEEDBACK_VOLTAGE_OVERRIDE +void handleTestDGROPumpFeedbackVoltageOverrideRequest( MESSAGE_T *message ); + +// MSG_ID_DG_DIALYSATE_FILL_INTEGRATED_VOLUME_OVERRIDE +void handleTestDGFillIntegratedVolumeOverrideRequest( MESSAGE_T *message ); + /**@}*/ #endif