Index: firmware/App/Modes/ModeFill.c =================================================================== diff -u -rc0160362dc799802ec589d5b6cf4c2bd1face77e -re6f3a632890f96a5aa282922d11df148bdd06587 --- firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision c0160362dc799802ec589d5b6cf4c2bd1face77e) +++ firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision e6f3a632890f96a5aa282922d11df148bdd06587) @@ -26,6 +26,7 @@ #include "Pressures.h" #include "Reservoirs.h" #include "ROPump.h" +#include "SystemComm.h" #include "TaskGeneral.h" #include "TemperatureSensors.h" #include "Timers.h" @@ -43,38 +44,58 @@ #define TARGET_RO_FLOW_RATE_L 0.8 ///< Target flow rate for RO pump. #define DIALYSATE_FILL_TIME_OUT ( 5 * SEC_PER_MIN * MS_PER_SECOND ) ///< Time out period when reservoir is not filled with correct dialysate. +#define EMPTY_BOTTLE_DETECT_PERSISTENT_PERIOD_MS ( 5 * MS_PER_SECOND ) ///< Persistent period for empty bottle detect. +///< Persistent time interval for concentrate pumps prime. +#define CONCENTRATE_PUMP_PRIME_INTERVAL ( 3 * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) #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 CONCENTRATE_PUMP_PRIME_EXTRA_SPEED_ML_MIN 5.0 ///< Concentrate pump additional speed during priming in mL/min. +#define CONCENTRATE_TEST_TIME_OUT_MS ( 45 * 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. +#define DIALYSATE_TEMPERATURE_TOLERANCE_C 2.0 ///< Dialysate temperature tolerance in degree C. -/// 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 ) ); +#define ACID_CONCENTRATION_BOTTLE_VOLUME_ML 3000.0 ///< Volume of acid concentration in ml. +#define BICARB_CONCENTRATION_BOTTLE_VOLUME_ML 3000.0 ///< Volume of bicarb concentration in ml. +#define CONCENTRATION_BOTTLE_LOW_VOLUME_ML 100.0 ///< Concentration bottle low volume in ml. +/// Multiplier to convert flow (mL/min) into volume (mL) for period of general task interval. +static const F32 FLOW_INTEGRATOR = ( (F32)TASK_GENERAL_INTERVAL / (F32)( 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_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. +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 U32 waterQualityCheckStartTime; ///< Starting time for inlet water quality check. +static U32 concentrateTestStartTime; ///< Starting time for concentrate test. +static U32 concentratePumpPrimeCount; ///< Interval count for concentrate pump prime. + +static F32 totalROFlowRate_mL_min; ///< Total RO flow rate over period of time. +static F32 acidConductivityTotal; ///< Total of acid conductivity during fill. +static F32 dialysateConductivityTotal; ///< Total of dialysate conductivity during fill. +static U32 conductivitySampleCount; ///< Sample count of conductivity during fill. + +static OVERRIDE_F32_T usedAcidVolume_mL = { 0.0, 0.0, 0.0, 0.0 }; ///< The integrated acid concentration volume has been used in ml. +static OVERRIDE_F32_T usedBicarbVolume_mL = { 0.0, 0.0, 0.0, 0.0 }; ///< The integrated bicarb concentration volume has been used in ml. + // ********** 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 ); +static DG_FILL_MODE_STATE_T handlePausedState( void ); -static void handleDialysateMixing( void ); +static BOOL isWaterQualityGood( void ); +static BOOL checkDialysateTemperature( void ); +static void handleDialysateMixing( F32 measuredROFlowRate_mL_min ); +static F32 getUsedAcidVolume( void ); +static F32 getUsedBicarbVolume( void ); /*********************************************************************//** * @brief @@ -88,11 +109,15 @@ fillState = DG_FILL_MODE_STATE_START; dialysateFillStartTime = 0; reservoirBaseWeight = 0.0; - totalROFlowRate_LPM = 0.0; + totalROFlowRate_mL_min = 0.0; concentrateTestStartTime = 0; + acidConductivityTotal = 0.0; + dialysateConductivityTotal = 0.0; + conductivitySampleCount = 0; + concentratePumpPrimeCount = 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 ); + initPersistentAlarm( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE, 0, EMPTY_BOTTLE_DETECT_PERSISTENT_PERIOD_MS ); + initPersistentAlarm( ALARM_ID_BICARB_CONDUCTIVITY_OUT_OF_RANGE, 0, EMPTY_BOTTLE_DETECT_PERSISTENT_PERIOD_MS ); } /*********************************************************************//** @@ -104,11 +129,13 @@ *************************************************************************/ void transitionToFillMode( void ) { - initFillMode(); + DG_RESERVOIR_ID_T inactiveReservoir = getInactiveReservoir(); + initFillMode(); + reservoirBaseWeight = getReservoirWeight( inactiveReservoir ); dialysateFillStartTime = getMSTimerCount(); - // set initial actuator states + // Set initial actuator states setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); @@ -131,22 +158,27 @@ *************************************************************************/ U32 execFillMode( void ) { - // check inlet water conductivity, temperature, pressure, and RO rejection ratio + // Check inlet water conductivity, temperature, pressure, and RO rejection ratio checkInletWaterConductivity(); checkInletWaterTemperature(); checkInletPressure(); checkRORejectionRatio(); // TODO: Check for open straw door status and alarm if closed - - // check if run out of time to fill the reservoir + // Check if run out of time to fill the reservoir if ( TRUE == didTimeout( dialysateFillStartTime, DIALYSATE_FILL_TIME_OUT ) ) { activateAlarmNoData( ALARM_ID_DG_DIALYSATE_FILL_OUT_OF_TIME ); - requestNewOperationMode( DG_MODE_CIRC ); + requestNewOperationMode( DG_MODE_GENE ); } - // execute current Fill state + // Transition to generation idle mode when HD is not communicating + if ( FALSE == isHDCommunicating() ) + { + requestNewOperationMode( DG_MODE_GENE ); + } + + // Execute current Fill state switch ( fillState ) { case DG_FILL_MODE_STATE_START: @@ -174,6 +206,10 @@ fillState = handleDeliverDialysateState(); break; + case DG_FILL_MODE_STATE_PAUSED: + fillState = handlePausedState(); + break; + default: 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; @@ -185,59 +221,6 @@ /*********************************************************************//** * @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 @@ -247,25 +230,24 @@ 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 isInletWaterReady = isWaterQualityGood(); - #ifndef DISABLE_DIALYSATE_CHECK - if ( isInletWaterReady ) + if ( TRUE == isWaterQualityGood() ) #endif { - reservoirBaseWeight = getReservoirWeight( inactiveReservoir ); concentrateTestStartTime = getMSTimerCount(); requestConcentratePumpsOff( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpsOn( CONCENTRATEPUMPS_CP2_BICARB ); +#ifndef DISABLE_MIXING + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, CONCENTRATE_PUMP_MAX_SPEED ); +#endif result = DG_FILL_MODE_STATE_BICARB_PUMP_CHECK; } if ( TRUE == didTimeout( waterQualityCheckStartTime, WATER_QUALITY_CHECK_TIME_OUT_MS ) ) { activateAlarmNoData( ALARM_ID_DG_BAD_INLET_WATER_QUALITY ); - requestNewOperationMode( DG_MODE_CIRC ); + requestNewOperationMode( DG_MODE_GENE ); } return result; @@ -282,23 +264,44 @@ static DG_FILL_MODE_STATE_T handleBicarbPumpCheckState( void ) { DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_BICARB_PUMP_CHECK; + DG_BICARB_CONCENTRATES_RECORD_T bicarb = getBicarbConcentrateCalRecord(); + F32 const measuredROFlowRate_mL_min = getMeasuredROFlowRate() * ML_PER_LITER; + F32 const bicarbPumpFlowRate_mL_min = measuredROFlowRate_mL_min * bicarb.bicarbConcentrate[ CAL_DATA_BICARB_CONCENTRATE_1 ].bicarbConcMixRatio + + CONCENTRATE_PUMP_PRIME_EXTRA_SPEED_ML_MIN; - handleDialysateMixing(); - #ifndef DISABLE_DIALYSATE_CHECK - if ( TRUE == isBicarbConductivityInRange() ) + F32 const bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); +#else + F32 const bicarbConductivity = MIN_BICARB_CONCENTRATE_CONDUCTIVITY; #endif + + if ( MIN_BICARB_CONCENTRATE_CONDUCTIVITY <= bicarbConductivity ) { - concentrateTestStartTime = getMSTimerCount(); - requestConcentratePumpsOn( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpsOff( CONCENTRATEPUMPS_CP2_BICARB ); - result = DG_FILL_MODE_STATE_ACID_PUMP_CHECK; + // Reduce acid pump speed after reaching minimum conductivity + // This prevents conductivity value to go out of sensor's range + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, bicarbPumpFlowRate_mL_min ); + + if ( concentratePumpPrimeCount++ > CONCENTRATE_PUMP_PRIME_INTERVAL ) + { + concentratePumpPrimeCount = 0; + concentrateTestStartTime = getMSTimerCount(); + requestConcentratePumpsOn( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpsOff( CONCENTRATEPUMPS_CP2_BICARB ); +#ifndef DISABLE_MIXING + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, CONCENTRATE_PUMP_MAX_SPEED ); +#endif + result = DG_FILL_MODE_STATE_ACID_PUMP_CHECK; + } } + else + { + concentratePumpPrimeCount = 0; + } 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 ); + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_BICARB_CONDUCTIVITY_OUT_OF_RANGE, bicarbConductivity ); + requestNewOperationMode( DG_MODE_GENE ); } return result; @@ -315,22 +318,40 @@ static DG_FILL_MODE_STATE_T handleAcidPumpCheckState( void ) { DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_ACID_PUMP_CHECK; + DG_ACID_CONCENTRATES_RECORD_T acid = getAcidConcentrateCalRecord(); + F32 const measuredROFlowRate_mL_min = getMeasuredROFlowRate() * ML_PER_LITER; + F32 const acidPumpFlowRate_mL_min = measuredROFlowRate_mL_min * acid.acidConcentrate[ CAL_DATA_ACID_CONCENTRATE_1 ].acidConcMixRatio + + CONCENTRATE_PUMP_PRIME_EXTRA_SPEED_ML_MIN; - handleDialysateMixing(); - #ifndef DISABLE_DIALYSATE_CHECK - if ( TRUE == isAcidConductivityInRange() ) + F32 const acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); +#else + F32 const acidConductivity = MIN_ACID_CONCENTRATE_CONDUCTIVITY; #endif + + if ( MIN_ACID_CONCENTRATE_CONDUCTIVITY <= acidConductivity ) { - requestConcentratePumpsOn( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpsOn( CONCENTRATEPUMPS_CP2_BICARB ); - result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; + // Reduce acid pump speed after reaching minimum conductivity + // This prevents conductivity value to go out of sensor's range + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, acidPumpFlowRate_mL_min ); + + if ( concentratePumpPrimeCount++ > CONCENTRATE_PUMP_PRIME_INTERVAL ) + { + concentratePumpPrimeCount = 0; + requestConcentratePumpsOff( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpsOff( CONCENTRATEPUMPS_CP2_BICARB ); + result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; + } } + else + { + concentratePumpPrimeCount = 0; + } 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 ); + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE, acidConductivity ); + requestNewOperationMode( DG_MODE_GENE ); } return result; @@ -340,24 +361,36 @@ * @brief * The handleDialysateProductionState function executes the dialysate production * state of the fill mode state machine. - * @details Inputs: none + * @details Inputs: inlet water quality and dialysate temperature * @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; + F32 const measuredROFlowRate_mL_min = getMeasuredROFlowRate() * ML_PER_LITER; - handleDialysateMixing(); - - // TODO - transition when temperature and mix is in range #ifndef DISABLE_DIALYSATE_CHECK - if ( TRUE == checkDialysateConductivity() ) + if ( ( TRUE == isWaterQualityGood() ) && ( TRUE == checkDialysateTemperature() ) ) +#else + if ( TRUE ) #endif { - setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); - result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; + // Prime mixing before deliver result to reservoir + requestConcentratePumpsOn( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpsOn( CONCENTRATEPUMPS_CP2_BICARB ); + handleDialysateMixing( measuredROFlowRate_mL_min ); + + if ( concentratePumpPrimeCount++ > CONCENTRATE_PUMP_PRIME_INTERVAL ) + { + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); + result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; + } } + else + { + concentratePumpPrimeCount = 0; + } return result; } @@ -366,33 +399,79 @@ * @brief * The handleDeliverDialysateState function executes the deliver dialysate * state of the fill mode state machine. - * @details Inputs: none - * @details Outputs: Deliver dialysate + * @details Inputs: inlet water quality and dialysate temperature + * @details Outputs: Deliver dialysate to inactive reservoir * @return the next state *************************************************************************/ static DG_FILL_MODE_STATE_T handleDeliverDialysateState( void ) { + F32 const measuredROFlowRate_mL_min = getMeasuredROFlowRate() * ML_PER_LITER; + F32 const acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); + F32 const dialysateConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); + BOOL const isAcidConductivityOutOfRange = ( acidConductivity <= MIN_ACID_CONCENTRATE_CONDUCTIVITY ) && ( MAX_ACID_CONCENTRATE_CONDUCTIVITY >= acidConductivity ); + BOOL const isDialysateConductivityOutOfRange = ( dialysateConductivity <= MIN_DIALYSATE_CONDUCTIVITY ) && ( MAX_DIALYSATE_CONDUCTIVITY >= dialysateConductivity ); + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; DG_RESERVOIR_ID_T inactiveReservoir = getInactiveReservoir(); - totalROFlowRate_LPM += getMeasuredROFlowRate(); + F32 integratedVolume_mL; - handleDialysateMixing(); + // Set concentrate pumps speed based off RO pump flow rate + handleDialysateMixing( measuredROFlowRate_mL_min ); - // TODO - transition back when temperature or mix out of range - if ( FALSE == checkDialysateConductivity() ) - { + totalROFlowRate_mL_min += measuredROFlowRate_mL_min; + integratedVolume_mL = totalROFlowRate_mL_min * FLOW_INTEGRATOR * ACID_BICARB_CONCENTRATE_ADDITION_MULTIPLER; + usedAcidVolume_mL.data += getMeasuredPumpSpeed( CONCENTRATEPUMPS_CP1_ACID ) * FLOW_INTEGRATOR; + usedBicarbVolume_mL.data += getMeasuredPumpSpeed( CONCENTRATEPUMPS_CP2_BICARB ) * FLOW_INTEGRATOR; + + acidConductivityTotal += acidConductivity; + dialysateConductivityTotal += dialysateConductivity; + conductivitySampleCount++; + #ifndef DISABLE_DIALYSATE_CHECK + if ( ( isWaterQualityGood() != TRUE ) || ( checkDialysateTemperature() != TRUE ) ) + { + requestConcentratePumpsOff( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpsOff( CONCENTRATEPUMPS_CP2_BICARB ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; + } + + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE, isAcidConductivityOutOfRange ) ) + { + usedAcidVolume_mL.data = 0.0; + requestConcentratePumpsOff( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpsOff( CONCENTRATEPUMPS_CP2_BICARB ); + setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); + result = DG_FILL_MODE_STATE_PAUSED; + } + + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_BICARB_CONDUCTIVITY_OUT_OF_RANGE, isDialysateConductivityOutOfRange ) ) + { + usedBicarbVolume_mL.data = 0.0; + requestConcentratePumpsOff( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpsOff( CONCENTRATEPUMPS_CP2_BICARB ); + setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); + result = DG_FILL_MODE_STATE_PAUSED; + } #endif + + if ( ( ACID_CONCENTRATION_BOTTLE_VOLUME_ML - getUsedAcidVolume() ) <= CONCENTRATION_BOTTLE_LOW_VOLUME_ML ) + { + activateAlarmNoData( ALARM_ID_DG_ACID_BOTTLE_LOW_VOLUME ); } - // if we've reached our target fill to volume (by weight), we're done filling - go back to re-circ mode - if ( hasTargetFillVolumeBeenReached( inactiveReservoir ) ) + if ( ( BICARB_CONCENTRATION_BOTTLE_VOLUME_ML - getUsedBicarbVolume() ) <= CONCENTRATION_BOTTLE_LOW_VOLUME_ML ) { + activateAlarmNoData( ALARM_ID_DG_BICARB_BOTTLE_LOW_VOLUME ); + } + + // if we've reached our target fill to volume (by weight), we're done filling - go back to generation idle mode + if ( ( TRUE == hasTargetFillVolumeBeenReached( inactiveReservoir ) ) || ( ( integratedVolume_mL - reservoirBaseWeight ) >= MAX_RESERVOIR_VOLUME_ML ) ) + { 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 ) ); + F32 const avgAcidConductivity = acidConductivityTotal / conductivitySampleCount; + F32 const avgDialysateConductivity = dialysateConductivityTotal / conductivitySampleCount; if ( integratedVolumeToLoadCellReadingPercent > FLOW_INTEGRATED_VOLUME_CHECK_TOLERANCE ) { @@ -401,35 +480,142 @@ #endif } - requestNewOperationMode( DG_MODE_CIRC ); +#ifndef DISABLE_DIALYSATE_CHECK + if ( ( avgAcidConductivity < MIN_ACID_CONCENTRATE_CONDUCTIVITY ) || ( avgAcidConductivity > MAX_ACID_CONCENTRATE_CONDUCTIVITY ) || + ( avgDialysateConductivity < MIN_DIALYSATE_CONDUCTIVITY ) || ( avgDialysateConductivity > MAX_DIALYSATE_CONDUCTIVITY ) ) + { + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DIALYSATE_CONDUCTIVITY_FAULT, avgAcidConductivity, avgDialysateConductivity ); + } +#endif + + requestNewOperationMode( DG_MODE_GENE ); } return result; } /*********************************************************************//** * @brief + * The handlePausedState function executes the paused state of the fill + * mode state machine. + * @details Inputs: Empty bottle alarm active + * @details Outputs: none + * @return the next state + *************************************************************************/ +static DG_FILL_MODE_STATE_T handlePausedState( void ) +{ + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_PAUSED; + + if ( ( FALSE == isAlarmActive( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE ) ) && + ( FALSE == isAlarmActive( ALARM_ID_BICARB_CONDUCTIVITY_OUT_OF_RANGE ) ) ) + { + result = DG_FILL_MODE_STATE_CHECK_INLET_WATER; + } + + return result; +} + +/*********************************************************************//** + * @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 checkDialysateTemperature function checks dialysate temperature after + * it gets heated up by primary heater. + * @details Inputs: TPo temperature value + * @details Outputs: None + * @return TRUE if dialysate temperature is in range, otherwise FALSE + *************************************************************************/ +static BOOL checkDialysateTemperature( void ) +{ + F32 const dialysateTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); + F32 const targetTemp = getPrimaryHeaterTargetTemperature(); + + return ( ( fabs( dialysateTemp - targetTemp ) <= DIALYSATE_TEMPERATURE_TOLERANCE_C ) ? TRUE : FALSE ); +} + +/*********************************************************************//** + * @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 + * @param measuredROFlowRate_mL_min measured RO flow rate in mL/min * @return none *************************************************************************/ -static void handleDialysateMixing( void ) +static void handleDialysateMixing( F32 measuredROFlowRate_mL_min ) { #ifndef DISABLE_MIXING // TODO what should we do with start volume from the structure? DG_ACID_CONCENTRATES_RECORD_T acid = getAcidConcentrateCalRecord(); DG_BICARB_CONCENTRATES_RECORD_T bicarb = getBicarbConcentrateCalRecord(); // Set concentrate pumps speed based off RO pump flow rate - F32 const measuredROFlowRate = getMeasuredROFlowRate(); - F32 const acidCP1PumpFlowRate = acid.acidConcentrate[ CAL_DATA_ACID_CONCENTRATE_1 ].acidConcMixRatio * measuredROFlowRate * ML_PER_LITER; - F32 const bicarbCP2PumpFlowRate = bicarb.bicarbConcentrate[ CAL_DATA_BICARB_CONCENTRATE_1 ].bicarbConcMixRatio * measuredROFlowRate * ML_PER_LITER; + F32 const acidCP1PumpFlowRate = acid.acidConcentrate[ CAL_DATA_ACID_CONCENTRATE_1 ].acidConcMixRatio * measuredROFlowRate_mL_min; + F32 const bicarbCP2PumpFlowRate = bicarb.bicarbConcentrate[ CAL_DATA_BICARB_CONCENTRATE_1 ].bicarbConcMixRatio * measuredROFlowRate_mL_min; setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, acidCP1PumpFlowRate ); setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, bicarbCP2PumpFlowRate ); #endif } +/*********************************************************************//** + * @brief + * The getUsedAcidVolume function returns the used volume of acid concentration. + * @details Inputs: usedAcidVolume_mL + * @details Outputs: none + * @return used acid concentration volume + *************************************************************************/ +static F32 getUsedAcidVolume( void ) +{ + F32 result = usedAcidVolume_mL.data; + + if ( OVERRIDE_KEY == usedAcidVolume_mL.override ) + { + result = usedAcidVolume_mL.ovData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getUsedBicarbVolume function returns the used volume of bicarbonate concentration. + * @details Inputs: usedBicarbVolume_mL + * @details Outputs: none + * @return used bicarbonate concentration volume + *************************************************************************/ +static F32 getUsedBicarbVolume( void ) +{ + F32 result = usedBicarbVolume_mL.data; + + if ( OVERRIDE_KEY == usedBicarbVolume_mL.override ) + { + result = usedBicarbVolume_mL.ovData; + } + + return result; +} + + +/************************************************************************* + * TEST SUPPORT FUNCTIONS + *************************************************************************/ + + + /**@}*/