Index: firmware/App/Modes/ModeFill.c =================================================================== diff -u -rf7292cd3376119210980f14e8bdb0ec5e0cf5df5 -r6835f89b10dfac6fa12d27b357a85670fdcde088 --- firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision f7292cd3376119210980f14e8bdb0ec5e0cf5df5) +++ firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision 6835f89b10dfac6fa12d27b357a85670fdcde088) @@ -18,15 +18,18 @@ #include "ConcentratePumps.h" #include "ConductivitySensors.h" #include "FPGA.h" +#include "Heaters.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 "UVReactors.h" #include "Valves.h" /** @@ -36,25 +39,41 @@ // ********** 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. + +#define CONCENTRATE_TEST_TIME_OUT_MS ( 30 * MS_PER_SECOND ) ///< Concentrate test time out period in ms. +#define WATER_QUALITY_CHECK_TIME_OUT_MS ( 30 * MS_PER_SECOND ) ///< Inlet water quality check time out period in ms. + +#define CONDUCTIVITY_ERROR_PERSISTENCE_PERIOD_MS ( 5 * MS_PER_SECOND ) ///< Persistence period for conductivity error. + +/// Multiplier to convert flow (L/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 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_LPM; ///< Total RO flow rate over period of time. +static U32 concentrateTestStartTime; ///< Starting time for concentrate test. +static U32 waterQualityCheckStartTime; ///< Starting time for inlet water quality check. // ********** 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 ); @@ -70,10 +89,13 @@ void initFillMode( void ) { fillState = DG_FILL_MODE_STATE_START; + dialysateFillStartTime = 0; + reservoirBaseWeight = 0.0; + totalROFlowRate_LPM = 0.0; + concentrateTestStartTime = 0; - // TODO figure this out - //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 ); + 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 ); } /*********************************************************************//** @@ -85,12 +107,19 @@ *************************************************************************/ void transitionToFillMode( void ) { - fillState = DG_FILL_MODE_STATE_START; + initFillMode(); + dialysateFillStartTime = getMSTimerCount(); // 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 ); } /*********************************************************************//** @@ -108,23 +137,35 @@ checkInletPressure(); checkRORejectionRatio(); + // TODO: Check for open straw door status and alarm if closed + // check if run out of time to fill the reservoir - if ( didTimeout( dialysateFillStartTime, DIALYSATE_FILL_TIME_OUT ) ) + if ( TRUE == didTimeout( dialysateFillStartTime, DIALYSATE_FILL_TIME_OUT ) ) { activateAlarmNoData( ALARM_ID_DG_DIALYSATE_FILL_OUT_OF_TIME ); + requestNewOperationMode( DG_MODE_CIRC ); } // execute current Fill state switch ( fillState ) { case DG_FILL_MODE_STATE_START: + waterQualityCheckStartTime = getMSTimerCount(); 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_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; @@ -134,7 +175,7 @@ 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 + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_FILL_MODE_INVALID_EXEC_STATE, fillState ) fillState = DG_FILL_MODE_STATE_START; break; } @@ -144,6 +185,59 @@ /*********************************************************************//** * @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 ) ) + { +#ifndef DISABLE_DIALYSATE_CHECK + 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 ) ); + } +#endif + } + + 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 @@ -153,28 +247,92 @@ 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 = isWaterQualityGood(); - BOOL isInletWaterReady = isWaterTemperatureGood && isWaterConductivityGood; +#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; + } -#ifdef DISABLE_DIALYSATE_CHECK - isInletWaterReady = TRUE; + if ( TRUE == didTimeout( waterQualityCheckStartTime, WATER_QUALITY_CHECK_TIME_OUT_MS ) ) + { + activateAlarmNoData( ALARM_ID_DG_BAD_INLET_WATER_QUALITY ); + requestNewOperationMode( DG_MODE_CIRC ); + } + + return result; +} + +/*********************************************************************//** + * @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(); + +#ifndef DISABLE_DIALYSATE_CHECK + if ( TRUE == isBicarbConductivityInRange() ) #endif + { + concentrateTestStartTime = getMSTimerCount(); + requestConcentratePumpsOn( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpsOff( CONCENTRATEPUMPS_CP2_BICARB ); + result = DG_FILL_MODE_STATE_ACID_PUMP_CHECK; + } - if ( isInletWaterReady ) + if ( TRUE == didTimeout( concentrateTestStartTime, CONCENTRATE_TEST_TIME_OUT_MS ) ) { - // Concentrate pumps on request and set RO pump to flow rate 800 mL/min - requestConcentratePumpsOn( CONCENTRATEPUMPS_CP1 ); - requestConcentratePumpsOn( CONCENTRATEPUMPS_CP2 ); + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_BICARB_CONDUCTIVITY_OUT_OF_RANGE, getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ) ); + requestNewOperationMode( DG_MODE_CIRC ); + } - setROPumpTargetFlowRate( TARGET_RO_FLOW_RATE_L, TARGET_RO_PRESSURE_PSI ); + 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(); + +#ifndef DISABLE_DIALYSATE_CHECK + if ( TRUE == isAcidConductivityInRange() ) +#endif + { + 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; } @@ -191,16 +349,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; @@ -220,29 +373,34 @@ 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(); + DG_RESERVOIR_ID_T inactiveReservoir = getInactiveReservoir(); + totalROFlowRate_LPM += getMeasuredROFlowRate(); 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 ) + 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 if ( hasTargetFillVolumeBeenReached( inactiveReservoir ) ) { - requestConcentratePumpsOff( CONCENTRATEPUMPS_CP1 ); - requestConcentratePumpsOff( CONCENTRATEPUMPS_CP2 ); + F32 const filledVolume_mL = getReservoirWeight( inactiveReservoir ) - reservoirBaseWeight; + F32 const integratedVolume_mL = totalROFlowRate_LPM * RO_FLOW_INTEGRATOR * ACID_BICARB_CONCENTRATE_ADDITION_MULTIPLER; + F32 const integratedVolumeToLoadCellReadingPercent = fabs( 1 - ( filledVolume_mL / integratedVolume_mL ) ); + + if ( integratedVolumeToLoadCellReadingPercent > FLOW_INTEGRATED_VOLUME_CHECK_TOLERANCE ) + { +#ifndef DISABLE_MIXING + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DG_FLOW_METER_CHECK_FAILURE, filledVolume_mL, integratedVolume_mL ); +#endif + } + requestNewOperationMode( DG_MODE_CIRC ); } @@ -259,17 +417,15 @@ *************************************************************************/ static void handleDialysateMixing( void ) { +#ifndef DISABLE_MIXING // 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 ); - - //checkPersistentAlarm( PERSISTENT_ALARM_RO_PUMP_FLOW_RATE_OUT_OF_RANGE, isROPumpFlowRateOutOfRange, measuredROFlowRate ); TODO figure this out + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, acidCP1PumpFlowRate ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, bicarbCP2PumpFlowRate ); +#endif } /**@}*/