/************************************************************************** * * Copyright (c) 2019-2022 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 ModeFlush.c * * @author (last) Bill Bracken * @date (last) 22-Aug-2022 * * @author (original) Leonardo Baloa * @date (original) 20-Dec-2019 * ***************************************************************************/ #include "ConcentratePumps.h" #include "DrainPump.h" #include "Heaters.h" #include "LoadCell.h" #include "MessageSupport.h" #include "ModeFault.h" #include "ModeFlush.h" #include "OperationModes.h" #include "Pressures.h" #include "Reservoirs.h" #include "ROPump.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "Timers.h" #include "UVReactors.h" #include "Valves.h" #include "CPLD.h" /** * @addtogroup DGFlushMode * @{ */ // ********** private definitions ********** // General defines #define FLUSH_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Mode flush data publish interval in counts. #define RO_PUMP_TARGET_FLOW_RATE_LPM 0.8F ///< RO pump target flow rate during flush/fill in L/min. #define RO_PUMP_MAX_PRESSURE_PSI 130 ///< Maximum RO pump pressure during flush/fill states in psi. #define DRAIN_PUMP_TARGET_RPM 2400 ///< Drain pump target RPM during drain. // Drain R1 & R2 states defines #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 ( 2 * 60 * MS_PER_SECOND ) ///< Flush Drain path wait time in milliseconds. // Flush dialysate state defines #define FLUSH_DIALYSATE_WAIT_TIME_MS ( 60 * MS_PER_SECOND ) ///< Flush dialysate wait time in milliseconds. // Flush concentrate straws state defines #define FLUSH_CONCENTRATE_STRAWS_TIME_MS ( 3 * 60 * MS_PER_SECOND ) ///< Flush concentrate straws wait time in milliseconds. #define ACID_PUMP_SPEED_ML_PER_MIN -30.0F ///< Acid 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 flush #define BICARB_PUMP_SPEED_ML_PER_MIN 30.6F ///< Bicarb pump speed in mL/min. // Flush and drain R1 and R2 state defines #define RSRVRS_FULL_VOL_ML 1800.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 * 60 * MS_PER_SECOND ) ///< Reservoirs 1 & 2 full fill up timeout in ms. #define RSRVRS_PARTIAL_FILL_TIMEOUT_MS ( 2 * 60 * MS_PER_SECOND ) ///< Reservoirs 1 & 2 partial fill up timeout in ms. #define RSRVRS_DRAIN_TIMEOUT_MS ( 2 * 60 * MS_PER_SECOND ) ///< Reservoirs 1 & 2 drain timeout in ms. #define FINAL_DRAIN_RO_PUMP_FLOW_LPM 0.6F ///< Final drain RO pump flow rate in L/min. This is used to flush the drain line during drain. // Flush drain line state defines #define FLUSH_DRAIN_LINE_VOLUME_L 0.1F ///< Water volume to flush in liters. #define FLUSH_DRAIN_LINE_TIMEOUT_MS ( 1 * 60 * MS_PER_SECOND ) ///< Flush drain lines timeout in milliseconds. // Flush circulation state defines #define FLUSH_CIRCULATION_WAIT_TIME_MS ( 0.5F * 60 * MS_PER_SECOND ) ///< Flush circulation wait time in milliseconds. // Flush with fresh water state defines #define FLUSH_WITH_FRESH_WATER_WAIT_TIME_MS ( 0.5F * 60 * MS_PER_SECOND ) ///< Flush with fresh water wait time in milliseconds. // ********** private data ********** static DG_FLUSH_STATE_T flushState; ///< Current active flush state. static DG_FLUSH_STATE_T prevFlushState; ///< Previous flush state. static DG_FLUSH_UI_STATE_T flushUIState; ///< Current UI flush state. static U32 rsrvrFillStableTimeCounter; ///< Reservoirs fill stable time counter. static U32 overallFlushElapsedTimeStart; ///< Overall flush mode elapsed time start. static U32 stateTimerStart; ///< State timer start. static ALARM_ID_T alarmDetectedPendingTrigger; ///< Alarm ID that is detected and is pending to be triggered. static DG_RESERVOIR_STATUS_T rsrvr1Status; ///< Reservoir 1 status. static DG_RESERVOIR_STATUS_T rsrvr2Status; ///< Reservoir 2 status. static BOOL isThisInitialDrain; ///< Initial drain boolean flag. static U32 dataPublishCounter; ///< Flush data publish counter. static BOOL hasWaterCancellationBeenSet; ///< Water cancellation set/not set boolean flag. static F32 flushLinesVolumeL; ///< Volume of water pumped by RO pump during flush lines state. static BOOL haveDrainParamsBeenInit[ NUM_OF_DG_RESERVOIRS ]; ///< Boolean flag to indicate whether the drain parameters have been reset or not. // ********** private function prototypes ********** static DG_FLUSH_STATE_T handleFlushModeStartState( void ); static DG_FLUSH_STATE_T handleFlushModeDrainR1State( void ); static DG_FLUSH_STATE_T handleFlushModeDrainR2State( void ); static DG_FLUSH_STATE_T handleFlushModeFlushDrainState( void ); static DG_FLUSH_STATE_T handleFlushModeFlushDialysateState( void ); static DG_FLUSH_STATE_T handleFlushModeFlushConcentrateStrawsState( void ); static DG_FLUSH_STATE_T handleFlushModeFlushR1ToR2State( void ); static DG_FLUSH_STATE_T handleFlushModeFlushR2AndDrainR1State( void ); static DG_FLUSH_STATE_T handleFlushModeFlushCirculationDrainLineState( void ); static DG_FLUSH_STATE_T handleFlushModeFlushCirculationState( void ); static DG_FLUSH_STATE_T handleFlushModeFlushWithFreshWaterState( void ); static DG_FLUSH_STATE_T handleFlushModeCancelBasicPathState( void ); static DG_FLUSH_STATE_T handleFlushModeCancelWaterPathState( void ); static DG_FLUSH_STATE_T handleFlushModeComplete( void ); static void failFlushMode( 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 void publishFlushData( void ); static void monitorModeFlush( void ); /*********************************************************************//** * @brief * The initFlushMode function initializes flush mode module. * @details Inputs: none * @details Outputs: flushState, prevFlushState, rsrvrFillStableTimeCounter, * overallFlushElapsedTime, isThisInitialDrain, dataPublishCounter, * rsrvr1Status, rsrvr2Status, hasWaterCancellationBeenSet, * flushLinesVolumeL, haveDrainParamsBeenInit, stateTimerStart * @return none *************************************************************************/ void initFlushMode( void ) { // Initialize the variables flushState = DG_FLUSH_STATE_START; prevFlushState = DG_FLUSH_STATE_START; flushUIState = FLUSH_UI_STATE_NOT_RUNNING; rsrvrFillStableTimeCounter = 0; overallFlushElapsedTimeStart = 0; isThisInitialDrain = TRUE; dataPublishCounter = 0; rsrvr1Status = NUM_OF_DG_RESERVOIR_STATUS; rsrvr2Status = NUM_OF_DG_RESERVOIR_STATUS; hasWaterCancellationBeenSet = FALSE; flushLinesVolumeL = 0.0; haveDrainParamsBeenInit[ DG_RESERVOIR_1 ] = FALSE; haveDrainParamsBeenInit[ DG_RESERVOIR_2 ] = FALSE; stateTimerStart = 0; } /*********************************************************************//** * @brief * The transitionToFlushMode function prepares for transition to flush mode. * @details Inputs: none * @details Outputs: Prepares for transition to flush mode * @return initial flush state *************************************************************************/ U32 transitionToFlushMode( void ) { // Reset all the actuators deenergizeActuators(); initFlushMode(); setCPLDCleanLEDColor( CPLD_CLEAN_LED_BLUE ); return flushState; } /*********************************************************************//** * @brief * The execFlushMode function executes the flush mode state machine. * @details Inputs: flushState * @details Outputs: flushState * @return current state *************************************************************************/ U32 execFlushMode( void ) { monitorModeFlush(); // Execute current flush state switch ( flushState ) { case DG_FLUSH_STATE_START: flushState = handleFlushModeStartState(); break; case DG_FLUSH_STATE_DRAIN_R1: flushState = handleFlushModeDrainR1State(); break; case DG_FLUSH_STATE_DRAIN_R2: flushState = handleFlushModeDrainR2State(); break; case DG_FLUSH_STATE_FLUSH_DRAIN: flushState = handleFlushModeFlushDrainState(); break; case DG_FLUSH_STATE_FLUSH_DIALYSATE: flushState = handleFlushModeFlushDialysateState(); break; case DG_FLUSH_STATE_FLUSH_CONCENTRATE_STRAWS: flushState = handleFlushModeFlushConcentrateStrawsState(); break; case DG_FLUSH_STATE_FLUSH_R1_TO_R2: flushState = handleFlushModeFlushR1ToR2State(); break; case DG_FLUSH_STATE_FLUSH_R2_AND_DRAIN_R1: flushState = handleFlushModeFlushR2AndDrainR1State(); break; case DG_FLUSH_STATE_FLUSH_CIRCULATION_DRAIN_LINE: flushState = handleFlushModeFlushCirculationDrainLineState(); break; case DG_FLUSH_STATE_FLUSH_CIRCULATION: flushState = handleFlushModeFlushCirculationState(); break; case DG_FLUSH_STATE_FLUSH_WITH_FRESH_WATER: flushState = handleFlushModeFlushWithFreshWaterState(); break; case DG_FLUSH_STATE_CANCEL_BASIC_PATH: flushState = handleFlushModeCancelBasicPathState(); break; case DG_FLUSH_STATE_CANCEL_WATER_PATH: flushState = handleFlushModeCancelWaterPathState(); break; case DG_FLUSH_STATE_COMPLETE: flushState = handleFlushModeComplete(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_DG_FLUSH_INVALID_EXEC_STATE, flushState ) flushState = DG_FLUSH_STATE_START; break; } // Publish the data publishFlushData(); return (U32)flushState; } /*********************************************************************//** * @brief * The getCurrentFlushState function returns the current state of the flush * mode. * @details Inputs: flushState * @details Outputs: none * @return flushState which is the current state of flush mode. *************************************************************************/ DG_FLUSH_STATE_T getCurrentFlushState( void ) { return flushState; } /*********************************************************************//** * @brief * The stopDGFlush function stops flush mode if the current operation mode * is flush mode. * @details Inputs: none * @details Outputs: none * @return TRUE if the current mode is flush otherwise, FALSE *************************************************************************/ BOOL stopDGFlush( void ) { BOOL status = FALSE; // Check if the current operation mode is flush if ( DG_MODE_FLUS == getCurrentOperationMode() ) { // Reset all the actuators deenergizeActuators(); // Transition to mode standby requestNewOperationMode( DG_MODE_STAN ); status = TRUE; } return status; } // ********** private functions ********** /*********************************************************************//** * @brief * The handleFlushModeStartState function handles the flush start state. * If the sensors are in range, it transitions to the next state otherwise, * it transitions to basic cancellation state. * @details Inputs: stateTimerStart, rsrvr1Status * @details Outputs: stateTimerStart, rsrvr1Status * @return next state of the flush state machine *************************************************************************/ static DG_FLUSH_STATE_T handleFlushModeStartState( void ) { DG_FLUSH_STATE_T state = DG_FLUSH_STATE_START; // Start overall flush timer overallFlushElapsedTimeStart = getMSTimerCount(); // Close VPi to prevent wasting water setValveState( VPI, VALVE_STATE_CLOSED ); // Set the actuators to drain R1 setValveState( VRD1, VALVE_STATE_OPEN ); // Set VPO setValveState( VPO, VALVE_STATE_FILL_C_TO_NC); setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); flushUIState = FLUSH_UI_STATE_DRAIN_DEVICE; stateTimerStart = getMSTimerCount(); rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; state = DG_FLUSH_STATE_DRAIN_R1; return state; } /*********************************************************************//** * @brief * The handleFlushModeDrainR1State function handles the drain reservoir 1. * If the drain is completed within the defined time, it transitions to the * next state, otherwise, it transitions to basic cancellation state. * @details Inputs: stateTimerStart, rsrvr1Status,rsrvr2Status, isThisInitialDrain * @details Outputs: stateTimerStart, rsrvr1Status, rsrvr2Status * @return next state of the flush state machine *************************************************************************/ static DG_FLUSH_STATE_T handleFlushModeDrainR1State( void ) { DG_FLUSH_STATE_T state = DG_FLUSH_STATE_DRAIN_R1; if ( DG_RESERVOIR_ABOVE_TARGET == rsrvr1Status ) { rsrvr1Status = getRsrvrDrainStatus( DG_RESERVOIR_1, DRAIN_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_DRAIN_TIMEOUT_MS ); } else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) { if ( TRUE == isThisInitialDrain ) { // Request a tare for reservoir 1 tareLoadCell( LOAD_CELL_RESERVOIR_1_PRIMARY ); tareLoadCell( LOAD_CELL_RESERVOIR_1_BACKUP ); } // Close reservoir 1 drain setValveState( VRD1, VALVE_STATE_CLOSED ); // Set the actuators to drain R2 // NOTE: Drain pump is already on and VDr is already on drain state setValveState( VRD2, VALVE_STATE_OPEN ); // Set VPO setValveState( VPO, VALVE_STATE_FILL_C_TO_NC); stateTimerStart = getMSTimerCount(); rsrvr2Status = DG_RESERVOIR_ABOVE_TARGET; state = DG_FLUSH_STATE_DRAIN_R2; } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { state = DG_FLUSH_STATE_CANCEL_BASIC_PATH; } return state; } /*********************************************************************//** * @brief * The handleFlushModeDrainR2State function handles the drain reservoir 2. * If the drain is completed within the defined time, it transitions to the * next state, otherwise, it transitions to basic cancellation state. * @details Inputs: stateTimerStart, rsrvr1Status, rsrvr2Status, isThisInitialDrain * @details Outputs: stateTimerStart, rsrvr1Status, rsrvr2Status * @return next state of the flush state machine *************************************************************************/ static DG_FLUSH_STATE_T handleFlushModeDrainR2State( void ) { DG_FLUSH_STATE_T state = DG_FLUSH_STATE_DRAIN_R2; if ( DG_RESERVOIR_ABOVE_TARGET == rsrvr2Status ) { rsrvr2Status = getRsrvrDrainStatus( DG_RESERVOIR_2, DRAIN_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_DRAIN_TIMEOUT_MS ); } else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) { // Done with draining signalDrainPumpHardStop(); if ( TRUE == isThisInitialDrain ) { tareLoadCell( LOAD_CELL_RESERVOIR_2_PRIMARY ); tareLoadCell( LOAD_CELL_RESERVOIR_2_BACKUP ); // Set the actuators to flush drain setValveState( VPI, VALVE_STATE_OPEN ); setValveState( VPD, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VRD2, VALVE_STATE_CLOSED ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); state = DG_FLUSH_STATE_FLUSH_DRAIN; } else { // Set the actuators to pull fresh water in to drain the circulation line setValveState( VPI, VALVE_STATE_OPEN ); setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); setValveState( VRD2, VALVE_STATE_CLOSED ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); setValveState( VRC, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); setROPumpTargetFlowRateLPM( FINAL_DRAIN_RO_PUMP_FLOW_LPM, RO_PUMP_MAX_PRESSURE_PSI ); // Turn on the UV reactors turnOnUVReactor( INLET_UV_REACTOR ); turnOnUVReactor( OUTLET_UV_REACTOR ); flushUIState = FLUSH_UI_STATE_FLUSH_RECIRCULATION_PATH; state = DG_FLUSH_STATE_FLUSH_CIRCULATION_DRAIN_LINE; } stateTimerStart = getMSTimerCount(); } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) { state = DG_FLUSH_STATE_CANCEL_BASIC_PATH; } return state; } /*********************************************************************//** * @brief * The handleFlushModeFlushDrainState function handles the flush drain state. * Once the flush drain time has elapsed, it transitions to the next state. * @details Inputs: stateTimerStart * @details Outputs: stateTimerStart * @return next state of the flush state machine *************************************************************************/ static DG_FLUSH_STATE_T handleFlushModeFlushDrainState( void ) { DG_FLUSH_STATE_T state = DG_FLUSH_STATE_FLUSH_DRAIN; if ( TRUE == didTimeout( stateTimerStart, FLUSH_DRAIN_WAIT_TIME_MS ) ) { setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); setROPumpTargetFlowRateLPM( RO_PUMP_TARGET_FLOW_RATE_LPM, RO_PUMP_MAX_PRESSURE_PSI ); // Turn on the UV reactors turnOnUVReactor( INLET_UV_REACTOR ); turnOnUVReactor( OUTLET_UV_REACTOR ); stateTimerStart = getMSTimerCount(); state = DG_FLUSH_STATE_FLUSH_DIALYSATE; } return state; } /*********************************************************************//** * @brief * The handleFlushModeFlushDialysateState function handles the flush * dialysate state. Once the flush dialysate time has elapsed, it * transitions to the next state. * @details Inputs: stateTimerStart * @details Outputs: stateTimerStart * @return next state of the flush state machine *************************************************************************/ static DG_FLUSH_STATE_T handleFlushModeFlushDialysateState( void ) { DG_FLUSH_STATE_T state = DG_FLUSH_STATE_FLUSH_DIALYSATE; if ( TRUE == didTimeout( stateTimerStart, FLUSH_DIALYSATE_WAIT_TIME_MS ) ) { // 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 on the concentrate pumps requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); stateTimerStart = getMSTimerCount(); state = DG_FLUSH_STATE_FLUSH_CONCENTRATE_STRAWS; } return state; } /*********************************************************************//** * @brief * The handleFlushModeFlushDialysateState function handles the flush * dialysate state. Once the flush dialysate time has elapsed, it * transitions to the next state. * @details Inputs: rsrvr1Status, rsrvr2Status, stateTimerStart * @details Outputs: rsrvr1Status, rsrvr2Status, stateTimerStart * @return next state of the flush state machine *************************************************************************/ static DG_FLUSH_STATE_T handleFlushModeFlushConcentrateStrawsState( void ) { DG_FLUSH_STATE_T state = DG_FLUSH_STATE_FLUSH_CONCENTRATE_STRAWS; if ( TRUE == didTimeout( stateTimerStart, FLUSH_CONCENTRATE_STRAWS_TIME_MS ) ) { // Done with flushing the concentrate pumps line requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); setValveState( VRF, VALVE_STATE_R1_C_TO_NC ); setValveState( VRI, VALVE_STATE_R2_C_TO_NC ); stateTimerStart = getMSTimerCount(); rsrvr1Status = DG_RESERVOIR_BELOW_TARGET; rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; flushUIState = FLUSH_UI_STATE_FLUSH_RESERVOIRS; state = DG_FLUSH_STATE_FLUSH_R1_TO_R2; } return state; } /*********************************************************************//** * @brief * The handleFlushModeFlushR1ToR2State function handles the flush * reservoir 1 to reservoir 2 state. If any of the reservoirs flush within * the defined period of time, it transitions to the next state, otherwise, * it transitions to water cancellation state. If reservoir 2 is filled to * 500 mL before reservoir 1 is filled, it transitions to water cancellation * state. * @details Inputs: rsrvr1Status, rsrvr2Status, stateTimerStart * @details Outputs: rsrvr1Status, rsrvr2Status, stateTimerStart, * prevFlushState, alarmDetectedPendingTrigger * @return next state of the flush state machine *************************************************************************/ static DG_FLUSH_STATE_T handleFlushModeFlushR1ToR2State( void ) { DG_FLUSH_STATE_T state = DG_FLUSH_STATE_FLUSH_R1_TO_R2; if ( DG_RESERVOIR_BELOW_TARGET == rsrvr1Status ) { rsrvr1Status = getRsrvrFillStatus( DG_RESERVOIR_1, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS ); // Keep monitoring the status of reservoir 2 as the same time rsrvr2Status = getRsrvrFillStatus( DG_RESERVOIR_2, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_PARTIAL_FILL_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 ) { prevFlushState = state; alarmDetectedPendingTrigger = ALARM_ID_DG_INVALID_LOAD_CELL_VALUE; state = DG_FLUSH_STATE_CANCEL_WATER_PATH; } } else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) { if ( DG_RESERVOIR_BELOW_TARGET == rsrvr2Status ) { rsrvr2Status = getRsrvrFillStatus( DG_RESERVOIR_2, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_PARTIAL_FILL_TIMEOUT_MS ); } else 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 ); stateTimerStart = getMSTimerCount(); // Set both reservoirs' status rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; state = DG_FLUSH_STATE_FLUSH_R2_AND_DRAIN_R1; } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) { state = DG_FLUSH_STATE_CANCEL_WATER_PATH; } } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { state = DG_FLUSH_STATE_CANCEL_WATER_PATH; } return state; } /*********************************************************************//** * @brief * The handleFlushModeFlushR2AndDrainR1State function handles the flush * reservoir 2 and drain reservoir 1 state. If reservoir 2 flushes within * the defined period of time and reservoir 1 drains within the defined * period of time, it transitions to the next state. If the flush times out, * the function transitions to water cancellation state. If the drain times * out the function transitions to basic cancellation state. If reservoir 1 * is filled to 500 mL before reservoir 2 is filled, it transitions to * water cancellation state. * @details Inputs: rsrvr1Status, rsrvr2Status, stateTimerStart * @details Outputs: rsrvr1Status, rsrvr2Status, stateTimerStart, * prevFlushState, alarmDetectedPendingTrigger, isThisInitialDrain * @return next state of the flush state machine *************************************************************************/ static DG_FLUSH_STATE_T handleFlushModeFlushR2AndDrainR1State( void ) { DG_FLUSH_STATE_T state = DG_FLUSH_STATE_FLUSH_R2_AND_DRAIN_R1; // 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_DRAIN_TIMEOUT_MS ); } else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) { // Done with draining R1 signalDrainPumpHardStop(); setValveState( VRD1, VALVE_STATE_CLOSED ); } // Reservoir 1 drain time out else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { state = DG_FLUSH_STATE_CANCEL_BASIC_PATH; } // First reservoir 2 must be completely full but if reservoir 1 is filled before reservoir 2 is full, alarm if ( DG_RESERVOIR_BELOW_TARGET == rsrvr2Status ) { 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 ) ) { prevFlushState = state; alarmDetectedPendingTrigger = ALARM_ID_DG_INVALID_LOAD_CELL_VALUE; state = DG_FLUSH_STATE_CANCEL_WATER_PATH; } } // Once reservoir 2 is full, reservoir 1 must be partially full else if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) { rsrvr1Status = getRsrvrFillStatus( DG_RESERVOIR_1, RSRVRS_PARTIAL_FILL_VOL_ML, RSRVRS_PARTIAL_FILL_TIMEOUT_MS ); // Once R1 is partially full, transition to the next state if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) { signalROPumpHardStop(); setValveState( VRD1, VALVE_STATE_OPEN ); setValveState( VRO, VALVE_STATE_R1_C_TO_NO ); setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); // Turn off the UV reactors prior to transitioning to drain states. // In the drain states, there is not fluid flowing in the RO section so the reactors are turned off turnOffUVReactor( INLET_UV_REACTOR ); turnOffUVReactor( OUTLET_UV_REACTOR ); // Close VPi and VPd to drain setValveState(VPI, VALVE_STATE_CLOSED); setValveState(VPD, VALVE_STATE_DRAIN_C_TO_NO); // Set VPO setValveState( VPO, VALVE_STATE_FILL_C_TO_NC); stateTimerStart = getMSTimerCount(); isThisInitialDrain = FALSE; rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; flushUIState = FLUSH_UI_STATE_DRAIN_RESERVOIRS; state = DG_FLUSH_STATE_DRAIN_R1; } // Check if reservoir 1 fill timed out else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { state = DG_FLUSH_STATE_CANCEL_WATER_PATH; } } // Check if reservoir 2 fill time out else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) { state = DG_FLUSH_STATE_CANCEL_WATER_PATH; } return state; } /*********************************************************************//** * @brief * The handleFlushModeFlushCirculationDrainLineState function handles the * flush drain line state. Once the drain line was flushed with sufficient * water, it transitions to the next state. If the line is not drained * within the defined time, it transitions to water cancellation state. * @details Inputs: stateTimerStart, flushLinesVolumeL * @details Outputs: stateTimerStart, flushLinesVolumeL, * alarmDetectedPendingTrigger * @return next state of the flush state machine *************************************************************************/ static DG_FLUSH_STATE_T handleFlushModeFlushCirculationDrainLineState( void ) { DG_FLUSH_STATE_T state = DG_FLUSH_STATE_FLUSH_CIRCULATION_DRAIN_LINE; F32 waterFlowRate = getMeasuredROFlowRateLPM(); F32 waterVolume = ( ( waterFlowRate / SEC_PER_MIN ) / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ); // Integrate volume of water moved through line flushLinesVolumeL += waterVolume; // When enough water volume has flowed to flush the lines, transition to flush circulation state if ( flushLinesVolumeL >= FLUSH_DRAIN_LINE_VOLUME_L ) { // Set the actuators to transition to flush circulation // The valves are set to do flush drain and flush circulation setValveState( VPD, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VRC, VALVE_STATE_RECIRC_C_TO_NC ); setValveState( VDR, VALVE_STATE_RECIRC_C_TO_NC ); setROPumpTargetFlowRateLPM( RO_PUMP_TARGET_FLOW_RATE_LPM, RO_PUMP_MAX_PRESSURE_PSI ); stateTimerStart = getMSTimerCount(); state = DG_FLUSH_STATE_FLUSH_CIRCULATION; } else if ( TRUE == didTimeout( stateTimerStart, FLUSH_DRAIN_LINE_TIMEOUT_MS ) ) { alarmDetectedPendingTrigger = ALARM_ID_DG_DRAIN_CIRCULATION_LINE_TIMEOUT; // Could not reach to the defined drain line on time state = DG_FLUSH_STATE_CANCEL_BASIC_PATH; } return state; } /*********************************************************************//** * @brief * The handleFlushModeFlushCirculationState function handles the flush * circulation state. Once the flush circulation time has elapsed, it * transitions to the next state. * @details Inputs: stateTimerStart * @details Outputs: stateTimerStart * @return next state of the flush state machine *************************************************************************/ static DG_FLUSH_STATE_T handleFlushModeFlushCirculationState( void ) { DG_FLUSH_STATE_T state = DG_FLUSH_STATE_FLUSH_CIRCULATION; if ( TRUE == didTimeout( stateTimerStart, FLUSH_CIRCULATION_WAIT_TIME_MS ) ) { setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); setValveState( VRC, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); stateTimerStart = getMSTimerCount(); state = DG_FLUSH_STATE_FLUSH_WITH_FRESH_WATER; } return state; } /*********************************************************************//** * @brief * The handleFlushModeFlushWithFreshWaterState function handles the * flush with fresh water state. It runs the circulation state with fresh * water for the defined period of time. * @details Inputs: stateTimerStart * @details Outputs: none * @return next state of the flush state machine *************************************************************************/ static DG_FLUSH_STATE_T handleFlushModeFlushWithFreshWaterState( void ) { DG_FLUSH_STATE_T state = DG_FLUSH_STATE_FLUSH_WITH_FRESH_WATER; if ( TRUE == didTimeout( stateTimerStart, FLUSH_WITH_FRESH_WATER_WAIT_TIME_MS ) ) { deenergizeActuators(); flushUIState = FLUSH_UI_STATE_COMPLETE; state = DG_FLUSH_STATE_COMPLETE; } return state; } /*********************************************************************//** * @brief * The handleFlushModeCancelBasicPathState function handles the flush * cancel mode basic path state. The state sets the state to complete and * raises an alarm. * @details Inputs: none * @details Outputs: none * @return next state of the flush state machine *************************************************************************/ static DG_FLUSH_STATE_T handleFlushModeCancelBasicPathState( void ) { DG_FLUSH_STATE_T state = DG_FLUSH_STATE_CANCEL_BASIC_PATH; // Once the fault alarm is raised, the DG Software will // deenergize all the actuators failFlushMode(); flushUIState = FLUSH_UI_STATE_CANCEL_FLUSH; return state; } /*********************************************************************//** * @brief * The handleFlushModeCancelWaterPathState function handles the flush mode * cancel water path state. The state drains the 2 reservoirs. If the drain * times out, it transitions to basic cancellation path. * @details Inputs: rsrvr1Status, rsrvr2Status, stateTimer, * hasWaterCancellationBeenSet * @details Outputs: rsrvr1Status, rsrvr2Status, stateTimer, * hasWaterCancellationBeenSet * @return next state of the flush state machine *************************************************************************/ static DG_FLUSH_STATE_T handleFlushModeCancelWaterPathState( void ) { DG_FLUSH_STATE_T state = DG_FLUSH_STATE_CANCEL_WATER_PATH; if ( FALSE == hasWaterCancellationBeenSet ) { // Stop all the actuators first then decide who should run next deenergizeActuators(); // Set the actuators setValveState( VPI, VALVE_STATE_CLOSED ); setValveState( VPD, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VRD2, VALVE_STATE_OPEN ); // Set both reservoirs to be considered as full rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; rsrvr2Status = DG_RESERVOIR_ABOVE_TARGET; // Water cancellation path was set hasWaterCancellationBeenSet = TRUE; // The drain is set to start from reservoir 2 since all the actuators have been de-energized // Start the drain pump setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); flushUIState = FLUSH_UI_STATE_CANCEL_FLUSH; // Start the timer for drain timeout stateTimerStart = getMSTimerCount(); } // If reservoir 2 is empty, set to drain reservoir 1 if ( DG_RESERVOIR_ABOVE_TARGET == rsrvr2Status ) { // If the cancellation water path cannot be done, got to basic cancellation path rsrvr2Status = getRsrvrDrainStatus( DG_RESERVOIR_2, DRAIN_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_DRAIN_TIMEOUT_MS ); if ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) { setValveState( VRD1, VALVE_STATE_OPEN ); setValveState( VRD2, VALVE_STATE_CLOSED ); } } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr2Status ) { state = DG_FLUSH_STATE_CANCEL_BASIC_PATH; } // If reservoir 2 has already been drained and reservoir 1 is empty, reset and switch to complete if ( ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status ) && ( DG_RESERVOIR_ABOVE_TARGET == rsrvr1Status ) ) { // If the cancellation water path cannot be done, go to basic cancellation path rsrvr1Status = getRsrvrDrainStatus( DG_RESERVOIR_1, DRAIN_WEIGHT_UNCHANGE_TIMEOUT, RSRVRS_DRAIN_TIMEOUT_MS ); if ( DG_RESERVOIR_REACHED_TARGET == rsrvr1Status ) { setValveState( VRD2, VALVE_STATE_CLOSED ); // Done with draining signalDrainPumpHardStop(); // Raise the alarm failFlushMode(); } } else if ( DG_RESERVOIR_NOT_REACHED_TARGET == rsrvr1Status ) { state = DG_FLUSH_STATE_CANCEL_BASIC_PATH; } return state; } /*********************************************************************//** * @brief * The handleFlushModeComplete function handles the complete state. * @details Inputs: none * @details Outputs: none * @return next state of the flush state machine *************************************************************************/ static DG_FLUSH_STATE_T handleFlushModeComplete( void ) { DG_FLUSH_STATE_T state = DG_FLUSH_STATE_COMPLETE; stopDGFlush(); return state; } /*********************************************************************//** * @brief * The failFlushMode function sets the alarm that failed the flush mode. * @details Inputs: alarm, prevHeatDisinfectState * @details Outputs: none * @return none *************************************************************************/ static void failFlushMode( void ) { SET_ALARM_WITH_1_U32_DATA( alarmDetectedPendingTrigger, prevFlushState ) } /*********************************************************************//** * @brief * The getRsrvrFillStatus function checks whether the target reservoir is * full or not. If the fill times out, the function sets the status to did * not reach to target. * @details Inputs: rsrvrFillStableTimeCounter, alarm, stateTimer, flushState, * prevFlushState * @details Outputs: rsrvrFillStableTimeCounter, alarm, stateTimer * @param r is DG_RESERVOIR_1 or DG_RESERVOIR_2 * @param targetVol is the target fill volume * @param timeout is the fill up time out that is checked against * @return the status of the reservoirs during filling *************************************************************************/ static DG_RESERVOIR_STATUS_T getRsrvrFillStatus( DG_RESERVOIR_ID_T r, F32 targetVol, U32 timeout ) { DG_RESERVOIR_STATUS_T status = DG_RESERVOIR_BELOW_TARGET; F32 volume = 0.0; if ( DG_RESERVOIR_1 == r ) { volume = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); } else if ( DG_RESERVOIR_2 == r ) { volume = getLoadCellSmallFilteredWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); } // Check the volume of the reservoir against the target volume if ( volume >= targetVol ) { if ( ++rsrvrFillStableTimeCounter >= RSRVRS_FULL_STABLE_TIME_COUNT ) { status = DG_RESERVOIR_REACHED_TARGET; rsrvrFillStableTimeCounter = 0; // Set the state timer in case it needs to be used for another timeout check if ( ( DG_FLUSH_STATE_FLUSH_R2_AND_DRAIN_R1 == flushState) && ( DG_RESERVOIR_2 == r) ) { if ( rsrvr1Status == DG_RESERVOIR_REACHED_TARGET ) { stateTimerStart = getMSTimerCount(); } } else { stateTimerStart = getMSTimerCount(); } } } else if ( TRUE == didTimeout( stateTimerStart, timeout ) ) { // Failed to fill on time prevFlushState = flushState; alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_FILL_TIMEOUT; status = DG_RESERVOIR_NOT_REACHED_TARGET; } return status; } /*********************************************************************//** * @brief * The getRsrvrDrainStatus function returns the status of draining a * reservoir. If the drain times out, it set the status to did not reach * target. * @details Inputs: rsrvrFillStableTimeCounter, alarm, stateTimer * @details Outputs: rsrvrFillStableTimeCounter, alarm, stateTimer, * prevFlushState * @param r is DG_RESERVOIR_1 or DG_RESERVOIR_2 * @param drainSteadyStateTimeout which is the time the reservoir's level * does not change and is steady state * @param timeout which is the timeout that a reservoir must be drained by * then * @return the status of the reservoirs during draining *************************************************************************/ static DG_RESERVOIR_STATUS_T getRsrvrDrainStatus( DG_RESERVOIR_ID_T r, U32 drainSteadyStateTimeout, U32 timeout ) { DG_RESERVOIR_STATUS_T status = DG_RESERVOIR_ABOVE_TARGET; // 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 ) { if ( ( DG_FLUSH_STATE_FLUSH_R2_AND_DRAIN_R1 == flushState) && ( DG_RESERVOIR_1 == r) ) { if ( ( DG_RESERVOIR_REACHED_TARGET == rsrvr2Status) && ( 0 == getDrainPumpTargetRPM() ) ) { stateTimerStart = getMSTimerCount(); } } else { stateTimerStart = getMSTimerCount(); } haveDrainParamsBeenInit[ r ] = FALSE; status = DG_RESERVOIR_REACHED_TARGET; } else if ( TRUE == didTimeout( stateTimerStart, timeout ) ) { // Failed to drain on time prevFlushState = flushState; haveDrainParamsBeenInit [ r ] = FALSE; alarmDetectedPendingTrigger = ALARM_ID_DG_RESERVOIR_DRAIN_TIMEOUT; status = DG_RESERVOIR_NOT_REACHED_TARGET; } return status; } /*********************************************************************//** * @brief * The publishFlushData function publishes the flush mode data at the set * interval. * @details Inputs: dataPublishCounter * @details Outputs: dataPublishCounter * @return: none *************************************************************************/ static void publishFlushData( void ) { if ( ++dataPublishCounter >= FLUSH_DATA_PUB_INTERVAL ) { MODE_FLUSH_DATA_T data; data.flushState = (U32)flushState; data.overallElapsedTime = calcTimeSince( overallFlushElapsedTimeStart ); data.stateElapsedTime = calcTimeSince( stateTimerStart ); data.drainLineVolume = flushLinesVolumeL; data.flushUIState = (U32)flushUIState; broadcastData( MSG_ID_DG_FLUSH_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&data, sizeof( MODE_FLUSH_DATA_T ) ); dataPublishCounter = 0; } } /*********************************************************************//** * @brief * The monitorModeFlush 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: none * @details Outputs: prevFlushState, flushState, alarmDetectedPendingTrigger * @return: none *************************************************************************/ static void monitorModeFlush( void ) { #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_CAPS_MONITOR ) != SW_CONFIG_ENABLE_VALUE ) #endif { if ( ( STATE_OPEN == getSwitchStatus( CONCENTRATE_CAP ) ) || ( 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 U32 cap = (U32)( STATE_OPEN == getSwitchStatus( CONCENTRATE_CAP ) ? CONCENTRATE_CAP : DIALYSATE_CAP ); prevFlushState = flushState; flushState = DG_FLUSH_STATE_CANCEL_WATER_PATH; alarmDetectedPendingTrigger = ALARM_ID_DG_DIALYSATE_OR_CONC_CAP_NOT_IN_PROPER_POSITION; } } } /**@}*/