/************************************************************************** * * 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 "Heaters.h" #include "LoadCell.h" #include "ModeFill.h" #include "NVDataMgmtDGRecords.h" #include "NVDataMgmt.h" #include "OperationModes.h" #include "PersistentAlarm.h" #include "Pressures.h" #include "Reservoirs.h" #include "ROPump.h" #include "SystemComm.h" #include "TaskGeneral.h" #include "TemperatureSensors.h" #include "Timers.h" #include "UVReactors.h" #include "Valves.h" /** * @addtogroup DGFillMode * @{ */ // ********** private definitions ********** #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 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. #define CONCENTRATE_PUMP_PRIME_INTERVAL ( 3 * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Persistent time interval for concentrate pumps prime. #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_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 DIALYSATE_TEMPERATURE_TOLERANCE_C 2.0 ///< Dialysate temperature tolerance in degree C. #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 ********** /// Fill conditions status typedef struct { F32 fillFlowRateRunningSum; ///< Fill flow running sum. U32 fillSampleCounter; ///< Fill flow sample counter. F32 fillTemperatureRunningSum; ///< Fill temperature running sum. F32 fillTemperatureAverage; ///< Fill temperature average value. F32 fillFlowRateAverage; ///< Fill flow average value. F32 fillLastTemperature; ///< Fill last temperature value. } FILL_CONDITION_STATUS_T; 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 FILL_CONDITION_STATUS_T fillStatus; ///< Fill condition status. 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 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 BOOL isWaterQualityGood( void ); static BOOL checkDialysateTemperature( void ); static void handleDialysateMixing( F32 measuredROFlowRate_mL_min ); static void setFillInfoToRTCRAM( 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; dialysateFillStartTime = 0; reservoirBaseWeight = 0.0; totalROFlowRate_mL_min = 0.0; concentrateTestStartTime = 0; acidConductivityTotal = 0.0; dialysateConductivityTotal = 0.0; conductivitySampleCount = 0; concentratePumpPrimeCount = 0; // Get the heaters info form the NV data management DG_HEATERS_RECORD_T heaterInfo = getHeatersInfoReocrd(); // If the data in the NV data management was not initialized properly, set it to 0 otherwise, set the average flow rate fillStatus.fillFlowRateAverage = ( heaterInfo.averageFillFlow < NEARLY_ZERO ? 0.0 : heaterInfo.averageFillFlow ); fillStatus.fillFlowRateRunningSum = 0.0; fillStatus.fillSampleCounter = 0; fillStatus.fillTemperatureRunningSum = 0.0; fillStatus.fillTemperatureAverage = 0.0; 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 ); } /*********************************************************************//** * @brief * The transitionToFillMode function prepares for transition to fill mode. * @details Inputs: none * @details Outputs: Re-initialized fill mode * @return none *************************************************************************/ U32 transitionToFillMode( void ) { DG_RESERVOIR_ID_T inactiveReservoir = getInactiveReservoir(); initFillMode(); reservoirBaseWeight = getReservoirWeight( inactiveReservoir ); 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 ); // NOTE: The target flow rate should be set prior to setting the start primary heater // because the initial guess in the heaters driver needs the target flow to calculate // the new PWMs for the main and small primary heaters #ifndef DISABLE_FLOW_CONTROL_TREATMENT setROPumpTargetFlowRate( TARGET_RO_FLOW_RATE_L, TARGET_RO_PRESSURE_PSI ); #endif startHeater( DG_PRIMARY_HEATER ); return fillState; } /*********************************************************************//** * @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(); // TODO: Check for open straw door status and alarm if closed // 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_GENE ); } // 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; case DG_FILL_MODE_STATE_DELIVER_DIALYSATE: 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; break; } return fillState; } /*********************************************************************//** * @brief * The getAvgFillFlowRate function returns the average fill flow rate in * each fill. * @details Inputs: none * @details Outputs: fillFlowRateAverage * @return average of the fill flow rate *************************************************************************/ F32 getAvgFillFlowRate( void ) { return fillStatus.fillFlowRateAverage; } /*********************************************************************//** * @brief * The getAverageFillTemperature function returns the average fill temperature * in each fill. * @details Inputs: none * @details Outputs: fillTemperatureAverage * @return average fill temperature *************************************************************************/ F32 getAvgFillTemperature( void ) { return fillStatus.fillTemperatureAverage; } /*********************************************************************//** * @brief * The getLastFillTemperature function returns the last fill temperature * in each fill. * @details Inputs: none * @details Outputs: fillLastTemperature * @return last fill temperature *************************************************************************/ F32 getLastFillTemperature( void ) { return fillStatus.fillLastTemperature; } /*********************************************************************//** * @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; #ifndef DISABLE_DIALYSATE_CHECK if ( TRUE == isWaterQualityGood() ) #endif { concentrateTestStartTime = getMSTimerCount(); #ifndef DISABLE_MIXING setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, CONCENTRATE_PUMP_MAX_SPEED ); requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); #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_GENE ); } 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; 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; #ifndef DISABLE_DIALYSATE_CHECK F32 const bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); #else F32 const bicarbConductivity = MIN_BICARB_CONCENTRATE_CONDUCTIVITY; #endif if ( MIN_BICARB_CONCENTRATE_CONDUCTIVITY <= bicarbConductivity ) { // 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(); #ifndef DISABLE_MIXING setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, CONCENTRATE_PUMP_MAX_SPEED ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); #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, bicarbConductivity ); requestNewOperationMode( DG_MODE_GENE ); } 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; DG_ACID_CONCENTRATES_RECORD_T acid = getAcidConcentrateCalRecord(); F32 measuredROFlowRate_mL_min = getMeasuredROFlowRate() * ML_PER_LITER; F32 acidPumpFlowRate_mL_min = measuredROFlowRate_mL_min * acid.acidConcentrate[ CAL_DATA_ACID_CONCENTRATE_1 ].acidConcMixRatio + CONCENTRATE_PUMP_PRIME_EXTRA_SPEED_ML_MIN; #ifndef DISABLE_DIALYSATE_CHECK F32 const acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); #else F32 const acidConductivity = MIN_ACID_CONCENTRATE_CONDUCTIVITY; #endif if ( MIN_ACID_CONCENTRATE_CONDUCTIVITY <= acidConductivity ) { // 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; requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOff( 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, acidConductivity ); requestNewOperationMode( DG_MODE_GENE ); } return result; } /*********************************************************************//** * @brief * The handleDialysateProductionState function executes the dialysate production * state of the fill mode state machine. * @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 measuredROFlowRate_mL_min = getMeasuredROFlowRate() * ML_PER_LITER; #ifndef DISABLE_DIALYSATE_CHECK if ( ( TRUE == isWaterQualityGood() ) && ( TRUE == checkDialysateTemperature() ) ) #else if ( TRUE ) #endif { // Prime mixing before deliver result to reservoir handleDialysateMixing( measuredROFlowRate_mL_min ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); if ( concentratePumpPrimeCount++ > CONCENTRATE_PUMP_PRIME_INTERVAL ) { setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; } } else { concentratePumpPrimeCount = 0; requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); } return result; } /*********************************************************************//** * @brief * The handleDeliverDialysateState function executes the deliver dialysate * state of the fill mode state machine. * @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 ) || ( acidConductivity >= MAX_ACID_CONCENTRATE_CONDUCTIVITY ); BOOL const isDialysateConductivityOutOfRange = ( dialysateConductivity <= MIN_DIALYSATE_CONDUCTIVITY ) || ( dialysateConductivity >= MAX_DIALYSATE_CONDUCTIVITY ); DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; DG_RESERVOIR_ID_T inactiveReservoir = getInactiveReservoir(); F32 integratedVolume_mL; // Set concentrate pumps speed based off RO pump flow rate handleDialysateMixing( measuredROFlowRate_mL_min ); 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++; // DG is delivering dialysate keep collecting the sample counter and the measured flow fillStatus.fillSampleCounter += 1; fillStatus.fillFlowRateRunningSum += getMeasuredROFlowRate(); fillStatus.fillTemperatureRunningSum += getTemperatureValue( (U32)TEMPSENSORS_OUTLET_PRIMARY_HEATER ); #ifndef DISABLE_DIALYSATE_CHECK if ( ( isWaterQualityGood() != TRUE ) || ( checkDialysateTemperature() != TRUE ) ) { requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOff( 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; requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOff( 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; requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); result = DG_FILL_MODE_STATE_PAUSED; } #endif if ( ( ACID_CONCENTRATION_BOTTLE_VOLUME_ML - getF32OverrideValue( &usedAcidVolume_mL ) ) <= CONCENTRATION_BOTTLE_LOW_VOLUME_ML ) { activateAlarmNoData( ALARM_ID_DG_ACID_BOTTLE_LOW_VOLUME ); } if ( ( BICARB_CONCENTRATION_BOTTLE_VOLUME_ML - getF32OverrideValue( &usedBicarbVolume_mL ) ) <= 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 integratedVolumeToLoadCellReadingPercent = fabs( 1 - ( filledVolume_mL / integratedVolume_mL ) ); F32 const avgAcidConductivity = acidConductivityTotal / conductivitySampleCount; F32 const avgDialysateConductivity = dialysateConductivityTotal / conductivitySampleCount; 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 } #ifndef DISABLE_DIALYSATE_CHECK if ( ( avgDialysateConductivity < MIN_DIALYSATE_CONDUCTIVITY ) || ( avgDialysateConductivity > MAX_DIALYSATE_CONDUCTIVITY ) ) { SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DIALYSATE_CONDUCTIVITY_FAULT, avgAcidConductivity, avgDialysateConductivity ); } #endif // Done with this fill. Calculate the average fill flow rate and average temperature fillStatus.fillFlowRateAverage = fillStatus.fillFlowRateRunningSum / (F32)fillStatus.fillSampleCounter; fillStatus.fillTemperatureAverage = fillStatus.fillTemperatureRunningSum / (F32)fillStatus.fillSampleCounter; // Reset the variables for the next fill fillStatus.fillFlowRateRunningSum = 0.0; fillStatus.fillTemperatureRunningSum = 0.0; fillStatus.fillSampleCounter = 1; // Get the last fill temperature before leaving to Generation Idle fillStatus.fillLastTemperature = getTemperatureValue( (U32)TEMPSENSORS_OUTLET_PRIMARY_HEATER ); // Write the latest fill data into the RTC RAM for heaters control setFillInfoToRTCRAM(); 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 ) ? TRUE : FALSE ); } /*********************************************************************//** * @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 dialysateTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); F32 targetTemp = getHeaterTargetTemperature( DG_PRIMARY_HEATER ); 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( 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 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 setFillInfoToRTCRAM function writes the fill information to the RTC * RAM at the end of each fill. This information is used for dialysate temperature * control. * @details Inputs: fillStatus.fillFlowRateAverage * @details Outputs: none * @return none *************************************************************************/ static void setFillInfoToRTCRAM( void ) { DG_HEATERS_RECORD_T record; record.averageFillFlow = fillStatus.fillFlowRateAverage; setHeatersInfoRecord( (U08*)&record ); } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /**@}*/