Index: firmware/App/Controllers/BalancingChamber.c =================================================================== diff -u -rfd897db8177752330ad08d877e0a13620513dbdc -rc4e1d97e73dd863ddffe895833d209e172b5ae4a --- firmware/App/Controllers/BalancingChamber.c (.../BalancingChamber.c) (revision fd897db8177752330ad08d877e0a13620513dbdc) +++ firmware/App/Controllers/BalancingChamber.c (.../BalancingChamber.c) (revision c4e1d97e73dd863ddffe895833d209e172b5ae4a) @@ -8,7 +8,7 @@ * @file BalancingChamber.c * * @author (last) Sameer Kalliadan Poyil -* @date (last) 16-Apr-2026 +* @date (last) 28-Apr-2026 * * @author (original) Vinayakam Mani * @date (original) 28-Jan-2025 @@ -22,6 +22,7 @@ #include "DryBiCart.h" #include "FpgaDD.h" #include "Heaters.h" +#include "Level.h" #include "ModeGenDialysate.h" #include "ModeStandby.h" #include "Messaging.h" @@ -44,22 +45,20 @@ #define BAL_CHAMBER_FILL_PRES_DROP_MS ( 200 / TASK_GENERAL_INTERVAL ) ///< Time (ms/tasktime) to confirm the balancing chamber filling started and corrosponding valves opened. #define BAL_CHAMBER_FILL_COMPLETE_MS ( 300 / TASK_GENERAL_INTERVAL ) ///< Time (ms/tasktime) to confirm the balancing chamber fill completed and pressure is within range #define SPENT_FILL_COMPLETE_PRES 25.0F ///< Spent fill complete pressure (PSI) for Diener 2000 pump. -#define SPENT_FILL_COMPLETE_PRES_QD_MID_PSIG 15.0F ///< Spent fill complete pressure (PSI) when Diener 1000 and 200 < Qd <= 400. +#define SPENT_FILL_COMPLETE_PRES_QD_MID_PSIG 10.0F ///< Spent fill complete pressure (PSI) when Diener 1000 and 200 < Qd <= 400. #define SPENT_FILL_COMPLETE_PRES_QD_HIGH_PSIG 20.0F ///< Spent fill complete pressure (PSI) when Diener 1000 and Qd > 400. #define SPENT_FILL_COMPLETE_QD_SLOPE_MAX_MLPM 300.0F ///< For Qd <= 300 mL/min, spent fill completion uses pressure-rise slope detection. -#define SPENT_FILL_COMPLETE_QD_TWO_HIT_MAX_MLPM 150.0F ///< For Qd <= 150 mL/min, require two rise detections; above this and <=300, one rise detection is enough. #define SPENT_FILL_COMPLETE_DP_RISE_PSIG 0.5F ///< Minimum 100 ms spent pressure rise (current minus n-2) to count as a rise hit at low Qd. #define SPENT_FILL_SLOPE_SPENT_PRESSURE_HIGH_PSIG 2.0F ///< Above this absolute spent pressure, only one rise hit is required. -#define SPENT_FILL_SLOPE_SPENT_PRESSURE_LOW_PSIG 0.5F ///< Below this absolute spent pressure, use Qd-based required rise hit count. #define SPENT_FILL_SLOPE_MAX_RISE_HITS 2 ///< Rise hits required when Qd <= 150 in the low spent-pressure band. -#define SPENT_FILL_DETECT_COUNT_TOL_PCT 0.20F ///< Allowed detection timing tolerance as a fraction of expected BC switching count. +#define SPENT_FILL_DETECT_COUNT_TOL_PCT 1.0F ///< Allowed detection timing tolerance as a fraction of expected BC switching count. #define QD_THRESHOLD_HIGH_MLPM 400.0F ///< Qd threshold (mL/min) below which mid pressure limit applies. #define SPENT_DIFF_COUNT_ZERO 0 ///< Zero count difference for spent side fill comparing target count #define D48_SPEED_ADJUST_FACTOR 0.5F ///< D48 speed adjustment factor ( 50% of speed adjustment = 0.5) #define D48_SPEED_RANGE_LIMIT 0.25F ///< D48 speed adjustment range check limit ( D48 speed can vary +/-25% of initial calculated speed) #define BICARB_CHAMBER_PERIODIC_FILL_TIME ( 1 * SEC_PER_MIN * \ ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ) ///< Periodic bicarb chamber fill request 60 sec x 20 = 1200 -#define BAL_CHAMBER_FILL_TIMEOUT_FACTOR 1.5 ///< Balancing Chamber fill timeout factor (150% of observed fill count) +#define BAL_CHAMBER_FILL_TIMEOUT_FACTOR 3.0F ///< Balancing Chamber fill timeout factor (300% of observed fill count) /// Payload record structure for balancing chamber switch only request typedef struct @@ -103,6 +102,9 @@ static BOOL isBalChamberSwitchingActive; ///< Flag indicating balancing chamber switching is active or not. static BOOL isBalChamberSwitchingOnRequested; ///< Flag indicating that a request was made to activate balancing chamber switching. static BOOL isBalChamberSwitchingOffRequested; ///< Flag indicating that a request was made to deactivate balancing chamber switching. +static F32 pendingTdDialysateFlowrate; ///< Pending TD dialysate flow rate; applied at FillEnd after a BC switch completes. +static BOOL isBalChamberSwitchingPeriodUpdatePending; ///< BC switching period update pending apply at fill end. +static U32 bcSwitchingBasedOnClosedPeriodCounter; ///< Valve-close segments remaining before first-cycle relaxations clear after Qd timing apply. //TODO: remove later once level sensor working static U32 bicarbChamberPeriodicFillCounter; @@ -111,16 +113,19 @@ static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState1FillStart( void ); static BAL_CHAMBER_EXEC_STATE_T handleBalChamberConcentrateControl(void); -static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState1ValvesClose( void ); +static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState1FillDetectComplete( void ); static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState1FillEnd(void); static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState2FillStart( void ); -static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState2ValvesClose( void ); +static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState2FillDetectComplete( void ); static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState2FillEnd(void); static BAL_CHAMBER_EXEC_STATE_T handleBalChamberStateIdle( void ); static void publishBalChamberData( void ); static U32 getBalChamberDataPublishInterval( void ); static void checkSpentFillComplete( F32 spentDialPressure ); static BOOL isSpentFillCompleteByPressure( F32 spentDialPressure, F32 qdMlpm, F32 spentFillCompletePresPsig ); +static void applyBalChamberSwitchingPeriod( F32 tdDialysateFlowrate ); +static BOOL isBalChamberTimeBasedSwitching( void ); +static void scheduleFirstCycleRelaxAfterQdApply( void ); /*********************************************************************//** * @brief @@ -176,6 +181,9 @@ isBalChamberSwitchingActive = FALSE; isBalChamberSwitchingOnRequested = FALSE; isBalChamberSwitchingOffRequested = FALSE; + pendingTdDialysateFlowrate = 0.0F; + isBalChamberSwitchingPeriodUpdatePending = FALSE; + bcSwitchingBasedOnClosedPeriodCounter = 0; //TODO:remove once level sensor working bicarbChamberPeriodicFillCounter = 0; } @@ -208,6 +216,42 @@ if ( lastTdDialysateFlowrate != tdDialysateFlowrate ) { + // Defer Qd timing update to FillEnd at end of the current BC switch + if ( ( TRUE == isBalChamberSwitchingActive ) && ( BAL_CHAMBER_STATE_IDLE != balChamberExecState ) ) + { + pendingTdDialysateFlowrate = tdDialysateFlowrate; + isBalChamberSwitchingPeriodUpdatePending = TRUE; + } + // Apply immediately when BC is inactive or still idle + else + { + pendingTdDialysateFlowrate = tdDialysateFlowrate; + isBalChamberSwitchingPeriodUpdatePending = TRUE; + applyBalChamberSwitchingPeriod( pendingTdDialysateFlowrate ); + scheduleFirstCycleRelaxAfterQdApply(); + } + } + else + { + pendingTdDialysateFlowrate = 0.0F; + isBalChamberSwitchingPeriodUpdatePending = FALSE; + } +} + +/*********************************************************************//** + * @brief + * The applyBalChamberSwitchingPeriod function applies the active balancing + * chamber switching time based on the provided dialysis flow rate. + * @details \b Inputs: Dialysis flow rate. + * @details \b Outputs: balChamberSwitchingFreq,balChamberSwitchingPeriod + * @return none + *************************************************************************/ +static void applyBalChamberSwitchingPeriod( F32 tdDialysateFlowrate ) +{ + if ( TRUE == isBalChamberSwitchingPeriodUpdatePending ) + { + U32 initialPumpSpeed = 0; + // update the balancing chamber switching frequency balChamberSwitchingFreq.data = tdDialysateFlowrate / BAL_CHAMBER_FILL_VOLUME_ML; @@ -220,7 +264,7 @@ //Update last td dialysate flow rate lastTdDialysateFlowrate = tdDialysateFlowrate; - // Update fill timeout count based on the switching period (e.g. 150% of period) + // Update fill timeout count based on the switching period (e.g. 300% of period) balChamberFillTimeoutCount = (U32)( (F32)balChamberSwitchingPeriod * BAL_CHAMBER_FILL_TIMEOUT_FACTOR ); //Reset the BC switching flag for new Qd. @@ -241,11 +285,48 @@ spentDialPressure = getFilteredPressure( D51_PRES ); lastPrevSpentDialPressure = spentDialPressure; prevSpentDialPressure = spentDialPressure; + + initialPumpSpeed = getCalculatedD48PumpSpeedForBCFill(); + setD48PumpSpeedForBCFill( initialPumpSpeed ); + setDialysatePumpTargetRPM( D48_PUMP, initialPumpSpeed, TRUE ); + + pendingTdDialysateFlowrate = 0.0F; + isBalChamberSwitchingPeriodUpdatePending = FALSE; } } /*********************************************************************//** * @brief + * The scheduleFirstCycleRelaxAfterQdApply function configures how many BC + * switches still use first-cycle relaxations after a Qd timing update. + * @details \b Inputs: none + * @details \b Outputs: bcSwitchingBasedOnClosedPeriodCounter + * @return none + *************************************************************************/ +static void scheduleFirstCycleRelaxAfterQdApply( void ) +{ + bcSwitchingBasedOnClosedPeriodCounter = 2; +} + +/*********************************************************************//** + * @brief + * The isBalChamberTimeBasedSwitching function checks whether the nominal + * (pre-clamp) D48 starting speed should use fixed-speed, time-based balancing + * chamber switching. + * @details \b Inputs: nominal ceiled D48 speed + * @details \b Outputs: none + * @return TRUE if time-based switching should be used; otherwise FALSE. + *************************************************************************/ +static BOOL isBalChamberTimeBasedSwitching( void ) +{ + BOOL state = FALSE; + state = ( getD48PumpSpeed() <= BAL_CHAMBER_TIME_BASED_D48_SPEED_RPM ) ? TRUE : FALSE; + + return state; +} + +/*********************************************************************//** + * @brief * The execBalancingChamberControl function executes the balancing chamber state machine. * @details \b Inputs: balChamberExecState * @details \b Outputs: balChamberExecState @@ -254,14 +335,25 @@ *************************************************************************/ U32 execBalancingChamberControl( void ) { - updateBalChamberSwitchingPeriod(); + LVL_STATE_T bicarbChamberLevel = getBicarbChamberLevelStatus(); // Increment counter indicating fill is in progress. currentBalChamberSwitchingCounter += 1; currentBalChamberFillCounter += 1; if ( ( getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DRY_BICARB ) == TRUE ) && ( balChamberExecState > BAL_CHAMBER_STATE_IDLE ) ) { +#ifdef CONDUCTIVE_LEVEL_SENSOR_ENABLED + if ( LVL_STATE_LOW == bicarbChamberLevel ) + { + if ( FALSE == setBicarbChamberFillRequested() ) + { + // TODO + //drybicart state machines are combined and so this is not an issue in future + //set alarm ? + } + } +#else // time based chamber F fill // Increment counter for dry bicarb chamber fill bicarbChamberPeriodicFillCounter += 1; // Fill bicarb chamber once every 60secs. @@ -275,6 +367,7 @@ } bicarbChamberPeriodicFillCounter = 0; } +#endif } switch ( balChamberExecState ) @@ -291,8 +384,8 @@ balChamberExecState = handleBalChamberConcentrateControl(); break; - case BAL_CHAMBER_STATE1_VALVES_CLOSE: - balChamberExecState = handleBalChamberState1ValvesClose(); + case BAL_CHAMBER_STATE1_FILL_DETECT_COMPLETE: + balChamberExecState = handleBalChamberState1FillDetectComplete(); break; case BAL_CHAMBER_STATE1_FILL_END: @@ -307,8 +400,8 @@ balChamberExecState = handleBalChamberConcentrateControl(); break; - case BAL_CHAMBER_STATE2_VALVES_CLOSE: - balChamberExecState = handleBalChamberState2ValvesClose(); + case BAL_CHAMBER_STATE2_FILL_DETECT_COMPLETE: + balChamberExecState = handleBalChamberState2FillDetectComplete(); break; case BAL_CHAMBER_STATE2_FILL_END: @@ -513,7 +606,6 @@ BAL_CHAMBER_EXEC_STATE_T state = BAL_CHAMBER_STATE1_FILL_START; balChamberSWState = BAL_CHAMBER_SW_STATE1; - balChamberFillTimeoutCount = 0; isBalChamberFillInProgress = FALSE; isPressureStabilizedDuringFill = FALSE; isPressureDroppedDuringFill = FALSE; @@ -627,17 +719,16 @@ ( TRUE == isConcentratePumpDosingCompleted( D10_PUMP ) ) ) || ( TRUE == getBalChamberSwitchingOnlyStatus() ) ) { - if ( BAL_CHAMBER_SW_STATE1 == balChamberSWState ) { // Low-Qd spent slope: baseline = spent pressure on last dosing tick (this task period) before VALVES_CLOSE. prevSpentDialPressure = spentDialPressure; - state = BAL_CHAMBER_STATE1_VALVES_CLOSE; + state = BAL_CHAMBER_STATE1_FILL_DETECT_COMPLETE; } else { prevSpentDialPressure = spentDialPressure; - state = BAL_CHAMBER_STATE2_VALVES_CLOSE; + state = BAL_CHAMBER_STATE2_FILL_DETECT_COMPLETE; } } @@ -646,33 +737,38 @@ /*********************************************************************//** * @brief - * The handleBalChamberState1ValvesClose function check for the balancing chamber + * The handleBalChamberState1FillDetectComplete function check for the balancing chamber * fill complete and close the currently opened valves. * @details \b Inputs: currentBalChamberSwitchingCounter, spent and fresh dialysate pressure * @details \b Outputs: isPressureStalbilizedDuringFill,isBalChamberFillInProgress * @return next balancing chamber state. *************************************************************************/ -static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState1ValvesClose( void ) +static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState1FillDetectComplete( void ) { - BAL_CHAMBER_EXEC_STATE_T state = BAL_CHAMBER_STATE1_VALVES_CLOSE; + BAL_CHAMBER_EXEC_STATE_T state = BAL_CHAMBER_STATE1_FILL_DETECT_COMPLETE; BOOL isBothFillsComplete = FALSE; BOOL isFirstCycleNotDone = FALSE; BOOL isFillCompleteOrFirstCycle = FALSE; freshDialPressure = getFilteredPressure( D18_PRES ); spentDialPressure = getFilteredPressure( D51_PRES ); - // If fill is taking too long, set an alarm for fill timeout - if ( ( balChamberFillTimeoutCount > 0 ) && ( currentBalChamberFillCounter > balChamberFillTimeoutCount ) ) + // After the first BC cycle, fault if fill never completes at the Qd-based timeout. + if ( ( currentBalChamberFillCounter > balChamberFillTimeoutCount ) && ( TRUE != getTestConfigStatus( TEST_CONFIG_DD_DISABLE_BC_PRESSURE_ALARMS ) ) ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_BC_FILL_TIMEOUT_FAULT, currentBalChamberFillCounter, balChamberFillTimeoutCount ); // Move to the idle state state = BAL_CHAMBER_STATE_IDLE; } + // If the dry bicarbonate is enabled then we ignore looking at d18 pressure to be in range. + if ( ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DRY_BICARB ) ) && ( TRUE == getBicarbChamberFillRequested() ) ) + { + isPressureStabilizedDuringFill = TRUE; + } // Check fresh dialysate pressure back in range to indicate fresh fill complete. - if ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) + else if ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) { if ( ++balChamberFillCompleteStablePressureCounter >= BAL_CHAMBER_FILL_COMPLETE_MS ) { @@ -681,26 +777,38 @@ } } - // Spent side of balancing chamber fill is complete or not checkSpentFillComplete( spentDialPressure ); // Check both spent and fresh side fill is complete - isBothFillsComplete = ( TRUE == isSpentFillComplete ) && ( TRUE == isPressureStabilizedDuringFill ); - isFirstCycleNotDone = ( FALSE == isFirstCycleBCSwitchingCompleted ); + isBothFillsComplete = ( ( TRUE == isSpentFillComplete ) && ( TRUE == isPressureStabilizedDuringFill ) ) ? TRUE : FALSE; + isFirstCycleNotDone = ( FALSE == isFirstCycleBCSwitchingCompleted ) && ( currentBalChamberSwitchingCounter >= balChamberValveClosePeriod ); if ( FALSE == getBalChamberSwitchingOnlyStatus() ) { - isFillCompleteOrFirstCycle = isBothFillsComplete || isFirstCycleNotDone; + isFillCompleteOrFirstCycle = ( ( TRUE == isBothFillsComplete ) || ( TRUE == isFirstCycleNotDone ) ) ? TRUE : FALSE; } if ( ( TRUE == isFillCompleteOrFirstCycle ) || - ( ( currentBalChamberSwitchingCounter >= balChamberValveClosePeriod ) && ( TRUE == getBalChamberSwitchingOnlyStatus() ) ) ) + ( ( currentBalChamberSwitchingCounter >= balChamberValveClosePeriod ) && ( TRUE == getBalChamberSwitchingOnlyStatus() ) ) ) { // close the state 1 opened valves valveControlForBCState1FillEnd(); isBalChamberFillInProgress = FALSE; - setDialysatePumpTargetRPM( D48_PUMP, getD48PumpSpeedForBCFill(), TRUE ); + if ( FALSE == getBalChamberSwitchingOnlyStatus() ) + { + setDialysatePumpTargetRPM( D48_PUMP, getD48PumpSpeedForBCFill(), TRUE ); + } + if ( bcSwitchingBasedOnClosedPeriodCounter > 0 ) + { + bcSwitchingBasedOnClosedPeriodCounter -= 1; + } + + if ( 0 == bcSwitchingBasedOnClosedPeriodCounter ) + { + isFirstCycleBCSwitchingCompleted = TRUE; + } + // Transition to next state state = BAL_CHAMBER_STATE1_FILL_END; } @@ -719,8 +827,6 @@ * balanceChamberSwitchingOnlyOffRequested * @details \b Alarm: ALARM_ID_DD_BC_STATE1_FILL_PRESSURE_DROP_OUT_OF_RANGE * when pressure drop is not in range during balancing chamber fill in progress. - * @details \b Alarm: ALARM_ID_DD_BC_STATE1_FILL_END_PRESSURE_OUT_OF_RANGE - * when pressure is not in range during balancing chamber fill complete. * @return next balancing chamber state. *************************************************************************/ static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState1FillEnd( void ) @@ -732,6 +838,12 @@ if ( TRUE != getBalChamberSwitchingOnlyStatus() ) { + if ( TRUE == isBalChamberSwitchingPeriodUpdatePending ) + { + applyBalChamberSwitchingPeriod( pendingTdDialysateFlowrate ); + scheduleFirstCycleRelaxAfterQdApply(); + } + if ( getTestConfigStatus( TEST_CONFIG_DD_DISABLE_BC_PRESSURE_ALARMS ) != TRUE ) { if ( ( TRUE != isPressureDroppedDuringFill ) && ( TRUE == isFirstCycleBCSwitchingCompleted ) ) @@ -742,14 +854,6 @@ // Move to the idle state state = BAL_CHAMBER_STATE_IDLE; } - else if ( TRUE != isPressureStabilizedDuringFill ) - { - // Alarm when switching time expired, but still pressure not in range which indicates fill is not yet completed. - SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DD_BC_STATE1_FILL_END_PRESSURE_OUT_OF_RANGE, freshDialPressure, spentDialPressure ); - - // Move to the idle state - state = BAL_CHAMBER_STATE_IDLE; - } else { // Move to next state when pressure is in range. @@ -822,7 +926,6 @@ isPressureStabilizedDuringFill = FALSE; isPressureDroppedDuringFill = FALSE; balChamberSWState = BAL_CHAMBER_SW_STATE2; - balChamberFillTimeoutCount = 0; lastPrevSpentDialPressure = 0.0F; prevSpentDialPressure = 0.0F; currentBalChamberSwitchingCounter = 0; @@ -894,61 +997,88 @@ /*********************************************************************//** * @brief - * The handleBalChamberState2ValvesClose function check for the balancing chamber + * The handleBalChamberState2FillDetectComplete function check for the balancing chamber * fill complete and close the currently opened valves. * @details \b Inputs: currentBalChamberSwitchingCounter, spent and fresh dialysate pressure * @details \b Outputs: isPressureStalbilizedDuringFill,isBalChamberFillInProgress * @return next balancing chamber state. *************************************************************************/ -static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState2ValvesClose( void ) +static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState2FillDetectComplete( void ) { - BAL_CHAMBER_EXEC_STATE_T state = BAL_CHAMBER_STATE2_VALVES_CLOSE; + BAL_CHAMBER_EXEC_STATE_T state = BAL_CHAMBER_STATE2_FILL_DETECT_COMPLETE; BOOL isBothFillsComplete = FALSE; BOOL isFirstCycleNotDone = FALSE; BOOL isFillCompleteOrFirstCycle = FALSE; freshDialPressure = getFilteredPressure( D18_PRES ); spentDialPressure = getFilteredPressure( D51_PRES ); - // Check fresh dialysate pressure back in range to indicate fill complete. - if ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) + // If the dry bicarbonate is enabled then we ignore looking at d18 pressure to be in range. + if ( ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DRY_BICARB ) ) && ( TRUE == getBicarbChamberFillRequested() ) ) { + isPressureStabilizedDuringFill = TRUE; + } + // Check fresh dialysate pressure back in range to indicate fresh fill complete. + else if ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) + { if ( ++balChamberFillCompleteStablePressureCounter >= BAL_CHAMBER_FILL_COMPLETE_MS ) { // stabilized pressure indicating fresh side fill is complete isPressureStabilizedDuringFill = TRUE; } } - // If fill is taking too long, set an alarm for fill timeout - if ( ( balChamberFillTimeoutCount > 0 ) && ( currentBalChamberFillCounter > balChamberFillTimeoutCount ) ) + // After the first BC cycle, fault if fill never completes at the Qd-based timeout. + if ( ( currentBalChamberFillCounter > balChamberFillTimeoutCount ) && ( TRUE != getTestConfigStatus( TEST_CONFIG_DD_DISABLE_BC_PRESSURE_ALARMS ) ) ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_BC_FILL_TIMEOUT_FAULT, currentBalChamberFillCounter, balChamberFillTimeoutCount ); // Move to the idle state state = BAL_CHAMBER_STATE_IDLE; } - // Spent side of balancing chamber fill is complete or not + // Check fresh dialysate pressure back in range to indicate fill complete. + if ( ( freshDialPressure >= FRESH_DIAL_PRESSURE_MIN_PSIG ) && ( freshDialPressure <= FRESH_DIAL_PRESSURE_MAX_PSIG ) ) + { + if ( ++balChamberFillCompleteStablePressureCounter >= BAL_CHAMBER_FILL_COMPLETE_MS ) + { + // stabilized pressure indicating fresh side fill is complete + isPressureStabilizedDuringFill = TRUE; + } + } + checkSpentFillComplete( spentDialPressure ); // Check switching cycle time or pressure check for valve closure - isBothFillsComplete = ( TRUE == isSpentFillComplete ) && ( TRUE == isPressureStabilizedDuringFill ); - isFirstCycleNotDone = ( FALSE == isFirstCycleBCSwitchingCompleted ); + isBothFillsComplete = ( ( TRUE == isSpentFillComplete ) && ( TRUE == isPressureStabilizedDuringFill ) ) ? TRUE : FALSE; + isFirstCycleNotDone = ( ( FALSE == isFirstCycleBCSwitchingCompleted ) && ( currentBalChamberSwitchingCounter >= balChamberValveClosePeriod ) ); if ( FALSE == getBalChamberSwitchingOnlyStatus() ) { - isFillCompleteOrFirstCycle = isBothFillsComplete || isFirstCycleNotDone; + isFillCompleteOrFirstCycle = ( ( TRUE == isBothFillsComplete ) || ( TRUE == isFirstCycleNotDone ) ) ? TRUE : FALSE; } if ( ( TRUE == isFillCompleteOrFirstCycle ) || - ( ( currentBalChamberSwitchingCounter >= balChamberValveClosePeriod ) && ( TRUE == getBalChamberSwitchingOnlyStatus() ) ) ) + ( ( currentBalChamberSwitchingCounter >= balChamberValveClosePeriod ) && ( TRUE == getBalChamberSwitchingOnlyStatus() ) ) ) { // close the valves valveControlForBCState2FillEnd(); isBalChamberFillInProgress = FALSE; - setDialysatePumpTargetRPM( D48_PUMP, getD48PumpSpeedForBCFill(), TRUE ); + if ( FALSE == getBalChamberSwitchingOnlyStatus() ) + { + setDialysatePumpTargetRPM( D48_PUMP, getD48PumpSpeedForBCFill(), TRUE ); + } + if ( bcSwitchingBasedOnClosedPeriodCounter > 0 ) + { + bcSwitchingBasedOnClosedPeriodCounter -= 1; + } + + if ( 0 == bcSwitchingBasedOnClosedPeriodCounter ) + { + isFirstCycleBCSwitchingCompleted = TRUE; + } + //Transition to next state state = BAL_CHAMBER_STATE2_FILL_END; } @@ -967,8 +1097,6 @@ * balanceChamberSwitchingOnlyOffRequested * @details \b Alarm: ALARM_ID_DD_BC_STATE2_FILL_PRESSURE_DROP_OUT_OF_RANGE * when pressure is not in range during balacing chamber state 2 fill in progress. - * @details \b Alarm: ALARM_ID_DD_BC_STATE2_FILL_END_PRESSURE_OUT_OF_RANGE - * when pressure is not in range during balacing chamber state 2 fill complete. * @return next balancing chamber state. *************************************************************************/ static BAL_CHAMBER_EXEC_STATE_T handleBalChamberState2FillEnd( void ) @@ -978,53 +1106,43 @@ balChamberFillPressureDropCounter = 0; balChamberFillCompleteStablePressureCounter = 0; - // Pressure alarm check - if ( TRUE != getBalChamberSwitchingOnlyStatus() ) + if ( TRUE != getBalChamberSwitchingOnlyStatus() ) + { + if ( TRUE == isBalChamberSwitchingPeriodUpdatePending ) { - if ( getTestConfigStatus( TEST_CONFIG_DD_DISABLE_BC_PRESSURE_ALARMS ) != TRUE ) + applyBalChamberSwitchingPeriod( pendingTdDialysateFlowrate ); + scheduleFirstCycleRelaxAfterQdApply(); + } + + if ( getTestConfigStatus( TEST_CONFIG_DD_DISABLE_BC_PRESSURE_ALARMS ) != TRUE ) + { + if ( ( TRUE != isPressureDroppedDuringFill ) && ( TRUE == isFirstCycleBCSwitchingCompleted ) ) { - if ( ( TRUE != isPressureDroppedDuringFill ) && ( TRUE == isFirstCycleBCSwitchingCompleted ) ) - { - // When fill initiated, pressure is not dropped to the expected range, possible valve failures. - SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DD_BC_STATE2_FILL_PRESSURE_DROP_OUT_OF_RANGE, freshDialPressure, spentDialPressure ); + // When fill initiated, pressure is not dropped to the expected range, possible valve failures. + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DD_BC_STATE2_FILL_PRESSURE_DROP_OUT_OF_RANGE, freshDialPressure, spentDialPressure ); - // Move to the idle state - state = BAL_CHAMBER_STATE_IDLE; - } - else if ( TRUE != isPressureStabilizedDuringFill ) - { - // Alarm when switching time expired, but still pressure not in range which indicates fill is not completed. - SET_ALARM_WITH_2_F32_DATA( ALARM_ID_DD_BC_STATE2_FILL_END_PRESSURE_OUT_OF_RANGE, freshDialPressure, spentDialPressure ); - - // Move to the idle state - state = BAL_CHAMBER_STATE_IDLE; - } - else - { - // Move to next state when pressure is in range. - state = BAL_CHAMBER_STATE1_FILL_START; - } + // Move to the idle state + state = BAL_CHAMBER_STATE_IDLE; } else { - // Allow to proceed next state even though pressure is not stabilized. + // Move to next state when pressure is in range. state = BAL_CHAMBER_STATE1_FILL_START; } } else { - // For Balancing Chamber Switch Only command, change state as per the switching period - if ( currentBalChamberSwitchingCounter >= balChamberSwitchingPeriod ) - { - state = BAL_CHAMBER_STATE1_FILL_START; - } + // Allow to proceed next state even though pressure is not stabilized. + state = BAL_CHAMBER_STATE1_FILL_START; } - - // Trigger pressure drop alarm after first cycle of balancing chamber switching complete - if ( FALSE == isFirstCycleBCSwitchingCompleted ) + } + else + { // For Balancing Chamber Switch Only command, change state as per the switching period + if ( currentBalChamberSwitchingCounter >= balChamberSwitchingPeriod ) { - isFirstCycleBCSwitchingCompleted = TRUE; + state = BAL_CHAMBER_STATE1_FILL_START; } + } // Clear the switching only on request flag. balanceChamberSwitchingOnlyOnRequested = FALSE; @@ -1147,10 +1265,6 @@ { requiredRiseCount = 1U; } - else if ( riseDeltaPsi < SPENT_FILL_SLOPE_SPENT_PRESSURE_LOW_PSIG ) - { - requiredRiseCount = ( qdMlpm <= SPENT_FILL_COMPLETE_QD_TWO_HIT_MAX_MLPM ) ? SPENT_FILL_SLOPE_MAX_RISE_HITS : 1U; - } else { requiredRiseCount = 2U; @@ -1208,68 +1322,81 @@ U32 minD48Speed = initialD48PumpSpeed - ( initialD48PumpSpeed * D48_SPEED_RANGE_LIMIT ); U32 maxD48Speed = initialD48PumpSpeed + ( initialD48PumpSpeed * D48_SPEED_RANGE_LIMIT ); U32 d48SpeedPostRangeCheck = 0; - F32 qdMlpm = getTDDialysateFlowrate(); + F32 qdMlpm = getBalChamberActiveDialysateFlowrate(); BOOL result = FALSE; F32 spentFillCompletePresPsig; BOOL isSpentFillCompleteDetected = FALSE; - // Diener 2000 pump: spent fill complete pressure use legacy 25 PSI. Other pump: depends on Qd. - if ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DIENER_2000_PUMP ) ) + if ( TRUE == isBalChamberTimeBasedSwitching() ) { - spentFillCompletePresPsig = SPENT_FILL_COMPLETE_PRES; + if ( currentBalChamberSwitchingCounter >= balChamberValveClosePeriod ) + { + isSpentFillComplete = TRUE; + } } else { - if ( qdMlpm <= QD_THRESHOLD_HIGH_MLPM ) + // Diener 2000 pump: spent fill complete pressure use legacy 25 PSI. Other pump: depends on Qd. + if ( TRUE == getTestConfigStatus( TEST_CONFIG_DD_ENABLE_DIENER_2000_PUMP ) ) { - spentFillCompletePresPsig = SPENT_FILL_COMPLETE_PRES_QD_MID_PSIG; + spentFillCompletePresPsig = SPENT_FILL_COMPLETE_PRES; } else { - spentFillCompletePresPsig = SPENT_FILL_COMPLETE_PRES_QD_HIGH_PSIG; + if ( qdMlpm <= QD_THRESHOLD_HIGH_MLPM ) + { + spentFillCompletePresPsig = SPENT_FILL_COMPLETE_PRES_QD_MID_PSIG; + } + else + { + spentFillCompletePresPsig = SPENT_FILL_COMPLETE_PRES_QD_HIGH_PSIG; + } } - } - isSpentFillCompleteDetected = isSpentFillCompleteByPressure( spentDialPressure, qdMlpm, spentFillCompletePresPsig ); + isSpentFillCompleteDetected = isSpentFillCompleteByPressure( spentDialPressure, qdMlpm, spentFillCompletePresPsig ); - if ( ( TRUE == isSpentFillCompleteDetected ) && ( isSpentFillComplete != TRUE ) ) - { - // Check spent fill time against the balancing chamber closing period - if ( balChamberValveClosePeriod > 0 ) + if ( ( TRUE == isSpentFillCompleteDetected ) && ( isSpentFillComplete != TRUE ) ) { - diffSpentFillCompleteCount = balChamberValveClosePeriod - currentBalChamberFillCounter; - absDiffSpentFillCount = abs(diffSpentFillCompleteCount); - adjustedSpeed = ( ( (F32)absDiffSpentFillCount / (F32)balChamberValveClosePeriod ) * spentDialPumpSpeed ) * D48_SPEED_ADJUST_FACTOR; + // Check spent fill time against the balancing chamber closing period + if ( balChamberValveClosePeriod > 0 ) + { + diffSpentFillCompleteCount = balChamberValveClosePeriod - currentBalChamberFillCounter; + absDiffSpentFillCount = abs(diffSpentFillCompleteCount); + adjustedSpeed = ( ( (F32)absDiffSpentFillCount / (F32)balChamberValveClosePeriod ) * spentDialPumpSpeed ) * D48_SPEED_ADJUST_FACTOR; - // Skip D48 trim in deadband: diff 0..-2 counts (0..100 ms at 50 ms/task); positive band only (larger negative may mean under-fill). - result = ( ( diffSpentFillCompleteCount <= 0 ) && ( diffSpentFillCompleteCount >= -2 ) ) ? TRUE : FALSE; + // Skip D48 trim in deadband: diff 0..-2 counts (0..100 ms at 50 ms/task); positive band only (larger negative may mean under-fill). + result = ( ( diffSpentFillCompleteCount <= 0 ) && ( diffSpentFillCompleteCount >= -2 ) ) ? TRUE : FALSE; - if ( FALSE == result ) - { - if ( diffSpentFillCompleteCount < SPENT_DIFF_COUNT_ZERO ) + if ( FALSE == result ) { - // Increase the D48 pump speed - spentDialPumpSpeed += adjustedSpeed; - } - else - { - //Decrease the D48 pump speed - spentDialPumpSpeed -= adjustedSpeed; - } + if ( diffSpentFillCompleteCount < SPENT_DIFF_COUNT_ZERO ) + { + // Increase the D48 pump speed + spentDialPumpSpeed += adjustedSpeed; + } + else + { + //Decrease the D48 pump speed + spentDialPumpSpeed -= adjustedSpeed; + } - d48SpeedPostRangeCheck = RANGE( spentDialPumpSpeed, minD48Speed, maxD48Speed ); + d48SpeedPostRangeCheck = RANGE( spentDialPumpSpeed, minD48Speed, maxD48Speed ); - // Do not turn on the pump if the switching only is enabled in the standby mode. - if ( FALSE == getBalChamberSwitchingOnlyStatus() ) - { - // Update the D48 pump speed - setD48PumpSpeedForBCFill( d48SpeedPostRangeCheck ); + // Do not turn on the pump if the switching only is enabled in the standby mode. + if ( FALSE == getBalChamberSwitchingOnlyStatus() ) + { + // Update the D48 pump speed + setD48PumpSpeedForBCFill( d48SpeedPostRangeCheck ); + } } } - } - //Update spent fill is complete - isSpentFillComplete = TRUE; + //Update spent fill is complete + if ( currentBalChamberSwitchingCounter >= balChamberValveClosePeriod ) + { + isSpentFillComplete = TRUE; + } + } } } @@ -1321,6 +1448,19 @@ /*********************************************************************//** * @brief + * The getBalChamberActiveDialysateFlowrate function gets the Qd currently + * applied to balancing chamber timing. + * @details \b Inputs: lastTdDialysateFlowrate + * @details \b Outputs: none + * @return active balancing chamber dialysate flow rate + *************************************************************************/ +F32 getBalChamberActiveDialysateFlowrate( void ) +{ + return lastTdDialysateFlowrate; +} + +/*********************************************************************//** + * @brief * The setBalChamberSwitchingOnlyStatus function sets the balancing chamber * switching only On/Off status. * @details \b Inputs: balanceChamberSwitchingOnly @@ -1399,8 +1539,7 @@ data.balChamberExecState = (U32)balChamberExecState; data.balChamberSWState = (U32)balChamberSWState; data.balChamberSWFreq = getBalChamberSwitchingFreq(); - data.balChamberSwPeriod = getD48PumpSpeedForBCFill(); - data.isBalChamberFillInProgress = isSpentFillComplete; + data.isSpentFillComplete = isSpentFillComplete; data.currentBalChamberSwitchingCounter = currentBalChamberFillCounter; data.isPressureStabilizedDuringFill = isPressureStabilizedDuringFill; data.balChamberSWOnlyState = balanceChamberSwitchingOnly;