/************************************************************************** * * 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 "TaskGeneral.h" #include "TemperatureSensors.h" #include "Timers.h" #include "Valves.h" /** * @addtogroup DGFillMode * @{ */ // ********** private definitions ********** #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 TARGET_RO_PRESSURE_PSI 130 ///< Target pressure for RO pump. #define TARGET_RO_FLOW_RATE_L 0.8 ///< Target flow rate for RO pump. #define RO_FLOW_RATE_OUT_OF_RANGE_PERSISTENCE_PERIOD ( 5 * MS_PER_SECOND ) ///< Persistence period for RO flow rate out of range. #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. #define ACID_BICARB_CONCENTRATE_ADDITION_MULTIPLER 1.06 ///< Acid and bicarbonate concentrates make up around 6% to total volume. #define FLOW_INTEGRATED_VOLUME_CHECK_TOLERANCE 0.1 ///< Flow integrated volume has 10% tolerance compare to load cell reading. /// Multiplier to conver flow (mL/min) into volume (mL) for period of general task interval. static const F32 RO_FLOW_INTEGRATOR = ( ( ML_PER_LITER * TASK_GENERAL_INTERVAL ) / ( SEC_PER_MIN * MS_PER_SECOND ) ); // ********** private data ********** static DG_FILL_MODE_STATE_T fillState; ///< Currently active fill state. static U32 dialysateFillStartTime; ///< Current time when starting to fill dialysate. static F32 reservoirBaseWeight; ///< Fill reservoir base weight. static F32 totalROFlowRate; ///< Total RO flow rate over period of time. // ********** private function prototypes ********** 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 ) { fillState = DG_FILL_MODE_STATE_START; } /*********************************************************************//** * @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 = DG_FILL_MODE_STATE_START; dialysateFillStartTime = getMSTimerCount(); reservoirBaseWeight = 0.0; totalROFlowRate = 0.0; // set initial actuator states setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); } /*********************************************************************//** * @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. *************************************************************************/ 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 DG_FILL_MODE_STATE_START: fillState = DG_FILL_MODE_STATE_CHECK_INLET_WATER; break; case DG_FILL_MODE_STATE_CHECK_INLET_WATER: fillState = handleCheckInletWaterState(); break; case DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION: fillState = handleDialysateProductionState(); break; 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; } return fillState; } /*********************************************************************//** * @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; DG_RESERVOIR_ID_T inactiveReservoir = getInactiveReservoir(); 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 ) { reservoirBaseWeight = getReservoirWeight( inactiveReservoir ); // Concentrate pumps on request and set RO pump to flow rate 800 mL/min requestConcentratePumpsOn( CONCENTRATEPUMPS_CP1 ); requestConcentratePumpsOn( CONCENTRATEPUMPS_CP2 ); setROPumpTargetFlowRate( TARGET_RO_FLOW_RATE_L, TARGET_RO_PRESSURE_PSI ); result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; } return result; } /*********************************************************************//** * @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_ACID_CONDUCTIVITY_OUT_OF_RANGE ) && !isAlarmActive( ALARM_ID_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; DG_RESERVOIR_ID_T inactiveReservoir = getInactiveReservoir(); handleDialysateMixing(); checkConcentrateConductivity(); totalROFlowRate += getMeasuredROFlowRate(); BOOL isDialysateConductivityBad = ( isAlarmActive( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE ) || isAlarmActive( ALARM_ID_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 ) ) { F32 const filledWeight = getReservoirWeight( inactiveReservoir ) - reservoirBaseWeight; F32 const integratedVolume = totalROFlowRate * RO_FLOW_INTEGRATOR * ACID_BICARB_CONCENTRATE_ADDITION_MULTIPLER; F32 const integratedVolumeToLoadCellReadingPercent = 1 - ( filledWeight / integratedVolume ); if ( integratedVolumeToLoadCellReadingPercent > FLOW_INTEGRATED_VOLUME_CHECK_TOLERANCE ) { SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DG_FLOW_METER_CHECK_FAILURE, filledWeight, integratedVolume ); } 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 ); } /**@}*/