Index: firmware/App/Modes/ModeFill.c =================================================================== diff -u -r499e5de29e706d09f79ba22511068990c4044e84 -rfeb93744f73bc0a3d58841bb02bd05c38357f35d --- firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision 499e5de29e706d09f79ba22511068990c4044e84) +++ firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision feb93744f73bc0a3d58841bb02bd05c38357f35d) @@ -1,164 +1,276 @@ /************************************************************************** - * - * 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 ModeFill.c - * - * @date 19-Nov-2019 - * @author L. Baloa - * - * @brief Top-level state machine for the fill 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 ModeFill.c +* +* @author (last) Quang Nguyen +* @date (last) 25-Aug-2020 +* +* @author (original) Leonardo Baloa +* @date (original) 19-Nov-2019 +* +***************************************************************************/ +#include "ConcentratePumps.h" +#include "ConductivitySensors.h" +#include "FPGA.h" +#include "LoadCell.h" #include "ModeFill.h" #include "OperationModes.h" +#include "PersistentAlarm.h" +#include "Pressures.h" +#include "Reservoirs.h" +#include "ROPump.h" +#include "TemperatureSensors.h" #include "Timers.h" +#include "Valves.h" -#ifdef RM46_EVAL_BOARD_TARGET - #include "CPLD.h" -#endif +/** + * @addtogroup DGFillMode + * @{ + */ -// ********** private data ********** -static volatile enum FillModeSubModes -{ - CHECK_INLET_WATER = 0, - CREATE_PRODUCT_WATER, - DIALYSATE_PRODUCTION, - DELIVER_DIALYSATE -} fillCurrentSubMode; +// ********** private definitions ********** -static U32 timer; -static U16 toggle_counter = 0; +#define FILL_MIN_RO_FLOW_RATE 0.6 ///< Minimum RO flow rate in fill mode. +#define FILL_MAX_RO_FLOW_RATE 1.0 ///< Maximum RO flow rate in fill mode. +#define FILL_TARGET_RO_FLOW_RATE 0.8 ///< Target RO flow rate in fill mode. +#define FILL_TARGET_RO_PRESSURE_PSI 120 ///< Target RO pressure in fill mode. +#define RO_FLOW_RATE_OUT_OF_RANGE_PERSISTENCE_PERIOD ( 5 * MS_PER_SECOND ) ///< Persistence period for RO flow rate out of range. -#define QUARTER_SECOND 250 -#define HALF_SECOND 500 +#define DIALYSATE_ACID_CONCENTRATE_RATIO ( 2.35618 / FRACTION_TO_PERCENT_FACTOR ) ///< Ratio between RO water and acid concentrate. +#define DIALYSATE_BICARB_CONCENTRATE_RATIO ( 4.06812 / FRACTION_TO_PERCENT_FACTOR ) ///< Ratio between RO water and bicarbonate concentrate. +#define DIALYSATE_FILL_TIME_OUT ( 5 * SEC_PER_MIN * MS_PER_SECOND ) ///< Time out period when reservoir is not filled with correct dialysate. +// ********** private data ********** + +static DG_FILL_MODE_STATE_T fillState; ///< Currently active fill state. +static U32 dialysateFillStartTime; ///< Current time when starting to fill dialysate. + // ********** private function prototypes ********** -/************************************************************************* - * @brief initFillMode - * The initFillMode function initializes the Fill Mode module. - * @details - * Inputs : none - * Outputs : Fill Mode module initialized. - * @param none +static DG_FILL_MODE_STATE_T handleCheckInletWaterState( void ); +static DG_FILL_MODE_STATE_T handleDialysateProductionState( void ); +static DG_FILL_MODE_STATE_T handleDeliverDialysateState( void ); + +static void handleDialysateMixing( void ); + +/*********************************************************************//** + * @brief + * The initFillMode function initializes the fill mode module. + * @details Inputs: none + * @details Outputs: Fill mode module initialized * @return none *************************************************************************/ void initFillMode( void ) { - fillCurrentSubMode = CHECK_INLET_WATER; - timer = getMSTimerCount(); + fillState = DG_FILL_MODE_STATE_START; + + initPersistentAlarm( PERSISTENT_ALARM_RO_PUMP_FLOW_RATE_OUT_OF_RANGE, ALARM_ID_RO_PUMP_FLOW_RATE_OUT_OF_RANGE, + FALSE, RO_FLOW_RATE_OUT_OF_RANGE_PERSISTENCE_PERIOD, RO_FLOW_RATE_OUT_OF_RANGE_PERSISTENCE_PERIOD ); } -/************************************************************************* - * @brief transitionToFillMode - * The transitionToFillMode function prepares for transition to \n - * fill mode. - * @details - * Inputs : none - * Outputs : - * @param none +/*********************************************************************//** + * @brief + * The transitionToFillMode function prepares for transition to fill mode. + * @details Inputs: none + * @details Outputs: Re-initialized fill mode * @return none *************************************************************************/ void transitionToFillMode( void ) { - fillCurrentSubMode = CHECK_INLET_WATER; - timer = getMSTimerCount(); - toggle_counter = 0; + fillState = DG_FILL_MODE_STATE_START; + dialysateFillStartTime = getMSTimerCount(); + + // set initial actuator states + setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); + setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); } -/************************************************************************* - * @brief execFillMode - * The execFillMode function executes the Fill Mode state machine. - * @details - * Inputs : none - * Outputs : - * @param none - * @return none +/*********************************************************************//** + * @brief + * The execFillMode function executes the fill mode state machine. + * @details Inputs: fillState + * @details Outputs: Check water quality, fill mode state machine executed + * @return current state. *************************************************************************/ -void execFillMode( void ) +U32 execFillMode( void ) { + // check inlet water conductivity, temperature, pressure, and RO rejection ratio + checkInletWaterConductivity(); + checkInletWaterTemperature(); + checkInletPressure(); + checkRORejectionRatio(); - switch (fillCurrentSubMode){ + // check if run out of time to fill the reservoir + if ( didTimeout( dialysateFillStartTime, DIALYSATE_FILL_TIME_OUT ) ) + { + activateAlarmNoData( ALARM_ID_DG_DIALYSATE_FILL_OUT_OF_TIME ); + } - case CHECK_INLET_WATER: + // execute current Fill state + switch ( fillState ) + { + case DG_FILL_MODE_STATE_START: + fillState = DG_FILL_MODE_STATE_CHECK_INLET_WATER; + break; - // We check every half second to toggle LED - if (TRUE == didTimeout(timer, QUARTER_SECOND)) - { - timer = getMSTimerCount(); // Reset timer - #ifdef RM46_EVAL_BOARD_TARGET - toggleUserLED(); - #endif - toggle_counter++; - } + case DG_FILL_MODE_STATE_CHECK_INLET_WATER: + fillState = handleCheckInletWaterState(); + break; - if (toggle_counter == 8) - { - toggle_counter = 0; + case DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION: + fillState = handleDialysateProductionState(); + break; - // switch to submode - fillCurrentSubMode = CREATE_PRODUCT_WATER; - } + case DG_FILL_MODE_STATE_DELIVER_DIALYSATE: + fillState = handleDeliverDialysateState(); + break; + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, 0, fillState ) // TODO - add s/w fault enum to 1st data param + fillState = DG_FILL_MODE_STATE_START; break; + } - case CREATE_PRODUCT_WATER: + return fillState; +} - // We check every half second to toggle LED - if (TRUE == didTimeout(timer, HALF_SECOND)) - { - timer = getMSTimerCount(); // Reset timer - #ifdef RM46_EVAL_BOARD_TARGET - toggleUserLED(); - #endif - toggle_counter++; - } +/*********************************************************************//** + * @brief + * The handleCheckInletWaterState function checks for inlet water quality + * before jumping to dialysate production state. + * @details Inputs: Temperature and conductivity alarms + * @details Outputs: request concentrate pump on and set RO pump flow rate + * @return the next state + *************************************************************************/ +static DG_FILL_MODE_STATE_T handleCheckInletWaterState( void ) +{ + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_CHECK_INLET_WATER; - if (toggle_counter == 4) - { - toggle_counter = 0; + BOOL const isWaterTemperatureGood = !isAlarmActive( ALARM_ID_INLET_WATER_HIGH_TEMPERATURE ) && !isAlarmActive( ALARM_ID_INLET_WATER_LOW_TEMPERATURE ); + BOOL const isWaterConductivityGood = !isAlarmActive( ALARM_ID_INLET_WATER_HIGH_CONDUCTIVITY ) && !isAlarmActive( ALARM_ID_INLET_WATER_LOW_CONDUCTIVITY ) && + !isAlarmActive( ALARM_ID_RO_REJECTION_RATIO_OUT_OF_RANGE ); - // switch to submode - fillCurrentSubMode = DIALYSATE_PRODUCTION; - } - break; + BOOL isInletWaterReady = isWaterTemperatureGood && isWaterConductivityGood; - case DIALYSATE_PRODUCTION: - // We check every half second to toggle LED - if (TRUE == didTimeout(timer, HALF_SECOND + QUARTER_SECOND)) - { - timer = getMSTimerCount(); // Reset timer - #ifdef RM46_EVAL_BOARD_TARGET - toggleUserLED(); - #endif - toggle_counter++; - } +#ifdef DISABLE_DIALYSATE_CHECK + isInletWaterReady = TRUE; +#endif - if (toggle_counter == 4) - { - toggle_counter = 0; + if ( isInletWaterReady ) + { + // Concentrate pumps on request and set RO pump to flow rate 800 mL/min + requestConcentratePumpsOn( CONCENTRATEPUMPS_CP1 ); + requestConcentratePumpsOn( CONCENTRATEPUMPS_CP2 ); + // TODO: Change to set flow rate once RO pump driver is updated + // setROPumpFlowRate( FILL_TARGET_RO_FLOW_RATE ); + setROPumpTargetPressure( FILL_TARGET_RO_PRESSURE_PSI, PUMP_CONTROL_MODE_CLOSED_LOOP ); - // switch to submode - fillCurrentSubMode = DELIVER_DIALYSATE; - } - break; + result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; + } - case DELIVER_DIALYSATE: + return result; +} - if(toggle_counter == 0) - { - #ifdef RM46_EVAL_BOARD_TARGET - setUserLED(TRUE); - #endif - toggle_counter++; - } - break; +/*********************************************************************//** + * @brief + * The handleDialysateProductionState function executes the dialysate production + * state of the fill mode state machine. + * @details Inputs: none + * @details Outputs: none + * @return the next state + *************************************************************************/ +static DG_FILL_MODE_STATE_T handleDialysateProductionState( void ) +{ + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; + + handleDialysateMixing(); + checkConcentrateConductivity(); + + BOOL isDialysateProductionGood = ( !isAlarmActive( ALARM_ID_POST_ACID_CONDUCTIVITY_OUT_OF_RANGE ) && !isAlarmActive( ALARM_ID_POST_BICARB_CONDUCTIVITY_OUT_OF_RANGE ) ); + +#ifdef DISABLE_DIALYSATE_CHECK + isDialysateProductionGood = TRUE; +#endif + + // TODO - transition when temperature and mix is in range + if ( isDialysateProductionGood ) + { + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); + result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; } + + return result; } +/*********************************************************************//** + * @brief + * The handleDeliverDialysateState function executes the deliver dialysate + * state of the fill mode state machine. + * @details Inputs: none + * @details Outputs: Deliver dialysate + * @return the next state + *************************************************************************/ +static DG_FILL_MODE_STATE_T handleDeliverDialysateState( void ) +{ + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; + RESERVOIR_ID_T inactiveReservoir = getInactiveReservoir(); + + handleDialysateMixing(); + checkConcentrateConductivity(); + + BOOL isDialysateConductivityBad = ( isAlarmActive( ALARM_ID_POST_ACID_CONDUCTIVITY_OUT_OF_RANGE ) || isAlarmActive( ALARM_ID_POST_BICARB_CONDUCTIVITY_OUT_OF_RANGE ) ); + +#ifdef DISABLE_DIALYSATE_CHECK + isDialysateConductivityBad = FALSE; +#endif + + // TODO - transition back when temperature or mix out of range + if ( isDialysateConductivityBad ) + { + setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); + result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; + } + + // if we've reached our target fill to volume (by weight), we're done filling - go back to re-circ mode + if ( hasTargetFillVolumeBeenReached( inactiveReservoir ) ) + { + requestConcentratePumpsOff( CONCENTRATEPUMPS_CP1 ); + requestConcentratePumpsOff( CONCENTRATEPUMPS_CP2 ); + requestNewOperationMode( DG_MODE_CIRC ); + } + + return result; +} + +/*********************************************************************//** + * @brief + * The handleDialysateMixing function handles the dialysate mixing by setting + * the concentrate pump speed relative to the RO pump flow rate. + * @details Inputs: none + * @details Outputs: Set concentrate pump speed relative to RO pump flow rate + * @return none + *************************************************************************/ +static void handleDialysateMixing( void ) +{ + // Set concentrate pumps speed based off RO pump flow rate + F32 const measuredROFlowRate = getMeasuredROFlowRate(); + F32 const acidCP1PumpFlowRate = DIALYSATE_ACID_CONCENTRATE_RATIO * measuredROFlowRate * ML_PER_LITER; + F32 const bicarbCP2PumpFlowRate = DIALYSATE_BICARB_CONCENTRATE_RATIO * measuredROFlowRate * ML_PER_LITER; + + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1, acidCP1PumpFlowRate ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2, bicarbCP2PumpFlowRate ); + + BOOL const isROPumpFlowRateOutOfRange = ( measuredROFlowRate <= FILL_MIN_RO_FLOW_RATE ) || ( measuredROFlowRate >= FILL_MAX_RO_FLOW_RATE ); + + // TODO add the right limit values + checkPersistentAlarm( PERSISTENT_ALARM_RO_PUMP_FLOW_RATE_OUT_OF_RANGE, isROPumpFlowRateOutOfRange, measuredROFlowRate, 0 ); +} + +/**@}*/