/************************************************************************** * * 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) Sean Nash * @date (last) 26-May-2020 * * @author (original) Sean * @date (original) 20-Apr-2020 * ***************************************************************************/ #include "Timers.h" #include "ModeHeatDisinfect.h" #include "OperationModes.h" #include "Valves.h" #include "Heaters.h" #include "DrainPump.h" #include "LoadCell.h" #include "ROPump.h" #include "TemperatureSensors.h" #include "UVReactors.h" //TODO control composition pumps /** * @addtogroup DGHeatDisinfectMode * @{ */ // ********** private definitions ********** #define HEAT_DISINFECT_TARGET_TEMPERATURE 85U ///< Heat disinfection target temperature #define MAX_TPO_AND_TDI_SENSORS_DIFFERENCE 1U #define MAX_TEMPERATURE_DEVIATION_FROM_TARGET 4U #define HEAT_DISINFECT_EVAC_RECIRC_PATH_TIME_MS 4000U //TODo do we need this? #define HEAT_DISINFECT_RECIRC_PATH_TIME_MS 120000U #define HEAT_DISINFECT_FLUSH_TIME_MINS 3U #define HEAT_DISINFECT_OVERALL_TIME_MINS 50U #define DRAIN_PUMP_TARGET_DELTA_PRESSURE 0U #define DRAIN_PUMP_TARGET_RPM 2800U #define RO_PUMP_TARGET_FLOW_RATE_LPM 0.9 #define FULL_RESERVOIRS_WEIGHT_GRAMS 2000U #define EMPTY_RESERVOIRS_WEIGHT_GRAMS 0U // ********** private data ********** /// Heat disinfect evacuate states typedef enum evacuate_path { EVACUATE_OFF_STATE = 0, EVACUATE_RECIRC_PATH_STATE, EVACUATE_RESERVOIR_1_STATE, EVACUATE_RESERVOIR_2_STATE, NUM_OF_EVACUTE_STATES } HEAT_DISINFECT_EVACUTE_STATE_T; //TODO do we need this enum? static HEAT_DISINFECT_EVACUTE_STATE_T heatDisinfectEvacState = EVACUATE_OFF_STATE; static DG_HEAT_DISINFECT_STATE_T heatDisinfectionState = DG_HEAT_DISINFECT_STATE_START; ///< Currently active heat disinfect state. static U32 heatDisinfectElapsedTime; static U32 stateTimer; // ********** private function prototypes ********** static void stopActuators( void ); static BOOL isTemperatureInRange( void ); static void execEvacuateFluidPath( void ); // TODO may not be needed (internal state machine) static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectStart( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectEvacDialysateRecircPath( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectEvacDialysateReservoir1( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectEvacDialysateReservoir2( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFillWithWater( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectHeatWater( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectRecirculationPath( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectReservoir1To2( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectReservoir2To1( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectDrainPath( void ); static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectDeprimeReservoirs( void ); /*********************************************************************//** * @brief * The initHeatDisinfectMode function initializes the heat disinfect Mode module. * @details * Inputs : none * Outputs : none * @return none *************************************************************************/ void initHeatDisinfectMode( void ) { heatDisinfectionState = DG_HEAT_DISINFECT_STATE_START; } /*********************************************************************//** * @brief * The transitionToHeatDisinfectMode function prepares for transition * to heat disinfect mode. * @details * Inputs : none * Outputs : none * @return none *************************************************************************/ void transitionToHeatDisinfectMode( void ) { initHeatDisinfectMode(); stopActuators(); } /*********************************************************************//** * @brief * The execHeatDisinfectMode function executes the heat disinfect Mode * state machine. * @details * Inputs : none * Outputs : none * @return current state. *************************************************************************/ U32 execHeatDisinfectMode( void ) { switch ( heatDisinfectionState ) { case DG_HEAT_DISINFECT_STATE_START: heatDisinfectionState = handleHeatDisinfectStart(); break; case DG_HEAT_DISINFECT_STATE_EVAC_RECIRC_PATH: break; case DG_HEAT_DISINFECT_STATE_EVAC_DIALYSATE_RESERVOIR_1: heatDisinfectionState = handleHeatDisinfectEvacDialysateReservoir1(); break; case DG_HEAT_DISINFECT_STATE_EVAC_DIALYSATE_RESERVOIR_2: heatDisinfectionState = handleHeatDisinfectEvacDialysateReservoir2(); break; case DG_HEAT_DISINFECT_STATE_FILL_WITH_WATER: heatDisinfectionState = handleHeatDisinfectFillWithWater(); break; case DG_HEAT_DISINFECT_STATE_HEAT_WATER: heatDisinfectionState = handleHeatDisinfectHeatWater(); break; case DG_HEAT_DISINFECT_STATE_DISINFECT_RECIRC_PATH: heatDisinfectionState = handleHeatDisinfectRecirculationPath(); break; case DG_HEAT_DISINFECT_STATE_DISINFECT_RESERVOIR_1_TO_2: heatDisinfectionState = handleHeatDisinfectReservoir1To2(); break; case DG_HEAT_DISINFECT_STATE_DISINFECT_RESERVOIR_2_TO_1: heatDisinfectionState = handleHeatDisinfectReservoir2To1(); break; case DG_HEAT_DISINFECT_STATE_DISINFECT_DRAIN_PATH: heatDisinfectionState = handleHeatDisinfectDrainPath(); break; case DG_HEAT_DISINFECT_STATE_DEPRIME_RESERVOIRS: heatDisinfectionState = handleHeatDisinfectDeprimeReservoirs(); break; default: // TODO - s/w fault heatDisinfectionState = DG_HEAT_DISINFECT_STATE_START; break; } return (U32)heatDisinfectionState; } /*********************************************************************//** * @brief * The getCurrentHeatDisinfectState function returns the current state of the \n * heat disinfect mode. * @details * Inputs : heatState * Outputs : none * @return the current state of heat disinfect mode. *************************************************************************/ DG_HEAT_DISINFECT_STATE_T getCurrentHeatDisinfectState( void ) { return heatDisinfectionState; } /*********************************************************************//** * @brief * The startDGHeatDisinfect function starts heat disinfect mode. * @details * Inputs : none * Outputs : none * @return none *************************************************************************/ void startDGHeatDisinfect( void ) { // TODO: make sure DG is not in the middle of something and it is in standby initHeatDisinfectMode(); requestNewOperationMode( DG_MODE_HEAT ); } /*********************************************************************//** * @brief * The stopDGHeatDisinfect function stops heat disinfect mode. * @details * Inputs : heatDisinfectionState * Outputs : heatDisinfectionState * @return none *************************************************************************/ void stopDGHeatDisinfect( void ) { heatDisinfectionState = DG_HEAT_DISINFECT_STATE_COMPLETE; stopActuators(); requestNewOperationMode( DG_MODE_STAN ); } // ********** private function definitions ********** static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectStart( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_EVAC_RECIRC_PATH; stopActuators(); // Set the actuators for evacuate recirculation path setValveState ( VPI, VALVE_STATE_OPEN ); setValveState ( VBF, VALVE_STATE_OPEN ); 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_R2_C_TO_NO ); setValveState ( VRD, VALVE_STATE_R1_C_TO_NO ); setValveState ( VRI, VALVE_STATE_R1_C_TO_NO ); setValveState ( VRF, VALVE_STATE_R2_C_TO_NO ); setROPumpTargetFlowRate( RO_PUMP_TARGET_FLOW_RATE_LPM ); stateTimer = getMSTimerCount(); // For evac recirc path return state; } static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectEvacDialysateRecircPath( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_EVAC_RECIRC_PATH; if ( didTimeout( stateTimer, HEAT_DISINFECT_EVAC_RECIRC_PATH_TIME_MS ) ) { // Set the state to evacuate reservoir 1 signalROPumpHardStop(); setValveState ( VPI, VALVE_STATE_OPEN ); setValveState ( VBF, VALVE_STATE_OPEN ); 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_R2_C_TO_NO ); setValveState ( VRD, VALVE_STATE_R1_C_TO_NC ); setValveState ( VRI, VALVE_STATE_R1_C_TO_NO ); setValveState ( VRF, VALVE_STATE_R2_C_TO_NO ); setDrainPumpTargetSpeed ( DRAIN_PUMP_TARGET_RPM ); state = DG_HEAT_DISINFECT_STATE_EVAC_DIALYSATE_RESERVOIR_1; } return state; } static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectEvacDialysateReservoir1( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_EVAC_DIALYSATE_RESERVOIR_1; F32 reservoir1Weight = getLoadCellFilteredWeight ( LOAD_CELL_A1 ); if ( reservoir1Weight <= EMPTY_RESERVOIRS_WEIGHT_GRAMS ) { // Set the state to evacuate reservoir 2 setValveState ( VPI, VALVE_STATE_OPEN ); setValveState ( VBF, VALVE_STATE_OPEN ); 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_R2_C_TO_NO ); setValveState ( VRD, VALVE_STATE_R1_C_TO_NO ); setValveState ( VRI, VALVE_STATE_R1_C_TO_NO ); setValveState ( VRF, VALVE_STATE_R2_C_TO_NO ); //setDrainPumpTargetSpeed ( DRAIN_PUMP_TARGET_RPM ); probably it is not needed state = DG_HEAT_DISINFECT_STATE_EVAC_DIALYSATE_RESERVOIR_2; } return state; } static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectEvacDialysateReservoir2( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_EVAC_DIALYSATE_RESERVOIR_2; F32 reservoir2Weight = getLoadCellFilteredWeight ( LOAD_CELL_B1 ); if ( reservoir2Weight <= EMPTY_RESERVOIRS_WEIGHT_GRAMS ) { // Set the state to fill water setValveState ( VPI, VALVE_STATE_OPEN ); setValveState ( VBF, VALVE_STATE_OPEN ); setValveState ( VSP, VALVE_STATE_CLOSED ); setValveState ( VPD, VALVE_STATE_OPEN_C_TO_NO ); setValveState ( VPO, VALVE_STATE_FILL_C_TO_NC ); setValveState ( VDR, VALVE_STATE_DRAIN_C_TO_NO ); setValveState ( VRC, VALVE_STATE_DRAIN_C_TO_NO ); setValveState ( VRO, VALVE_STATE_R2_C_TO_NO ); setValveState ( VRD, VALVE_STATE_R2_C_TO_NO ); setValveState ( VRI, VALVE_STATE_R2_C_TO_NC ); setValveState ( VRF, VALVE_STATE_R1_C_TO_NC ); setROPumpTargetFlowRate( RO_PUMP_TARGET_FLOW_RATE_LPM ); // Start the UV reactors to disinfect the water that // is being filled up startInletUVReactor(); startOutletUVReactor(); state = DG_HEAT_DISINFECT_STATE_FILL_WITH_WATER; } return state; } static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectFillWithWater( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_FILL_WITH_WATER; F32 reservoir1Weight = getLoadCellFilteredWeight ( LOAD_CELL_A1 ); F32 reservoir2Weight = getLoadCellFilteredWeight ( LOAD_CELL_B1 ); if ( reservoir1Weight >= FULL_RESERVOIRS_WEIGHT_GRAMS && reservoir2Weight >= FULL_RESERVOIRS_WEIGHT_GRAMS ) { // Drain pump is stopped to exit the open loop mode signalDrainPumpHardStop(); setValveState ( VPI, VALVE_STATE_OPEN ); setValveState ( VBF, VALVE_STATE_OPEN ); setValveState ( VSP, VALVE_STATE_CLOSED ); setValveState ( VPD, VALVE_STATE_OPEN_C_TO_NO ); setValveState ( VPO, VALVE_STATE_FILL_C_TO_NC ); setValveState ( VDR, VALVE_STATE_RECIRC_C_TO_NC ); setValveState ( VRC, VALVE_STATE_RECIRC_C_TO_NC ); setValveState ( VRO, VALVE_STATE_R2_C_TO_NC ); setValveState ( VRD, VALVE_STATE_R2_C_TO_NC ); setValveState ( VRI, VALVE_STATE_R1_C_TO_NO ); setValveState ( VRF, VALVE_STATE_R2_C_TO_NO ); setROPumpTargetFlowRate( RO_PUMP_TARGET_FLOW_RATE_LPM ); setDrainPumpTargetDeltaPressure( DRAIN_PUMP_TARGET_DELTA_PRESSURE ); setPrimaryHeaterTargetTemperature( HEAT_DISINFECT_TARGET_TEMPERATURE ); startPrimaryHeater(); state = DG_HEAT_DISINFECT_STATE_HEAT_WATER; } return state; } static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectHeatWater( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_HEAT_WATER; F32 TPi = getTemperatureValue ( TEMPSENSORS_OUTLET_PRIMARY_HEATER_TEMP_SENSOR ); F32 TDi = getTemperatureValue ( TEMPSENSORS_INLET_DIALYSATE_TEMP_SENSOR ); if ( fabs(TPi - TDi) <= MAX_TPO_AND_TDI_SENSORS_DIFFERENCE ) { setValveState ( VPI, VALVE_STATE_OPEN ); setValveState ( VBF, VALVE_STATE_OPEN ); 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_RECIRC_C_TO_NC ); setValveState ( VRC, VALVE_STATE_RECIRC_C_TO_NC ); setValveState ( VRO, VALVE_STATE_R2_C_TO_NO ); setValveState ( VRD, VALVE_STATE_R1_C_TO_NO ); setValveState ( VRI, VALVE_STATE_R1_C_TO_NO ); setValveState ( VRF, VALVE_STATE_R2_C_TO_NO ); stateTimer = getMSTimerCount(); state = DG_HEAT_DISINFECT_STATE_DISINFECT_RECIRC_PATH; } return state; } static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectRecirculationPath( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_DISINFECT_RECIRC_PATH; // If the temperature is out of tolerance, go back to heat water if ( ! isTemperatureInRange() ) { state = DG_HEAT_DISINFECT_STATE_HEAT_WATER; } else if ( didTimeout( stateTimer, HEAT_DISINFECT_EVAC_RECIRC_PATH_TIME_MS ) ) { heatDisinfectElapsedTime = heatDisinfectElapsedTime + HEAT_DISINFECT_EVAC_RECIRC_PATH_TIME_MS; //TODO Test setValveState ( VPI, VALVE_STATE_OPEN ); setValveState ( VBF, VALVE_STATE_OPEN ); setValveState ( VSP, VALVE_STATE_CLOSED ); setValveState ( VPD, VALVE_STATE_OPEN_C_TO_NO ); setValveState ( VPO, VALVE_STATE_FILL_C_TO_NC ); setValveState ( VDR, VALVE_STATE_RECIRC_C_TO_NC ); setValveState ( VRC, VALVE_STATE_RECIRC_C_TO_NC ); setValveState ( VRO, VALVE_STATE_R2_C_TO_NO ); setValveState ( VRD, VALVE_STATE_R1_C_TO_NO ); setValveState ( VRI, VALVE_STATE_R2_C_TO_NC ); setValveState ( VRF, VALVE_STATE_R1_C_TO_NC ); setDrainPumpTargetDeltaPressure( DRAIN_PUMP_TARGET_DELTA_PRESSURE ); setROPumpTargetFlowRate( RO_PUMP_TARGET_FLOW_RATE_LPM ); state = DG_HEAT_DISINFECT_STATE_DISINFECT_RESERVOIR_1_TO_2; } return state; } static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectReservoir1To2( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_DISINFECT_RESERVOIR_1_TO_2; return state; } static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectReservoir2To1( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_DISINFECT_RESERVOIR_2_TO_1; return state; } static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectDrainPath( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_DISINFECT_DRAIN_PATH; return state; } static DG_HEAT_DISINFECT_STATE_T handleHeatDisinfectDeprimeReservoirs( void ) { DG_HEAT_DISINFECT_STATE_T state = DG_HEAT_DISINFECT_STATE_DEPRIME_RESERVOIRS; return state; } static void execEvacuateFluidPath( void ) { switch ( heatDisinfectEvacState ) { case EVACUATE_OFF_STATE: break; case EVACUATE_RECIRC_PATH_STATE: break; case EVACUATE_RESERVOIR_1_STATE: break; case EVACUATE_RESERVOIR_2_STATE: break; default: break; } } static void stopActuators( void ) { /*setValveState ( VRF, VALVE_STATE_R1_C_TO_NC ); setValveState ( VRI, VALVE_STATE_R2_C_TO_NC ); setValveState ( VRD, VALVE_STATE_R2_C_TO_NO ); setValveState ( VRO, VALVE_STATE_R2_C_TO_NC ); setValveState ( VPO, VALVE_STATE_FILL_C_TO_NC ); setValveState ( VBF, VALVE_STATE_CLOSED ); setValveState ( VRC, VALVE_STATE_DRAIN_C_TO_NO ); setValveState ( VDR, VALVE_STATE_DRAIN_C_TO_NO ); setValveState ( VPI, VALVE_STATE_CLOSED ); setValveState ( VSP, VALVE_STATE_CLOSED ); setValveState ( VPD, VALVE_STATE_DRAIN_C_TO_NC );*/ //TODO composition pumps signalROPumpHardStop(); signalDrainPumpHardStop(); stopInletUVReactor(); stopOutletUVReactor(); stopPrimaryHeater(); stopTrimmerHeater(); } static BOOL isTemperatureInRange( void ) { BOOL result = FALSE; F32 TPi = getTemperatureValue ( TEMPSENSORS_OUTLET_PRIMARY_HEATER_TEMP_SENSOR ); F32 TDi = getTemperatureValue ( TEMPSENSORS_INLET_DIALYSATE_TEMP_SENSOR ); if ( fabs( TPi - TDi ) <= MAX_TEMPERATURE_DEVIATION_FROM_TARGET ) { // Reset the heat disinfect elapsed time // It has to start over heatDisinfectElapsedTime = 0; result = TRUE; } return result; } /**@}*/