/************************************************************************** * * 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) 05-Jan-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 "NVDataMgmtDGRecords.h" #include "NVDataMgmt.h" #include "OperationModes.h" #include "PersistentAlarm.h" #include "Pressures.h" #include "Reservoirs.h" #include "ROPump.h" #include "SystemComm.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "TemperatureSensors.h" #include "Timers.h" #include "UVReactors.h" #include "Valves.h" /** * @addtogroup DGFillMode * @{ */ // ********** private definitions ********** #define TARGET_RO_PRESSURE_PSI 130 ///< Target pressure for RO pump. #define RO_PUMP_400_ML_PER_MIN 400.0 ///< RO pump speed of 400.0 mL/minute. #define RO_PUMP_800_ML_PER_MIN 800.0 ///< RO pump speed of 800.0 mL/minute. #define MILLILITERS_PER_LITER 1000.0 ///< One liter is 1000 milliliters #define ACID_PUMP_20_ML_PER_MIN 20.0 ///< Acid pump speed of 20.0 mL/minute. #define BICARB_PUMP_40_ML_PER_MIN 40.0 ///< Bicarb pump speed of 40.0 mL/minute. #define CONCENTRATE_PUMP_40_ML_PER_MIN 40.0 ///< 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 ( 1 * 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 CONDUCTIVITY_WHEN_ACID_JUG_EMPTY 10000.0 ///< The conductivity value when the acid jug is empty. #define CONDUCTIVITY_WHEN_BICARB_JUG_EMPTY 12000.0 ///< The conductivity value when the bicard jug is empty. #define ACID_TEST_CD1_TCD 12252.1 ///< Used for testing CD1 acid theoretical conductivity. #define ACID_TEST_CD2_TCD ACID_TEST_CD1_TCD ///< Used for testing CD2 acid theoretical conductivity. #define BICARD_TEST_CD2_TCD 6820.91 ///< Used for testing CD2 bicard theoretical conductivity. #define FIVE_PERCENT_FACTOR 0.05 ///< 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.0 ///< 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 WATER_QUALITY_TEST_TIME_OUT_MS ( 30 * MS_PER_SECOND ) ///< Inlet water quality 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.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 3430.0 ///< Bottle volume of acid concentration in mL. #define BICARB_CONCENTRATION_BOTTLE_VOLUME_ML 3780.0 ///< Bottle volume of bicarb concentration in mL. #define CONCENTRATION_BOTTLE_LOW_VOLUME_ML 100.0 ///< Concentration bottle low volume in mL. /// Multiplier to convert flow (mL/min) into volume (mL) for period of general task interval. static const F32 FLOW_INTEGRATOR = ( (F32)TASK_GENERAL_INTERVAL / (F32)( SEC_PER_MIN * MS_PER_SECOND ) ); // ********** private data ********** /// Fill conditions status typedef struct { F32 fillFlowRateRunningSum; ///< Fill flow running sum. U32 fillSampleCounter; ///< Fill flow sample counter. F32 fillTemperatureRunningSum; ///< Fill temperature running sum. F32 fillTemperatureAverage; ///< Fill temperature average value. F32 fillFlowRateAverage; ///< Fill flow average value. F32 fillLastTemperature; ///< Fill last temperature value. BOOL isThisFirstFill; ///< Fill flag to indicate whether it is the first fill or not. BOOL fillAvgConductivityOutOfRange; ///< Fill average conductivity out of range. BOOL fillEmtyAcidBottleDetected; ///< Fill acid bottle empty detected. BOOL fillEmtyBicarbBottleDetected; ///< Fill bicarb bottle empty detected. BOOL fillBottlesNeedPrimeFlag; ///< Fill flag to indicate acid and bicarb bottle need priming. } FILL_CONDITION_STATUS_T; /// DG broadcast dialysate fill data structure. typedef struct { F32 averageBicardConductivity; ///< The average bicarb conductivity calculated in one fill. F32 averageAcidConductivity; ///< The average acid conductivity calculated in one fill. BOOL isThisTheFirstFill; ///< Indicate if this is the first fill. F32 pctDiffInConductivity; ///< The percent difference between CD1 and CD2. F32 usedAcidVolumeML; ///< The used acid volume in ML. F32 usedBicarbVolumeML; ///< The used bicarb volume in ML. } DG_FILL_MODE_DATA_T; static U32 fillModeDataPublicationTimerCounter; ///< 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 waterQualityTestStartTime; ///< Starting time for inlet water quality test. 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 acidConductivityTotal; ///< Total of acid conductivity during fill. static F32 bicarbConductivityTotal; ///< Total of bicard conductivity during fill. static U32 conductivitySampleCount; ///< Sample count of conductivity during fill. static F32 averageBicardConductivity; ///< Average bicard 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 bicardConductivitySampleCount; ///< Sample count of bicard conductivity over 30 seconds. static U32 acidConductivitySampleCount; ///< Sample count of acid conductivity over 30 seconds. static F32 totalBicardConductivity; ///< Total bicard conductivity over 30 seconds. static F32 totalAcidConductivity; ///< Total acid conductivity over 30 seconds. 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. static F32 roPumpFlushBubblesSpeed[ RO_PUMP_LOOKUP_TABLE_SIZE ] = { RO_PUMP_400_ML_PER_MIN, ///< Lookup table to determine the desired RO speed in mL/min when flushing bubbles. RO_PUMP_800_ML_PER_MIN, RO_PUMP_400_ML_PER_MIN, RO_PUMP_800_ML_PER_MIN }; static U32 pumpSpeedIndex; ///< Index used to access the desired pump speed in roPumpFlushBubblesSpeed table. // ********** private function prototypes ********** static DG_FILL_MODE_STATE_T handleTestInletWaterState( void ); static DG_FILL_MODE_STATE_T handlePrimeConcentrateLines( void ); static DG_FILL_MODE_STATE_T handleFlushBubbles( 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 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, * bicarbConductivityTotal, conductivitySampleCount, * concentratePumpPrimeCount * @return none *************************************************************************/ void initFillMode( void ) { fillState = DG_FILL_MODE_STATE_START; dialysateFillStartTime = 0; fillModeDataPublicationTimerCounter = 0; reservoirBaseWeight = 0.0; totalROFlowRateMLPM = 0.0; concentrateTestStartTime = 0; acidConductivityTotal = 0.0; bicarbConductivityTotal = 0.0; conductivitySampleCount = 0; concentratePumpPrimeCount = 0; pumpSpeedIndex = 0; averageBicardConductivity = 0.0; averageAcidConductivity = 0.0; pctDiffInConductivity = 0.0; bicardConductivitySampleCount = 0; acidConductivitySampleCount = 0; totalBicardConductivity = 0.0; totalAcidConductivity = 0.0; initPersistentAlarm( ALARM_ID_DG_ACID_BOTTLE_LOW_VOLUME, 0, EMPTY_BOTTLE_DETECT_PERSISTENT_PERIOD_MS ); initPersistentAlarm( ALARM_ID_DG_BICARB_BOTTLE_LOW_VOLUME, 0, EMPTY_BOTTLE_DETECT_PERSISTENT_PERIOD_MS ); initPersistentAlarm( ALARM_ID_DG_DIALYSATE_TEMPERATURE_SENSORS_DRFIT_TIMEOUT, 0, DIALYSATE_TEMPERATURE_SENSORS_DRIFT_TIMEOUT_MS ); } /*********************************************************************//** * @brief * The transitionToFillMode function prepares for transition to fill mode. * @details Inputs: none * @details Outputs: Re-initialized fill mode * @return none *************************************************************************/ U32 transitionToFillMode( void ) { DG_RESERVOIR_ID_T inactiveReservoir = getInactiveReservoir(); initFillMode(); reservoirBaseWeight = getReservoirWeight( inactiveReservoir ); dialysateFillStartTime = getMSTimerCount(); // Set initial actuator states setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); turnOnUVReactor( INLET_UV_REACTOR ); turnOnUVReactor( OUTLET_UV_REACTOR ); // NOTE: The target flow rate should be set prior to setting the start primary heater // because the initial guess in the heaters driver needs the target flow to calculate // the new PWMs for the main and small primary heaters #ifndef DISABLE_FLOW_CONTROL_TREATMENT setROPumpTargetFlowRateLPM( getTargetFillFlowRateLPM(), TARGET_RO_PRESSURE_PSI ); #endif setHeaterTargetTemperature( DG_PRIMARY_HEATER, getPrimaryHeaterTargetTemperature() ); startHeater( DG_PRIMARY_HEATER ); return fillState; } /*********************************************************************//** * @brief * The execFillMode function executes the fill mode state machine. * @details Inputs: fillState * @details Outputs: Check water quality, fill mode state machine executed * @return current state. *************************************************************************/ U32 execFillMode( void ) { // Check inlet water conductivity, temperature, pressure, and RO rejection ratio checkInletWaterConductivity(); checkInletWaterTemperature(); checkInletPressure(); checkRORejectionRatio(); checkDialysateTemperatureSensorsDrift(); publishFillModeData(); // TODO: Check for open straw door status and alarm if closed // Check if run out of time to fill the reservoir if ( TRUE == didTimeout( dialysateFillStartTime, DIALYSATE_FILL_TIME_OUT ) ) { activateAlarmNoData( ALARM_ID_DG_DIALYSATE_FILL_OUT_OF_TIME ); requestNewOperationMode( DG_MODE_GENE ); } // Execute current Fill state switch ( fillState ) { case DG_FILL_MODE_STATE_START: waterQualityTestStartTime = getMSTimerCount(); fillState = DG_FILL_MODE_STATE_TEST_INLET_WATER; break; case DG_FILL_MODE_STATE_TEST_INLET_WATER: fillState = handleTestInletWaterState(); break; case DG_FILL_MODE_STATE_PRIME_CONCENTRATE_LINES: fillState = handlePrimeConcentrateLines(); break; case DG_FILL_MODE_STATE_FLUSH_BUBBLES: fillState = handleFlushBubbles(); break; 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; case DG_FILL_MODE_STATE_PAUSED: fillState = handlePausedState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_FILL_MODE_INVALID_EXEC_STATE, fillState ) fillState = DG_FILL_MODE_STATE_START; break; } return fillState; } /*********************************************************************//** * @brief * The getAvgFillFlowRate function returns the average fill flow rate in * each fill. * @details Inputs: none * @details Outputs: fillFlowRateAverage * @return average of the fill flow rate *************************************************************************/ F32 getAvgFillFlowRate( void ) { return fillStatus.fillFlowRateAverage; } /*********************************************************************//** * @brief * The getAverageFillTemperature function returns the average fill temperature * in each fill. * @details Inputs: none * @details Outputs: fillTemperatureAverage * @return average fill temperature *************************************************************************/ F32 getAvgFillTemperature( void ) { return fillStatus.fillTemperatureAverage; } /*********************************************************************//** * @brief * The getLastFillTemperature function returns the last fill temperature * in each fill. * @details Inputs: none * @details Outputs: fillLastTemperature * @return last fill temperature *************************************************************************/ F32 getLastFillTemperature( void ) { return fillStatus.fillLastTemperature; } /*********************************************************************//** * @brief * The resetFillStatusParameters function resets the fill status parameters. * @details Inputs: none * @details Outputs: fillStatus * @return none *************************************************************************/ 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 ); // 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; // 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.fillAvgConductivityOutOfRange = FALSE; fillStatus.fillEmtyAcidBottleDetected = FALSE; fillStatus.fillEmtyBicarbBottleDetected = FALSE; fillStatus.fillBottlesNeedPrimeFlag = FALSE; } /*********************************************************************//** * @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 isAvgConductivityOutOfRange function returns the boolean flag that indicates * whether the average conductivity is out of range. * @details Inputs: none * @details Outputs: none * @return fillStatus.fillAvgConductivityOutOfRange *************************************************************************/ BOOL isAvgConductivityOutOfRange( void ) { return fillStatus.fillAvgConductivityOutOfRange; } /*********************************************************************//** * @brief * The isBadFill function returns the boolean flag that indicates * whether the bad fill is detected at the end of the fill mode. * @details Inputs: none * @details Outputs: none * @return TRUE if bad fill, otherwise FALSE *************************************************************************/ BOOL isBadFill( void ) { BOOL badFillFlag = FALSE; if ( TRUE == isAvgConductivityOutOfRange() ) { badFillFlag = TRUE; } return badFillFlag; } /*********************************************************************//** * @brief * The isEmptyAcidBottle function returns the boolean flag that indicates * whether the acid bottle is empty. * @details Inputs: none * @details Outputs: none * @return fillStatus.fillEmtyAcidBottleDetected *************************************************************************/ BOOL isEmptyAcidBottle( void ) { return fillStatus.fillEmtyAcidBottleDetected; } /*********************************************************************//** * @brief * The isEmptyBicarbBottle function returns the boolean flag that indicates * whether the bicarb bottle is empty. * @details Inputs: none * @details Outputs: none * @return fillStatus.fillEmtyBicarbBottleDetected *************************************************************************/ BOOL isEmptyBicarbBottle( void ) { return fillStatus.fillEmtyBicarbBottleDetected; } /*********************************************************************//** * @brief * The setBottlesNeedPrimeFlag function sets the boolean flag that indicates * the acid and bicarb bottle need priming. * @details Inputs: none * @details Outputs: none * @return fillStatus.fillBottlesNeedPrimeFlag * @param flag to TRUE if prime is needed otherwise FALSE *************************************************************************/ BOOL setBottlesNeedPrimeFlag( BOOL flag ) { fillStatus.fillBottlesNeedPrimeFlag = flag; return fillStatus.fillBottlesNeedPrimeFlag; } /*********************************************************************//** * @brief * The getBottlesNeedPrimeFlag function gets the boolean flag that indicates * the acid and bicarb bottle need priming. * @details Inputs: none * @details Outputs: none * @return fillStatus.fillBottlesNeedPrimeFlag *************************************************************************/ BOOL getBottlesNeedPrimeFlag( void ) { return fillStatus.fillBottlesNeedPrimeFlag; } /*********************************************************************//** * @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 handleTestInletWaterState( void ) { 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 #ifndef DISABLE_MIXING if ( TRUE == isThisTheFirstFill() || TRUE == getBottlesNeedPrimeFlag() ) { // 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 ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); // TODO if requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); concentratePrimingStartTime = getMSTimerCount(); result = DG_FILL_MODE_STATE_PRIME_CONCENTRATE_LINES; } else #endif { result = DG_FILL_MODE_STATE_PRODUCE_DIALYSATE; } } #ifndef DISABLE_DIALYSATE_CHECK if ( TRUE == didTimeout( waterQualityTestStartTime, WATER_QUALITY_TEST_TIME_OUT_MS ) ) { activateAlarmNoData( ALARM_ID_DG_BAD_INLET_WATER_QUALITY ); // alarm is recoverable should go to fill paused state result = DG_FILL_MODE_STATE_PAUSED; // paused state should go back to DG_FILL_MODE_STATE_TEST_INLET_WATER } // when alarm acknowledged #endif return result; } /*********************************************************************//** * @brief * The handlePrimeConcentrateLines function primes the acid and bicarb * lines. * @details Inputs: None * @details Outputs: None * @return the next state *************************************************************************/ static DG_FILL_MODE_STATE_T handlePrimeConcentrateLines( 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( roPumpFlushBubblesSpeed[ pumpSpeedIndex ] / MILLILITERS_PER_LITER, TARGET_RO_PRESSURE_PSI ); flushBubblesStartTime = getMSTimerCount(); // State transition result = DG_FILL_MODE_STATE_FLUSH_BUBBLES; } return result; } /*********************************************************************//** * @brief * The handleFlushBubbles 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 handleFlushBubbles( 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( roPumpFlushBubblesSpeed[ pumpSpeedIndex ] / MILLILITERS_PER_LITER, TARGET_RO_PRESSURE_PSI ); flushBubblesStartTime = getMSTimerCount(); } else { // Initialization totalBicardConductivity = 0.0; averageBicardConductivity = 0.0; bicardConductivitySampleCount = 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 handleTestBicarbConductivityState( void ) { DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_TEST_BICARB_CONDUCTIVITY; F32 bicardConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); totalBicardConductivity += bicardConductivity; bicardConductivitySampleCount++; if ( TRUE == didTimeout( concentrateTestStartTime, CONCENTRATE_TEST_TIME_OUT_MS ) ) { averageBicardConductivity = totalBicardConductivity / bicardConductivitySampleCount; if ( TRUE == isValueWithinPercentRange( averageBicardConductivity, BICARD_TEST_CD2_TCD, FIVE_PERCENT_FACTOR ) ) { // Initialization requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); totalBicardConductivity = 0.0; totalAcidConductivity = 0.0; averageBicardConductivity = 0.0; averageAcidConductivity = 0.0; pctDiffInConductivity = 0.0; bicardConductivitySampleCount = 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 { SET_ALARM_WITH_1_F32_DATA( ALARM_ID_BICARB_CONDUCTIVITY_OUT_OF_RANGE, averageBicardConductivity ); requestNewOperationMode( DG_MODE_GENE ); } } return result; } /*********************************************************************//** * @brief * The handleTestAcidConductivityState function validates that the average * conductivity of the acid and bicard concentrate are within 5% of the * theoretical conductivity over 30 seconds sampling period. * acid concentrate. * @details Inputs: Acid and bicard conductivity sensor values * @details Outputs: Verified acid, bicard, acid & bicard conductivity values * @return the next state *************************************************************************/ static DG_FILL_MODE_STATE_T handleTestAcidConductivityState( void ) { DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_TEST_ACID_CONDUCTIVITY; F32 acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); F32 bicardConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); totalBicardConductivity += bicardConductivity; bicardConductivitySampleCount++; totalAcidConductivity += acidConductivity; acidConductivitySampleCount++; if ( TRUE == didTimeout( concentrateTestStartTime, CONCENTRATE_TEST_TIME_OUT_MS ) ) { averageBicardConductivity = totalBicardConductivity / bicardConductivitySampleCount; averageAcidConductivity = totalAcidConductivity / acidConductivitySampleCount; pctDiffInConductivity = fabs( 2.0 * ( averageAcidConductivity - averageBicardConductivity ) / ( averageAcidConductivity + averageBicardConductivity ) ); if ( ( TRUE == isValueWithinPercentRange(averageBicardConductivity, BICARD_TEST_CD2_TCD, FIVE_PERCENT_FACTOR) ) && ( TRUE == isValueWithinPercentRange(averageAcidConductivity, ACID_TEST_CD2_TCD, FIVE_PERCENT_FACTOR) ) && ( pctDiffInConductivity < FIVE_PERCENT_FACTOR ) ) { // Do the necessary setup here before transition to Produce Dialysate State fillStatus.isThisFirstFill = FALSE; requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); setROPumpTargetFlowRateLPM( getTargetFillFlowRateLPM(), TARGET_RO_PRESSURE_PSI ); result = DG_FILL_MODE_STATE_PRODUCE_DIALYSATE; } else { SET_ALARM_WITH_1_F32_DATA( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE, averageAcidConductivity ); requestNewOperationMode( DG_MODE_GENE ); } } return result; } /*********************************************************************//** * @brief * 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 handleProduceDialysateState( void ) { DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_PRODUCE_DIALYSATE; F32 measuredROFlowRateMLPM = getMeasuredROFlowRateLPM() * ML_PER_LITER; #ifndef DISABLE_DIALYSATE_CHECK if ( ( TRUE == isWaterQualityGood() ) && ( TRUE == checkDialysateTemperature() ) ) // SRSDG 216 #else if ( TRUE ) #endif { // Prime mixing before deliver result to reservoir handleDialysateMixing( measuredROFlowRateMLPM ); #ifndef DISABLE_MIXING requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); // SRSDG 217 requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); // SRSDG 217 if ( concentratePumpPrimeCount++ > CONCENTRATE_PUMP_PRIME_INTERVAL ) #endif { fillStatus.isThisFirstFill = FALSE; setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; } } else { concentratePumpPrimeCount = 0; } return result; } /*********************************************************************//** * @brief * The handleDeliverDialysateState function executes the deliver dialysate * state of the fill mode state machine. * @details Inputs: inlet water quality and dialysate temperature * @details Outputs: Deliver dialysate to inactive reservoir * @return the next state *************************************************************************/ static DG_FILL_MODE_STATE_T handleDeliverDialysateState( void ) { DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; DG_RESERVOIR_ID_T inactiveReservoir = getInactiveReservoir(); F32 integratedVolumeML; F32 measuredROFlowRateMLPM = getMeasuredROFlowRateLPM() * ML_PER_LITER; F32 acidConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); F32 bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); // Set concentrate pumps speed based on the RO pump flow rate handleDialysateMixing( measuredROFlowRateMLPM ); 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; 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 ) ) // SRSDG 240 , SRSDG 397 { requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); result = DG_FILL_MODE_STATE_PRODUCE_DIALYSATE; } #endif #ifndef DISABLE_MIXING // Detect empty bottles using integrated volumes // TODO: empty bottles detection using conductivity sensors if ( ( ( ACID_CONCENTRATION_BOTTLE_VOLUME_ML - getF32OverrideValue( &usedAcidVolumeML ) ) <= CONCENTRATION_BOTTLE_LOW_VOLUME_ML ) ) // || // SRSDG 437 { usedAcidVolumeML.data = 0.0; requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); //activateAlarmNoData( ALARM_ID_DG_ACID_BOTTLE_LOW_VOLUME ); // trigger empty acid bottle alarm at the end of drain mode and set both bottles need prime fillStatus.isThisFirstFill = TRUE; // empty bottles need replaced, set isThisFirstFill to FALSE so that prime, flush, acid & bicarb test are needed fillStatus.fillEmtyAcidBottleDetected = TRUE; // set this variable to FALSE when user presses OK on the alarm to confirm bottle has been replaced and resume //requestNewOperationMode( DG_MODE_GENE ); } if ( ( ( BICARB_CONCENTRATION_BOTTLE_VOLUME_ML - getF32OverrideValue( &usedBicarbVolumeML ) ) <= CONCENTRATION_BOTTLE_LOW_VOLUME_ML ) ) // || // SRSDG 438 { usedBicarbVolumeML.data = 0.0; requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); //activateAlarmNoData( ALARM_ID_DG_BICARB_BOTTLE_LOW_VOLUME ); // trigger empty bicarb bottle alarm at the end of drain mode and set both bottles need prime fillStatus.isThisFirstFill = TRUE; fillStatus.fillEmtyBicarbBottleDetected = TRUE; //requestNewOperationMode( DG_MODE_GENE ); // Fill -> Generation Idle } #endif // 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; F32 avgBicardConductivity = bicarbConductivityTotal / conductivitySampleCount; fillStatus.fillAvgConductivityOutOfRange = FALSE; // init to FALSE when fill has reached target #ifndef DISABLE_FLOW_CHECK_IN_FILL if ( integratedVolumeToLoadCellReadingPercent > FLOW_INTEGRATED_VOLUME_CHECK_TOLERANCE ) // SRSDG 439 { SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DG_FLOW_METER_CHECK_FAILURE, filledVolumeML, integratedVolumeML ); } #endif #ifndef DISABLE_DIALYSATE_CHECK // SRSDG 400 if ( TRUE == isValueWithinPercentRange( avgBicardConductivity, BICARB_NORMAL_CONDUCTIVITY, FIVE_PERCENT_FACTOR ) ) { fillStatus.fillAvgConductivityOutOfRange = TRUE; // flag to signal idle mode to handle bad fill fillStatus.isThisFirstFill = TRUE; setBottlesNeedPrimeFlag( TRUE ); SET_ALARM_WITH_2_F32_DATA( ALARM_ID_REPLACE_CONCENTRATE_BOTTLES, avgBicardConductivity, BICARB_NORMAL_CONDUCTIVITY ); // trigger replace bottles alarm #1 SET_ALARM_WITH_2_F32_DATA( ALARM_ID_CREATING_DIALYSATE_PLEASE_WAIT, avgBicardConductivity, BICARB_NORMAL_CONDUCTIVITY ); // immediately trigger wait for dialysate alarm #2 requestNewOperationMode( DG_MODE_GENE ); // leave fill mode and go to idle } if ( TRUE == isValueWithinPercentRange( avgAcidConductivity, ACID_NORMAL_CONDUCTIVITY, FIVE_PERCENT_FACTOR ) ) { fillStatus.fillAvgConductivityOutOfRange = TRUE; // flag to signal idle mode to handle bad fill fillStatus.isThisFirstFill = TRUE; setBottlesNeedPrimeFlag( TRUE ); SET_ALARM_WITH_2_F32_DATA( ALARM_ID_REPLACE_CONCENTRATE_BOTTLES, avgAcidConductivity, ACID_NORMAL_CONDUCTIVITY ); // trigger replace bottles alarm #1 then SET_ALARM_WITH_2_F32_DATA( ALARM_ID_CREATING_DIALYSATE_PLEASE_WAIT, avgAcidConductivity, ACID_NORMAL_CONDUCTIVITY ); // immediately trigger wait for dialysate alarm #2 requestNewOperationMode( DG_MODE_GENE ); // leave fill mode and go to idle } #endif // Done with this fill. Calculate the average fill flow rate and average temperature // Reset the variables for the next fill // Get the last fill temperature before leaving to Generation Idle fillStatus.fillFlowRateAverage = fillStatus.fillFlowRateRunningSum / (F32)fillStatus.fillSampleCounter; fillStatus.fillTemperatureAverage = fillStatus.fillTemperatureRunningSum / (F32)fillStatus.fillSampleCounter; fillStatus.fillFlowRateRunningSum = 0.0; fillStatus.fillTemperatureRunningSum = 0.0; fillStatus.fillSampleCounter = 1; fillStatus.fillLastTemperature = getTemperatureValue( (U32)TEMPSENSORS_OUTLET_PRIMARY_HEATER ); // Write the latest fill data into the RTC RAM for heaters control // TODO test this and make sure it is writing it correctly setFillInfoToRTCRAM(); // SRSDG ??? requestNewOperationMode( DG_MODE_GENE ); } return result; } /*********************************************************************//** * @brief * The handlePausedState function executes the paused state of the fill * mode state machine. * @details Inputs: Empty bottle alarm active * @details Outputs: none * @return the next state *************************************************************************/ static DG_FILL_MODE_STATE_T handlePausedState( void ) { DG_FILL_MODE_STATE_T result = DG_FILL_MODE_STATE_PAUSED; if ( ( FALSE == isAlarmActive( ALARM_ID_ACID_CONDUCTIVITY_OUT_OF_RANGE ) ) && ( FALSE == isAlarmActive( ALARM_ID_BICARB_CONDUCTIVITY_OUT_OF_RANGE ) && ( FALSE == isAlarmActive( ALARM_ID_DG_BAD_INLET_WATER_QUALITY ))) ) { result = DG_FILL_MODE_STATE_TEST_INLET_WATER; } return result; } /*********************************************************************//** * @brief * The isWaterQualityGood function checks for inlet water quality. * @details Inputs: Temperature and conductivity alarms * @details Outputs: none * @return TRUE if water quality is good, otherwise FALSE *************************************************************************/ static BOOL isWaterQualityGood( void ) { BOOL isInletPressureGood = ( FALSE == isAlarmActive( ALARM_ID_INLET_WATER_LOW_PRESSURE ) ? FALSE : TRUE ); BOOL isWaterTemperatureGood = ( ( FALSE == isAlarmActive( ALARM_ID_INLET_WATER_HIGH_TEMPERATURE ) && FALSE == isAlarmActive( ALARM_ID_INLET_WATER_LOW_TEMPERATURE ) && FALSE == isAlarmActive( ALARM_ID_INLET_WATER_TEMPERATURE_IN_LOW_RANGE ) && FALSE == isAlarmActive( ALARM_ID_INLET_WATER_TEMPERATURE_IN_HIGH_RANGE ) ) ? FALSE : TRUE ); BOOL isWaterConductivityGood = ( ( FALSE == isAlarmActive( ALARM_ID_INLET_WATER_CONDUCTIVITY_IN_WARNING_RANGE ) && FALSE == isAlarmActive( ALARM_ID_INLET_WATER_LOW_CONDUCTIVITY ) && FALSE == isAlarmActive( ALARM_ID_RO_REJECTION_RATIO_OUT_OF_RANGE ) ) ? FALSE : TRUE ); return ( ( isInletPressureGood && isWaterTemperatureGood && isWaterConductivityGood ) ); } /*********************************************************************//** * @brief * The checkDialysateTemperatureSensorsDrift 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 ) { #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 ) ) { activateAlarmNoData( ALARM_ID_DG_DIALYSATE_TEMPERATURE_SENSORS_DRFIT_TIMEOUT ); } #endif } /*********************************************************************//** * @brief * The checkDialysateTemperature function checks dialysate temperature after * it gets heated up by primary heater. * @details Inputs: TPo temperature value * @details Outputs: None * @return TRUE if dialysate temperature is in range, otherwise FALSE *************************************************************************/ static BOOL checkDialysateTemperature( void ) { F32 dialysateTemp = getTemperatureValue( TEMPSENSORS_OUTLET_PRIMARY_HEATER ); F32 targetTemp = getHeaterTargetTemperature( DG_PRIMARY_HEATER ); return ( ( fabs( dialysateTemp - targetTemp ) <= DIALYSATE_TEMPERATURE_TOLERANCE_C ) ? TRUE : FALSE ); } /*********************************************************************//** * @brief * The 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 * @details Outputs: Set concentrate pump speed relative to RO pump flow rate * @param measuredROFlowRate_mL_min measured RO flow rate in mL/min * @return none *************************************************************************/ static void handleDialysateMixing( F32 measuredROFlowRate_mL_min ) { #ifndef DISABLE_MIXING // TODO what should we do with start volume from the structure? DG_ACID_CONCENTRATES_RECORD_T acid = getAcidConcentrateCalRecord(); DG_BICARB_CONCENTRATES_RECORD_T bicarb = getBicarbConcentrateCalRecord(); // Set concentrate pumps speed based off RO pump flow rate F32 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 } /*********************************************************************//** * @brief * The setFillInfoToRTCRAM function writes the fill information to the RTC * RAM at the end of each fill. This information is used for dialysate temperature * control. * @details Inputs: fillStatus.fillFlowRateAverage * @details Outputs: none * @return none *************************************************************************/ static void setFillInfoToRTCRAM( void ) { DG_HEATERS_RECORD_T record; record.averageFillFlow = fillStatus.fillFlowRateAverage; setHeatersInfoRecord( (U08*)&record ); } /*********************************************************************//** * @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 ( ++fillModeDataPublicationTimerCounter >= getU32OverrideValue( &fillModeDataPublishInterval ) ) { DG_FILL_MODE_DATA_T fillModeData; // Populate the data structure for publication fillModeData.averageAcidConductivity = averageAcidConductivity; fillModeData.averageBicardConductivity = averageBicardConductivity; fillModeData.isThisTheFirstFill = isThisTheFirstFill(); fillModeData.pctDiffInConductivity = pctDiffInConductivity; fillModeData.usedAcidVolumeML = usedAcidVolumeML.data; fillModeData.usedBicarbVolumeML = usedBicarbVolumeML.data; broadcastData( MSG_ID_DG_FILL_MODE_DATA, COMM_BUFFER_OUT_CAN_DG_BROADCAST, (U08*)&fillModeData, sizeof( DG_FILL_MODE_DATA_T ) ); fillModeDataPublicationTimerCounter = 0; } } /*********************************************************************//** * @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 bicard volume * @details Outputs: used bicard volume * @param: value : override used bicard 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 bicard volume. * @details Inputs: used bicard volume * @details Outputs: used bicard 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; } /**@}*/