/************************************************************************** * * Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. * * THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN * WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. * * @file ModeHeatDisinfect.c * * @author (last) Quang Nguyen * @date (last) 01-Sep-2020 * * @author (original) Sean * @date (original) 20-Apr-2020 * ***************************************************************************/ #include "ConductivitySensors.h" #include "DrainPump.h" #include "Heaters.h" #include "LoadCell.h" #include "ModeHeatDisinfect.h" #include "OperationModes.h" #include "Pressures.h" #include "ROPump.h" #include "TemperatureSensors.h" #include "Timers.h" #include "UVReactors.h" #include "Valves.h" /** * @addtogroup DGHeatDisinfectMode * @{ */ // ********** private definitions ********** // General defines #define MAX_ALLOWED_STATE_TRIALS 1 ///< Max allowed trials on a state. This is general among all the states. // Start state defines #define MIN_INLET_PRESSURE_PSI 30.0 ///< Minimum water inlet pressure in psi. #define MAX_START_STATE_TEMP_SENSORS_DIFF_C 1.0 ///< Max start state TDi and TRo difference tolerance in C. // Drain R1 & R2 states defines #define DRAIN_PUMP_TARGET_RPM 2500 ///< Drain pump target RPM during drain. #define DRAIN_RESERVOIRS_TIME_OUT_MS ( 60 * MS_PER_SECOND ) ///< Drain reservoirs time out in milliseconds. #define EMPTY_RESERVOIRS_WEIGHT_GRAMS 50 ///< The weight of an empty reservoir. //TODO Change this value to actual value // Flush drain path state defines #define FLUSH_DRAIN_WAIT_TIME_MS ( 60 * MS_PER_SECOND ) ///< Flush Drain path wait time in milliseconds. #define MIN_INLET_TEMPERATURE_C 25.0 ///< Minimum water inlet temperature in C. #define MIN_INLET_CONDUCTIVITY_S_PER_CM 0.0 ///< Minimum water inlet conductivity in mS/cm? TODO find out the real value // Flush circulation path state defines #define RO_PUMP_TARGET_FLOW_RATE_LPM 0.8 ///< RO pump target flow rate in L/min. #define MAX_RO_PUMP_PRESSURE_PSI 130 ///< Maximum RO pump pressure in psi. #define FLUSH_CICRCULATION_WAIT_TIME_MS ( 30 * MS_PER_SECOND ) ///< Flush circulation path wait time in milliseconds. #define MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C 3.0 ///< Maximum flush circulation temperature difference tolerance in C. // ********** private data ********** static DG_HEAT_DISINFECT_STATE_T heatDisinfectState = DG_HEAT_DISINFECT_STATE_START; ///< Currently active heat disinfect state. static U32 stateTimer = 0; ///< Heat disinfect state timer to be used in different states. static U32 stateTrialCounter = 0; ///< Heat disinfect state trial counter to be used for retries in different states. static BOOL areTempSensorsInRange = FALSE; ///< Heat disinfect temperature sensors in/out range flag. /// 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 BOOL isThisLastDrain = FALSE; // ********** private function prototypes ********** static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectStartState( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectDrainR1State( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectDrainR2State( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFlushDrainState( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFlushCirculationState( void ); static void resetActuators( void ); static void setModeToFailed( DG_HEAT_DISINFECT_STATE_T failedState ); /*********************************************************************//** * @brief * The initHeatDisinfectMode function initializes the heat disinfect mode module. * @details Inputs: none TODO update as we go * @details Outputs: Initialized heat disinfect mode module * @return none *************************************************************************/ void initHeatDisinfectMode( void ) { heatDisinfectState = DG_HEAT_DISINFECT_STATE_START; stateTimer = 0; isThisLastDrain = FALSE; stateTrialCounter = 0; areTempSensorsInRange = FALSE; } /*********************************************************************//** * @brief * The transitionToHeatDisinfectMode function prepares for transition to * heat disinfect mode. * @details Inputs: none * @details Outputs: none * @return none *************************************************************************/ void transitionToHeatDisinfectMode( void ) { initHeatDisinfectMode(); } /*********************************************************************//** * @brief * The execHeatDisinfectMode function executes the heat disinfect mode * state machine. * @details Inputs: heatDisinfectState * @details Outputs: heatDisinfectState * @return current state *************************************************************************/ U32 execHeatDisinfectMode( void ) { checkInletPressureFault(); // TODO what is this? switch ( heatDisinfectState ) { case DG_HEAT_DISINFECT_STATE_START: heatDisinfectState = handleHeatDisinfectStartState(); break; case DG_HEAT_DISINFECT_STATE_DRAIN_R1: heatDisinfectState = handleHeatDisinfectDrainR1State(); break; case DG_HEAT_DISINFECT_STATE_DRAIN_R2: heatDisinfectState = handleHeatDisinfectDrainR2State(); break; case DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN: heatDisinfectState = handleHeatDisinfectFlushDrainState(); break; case DG_HEAT_DISINFECT_STATE_FLUSH_CIRCULATION: heatDisinfectState = handleHeatDisinfectFlushCirculationState(); break; case DG_HEAT_DISINFECT_STATE_FLUSH_R1_AND_R2: break; case DG_HEAT_DISINFECT_STATE_FLUSH_R2_AND_DRAIN_R2: break; case DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN_R2: break; case DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN_R1: break; case DG_HEAT_DISINFECT_STATE_FILL_WITH_WATER: break; case DG_HEAT_DISINFECT_STATE_DISINFECT_R1_TO_R2: break; case DG_HEAT_DISINFECT_STATE_FILL_R2_WITH_HOT_WATER: break; case DG_HEAT_DISINFECT_STATE_DISINFECT_R2_TO_R1: break; case DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R1: break; case DG_HEAT_DISINFECT_STATE_MIX_DRAIN_R2: break; case DG_HEAT_DISINFECT_STATE_RINSE_R1_TO_R2: break; case DG_HEAT_DISINFECT_STATE_RINSE_R2_TO_R1_AND_DRAIN_R1: break; case DG_HEAT_DISINFECT_STATE_RINSE_CIRCULATION: break; case DG_HEAT_DISINFECT_STATE_COMPLETE: break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_HEAT_DISINFECT_INVALID_EXEC_STATE, heatDisinfectState ) heatDisinfectState = DG_HEAT_DISINFECT_STATE_START; break; } return heatDisinfectState; } /*********************************************************************//** * @brief * The getCurrentHeatDisinfectState function returns the current state of * the heat disinfect mode. * @details Inputs: heatDisinfectState * @details Outputs: none * @return the current state of heat disinfect mode. *************************************************************************/ DG_HEAT_DISINFECT_STATE_T getCurrentHeatDisinfectState( void ) { return heatDisinfectState; } /*********************************************************************//** * @brief * The stopDGHeatDisinfect function stops heat disinfect mode. * @details Inputs: heatDisinfectionState * @details Outputs: heatDisinfectionState * @return none *************************************************************************/ void stopDGHeatDisinfect( void ) { heatDisinfectState = DG_HEAT_DISINFECT_STATE_COMPLETE; requestNewOperationMode( DG_MODE_STAN ); } // ********** private function prototypes ********** /*********************************************************************//** * @brief * The handleHeatDisinfectStartState function handles the heat disinfect * start state. * @details Inputs: none * @details Outputs: none * @return next state of the heat disinfect state machine *************************************************************************/ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectStartState( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_DRAIN_R1; // Set all the actuators to reset and deenergized state resetActuators(); F32 ppiPressure = getMeasuredDGPressure( PRESSURE_SENSOR_RO_PUMP_INLET ); F32 TDiTemp = getTemperatureValue( TEMPSENSORS_INLET_DIALYSATE ); F32 TRoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_REDUNDANT ); // If the inlet pressure is less than the threshold or TDi and TRo difference is greater than 1 C, the cycle // should be canceled if ( ppiPressure < MIN_INLET_PRESSURE_PSI && fabs( TDiTemp - TRoTemp ) > MAX_START_STATE_TEMP_SENSORS_DIFF_C ) { setModeToFailed( state ); } else { // Close VPi to prevent wasting water setValveState( VPI, VALVE_STATE_CLOSED ); // Set the actuators to drain R1 setValveState( VRD, VALVE_STATE_R1_C_TO_NC ); setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); stateTimer = getMSTimerCount(); } return state; } /*********************************************************************//** * @brief * The handleHeatDisinfectDrainR1State function handles the heat disinfect * drain R1 state. * @details Inputs: stateTimer * @details Outputs: stateTimer * @return next state of the heat disinfect state machine *************************************************************************/ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectDrainR1State( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_DRAIN_R1; F32 R1Weight = getLoadCellFilteredWeight( LOAD_CELL_A1 ); if ( R1Weight < EMPTY_RESERVOIRS_WEIGHT_GRAMS ) { // Set the actuators to drain R2. // NOTE: Drain pump is already on and VDr is already on drain setValveState( VRD, VALVE_STATE_R2_C_TO_NO ); stateTimer = getMSTimerCount(); state = DG_HEAT_DISINFECT_STATE_DRAIN_R2; } else if ( didTimeout( stateTimer, DRAIN_RESERVOIRS_TIME_OUT_MS ) ) { setModeToFailed( state ); } return state; } /*********************************************************************//** * @brief * The handleHeatDisinfectDrainR2State function handles the heat disinfect * drain R2 state. * @details Inputs: stateTimer * @details Outputs: stateTimer * @return next state of the heat disinfect state machine *************************************************************************/ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectDrainR2State( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_DRAIN_R2; F32 R2Weight = getLoadCellFilteredWeight( LOAD_CELL_A2 ); if ( R2Weight < EMPTY_RESERVOIRS_WEIGHT_GRAMS ) { if ( isThisLastDrain ) { //TODO set the rest of the actuators state = DG_HEAT_DISINFECT_STATE_RINSE_CIRCULATION; } else { stateTrialCounter = 0; setValveState( VPI, VALVE_STATE_OPEN ); stateTimer = getMSTimerCount(); state = DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN; } } else if ( didTimeout( stateTimer, DRAIN_RESERVOIRS_TIME_OUT_MS ) ) { setModeToFailed( state ); } return state; } /*********************************************************************//** * @brief * The handleHeatDisinfectFlushDrainState function handles the heat disinfect * flush drain state. * @details Inputs: stateTimer, stateTrialCounter * @details Outputs: stateTimer, stateTrialCounter * @return next state of the heat disinfect state machine *************************************************************************/ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFlushDrainState( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_FLUSH_DRAIN; // Check if flush time has elapsed if ( didTimeout( stateTimer, FLUSH_DRAIN_WAIT_TIME_MS ) ) { // 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 ) > MIN_INLET_CONDUCTIVITY_S_PER_CM ) { setValveState( VPD, VALVE_STATE_DRAIN_C_TO_NC ); setROPumpTargetFlowRate( RO_PUMP_TARGET_FLOW_RATE_LPM, MAX_RO_PUMP_PRESSURE_PSI ); stateTimer = getMSTimerCount(); stateTrialCounter = 0; state = DG_HEAT_DISINFECT_STATE_FLUSH_CIRCULATION; } // If the failure is still in range, reset the timer and start over else if ( ++stateTrialCounter < MAX_ALLOWED_STATE_TRIALS ) { stateTimer = getMSTimerCount(); } // Couldn't get a good water sample after a couple of trials and the disinfection cycle failed else { setModeToFailed( state ); } } return state; } /*********************************************************************//** * @brief * The handleHeatDisinfectFlushCirculationState function handles the heat * disinfect flush circulation state. * @details Inputs: stateTimer, stateTrialCounter * @details Outputs: stateTimer, stateTrialCounter * @return next state of the heat disinfect state machine *************************************************************************/ static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFlushCirculationState( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_FLUSH_CIRCULATION; // Check if the flush circulation time has elapsed and the temperature sensors are not in range yet if ( didTimeout( stateTimer, FLUSH_CICRCULATION_WAIT_TIME_MS ) && FALSE == areTempSensorsInRange ) { F32 TPmTemp = 0; //TODO add TPm later. This is the new temp sensor of the coldest spot F32 TPoTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); F32 TD1Temp = getTemperatureValue( TEMPSENSORS_CONDUCTIVITY_SENSOR_1 ); F32 TD2Temp = getTemperatureValue( TEMPSENSORS_CONDUCTIVITY_SENSOR_2 ); F32 avgTemp = ( TPmTemp + TPoTemp + TD1Temp + TD2Temp ) / 3.0; //TODO change the number to 4 once the new sensor has been added BOOL isTPmOut = FALSE; //TODO change this calculations once TPm is added BOOL isTPoOut = fabs( TPoTemp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C; BOOL isTD1Out = fabs( TD1Temp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C; BOOL isTD2Out = fabs( TD2Temp - avgTemp ) > MAX_FLUSH_CIRC_TEMP_SENSOR_DIFF_C; // Check if any of the temperature sensors are out of tolerance if( isTPmOut || isTPoOut || isTD1Out || isTD2Out ) { // Check if we have exceeded the number of trials. If not, try another time if ( ++stateTrialCounter < MAX_ALLOWED_STATE_TRIALS ) { stateTimer = getMSTimerCount(); } // State failed. Cancel heat disinfect mode else { setModeToFailed( state ); } } else { areTempSensorsInRange = TRUE; stateTimer = getMSTimerCount(); // TODO Turn on the composite pumps and wait for 30 seconds } } // Only start the concentrate pumps if the temperature sensors are in range if ( areTempSensorsInRange ) { // TODO: enable the timeout once the concentrate pumps are available. //if ( didTimeout( stateTimer, FLUSH_CICRCULATION_WAIT_TIME_MS ) ) if ( TRUE ) { state = DG_HEAT_DISINFECT_STATE_COMPLETE; } } return state; } /*********************************************************************//** * @brief * The resetActuators function sets all the actuators to reset and * deenergized state. * @details Inputs: none * @details Outputs: none * @return none *************************************************************************/ static void resetActuators( void ) { // UV reactors will not be used in the heat disinfection since their operating temperature // range is below 85C and they might be damaged by the high temperature. turnOffUVReactor( INLET_UV_REACTOR ); turnOffUVReactor( OUTLET_UV_REACTOR ); // Deenergize all the valves setValveState( VPI, VALVE_STATE_OPEN ); setValveState( VBF, VALVE_STATE_CLOSED ); setValveState( VSP, VALVE_STATE_CLOSED ); setValveState( VPD, VALVE_STATE_OPEN_C_TO_NO ); 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 ); setValveState( VRD, VALVE_STATE_R2_C_TO_NO ); setValveState( VRI, VALVE_STATE_R1_C_TO_NO ); setValveState( VRF, VALVE_STATE_R2_C_TO_NO ); //TODO add the composition pumps signalROPumpHardStop(); signalDrainPumpHardStop(); stopPrimaryHeater(); stopTrimmerHeater(); } /*********************************************************************//** * @brief * The setModeToFailed function sets the heat disinfect mode to failed * by changing the state to complete and calling another function to set * the actuators to reset state. * @details Inputs: stateTrialCounter, stateTimer, areTempSensorsInRange, * heatDisinfectState TODO add more variables if needed * @details Outputs: none * @return none *************************************************************************/ static void setModeToFailed( DG_HEAT_DISINFECT_STATE_T failedState ) { // Reset all the variables stateTrialCounter = 0; stateTimer = 0; areTempSensorsInRange = FALSE; heatDisinfectState = DG_HEAT_DISINFECT_STATE_COMPLETE; // Reset the actuators before alarming resetActuators(); SET_ALARM_WITH_1_U32_DATA( ALARM_ID_DG_HEAT_DISINFECT_CYCLE_FAILED, failedState ) } /**@}*/