Index: firmware/App/Modes/ModeFlush.c =================================================================== diff -u -r499e5de29e706d09f79ba22511068990c4044e84 -r99b0c8f1ff9f9319f68e5043cd8c007e317a05c0 --- firmware/App/Modes/ModeFlush.c (.../ModeFlush.c) (revision 499e5de29e706d09f79ba22511068990c4044e84) +++ firmware/App/Modes/ModeFlush.c (.../ModeFlush.c) (revision 99b0c8f1ff9f9319f68e5043cd8c007e317a05c0) @@ -1,63 +1,976 @@ /************************************************************************** - * - * 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 ModeFlush.c - * - * @date 20-Dec-2019 - * @author L. Baloa - * - * @brief Top-level state machine for flush mode. - * - *************************************************************************/ +* +* 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 ModeFlush.c +* +* @author (last) Quang Nguyen +* @date (last) 01-Sep-2020 +* +* @author (original) Leonardo Baloa +* @date (original) 20-Dec-2019 +* +***************************************************************************/ +#include "DrainPump.h" +#include "Heaters.h" +#include "LoadCell.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" +/** + * @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.8 ///< RO pump target flow rate during flush/fill in L/min. TODO original flow was 0.8 +#define RO_PUMP_MAX_PRESSURE_PSI 130 ///< Maximum RO pump pressure during flush/fill states in psi. +#define DRAIN_PUMP_TARGET_RPM 1800 ///< 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 ( 1 * 60 * MS_PER_SECOND ) ///< Flush Drain path wait time in milliseconds. TODo original time is 2 minutes + +// Flush dialysate state defines +#define FLUSH_DIALYSATE_WAIT_TIME_MS ( 0.5 * 60 * MS_PER_SECOND ) ///< Flush dialysate wait time in milliseconds. + +// Flush concentrate straws state defines +#define FLUSH_CONCENTRATE_STRAWS_TIME_MS ( 0.5 * 60 * MS_PER_SECOND ) ///< Flush concentrate straws wait time in milliseconds. TODO original time is 3 minutes + +// Flush and drain R1 and R2 state defines +#define RSRVRS_FULL_VOL_ML 1650.0 ///< Reservoirs 1 & 2 full volume in mL. TODo original value was 1900 +#define RSRVRS_PARTIAL_FILL_VOL_ML 500.0 ///< 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. TODO original value was 5 minutes +#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 ( 3 * 60 * MS_PER_SECOND ) ///< Reservoirs 1 & 2 drain timeout in ms. TODO original value was 2 minutes +#define FINAL_DRAIN_RO_PUMP_FLOW_LPM 0.6 ///< 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.1 ///< 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.5 * 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.5 * 60 * MS_PER_SECOND ) ///< Flush with fresh water wait time in milliseconds. + // ********** private data ********** +static DG_FLUSH_STATE_T flushState = DG_FLUSH_STATE_START; ///< Current active flush state. +static DG_FLUSH_STATE_T prevFlushState = DG_FLUSH_STATE_START; ///< Previous flush state. +static U32 rsrvrFillStableTimeCounter = 0; ///< Reservoirs fill stable time counter. +static U32 overallFlushElapsedTimeStart = 0; ///< Overall flush mode elapsed time start. +static U32 stateTimerStart = 0; ///< 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 = NUM_OF_DG_RESERVOIR_STATUS; ///< Reservoir 1 status. +static DG_RESERVOIR_STATUS_T rsrvr2Status = NUM_OF_DG_RESERVOIR_STATUS; ///< Reservoir 2 status. +static BOOL isThisInitialDrain = TRUE; ///< Initial drain boolean flag. +static U32 dataPublishCounter = 0; ///< Flush data publish counter. +static BOOL hasWaterCancellationBeenSet = FALSE; ///< Water cancellation set/not set boolean flag. +static F32 flushLinesVolumeL = 0.0; ///< Volume of water pumped by RO pump during flush lines state. + // ********** private function prototypes ********** -/************************************************************************* - * @brief initFlushMode - * The initFlushMode function initializes flush Mode module. - * @details - * Inputs : none - * Outputs : none - * @param none +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 ); + +/*********************************************************************//** + * @brief + * The initFlushMode function initializes flush mode module. + * @details Inputs: none + * @details Outputs: flushState, prevFlushState, rsrvrFillStableTimeCounter, + * overallFlushElapsedTime, isThisInitialDrain, dataPublishCounter, + * rsrvr1Status, rsrvr2Status, hasWaterCancellationBeenSet, + * flushLinesVolumeL * @return none *************************************************************************/ void initFlushMode( void ) { + // Initialize the variables + flushState = DG_FLUSH_STATE_START; + prevFlushState = DG_FLUSH_STATE_START; + 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; } -/************************************************************************* - * @brief transitionToFlushMode - * The transitionToFlushMode function prepares for transition to \n - * flush mode. - * @details - * Inputs : none - * Outputs : - * @param none +/*********************************************************************//** + * @brief + * The transitionToFlushMode function prepares for transition to flush mode. + * @details Inputs: none + * @details Outputs: Prepares for transition to flush mode * @return none *************************************************************************/ void transitionToFlushMode( void ) { + // Reset all the actuators + deenergizeActuators(); + + initFlushMode(); } -/************************************************************************* - * @brief execFlushMode - * The execFlushMode function executes the flush Mode state machine. - * @details - * Inputs : none - * Outputs : - * @param none - * @return none +/*********************************************************************//** + * @brief + * The execFlushMode function executes the flush mode state machine. + * @details Inputs: flushState + * @details Outputs: flushState + * @return current state *************************************************************************/ -void execFlushMode( void ) +U32 execFlushMode( void ) { + // 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; + + // Reset the load cells lowest weight prior to starting the run + resetReservoirsLowestWeight(); + + // Start overall flush timer + overallFlushElapsedTimeStart = getMSTimerCount(); + + // Close VPi to prevent wasting water + setValveState( VPI, VALVE_STATE_CLOSED ); + + // Request a tare for reservoir 1 + tareReservoir(); + + // Set the actuators to drain R1 + setValveState( VRD, VALVE_STATE_R1_C_TO_NC ); + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + + 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 2 + tareReservoir(); + } + + // Set the actuators to drain R2 + // NOTE: Drain pump is already on and VDr is already on drain state + setValveState( VRD, VALVE_STATE_R2_C_TO_NO ); + + 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 ) + { + // Set the actuators to flush drain + setValveState( VPI, VALVE_STATE_OPEN ); + setValveState( VPD, VALVE_STATE_DRAIN_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( VRC, VALVE_STATE_DRAIN_C_TO_NO ); + setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); + setROPumpTargetFlowRate( FINAL_DRAIN_RO_PUMP_FLOW_LPM, RO_PUMP_MAX_PRESSURE_PSI ); + + 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 ); + setROPumpTargetFlowRate( 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 ) ) + { + // TODO turn on the concentrate pumps + 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 ) ) + { + // TODO turn off the concentrate 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 ); + stateTimerStart = getMSTimerCount(); + + rsrvr1Status = DG_RESERVOIR_BELOW_TARGET; + rsrvr2Status = DG_RESERVOIR_BELOW_TARGET; + 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( VRD, VALVE_STATE_R1_C_TO_NC ); + 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(); + } + // 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 + if ( DG_RESERVOIR_BELOW_TARGET == rsrvr2Status ) + { + rsrvr2Status = getRsrvrFillStatus( DG_RESERVOIR_2, RSRVRS_FULL_VOL_ML, RSRVRS_FILL_UP_TIMEOUT_MS ); + + U32 drainPumpRPM = getTargetDrainPumpRPM(); + // 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( VRD, VALVE_STATE_R1_C_TO_NC ); + setDrainPumpTargetRPM( DRAIN_PUMP_TARGET_RPM ); + + stateTimerStart = getMSTimerCount(); + isThisInitialDrain = FALSE; + rsrvr1Status = DG_RESERVOIR_ABOVE_TARGET; + 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 = getMeasuredROFlowRate(); + 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 ); + setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); + setROPumpTargetFlowRate( 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(); + + 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(); + + 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 ); + + // 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 ); + + // 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 ) + { + // Set the drain valve to reservoir 1 + setValveState( VRD, VALVE_STATE_R1_C_TO_NC ); + } + } + 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 ) + { + // 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 + 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; + + BOOL isDrainComplete = hasTargetDrainVolumeBeenReached( r, drainSteadyStateTimeout ); + + if ( TRUE == isDrainComplete ) + { + // Set the state timer in case it needs to be used for another timeout check + stateTimerStart = getMSTimerCount(); + status = DG_RESERVOIR_REACHED_TARGET; + } + else if ( TRUE == didTimeout( stateTimerStart, timeout ) ) + { + // Failed to drain on time + prevFlushState = flushState; + 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; + + broadcastFlushData( &data ); + + dataPublishCounter = 0; + } +} + +/**@}*/