Index: firmware/App/Modes/ModeFill.c =================================================================== diff -u -ra9315539f527b92523b1598ff91e47db4d71dae2 -r9df576f7d1962f640d8d8a4b56403431c67a8639 --- firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision a9315539f527b92523b1598ff91e47db4d71dae2) +++ firmware/App/Modes/ModeFill.c (.../ModeFill.c) (revision 9df576f7d1962f640d8d8a4b56403431c67a8639) @@ -8,7 +8,7 @@ * @file ModeFill.c * * @author (last) Dara Navaei -* @date (last) 03-Feb-2022 +* @date (last) 31-Mar-2022 * * @author (original) Leonardo Baloa * @date (original) 19-Nov-2019 @@ -23,6 +23,7 @@ #include "Heaters.h" #include "LoadCell.h" #include "ModeFill.h" +#include "ModeGenIdle.h" #include "NVDataMgmtDGRecords.h" #include "NVDataMgmt.h" #include "OperationModes.h" @@ -31,6 +32,7 @@ #include "Reservoirs.h" #include "ROPump.h" #include "SystemComm.h" +#include "SystemCommMessages.h" #include "TaskGeneral.h" #include "TemperatureSensors.h" #include "Timers.h" @@ -45,30 +47,43 @@ // ********** private definitions ********** #define TARGET_RO_PRESSURE_PSI 130 ///< Target pressure 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. +#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 DIALYSATE_TEMPERATURE_SENSORS_MAX_DRIFT_C 2.0 ///< Dialysate temperature sensors maximum allowed drift in C. -#define DIALYSATE_TEMPERATURE_SENSORS_DRIFT_TIMEOUT_MS ( 10 * MS_PER_SECOND ) ///< Dialysate temperature sensors drift timeout in milliseconds. - -#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 @@ -83,63 +98,91 @@ BOOL isThisFirstFill; ///< Fill flag to indicate whether it is the first fill or not. } FILL_CONDITION_STATUS_T; +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 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 totalROFlowRateMLPM; ///< Total RO flow rate over period of time. +static F32 integratedVolumeML; ///< 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 F32 bicarbConductivityTotal; ///< Total of bicarb conductivity during fill. static U32 conductivitySampleCount; ///< Sample count of conductivity during fill. -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 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 void checkDialysateTemperatureSensorsDrift( 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: fillState, dialysateFillStartTime, reservoirBaseWeight, * totalROFlowRateMLPM, concentrateTestStartTime, acidConductivityTotal, - * dialysateConductivityTotal, conductivitySampleCount, + * bicarbConductivityTotal, conductivitySampleCount, havePauseActuatorsBeenSet * concentratePumpPrimeCount * @return none *************************************************************************/ void initFillMode( void ) { - fillState = DG_FILL_MODE_STATE_START; - dialysateFillStartTime = 0; - reservoirBaseWeight = 0.0; - totalROFlowRateMLPM = 0.0; - concentrateTestStartTime = 0; - acidConductivityTotal = 0.0; - dialysateConductivityTotal = 0.0; - conductivitySampleCount = 0; - concentratePumpPrimeCount = 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_DRFIT_TIMEOUT, 0, DIALYSATE_TEMPERATURE_SENSORS_DRIFT_TIMEOUT_MS ); + initPersistentAlarm( ALARM_ID_DG_DIALYSATE_TEMPERATURE_SENSORS_OUT_OF_RANGE, 0, DIALYSATE_TEMP_SNSRS_OUT_OF_RANGE_TIMEOUT_MS ); } /*********************************************************************//** @@ -154,8 +197,7 @@ DG_RESERVOIR_ID_T inactiveReservoir = getInactiveReservoir(); initFillMode(); - reservoirBaseWeight = getReservoirWeight( inactiveReservoir ); - dialysateFillStartTime = getMSTimerCount(); + reservoirBaseWeight = getReservoirWeight( inactiveReservoir ); // Set initial actuator states setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); @@ -167,9 +209,7 @@ // 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 setROPumpTargetFlowRateLPM( getTargetFillFlowRateLPM(), TARGET_RO_PRESSURE_PSI ); -#endif setHeaterTargetTemperature( DG_PRIMARY_HEATER, getPrimaryHeaterTargetTemperature() ); startHeater( DG_PRIMARY_HEATER ); @@ -185,45 +225,42 @@ *************************************************************************/ U32 execFillMode( void ) { - // Check inlet water conductivity, temperature, pressure, and RO rejection ratio - checkInletWaterConductivity(); - checkInletWaterTemperature(); - checkInletPressure(); checkRORejectionRatio(); - checkDialysateTemperatureSensorsDrift(); + 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; @@ -238,6 +275,8 @@ break; } + publishFillModeData(); + return fillState; } @@ -290,10 +329,9 @@ void resetFillStatusParameters( void ) { DG_HEATERS_RECORD_T heaterInfo; - // There is no number of NV data to check for the heaters info so it is passed as 0 - U08 numOfNVData2Check = 0; + // 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 ), numOfNVData2Check, ALARM_ID_NO_ALARM ); + 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.fillFlowRateAverage = ( heaterInfo.averageFillFlow < NEARLY_ZERO ? 0.0 : heaterInfo.averageFillFlow ); @@ -302,9 +340,9 @@ 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; + fillStatus.fillTemperatureAverage = getHeaterTargetTemperature( DG_TRIMMER_HEATER ); + fillStatus.fillLastTemperature = getHeaterTargetTemperature( DG_TRIMMER_HEATER ) + RESERVOIR_EXTRA_TEMPERATURE; + fillStatus.isThisFirstFill = TRUE; } /*********************************************************************//** @@ -322,194 +360,331 @@ /*********************************************************************//** * @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 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() ) { - // If this is the first fill of a treatment, prime and 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_CP2_BICARB, CONCENTRATE_PUMP_MAX_SPEED ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); - #endif - result = DG_FILL_MODE_STATE_BICARB_PUMP_CHECK; + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, CONCENTRATE_PUMP_MAX_SPEED ); + setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, CONCENTRATE_PUMP_MAX_SPEED ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); + requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); + + 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; + } + + 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 ) ) + { + 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 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 { - result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; + // 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; } } - 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 + * 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_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_TEST_BICARB_CONDUCTIVITY; + F32 bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); - DG_BICARB_CONCENTRATES_RECORD_T bicarb = getBicarbConcentrateCalRecord(); - F32 measuredROFlowRateMLPM = getMeasuredROFlowRateLPM() * ML_PER_LITER; - F32 bicarbPumpFlowRateMLPM = measuredROFlowRateMLPM * bicarb.bicarbConcentrate[ CAL_DATA_BICARB_CONCENTRATE_1 ].bicarbConcMixRatio - + CONCENTRATE_PUMP_PRIME_EXTRA_SPEED_ML_MIN; + totalBicarbConductivity += bicarbConductivity; + bicarbConductivitySampleCount++; -#ifndef DISABLE_DIALYSATE_CHECK - F32 bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); -#else - F32 bicarbConductivity = MAX_BICARB_CONCENTRATE_CONDUCTIVITY; -#endif + if ( TRUE == didTimeout( concentrateTestStartTime, CONCENTRATE_TEST_TIME_OUT_MS ) ) + { + BOOL isConductivityInRange = FALSE; + averageBicarbConductivity = totalBicarbConductivity / bicarbConductivitySampleCount; - bicarbPumpFlowRateMLPM = MIN( bicarbPumpFlowRateMLPM, CONCENTRATE_PUMP_MAX_SPEED ); + if ( TRUE == isValueWithinPercentRange( averageBicarbConductivity, BICARB_TEST_CD2_TCD, FIVE_PERCENT_FACTOR ) ) + { + isConductivityInRange = TRUE; + } - if ( bicarbConductivity >= MIN_BICARB_CONCENTRATE_CONDUCTIVITY ) - { - // Reduce acid pump speed after reaching minimum conductivity - // This prevents conductivity value to go out of sensor's range - setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, bicarbPumpFlowRateMLPM ); +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_BICARB_CONDUCTIVITY_TEST ) == SW_CONFIG_ENABLE_VALUE ) + { + isConductivityInRange = TRUE; + } +#endif - if ( concentratePumpPrimeCount++ > CONCENTRATE_PUMP_PRIME_INTERVAL ) + if ( TRUE == isConductivityInRange ) { - concentratePumpPrimeCount = 0; - concentrateTestStartTime = getMSTimerCount(); -#ifndef DISABLE_MIXING - setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, CONCENTRATE_PUMP_MAX_SPEED ); - requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); + // Initialization requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); -#endif + totalBicarbConductivity = 0.0; + totalAcidConductivity = 0.0; + bicarbConductivitySampleCount = 0; + acidConductivitySampleCount = 0; - result = DG_FILL_MODE_STATE_ACID_PUMP_CHECK; + // 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_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_TEST_ACID_CONDUCTIVITY; + F32 acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); + F32 bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); - DG_ACID_CONCENTRATES_RECORD_T acid = getAcidConcentrateCalRecord(); - F32 measuredROFlowRateMLPM = getMeasuredROFlowRateLPM() * ML_PER_LITER; - F32 acidPumpFlowRateMLPM = measuredROFlowRateMLPM * acid.acidConcentrate[ CAL_DATA_ACID_CONCENTRATE_1 ].acidConcMixRatio + - CONCENTRATE_PUMP_PRIME_EXTRA_SPEED_ML_MIN; + totalBicarbConductivity += bicarbConductivity; + bicarbConductivitySampleCount++; -#ifndef DISABLE_DIALYSATE_CHECK - F32 acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); -#else - F32 acidConductivity = MAX_ACID_CONCENTRATE_CONDUCTIVITY; -#endif + totalAcidConductivity += acidConductivity; + acidConductivitySampleCount++; - acidPumpFlowRateMLPM = MIN( acidPumpFlowRateMLPM, CONCENTRATE_PUMP_MAX_SPEED ); - - if ( acidConductivity >= MIN_ACID_CONCENTRATE_CONDUCTIVITY ) + 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, acidPumpFlowRateMLPM ); + 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; + haveTestsPassed = TRUE; + } + +#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; - requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); - result = DG_FILL_MODE_STATE_DIALYSATE_PRODUCTION; + 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 ); + } } - 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 + * 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 measuredROFlowRateMLPM = getMeasuredROFlowRateLPM() * 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( measuredROFlowRateMLPM ); -#ifndef DISABLE_MIXING - requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); - requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); -#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; } @@ -524,95 +699,73 @@ *************************************************************************/ static DG_FILL_MODE_STATE_T handleDeliverDialysateState( void ) { - F32 integratedVolumeML; - - F32 measuredROFlowRateMLPM = getMeasuredROFlowRateLPM() * ML_PER_LITER; - F32 acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); - F32 dialysateConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); - BOOL isAcidConductivityOutOfRange = ( acidConductivity <= MIN_ACID_CONCENTRATE_CONDUCTIVITY ) || - ( acidConductivity >= MAX_ACID_CONCENTRATE_CONDUCTIVITY ) ? TRUE : FALSE; - BOOL isDialysateConductivityOutOfRange = ( dialysateConductivity <= MIN_DIALYSATE_CONDUCTIVITY ) || - ( dialysateConductivity >= MAX_DIALYSATE_CONDUCTIVITY ) ? TRUE : FALSE; - DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; DG_RESERVOIR_ID_T inactiveReservoir = getInactiveReservoir(); + F32 acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); + F32 bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); - // Set concentrate pumps speed based off RO pump flow rate - handleDialysateMixing( measuredROFlowRateMLPM ); + // Set concentrate pumps speed based on the RO pump flow rate + handleDialysateMixing( getMeasuredROFlowRateLPM() * ML_PER_LITER ); - totalROFlowRateMLPM += measuredROFlowRateMLPM; - integratedVolumeML = totalROFlowRateMLPM * FLOW_INTEGRATOR * ACID_BICARB_CONCENTRATE_ADDITION_MULTIPLER; - usedAcidVolumeML.data += getMeasuredPumpSpeed( CONCENTRATEPUMPS_CP1_ACID ) * FLOW_INTEGRATOR; - usedBicarbVolumeML.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 += 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 ) ) - { - usedAcidVolumeML.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 ) ) - { - usedBicarbVolumeML.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( &usedAcidVolumeML ) ) <= CONCENTRATION_BOTTLE_LOW_VOLUME_ML ) - { - activateAlarmNoData( ALARM_ID_DG_ACID_BOTTLE_LOW_VOLUME ); - } - - if ( ( BICARB_CONCENTRATION_BOTTLE_VOLUME_ML - getF32OverrideValue( &usedBicarbVolumeML ) ) <= 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 filledVolumeML = getReservoirWeight( inactiveReservoir ) - reservoirBaseWeight; - F32 integratedVolumeToLoadCellReadingPercent = fabs( 1 - ( filledVolumeML / integratedVolumeML ) ); - F32 avgAcidConductivity = acidConductivityTotal / conductivitySampleCount; // TODO - should we be checking this below? - F32 avgDialysateConductivity = dialysateConductivityTotal / conductivitySampleCount; + 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, filledVolumeML, integratedVolumeML ); +#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 // Reset the variables for the next fill @@ -626,7 +779,7 @@ // Write the latest fill data into the RTC RAM for heaters control // TODO test this and make sure it is writing it correctly - setFillInfoToRTCRAM(); + setFillInfoToRTCRAM(); // SRSDG ??? requestNewOperationMode( DG_MODE_GENE ); } @@ -638,81 +791,108 @@ * @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 isInletPressureGood = ( FALSE == isAlarmActive( ALARM_ID_INLET_WATER_LOW_PRESSURE ) ? FALSE : TRUE ); + // Check inlet water conductivity, temperature, pressure, and RO rejection ratio + BOOL status = FALSE; + checkInletWaterConductivity(); + checkInletWaterTemperature(); + checkInletWaterPressure(); - BOOL isWaterTemperatureGood = ( ( FALSE == isAlarmActive( ALARM_ID_INLET_WATER_HIGH_TEMPERATURE ) && - FALSE == isAlarmActive( ALARM_ID_INLET_WATER_LOW_TEMPERATURE ) ) ? FALSE : TRUE ); + // 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; + } - BOOL isWaterConductivityGood = ( ( FALSE == isAlarmActive( ALARM_ID_INLET_WATER_HIGH_CONDUCTIVITY ) && - FALSE == isAlarmActive( ALARM_ID_INLET_WATER_LOW_CONDUCTIVITY ) && - FALSE == isAlarmActive( ALARM_ID_RO_REJECTION_RATIO_OUT_OF_RANGE ) ) ? FALSE : TRUE ); - - return ( ( isInletPressureGood && isWaterTemperatureGood && isWaterConductivityGood ) ); + return status; } /*********************************************************************//** * @brief - * The checkDialysateTemperatureSensorsDrift function checks whether the + * 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 none *************************************************************************/ -static void checkDialysateTemperatureSensorsDrift( void ) +static void checkDialysateTemperatureSensors( void ) { #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 ); - if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_DG_DIALYSATE_TEMPERATURE_SENSORS_DRFIT_TIMEOUT, isDriftOut ) ) + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_DG_DIALYSATE_TEMPERATURE_SENSORS_OUT_OF_RANGE, isDriftOut ) ) { - activateAlarmNoData( ALARM_ID_DG_DIALYSATE_TEMPERATURE_SENSORS_DRFIT_TIMEOUT ); + activateAlarmNoData( ALARM_ID_DG_DIALYSATE_TEMPERATURE_SENSORS_OUT_OF_RANGE ); } #endif } /*********************************************************************//** * @brief - * The checkDialysateTemperature function checks dialysate temperature after - * it gets heated up by primary heater. - * @details Inputs: TPo temperature value + * 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 - * @return TRUE if dialysate temperature is in range, otherwise FALSE + * @param: testValue, baseValue, percentFactor + * @return TRUE if testValue is within range. Otherwise, return FALSE *************************************************************************/ -static BOOL checkDialysateTemperature( void ) +static BOOL isValueWithinPercentRange( F32 testValue, F32 baseValue, F32 percentFactor ) { - F32 dialysateTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); - F32 targetTemp = getHeaterTargetTemperature( DG_PRIMARY_HEATER ); + BOOL testFlag = FALSE; + F32 percentValue = baseValue * percentFactor; + F32 valueMax = baseValue + percentValue; + F32 valueMin = baseValue - percentValue; - return ( ( fabs( dialysateTemp - targetTemp ) <= DIALYSATE_TEMPERATURE_TOLERANCE_C ) ? TRUE : FALSE ); + if ( ( testValue >= valueMin ) && ( testValue <= valueMax ) ) + { + testFlag = TRUE; + } + + return testFlag; } /*********************************************************************//** @@ -726,7 +906,6 @@ *************************************************************************/ 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(); @@ -741,7 +920,6 @@ setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, acidCP1PumpFlowRate ); setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, bicarbCP2PumpFlowRate ); -#endif } /*********************************************************************//** @@ -762,4 +940,182 @@ 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; +} + /**@}*/