/************************************************************************** * * Copyright (c) 2019-2025 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) 10-Apr-2025 * * @author (original) Leonardo Baloa * @date (original) 19-Nov-2019 * ***************************************************************************/ #include // For fabs #include "ConcentratePumps.h" #include "ConductivitySensors.h" #include "FlowSensors.h" #include "FPGA.h" #include "CPLD.h" #include "Heaters.h" #include "LoadCell.h" #include "ModeFill.h" #include "ModeGenIdle.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 "Utilities.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.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.8F ///< Target flow rate for RO pump during pause in liters. #define MILLILITERS_PER_LITER 1000.0F ///< One liter is 1000 milliliters #define ACID_PUMP_23_ML_PER_MIN 23.0F ///< Acid pump speed of 23.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 CONC_PUMPS_PRIME_AT_MAX_SPEED_TIME_OUT_MS ( 10 * MS_PER_SECOND ) ///< Concentrate pumps prime at maximum speed timeout in milliseconds. #define CONC_PUMPS_PRIME_CHECK_COND_SNSRS_TIME_OUT_MS ( 13 * MS_PER_SECOND ) ///< Concentrate pumps prime check conductivity sensors timeout in milliseconds. #define CONC_PUMPS_PRIME_MAX_ALLOWED_PRIME_TIME_OUT_MS ( 45 * MS_PER_SECOND ) ///< Concentrate pumps prime maximum allowed timeout in prime in milliseconds. #define HUNDRED_PERCENT_FACTOR 1.00F ///< Hundred percent of expected conductivity when first time reaches it. #define FLOW_INTEGRATED_VOLUME_CHECK_TOLERANCE 0.1F ///< Flow integrated volume has 10% tolerance compare to load cell reading. #define FIVE_PERCENT_FACTOR 0.05F ///< 5.0 / 100.0 used to calculate conductivity within range of -/+ 5%. #define BICARB_CHECK_TOL_FACTOR 0.2F ///< Bicarb check tolerance factor. #define ACID_CHECK_TOL_FACTOR 0.1F ///< Acid check tolerance factor. #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 DELAY_FMP_CHECK_START_BY_MS ( 10 * MS_PER_SECOND ) ///< Delay start of FMP check during dialysate deliver state by this amount of time (in ms). #define CONCENTRATE_TEST_COND_COLLECTION_DELAY_MS ( 5 * MS_PER_SECOND ) ///< Concentrate test conductivity data collection delay in milliseconds. #define MAX_CPO_CONDUCTIVITY_ALLOW 100.0F ///< Maximum CPo sensor conductivity value. #define MIN_FILL_TARGET_TO_CHECK_RO_AND_CPO_ML 550 ///< Minimum fill target to check the RO and CPo alarms in milliliters. // 2m long tubing to cap = 19.5 mL (acid line) + 7.92 mL/m * 2 m (tubing to cap) + 20.82 mL (straw) = 56.15 mL // Prime time in seconds = ( 56.15 mL / 48 mL/min ) x 60 second/min + 25 seconds margin time = 95 seconds. #define PRIME_CONCENTRATE_LINES_TIME_OUT_MS ( 95 * MS_PER_SECOND ) ///< Time required to prime the concentrate lines. #define FLUSH_BUBBLES_PUMP_TIME_OUT_MS ( 2 * MS_PER_SECOND ) ///< RO pump on during flush bubble interval in ms. #define DIALYSATE_TEMPERATURE_TOLERANCE_C 2.0F ///< Dialysate temperature tolerance in degree C. #define DATA_PUBLISH_COUNTER_START_COUNT 63 ///< Data publish counter start count. /// 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 for calibration check enumeration from dialin typedef enum dialin_fill_for_cal { DIALIN_FILL_FOR_CAL_PRIME = 0, ///< Dialin fill for calibration prime. DIALIN_FILL_FOR_CAL_BICARB_CHECK, ///< Dialin fill for calibration bicarb check. DIALIN_FILL_FOR_CAL_ACID_CHECK, ///< Dialin fill for calibration acid check. DIALIN_FILL_FOR_CAL_NONE, ///< Dialin fill for calibration none. NUM_OF_DIALIN_FILL_FOR_CAL ///< Number of dialin fill for calibration. } DIALIN_FILL_FOR_CAL_CHECK_T; /// 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 fillFlowRateAverageLPM; ///< Fill flow average value in L/min. F32 fillLastTemperature; ///< Fill last temperature value. BOOL isThisFirstFill; ///< Fill flag to indicate whether it is the first fill or not. BOOL isPrimeCondChecksSkipped; ///< Fill flag to indicate whether the acid and bicarb conductivity checks should be skipped after priming. } FILL_CONDITION_STATUS_T; /// Fill acid and bicarb types typedef struct { DG_ACID_TYPES_T acidType; ///< Acid type. DG_BICARB_TYPES_T bicarbType; ///< Bicarb type. } FILL_ACID_BICARB_TYPES_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 F32 reservoirBaseWeight; ///< Fill reservoir base weight. static FILL_CONDITION_STATUS_T fillStatus; ///< Fill condition status. static BOOL didFillRecoverFromPause; ///< Flag to indicate whether the previous state was pause or not. static U32 dialysateFillStartTimeMS; ///< Current time when starting to fill dialysate in milliseconds. static U32 dialysatePauseStartTimeMS; ///< Current time when starting a pause during fill dialysate in milliseconds. static U32 dialysatePauseElapsedTimeMS; ///< Elapsed time in pause during fill dialysate in milliseconds. 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 concPumpPrimeStartTimeMS; ///< Concentrate pump prime start time in milliseconds. static BOOL didFMPCheckStart; ///< Flag indicates whether FMP flow vs. LC volume check has begun. static F32 acidConductivityTotal; ///< Total of acid conductivity during fill. static F32 bicarbConductivityTotal; ///< Total of bicarb conductivity during fill. static U32 conductivitySampleCount; ///< Sample count of conductivity during fill. static 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 F32 sumFillCPoConductivity; ///< Sum of CPo conductivity readings over course of a fill to calculate an average at end of fill. static F32 sumFillRejRatio; ///< Sum of Rejection Ratio values over course of a fill to calculate an average at end of fill. static U32 fillCPoConductivitySampleCnt; ///< Number of samples in sum of CPo and Rej. Ratio conductivity readings for fill. static U32 pumpSpeedIndex; ///< Index used to access the desired pump speed in roPumpFlushBubblesSpeed table. static FILL_ACID_BICARB_TYPES_T chemicalsTypes; ///< Chemicals Types (acid and bicarb). static DG_CHEMICALS_FILL_COND_CAL_RECORD_T chemicalsCond; ///< Chemicals fill conductivity records. static F32 acidMixRatio; ///< Acid conductivity mix ratio. static F32 bicarbMixRatio; ///< Bicarb conductivity mix ratio. // NOTE: This variable should be initialized here because the init function is called every time and then this variable is set to FALSE even if the settings from the // UI wants the RO only mode. static BOOL hasROOnlyModeBeenEnabled = FALSE; ///< Flag to indicate the RO only mode has been set or not. // NOTE: This variable should be initialized here because the init function is called every time and then it cannot be initialized there. This variable is // set via Dialin for calibration check purposes only static DIALIN_FILL_FOR_CAL_CHECK_T dialinFillForCalCheck = DIALIN_FILL_FOR_CAL_NONE; ///< Dialin fill for calibration check. // NOTE: this variable should be initialized here because the init function is called every time and then this variable is set to 0. static U32 minRORejectionRatioPCTFromHD = 0; ///< Min RO rejection ratio in percent from HD. 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 OVERRIDE_F32_T integratedVolumeML = { 0.0, 0.0, 0.0, 0.0 }; ///< Total RO flow rate over period of time. // ********** private function prototypes ********** 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 void setModeFillStateTransition( DG_FILL_MODE_STATE_T state ); static BOOL areInletWaterConditionsAlarmsActive( void ); static BOOL areConductivityAlarmsActive( void ); static void handleDialysateMixing( F32 measuredROFlowRate_mL_min, F32 acidMixingRatio, F32 bicarbMixingRatio ); static BOOL isValueWithinPercentRange( F32 testValue, F32 baseValue, F32 percentFactor ); static void publishFillModeData( void ); /*********************************************************************//** * @brief * The initFillMode function initializes the fill mode module. * @details Inputs: none * @details Outputs: fill mode variables initialized. * @return none *************************************************************************/ void initFillMode( void ) { DG_ACID_CONCENTRATES_RECORD_T acid; DG_BICARB_CONCENTRATES_RECORD_T bicarb; getAcidConcentrateCalRecord( &acid ); getBicarbConcentrateCalRecord( &bicarb ); acidConductivitySampleCount = 0; acidConductivityTotal = 0.0F; acidMixRatio = acid.acidConcentrate[ CAL_DATA_ACID_CONCENTRATE_1 ].acidConcMixRatio; averageAcidConductivity = 0.0F; averageBicarbConductivity = 0.0F; bicarbConductivitySampleCount = 0; bicarbConductivityTotal = 0.0F; bicarbMixRatio = bicarb.bicarbConcentrate[ CAL_DATA_BICARB_CONCENTRATE_1 ].bicarbConcMixRatio; concentrateTestStartTime = 0; concPumpPrimeStartTimeMS = getMSTimerCount(); conductivitySampleCount = 0; dataPublishCounter = DATA_PUBLISH_COUNTER_START_COUNT; // This timer is set to 0 to be checked whether we were in pause but this is still 0 so so we the start time regardless // If we were filling and then went to pause we do not reset this anymore dialysateFillStartTimeMS = 0; dialysatePauseElapsedTimeMS = 0; dialysatePauseStartTimeMS = 0; didFillRecoverFromPause = FALSE; didFMPCheckStart = FALSE; fillCPoConductivitySampleCnt = 0; fillState = DG_FILL_MODE_STATE_TEST_INLET_WATER; integratedVolumeML.data = 0.0F; pctDiffInConductivity = 0.0F; pumpSpeedIndex = 0; reservoirBaseWeight = 0.0F; sumFillCPoConductivity = 0.0F; sumFillRejRatio = 0.0F; totalAcidConductivity = 0.0F; totalBicarbConductivity = 0.0F; } /*********************************************************************//** * @brief * The transitionToFillMode function prepares for transition to fill mode. * @details Inputs: none * @details Outputs: Re-initialized fill mode * @return none *************************************************************************/ U32 transitionToFillMode( void ) { F32 targetFlowLPM = getTargetFillFlowRateLPM(); initFillMode(); setCurrentSubState( NO_SUB_STATE ); // Set initial actuator states setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); #if !defined(DEBUG_ENABLED) && !defined (_RELEASE_) && !defined (_VECTORCAST_) // Compile this part of code only if it is not debug, release, or VectorCAST. This is to make sure this part of code is not compiled // and instrumented in VectorCAST // Reset the state machine // Note: the substate timers, and operation flags need to be reset on recovery, consider // reinitializing the state variables, the resetting the state, by calling setModeFillStateTransition() if ( ( TRUE == getTestConfigStatus( TEST_CONFIG_RECOVER_TREATMENT ) ) && ( DG_MODE_FAUL == getPreviousOperationMode() ) ) { // Restore the prior fillstate to a valid, running fill state, with timer state reset. fillState = restoreFillState; setValveState( VPI, VALVE_STATE_OPEN ); setValveState( VPD, VALVE_STATE_OPEN_C_TO_NC ); switch( fillState ) { case DG_FILL_MODE_STATE_TEST_INLET_WATER: setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); break; case DG_FILL_MODE_STATE_PRIME_CONCENTRATE_LINES: targetFlowLPM = RO_PUMP_800_ML_PER_MIN / MILLILITERS_PER_LITER; concentratePrimingStartTime = getMSTimerCount(); setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, CONCENTRATE_PUMP_MAX_SPEED ); setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, CONCENTRATE_PUMP_MAX_SPEED ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); setValveState( VDR, VALVE_STATE_DRAIN_C_TO_NO ); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); break; case DG_FILL_MODE_STATE_FLUSH_BUBBLES: case DG_FILL_MODE_STATE_TEST_BICARB_CONDUCTIVITY: case DG_FILL_MODE_STATE_TEST_ACID_CONDUCTIVITY: // For the above states, transition back to flush bubbles state pumpSpeedIndex = 0; targetFlowLPM = RO_PUMP_FLUSH_BUBBLES_FLOWS[ pumpSpeedIndex ] / MILLILITERS_PER_LITER; flushBubblesStartTime = getMSTimerCount(); fillState = DG_FILL_MODE_STATE_FLUSH_BUBBLES; break; case DG_FILL_MODE_STATE_PRODUCE_DIALYSATE: requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); // Do the necessary setup here before transition to Produce Dialysate State fillStatus.isThisFirstFill = FALSE; concPumpPrimeStartTimeMS = getMSTimerCount(); break; case DG_FILL_MODE_STATE_DELIVER_DIALYSATE: requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); dialysateFillStartTimeMS = getMSTimerCount(); fillStatus.isThisFirstFill = FALSE; break; case DG_FILL_MODE_STATE_PAUSED: targetFlowLPM = TARGET_RO_FLOW_RATE_IN_PAUSE_L; dialysatePauseStartTimeMS = getMSTimerCount(); break; } } #endif getFillChemicalCondRecord( &chemicalsCond ); 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 setROPumpTargetFlowRateLPM( targetFlowLPM, TARGET_RO_PRESSURE_PSI ); setHeaterTargetTemperature( DG_PRIMARY_HEATER, getPrimaryHeaterTargetTemperature() ); startHeater( DG_PRIMARY_HEATER ); setCPLDCleanLEDColor( CPLD_CLEAN_LED_OFF ); 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 ) { checkDialysateTemperatureSensors(); setHeaterTargetTemperature( DG_PRIMARY_HEATER, getPrimaryHeaterTargetTemperature() ); if ( fillState != DG_FILL_MODE_STATE_PAUSED ) { if ( ( TRUE == areInletWaterConditionsAlarmsActive() ) || ( TRUE == areConductivityAlarmsActive() ) ) { setModeFillStateTransition( DG_FILL_MODE_STATE_PAUSED ); fillState = DG_FILL_MODE_STATE_PAUSED; } } // Execute current Fill state switch ( fillState ) { case DG_FILL_MODE_STATE_TEST_INLET_WATER: fillState = handleTestInletWaterState(); break; case DG_FILL_MODE_STATE_PRIME_CONCENTRATE_LINES: fillState = handlePrimeConcentrateLinesState(); break; case DG_FILL_MODE_STATE_FLUSH_BUBBLES: fillState = handleFlushBubblesState(); 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_TEST_INLET_WATER; break; } publishFillModeData(); return fillState; } /*********************************************************************//** * @brief * The getAvgFillFlowRateLPM function returns the average fill flow rate in * each fill. * @details Inputs: none * @details Outputs: fillFlowRateAverageLPM * @return average of the fill flow rate *************************************************************************/ F32 getAvgFillFlowRateLPM( void ) { return fillStatus.fillFlowRateAverageLPM; } /*********************************************************************//** * @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 getIntegratedVolumeML function returns the integrated volume in mL * @details Inputs: integratedVolumeML * @details Outputs: integrated volume * @return integrated volume *************************************************************************/ F32 getIntegratedVolumeML( void ) { F32 integratedVolume = getF32OverrideValue( &integratedVolumeML ); return integratedVolume; } /*********************************************************************//** * @brief * The resetFillStatusParameters function resets the fill status parameters. * @details Inputs: none * @details Outputs: fillStatus * @return none *************************************************************************/ void resetFillStatusParameters( void ) { fillStatus.fillFlowRateAverageLPM = getTargetROPumpFlowRateLPM(); fillStatus.fillFlowRateRunningSum = 0.0F; fillStatus.fillSampleCounter = 0; fillStatus.fillTemperatureRunningSum = 0.0F; // 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.isPrimeCondChecksSkipped = 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 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 * @return none *************************************************************************/ void setThisFirstFillFlag( BOOL flag ) { fillStatus.isThisFirstFill = flag; } /*********************************************************************//** * @brief * The enableROOnlyMode function sets the boolean flag that indicates RO * only mode has been enabled. * @details Inputs: none * @details Outputs: hasROOnlyModeBeenEnabled * @param enableROOnlyMode TRUE if the RO only mode is requested otherwise, FALSE * @return none *************************************************************************/ void setROMode( BOOL enableROOnlyMode ) { hasROOnlyModeBeenEnabled = enableROOnlyMode; } /*********************************************************************//** * @brief * The isROOnlyModeEnabled function returns the status of RO only mode. * @details Inputs: none * @details Outputs: none * @return hasROOnlyModeBeenEnabled, TRUE if the RO only mode has been set * otherwise, FALSE *************************************************************************/ BOOL isROOnlyModeEnabled( void ) { return hasROOnlyModeBeenEnabled; } /*********************************************************************//** * @brief * The setAcidAndBicarbType function sets the acid and bicarb types to be * used in filling. * @details Inputs: none * @details Outputs: chemicalsTypes * @param acid which is the type of acid * @param bicarb which is the type of bicarb * @return none *************************************************************************/ void setAcidAndBicarbType( U32 acid, U32 bicarb ) { chemicalsTypes.acidType = (DG_ACID_TYPES_T)acid; chemicalsTypes.bicarbType = (DG_BICARB_TYPES_T)bicarb; } /*********************************************************************//** * @brief * The setMinRORejectionRatioPCT function sets the RO rejection ratio in * percent that has been received from the HD institutional record. * @details Inputs: none * @details Outputs: roRejectionRatioFromHD * @param RO rejection ratio * @return none *************************************************************************/ void setMinRORejectionRatioPCT( U32 roRejectionRatio ) { U32 priorMinRORejectionRatio = minRORejectionRatioPCTFromHD; minRORejectionRatioPCTFromHD = roRejectionRatio; SEND_EVENT_WITH_2_U32_DATA( DG_EVENT_MIN_RO_REJECTION_RATIO_PCT_FROM_HD_INSTIT_RECORD, priorMinRORejectionRatio, minRORejectionRatioPCTFromHD ) } /*********************************************************************//** * @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: none * @details Outputs: none * @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; if ( ( isConcPumpParkInProgress( CONCENTRATEPUMPS_CP1_ACID ) != TRUE ) && ( isConcPumpParkInProgress( CONCENTRATEPUMPS_CP2_BICARB ) ) != TRUE ) { // If this is the first fill of a treatment, prime acid and bicarb lines, otherwise transition // to dialysate production directly if ( TRUE == isThisTheFirstFill() ) { result = DG_FILL_MODE_STATE_PRIME_CONCENTRATE_LINES; if ( TRUE == getTestConfigStatus( TEST_CONFIG_EXPEDITE_PRE_TREATMENT ) ) { // If expedite pre-treatment flag is enabled, do not do the initial fill result = DG_FILL_MODE_STATE_PRODUCE_DIALYSATE; } } else { result = DG_FILL_MODE_STATE_PRODUCE_DIALYSATE; } if ( TRUE == isTestingActivated() ) { switch ( dialinFillForCalCheck ) { case DIALIN_FILL_FOR_CAL_PRIME: result = DG_FILL_MODE_STATE_PRIME_CONCENTRATE_LINES; break; case DIALIN_FILL_FOR_CAL_BICARB_CHECK: result = DG_FILL_MODE_STATE_TEST_BICARB_CONDUCTIVITY; break; case DIALIN_FILL_FOR_CAL_ACID_CHECK: result = DG_FILL_MODE_STATE_TEST_ACID_CONDUCTIVITY; break; default: // Do nothing. This means there is not a request for the fill calibration check so proceed with the normal operations. break; } } setModeFillStateTransition( result ); } return result; } /*********************************************************************//** * @brief * The handlePrimeConcentrateLinesState function primes the acid and bicarb * lines. * @details Inputs: concentratePrimingStartTime * @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 ) ) { result = DG_FILL_MODE_STATE_FLUSH_BUBBLES; setModeFillStateTransition( result ); } 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 - 1 ) { setROPumpTargetFlowRateLPM( RO_PUMP_FLUSH_BUBBLES_FLOWS[ ++pumpSpeedIndex ] / MILLILITERS_PER_LITER, TARGET_RO_PRESSURE_PSI ); flushBubblesStartTime = getMSTimerCount(); } else { result = ( FALSE == fillStatus.isPrimeCondChecksSkipped ? DG_FILL_MODE_STATE_TEST_BICARB_CONDUCTIVITY : DG_FILL_MODE_STATE_PRODUCE_DIALYSATE ); setModeFillStateTransition( result ); } } 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 bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); if ( TRUE == didTimeout( concentrateTestStartTime, CONCENTRATE_TEST_COND_COLLECTION_DELAY_MS ) ) { totalBicarbConductivity += bicarbConductivity; bicarbConductivitySampleCount++; } if ( TRUE == didTimeout( concentrateTestStartTime, CONCENTRATE_TEST_TIME_OUT_MS ) ) { // In the bicarb test, the acid value is 0, so it is not queried F32 bicarbCondUSPerCM = chemicalsCond.fillCondValues[ chemicalsTypes.acidType ][ FILL_COND_BICARB_TEST ].bicarbConduSPerCM; BOOL isConductivityInRange = FALSE; averageBicarbConductivity = totalBicarbConductivity / bicarbConductivitySampleCount; SEND_EVENT_WITH_2_F32_DATA( DG_EVENT_BICARB_CHECK_RESULT, averageBicarbConductivity, bicarbCondUSPerCM ) if ( TRUE == isValueWithinPercentRange( averageBicarbConductivity, bicarbCondUSPerCM, BICARB_CHECK_TOL_FACTOR ) ) { isConductivityInRange = TRUE; } if ( ( TRUE == isConductivityInRange ) || ( TRUE == getTestConfigStatus( TEST_CONFIG_MIX_WITH_WATER ) ) ) { result = DG_FILL_MODE_STATE_TEST_ACID_CONDUCTIVITY; setModeFillStateTransition( result ); } else { // Bicarb test failed, go to fault SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DG_BICARB_CONDUCTIVITY_OUT_OF_RANGE, averageBicarbConductivity, bicarbCondUSPerCM ) } } return result; } /*********************************************************************//** * @brief * 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 and bicarb conductivity sensor values * @details Outputs: Verified acid, bicarb, acid & bicarb 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 bicarbConductivity = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); if ( TRUE == didTimeout( concentrateTestStartTime, CONCENTRATE_TEST_COND_COLLECTION_DELAY_MS ) ) { totalBicarbConductivity += bicarbConductivity; bicarbConductivitySampleCount++; totalAcidConductivity += acidConductivity; acidConductivitySampleCount++; } if ( TRUE == didTimeout( concentrateTestStartTime, CONCENTRATE_TEST_TIME_OUT_MS ) ) { F32 acidCondUSPerCM = chemicalsCond.fillCondValues[ chemicalsTypes.acidType ][ FILL_COND_ACID_TEST ].bicarbConduSPerCM; BOOL hasAcidTestPassed = FALSE; averageBicarbConductivity = totalBicarbConductivity / bicarbConductivitySampleCount; averageAcidConductivity = totalAcidConductivity / acidConductivitySampleCount; pctDiffInConductivity = fabs( 2.0F * ( averageAcidConductivity - averageBicarbConductivity ) / ( averageAcidConductivity + averageBicarbConductivity ) ); // NOTE: CD2 or bicarb conductivity sensor is used for acid check since it is a more accurate sensor. // CD2 is the downstream of the acid concentrate pump so the acid sensed there. SEND_EVENT_WITH_2_F32_DATA( DG_EVENT_ACID_CHECK_RESULT, averageBicarbConductivity, acidCondUSPerCM ) SEND_EVENT_WITH_2_F32_DATA( DG_EVENT_COND1_VS_COND2_DIFF_RESULT, pctDiffInConductivity, 0.0F ) if ( ( TRUE == isValueWithinPercentRange( averageBicarbConductivity, acidCondUSPerCM, ACID_CHECK_TOL_FACTOR ) ) || ( TRUE == getTestConfigStatus( TEST_CONFIG_MIX_WITH_WATER ) ) ) { hasAcidTestPassed = TRUE; } else { SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DG_ACID_CONDUCTIVITY_OUT_OF_RANGE, averageBicarbConductivity, acidCondUSPerCM ) } if ( TRUE == hasAcidTestPassed ) { result = DG_FILL_MODE_STATE_PRODUCE_DIALYSATE; setModeFillStateTransition( result ); } } return result; } /*********************************************************************//** * @brief * The handleProduceDialysateState function executes the dialysate production * state of the fill mode state machine. * @details Inputs: chemicalsCond, concPumpPrimeStartTimeMS * @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; if ( TRUE == didTimeout( concPumpPrimeStartTimeMS, CONC_PUMPS_PRIME_AT_MAX_SPEED_TIME_OUT_MS ) ) { // Once the time for running the concentrate pumps at full speed has been elapsed, start running the pumps and the target mix ratio handleDialysateMixing( getMeasuredFlowRateLPM( RO_FLOW_SENSOR ) * ML_PER_LITER, acidMixRatio, bicarbMixRatio ); if ( TRUE == didTimeout( concPumpPrimeStartTimeMS, CONC_PUMPS_PRIME_CHECK_COND_SNSRS_TIME_OUT_MS ) ) { /* Once the time for priming the concentrate lines has elapsed, check the mixing conductivity of the sensors * If the bicarb conductivity values are at about 100% of the target concentrate during fill, transition to the next state * If the bicarb conductivity values are not at 100% but the maximum prime time has elapsed, transition to the next state */ F32 bicarbConduSPerCM = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); F32 bicarbFillConduSPerCM = chemicalsCond.fillCondValues[ chemicalsTypes.acidType ][ FILL_COND_NORMAL_OP ].bicarbConduSPerCM; /* we are insisting conductivity reach 100% of expected level since signal is very noisy and peak noise will reach 100% when average is about 93%. * if sensor noise is reduced, this level should also be reduced accordingly. */ if ( bicarbConduSPerCM >= ( bicarbFillConduSPerCM * HUNDRED_PERCENT_FACTOR ) ) { result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; setModeFillStateTransition( result ); } else if ( TRUE == didTimeout( concPumpPrimeStartTimeMS, CONC_PUMPS_PRIME_MAX_ALLOWED_PRIME_TIME_OUT_MS ) ) { result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; setModeFillStateTransition( result ); } if ( TRUE == getTestConfigStatus( TEST_CONFIG_MIX_WITH_WATER ) ) { // If mix with water is enabled do not wait for the conductivity values or timeout, transition to the next state result = DG_FILL_MODE_STATE_DELIVER_DIALYSATE; setModeFillStateTransition( result ); } } } 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 inactiveRsrvr = getInactiveReservoir(); F32 acidConduSPerCM = getConductivityValue( CONDUCTIVITYSENSORS_CD1_SENSOR ); F32 bicarbConduSPerCM = getConductivityValue( CONDUCTIVITYSENSORS_CD2_SENSOR ); F32 inletTemperature = getTemperatureValue( TEMPSENSORS_CONDUCTIVITY_SENSOR_2 ); F32 cpiConduSPerCM = getConductivityValue( CONDUCTIVITYSENSORS_CPI_SENSOR ); F32 cpoConduSPerCM = getConductivityValue( CONDUCTIVITYSENSORS_CPO_SENSOR ); F32 rejRatio = RO_REJECTION_RATIO_OUT_OF_RANGE_VALUE; // default 1:1 before calculation in case can't divide by zero. #ifndef _RELEASE_ if ( ( HW_CONFIG_BETA == getHardwareConfigStatus() ) || ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_ENABLE_USING_TPO_FOR_PRIMARY_HEATER_CONTROL ) ) ) { inletTemperature = getTemperatureValue( (U32)TEMPSENSORS_OUTLET_PRIMARY_HEATER ); } #endif // Set concentrate pumps speed based on the RO pump flow rate handleDialysateMixing( getMeasuredFlowRateLPM( RO_FLOW_SENSOR ) * ML_PER_LITER, acidMixRatio, bicarbMixRatio ); // Sum conductivity readings for CPo and RR checks if ( fabs(cpiConduSPerCM) >= NEARLY_ZERO ) { rejRatio = cpoConduSPerCM / cpiConduSPerCM; } sumFillCPoConductivity += cpoConduSPerCM; sumFillRejRatio += rejRatio; fillCPoConductivitySampleCnt++; // Delay start of FMP check, then begin integrating flow to a volume if ( TRUE == didTimeout( dialysateFillStartTimeMS, DELAY_FMP_CHECK_START_BY_MS ) ) { if ( FALSE == didFMPCheckStart ) { // When FMP check starts, set baseline weight of reservoir we are filling didFMPCheckStart = TRUE; reservoirBaseWeight = getReservoirWeight( getInactiveReservoir() ); } integratedVolumeML.data += getMeasuredROFlowRateWithConcPumpsLPM() * ML_PER_LITER * FLOW_INTEGRATOR; } acidConductivityTotal += acidConduSPerCM; bicarbConductivityTotal += bicarbConduSPerCM; conductivitySampleCount++; // DG is delivering dialysate keep collecting the sample counter and the measured flow fillStatus.fillSampleCounter += 1; fillStatus.fillFlowRateRunningSum += getMeasuredFlowRateLPM( RO_FLOW_SENSOR ); fillStatus.fillTemperatureRunningSum += inletTemperature; // TODO: Check for open straw door status and alarm if closed // Check if run out of time to fill the reservoir, disregarding time paused, // because pause time isn't the reservoir's, or loadcell's fault // the 5 minutes DIALYSATE_FILL_TIME_OUT is a mystery number. if ( TRUE == didTimeout( dialysateFillStartTimeMS, DIALYSATE_FILL_TIME_OUT + dialysatePauseElapsedTimeMS ) ) { activateAlarmNoData( ALARM_ID_DG_DIALYSATE_FILL_OUT_OF_TIME ); } // 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( inactiveRsrvr ) ) { F32 filledVolumeML = getReservoirWeight( inactiveRsrvr ) - reservoirBaseWeight; F32 integratedVolumeToLoadCellReadingPercent = fabs( 1.0F - ( getIntegratedVolumeML() / filledVolumeML ) ); // Filled volume more accurate and stable than the integrated volume from the flowmeter F32 avgAcidConduSPerCM = acidConductivityTotal / conductivitySampleCount; F32 avgBicarbConduSPerCM = bicarbConductivityTotal / conductivitySampleCount; F32 bicarbNormalConduSPerCM = chemicalsCond.fillCondValues[ chemicalsTypes.acidType ][ FILL_COND_NORMAL_OP ].bicarbConduSPerCM; SEND_EVENT_WITH_2_F32_DATA( DG_EVENT_AVG_DIALYSATE_FILL_COND_VALUES, avgAcidConduSPerCM, avgBicarbConduSPerCM ) SEND_EVENT_WITH_2_F32_DATA( DG_EVENT_RESERVOIR_FILL_VALUES, reservoirBaseWeight, filledVolumeML ) if ( getTargetFillVolumeML() > MIN_FILL_TARGET_TO_CHECK_RO_AND_CPO_ML ) { F32 avgCPo = sumFillCPoConductivity / (F32)fillCPoConductivitySampleCnt; // sample count incremented above w/o condition so no need for divide by zero checks F32 avgRR = sumFillRejRatio / (F32)fillCPoConductivitySampleCnt; // The rejection ratio = 1 - (institutional RR value in percent) / 100 // If the instit RR value is 0% the converted RR becomes 1. But If the instit RR value is 0, the RR alarm is not checked. // The institutional values are stored in HD and requested by DG F32 convRR = 1.0F - ( ( (F32)minRORejectionRatioPCTFromHD ) / FRACTION_TO_PERCENT_FACTOR ); SEND_EVENT_WITH_2_F32_DATA( DG_EVENT_CALC_RO_REJECTION_RATIO, convRR, avgRR ) if ( ( avgCPo > MAX_CPO_CONDUCTIVITY_ALLOW ) && ( getTestConfigStatus( TEST_CONFIG_MIX_WITH_WATER ) != TRUE ) ) { // Fault alarm per PRS 483 SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DG_OUTLET_PRIMARY_CONDUCTIVITY_OUT_OF_RANGE, avgCPo, MAX_CPO_CONDUCTIVITY_ALLOW ); } if ( ( avgRR > convRR ) && ( isROOnlyModeEnabled() != TRUE ) && ( minRORejectionRatioPCTFromHD > 0 ) ) { // Fault alarm per PRS 483 SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DG_RO_REJECTION_RATIO_OUT_OF_RANGE, avgRR, convRR ); } } // Check FMP vs. LC #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_FLOW_VS_LOAD_CELL_CHECK_IN_FILL ) != SW_CONFIG_ENABLE_VALUE ) #endif { if ( getTargetFillVolumeML() > MIN_FILL_TARGET_TO_CHECK_RO_AND_CPO_ML ) { if ( integratedVolumeToLoadCellReadingPercent > FLOW_INTEGRATED_VOLUME_CHECK_TOLERANCE ) // SRSDG 240 { SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DG_FLOW_METER_CHECK_FAILURE, filledVolumeML, getIntegratedVolumeML() ); } } } // SRSDG 400 if ( ( FALSE == isValueWithinPercentRange( avgBicarbConduSPerCM, bicarbNormalConduSPerCM, FIVE_PERCENT_FACTOR ) ) && ( getTargetFillVolumeML() > MIN_FILL_TARGET_TO_CHECK_RO_AND_CPO_ML ) && ( getTestConfigStatus( TEST_CONFIG_MIX_WITH_WATER ) != TRUE ) ) { setBadAvgConductivityDetectedFlag( TRUE ); // signal idle bad avg conductivity detected setThisFirstFillFlag( TRUE ); // Bad fill conductivity. Set this flag so after priming the concentrate lines, acid and bicarb conductivities are not checked // again to save time fillStatus.isPrimeCondChecksSkipped = TRUE; SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DG_FILL_CONDUCTIVITY_OUT_OF_RANGE, avgBicarbConduSPerCM, bicarbNormalConduSPerCM ); // trigger replace bottles alarm #1 clearAlarm( ALARM_ID_DG_CREATING_DIALYSATE_PLEASE_WAIT ); // clear this alarm before triggering in case previous fill was bad and still active from before activateAlarmNoData ( ALARM_ID_DG_CREATING_DIALYSATE_PLEASE_WAIT ); } // 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.fillFlowRateAverageLPM = fillStatus.fillFlowRateRunningSum / (F32)fillStatus.fillSampleCounter; fillStatus.fillTemperatureAverage = fillStatus.fillTemperatureRunningSum / (F32)fillStatus.fillSampleCounter; fillStatus.fillFlowRateRunningSum = 0.0F; fillStatus.fillTemperatureRunningSum = 0.0F; fillStatus.fillSampleCounter = 0; fillStatus.fillLastTemperature = inletTemperature; calculateHeaterEstimationGain( DG_PRIMARY_HEATER ); requestNewOperationMode( DG_MODE_GENE ); } return result; } /*********************************************************************//** * @brief * The handlePausedState function executes the paused state of the fill * mode state machine. * @details Inputs: Inlet water conditions and conductivity * @details Outputs: didFillRecoverFromPause * @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 == areInletWaterConditionsAlarmsActive() ) && ( FALSE == areConductivityAlarmsActive() ) ) { didFillRecoverFromPause = TRUE; result = DG_FILL_MODE_STATE_TEST_INLET_WATER; setModeFillStateTransition( result ); } return result; } /*********************************************************************//** * @brief * The setModeFillStateTransition function sets the actuators and variables * for the state transition in mode fill. * @details Inputs: pumpSpeedIndex, dialysatePauseStartTimeMS * didFillRecoverFromPause * @details Outputs: concPumpPrimeStartTimeMS, fillStatus, pumpSpeedIndex, * concentratePrimingStartTime, totalBicarbConductivity, averageBicarbConductivity, * bicarbConductivitySampleCount, concentrateTestStartTime, fillStatus, * flushBubblesStartTime, didFillRecoverFromPause * dialysatePauseElapsedTimeMS, dialysatePauseStartTimeMS * @param state mode fill state enum * @return none *************************************************************************/ static void setModeFillStateTransition( DG_FILL_MODE_STATE_T state ) { // Execute cleanup on exiting pause if ( state != fillState ) { if ( DG_FILL_MODE_STATE_PAUSED == fillState ) { // accumulate pause dialysatePauseElapsedTimeMS += calcTimeSince( dialysatePauseStartTimeMS ); } } // Execute on running state switch( state ) { case DG_FILL_MODE_STATE_TEST_INLET_WATER: // Do nothing break; case DG_FILL_MODE_STATE_PRIME_CONCENTRATE_LINES: // 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 ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); concentratePrimingStartTime = getMSTimerCount(); break; case DG_FILL_MODE_STATE_FLUSH_BUBBLES: requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID, NO_PARK_CONC_PUMPS ); requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB, NO_PARK_CONC_PUMPS ); // 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(); if ( ( DIALIN_FILL_FOR_CAL_PRIME == dialinFillForCalCheck ) && ( TRUE == isTestingActivated() ) ) { // After prime, the state transitions to flush bubbles. If the cal for check prime was set, transition to Gen Idle and // set the fill for cal check to none. This is done upon the transition of the next state so then we can transition to Gen Idle. dialinFillForCalCheck = DIALIN_FILL_FOR_CAL_NONE; requestNewOperationMode( DG_MODE_GENE ); } break; case DG_FILL_MODE_STATE_TEST_BICARB_CONDUCTIVITY: // Set pumps flow rate to prepare for bicarb conductivity testing setROPumpTargetFlowRateLPM( RO_PUMP_800_ML_PER_MIN / MILLILITERS_PER_LITER, TARGET_RO_PRESSURE_PSI ); setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, BICARB_PUMP_40_ML_PER_MIN ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); // Prior to collecting data for bicarb test, change the CD2 calibration table setCondcutivitySensorCalTable( CONDUCTIVITYSENSORS_CD2_SENSOR, CAL_DATA_CD2_COND_SENSOR_FILL_BICARB_TEST ); totalBicarbConductivity = 0.0F; averageBicarbConductivity = 0.0F; bicarbConductivitySampleCount = 0; concentrateTestStartTime = getMSTimerCount(); break; case DG_FILL_MODE_STATE_TEST_ACID_CONDUCTIVITY: // Initialization requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB, NO_PARK_CONC_PUMPS ); // 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_23_ML_PER_MIN ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); // Set back the conductivity of CD2 calibration table to the normal calibration table setCondcutivitySensorCalTable( CONDUCTIVITYSENSORS_CD2_SENSOR, CAL_DATA_CD2_COND_SENSOR ); totalBicarbConductivity = 0.0F; totalAcidConductivity = 0.0F; bicarbConductivitySampleCount = 0; acidConductivitySampleCount = 0; concentrateTestStartTime = getMSTimerCount(); if ( ( DIALIN_FILL_FOR_CAL_BICARB_CHECK == dialinFillForCalCheck ) && ( TRUE == isTestingActivated() ) ) { // After bicarb test, the state transitions to acid test. If the cal for check prime was set, transition to Gen Idle and // set the fill for cal check to none. This is done upon the transition of the next state so then we can transition to Gen Idle. // NOTE: If the bicarb test fails, it transitions to fault mode so it automatically exits mode fill so this is only for the situation // that bicarb test was successful dialinFillForCalCheck = DIALIN_FILL_FOR_CAL_NONE; requestNewOperationMode( DG_MODE_GENE ); } break; case DG_FILL_MODE_STATE_PRODUCE_DIALYSATE: setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, CONCENTRATE_PUMP_MAX_SPEED ); setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, CONCENTRATE_PUMP_MAX_SPEED ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP1_ACID ); requestConcentratePumpOn( CONCENTRATEPUMPS_CP2_BICARB ); concPumpPrimeStartTimeMS = getMSTimerCount(); fillStatus.isThisFirstFill = FALSE; fillStatus.isPrimeCondChecksSkipped = FALSE; if ( ( DIALIN_FILL_FOR_CAL_ACID_CHECK == dialinFillForCalCheck ) && ( TRUE == isTestingActivated() ) ) { // After acid test, the state transitions to acid test. If the cal for check prime was set, transition to Gen Idle and // set the fill for cal check to none. This is done upon the transition of the next state so then we can transition to Gen Idle. // NOTE: If the acid test fails, it transitions to fault mode so it automatically exits mode fill so this is only for the situation // that bicarb test was successful dialinFillForCalCheck = DIALIN_FILL_FOR_CAL_NONE; requestNewOperationMode( DG_MODE_GENE ); } break; case DG_FILL_MODE_STATE_DELIVER_DIALYSATE: setValveState( VPO, VALVE_STATE_FILL_C_TO_NC ); if ( ( didFillRecoverFromPause != TRUE ) || ( 0 == dialysateFillStartTimeMS ) ) { dialysateFillStartTimeMS = getMSTimerCount(); } didFillRecoverFromPause = FALSE; fillStatus.isThisFirstFill = FALSE; break; case DG_FILL_MODE_STATE_PAUSED: dialysatePauseStartTimeMS = getMSTimerCount(); setValveState( VPO, VALVE_STATE_NOFILL_C_TO_NO ); setROPumpTargetFlowRateLPM( TARGET_RO_FLOW_RATE_IN_PAUSE_L, TARGET_RO_PRESSURE_PSI ); requestConcentratePumpOff( CONCENTRATEPUMPS_CP1_ACID, NO_PARK_CONC_PUMPS ); requestConcentratePumpOff( CONCENTRATEPUMPS_CP2_BICARB, NO_PARK_CONC_PUMPS ); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DG_SOFTWARE_FAULT, SW_FAULT_ID_FILL_MODE_INVALID_EXEC_STATE, state ) break; } } /*********************************************************************//** * @brief * 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 areInletWaterConditionsAlarmsActive( void ) { // Check inlet water conductivity, temperature, pressure BOOL status = FALSE; checkInletWaterConductivity(); checkInletWaterTemperature(); checkInletWaterPressure(); // Check if any of the alarms are active if ( ( TRUE == isAlarmActive( ALARM_ID_DG_INLET_WATER_CONDUCTIVITY_IN_LOW_RANGE ) ) || ( TRUE == isAlarmActive( ALARM_ID_DG_INLET_WATER_CONDUCTIVITY_IN_HIGH_RANGE ) ) || ( TRUE == isAlarmActive( ALARM_ID_DG_INLET_WATER_TEMPERATURE_IN_HIGH_RANGE ) ) || ( TRUE == isAlarmActive( ALARM_ID_DG_INLET_WATER_TEMPERATURE_IN_LOW_RANGE ) ) || ( TRUE == isAlarmActive( ALARM_ID_DG_OUTLET_PRIMARY_CONDUCTIVITY_OUT_OF_RANGE ) ) || ( TRUE == isAlarmActive( ALARM_ID_DG_RO_REJECTION_RATIO_OUT_OF_RANGE ) ) || ( TRUE == isAlarmActive( ALARM_ID_DG_INLET_WATER_PRESSURE_IN_HIGH_RANGE ) ) || ( TRUE == isAlarmActive( ALARM_ID_DG_INLET_WATER_PRESSURE_IN_LOW_RANGE ) ) ) { status = TRUE; } return status; } /*********************************************************************//** * @brief * The areConductivityAlarmsActive function checks whether the inlet * acid/bicarb conductivity alarms are active. * @details Inputs: none * @details Outputs: None * @return TRUE if any of the alarms are active, otherwise, FALSE *************************************************************************/ static BOOL areConductivityAlarmsActive( void ) { BOOL status = FALSE; if ( ( TRUE == isAlarmActive( ALARM_ID_DG_BICARB_CONDUCTIVITY_OUT_OF_RANGE ) ) || ( TRUE == isAlarmActive( ALARM_ID_DG_ACID_CONDUCTIVITY_OUT_OF_RANGE ) ) ) { status = TRUE; } return status; } /*********************************************************************//** * @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 value to check range for * @param baseValue value associated with 100% * @param percentFactor percentage range (100% +/- this value) * @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, F32 acidMixingRatio, F32 bicarbMixingRatio ) { // Set concentrate pumps speed based off RO pump flow rate F32 acidCP1PumpFlowRate = acidMixingRatio * measuredROFlowRate_mL_min; F32 bicarbCP2PumpFlowRate = bicarbMixingRatio * 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 ); acidCP1PumpFlowRate = MAX( acidCP1PumpFlowRate, 0.0F ); bicarbCP2PumpFlowRate = MAX( bicarbCP2PumpFlowRate, 0.0F ); setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP1_ACID, acidCP1PumpFlowRate ); setConcentratePumpTargetSpeed( CONCENTRATEPUMPS_CP2_BICARB, bicarbCP2PumpFlowRate ); } /*********************************************************************//** * @brief * The publishFillModeData function publishes fill mode data * at the set interval. * @details Inputs: fillModeDataPublicationTimerCounter, fillModeDataPublishInterval, * dataPublishCounter * @details Outputs: fillModeData * @return none *************************************************************************/ static void publishFillModeData( void ) { // publish Fill mode 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.integratedVolumeML = getIntegratedVolumeML(); fillModeData.roOnlyModeStatus = (U32)isROOnlyModeEnabled(); fillModeData.badFillSignal = getCurrentBadFillSignal(); fillModeData.badFillState = getCurrentGenIdleBadFillState(); 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 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; } /*********************************************************************//** * @brief * The testSetIntegratedVolumeOverride function overrides the * integrated volume. * @details Inputs: value * @details Outputs: integratedVolumeML * @param value integrated volume in mL * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetIntegratedVolumeOverride( F32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { integratedVolumeML.ovData = value; integratedVolumeML.override = OVERRIDE_KEY; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testResetIntegratedVolumeOverride function resets the * override of integrated volume. * @details Inputs: none * @details Outputs: integratedVolumeML * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetIntegratedVolumeOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { integratedVolumeML.override = OVERRIDE_RESET; integratedVolumeML.ovData = integratedVolumeML.ovInitData; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testSetEnableTestChemsCondValuesStatus function enables the testing * of acid and bicarb conductivity values after priming. * @details Inputs: none * @details Outputs: fillStatus.isPrimeCondChecksSkipped * @return TRUE if set successful, FALSE if not *************************************************************************/ BOOL testSetEnableTestChemsCondValuesStatus( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { fillStatus.isPrimeCondChecksSkipped = FALSE; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testSetModeFillForCal function sets the variable to run mode fill only * for calibration check * @details Inputs: none * @details Outputs: fillForCalCheck * @param calForCheck the mode that is requested (0 = prime, 1 = bicarb check, * 2 = acid check) * @return TRUE if the request is in the range otherwise, FALSE *************************************************************************/ BOOL testSetModeFillForCal( U32 calForCheck ) { BOOL result = FALSE; if ( ( calForCheck < NUM_OF_DIALIN_FILL_FOR_CAL ) && ( TRUE == isTestingActivated() ) ) { dialinFillForCalCheck = (DIALIN_FILL_FOR_CAL_CHECK_T)calForCheck; result = TRUE; requestNewOperationMode( DG_MODE_FILL ); } return result; } /**@}*/