Index: firmware/App/Services/StateServices/FluidBolus.c =================================================================== diff -u -r069dee3a2428d3c6bb281db1844a372ae6e2063a -re4eceb1ac6e8091d10e0fab49d2d5a9c5e5f1c67 --- firmware/App/Services/StateServices/FluidBolus.c (.../FluidBolus.c) (revision 069dee3a2428d3c6bb281db1844a372ae6e2063a) +++ firmware/App/Services/StateServices/FluidBolus.c (.../FluidBolus.c) (revision e4eceb1ac6e8091d10e0fab49d2d5a9c5e5f1c67) @@ -8,7 +8,7 @@ * @file FluidBolus.c * * @author (last) Praneeth Bunne -* @date (last) 02-Jul-2026 +* @date (last) 04-Jun-2026 * * @author (original) Praneeth Bunne * @date (original) 04-Jun-2026 @@ -20,15 +20,13 @@ #include "DDInterface.h" #include "FluidBolus.h" #include "Messaging.h" -#include "ModeStandby.h" -#include "ModeTreatment.h" #include "Pressures.h" +#include "ModeTreatment.h" #include "StateTxBloodPrime.h" #include "StateTxDialysis.h" #include "StateTxPaused.h" #include "TaskGeneral.h" #include "Timers.h" -#include "TubeSetInstall.h" #include "TxParams.h" #include "Valves.h" @@ -39,32 +37,48 @@ // ********** private definitions ********** +#define NUM_OF_FLUID_BOLUS_PERMITTED_ALARMS 6U ///< Number of permitted alarms for fluid bolus from paused state. + static const U32 FLUID_BOLUS_DATA_PUB_INTERVAL = ( MS_PER_SECOND / TASK_GENERAL_INTERVAL); ///< Saline bolus data broadcast interval (ms/task time) count. // ********** private data ********** static FLUID_BOLUS_STATE_T currentFluidBolusState; ///< Current fluid bolus state -static FLUID_TYPE_T fluidType; ///< Fluid type for bolus +static FLUID_BOLUS_MEDIUM_T currentFluidBolusMedium; ///< Current fluid bolus medium static U32 fluidBolusBroadCastTimerCtr; ///< Broadcast Timer counter static BOOL fluidBolusStartRequested; ///< Flag indicates a fluid bolus start has been requested by user. static BOOL fluidBolusAbortRequested; ///< Flag indicates a fluid bolus abort has been requested by user. static BOOL pubBolusPermitted; ///< Flag indicates a bolus is permitted or not to UI (used to broadcast). -static BOOL newAlarmIndicateStop; ///< Flag indicating alarm with stop property is triggered. static U32 targetBloodFlowMLPM; ///< Blood pump flow rate (mL/min) to use for current bolus delivery. static F32 totalFluidVolumeDelivered_mL; ///< Volume (mL) in total of fluid delivered so far (cumulative for all boluses including current one). static F32 bolusFluidVolumeDelivered_mL; ///< Volume (mL) of current bolus delivered so far (calculated from measured blood flow rate). static U32 bolusVolumeLastUpdateTimeStamp; ///< Time stamp for last bolus volume update. +// TODO: replace TRUE with getDialysateGoodToDeliverStatus() when we are ready +BOOL isDialysateGoodToDeliver = TRUE; ///< Flag indicating dialysate is good to deliver to the patient. + +///< Permitted alarm list-bolus allowed from paused state +static const ALARM_ID_T FLUID_BOLUS_PERMITTED_PAUSED_ALARMS[ NUM_OF_FLUID_BOLUS_PERMITTED_ALARMS ] = +{ + ALARM_ID_TD_TREATMENT_STOPPED_BY_USER, + ALARM_ID_TD_ARTERIAL_PRESSURE_HIGH, + ALARM_ID_TD_ARTERIAL_PRESSURE_LOW, + ALARM_ID_TD_VENOUS_PRESSURE_HIGH, + ALARM_ID_TD_TMP_PRESSURE_HIGH, + ALARM_ID_TD_TMP_PRESSURE_LOW +}; + // ********** private function prototypes ********** static void updateFluidBolusVolumeDelivered( void ); static void completeBolusToCumulative( void ); static FLUID_BOLUS_STATE_T handleFluidBolusIdleState( void ); static FLUID_BOLUS_STATE_T handleFluidBolusWait4Pumps2Stop( void ); -static FLUID_BOLUS_STATE_T handleFluidBolusInProgressState( void ); +static FLUID_BOLUS_STATE_T handleFluidBolusSalineInProgressState( void ); +static FLUID_BOLUS_STATE_T handleFluidBolusSubstituteInProgressState( void ); /*********************************************************************//** * @brief @@ -75,14 +89,13 @@ * @details \b Outputs: currentFluidBolusState, currentFluidBolusMedium, * fluidBolusBroadCastTimerCtr, targetBloodFlowMLPM, totalFluidVolumeDelivered_mL, * bolusFluidVolumeDelivered_mL, fluidBolusStartRequested, - * fluidBolusAbortRequested, bolusVolumeLastUpdateTimeStamp, pubBolusPermitted, - * newAlarmIndicateStop + * fluidBolusAbortRequested, bolusVolumeLastUpdateTimeStamp, pubBolusPermitted * @return none *************************************************************************/ void initFluidBolus( void ) { currentFluidBolusState = FLUID_BOLUS_IDLE_STATE; - fluidType = getFluidType(); + currentFluidBolusMedium = getFluidBolusMedium(); fluidBolusBroadCastTimerCtr = FLUID_BOLUS_DATA_PUB_INTERVAL - 10; // setup to stagger publish from other broadcasters targetBloodFlowMLPM = 0; totalFluidVolumeDelivered_mL = 0.0F; @@ -91,24 +104,23 @@ fluidBolusAbortRequested = FALSE; bolusVolumeLastUpdateTimeStamp = getMSTimerCount(); pubBolusPermitted = TRUE; - newAlarmIndicateStop = FALSE; } /*********************************************************************//** * @brief * The execFluidBolus function executes the fluid bolus state machine. - * @details \b Inputs: currentFluidBolusState, newAlarmIndicateStop - * @details \b Outputs: currentFluidBolusState, newAlarmIndicateStop + * @details \b Inputs: currentFluidBolusState + * @details \b Outputs: currentFluidBolusState * @return none *************************************************************************/ void execFluidBolus( void ) { FLUID_BOLUS_STATE_T priorState = currentFluidBolusState; // If alarm fires while bolus is active, abort the bolus - if ( ( TRUE == newAlarmIndicateStop ) && ( TRUE == isFluidBolusActive() ) ) + if ( ( TRUE == doesAlarmStatusIndicateStop() ) && ( TRUE == isFluidBolusActive() ) && + ( TREATMENT_PAUSED_STATE != getTreatmentState() ) ) { - newAlarmIndicateStop = FALSE; signalAbortFluidBolus(); } @@ -122,10 +134,14 @@ currentFluidBolusState = handleFluidBolusWait4Pumps2Stop(); break; - case FLUID_BOLUS_IN_PROGRESS_STATE: - currentFluidBolusState = handleFluidBolusInProgressState(); + case FLUID_BOLUS_SALINE_IN_PROGRESS_STATE: + currentFluidBolusState = handleFluidBolusSalineInProgressState(); break; + case FLUID_BOLUS_SUBSITUTE_IN_PROGRESS_STATE: + currentFluidBolusState = handleFluidBolusSubstituteInProgressState(); + break; + default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_FLUID_BOLUS_STATE, currentFluidBolusState ) currentFluidBolusState = FLUID_BOLUS_IDLE_STATE; @@ -156,6 +172,29 @@ /*********************************************************************//** * @brief + * The getFluidBolusMedium function determines the fluid bolus medium based + * on the current treatment modality and online fluid configuration. + * Substitution fluid applies to HDF treatment, or HD treatment with online + * fluid enabled. Saline applies to standard HD without online fluid. + * @details \b Inputs: none + * @details \b Outputs: none + * @return FLUID_BOLUS_MEDIUM_SUBSTITUTE for HDF or HD-online, FLUID_BOLUS_MEDIUM_SALINE otherwise. + *************************************************************************/ +FLUID_BOLUS_MEDIUM_T getFluidBolusMedium( void ) +{ + FLUID_BOLUS_MEDIUM_T medium = FLUID_BOLUS_MEDIUM_SALINE; + U32 modality = getTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_MODALITY ); + + if ( ( modality == TREATMENT_MODALITY_HDF ) ) // || ( modality == TREATMENT_MODALITY_HD ) && ( check online fluid enabled ) + { + medium = FLUID_BOLUS_MEDIUM_SUBSTITUTE; + } + + return medium; +} + +/*********************************************************************//** + * @brief * The isFluidBolusActive function determines whether a fluid bolus is * currently being delivered. * @details \b Inputs: currentFluidBolusState @@ -259,8 +298,7 @@ * The handleFluidBolusIdleState function handles the idle state of the * fluid bolus state machine. * @details \b Inputs: fluidBolusStartRequested - * @details \b Outputs: fluidBolusStartRequested, bolusVolumeLastUpdateTimeStamp, - * newAlarmIndicateStop + * @details \b Outputs: fluidBolusStartRequested, bolusVolumeLastUpdateTimeStamp * @return next fluid bolus state *************************************************************************/ static FLUID_BOLUS_STATE_T handleFluidBolusIdleState( void ) @@ -271,8 +309,6 @@ if ( TRUE == fluidBolusStartRequested ) { fluidBolusStartRequested = FALSE; - // Clear when alarm is triggered and can be set when new alarm is triggered. - newAlarmIndicateStop = FALSE; // Stop blood pump signalBloodPumpHardStop(); // Stop substitution pump @@ -313,67 +349,93 @@ setValvePosition( H1_VALV, VALVE_POSITION_C_CLOSE ); setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); - if ( FLUID_TYPE_SALINE == fluidType ) + if ( FLUID_BOLUS_MEDIUM_SALINE == currentFluidBolusMedium ) { setBloodPumpTargetFlowRate( targetBloodFlowMLPM, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + state = FLUID_BOLUS_SALINE_IN_PROGRESS_STATE; } - else + else if ( TRUE == isDialysateGoodToDeliver ) { // set D92 flow rate cmdSubstitutionRate( (F32)targetBloodFlowMLPM ); + state = FLUID_BOLUS_SUBSITUTE_IN_PROGRESS_STATE; } - - state = FLUID_BOLUS_IN_PROGRESS_STATE; + else + { + state = FLUID_BOLUS_IDLE_STATE; + } } return state; } /*********************************************************************//** * @brief - * The handleFluidBolusInProgressState function handles the in-progress - * state of the fluid bolus state machine. Integrates delivered volume from - * measured flow rate every tick. For saline medium, fires an alarm if the - * saline bag is empty. For substitution medium, aborts if dialysate is no - * longer good to deliver. Completes the bolus when the target volume is - * reached or an abort is requested. - * @details \b Alarms: ALARM_ID_TD_EMPTY_SALINE_BAG if saline bag is empty or - * saline line is clamped. - * @details \b Inputs: bolusFluidVolumeDelivered_mL, fluidBolusAbortRequested, - * currentFluidBolusMedium + * The handleFluidBolusSalineInProgressState function handles the saline + * in-progress state of the fluid bolus state machine. Integrates delivered + * volume from measured blood flow, fires an alarm if the saline bag is empty, + * and completes the bolus when the target volume is reached or an abort is requested. + * @details \b Inputs: bolusFluidVolumeDelivered_mL, fluidBolusAbortRequested * @details \b Outputs: fluidBolusAbortRequested * @return next fluid bolus state *************************************************************************/ -static FLUID_BOLUS_STATE_T handleFluidBolusInProgressState( void ) +static FLUID_BOLUS_STATE_T handleFluidBolusSalineInProgressState( void ) { - FLUID_BOLUS_STATE_T state = FLUID_BOLUS_IN_PROGRESS_STATE; - F32 bolusTargetVolume = (F32)getTreatmentParameterU32( TREATMENT_PARAM_FLUID_BOLUS_VOLUME ); + FLUID_BOLUS_STATE_T state = FLUID_BOLUS_SALINE_IN_PROGRESS_STATE; + F32 bolusTargetVolume = (F32)getTreatmentParameterU32( TREATMENT_PARAM_FLUID_BOLUS_VOLUME ); updateFluidBolusVolumeDelivered(); - if ( FLUID_TYPE_SALINE == fluidType ) + // Check for empty saline bag per arterial line pressure + if ( TRUE == isSalineBagEmpty() ) { - // Check for empty saline bag per arterial line pressure - if ( TRUE == isSalineBagEmpty() ) - { - SET_ALARM_WITH_1_F32_DATA( ALARM_ID_TD_EMPTY_SALINE_BAG, getFilteredArterialPressure() ); - state = FLUID_BOLUS_IDLE_STATE; - } + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_TD_EMPTY_SALINE_BAG, getFilteredArterialPressure() ); + state = FLUID_BOLUS_IDLE_STATE; } + // Determine if bolus is complete or stopped by user + else if ( ( bolusFluidVolumeDelivered_mL >= bolusTargetVolume ) || ( TRUE == fluidBolusAbortRequested ) ) + { + fluidBolusAbortRequested = FALSE; + state = FLUID_BOLUS_IDLE_STATE; + } else { - if ( FALSE == getDialysateGoodToDeliverStatus() ) - { - state = FLUID_BOLUS_IDLE_STATE; - } + // No action required } - // Bolus ended or target volume reached or aborted, stop delivery and record volume. - if ( ( bolusFluidVolumeDelivered_mL >= bolusTargetVolume ) || ( TRUE == fluidBolusAbortRequested ) || - ( FLUID_BOLUS_IDLE_STATE == state ) ) + // Bolus ended, stop blood pump and record delivered volume + if ( state != FLUID_BOLUS_SALINE_IN_PROGRESS_STATE ) { - fluidBolusAbortRequested = FALSE; signalBloodPumpHardStop(); + completeBolusToCumulative(); + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleFluidBolusSubstituteInProgressState function handles the + * substitute in-progress state state of the fluid bolus state machine. + * Integrates delivered volume from measured blood flow. Monitors dialysate + * readiness every tick, aborts if dialysate is no longer good to deliver. + * Completes the bolus when target is reached or an abort is requested. + * @details \b Inputs: bolusFluidVolumeDelivered_mL, fluidBolusAbortRequested + * @details \b Outputs: fluidBolusAbortRequested, + * totalFluidVolumeDelivered_mL via completeBolusToCumulative() + * @return next fluid bolus state. + *************************************************************************/ +static FLUID_BOLUS_STATE_T handleFluidBolusSubstituteInProgressState( void ) +{ + FLUID_BOLUS_STATE_T state = FLUID_BOLUS_SUBSITUTE_IN_PROGRESS_STATE; + F32 bolusTargetVolume = (F32)getTreatmentParameterU32( TREATMENT_PARAM_FLUID_BOLUS_VOLUME ); + + updateFluidBolusVolumeDelivered(); + + // Check for dialysate, target volume delivered or abort from user + if ( ( FALSE == isDialysateGoodToDeliver ) || ( bolusFluidVolumeDelivered_mL >= bolusTargetVolume ) || ( TRUE == fluidBolusAbortRequested ) ) + { + fluidBolusAbortRequested = FALSE; cmdSubstitutionRate( 0.0F ); completeBolusToCumulative(); state = FLUID_BOLUS_IDLE_STATE; @@ -414,6 +476,58 @@ } /*********************************************************************//** + * @brief + * The isBolusAllowedByActiveAlarms function checks whether all + * currently active alarms permit a fluid bolus from the paused state. + * For saline medium, a non-permitted alarm blocks the bolus only if it is + * TD source. For substitute medium, any non-permitted alarm blocks the bolus. + * @details \b Inputs: FLUID_BOLUS_PERMITTED_PAUSED_ALARMS[], currentFluidBolusMedium + * @details \b Outputs: none + * @return TRUE if all active alarms permit the bolus, FALSE otherwise. + *************************************************************************/ +BOOL isBolusAllowedByActiveAlarms( void ) +{ + U32 alarm; + U32 permittedIndex; + BOOL permitted = FALSE; + BOOL result = TRUE; + + for ( alarm = 0; alarm < NUM_OF_ALARM_IDS; alarm++ ) + { + if ( TRUE == isAlarmActive( alarm ) ) + { + for ( permittedIndex = 0; permittedIndex < NUM_OF_FLUID_BOLUS_PERMITTED_ALARMS; permittedIndex++ ) + { + if ( FLUID_BOLUS_PERMITTED_PAUSED_ALARMS[ permittedIndex ] == alarm ) + { + permitted = TRUE; + break; + } + } + + if ( FALSE == permitted ) + { + if ( FLUID_BOLUS_MEDIUM_SALINE == currentFluidBolusMedium ) + { + if ( ALM_SRC_TD == getAlarmSource( alarm ) ) + { + result = FALSE; + break; + } + } + else + { + result = FALSE; + break; + } + } + } + } + + return result; +} + +/*********************************************************************//** * @brief * The handleFluidBolusRequest function handles the UI fluid bolus request. * @details \b Message \b Sent: MSG_ID_TD_FLUID_BOLUS_RESPONSE @@ -439,6 +553,18 @@ // TD Fluid Bolus Additional Bolus Prevention rejReason = REQUEST_REJECT_REASON_FLUID_BOLUS_IN_PROGRESS; } + else if ( TREATMENT_PAUSED_STATE == getTreatmentState() ) + { + if ( ( TRUE == isBolusAllowedByActiveAlarms() ) ) + { + result = signalPauseTreatFluidBolusRequest(); + rejReason = ( result == TRUE ) ? REQUEST_REJECT_REASON_NONE : REQUEST_REJECT_REASON_INVALID_TREATMENT_SUB_STATE; + } + else + { + rejReason = REQUEST_REJECT_REASON_ALARM_IS_ACTIVE; + } + } else { // Route to whichever calling state is currently active @@ -454,11 +580,6 @@ rejReason = ( result == TRUE ) ? REQUEST_REJECT_REASON_NONE : REQUEST_REJECT_REASON_INVALID_TREATMENT_SUB_STATE; break; - case TREATMENT_PAUSED_STATE: - result = signalPauseTreatFluidBolusRequest(); - rejReason = ( result == TRUE ) ? REQUEST_REJECT_REASON_NONE : REQUEST_REJECT_REASON_INVALID_TREATMENT_SUB_STATE; - break; - // case TREATMENT_HDF_STATE: // result = signalHdfFluidBolusRequest(); // rejReason = ( result == TRUE ) ? REQUEST_REJECT_REASON_NONE : REQUEST_REJECT_REASON_INVALID_TREATMENT_SUB_STATE; @@ -511,17 +632,4 @@ return result; } -/*********************************************************************//** - * @brief - * The signalNewStopAlarmActivated function is called by the alarm system - * when an alarm with the stop property is activated. - * @details \b Inputs: none - * @details \b Outputs: newAlarmIndicateStop - * @return none - *************************************************************************/ -void signalNewStopAlarmActivated( void ) -{ - newAlarmIndicateStop = TRUE; -} - /**@}*/