Index: firmware/App/Modes/ModeFill.c =================================================================== diff -u -rf43eb1e9e0803776ec7420b16e1db8760b020bd9 -rfeb93744f73bc0a3d58841bb02bd05c38357f35d --- firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision f43eb1e9e0803776ec7420b16e1db8760b020bd9) +++ firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision feb93744f73bc0a3d58841bb02bd05c38357f35d) @@ -1,192 +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 "ModeFill.h" +#include "Valves.h" -#ifdef RM46_EVAL_BOARD_TARGET - #include "CPLD.h" -#endif - /** - * @addtogroup FillMode + * @addtogroup DGFillMode * @{ */ // ********** private definitions ********** -#define QUARTER_SECOND 250 -#define HALF_SECOND 500 +#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. -/// Enumberation of fill mode states. -typedef enum Fill_Mode_States -{ - FILL_MODE_STATE_START = 0, ///< Start fill mode state. - FILL_MODE_STATE_CHECK_INLET_WATER, ///< Check inlet water state. - FILL_MODE_STATE_CREATE_PRODUCT_WATER, ///< Create product water state. - FILL_MODE_STATE_DIALYSATE_PRODUCTION, ///< Dialysate production state. - FILL_MODE_STATE_DELIVER_DIALYSATE, ///< Deliver dialysate state. - NUM_OF_FILL_MODE_STATES ///< Number of fill mode states. -} FILL_MODE_STATE_T; +#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 FILL_MODE_STATE_T fillState; ///< Currently active fill state. +static DG_FILL_MODE_STATE_T fillState; ///< Currently active fill state. +static U32 dialysateFillStartTime; ///< Current time when starting to fill dialysate. // ********** private function prototypes ********** -static FILL_MODE_STATE_T handleCheckInletWaterState( void ); -static FILL_MODE_STATE_T handleCreateProductWaterState( void ); -static FILL_MODE_STATE_T handleDialysateProductionState( void ); -static FILL_MODE_STATE_T handleDeliverDialysateState( void ); +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 initFillMode - * The initFillMode function initializes the Fill Mode module. - * @details - * Inputs : none - * Outputs : Fill Mode module initialized. + * @brief + * The initFillMode function initializes the fill mode module. + * @details Inputs: none + * @details Outputs: Fill mode module initialized * @return none *************************************************************************/ void initFillMode( void ) { - fillState = FILL_MODE_STATE_START; + 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 : fillState + * @brief + * The transitionToFillMode function prepares for transition to fill mode. + * @details Inputs: none + * @details Outputs: Re-initialized fill mode * @return none *************************************************************************/ void transitionToFillMode( void ) { - fillState = FILL_MODE_STATE_START; + 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 : fillState - * Outputs : fillState - * @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(); + + // 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 ); + } + // execute current Fill state switch ( fillState ) { - case FILL_MODE_STATE_START: - fillState = FILL_MODE_STATE_CHECK_INLET_WATER; + case DG_FILL_MODE_STATE_START: + fillState = DG_FILL_MODE_STATE_CHECK_INLET_WATER; break; - case FILL_MODE_STATE_CHECK_INLET_WATER: + case DG_FILL_MODE_STATE_CHECK_INLET_WATER: fillState = handleCheckInletWaterState(); break; - case FILL_MODE_STATE_CREATE_PRODUCT_WATER: - fillState = handleCreateProductWaterState(); - break; - - case FILL_MODE_STATE_DIALYSATE_PRODUCTION: + case DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION: fillState = handleDialysateProductionState(); break; - case FILL_MODE_STATE_DELIVER_DIALYSATE: + case DG_FILL_MODE_STATE_DELIVER_DIALYSATE: fillState = handleDeliverDialysateState(); break; default: - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_SOFTWARE_FAULT, 0, fillState ) // TODO - add s/w fault enum to 1st data param - fillState = FILL_MODE_STATE_START; + 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; } + + return fillState; } /*********************************************************************//** * @brief - * The handleCheckInletWaterState function executes the Check Inlet Water \n - * state of the Fill Mode state machine. - * @details - * Inputs : none - * Outputs : - * @param none + * 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 FILL_MODE_STATE_T handleCheckInletWaterState( void ) +static DG_FILL_MODE_STATE_T handleCheckInletWaterState( void ) { - FILL_MODE_STATE_T result = FILL_MODE_STATE_CHECK_INLET_WATER; + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_CHECK_INLET_WATER; + 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 ); + + BOOL isInletWaterReady = isWaterTemperatureGood && isWaterConductivityGood; + +#ifdef DISABLE_DIALYSATE_CHECK + isInletWaterReady = TRUE; +#endif + + 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 ); + + result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; + } + return result; } /*********************************************************************//** * @brief - * The handleCreateProductWaterState function executes the Create Product \n - * Water state of the Fill Mode state machine. - * @details - * Inputs : none - * Outputs : - * @param none + * 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 FILL_MODE_STATE_T handleCreateProductWaterState( void ) +static DG_FILL_MODE_STATE_T handleDialysateProductionState( void ) { - FILL_MODE_STATE_T result = FILL_MODE_STATE_CREATE_PRODUCT_WATER; + 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 handleDialysateProductionState function executes the Dialysate Production \n - * state of the Fill Mode state machine. - * @details - * Inputs : none - * Outputs : - * @param none + * 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 FILL_MODE_STATE_T handleDialysateProductionState( void ) +static DG_FILL_MODE_STATE_T handleDeliverDialysateState( void ) { - FILL_MODE_STATE_T result = FILL_MODE_STATE_DIALYSATE_PRODUCTION; + 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 handleDeliverDialysateState function executes the Deliver Dialysate \n - * state of the Fill Mode state machine. - * @details - * Inputs : none - * Outputs : - * @param none - * @return the next state + * 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 FILL_MODE_STATE_T handleDeliverDialysateState( void ) +static void handleDialysateMixing( void ) { - FILL_MODE_STATE_T result = FILL_MODE_STATE_DELIVER_DIALYSATE; + // 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; - return result; + 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 ); } /**@}*/