Index: firmware/App/Modes/ModeFill.c =================================================================== diff -u -r484b185f0cf4b2ea0ba9de331573952b1b5124b4 -rb1dc3df084a8517ca1575bdbf741fecd96d56a12 --- firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision 484b185f0cf4b2ea0ba9de331573952b1b5124b4) +++ firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision b1dc3df084a8517ca1575bdbf741fecd96d56a12) @@ -18,6 +18,7 @@ #include "ConcentratePumps.h" #include "ConductivitySensors.h" #include "FPGA.h" +#include "Heaters.h" #include "LoadCell.h" #include "ModeFill.h" #include "OperationModes.h" @@ -28,6 +29,7 @@ #include "TaskGeneral.h" #include "TemperatureSensors.h" #include "Timers.h" +#include "UVReactors.h" #include "Valves.h" /** @@ -37,11 +39,8 @@ // ********** 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. @@ -51,6 +50,10 @@ #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. +#define CONCENTRATE_TEST_TIME_OUT_MS ( 30 * MS_PER_SECOND ) ///< Concentrate test time out period in ms. + +#define CONDUCTIVITY_ERROR_PERSISTENCE_PERIOD_MS ( 5 * MS_PER_SECOND ) ///< Persistence period for conductivity error. + /// 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 ) ); @@ -60,10 +63,15 @@ 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. +static U32 concentrateTestStartTime; ///< Starting time for concentrate test. // ********** private function prototypes ********** +static BOOL isWaterQualityGood( void ); +static BOOL checkDialysateConductivity( void ); static DG_FILL_MODE_STATE_T handleCheckInletWaterState( void ); +static DG_FILL_MODE_STATE_T handleBicarbPumpCheckState( void ); +static DG_FILL_MODE_STATE_T handleAcidPumpCheckState( void ); static DG_FILL_MODE_STATE_T handleDialysateProductionState( void ); static DG_FILL_MODE_STATE_T handleDeliverDialysateState( void ); @@ -79,6 +87,13 @@ void initFillMode( void ) { fillState = DG_FILL_MODE_STATE_START; + dialysateFillStartTime = 0; + reservoirBaseWeight = 0.0; + totalROFlowRate = 0.0; + concentrateTestStartTime = 0; + + initPersistentAlarm( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE, CONDUCTIVITY_ERROR_PERSISTENCE_PERIOD_MS, CONDUCTIVITY_ERROR_PERSISTENCE_PERIOD_MS ); + initPersistentAlarm( ALARM_ID_DIALYSATE_CONDUCTIVITY_OUT_OF_RANGE, CONDUCTIVITY_ERROR_PERSISTENCE_PERIOD_MS, CONDUCTIVITY_ERROR_PERSISTENCE_PERIOD_MS ); } /*********************************************************************//** @@ -90,14 +105,19 @@ *************************************************************************/ void transitionToFillMode( void ) { - fillState = DG_FILL_MODE_STATE_START; + initFillMode(); + 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 ); + + turnOnUVReactor( INLET_UV_REACTOR ); + turnOnUVReactor( OUTLET_UV_REACTOR ); + + startPrimaryHeater(); + setROPumpTargetFlowRate( TARGET_RO_FLOW_RATE_L, TARGET_RO_PRESSURE_PSI ); } /*********************************************************************//** @@ -132,6 +152,14 @@ fillState = handleCheckInletWaterState(); break; + case DG_FILL_MODE_STATE_BICARB_PUMP_CHECK: + fillState = handleBicarbPumpCheckState(); + break; + + case DG_FILL_MODE_STATE_ACID_PUMP_CHECK: + fillState = handleAcidPumpCheckState(); + break; + case DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION: fillState = handleDialysateProductionState(); break; @@ -151,6 +179,57 @@ /*********************************************************************//** * @brief + * The isWaterQualityGood function checks for inlet water quality. + * @details Inputs: Temperature and conductivity alarms + * @details Outputs: none + * @return TRUE if water quality is good, otherwise FALSE + *************************************************************************/ +static BOOL isWaterQualityGood( void ) +{ + BOOL const isInletPressureGood = !isAlarmActive( ALARM_ID_INLET_WATER_LOW_PRESSURE ); + 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 ); + + return ( isInletPressureGood && isWaterTemperatureGood && isWaterConductivityGood ); +} + +/*********************************************************************//** + * @brief + * The checkDialysateConductivity function checks dialysate conductivity + * after adding acid and bicarbonate and triggers an alarm when conductivity + * is out of range. + * @details Inputs: CD1 and CD2 sensor conductivity + * @details Outputs: Trigger alarms when dialysate conductivity is out of allowed range + * @return TRUE if dialysate conductivity is good, otherwise FALSE + *************************************************************************/ +static BOOL checkDialysateConductivity( void ) +{ + BOOL const isDialysateConductivityGood = isDialysateConductivityInRange(); + + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_DIALYSATE_CONDUCTIVITY_OUT_OF_RANGE, !isDialysateConductivityGood ) ) + { + if ( FALSE == isAcidConductivityInRange() ) + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE, getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ) ); + } + else + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_DIALYSATE_CONDUCTIVITY_OUT_OF_RANGE, getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ) ); + } + } + + if ( TRUE == isPersistentAlarmConditionCleared( ALARM_ID_DIALYSATE_CONDUCTIVITY_OUT_OF_RANGE, !isDialysateConductivityGood ) ) + { + clearAlarmCondition( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE ); + clearAlarmCondition( ALARM_ID_DIALYSATE_CONDUCTIVITY_OUT_OF_RANGE ); + } + + return ( isDialysateConductivityGood && isWaterQualityGood() ); +} + +/*********************************************************************//** + * @brief * The handleCheckInletWaterState function checks for inlet water quality * before jumping to dialysate production state. * @details Inputs: Temperature and conductivity alarms @@ -162,29 +241,80 @@ 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 = isWaterQualityGood(); - BOOL isInletWaterReady = isWaterTemperatureGood && isWaterConductivityGood; - -#ifdef DISABLE_DIALYSATE_CHECK - isInletWaterReady = TRUE; -#endif - +#ifndef DISABLE_DIALYSATE_CHECK if ( isInletWaterReady ) +#endif { reservoirBaseWeight = getReservoirWeight( inactiveReservoir ); + concentrateTestStartTime = getMSTimerCount(); + requestConcentratePumpsOff( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpsOn( CONCENTRATEPUMPS_CP2_BICARB ); + result = DG_FILL_MODE_STATE_BICARB_PUMP_CHECK; + } - // Concentrate pumps on request and set RO pump to flow rate 800 mL/min - requestConcentratePumpsOn( CONCENTRATEPUMPS_CP1 ); - requestConcentratePumpsOn( CONCENTRATEPUMPS_CP2 ); + return result; +} - setROPumpTargetFlowRate( TARGET_RO_FLOW_RATE_L, TARGET_RO_PRESSURE_PSI ); +/*********************************************************************//** + * @brief + * The handleBicarbPumpCheckState function checks conductivity value for + * bicarbonate concentrate. + * @details Inputs: Bicarbonate conductivity sensor value + * @details Outputs: Verified bicarbonate conductivity value + * @return the next state + *************************************************************************/ +static DG_FILL_MODE_STATE_T handleBicarbPumpCheckState( void ) +{ + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_BICARB_PUMP_CHECK; + handleDialysateMixing(); + + if ( TRUE == isBicarbConductivityInRange() ) + { + concentrateTestStartTime = getMSTimerCount(); + requestConcentratePumpsOn( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpsOff( CONCENTRATEPUMPS_CP2_BICARB ); + result = DG_FILL_MODE_STATE_ACID_PUMP_CHECK; + } + + if ( TRUE == didTimeout( concentrateTestStartTime, CONCENTRATE_TEST_TIME_OUT_MS ) ) + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_BICARB_CONDUCTIVITY_OUT_OF_RANGE, getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ) ); + requestNewOperationMode( DG_MODE_CIRC ); + } + + return result; +} + +/*********************************************************************//** + * @brief + * The handleAcidPumpCheckState function checks conductivity value for + * acid concentrate. + * @details Inputs: Acid conductivity sensor value + * @details Outputs: Verified acid conductivity value + * @return the next state + *************************************************************************/ +static DG_FILL_MODE_STATE_T handleAcidPumpCheckState( void ) +{ + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_ACID_PUMP_CHECK; + + handleDialysateMixing(); + + if ( TRUE == isAcidConductivityInRange() ) + { + requestConcentratePumpsOn( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpsOn( CONCENTRATEPUMPS_CP2_BICARB ); result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; } + if ( TRUE == didTimeout( concentrateTestStartTime, CONCENTRATE_TEST_TIME_OUT_MS ) ) + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE, getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ) ); + requestNewOperationMode( DG_MODE_CIRC ); + } + return result; } @@ -201,16 +331,11 @@ 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 ) +#ifndef DISABLE_DIALYSATE_CHECK + if ( TRUE == checkDialysateConductivity() ) +#endif { setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; @@ -231,23 +356,17 @@ { DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; DG_RESERVOIR_ID_T inactiveReservoir = getInactiveReservoir(); + totalROFlowRate += getMeasuredROFlowRate(); handleDialysateMixing(); - checkConcentrateConductivity(); - totalROFlowRate += getMeasuredROFlowRate(); - - 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 ) + if ( FALSE == checkDialysateConductivity() ) { +#ifndef DISABLE_DIALYSATE_CHECK setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; +#endif } // if we've reached our target fill to volume (by weight), we're done filling - go back to re-circ mode @@ -262,8 +381,6 @@ 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 ); } @@ -285,8 +402,8 @@ 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 ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, acidCP1PumpFlowRate ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, bicarbCP2PumpFlowRate ); } /**@}*/