Index: firmware/App/Modes/ModeFill.c =================================================================== diff -u -rdcd360fb4dc37db2dcbeb7fb14fb327fe68235f4 -ra89d6b091874136d75a9bfbdbbc1ff00f42467b3 --- firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision dcd360fb4dc37db2dcbeb7fb14fb327fe68235f4) +++ firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision a89d6b091874136d75a9bfbdbbc1ff00f42467b3) @@ -1,26 +1,29 @@ /************************************************************************** * -* Copyright (c) 2019-2021 Diality Inc. - All Rights Reserved. +* Copyright (c) 2019-2022 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) Dara Navaei -* @date (last) 06-Nov-2021 +* @date (last) 04-Aug-2022 * * @author (original) Leonardo Baloa * @date (original) 19-Nov-2019 * ***************************************************************************/ +#include // For fabs + #include "ConcentratePumps.h" #include "ConductivitySensors.h" #include "FPGA.h" #include "Heaters.h" #include "LoadCell.h" #include "ModeFill.h" +#include "ModeGenIdle.h" #include "NVDataMgmtDGRecords.h" #include "NVDataMgmt.h" #include "OperationModes.h" @@ -29,6 +32,7 @@ #include "Reservoirs.h" #include "ROPump.h" #include "SystemComm.h" +#include "SystemCommMessages.h" #include "TaskGeneral.h" #include "TemperatureSensors.h" #include "Timers.h" @@ -43,102 +47,144 @@ // ********** 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 RO_PUMP_400_ML_PER_MIN 400.0F ///< RO pump speed of 400.0 mL/minute. +#define RO_PUMP_800_ML_PER_MIN 800.0F ///< RO pump speed of 800.0 mL/minute. +#define TARGET_RO_FLOW_RATE_IN_PAUSE_L 0.3F ///< Target flow rate for RO pump during pause in liters. +#define MILLILITERS_PER_LITER 1000.0F ///< One liter is 1000 milliliters +#define ACID_PUMP_20_ML_PER_MIN 20.0F ///< Acid pump speed of 20.0 mL/minute. +#define BICARB_PUMP_40_ML_PER_MIN 40.0F ///< Bicarb pump speed of 40.0 mL/minute. +#define CONCENTRATE_PUMP_40_ML_PER_MIN 40.0F ///< Concentrate pump speed of 40.0 mL/minute. +#define FILL_MODE_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the fill mode data is published on the CAN bus. #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 FLOW_INTEGRATED_VOLUME_CHECK_TOLERANCE 0.1F ///< 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 ACID_TEST_CD1_TCD 12252.1F ///< Used for testing CD1 acid theoretical conductivity. +#define ACID_TEST_CD2_TCD ACID_TEST_CD1_TCD ///< Used for testing CD2 acid theoretical conductivity. +#define BICARB_TEST_CD2_TCD 6820.91F ///< Used for testing CD2 bicarb theoretical conductivity. +#define FIVE_PERCENT_FACTOR 0.05F ///< 5.0 / 100.0 used to calculate conductivity within range of -/+ 5%. +#define RO_PUMP_LOOKUP_TABLE_SIZE 4 ///< Size of array used as RO pump speed lookup table. +#define CONCENTRATE_PUMP_PRIME_EXTRA_SPEED_ML_MIN 5.0F ///< Concentrate pump additional speed during priming in mL/min. +#define CONCENTRATE_TEST_TIME_OUT_MS ( 30 * MS_PER_SECOND ) ///< Concentrate test time out period in ms. +// 2m long tubing to cap = 19.5 mL (acid line) + 7.92 mL/m * 2 m (tubing to cap) + 20.82 mL (straw) = 56.15 mL +// Prime time in seconds = ( 56.15 mL / 48 mL/min ) x 60 second/min + 25 seconds margin time = 95 seconds. +#define PRIME_CONCENTRATE_LINES_TIME_OUT_MS ( 95 * MS_PER_SECOND ) ///< Time required to prime the concentrate lines. +#define FLUSH_BUBBLES_PUMP_TIME_OUT_MS ( 2 * MS_PER_SECOND ) ///< RO pump on during flush bubble interval in ms. +#define DIALYSATE_TEMPERATURE_TOLERANCE_C 2.0F ///< Dialysate temperature tolerance in degree C. +#define DIALYSATE_TEMPERATURE_SENSORS_MAX_DRIFT_C 2.0F ///< Dialysate temperature sensors maximum allowed drift in C. +#define DIALYSATE_TEMP_SNSRS_OUT_OF_RANGE_TIMEOUT_MS ( 10 * MS_PER_SECOND ) ///< Dialysate temperature sensors drift timeout in milliseconds. +#define DATA_PUBLISH_COUNTER_START_COUNT 63 ///< Data publish counter start count. -#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 ) ); +static const F32 RO_PUMP_FLUSH_BUBBLES_FLOWS[ RO_PUMP_LOOKUP_TABLE_SIZE ] = { RO_PUMP_400_ML_PER_MIN, + RO_PUMP_800_ML_PER_MIN, + RO_PUMP_400_ML_PER_MIN, + RO_PUMP_800_ML_PER_MIN }; ///< Lookup table to determine the desired RO speed in mL/min when flushing bubbles. + // ********** 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. + 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 fillFlowRateAverageLPM; ///< Fill flow average value in L/min. + F32 fillLastTemperature; ///< Fill last temperature value. + BOOL isThisFirstFill; ///< Fill flag to indicate whether it is the first fill or not. } 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 dataPublishCounter; ///< Used to schedule dialysate fill data publication to CAN bus. +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 U32 concentrateTestStartTime; ///< Starting time for concentrate test. +static U32 concentratePrimingStartTime; ///< Starting time for concentrate priming. +static U32 flushBubblesStartTime; ///< Starting time for flush bubbles. +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 F32 integratedVolumeML; ///< Total RO flow rate over period of time. +static F32 acidConductivityTotal; ///< Total of acid conductivity during fill. +static F32 bicarbConductivityTotal; ///< Total of bicarb 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. +static F32 averageBicarbConductivity; ///< Average bicarb conductivity over 30 seconds. +static F32 averageAcidConductivity; ///< Average acid conductivity over 30 seconds. +static F32 pctDiffInConductivity; ///< Percent difference in conductivity between CD1 (acid) and CD2 (bicarb). +static U32 bicarbConductivitySampleCount; ///< Sample count of bicarb conductivity over 30 seconds. +static U32 acidConductivitySampleCount; ///< Sample count of acid conductivity over 30 seconds. + +static F32 totalBicarbConductivity; ///< Total bicarb conductivity over 30 seconds. +static F32 totalAcidConductivity; ///< Total acid conductivity over 30 seconds. + +static U32 pumpSpeedIndex; ///< Index used to access the desired pump speed in roPumpFlushBubblesSpeed table. +static BOOL havePauseActuatorsBeenSet; ///< Flag to indicate the actuators have been set to pause for the first time. + +static OVERRIDE_F32_T usedAcidVolumeML = { 0.0, 0.0, 0.0, 0.0 }; ///< The integrated acid concentration volume has been used in mL. +static OVERRIDE_F32_T usedBicarbVolumeML = { 0.0, 0.0, 0.0, 0.0 }; ///< The integrated bicarb concentration volume has been used in mL. +static OVERRIDE_U32_T fillModeDataPublishInterval = { FILL_MODE_DATA_PUB_INTERVAL, + FILL_MODE_DATA_PUB_INTERVAL, 0, 0 }; ///< Interval (in ms) at which to publish fill mode data to CAN bus. + // ********** 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 handleTestInletWaterState( void ); +static DG_FILL_MODE_STATE_T handlePrimeConcentrateLinesState( void ); +static DG_FILL_MODE_STATE_T handleFlushBubblesState( void ); +static DG_FILL_MODE_STATE_T handleTestBicarbConductivityState( void ); +static DG_FILL_MODE_STATE_T handleTestAcidConductivityState( void ); +static DG_FILL_MODE_STATE_T handleProduceDialysateState( 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 BOOL areInletWaterConditionsAlarmsActive( void ); +static void checkDialysateTemperatureSensors( void ); static void handleDialysateMixing( F32 measuredROFlowRate_mL_min ); static void setFillInfoToRTCRAM( void ); +static BOOL isValueWithinPercentRange( F32 testValue, F32 baseValue, F32 percentFactor ); +static void publishFillModeData( void ); /*********************************************************************//** * @brief * The initFillMode function initializes the fill mode module. * @details Inputs: none - * @details Outputs: Fill mode module initialized + * @details Outputs: fillState, dialysateFillStartTime, reservoirBaseWeight, + * totalROFlowRateMLPM, concentrateTestStartTime, acidConductivityTotal, + * bicarbConductivityTotal, conductivitySampleCount, havePauseActuatorsBeenSet + * concentratePumpPrimeCount * @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; + fillState = DG_FILL_MODE_STATE_START; + dialysateFillStartTime = 0; + dataPublishCounter = DATA_PUBLISH_COUNTER_START_COUNT; + reservoirBaseWeight = 0.0; + integratedVolumeML = 0.0; + concentrateTestStartTime = 0; + acidConductivityTotal = 0.0; + bicarbConductivityTotal = 0.0; + conductivitySampleCount = 0; + concentratePumpPrimeCount = 0; + pumpSpeedIndex = 0; + averageBicarbConductivity = 0.0; + averageAcidConductivity = 0.0; + pctDiffInConductivity = 0.0; + bicarbConductivitySampleCount = 0; + acidConductivitySampleCount = 0; + totalBicarbConductivity = 0.0; + totalAcidConductivity = 0.0; + havePauseActuatorsBeenSet = FALSE; - 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 ); + initPersistentAlarm( ALARM_ID_DG_DIALYSATE_TEMPERATURE_SENSORS_OUT_OF_RANGE, 0, DIALYSATE_TEMP_SNSRS_OUT_OF_RANGE_TIMEOUT_MS ); } /*********************************************************************//** @@ -154,7 +200,6 @@ initFillMode(); reservoirBaseWeight = getReservoirWeight( inactiveReservoir ); - dialysateFillStartTime = getMSTimerCount(); // Set initial actuator states setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); @@ -166,9 +211,8 @@ // 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 + setROPumpTargetFlowRateLPM( getTargetFillFlowRateLPM(), TARGET_RO_PRESSURE_PSI ); + setHeaterTargetTemperature( DG_PRIMARY_HEATER, getPrimaryHeaterTargetTemperature() ); startHeater( DG_PRIMARY_HEATER ); return fillState; @@ -183,44 +227,42 @@ *************************************************************************/ U32 execFillMode( void ) { - // Check inlet water conductivity, temperature, pressure, and RO rejection ratio - checkInletWaterConductivity(); - checkInletWaterTemperature(); - checkInletPressure(); checkRORejectionRatio(); + checkDialysateTemperatureSensors(); - // 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 ); - } + fillState = ( TRUE == areInletWaterConditionsAlarmsActive() ? DG_FILL_MODE_STATE_PAUSED : fillState ); // Execute current Fill state switch ( fillState ) { case DG_FILL_MODE_STATE_START: - waterQualityCheckStartTime = getMSTimerCount(); - fillState = DG_FILL_MODE_STATE_CHECK_INLET_WATER; + fillState = DG_FILL_MODE_STATE_TEST_INLET_WATER; break; - case DG_FILL_MODE_STATE_CHECK_INLET_WATER: - fillState = handleCheckInletWaterState(); + case DG_FILL_MODE_STATE_TEST_INLET_WATER: + fillState = handleTestInletWaterState(); break; - case DG_FILL_MODE_STATE_BICARB_PUMP_CHECK: - fillState = handleBicarbPumpCheckState(); + case DG_FILL_MODE_STATE_PRIME_CONCENTRATE_LINES: + fillState = handlePrimeConcentrateLinesState(); break; - case DG_FILL_MODE_STATE_ACID_PUMP_CHECK: - fillState = handleAcidPumpCheckState(); + case DG_FILL_MODE_STATE_FLUSH_BUBBLES: + fillState = handleFlushBubblesState(); break; - case DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION: - fillState = handleDialysateProductionState(); + case DG_FILL_MODE_STATE_TEST_BICARB_CONDUCTIVITY: + fillState = handleTestBicarbConductivityState(); break; + case DG_FILL_MODE_STATE_TEST_ACID_CONDUCTIVITY: + fillState = handleTestAcidConductivityState(); + break; + + case DG_FILL_MODE_STATE_PRODUCE_DIALYSATE: + fillState = handleProduceDialysateState(); + break; + case DG_FILL_MODE_STATE_DELIVER_DIALYSATE: fillState = handleDeliverDialysateState(); break; @@ -235,20 +277,22 @@ break; } + publishFillModeData(); + return fillState; } /*********************************************************************//** * @brief - * The getAvgFillFlowRate function returns the average fill flow rate in + * The getAvgFillFlowRateLPM function returns the average fill flow rate in * each fill. * @details Inputs: none - * @details Outputs: fillFlowRateAverage + * @details Outputs: fillFlowRateAverageLPM * @return average of the fill flow rate *************************************************************************/ -F32 getAvgFillFlowRate( void ) +F32 getAvgFillFlowRateLPM( void ) { - return fillStatus.fillFlowRateAverage; + return fillStatus.fillFlowRateAverageLPM; } /*********************************************************************//** @@ -279,184 +323,370 @@ /*********************************************************************//** * @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 + * The resetFillStatusParameters function resets the fill status parameters. + * @details Inputs: none + * @details Outputs: fillStatus + * @return none + *************************************************************************/ +void resetFillStatusParameters( void ) +{ + DG_HEATERS_RECORD_T heaterInfo; + + // Get the heaters info from the NV data management. Do not alarm on the status of the data since it has already been checked in NV POST + getNVRecord2Driver( GET_INF_HEATERS_RECORD, (U08*)&heaterInfo, sizeof( DG_HEATERS_RECORD_T ), 0, ALARM_ID_NO_ALARM ); + + // If the data in the NV data management was not initialized properly, set it to 0 otherwise, set the average flow rate + fillStatus.fillFlowRateAverageLPM = ( heaterInfo.averageFillFlow < NEARLY_ZERO ? 0.0 : heaterInfo.averageFillFlow ); + fillStatus.fillFlowRateRunningSum = 0.0; + fillStatus.fillSampleCounter = 0; + fillStatus.fillTemperatureRunningSum = 0.0; + // At the beginning the last and average temperatures are considered as the trimmer heater target temperature which + // is the dialysate temperature + fillStatus.fillTemperatureAverage = getHeaterTargetTemperature( DG_TRIMMER_HEATER ); + fillStatus.fillLastTemperature = getHeaterTargetTemperature( DG_TRIMMER_HEATER ) + RESERVOIR_EXTRA_TEMPERATURE; + fillStatus.isThisFirstFill = TRUE; +} + +/*********************************************************************//** + * @brief + * The isThisTheFirstFill function returns the boolean flag that indicates + * whether this is the first fill of a treatment or not. + * @details Inputs: none + * @details Outputs: none + * @return fillStatus.isThisFirstFill + *************************************************************************/ +BOOL isThisTheFirstFill( void ) +{ + return fillStatus.isThisFirstFill; +} + +/*********************************************************************//** + * @brief + * The getChemicalUsedVolumeML function returns the consumed volume of the called + * chemical bottle in milliliters. + * @details Inputs: none + * @details Outputs: none + * @param bottle which the type of bottle (acid or bicarb) can be specified + * @return used chemical volume in millilters + *************************************************************************/ +F32 getChemicalUsedVolumeML( CHEMICAL_BOTTLES_T bottle ) +{ + F32 volume; + + if ( ACID == bottle ) + { + volume = getF32OverrideValue( &usedAcidVolumeML ); + } + else if ( BICARB == bottle ) + { + volume = getF32OverrideValue( &usedBicarbVolumeML ); + } + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_CHEMICAL_BOTTLE_SELECTED, bottle ) + } + + return volume; +} + +/*********************************************************************//** + * @brief + * The resetChemicalUsedVolumeML function resets the used volume of a chemical + * bottle. + * @details Inputs: none + * @details Outputs: none + * @param bottle which the type of bottle (acid or bicarb) can be specified + * @return none + *************************************************************************/ +void resetChemicalUsedVolumeML( CHEMICAL_BOTTLES_T bottle ) +{ + if ( ACID == bottle ) + { + usedAcidVolumeML.data = 0.0; + } + else if ( BICARB == bottle ) + { + usedBicarbVolumeML.data = 0.0; + } + else + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_CHEMICAL_BOTTLE_SELECTED, bottle ) + } +} + +/*********************************************************************//** + * @brief + * The setThisFisrtFillFlag function sets the boolean flag that indicates + * the acid and bicarb bottle need priming. + * @details Inputs: none + * @details Outputs: none + * @param flag to TRUE if prime is needed otherwise FALSE + *************************************************************************/ +void setThisFisrtFillFlag( BOOL flag ) +{ + fillStatus.isThisFirstFill = flag; +} + +/*********************************************************************//** + * @brief + * The handleTestInletWaterState function tests for inlet water quality + * and if this is the first fill of a treatment, prime the acid and bicarb + * lines before jumping to dialysate production state. + * @details Inputs: Temperature, pressure, and conductivity alarms + * @details Outputs: request acid and bicarb pumps on * @return the next state *************************************************************************/ -static DG_FILL_MODE_STATE_T handleCheckInletWaterState( void ) +static DG_FILL_MODE_STATE_T handleTestInletWaterState( void ) { - DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_CHECK_INLET_WATER; + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_TEST_INLET_WATER; -#ifndef DISABLE_DIALYSATE_CHECK - if ( TRUE == isWaterQualityGood() ) -#endif + // If this is the first fill of a treatment, prime acid and bicarb lines, otherwise transition + // to dialysate production directly + if ( TRUE == isThisTheFirstFill() ) { - concentrateTestStartTime = getMSTimerCount(); -#ifndef DISABLE_MIXING + // Prepare the acid and bicarb pumps to prime the concentrate lines + setROPumpTargetFlowRateLPM( RO_PUMP_800_ML_PER_MIN / MILLILITERS_PER_LITER, TARGET_RO_PRESSURE_PSI ); + + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, CONCENTRATE_PUMP_MAX_SPEED ); setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, CONCENTRATE_PUMP_MAX_SPEED ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); -#endif - result = DG_FILL_MODE_STATE_BICARB_PUMP_CHECK; + + concentratePrimingStartTime = getMSTimerCount(); + result = DG_FILL_MODE_STATE_PRIME_CONCENTRATE_LINES; } + else + { + setROPumpTargetFlowRateLPM( getTargetFillFlowRateLPM(), TARGET_RO_PRESSURE_PSI ); + handleDialysateMixing( getMeasuredROFlowRateLPM() * ML_PER_LITER ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); + result = DG_FILL_MODE_STATE_PRODUCE_DIALYSATE; + } - if ( TRUE == didTimeout( waterQualityCheckStartTime, WATER_QUALITY_CHECK_TIME_OUT_MS ) ) + return result; +} + +/*********************************************************************//** + * @brief + * The handlePrimeConcentrateLinesState function primes the acid and bicarb + * lines. + * @details Inputs: None + * @details Outputs: None + * @return the next state + *************************************************************************/ +static DG_FILL_MODE_STATE_T handlePrimeConcentrateLinesState( void ) +{ + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_PRIME_CONCENTRATE_LINES; + + if ( TRUE == didTimeout( concentratePrimingStartTime, PRIME_CONCENTRATE_LINES_TIME_OUT_MS ) ) { - activateAlarmNoData( ALARM_ID_DG_BAD_INLET_WATER_QUALITY ); - requestNewOperationMode( DG_MODE_GENE ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); + + // Set the RO pump flow rate in according to the roPumpFlushBubblesSpeed table to flush bubbles + pumpSpeedIndex = 0; + setROPumpTargetFlowRateLPM( RO_PUMP_FLUSH_BUBBLES_FLOWS[ pumpSpeedIndex ] / MILLILITERS_PER_LITER, TARGET_RO_PRESSURE_PSI ); + + flushBubblesStartTime = getMSTimerCount(); + result = DG_FILL_MODE_STATE_FLUSH_BUBBLES; } return result; } /*********************************************************************//** * @brief - * The handleBicarbPumpCheckState function checks conductivity value for + * The handleFlushBubblesState function removes the bubbles in the RO lines by + * running the RP pump at 400 mL/minute for 2 seconds 0 + * then 800 mL/minute for 2 seconds 1 + * then 400 mL/minute for 2 seconds 2 + * then 800 mL/minute for 2 seconds 3 + * @details Inputs: None + * @details Outputs: request RO pump flow rate in mL/minute + * @return the next state + *************************************************************************/ +static DG_FILL_MODE_STATE_T handleFlushBubblesState( void ) +{ + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_FLUSH_BUBBLES; + + if ( TRUE == didTimeout( flushBubblesStartTime, FLUSH_BUBBLES_PUMP_TIME_OUT_MS ) ) + { + if ( pumpSpeedIndex < RO_PUMP_LOOKUP_TABLE_SIZE ) + { + setROPumpTargetFlowRateLPM( RO_PUMP_FLUSH_BUBBLES_FLOWS[ pumpSpeedIndex++ ] / MILLILITERS_PER_LITER, TARGET_RO_PRESSURE_PSI ); + flushBubblesStartTime = getMSTimerCount(); + } + else + { + // Initialization + totalBicarbConductivity = 0.0; + averageBicarbConductivity = 0.0; + bicarbConductivitySampleCount = 0; + + // Set pumps flow rate to prepare for bicarb conductivity testing + setROPumpTargetFlowRateLPM( RO_PUMP_400_ML_PER_MIN / MILLILITERS_PER_LITER, TARGET_RO_PRESSURE_PSI ); + + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, BICARB_PUMP_40_ML_PER_MIN ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); + + // State transition + concentrateTestStartTime = getMSTimerCount(); + result = DG_FILL_MODE_STATE_TEST_BICARB_CONDUCTIVITY; + } + } + + return result; +} + +/*********************************************************************//** + * @brief + * The handleTestBicarbConductivityState function tests 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 ) +static DG_FILL_MODE_STATE_T handleTestBicarbConductivityState( 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 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 = MAX_BICARB_CONCENTRATE_CONDUCTIVITY; -#endif + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_TEST_BICARB_CONDUCTIVITY; + F32 bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); - bicarbPumpFlowRate_mL_min = MIN( bicarbPumpFlowRate_mL_min, CONCENTRATE_PUMP_MAX_SPEED ); + totalBicarbConductivity += bicarbConductivity; + bicarbConductivitySampleCount++; - if ( MIN_BICARB_CONCENTRATE_CONDUCTIVITY <= bicarbConductivity ) + if ( TRUE == didTimeout( concentrateTestStartTime, CONCENTRATE_TEST_TIME_OUT_MS ) ) { - // 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 ); + BOOL isConductivityInRange = FALSE; + averageBicarbConductivity = totalBicarbConductivity / bicarbConductivitySampleCount; - if ( concentratePumpPrimeCount++ > CONCENTRATE_PUMP_PRIME_INTERVAL ) + if ( TRUE == isValueWithinPercentRange( averageBicarbConductivity, BICARB_TEST_CD2_TCD, FIVE_PERCENT_FACTOR ) ) { - concentratePumpPrimeCount = 0; - concentrateTestStartTime = getMSTimerCount(); -#ifndef DISABLE_MIXING - setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, CONCENTRATE_PUMP_MAX_SPEED ); - requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); + isConductivityInRange = TRUE; + } + +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_BICARB_CONDUCTIVITY_TEST ) == SW_CONFIG_ENABLE_VALUE ) + { + isConductivityInRange = TRUE; + } #endif - result = DG_FILL_MODE_STATE_ACID_PUMP_CHECK; + if ( TRUE == isConductivityInRange ) + { + // Initialization + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); + totalBicarbConductivity = 0.0; + totalAcidConductivity = 0.0; + bicarbConductivitySampleCount = 0; + acidConductivitySampleCount = 0; + + // Set pumps flow rate to prepare for acid conductivity testing + setROPumpTargetFlowRateLPM( RO_PUMP_800_ML_PER_MIN / MILLILITERS_PER_LITER, TARGET_RO_PRESSURE_PSI ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, ACID_PUMP_20_ML_PER_MIN ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); + // State transition + concentrateTestStartTime = getMSTimerCount(); + result = DG_FILL_MODE_STATE_TEST_ACID_CONDUCTIVITY; } + else + { + // Bicarb test failed, go to fault + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_BICARB_CONDUCTIVITY_OUT_OF_RANGE, averageBicarbConductivity ); + } } - 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 + * The handleTestAcidConductivityState function validates that the average + * conductivity of the acid and bicarb concentrate are within 5% of the + * theoretical conductivity over 30 seconds sampling period. * acid concentrate. - * @details Inputs: Acid conductivity sensor value - * @details Outputs: Verified acid conductivity value + * @details Inputs: Acid and bicarb conductivity sensor values + * @details Outputs: Verified acid, bicarb, acid & bicarb conductivity values * @return the next state *************************************************************************/ -static DG_FILL_MODE_STATE_T handleAcidPumpCheckState( void ) +static DG_FILL_MODE_STATE_T handleTestAcidConductivityState( 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; + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_TEST_ACID_CONDUCTIVITY; + F32 acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); + F32 bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); -#ifndef DISABLE_DIALYSATE_CHECK - F32 const acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); -#else - F32 const acidConductivity = MAX_ACID_CONCENTRATE_CONDUCTIVITY; -#endif + totalBicarbConductivity += bicarbConductivity; + bicarbConductivitySampleCount++; - acidPumpFlowRate_mL_min = MIN( acidPumpFlowRate_mL_min, CONCENTRATE_PUMP_MAX_SPEED ); + totalAcidConductivity += acidConductivity; + acidConductivitySampleCount++; - if ( MIN_ACID_CONCENTRATE_CONDUCTIVITY <= acidConductivity ) + if ( TRUE == didTimeout( concentrateTestStartTime, CONCENTRATE_TEST_TIME_OUT_MS ) ) { - // 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 ); + BOOL haveTestsPassed = FALSE; - if ( concentratePumpPrimeCount++ > CONCENTRATE_PUMP_PRIME_INTERVAL ) + averageBicarbConductivity = totalBicarbConductivity / bicarbConductivitySampleCount; + averageAcidConductivity = totalAcidConductivity / acidConductivitySampleCount; + pctDiffInConductivity = fabs( 2.0 * ( averageAcidConductivity - averageBicarbConductivity ) / + ( averageAcidConductivity + averageBicarbConductivity ) ); + + if ( ( TRUE == isValueWithinPercentRange( averageAcidConductivity, ACID_TEST_CD2_TCD, FIVE_PERCENT_FACTOR ) ) && + ( pctDiffInConductivity < FIVE_PERCENT_FACTOR ) ) { - concentratePumpPrimeCount = 0; - requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); - result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; + haveTestsPassed = TRUE; } - } - 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 ); +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_BICARB_CONDUCTIVITY_TEST ) == SW_CONFIG_ENABLE_VALUE ) + { + haveTestsPassed = TRUE; + } +#endif + + if ( TRUE == haveTestsPassed ) + { + setROPumpTargetFlowRateLPM( getTargetFillFlowRateLPM(), TARGET_RO_PRESSURE_PSI ); + + handleDialysateMixing( getMeasuredROFlowRateLPM() * ML_PER_LITER ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); + + // Do the necessary setup here before transition to Produce Dialysate State + fillStatus.isThisFirstFill = FALSE; + result = DG_FILL_MODE_STATE_PRODUCE_DIALYSATE; + } + else + { + // Acid conductivity did not pass, fault + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE, averageAcidConductivity ); + } } return result; } /*********************************************************************//** * @brief - * The handleDialysateProductionState function executes the dialysate production + * The handleProduceDialysateState 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 ) +static DG_FILL_MODE_STATE_T handleProduceDialysateState( void ) { - DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; - F32 measuredROFlowRate_mL_min = getMeasuredROFlowRate() * ML_PER_LITER; + DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_PRODUCE_DIALYSATE; -#ifndef DISABLE_DIALYSATE_CHECK - if ( ( TRUE == isWaterQualityGood() ) && ( TRUE == checkDialysateTemperature() ) ) -#else - if ( TRUE ) -#endif + // Prime mixing before deliver result to reservoir + handleDialysateMixing( getMeasuredROFlowRateLPM() * ML_PER_LITER ); + + if ( concentratePumpPrimeCount++ > CONCENTRATE_PUMP_PRIME_INTERVAL ) { - // Prime mixing before deliver result to reservoir - handleDialysateMixing( measuredROFlowRate_mL_min ); -#ifndef DISABLE_MIXING - requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); - if ( concentratePumpPrimeCount++ > CONCENTRATE_PUMP_PRIME_INTERVAL ) -#endif - { - setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); - result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; - } + dialysateFillStartTime = getMSTimerCount(); + fillStatus.isThisFirstFill = FALSE; + result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; } - else - { - concentratePumpPrimeCount = 0; - requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); - } return result; } @@ -471,105 +701,87 @@ *************************************************************************/ 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_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; DG_RESERVOIR_ID_T inactiveReservoir = getInactiveReservoir(); - F32 integratedVolume_mL; + F32 acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); + F32 bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); - // Set concentrate pumps speed based off RO pump flow rate - handleDialysateMixing( measuredROFlowRate_mL_min ); + // Set concentrate pumps speed based on the RO pump flow rate + handleDialysateMixing( getMeasuredROFlowRateLPM() * ML_PER_LITER ); - 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; + integratedVolumeML += getMeasuredROFlowRateWithConcPumpsLPM() * ( (F32)TASK_GENERAL_INTERVAL / (F32)( SEC_PER_MIN ) ); + usedAcidVolumeML.data += getMeasuredPumpSpeed( CONCENTRATEPUMPS_CP1_ACID ) * FLOW_INTEGRATOR; + usedBicarbVolumeML.data += getMeasuredPumpSpeed( CONCENTRATEPUMPS_CP2_BICARB ) * FLOW_INTEGRATOR; - acidConductivityTotal += acidConductivity; - dialysateConductivityTotal += dialysateConductivity; + acidConductivityTotal += acidConductivity; + bicarbConductivityTotal += bicarbConductivity; conductivitySampleCount++; // DG is delivering dialysate keep collecting the sample counter and the measured flow fillStatus.fillSampleCounter += 1; - fillStatus.fillFlowRateRunningSum += getMeasuredROFlowRate(); + fillStatus.fillFlowRateRunningSum += getMeasuredROFlowRateLPM(); fillStatus.fillTemperatureRunningSum += getTemperatureValue( (U32)TEMPSENSORS_OUTLET_PRIMARY_HEATER ); -#ifndef DISABLE_DIALYSATE_CHECK - if ( ( isWaterQualityGood() != TRUE ) || ( checkDialysateTemperature() != TRUE ) ) + // 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 ) ) { - requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); - setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); - result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; + activateAlarmNoData( ALARM_ID_DG_DIALYSATE_FILL_OUT_OF_TIME ); } - 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 -#ifndef DISABLE_MIXING - 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 ); - } -#endif - // If we've reached our target fill to volume (by weight), we're done filling - go back to generation idle mode + // If we've reached our target fill to volume (by weight), we're done filling - go back to generation idle mode // SRSDG 398 if ( TRUE == hasTargetFillVolumeBeenReached( inactiveReservoir ) ) { - F32 const filledVolume_mL = getReservoirWeight( inactiveReservoir ) - reservoirBaseWeight; - F32 const integratedVolumeToLoadCellReadingPercent = fabs( 1 - ( filledVolume_mL / integratedVolume_mL ) ); - F32 const avgAcidConductivity = acidConductivityTotal / conductivitySampleCount; // TODO - should we be checking this below? - F32 const avgDialysateConductivity = dialysateConductivityTotal / conductivitySampleCount; + F32 filledVolumeML = getReservoirWeight( inactiveReservoir ) - reservoirBaseWeight; + F32 integratedVolumeToLoadCellReadingPercent = fabs( 1.0F - ( filledVolumeML / integratedVolumeML ) ); + F32 avgAcidConductivity = acidConductivityTotal / conductivitySampleCount; + F32 avgBicarbConductivity = bicarbConductivityTotal / conductivitySampleCount; - if ( integratedVolumeToLoadCellReadingPercent > FLOW_INTEGRATED_VOLUME_CHECK_TOLERANCE ) - { -#ifndef DISABLE_FLOW_CHECK_IN_FILL - SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DG_FLOW_METER_CHECK_FAILURE, filledVolume_mL, integratedVolume_mL ); +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_FLOW_VS_LOAD_CELL_CHECK_IN_FILL ) != SW_CONFIG_ENABLE_VALUE ) #endif + { + if ( integratedVolumeToLoadCellReadingPercent > FLOW_INTEGRATED_VOLUME_CHECK_TOLERANCE ) // SRSDG 240 + { + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DG_FLOW_METER_CHECK_FAILURE, filledVolumeML, integratedVolumeML ); + } } -#ifndef DISABLE_DIALYSATE_CHECK - if ( ( avgDialysateConductivity < MIN_DIALYSATE_CONDUCTIVITY ) || ( avgDialysateConductivity > MAX_DIALYSATE_CONDUCTIVITY ) ) +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_MIXING_IN_FILL ) != SW_CONFIG_ENABLE_VALUE ) +#endif { - SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DIALYSATE_CONDUCTIVITY_FAULT, avgAcidConductivity, avgDialysateConductivity ); + // SRSDG 400 + if ( FALSE == isValueWithinPercentRange( avgBicarbConductivity, BICARB_NORMAL_CONDUCTIVITY, FIVE_PERCENT_FACTOR ) ) + { + setBadAvgConductivityDetectedFlag( TRUE ); // signal idle bad avg conductivity detected + setThisFisrtFillFlag( TRUE ); + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_FILL_CONDUCTIVITY_OUT_OF_RANGE, avgBicarbConductivity, BICARB_NORMAL_CONDUCTIVITY ); // trigger replace bottles alarm #1 + activateAlarmNoData ( ALARM_ID_CREATING_DIALYSATE_PLEASE_WAIT ); + } + + if ( FALSE == isValueWithinPercentRange( avgAcidConductivity, ACID_NORMAL_CONDUCTIVITY, FIVE_PERCENT_FACTOR ) ) + { + setBadAvgConductivityDetectedFlag( TRUE ); // signal idle bad avg conductivity detected + setThisFisrtFillFlag( TRUE ); + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_FILL_CONDUCTIVITY_OUT_OF_RANGE, avgAcidConductivity, ACID_NORMAL_CONDUCTIVITY ); // trigger replace bottles alarm #1 then + activateAlarmNoData ( ALARM_ID_CREATING_DIALYSATE_PLEASE_WAIT ); + } } -#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 + // Get the last fill temperature before leaving to Generation Idle + fillStatus.fillFlowRateAverageLPM = fillStatus.fillFlowRateRunningSum / (F32)fillStatus.fillSampleCounter; + fillStatus.fillTemperatureAverage = fillStatus.fillTemperatureRunningSum / (F32)fillStatus.fillSampleCounter; 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(); + // TODO test this and make sure it is writing it correctly + setFillInfoToRTCRAM(); // SRSDG ??? requestNewOperationMode( DG_MODE_GENE ); } @@ -581,58 +793,112 @@ * @brief * The handlePausedState function executes the paused state of the fill * mode state machine. - * @details Inputs: Empty bottle alarm active - * @details Outputs: none + * @details Inputs: havePauseActuatorsBeenSet + * @details Outputs: havePauseActuatorsBeenSet * @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 ) ) ) + if ( FALSE == havePauseActuatorsBeenSet ) { - result = DG_FILL_MODE_STATE_CHECK_INLET_WATER; + setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); + setROPumpTargetFlowRateLPM( TARGET_RO_FLOW_RATE_IN_PAUSE_L, TARGET_RO_PRESSURE_PSI ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); + havePauseActuatorsBeenSet = TRUE; } + if ( FALSE == areInletWaterConditionsAlarmsActive() ) + { + setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); + havePauseActuatorsBeenSet = FALSE; + result = DG_FILL_MODE_STATE_START; + } + 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 + * The areInletWaterConditionsAlarmsActive function checks whether the inlet + * water conditions are out of range and any of their alarms are active. + * @details Inputs: none + * @details Outputs: None + * @return TRUE if any of the alarms are active, otherwise, FALSE *************************************************************************/ -static BOOL isWaterQualityGood( void ) +static BOOL areInletWaterConditionsAlarmsActive( 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 ); + // Check inlet water conductivity, temperature, pressure, and RO rejection ratio + BOOL status = FALSE; + checkInletWaterConductivity(); + checkInletWaterTemperature(); + checkInletWaterPressure(); - return ( ( isInletPressureGood && isWaterTemperatureGood && isWaterConductivityGood ) ? TRUE : FALSE ); + // Check if any of the alarms are active + if ( ( TRUE == isAlarmActive( ALARM_ID_INLET_WATER_CONDUCTIVITY_IN_LOW_RANGE ) ) || + ( TRUE == isAlarmActive( ALARM_ID_INLET_WATER_CONDUCTIVITY_IN_HIGH_RANGE ) ) || + ( TRUE == isAlarmActive( ALARM_ID_INLET_WATER_TEMPERATURE_IN_HIGH_RANGE ) ) || + ( TRUE == isAlarmActive( ALARM_ID_INLET_WATER_TEMPERATURE_IN_LOW_RANGE ) ) || + ( TRUE == isAlarmActive( ALARM_ID_INLET_WATER_PRESSURE_IN_LOW_RANGE ) ) ) + { + status = TRUE; + } + + return status; } /*********************************************************************//** * @brief - * The checkDialysateTemperature function checks dialysate temperature after - * it gets heated up by primary heater. - * @details Inputs: TPo temperature value + * The checkDialysateTemperatureSensors function checks whether the + * dialysate temperature sensors have drifted. If they are drifted, it raises + * an alarm. + * @details Inputs: none * @details Outputs: None - * @return TRUE if dialysate temperature is in range, otherwise FALSE + * @return none *************************************************************************/ -static BOOL checkDialysateTemperature( void ) +static void checkDialysateTemperatureSensors( void ) { - F32 dialysateTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); - F32 targetTemp = getHeaterTargetTemperature( DG_PRIMARY_HEATER ); +#ifndef THD_USING_TRO_CONNECTOR // Do not use until TRo is back in line + F32 TDi = getTemperatureValue( TEMPSENSORS_INLET_DIALYSATE ); + F32 TRo = getTemperatureValue( TEMPSENSORS_OUTLET_REDUNDANT ); + BOOL isDriftOut = ( fabs( TDi - TRo ) >= DIALYSATE_TEMPERATURE_SENSORS_MAX_DRIFT_C ? TRUE : FALSE ); - return ( ( fabs( dialysateTemp - targetTemp ) <= DIALYSATE_TEMPERATURE_TOLERANCE_C ) ? TRUE : FALSE ); + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_DG_DIALYSATE_TEMPERATURE_SENSORS_OUT_OF_RANGE, isDriftOut ) ) + { + activateAlarmNoData( ALARM_ID_DG_DIALYSATE_TEMPERATURE_SENSORS_OUT_OF_RANGE ); + } +#endif } /*********************************************************************//** * @brief + * The isValueWithinPercentRange function validates if the test value + * is within the percentage range specified by the baseValue and + * percentFactor. + * @details Inputs: None + * @details Outputs: None + * @param: testValue, baseValue, percentFactor + * @return TRUE if testValue is within range. Otherwise, return FALSE + *************************************************************************/ +static BOOL isValueWithinPercentRange( F32 testValue, F32 baseValue, F32 percentFactor ) +{ + BOOL testFlag = FALSE; + F32 percentValue = baseValue * percentFactor; + F32 valueMax = baseValue + percentValue; + F32 valueMin = baseValue - percentValue; + + if ( ( testValue >= valueMin ) && ( testValue <= valueMax ) ) + { + testFlag = TRUE; + } + + return testFlag; +} + +/*********************************************************************//** + * @brief * The handleDialysateMixing function handles the dialysate mixing by setting * the concentrate pump speed relative to the RO pump flow rate. * @details Inputs: none @@ -642,18 +908,20 @@ *************************************************************************/ 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; + F32 acidCP1PumpFlowRate = acid.acidConcentrate[ CAL_DATA_ACID_CONCENTRATE_1 ].acidConcMixRatio * measuredROFlowRate_mL_min; + F32 bicarbCP2PumpFlowRate = bicarb.bicarbConcentrate[ CAL_DATA_BICARB_CONCENTRATE_1 ].bicarbConcMixRatio * measuredROFlowRate_mL_min; + // Cap to the maximum allowed concentrate pumps rate + acidCP1PumpFlowRate = MIN( acidCP1PumpFlowRate, CONCENTRATE_PUMP_MAX_SPEED ); + bicarbCP2PumpFlowRate = MIN( bicarbCP2PumpFlowRate, CONCENTRATE_PUMP_MAX_SPEED ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, acidCP1PumpFlowRate ); setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, bicarbCP2PumpFlowRate ); -#endif } /*********************************************************************//** @@ -668,16 +936,188 @@ static void setFillInfoToRTCRAM( void ) { DG_HEATERS_RECORD_T record; - record.averageFillFlow = fillStatus.fillFlowRateAverage; - setHeatersInfoRecord( (U08*)&record ); + record.averageFillFlow = fillStatus.fillFlowRateAverageLPM; + + setHeatersInfoRecord( (U08*)&record, sizeof( DG_HEATERS_RECORD_T ) ); } +/*********************************************************************//** + * @brief + * The publishFillModeData function publishes fill mode data + * at the set interval. + * @details Inputs: fillModeDataPublicationTimerCounter + * @details Outputs: fillModeDataPublicationTimerCounter + * @return none + *************************************************************************/ +static void publishFillModeData( void ) +{ + // publish Drain pump data on interval + if ( ++dataPublishCounter >= getU32OverrideValue( &fillModeDataPublishInterval ) ) + { + DG_FILL_MODE_DATA_T fillModeData; + // Populate the data structure for publication + fillModeData.averageAcidConductivity = averageAcidConductivity; + fillModeData.averageBicarbConductivity = averageBicarbConductivity; + fillModeData.isThisTheFirstFill = isThisTheFirstFill(); + fillModeData.pctDiffInConductivity = pctDiffInConductivity; + fillModeData.usedAcidVolumeML = getChemicalUsedVolumeML( ACID ); + fillModeData.usedBicarbVolumeML = getChemicalUsedVolumeML( BICARB ); + fillModeData.integratedVolumeML = integratedVolumeML; + + broadcastData( MSG_ID_DG_FILL_MODE_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&fillModeData, sizeof( DG_FILL_MODE_DATA_T ) ); + + dataPublishCounter = 0; + } +} + + /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ +/*********************************************************************//** + * @brief + * The testSetUsedAcidVolumeMLOverride function overrides the + * acid volume. + * @details Inputs: used acid volume + * @details Outputs: used acid volume + * @param: value : override used acid volume in mL + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetUsedAcidVolumeMLOverride( F32 value ) +{ + BOOL result = FALSE; + if ( TRUE == isTestingActivated() ) + { + usedAcidVolumeML.ovInitData = usedAcidVolumeML.data; + usedAcidVolumeML.ovData = value; + usedAcidVolumeML.override = OVERRIDE_KEY; + result = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetUsedAcidVolumeMLOverride function resets the override + * of the used acid volume. + * @details Inputs: used acid volume + * @details Outputs: used acid volume + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testResetUsedAcidVolumeMLOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + usedAcidVolumeML.data = usedAcidVolumeML.ovInitData; + usedAcidVolumeML.override = OVERRIDE_RESET; + usedAcidVolumeML.ovInitData = 0.0; + usedAcidVolumeML.ovData = 0.0; + result = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testSetUsedBicarbVolumeMLOverride function overrides the + * bicarb volume. + * @details Inputs: used bicarb volume + * @details Outputs: used bicarb volume + * @param: value : override used bicarb volume in mL + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetUsedBicarbVolumeMLOverride( F32 value ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + usedBicarbVolumeML.ovInitData = usedBicarbVolumeML.data; + usedBicarbVolumeML.ovData = value; + usedBicarbVolumeML.override = OVERRIDE_KEY; + result = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetUsedBicarbVolumeMLOverride function resets the override + * of the used bicarb volume. + * @details Inputs: used bicarb volume + * @details Outputs: used bicarb volume + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testResetUsedBicarbVolumeMLOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + usedBicarbVolumeML.data = usedBicarbVolumeML.ovInitData; + usedBicarbVolumeML.override = OVERRIDE_RESET; + usedBicarbVolumeML.ovInitData = 0.0; + usedBicarbVolumeML.ovData = 0.0; + result = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testSetFillModeDataPublishIntervalOverride function overrides the + * fill mode data publish interval. + * @details Inputs: FillModeDataPublishInterval + * @details Outputs: FillModeDataPublishInterval + * @param: value override fill mode data publish interval with (in ms) + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetFillModeDataPublishIntervalOverride( U32 value ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + U32 intvl = value / TASK_GENERAL_INTERVAL; + fillModeDataPublishInterval.ovData = intvl; + fillModeDataPublishInterval.override = OVERRIDE_KEY; + result = TRUE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetFillModeDataPublishIntervalOverride function resets the + * override of the fill mode data publish interval. + * @details Inputs: FillModeDataPublishInterval + * @details Outputs: FillModeDataPublishInterval + * @return TRUE if override reset successful, FALSE if not + *************************************************************************/ +BOOL testResetFillModeDataPublishIntervalOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + fillModeDataPublishInterval.override = OVERRIDE_RESET; + fillModeDataPublishInterval.ovData = fillModeDataPublishInterval.ovInitData; + result = TRUE; + } + + return result; +} + /**@}*/