Index: firmware/App/Modes/ModePreTreat.c =================================================================== diff -u -ra12f3ce494cd2a18aee31659c17d8a619fc70f7d -r58d4338b0c453ac49d3fdc4ba391b47949406b98 --- firmware/App/Modes/ModePreTreat.c (.../ModePreTreat.c) (revision a12f3ce494cd2a18aee31659c17d8a619fc70f7d) +++ firmware/App/Modes/ModePreTreat.c (.../ModePreTreat.c) (revision 58d4338b0c453ac49d3fdc4ba391b47949406b98) @@ -15,13 +15,15 @@ * ***************************************************************************/ +#include "BloodFlow.h" #include "Buttons.h" #include "DDInterface.h" #include "ModePreTreat.h" #include "OperationModes.h" #include "Timers.h" #include "TubeSetInstall.h" #include "TxParams.h" +#include "Valves.h" /** * @addtogroup TDPreTreatmentMode @@ -75,6 +77,13 @@ initPreTreatmentMode(); initTubeSetInstall(); + // TODO: remove after implementing blood pump homing in some state + homeBloodPump(); + + // TODO: remove these homeValve() when SELF_TEST_NO_CART_STATE is implemented (homing valves is handled over there). + homeValve(H1_VALV, FALSE, FALSE); + homeValve(H19_VALV, FALSE, FALSE); + return (U32)currentPreTreatmentState; } Index: firmware/App/Modes/ModeTreatment.c =================================================================== diff -u -ra12f3ce494cd2a18aee31659c17d8a619fc70f7d -r58d4338b0c453ac49d3fdc4ba391b47949406b98 --- firmware/App/Modes/ModeTreatment.c (.../ModeTreatment.c) (revision a12f3ce494cd2a18aee31659c17d8a619fc70f7d) +++ firmware/App/Modes/ModeTreatment.c (.../ModeTreatment.c) (revision 58d4338b0c453ac49d3fdc4ba391b47949406b98) @@ -20,6 +20,7 @@ #include "Bubbles.h" #include "Buttons.h" #include "DDInterface.h" +#include "FluidBolus.h" #include "Messaging.h" #include "ModeService.h" #include "ModeTreatment.h" @@ -53,8 +54,6 @@ #define TREATMENT_SETTINGS_RANGES_PUB_INTERVAL ( ( 60 * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ) /// Ultrafiltration data broadcast interval (ms/task time) count. #define ULTRAFILTRATION_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) -/// Saline bolus data broadcast interval (ms/task time) count. // TODO - move to StateTxSalineBolus.c when implemented -#define SALINE_BOLUS_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) /// Macro to calculate the elapsed treatment time in seconds. #define CALC_ELAPSED_TREAT_TIME_IN_SECS() ( treatmentTimeMS / MS_PER_SECOND ) @@ -81,7 +80,6 @@ static U32 treatmentStateBroadcastTimerCtr; ///< Treatment state data broadcast timer counter used to schedule when to transmit data. static U32 treatmentParamsRangesBroadcastTimerCtr; ///< Treatment parameter ranges broadcast timer counter used to schedule when to transmit updated ranges. static U32 ultrafiltrationBroadcastTimerCtr; ///< Ultrafiltration data broadcast timer counter used to schedule when to transmit data. -static U32 salineBolusBroadcastTimerCtr; ///< Saline bolus data broadcast timer counter used to schedule when to transmit data. /// Interval (in task intervals) at which to publish alarm status to CAN bus. static OVERRIDE_U32_T treatmentTimePublishInterval; @@ -104,15 +102,13 @@ static void broadcastTreatmentSettingsRanges( void ); static void broadcastTreatmentPeriodicData(); -static void publishSalineBolusData( void ); // TODO - move to StateTxSalineBolus.c when implemented static void publishUltrafiltrationData( void ); static U32 getMinTreatmentTimeInMinutes( void ); static TREATMENT_STATE_T handleTreatmentStartState( void ); static TREATMENT_STATE_T handleTreatmentBloodPrimeState( void ); static TREATMENT_STATE_T handleTreatmentDialysisState( void ); static TREATMENT_STATE_T handleTreatmentIsoUFState( void ); static TREATMENT_STATE_T handleTreatmentDialysatePausedState( void ); -static TREATMENT_STATE_T handleTreatmentSalineBolusState( void ); static TREATMENT_STATE_T handleTreatmentPausedState( void ); static TREATMENT_STATE_T handleTreatmentRinsebackState( void ); static TREATMENT_STATE_T handleTreatmentRecircState( void ); @@ -155,7 +151,6 @@ treatmentStateBroadcastTimerCtr = TREATMENT_STATE_DATA_PUB_INTERVAL; // So we send state data immediately when we begin treatment mode treatmentParamsRangesBroadcastTimerCtr = TREATMENT_SETTINGS_RANGES_PUB_INTERVAL; // So we send ranges immediately when we begin treatment mode ultrafiltrationBroadcastTimerCtr = ULTRAFILTRATION_DATA_PUB_INTERVAL; // So we send ultrafiltration immediately when we begin treatment mode - salineBolusBroadcastTimerCtr = SALINE_BOLUS_DATA_PUB_INTERVAL; // So we send saline bolus data immediately when we begin treatment mode presTreatmentTimeSecs = 0; presUFVolumeMl = 0.0F; @@ -193,6 +188,7 @@ // initRinseback(); // initTreatmentRecirc(); // initTreatmentEnd(); + initFluidBolus(); // Started the treatment - set the start time in epoch // setTxLastStartTimeEpoch( getRTCTimestamp() ); @@ -467,10 +463,6 @@ // currentTreatmentState = handleTreatmentIsoUFState(); // break; // -// case TREATMENT_SALINE_BOLUS_STATE: -// currentTreatmentState = handleTreatmentSalineBolusState(); -// break; -// // case TREATMENT_DIALYSATE_PAUSED_STATE: // currentTreatmentState = handleTreatmentDialysatePausedState(); // break; @@ -507,12 +499,13 @@ broadcastTreatmentPeriodicData(); // Publish ultrafiltration data at set interval (whether we are performing UF or not) publishUltrafiltrationData(); - // Publish saline bolus data at set interval (whether we are delivering one or not) - publishSalineBolusData(); // Manage air trap control execAirTrapMonitorTreatment(); + // Call fluid bolus + execFluidBolus(); + return currentTreatmentState; } @@ -627,10 +620,15 @@ U32 msSinceLast = calcTimeBetween( lastTreatmentTimeStamp, newTime ); DIALYSIS_STATE_T dialysisState = getDialysisState(); - // Update treatment time - treatmentTimeMS += msSinceLast; + // Always update timestamp to avoid time jump on bolus exit lastTreatmentTimeStamp = newTime; + // Only update treatment time when fluid bolus is not active + if ( FALSE == isFluidBolusActive() ) + { + treatmentTimeMS += msSinceLast; + } + // End treatment if treatment duration has been reached if ( CALC_ELAPSED_TREAT_TIME_IN_SECS() >= presTreatmentTimeSecs ) { @@ -915,7 +913,7 @@ payload.rinsebackState = 0; // getCurrentRinsebackState(); payload.txRecircState = 0; // getCurrentTreatmentRecircState(); payload.txEndState = 0; // getCurrentTreatmentEndState(); - payload.txSalBolusState = 0; // + payload.txFluidBolusState = getCurrentFluidBolusState(); payload.txHepState = 0; // broadcastData( MSG_ID_TD_TREATMENT_STATE_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)(&payload), sizeof( TREATMENT_STATE_DATA_T ) ); } @@ -1120,30 +1118,6 @@ /*********************************************************************//** * @brief - * The publishSalineBolusData function publishes the saline bolus data - * at the set time interval. - * @details \b Inputs: none - * @details \b Outputs: none - * @param dialysisState next dialysis state - * @return next saline bolus state - *************************************************************************/ -static void publishSalineBolusData( void ) -{ - if ( ++salineBolusBroadcastTimerCtr >= SALINE_BOLUS_DATA_PUB_INTERVAL ) - { - SALINE_BOLUS_DATA_PAYLOAD_T data; - - data.tgtSalineVolumeMl = getTreatmentParameterU32( TREATMENT_PARAM_FLUID_BOLUS_VOLUME ); - data.cumSalineVolumeMl = 0.0F; // TODO - data.bolSalineVolumeMl = 0.0F; // TODO - - sendMessage( MSG_ID_TD_SALINE_BOLUS_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)(&data), sizeof( SALINE_BOLUS_DATA_PAYLOAD_T ) ); - salineBolusBroadcastTimerCtr = 0; - } -} - -/*********************************************************************//** - * @brief * The publishUltrafiltrationData function publishes the ultrafiltration data * at the set time interval. * @details \b Inputs: none Index: firmware/App/Modes/ModeTreatment.h =================================================================== diff -u -ra12f3ce494cd2a18aee31659c17d8a619fc70f7d -r58d4338b0c453ac49d3fdc4ba391b47949406b98 --- firmware/App/Modes/ModeTreatment.h (.../ModeTreatment.h) (revision a12f3ce494cd2a18aee31659c17d8a619fc70f7d) +++ firmware/App/Modes/ModeTreatment.h (.../ModeTreatment.h) (revision 58d4338b0c453ac49d3fdc4ba391b47949406b98) @@ -55,7 +55,7 @@ U32 rinsebackState; ///< Rinse back state. U32 txRecircState; ///< Treatment recirc state. U32 txEndState; ///< Treatment end state. - U32 txSalBolusState; ///< Saline bolus state. + U32 txFluidBolusState; ///< Fluid bolus state. U32 txHepState; ///< Heparin state. } TREATMENT_STATE_DATA_T; Index: firmware/App/Monitors/Pressures.h =================================================================== diff -u -r6f876554db45a19590eaf2122ef47e33f7a7d69b -r58d4338b0c453ac49d3fdc4ba391b47949406b98 --- firmware/App/Monitors/Pressures.h (.../Pressures.h) (revision 6f876554db45a19590eaf2122ef47e33f7a7d69b) +++ firmware/App/Monitors/Pressures.h (.../Pressures.h) (revision 58d4338b0c453ac49d3fdc4ba391b47949406b98) @@ -7,8 +7,8 @@ * * @file Pressures.h * -* @author (last) Varshini Nagabooshanam -* @date (last) 13-Mar-2026 +* @author (last) Vijay Pamula +* @date (last) 28-Apr-2026 * * @author (original) Sean Nash * @date (original) 24-Sep-2024 Index: firmware/App/Services/AlarmMgmtSWFaults.h =================================================================== diff -u -r70f10c77f4f9a48d51030c00504d520fe25d0ba9 -r58d4338b0c453ac49d3fdc4ba391b47949406b98 --- firmware/App/Services/AlarmMgmtSWFaults.h (.../AlarmMgmtSWFaults.h) (revision 70f10c77f4f9a48d51030c00504d520fe25d0ba9) +++ firmware/App/Services/AlarmMgmtSWFaults.h (.../AlarmMgmtSWFaults.h) (revision 58d4338b0c453ac49d3fdc4ba391b47949406b98) @@ -14,21 +14,21 @@ * @date (original) 01-Aug-2024 * ***************************************************************************/ - -#ifndef __ALARM_MGMT_SW_FAULTS_H__ -#define __ALARM_MGMT_SW_FAULTS_H__ - -/** - * @addtogroup AlarmManagement - * @{ - */ - -// ********** public definitions ********** - -/// Listing of specific software faults for logging purposes. -typedef enum -{ - SW_FAULT_ID_NONE = 0, + +#ifndef __ALARM_MGMT_SW_FAULTS_H__ +#define __ALARM_MGMT_SW_FAULTS_H__ + +/** + * @addtogroup AlarmManagement + * @{ + */ + +// ********** public definitions ********** + +/// Listing of specific software faults for logging purposes. +typedef enum +{ + SW_FAULT_ID_NONE = 0, SW_FAULT_ID_ALARM_LAMP_INVALID_PATTERN_REQUESTED = 1, SW_FAULT_ID_ALARM_LAMP_INVALID_SELF_TEST_STATE = 2, SW_FAULT_ID_MODE_INIT_POST_INVALID_POST_STATE = 3, @@ -195,10 +195,10 @@ SW_FAULT_ID_MODE_POST_TREATMENT_INVALID_STATE = 164, SW_FAULT_ID_MODE_POST_TREATMENT_INVALID_STATE1 = 165, SW_FAULT_ID_MODE_POST_TX_AUTO_EJECT_INVALID_STATE = 166, - SW_FAULT_ID_INVALID_MESSAGE_PAYLOAD_LENGTH = 167, + SW_FAULT_ID_INVALID_FLUID_BOLUS_STATE = 167, NUM_OF_SW_FAULT_IDS -} SW_FAULT_ID_T; - +} SW_FAULT_ID_T; + /**@}*/ -#endif +#endif Index: firmware/App/Services/Messaging.c =================================================================== diff -u -r1abc0349c736a70fb56db6895947abfbba0eee22 -r58d4338b0c453ac49d3fdc4ba391b47949406b98 --- firmware/App/Services/Messaging.c (.../Messaging.c) (revision 1abc0349c736a70fb56db6895947abfbba0eee22) +++ firmware/App/Services/Messaging.c (.../Messaging.c) (revision 58d4338b0c453ac49d3fdc4ba391b47949406b98) @@ -7,8 +7,8 @@ * * @file Messaging.c * -* @author (last) Raghu Kallala -* @date (last) 30-Apr-2026 +* @author (last) Vijay Pamula +* @date (last) 29-May-2026 * * @author (original) Sean Nash * @date (original) 01-Aug-2024 @@ -41,8 +41,11 @@ #include "StateTxDialysis.h" #include "StateTxPaused.h" #include "Switches.h" +#include "SyringePump.h" #include "SystemCommTD.h" #include "Temperatures.h" +#include "TubeSetAutoEject.h" +#include "TubeSetInstall.h" #include "TxParams.h" #include "Utilities.h" #include "Valve3Way.h" @@ -111,12 +114,25 @@ { MSG_ID_DD_GEN_DIALYSATE_MODE_DATA, &setDialysateData }, { MSG_ID_DD_PRESSURES_DATA, &setDialysatePressure }, { MSG_ID_UI_TREATMENT_PARAMS_TO_VALIDATE, &validateAndSetTreatmentParameters }, + { MSG_ID_UI_TREATMENT_UF_VOLUME_VALIDATE_REQUEST, &validateAndSetUFVolume }, + { MSG_ID_UI_ULTRAFILTRATION_CHANGE_CONFIRM_REQUEST, &signalUserConfirmationOfUFVolume }, + { MSG_ID_UI_DURATION_VALIDATE_REQUEST, &validateAndSetTreatmentDuration }, + { MSG_ID_UI_DURATION_CONFIRM_REQUEST, &signalUserConfirmationOfTreatmentDuration }, + { MSG_ID_UI_BOLUS_VOLUME_CHANGE_REQUEST, &validateAndSetBolusVolume }, { MSG_ID_UI_INITIATE_TREATMENT_WORKFLOW, &signalUserInitiateTreatment }, { MSG_ID_UI_UF_PAUSE_RESUME_REQUEST, &signalPauseResumeUF }, { MSG_ID_TESTER_LOGIN_REQUEST, &handleTesterLogInRequest }, { MSG_ID_UI_PRESSURE_LIMIT_WIDEN_REQUEST, &pressureLimitHandleWidenRequest }, + { MSG_ID_UI_PRESSURE_LIMITS_CHANGE_REQUEST, &pressureLimitHandleChangeRequest }, + { MSG_ID_UI_TREATMENT_SET_POINTS_CHANGE_REQUEST, &validateAndSetTreatmentSetPoints }, + { MSG_ID_UI_TREATMENT_SET_POINT_BLOOD_FLOW_CHANGE_REQUEST, &validateAndSetBloodFlowRate }, + { MSG_ID_UI_TREATMENT_SET_POINT_DIALYSATE_FLOW_CHANGE_REQUEST, &validateAndSetDialysateFlowRate }, + { MSG_ID_UI_TREATMENT_SET_POINT_DIALYSATE_TEMP_CHANGE_REQUEST, &validateAndSetDialysateTemperature }, { MSG_ID_UI_BLOOD_PRIME_CMD_REQUEST, &bloodPrimeHandleCmdRequest }, { MSG_ID_UI_TREATMENT_SET_POINT_BLOOD_FLOW_CHANGE_REQUEST, &bloodPrimeHandleBloodFlowChangeRequest }, + { MSG_ID_UI_ADJUST_DISPOSABLES_CONFIRM_REQUEST, &handleAutoLoadRequest }, + { MSG_ID_UI_ADJUST_DISPOSABLES_REMOVAL_CONFIRM_REQUEST, &handleAutoEjectRequest }, + { MSG_ID_FFU_SIGNAL_TD_UPDATE_AVAILABLE, &handleUpdateAvailable }, { MSG_ID_UI_FLUID_BOLUS_REQUEST, &handleFluidBolusRequest }, { MSG_ID_TD_SOFTWARE_RESET_REQUEST, &testTDSoftwareResetRequest }, { MSG_ID_TD_BUBBLE_OVERRIDE_REQUEST, &testBubbleDetectOverride }, @@ -187,8 +203,22 @@ { MSG_ID_TD_BLOOD_PRIME_VOLUME_OVERRIDE, &testBloodPrimeVolumeOverride }, { MSG_ID_TD_BLOOD_PRIME_PUBLISH_INTERVAL_OVERRIDE_REQUEST, &testBloodPrimePublishIntervalOverride }, { MSG_ID_TD_ENABLE_VENOUS_BUBBLE_ALARM, &testEnableVenousBubbleAlarm }, + { MSG_ID_TD_SYRINGE_PUMP_OPERATION_REQUEST, &testSyringePumpOperationRequest }, + { MSG_ID_HD_SYRINGE_PUMP_PUBLISH_INTERVAL_OVERRIDE, &testSyringePumpDataPublishIntervalOverride }, + { MSG_ID_TD_SYRINGE_PUMP_RATE_OVERRIDE_REQUEST, &testSyringePumpMeasuredRateOverride }, + { MSG_ID_TD_SYRINGE_PUMP_FORCE_OVERRIDE_REQUEST, &testSyringePumpMeasuredForceOverride }, + { MSG_ID_TD_SYRINGE_PUMP_HOME_OVERRIDE_REQUEST, &testSyringePumpMeasuredHomeOverride }, + { MSG_ID_TD_SYRINGE_PUMP_POSITION_OVERRIDE_REQUEST, &testSyringePumpMeasuredPositionOverride }, + { MSG_ID_TD_SYRINGE_PUMP_VOLUME_OVERRIDE_REQUEST, &testSyringePumpMeasuredVolumeOverride }, + { MSG_ID_TD_SYRINGE_PUMP_STATUS_OVERRIDE_REQUEST, &testSyringePumpStatusOverride }, + { MSG_ID_TD_SYRINGE_PUMP_ENCODER_STATUS_OVERRIDE_REQUEST, &testSyringePumpEncoderStatusOverride }, + { MSG_ID_TD_SYRINGE_PUMP_ADC_DAC_STATUS_OVERRIDE_REQUEST, &testSyringePumpADCandDACStatusOverride }, + { MSG_ID_TD_SYRINGE_PUMP_ADC_READ_COUNTER_OVERRIDE_REQUEST, &testSyringePumpADCReadCounterOverride }, + { MSG_ID_TD_HEPARIN_BOLUS_TARGET_RATE_OVERRIDE_REQUEST, &testHeparinBolusTargetRateOverride }, + { MSG_ID_TD_SYRINGE_PUMP_FORCE_SENSOR_CALIBRATION_REQUEST, &testCalibrateForceSensor }, }; +/// Number of entries in the message handling function lookup table. #define NUM_OF_FUNCTION_HANDLERS (sizeof(MSG_FUNCTION_HANDLER_LOOKUP) / sizeof(MSG_HANDLER_LOOKUP_T)) // ********** private data ********** @@ -600,6 +630,18 @@ /*********************************************************************//** * @brief + * The resetMicroControllerUnit function resets the MCU. + * @details \b Inputs: none + * @details \b Outputs: none + * @return none + *************************************************************************/ +void resetMicroControllerUnit( void ) +{ + setSystemREG1_SYSECR( (0x2) << 14 ); // Reset processor +} + +/*********************************************************************//** + * @brief * The sendTestAckResponseMsg function constructs a simple response message * for a handled test message and queues it for transmit on the TD=>PC CAN * channel. @@ -668,6 +710,32 @@ /*********************************************************************//** * @brief + * The handleUpdateAvailable function handles a request from the + * Firmware FPGA Updater (FFU) to reset the TD firmware processor. + * @note If reset is successful, this function will not return. + * @details \b Inputs: none + * @details \b Outputs: TD processor soft reset if request is valid + * @param message Pointer to the reset request message + * @return FALSE if reset command rejected + *************************************************************************/ +BOOL handleUpdateAvailable( MESSAGE_T *message ) +{ + BOOL result = FALSE; + + // Verify payload length + if ( 0 == message->hdr.payloadLen ) + { // S/w reset of processor + result = TRUE; // Reset will prevent this from getting transmitted though + requestNewOperationMode( MODE_UPDT ); + } + + sendAckResponseMsg( (MSG_ID_T)message->hdr.msgID, COMM_BUFFER_OUT_CAN_TD_2_UI, result ); + + return result; +} + +/*********************************************************************//** + * @brief * The testTDSoftwareResetRequest function handles a request to reset the * TD firmware processor. * @note If reset is successful, this function will not return. @@ -684,7 +752,7 @@ if ( 0 == message->hdr.payloadLen ) { // S/w reset of processor result = TRUE; // Reset will prevent this from getting transmitted though - setSystemREG1_SYSECR( (0x2) << 14 ); // Reset processor + resetMicroControllerUnit(); } sendTestAckResponseMsg( (MSG_ID_T)message->hdr.msgID, result ); Index: firmware/App/Services/Messaging.h =================================================================== diff -u -r1abc0349c736a70fb56db6895947abfbba0eee22 -r58d4338b0c453ac49d3fdc4ba391b47949406b98 --- firmware/App/Services/Messaging.h (.../Messaging.h) (revision 1abc0349c736a70fb56db6895947abfbba0eee22) +++ firmware/App/Services/Messaging.h (.../Messaging.h) (revision 58d4338b0c453ac49d3fdc4ba391b47949406b98) @@ -7,22 +7,22 @@ * * @file Messaging.h * -* @author (last) Shyam Kumar Mathamala -* @date (last) 25-Aug-2025 +* @author (last) Vijay Pamula +* @date (last) 29-May-2026 * * @author (original) Sean Nash * @date (original) 01-Aug-2024 * ***************************************************************************/ - -#ifndef __MESSAGING_H__ -#define __MESSAGING_H__ +#ifndef __MESSAGING_H__ +#define __MESSAGING_H__ + #include "TDCommon.h" #include "TDDefs.h" #include "MessageSupport.h" #include "MsgQueues.h" - + /** * @defgroup Messaging Messaging * @brief The system communication messages unit provides helper functions @@ -35,7 +35,7 @@ // ********** public definitions ********** #define ACK_REQUIRED TRUE ///< Macro for functions that want to know if an outgoing message requires acknowledgement from receiver. -#define ACK_NOT_REQUIRED FALSE ///< Macro for functions that want to know if an outgoing message requires acknowledgement from receiver. +#define ACK_NOT_REQUIRED FALSE ///< Macro for functions that want to know if an outgoing message requires acknowledgement from receiver. #pragma pack(push, 1) /// Payload record structure for ACK response. @@ -58,47 +58,6 @@ U32 rejectionReason; ///< Rejection reason if not accepted. } UI_RESPONSE_PAYLOAD_T; -/// Payload record structure for UF change response. -typedef struct -{ - BOOL accepted; ///< Accepted/Rejected - U32 rejectionReason; ///< Rejection reason if not accepted. - F32 ufVolume; ///< UF Volume in mL. - U32 durationInMinutes; ///< new Treatment duration in minutes. - S32 timeDiff; ///< Treatment duration difference. - F32 ufRate; ///< new UF rate in mL/min. - F32 rateDiff; ///< difference between new and old UF rates. - F32 oldUFRate; ///< Previous UF Rate in mL/min. -} UF_SETTINGS_CHANGE_RESPONSE_PAYLOAD_T; - -/// Payload record structure for UF change confirmation response. -typedef struct -{ - BOOL accepted; ///< Accepted/Rejected - U32 rejectionReason; ///< Rejection reason if not accepted. - F32 volume; ///< UF Volume in mL. - U32 duration; ///< Treatment duration in minutes - F32 ufRate; ///< UF rate in mL. -} UF_SETTINGS_CONFIRMATION_RESPONSE_PAYLOAD_T; - -/// Payload record structure for treatment duration change response. -typedef struct -{ - BOOL accepted; ///< Accepted/Rejected - U32 rejectionReason; ///< Rejection reason if not accepted. - U32 duration; ///< Treatment duration in minutes - F32 volume; ///< UF Volume in mL. -} TREATMENT_TIME_CHANGE_RESPONSE_PAYLOAD_T; - -/// Payload record structure for blood / dialysate rate change response. -typedef struct -{ - BOOL accepted; ///< Accepted/Rejected - U32 rejectionReason; ///< Rejection reason if not accepted. - U32 bloodRate; ///< new blood flow rate - U32 dialRate; ///< new dialysate flow rate -} BLOOD_DIAL_RATE_CHANGE_RESPONSE_PAYLOAD_T; - /// Payload record structure for treatment parameter broadcast messages. typedef struct { @@ -110,16 +69,9 @@ U32 maxDialRate; ///< Maximum dialysate flow rate (in mL/min) } TREATMENT_PARAM_BROADCAST_PAYLOAD_T; -/// Payload record structure for Fluid Bolus response. +/// Institutional record structure containing treatment parameter range limits. typedef struct { - U32 accepted; ///< Accepted/Rejected - U32 rejectionReason; ///< Rejection reason if not accepted. - U32 targetVolumeMl; ///< Target fluid bolus volume in mL. -} FLUID_BOLUS_RESPONSE_PAYLOAD_T; - -typedef struct -{ U32 minBloodFlowMLPM; ///< Min blood flow in mL/min. U32 maxBloodFlowMLPM; ///< Max blood flow in mL/min. U32 minDialysateFlowMLPM; ///< Min dialysate flow in mL/min. @@ -146,13 +98,21 @@ F32 maxHeparinBolusVolumeML; ///< Max heparin bolus volume in mL. U32 enableChemicalDisinfect; ///< Enable/disable chemical disinfect. } HD_INSTITUTIONAL_LOCAL_RECORD_T; - -// ********** public function prototypes ********** -// Serialize message +/// Payload record structure for Fluid Bolus response. +typedef struct +{ + U32 accepted; ///< Accepted/Rejected + U32 rejectionReason; ///< Rejection reason if not accepted. + U32 targetVolumeMl; ///< Target fluid bolus volume in mL. +} FLUID_BOLUS_RESPONSE_PAYLOAD_T; + +// ********** public function prototypes ********** + +// Serialize message U32 serializeMessage( MESSAGE_T msg, COMM_BUFFER_T buffer, BOOL ackReq ); -// ACK MSG +// ACK MSG BOOL sendACKMsg( MESSAGE_T *message ); // Handle version request message @@ -162,10 +122,12 @@ BOOL isTestingActivated( void ); void setTesterStatusToLoggedOut( void ); +void resetMicroControllerUnit( void ); void handleIncomingMessage( MESSAGE_T *message ); BOOL handleUICheckIn( MESSAGE_T *message ); BOOL handleTesterLogInRequest( MESSAGE_T *message ); +BOOL handleUpdateAvailable( MESSAGE_T *message ); BOOL testTDSoftwareResetRequest( MESSAGE_T *message ); @@ -179,4 +141,4 @@ /**@}*/ -#endif +#endif Index: firmware/App/Services/StateServices/FluidBolus.c =================================================================== diff -u --- firmware/App/Services/StateServices/FluidBolus.c (revision 0) +++ firmware/App/Services/StateServices/FluidBolus.c (revision 58d4338b0c453ac49d3fdc4ba391b47949406b98) @@ -0,0 +1,635 @@ +/************************************************************************** +* +* Copyright (c) 2025-2026 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 FluidBolus.c +* +* @author (last) Praneeth Bunne +* @date (last) 01-May-2026 +* +* @author (original) Praneeth Bunne +* @date (original) 01-May-2026 +* +***************************************************************************/ + +#include "AlarmMgmtTD.h" +#include "BloodFlow.h" +#include "DDInterface.h" +#include "FluidBolus.h" +#include "Messaging.h" +#include "Pressures.h" +#include "ModeTreatment.h" +#include "StateTxBloodPrime.h" +#include "StateTxDialysis.h" +#include "StateTxPaused.h" +#include "TaskGeneral.h" +#include "Timers.h" +#include "TxParams.h" +#include "Valves.h" + +/** + * @addtogroup FluidBolus + * @{ + */ + +// ********** 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_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 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 handleFluidBolusSalineInProgressState( void ); +static FLUID_BOLUS_STATE_T handleFluidBolusSubstituteInProgressState( void ); + +/*********************************************************************//** + * @brief + * The initFluidBolus function initializes the Fluid Bolus module. + * Calling this function resets all bolus state and cumulative volume, + * and therefore should only be called when a new treatment is due to begin. + * @details \b Inputs: none + * @details \b Outputs: currentFluidBolusState, currentFluidBolusMedium, + * fluidBolusBroadCastTimerCtr, targetBloodFlowMLPM, totalFluidVolumeDelivered_mL, + * bolusFluidVolumeDelivered_mL, fluidBolusStartRequested, + * fluidBolusAbortRequested, bolusVolumeLastUpdateTimeStamp, pubBolusPermitted + * @return none + *************************************************************************/ +void initFluidBolus( void ) +{ + currentFluidBolusState = FLUID_BOLUS_IDLE_STATE; + currentFluidBolusMedium = getFluidBolusMedium(); + fluidBolusBroadCastTimerCtr = FLUID_BOLUS_DATA_PUB_INTERVAL - 10; // setup to stagger publish from other broadcasters + targetBloodFlowMLPM = 0; + totalFluidVolumeDelivered_mL = 0.0F; + bolusFluidVolumeDelivered_mL = 0.0F; + fluidBolusStartRequested = FALSE; + fluidBolusAbortRequested = FALSE; + bolusVolumeLastUpdateTimeStamp = getMSTimerCount(); + pubBolusPermitted = TRUE; +} + +/*********************************************************************//** + * @brief + * The execFluidBolus function executes the fluid bolus state machine. + * @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 == doesAlarmStatusIndicateStop() ) && ( TRUE == isFluidBolusActive() ) && + ( TREATMENT_PAUSED_STATE != getTreatmentState() ) ) + { + signalAbortFluidBolus(); + } + + switch ( currentFluidBolusState ) + { + case FLUID_BOLUS_IDLE_STATE: + currentFluidBolusState = handleFluidBolusIdleState(); + break; + + case FLUID_BOLUS_WAIT_FOR_PUMPS_STOP_STATE: + currentFluidBolusState = handleFluidBolusWait4Pumps2Stop(); + break; + + 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; + break; + } + + if ( priorState != currentFluidBolusState ) + { + SEND_EVENT_WITH_2_U32_DATA( TD_EVENT_SUB_STATE_CHANGE, priorState, currentFluidBolusState ); + } + + // Publish fluid bolus data at set interval (whether we are delivering one or not) + publishFluidBolusData(); +} + +/*********************************************************************//** + * @brief + * The getCurrentFluidBolusState function returns the current state of the + * Fluid Bolus service. + * @details \b Inputs: currentFluidBolusState + * @details \b Outputs: none + * @return currentFluidBolusState + *************************************************************************/ +FLUID_BOLUS_STATE_T getCurrentFluidBolusState( void ) +{ + return currentFluidBolusState; +} + +/*********************************************************************//** + * @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 + * @details \b Outputs: none + * @return TRUE if bolus state is not IDLE, FALSE otherwise. + *************************************************************************/ +BOOL isFluidBolusActive( void ) +{ + return ( FLUID_BOLUS_IDLE_STATE != currentFluidBolusState ) ? TRUE : FALSE; +} + +/*********************************************************************//** + * @brief + * The getTotalFluidBolusVolumeDelivered function gets the current total + * fluid volume delivered. + * @details \b Inputs: totalFluidVolumeDelivered + * @details \b Outputs: none + * @return totalFluidVolumeDelivered + *************************************************************************/ +F32 getTotalFluidBolusVolumeDelivered( void ) +{ + return totalFluidVolumeDelivered_mL; +} + +/*********************************************************************//** + * @brief + * The signalStartFluidBolus function is called by a parent treatment state + * to initiate a fluid bolus. + * @details \b Inputs: currentFluidBolusState + * @details \b Outputs: fluidBolusStartRequested, targetBloodFlowMLPM + * @return none + *************************************************************************/ +void signalStartFluidBolus( U32 flowRate ) +{ + if ( ( FLUID_BOLUS_IDLE_STATE == currentFluidBolusState ) && ( FALSE == fluidBolusStartRequested ) ) + { + fluidBolusStartRequested = TRUE; + targetBloodFlowMLPM = flowRate; + } +} + +/*********************************************************************//** + * @brief + * The signalAbortFluidBolus function signals an external abort to the + * fluid bolus service. Called by user-stop or the global alarm-stop hook. + * @details \b Inputs: currentFluidBolusState + * @details \b Outputs: fluidBolusAbortRequested + * @return none + *************************************************************************/ +void signalAbortFluidBolus( void ) +{ + if ( FLUID_BOLUS_IDLE_STATE != currentFluidBolusState ) + { + fluidBolusAbortRequested = TRUE; + } +} + +/*********************************************************************//** + * @brief + * The setBolusPermitted function sets the fluid bolus permitted flag, + * which is broadcast to the UI to enable or disable the fluid bolus + * button on screen. + * @details \b Inputs: none + * @details \b Outputs: pubBolusPermitted + * @param permitted TRUE if a fluid bolus is currently permitted, FALSE otherwise. + * @return none + *************************************************************************/ +void setBolusPermitted( BOOL permitted ) +{ + pubBolusPermitted = permitted; +} + +/*********************************************************************//** + * @brief + * The publishFluidBolusData function publishes the fluid bolus data + * at the set time interval. + * @details \b Inputs: fluidBolusBroadCastTimerCtr, currentFluidBolusState, + * bolusFluidVolumeDelivered_mL, totalFluidVolumeDelivered_mL, pubBolusPermitted + * @details \b Outputs: fluidBolusBroadCastTimerCtr + * @return none + *************************************************************************/ +void publishFluidBolusData( void ) +{ + if ( ++fluidBolusBroadCastTimerCtr >= FLUID_BOLUS_DATA_PUB_INTERVAL ) + { + FLUID_BOLUS_DATA_PAYLOAD_T data; + + data.tgtFluidVolumeMl = getTreatmentParameterU32( TREATMENT_PARAM_FLUID_BOLUS_VOLUME ); + data.bolFluidVolumeMl = bolusFluidVolumeDelivered_mL; + data.cumFluidVolumeMl = totalFluidVolumeDelivered_mL; + data.bolusPermitted = pubBolusPermitted; + + broadcastData( MSG_ID_TD_FLUID_BOLUS_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)&data, sizeof( FLUID_BOLUS_DATA_PAYLOAD_T ) ); + + fluidBolusBroadCastTimerCtr = 0; + } +} + +/*********************************************************************//** + * @brief + * The handleFluidBolusIdleState function handles the idle state of the + * fluid bolus state machine. + * @details \b Inputs: fluidBolusStartRequested + * @details \b Outputs: fluidBolusStartRequested, bolusVolumeLastUpdateTimeStamp + * @return next fluid bolus state + *************************************************************************/ +static FLUID_BOLUS_STATE_T handleFluidBolusIdleState( void ) +{ + FLUID_BOLUS_STATE_T state = FLUID_BOLUS_IDLE_STATE; + + // Handle fluid bolus start request + if ( TRUE == fluidBolusStartRequested ) + { + fluidBolusStartRequested = FALSE; + // Stop blood pump + signalBloodPumpHardStop(); + // Stop substitution pump + cmdSubstitutionRate( 0.0F ); + bolusVolumeLastUpdateTimeStamp = getMSTimerCount(); + state = FLUID_BOLUS_WAIT_FOR_PUMPS_STOP_STATE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleFluidBolusWait4Pumps2Stop function handles the wait for pumps + * to stop state of the fluid bolus state machine. + * @details \b Inputs: fluidBolusAbortRequested, currentFluidBolusMedium + * @details \b Outputs: fluidBolusAbortRequested, bolusFluidVolumeDelivered_mL, + * bolusVolumeLastUpdateTimeStamp + * @return next fluid bolus state + *************************************************************************/ +static FLUID_BOLUS_STATE_T handleFluidBolusWait4Pumps2Stop( void ) +{ + FLUID_BOLUS_STATE_T state = FLUID_BOLUS_WAIT_FOR_PUMPS_STOP_STATE; + + if( TRUE == fluidBolusAbortRequested ) + { + fluidBolusAbortRequested = FALSE; + signalBloodPumpHardStop(); + state = FLUID_BOLUS_IDLE_STATE; + } + else if ( FALSE == isBloodPumpRunning() ) // TODO: Write condition to check D92 when HDF is implemented + { + // Reset bolus data before we start + bolusFluidVolumeDelivered_mL = 0.0; + bolusVolumeLastUpdateTimeStamp = getMSTimerCount(); + + // Set arterial & venous valves + setValvePosition( H1_VALV, VALVE_POSITION_C_CLOSE ); + setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); + + if ( FLUID_BOLUS_MEDIUM_SALINE == currentFluidBolusMedium ) + { + setBloodPumpTargetFlowRate( targetBloodFlowMLPM, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + state = FLUID_BOLUS_SALINE_IN_PROGRESS_STATE; + } + else if ( TRUE == isDialysateGoodToDeliver ) + { + // set D92 flow rate + cmdSubstitutionRate( (F32)targetBloodFlowMLPM ); + state = FLUID_BOLUS_SUBSITUTE_IN_PROGRESS_STATE; + } + else + { + state = FLUID_BOLUS_IDLE_STATE; + } + } + + return state; +} + +/*********************************************************************//** + * @brief + * 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 handleFluidBolusSalineInProgressState( void ) +{ + FLUID_BOLUS_STATE_T state = FLUID_BOLUS_SALINE_IN_PROGRESS_STATE; + F32 bolusTargetVolume = (F32)getTreatmentParameterU32( TREATMENT_PARAM_FLUID_BOLUS_VOLUME ); + + updateFluidBolusVolumeDelivered(); + + // 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; + } + // Determine if bolus is complete or stopped by user + else if ( ( bolusFluidVolumeDelivered_mL >= bolusTargetVolume ) || ( TRUE == fluidBolusAbortRequested ) ) + { + fluidBolusAbortRequested = FALSE; + state = FLUID_BOLUS_IDLE_STATE; + } + else + { + // No action required + } + + // Bolus ended, stop blood pump and record delivered volume + if ( state != FLUID_BOLUS_SALINE_IN_PROGRESS_STATE ) + { + 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; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The updateFluidBolusVolumeDelivered function integrates the volume + * delivered for the active bolus. + * @details \b Inputs: bolusVolumeLastUpdateTimeStamp, targetBloodFlowMLPM + * @details \b Outputs: bolusFluidVolumeDelivered_mL, bolusVolumeLastUpdateTimeStamp + * @return none + *************************************************************************/ +static void updateFluidBolusVolumeDelivered( void ) +{ + F32 timeSinceLastVolumeUpdateMin = (F32)calcTimeSince( bolusVolumeLastUpdateTimeStamp ) / (F32)( MS_PER_SECOND * SEC_PER_MIN ); + + bolusFluidVolumeDelivered_mL += (F32)targetBloodFlowMLPM * timeSinceLastVolumeUpdateMin; + bolusVolumeLastUpdateTimeStamp = getMSTimerCount(); +} + +/*********************************************************************//** + * @brief + * The completeBolusToCumulative function rolls the per-bolus delivered + * volume into the cumulative counter and emits a treatment log event. + * @details \b Inputs: bolusFluidVolumeDelivered_mL + * @details \b Outputs: totalFluidVolumeDelivered_mL, bolusFluidVolumeDelivered_mL, + * @return none + *************************************************************************/ +static void completeBolusToCumulative( void ) +{ + totalFluidVolumeDelivered_mL += bolusFluidVolumeDelivered_mL; + SEND_EVENT_WITH_2_F32_DATA( FLUID_BOLUSES_CHANGE_EVENT, bolusFluidVolumeDelivered_mL, totalFluidVolumeDelivered_mL ); + bolusFluidVolumeDelivered_mL = 0.0F; +} + +/*********************************************************************//** + * @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 +* @details \b Inputs: none +* @details \b Outputs: none +* @return TRUE if request is accepted, FALSE if rejected. +*************************************************************************/ +BOOL handleFluidBolusRequest( MESSAGE_T *message ) +{ + BOOL result = FALSE; + U32 cmd = 0; + REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NONE; + FLUID_BOLUS_RESPONSE_PAYLOAD_T response; + + if ( sizeof(U32) == message->hdr.payloadLen ) + { + memcpy( &cmd, message->payload, sizeof(U32) ); + + if ( FLUID_BOLUS_CMD_START == cmd ) + { + if ( TRUE == isFluidBolusActive() ) + { + // 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 + switch ( getTreatmentState() ) + { + case TREATMENT_BLOOD_PRIME_STATE: + result = signalBloodPrimeFluidBolusRequest(); + rejReason = ( result == TRUE ) ? REQUEST_REJECT_REASON_NONE : REQUEST_REJECT_REASON_INVALID_TREATMENT_SUB_STATE; + break; + + case TREATMENT_DIALYSIS_STATE: + result = signalDialysisFluidBolusRequest(); + 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; +// break; +// +// case TREATMENT_ISO_UF_STATE: +// result = signalIsoUFFluidBolusRequest(); +// rejReason = ( result == TRUE ) ? REQUEST_REJECT_REASON_NONE : REQUEST_REJECT_REASON_INVALID_TREATMENT_SUB_STATE; +// break; +// +// case TREATMENT_END_STATE: +// result = signalTreatmentEndFluidBolusRequest(); +// rejReason = ( result == TRUE ) ? REQUEST_REJECT_REASON_NONE : REQUEST_REJECT_REASON_INVALID_TREATMENT_SUB_STATE; +// break; + + default: + rejReason = REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE; + break; + } + } + } + else if ( FLUID_BOLUS_CMD_STOP == cmd ) + { + if ( TRUE == isFluidBolusActive() ) + { + signalAbortFluidBolus(); + result = TRUE; + rejReason = REQUEST_REJECT_REASON_NONE; + } + else + { + rejReason = REQUEST_REJECT_REASON_FLUID_BOLUS_NOT_IN_PROGRESS; + } + } + else + { + rejReason = REQUEST_REJECT_REASON_INVALID_COMMAND; + } + } + else + { + rejReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + } + + response.accepted = result; + response.rejectionReason = rejReason; + response.targetVolumeMl = getTreatmentParameterU32( TREATMENT_PARAM_FLUID_BOLUS_VOLUME ); + sendMessage( MSG_ID_TD_FLUID_BOLUS_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08*)&response, sizeof( FLUID_BOLUS_RESPONSE_PAYLOAD_T ) ); + + return result; +} + +/**@}*/ Index: firmware/App/Services/StateServices/FluidBolus.h =================================================================== diff -u --- firmware/App/Services/StateServices/FluidBolus.h (revision 0) +++ firmware/App/Services/StateServices/FluidBolus.h (revision 58d4338b0c453ac49d3fdc4ba391b47949406b98) @@ -0,0 +1,75 @@ +/************************************************************************** +* +* Copyright (c) 2025-2026 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 FluidBolus.h +* +* @author (last) Praneeth Bunne +* @date (last) 01-May-2026 +* +* @author (original) Praneeth Bunne +* @date (original) 01-May-2026 +* +***************************************************************************/ + +#ifndef __FLUID_BOLUS_H__ +#define __FLUID_BOLUS_H__ + +#include "TDCommon.h" +#include "TDDefs.h" + +/** + * @defgroup FluidBolus FluidBolus + * @brief Fluid Bolus unit provides bolus service to the + * Blood Prime, HD/HDF, ISO UF, Pause, End states of Treatment mode. + * + * @addtogroup FluidBolus + * @{ + */ + +// ********** public definitions ********** + +// Enumeration of fluid bolus medium types +enum Fluid_Bolus_Medium +{ + FLUID_BOLUS_MEDIUM_SALINE = 0, ///< Saline bolus (HD modality). + FLUID_BOLUS_MEDIUM_SUBSTITUTE, ///< Substitution bolus (HDF / HD-online modality). + NUM_OF_FLUID_BOLUS_MEDIUMS ///< Number of fluid bolus medium types. +}; +typedef enum Fluid_Bolus_Medium FLUID_BOLUS_MEDIUM_T; + +#pragma pack(push,1) +/// Payload record structure for a fluid bolus data broadcast message. +typedef struct +{ + U32 tgtFluidVolumeMl; ///< Target Fluid volume in mL. + F32 bolFluidVolumeMl; ///< Bolus Fluid volume in mL. + F32 cumFluidVolumeMl; ///< Cumulative Fluid volume in mL. + BOOL bolusPermitted; ///< True if Bolus is permitted currently. +} FLUID_BOLUS_DATA_PAYLOAD_T; +#pragma pack(pop) + +// ********** public definitions ********** + +void initFluidBolus( void ); +void publishFluidBolusData( void ); +void execFluidBolus( void ); +void setBolusPermitted( BOOL permitted ); + +FLUID_BOLUS_STATE_T getCurrentFluidBolusState( void ); +FLUID_BOLUS_MEDIUM_T getFluidBolusMedium( void ); + +BOOL isFluidBolusActive( void ); +void signalStartFluidBolus( U32 flowRate ); +void signalAbortFluidBolus( void ); +F32 getTotalFluidBolusVolumeDelivered( void ); + +BOOL handleFluidBolusRequest( MESSAGE_T *message ); +BOOL isBolusAllowedByActiveAlarms( void ); + +/**@}*/ + +#endif Index: firmware/App/Services/StateServices/TubeSetAutoEject.c =================================================================== diff -u --- firmware/App/Services/StateServices/TubeSetAutoEject.c (revision 0) +++ firmware/App/Services/StateServices/TubeSetAutoEject.c (revision 58d4338b0c453ac49d3fdc4ba391b47949406b98) @@ -0,0 +1,437 @@ +/************************************************************************** +* +* Copyright (c) 2026-2026 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 TubeSetAutoEject.c +* +* @author (last) Praneeth Bunne +* @date (last) 21-May-2026 +* +* @author (original) Praneeth Bunne +* @date (original) 01-May-2026 +* +***************************************************************************/ + +#include "AlarmMgmtTD.h" +#include "BloodFlow.h" +#include "Ejector.h" +#include "Messaging.h" +#include "OperationModes.h" +#include "PeristalticPump.h" +#include "Switches.h" +#include "TaskGeneral.h" +#include "TubeSetAutoEject.h" + +/** + * @addtogroup TubeSetAutoEject + * @{ + */ + +// ********** private definitions ********** + +///< Auto eject timeout interval (ms/task time) +#define AUTO_EJECT_OPERATION_TIMEOUT_INTERVAL ( ( 10 * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ) + +///< Blood pump flow rate (mL/min) for the ejecting operation +#define AUTO_EJECT_BLOOD_FLOW_RATE_ML_MIN 100U + +// ********** private data ********** + +static BOOL autoEjectReqReceived; ///< Flag indicating that user confirmed to eject tubeset +static U32 autoEjectTimerCounter; ///< Timer counter shared across all auto-eject states +static BOOL bpLastHome; ///< Flag for blood pump last home (for rising-edge) +static U32 bpLeftHomeTimerCounter; ///< Timer counter to measure how long BP has been away from home + +static TUBE_SET_AUTO_EJECT_STATE_T previousAutoEjectState; ///< Previous tube set auto-eject state +static TUBE_SET_AUTO_EJECT_STATE_T currentAutoEjectState; ///< Current tube set auto-eject state + +// ********** private function prototypes ********** + +static TUBE_SET_AUTO_EJECT_STATE_T handleAutoEjectAwaitConfirmState( void ); +static TUBE_SET_AUTO_EJECT_STATE_T handleAutoEjectHomingState( void ); +static TUBE_SET_AUTO_EJECT_STATE_T handleAutoEjectExtendingEjectorState( void ); +static TUBE_SET_AUTO_EJECT_STATE_T handleAutoEjectEjectingState( void ); +static TUBE_SET_AUTO_EJECT_STATE_T handleAutoEjectRetractingEjectorState( void ); +static TUBE_SET_AUTO_EJECT_STATE_T handleAutoEjectBackOffState( void ); +static TUBE_SET_AUTO_EJECT_STATE_T handleAutoEjectCompleteState( void ); + +/*********************************************************************//** +* @brief +* The initTubeSetAutoEject function initializes the Tube Set Eject service. +* @details \b Inputs: none +* @details \b Outputs: Resets the flags, counters and sets the initial +* Tube Set Eject sub-state. +* @return none +**************************************************************************/ +void initTubeSetAutoEject( void ) +{ + previousAutoEjectState = TUBE_SET_AUTO_EJECT_STATE_AWAIT_CONFIRMATION; + currentAutoEjectState = TUBE_SET_AUTO_EJECT_STATE_AWAIT_CONFIRMATION; + autoEjectReqReceived = FALSE; + autoEjectTimerCounter = 0; + bpLastHome = TRUE; + bpLeftHomeTimerCounter = 0; +} + +/*********************************************************************//** + * @brief + * The execTubeSetAutoEject function executes the Tube Set Eject + * state machine. + * @details \b Inputs: currentAutoEjectState, previousAutoEjectState + * @details \b Outputs: currentAutoEjectState, previousAutoEjectState. + * Advances the Tube Set Eject sub-state + * @return none + *************************************************************************/ +void execTubeSetAutoEject( void ) +{ + switch ( currentAutoEjectState ) + { + case TUBE_SET_AUTO_EJECT_STATE_AWAIT_CONFIRMATION: + currentAutoEjectState = handleAutoEjectAwaitConfirmState(); + break; + + case TUBE_SET_AUTO_EJECT_STATE_HOMING: + currentAutoEjectState = handleAutoEjectHomingState(); + break; + + case TUBE_SET_AUTO_EJECT_STATE_EXTENDING_EJECTOR: + currentAutoEjectState = handleAutoEjectExtendingEjectorState(); + break; + + case TUBE_SET_AUTO_EJECT_STATE_EJECTING: + currentAutoEjectState = handleAutoEjectEjectingState(); + break; + + case TUBE_SET_AUTO_EJECT_STATE_RETRACTING_EJECTOR: + currentAutoEjectState = handleAutoEjectRetractingEjectorState(); + break; + + case TUBE_SET_AUTO_EJECT_STATE_BACK_OFF: + currentAutoEjectState = handleAutoEjectBackOffState(); + break; + + case TUBE_SET_AUTO_EJECT_STATE_COMPLETE: + currentAutoEjectState = handleAutoEjectCompleteState(); + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_MODE_POST_TX_AUTO_EJECT_INVALID_STATE, (U32)currentAutoEjectState ); + break; + } + + if ( previousAutoEjectState != currentAutoEjectState ) + { + previousAutoEjectState = currentAutoEjectState; + SEND_EVENT_WITH_2_U32_DATA( TD_EVENT_SUB_STATE_CHANGE, previousAutoEjectState, currentAutoEjectState ); + } +} + +/*********************************************************************//** + * @brief + * The handleAutoEjectAwaitConfirmState function handles the Await confirm + * state of the Tube Set Eject state machine. + * Waits for the user request to auto-eject + * @details \b Inputs: autoEjectReqReceived + * @details \b Outputs: autoEjectReqReceived, autoEjectTimerCounter + * @return next Auto-Eject state + *************************************************************************/ +static TUBE_SET_AUTO_EJECT_STATE_T handleAutoEjectAwaitConfirmState( void ) +{ + TUBE_SET_AUTO_EJECT_STATE_T state = TUBE_SET_AUTO_EJECT_STATE_AWAIT_CONFIRMATION; + + if ( TRUE == autoEjectReqReceived ) + { + homeBloodPump(); + autoEjectTimerCounter = 0; + autoEjectReqReceived = FALSE; + state = TUBE_SET_AUTO_EJECT_STATE_HOMING; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleAutoEjectHomingState function handles the Homing state + * of the Tube Set Eject state machine. + * Homes the blood pump (H4) + * @details \b Inputs: autoEjectTimerCounter + * @details \b Outputs: autoEjectTimerCounter + * @return next Auto-Eject state + *************************************************************************/ +static TUBE_SET_AUTO_EJECT_STATE_T handleAutoEjectHomingState( void ) +{ + TUBE_SET_AUTO_EJECT_STATE_T state = TUBE_SET_AUTO_EJECT_STATE_HOMING; + + if ( ++autoEjectTimerCounter >= AUTO_EJECT_OPERATION_TIMEOUT_INTERVAL) + { + signalBloodPumpHardStop(); + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_AUTO_LOAD_EJECT_BP_TIMEOUT, (U32)isPeristalticPumpHome(), autoEjectTimerCounter ); + } + else if ( TRUE != isPumpHomeInProgress() ) + { + // Door close not required from this point (ejector mechanically pushes the door during eject) + doorClosedRequired(FALSE); + extendEjector(); + autoEjectTimerCounter = 0; + state = TUBE_SET_AUTO_EJECT_STATE_EXTENDING_EJECTOR; + } + else + { + // No action required + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleAutoEjectExtendingEjectorState function handles the + * Extending Ejector state of the Tube Set Eject Service + * state machine. Extends the ejector pin (H5) to engage the tubeset. + * @details \b Inputs: autoEjectTimerCounter + * @details \b Outputs: autoEjectTimerCounter + * @return next Auto-Eject state + *************************************************************************/ +static TUBE_SET_AUTO_EJECT_STATE_T handleAutoEjectExtendingEjectorState( void ) +{ + TUBE_SET_AUTO_EJECT_STATE_T state = TUBE_SET_AUTO_EJECT_STATE_EXTENDING_EJECTOR; + + if ( ++autoEjectTimerCounter >= AUTO_EJECT_OPERATION_TIMEOUT_INTERVAL ) + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_AUTO_EJECT_EJECTOR_TIMEOUT, (U32)getEjectorState(), autoEjectTimerCounter ); + } + else if ( EJECTOR_STATE_EXTENDED == getEjectorState() ) + { + setBloodPumpTargetFlowRate( AUTO_EJECT_BLOOD_FLOW_RATE_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + bpLastHome = TRUE; + autoEjectTimerCounter = 0; + state = TUBE_SET_AUTO_EJECT_STATE_EJECTING; + } + else + { + // No action required + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleAutoEjectEjectingState function handles the Ejecting state + * of the Tube Set Eject state machine. Rotates the blood pump (H4) + * forward at 100 mL/min with the ejector extended to push the tubeset + * out of the rotor. If torque exceeds the limit, stops the pump, triggers + * the High Blood Pump Torque Alarm, and retracts the ejector for back-off + * recovery. Otherwise waits for one full rotation. + * @details \b Inputs: autoEjectTimerCounter, bpLastHome, bpLeftHomeTimerCounter + * @details \b Outputs: autoEjectTimerCounter, bpLastHome, bpLeftHomeTimerCounter + * @return next Auto-Eject state + *************************************************************************/ +static TUBE_SET_AUTO_EJECT_STATE_T handleAutoEjectEjectingState( void ) +{ + TUBE_SET_AUTO_EJECT_STATE_T state = TUBE_SET_AUTO_EJECT_STATE_EJECTING; + BOOL bpCurrentHome = isPeristalticPumpHome(); + F32 bpTorque = getMeasuredBloodPumpTorque(); + + if ( ++autoEjectTimerCounter >= AUTO_EJECT_OPERATION_TIMEOUT_INTERVAL ) + { + signalBloodPumpHardStop(); + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_AUTO_LOAD_EJECT_BP_TIMEOUT, isPeristalticPumpHome(), autoEjectTimerCounter ); + } + // Torque jam check. If measured torque exceeds the limit, the tubing set is likely jammed in the rotor. + else if ( ( ( FALSE == bpLastHome ) && ( ++bpLeftHomeTimerCounter >= AUTO_EJECT_OPERATION_TIMEOUT_INTERVAL ) ) || + ( bpTorque > AUTO_LOAD_EJECT_BP_TORQUE_LIMIT ) ) + { + signalBloodPumpHardStop(); + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_TD_AUTO_LOAD_EJECT_HIGH_BP_TORQUE, bpTorque ); + retractEjector(); + autoEjectTimerCounter = 0; + bpLeftHomeTimerCounter = 0; + state = TUBE_SET_AUTO_EJECT_STATE_RETRACTING_EJECTOR; + } + // BP home position check (rising edge: motor must leave home and return) + else if ( ( TRUE == bpCurrentHome ) && ( FALSE == bpLastHome ) ) + { + signalBloodPumpHardStop(); + retractEjector(); + autoEjectTimerCounter = 0; + bpLeftHomeTimerCounter = 0; + state = TUBE_SET_AUTO_EJECT_STATE_RETRACTING_EJECTOR; + } + else + { + // No action required + } + + if ( TRUE == bpCurrentHome ) + { + bpLeftHomeTimerCounter = 0; + } + + bpLastHome = bpCurrentHome; + + return state; +} + +/*********************************************************************//** + * @brief + * The handleAutoEjectRetractingEjectorState function handles the + * Retracting Ejector state of the Tube Set Eject state machine. + * Retracts the ejector pin (H5) after the tubeset has been ejected. Once + * retracted, command H4 to rotate reverse at 100 mL/min and transitions to + * Back-Off state if the High Blood Pump Torque Alarm is active, or completes + * the Auto-Eject service. + * @details \b Inputs: autoEjectTimerCounter + * @details \b Outputs: autoEjectTimerCounter + * @return next Auto-Eject state + *************************************************************************/ +static TUBE_SET_AUTO_EJECT_STATE_T handleAutoEjectRetractingEjectorState( void ) +{ + TUBE_SET_AUTO_EJECT_STATE_T state = TUBE_SET_AUTO_EJECT_STATE_RETRACTING_EJECTOR; + + if ( ++autoEjectTimerCounter >= AUTO_EJECT_OPERATION_TIMEOUT_INTERVAL ) + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_AUTO_EJECT_EJECTOR_TIMEOUT, (U32)getEjectorState(), autoEjectTimerCounter ); + } + else if ( EJECTOR_STATE_RETRACTED == getEjectorState() ) + { + autoEjectTimerCounter = 0; + + if ( TRUE == isAlarmActive( ALARM_ID_TD_AUTO_LOAD_EJECT_HIGH_BP_TORQUE ) ) + { + setBloodPumpTargetFlowRate( AUTO_EJECT_BLOOD_FLOW_RATE_ML_MIN, MOTOR_DIR_REVERSE, PUMP_CONTROL_MODE_OPEN_LOOP ); + state = TUBE_SET_AUTO_EJECT_STATE_BACK_OFF; + } + else + { + state = TUBE_SET_AUTO_EJECT_STATE_COMPLETE; + } + } + else + { + // No action required + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleAutoEjectBackOffState function handles the Auto-Eject Back-Off + * state of Tube Set Eject state machine. Waits for the blood pump to + * reverse to the home position, then stops the pump and clears the + * High Blood Pump Torque Alarm condition. Once the user acknowledges + * the alarm, transitions to Await Confirmation state to allow retry. + * @details \b Inputs: autoEjectTimerCounter + * @details \b Outputs: autoEjectTimerCounter + * @return next Auto-Eject state + *************************************************************************/ +static TUBE_SET_AUTO_EJECT_STATE_T handleAutoEjectBackOffState( void ) +{ + TUBE_SET_AUTO_EJECT_STATE_T state = TUBE_SET_AUTO_EJECT_STATE_BACK_OFF; + + if ( ++autoEjectTimerCounter >= AUTO_EJECT_OPERATION_TIMEOUT_INTERVAL ) + { + signalBloodPumpHardStop(); + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_AUTO_LOAD_EJECT_BP_TIMEOUT, isPeristalticPumpHome(), autoEjectTimerCounter ); + } + else if ( TRUE == isPeristalticPumpHome() ) + { + signalBloodPumpHardStop(); + clearAlarmTD( ALARM_ID_TD_AUTO_LOAD_EJECT_HIGH_BP_TORQUE ); + + if ( FALSE == isAlarmActive( ALARM_ID_TD_AUTO_LOAD_EJECT_HIGH_BP_TORQUE ) ) + { + autoEjectTimerCounter = 0; + state = TUBE_SET_AUTO_EJECT_STATE_AWAIT_CONFIRMATION; + } + } + else + { + // No action required + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleAutoEjectCompleteState function handles the Complete state of + * the Tube Set Eject state machine. + * @details \b Inputs: none + * @details \b Outputs: none + * @return Auto-Eject complete state + *************************************************************************/ +static TUBE_SET_AUTO_EJECT_STATE_T handleAutoEjectCompleteState( void ) +{ + return TUBE_SET_AUTO_EJECT_STATE_COMPLETE; +} + +/*********************************************************************//** + * @brief + * The isTubeSetEjectComplete function reports whether the eject + * service has finished successfully. + * @details \b Inputs: currentAutoEjectState + * @details \b Outputs: none + * @return TRUE if auto-eject completed and ejector is retracted, + * FALSE otherwise. + *************************************************************************/ +BOOL isTubeSetAutoEjectComplete( void ) +{ + BOOL result = FALSE; + + result = ( TUBE_SET_AUTO_EJECT_STATE_COMPLETE == currentAutoEjectState ? TRUE : FALSE ); + + return result; +} + +/*********************************************************************//** + * @brief + * The handleAutoEjectRequest function handles a UI request to remove the + * disposable tubeset. + * @details \b Message \b Sent: MSG_ID_TD_ADJUST_DISPOSABLES_REMOVAL_CONFIRM_RESPONSE + * @details \b Inputs: currentAutoEjectState + * @details \b Outputs: autoEjectReqReceived + * @param message UI message which includes the user confirmation to + * eject the tubeset + * @return TRUE if confirmation/rejection accepted, FALSE if not + *************************************************************************/ +BOOL handleAutoEjectRequest( MESSAGE_T *message ) +{ + BOOL result = FALSE; + BOOL validMode = FALSE; + TD_OP_MODE_T mode = getCurrentOperationMode(); + UI_RESPONSE_PAYLOAD_T response; + + response.rejectionReason = REQUEST_REJECT_REASON_NONE; + + if ( 0 == message->hdr.payloadLen ) + { + validMode = ( ( ( mode == MODE_POST ) || ( mode == MODE_TREA ) ) ? TRUE : FALSE ); + + if ( ( TRUE != validMode ) || ( TUBE_SET_AUTO_EJECT_STATE_AWAIT_CONFIRMATION != currentAutoEjectState ) ) + { + response.rejectionReason = REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE; + } + else + { + autoEjectReqReceived = TRUE; + result = TRUE; + } + } + else + { + response.rejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + } + + response.accepted = result; + sendMessage( MSG_ID_TD_ADJUST_DISPOSABLES_REMOVAL_CONFIRM_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08*)(&response), sizeof( UI_RESPONSE_PAYLOAD_T ) ); + + return result; +} + +/**@}*/ Index: firmware/App/Services/StateServices/TubeSetInstall.c =================================================================== diff -u --- firmware/App/Services/StateServices/TubeSetInstall.c (revision 0) +++ firmware/App/Services/StateServices/TubeSetInstall.c (revision 58d4338b0c453ac49d3fdc4ba391b47949406b98) @@ -0,0 +1,335 @@ +/************************************************************************** +* +* Copyright (c) 2026-2026 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 TubeSetInstall.c +* +* @author (last) Praneeth Bunne +* @date (last) 21-May-2026 +* +* @author (original) Praneeth Bunne +* @date (original) 01-May-2026 +* +***************************************************************************/ + +#include "AlarmMgmtTD.h" +#include "BloodFlow.h" +#include "Messaging.h" +#include "OperationModes.h" +#include "PeristalticPump.h" +#include "Switches.h" +#include "TaskGeneral.h" +#include "TDDefs.h" +#include "Timers.h" +#include "TubeSetInstall.h" + +/** + * @addtogroup TubeSetInstall + * @{ + */ + +// ********** private definitions ********** + +///< BP timeout interval (ms/task time) +#define BLOOD_PUMP_TIMEOUT_INTERVAL ( ( 10 * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ) + +///< Blood pump flow rate (mL/min) for Auto-Load operation +#define AUTO_LOAD_BLOOD_FLOW_RATE_ML_MIN 100U + +// ********** private data ********** + +static BOOL confirmTubesetPlaced; ///< Flag indicating user has confirmed tubeset is placed +static U32 bloodPumpTimerCounter; ///< Blood Pump timer counter +static BOOL bpLastHome; ///< Flag for blood pump last home (for rising-edge) +static U32 bpLeftHomeTimerCounter; ///< Timer counter to measure how long BP has been away from home + +static TUBE_SET_INSTALL_STATE_T previousInstallState; ///< Previous tube set install sub-state +static TUBE_SET_INSTALL_STATE_T currentInstallState; ///< Current tubing set install sub-state + +// ********** private function prototypes ********** + +static TUBE_SET_INSTALL_STATE_T handleAwaitTubesetConfirmationState( void ); ///< Handle Await Tubset Install Confirmation sub-state +static TUBE_SET_INSTALL_STATE_T handleAwaitBPDoorCloseState( void ); ///< Handle Await Blood Pump Door Close sub-state. +static TUBE_SET_INSTALL_STATE_T handleAutoLoadState( void ); ///< Handle Auto-Load sub-state +static TUBE_SET_INSTALL_STATE_T handleAutoLoadBackOffState( void ); ///< Handle Auto-Load Back-off sub-state +static TUBE_SET_INSTALL_STATE_T handleInstallCompleteState( void ); ///< Handle Install complete state + +/*********************************************************************//** +* @brief +* The initTubeSetInstall function initializes the Tube Set Install service. +* @details \b Inputs: none +* @details \b Outputs: Resets the flags, counters and sets the initial +* Tube Set Install sub-state. +* @return none +**************************************************************************/ +void initTubeSetInstall( void ) +{ + previousInstallState = TUBE_SET_INSTALL_STATE_AWAIT_TUBE_SET_CONFIRMATION; + currentInstallState = TUBE_SET_INSTALL_STATE_AWAIT_TUBE_SET_CONFIRMATION; + confirmTubesetPlaced = FALSE; + bloodPumpTimerCounter = 0; + bpLastHome = TRUE; + bpLeftHomeTimerCounter = 0; +} + +/*********************************************************************//** + * @brief + * The execTubeSetInstall function executes the Tube Set Install + * state machine. + * @details \b Inputs: currentInstallState, previousInstallState + * @details \b Outputs: currentInstallState, previousInstallState. + * Advances the Tube Set Install sub-state + * @return none + *************************************************************************/ +void execTubeSetInstall( void ) +{ + switch( currentInstallState ) + { + case TUBE_SET_INSTALL_STATE_AWAIT_TUBE_SET_CONFIRMATION: + currentInstallState = handleAwaitTubesetConfirmationState(); + break; + + case TUBE_SET_INSTALL_STATE_AWAIT_BP_DOOR_CLOSE: + currentInstallState = handleAwaitBPDoorCloseState(); + break; + + case TUBE_SET_INSTALL_STATE_AUTO_LOAD: + currentInstallState = handleAutoLoadState(); + break; + + case TUBE_SET_INSTALL_STATE_AUTO_LOAD_BACK_OFF: + currentInstallState = handleAutoLoadBackOffState(); + break; + + case TUBE_SET_INSTALL_STATE_COMPLETE: + currentInstallState = handleInstallCompleteState(); + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_MODE_PRE_TX_INSTALL_INVALID_STATE, (U32)currentInstallState ); + break; + } + + if ( previousInstallState != currentInstallState ) + { + previousInstallState = currentInstallState; + SEND_EVENT_WITH_2_U32_DATA( TD_EVENT_SUB_STATE_CHANGE, previousInstallState, currentInstallState ); + } +} + +/*********************************************************************//** + * @brief + * The handleAwaitTubesetConfirmationState function handles the Await + * tubeset confirmation state of Tubing Set install state machine. + * Should wait for the user to confirm the tubeset is placed + * @details \b Inputs: confirmTubsetPlaced + * @details \b Outputs: confirmTubesetPlaced + * @return next Install sub-state + *************************************************************************/ +static TUBE_SET_INSTALL_STATE_T handleAwaitTubesetConfirmationState( void ) +{ + TUBE_SET_INSTALL_STATE_T state = TUBE_SET_INSTALL_STATE_AWAIT_TUBE_SET_CONFIRMATION; + + if ( TRUE == confirmTubesetPlaced ) + { + // Door closed required from Auto-loading onwards, set to false if not required in subsequent states + doorClosedRequired( TRUE ); + confirmTubesetPlaced = FALSE; + state = TUBE_SET_INSTALL_STATE_AWAIT_BP_DOOR_CLOSE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleAwaitBPDoorCloseState function handles the Await Blood Pump + * Door Close state of Tubing Set install state machine. + * Should wait until blood pump door (H9) is closed. + * @details \b Inputs: H9_SWCH state + * @details \b Outputs: none + * @return next Install sub-state + *************************************************************************/ +static TUBE_SET_INSTALL_STATE_T handleAwaitBPDoorCloseState( void ) +{ + TUBE_SET_INSTALL_STATE_T state = TUBE_SET_INSTALL_STATE_AWAIT_BP_DOOR_CLOSE; + + if ( FALSE == isAlarmActive( ALARM_ID_TD_CARTRIDGE_DOOR_OPENED ) ) + { + setBloodPumpTargetFlowRate( AUTO_LOAD_BLOOD_FLOW_RATE_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + bpLastHome = TRUE; + state = TUBE_SET_INSTALL_STATE_AUTO_LOAD; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleAutoLoadState function handles the Auto-Load state of + * Tube Set install state. + * @details \b Inputs: bloodPumpTimerCounter, bpLastHome, bpLeftHomeTimerCounter + * @details \b Outputs: bloodPumpTimerCounter, bpLastHome, bpLeftHomeTimerCounter + * @return next Install sub-state + *************************************************************************/ +static TUBE_SET_INSTALL_STATE_T handleAutoLoadState( void ) +{ + TUBE_SET_INSTALL_STATE_T state = TUBE_SET_INSTALL_STATE_AUTO_LOAD; + F32 bpTorque = getMeasuredBloodPumpTorque(); + BOOL bpCurrentHome = isPeristalticPumpHome(); + + // Timeout check + if ( ++bloodPumpTimerCounter >= BLOOD_PUMP_TIMEOUT_INTERVAL ) + { + signalBloodPumpHardStop(); + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_AUTO_LOAD_EJECT_BP_TIMEOUT, isPeristalticPumpHome(), bloodPumpTimerCounter ); + } + // Torque jam check. If measured torque exceeds the limit, the tubing set is likely jammed in the rotor. + else if ( ( ( FALSE == bpLastHome ) && ( ++bpLeftHomeTimerCounter >= BLOOD_PUMP_TIMEOUT_INTERVAL ) ) || + ( bpTorque > AUTO_LOAD_EJECT_BP_TORQUE_LIMIT ) ) + { + signalBloodPumpHardStop(); + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_TD_AUTO_LOAD_EJECT_HIGH_BP_TORQUE, bpTorque ); + setBloodPumpTargetFlowRate( AUTO_LOAD_BLOOD_FLOW_RATE_ML_MIN, MOTOR_DIR_REVERSE, PUMP_CONTROL_MODE_OPEN_LOOP ); + bloodPumpTimerCounter = 0; + bpLeftHomeTimerCounter = 0; + state = TUBE_SET_INSTALL_STATE_AUTO_LOAD_BACK_OFF; + } + // BP home position check (rising edge: motor must leave home and return) + else if ( ( TRUE == bpCurrentHome ) && ( FALSE == bpLastHome ) ) + { + signalBloodPumpHardStop(); + bloodPumpTimerCounter = 0; + bpLeftHomeTimerCounter = 0; + state = TUBE_SET_INSTALL_STATE_COMPLETE; + } + else + { + // No action required + } + + if ( TRUE == bpCurrentHome ) + { + bpLeftHomeTimerCounter = 0; + } + + bpLastHome = bpCurrentHome; + + return state; +} + +/*********************************************************************//** + * @brief + * The handleAutoLoadBackOffState function handles the Auto-Load Back-Off + * state of Tube Set install state. + * @details \b Inputs: bloodPumpTimerCounter + * @details \b Outputs: bloodPumpTimerCounter + * @return next Install sub-state + *************************************************************************/ +static TUBE_SET_INSTALL_STATE_T handleAutoLoadBackOffState( void ) +{ + TUBE_SET_INSTALL_STATE_T state = TUBE_SET_INSTALL_STATE_AUTO_LOAD_BACK_OFF; + + if ( ++bloodPumpTimerCounter >= BLOOD_PUMP_TIMEOUT_INTERVAL ) + { + signalBloodPumpHardStop(); + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_AUTO_LOAD_EJECT_BP_TIMEOUT, isPeristalticPumpHome(), bloodPumpTimerCounter ); + } + else if ( TRUE == isPeristalticPumpHome() ) + { + signalBloodPumpHardStop(); + clearAlarmTD( ALARM_ID_TD_AUTO_LOAD_EJECT_HIGH_BP_TORQUE ); + + if ( FALSE == isAlarmActive( ALARM_ID_TD_AUTO_LOAD_EJECT_HIGH_BP_TORQUE ) ) + { + bloodPumpTimerCounter = 0; + state = TUBE_SET_INSTALL_STATE_AWAIT_TUBE_SET_CONFIRMATION; + } + } + else + { + // No action required + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleInstallCompleteState function handles the Install Complete + * state of Tube Set install state. + * @details \b Inputs: none + * @details \b Outputs: none + * @return install complete state + *************************************************************************/ +static TUBE_SET_INSTALL_STATE_T handleInstallCompleteState( void ) +{ + return TUBE_SET_INSTALL_STATE_COMPLETE; +} + +/*********************************************************************//** + * @brief + * The isTubeSetInstallComplete function reports whether the install + * service has finished successfully. + * @details \b Inputs: none + * @details \b Outputs: none + * @return TRUE if auto-load completed and pump is at home, FALSE otherwise. + *************************************************************************/ +BOOL isTubeSetInstallComplete( void ) +{ + BOOL result; + + result = ( TUBE_SET_INSTALL_STATE_COMPLETE == currentInstallState ? TRUE : FALSE ); + + return result; +} + +/*********************************************************************//** + * @brief + * The handleAutoLoadRequest function handles a UI request to + * confirm tubeset installation. + * @details \b Message \b Sent: MSG_ID_TD_ADJUST_DISPOSABLES_CONFIRM_RESPONSE + * @details \b Inputs: none + * @details \b Outputs: confirmTubesetPlaced + * @param message UI message which includes the user confirmation of + * tubset installation. + * @return TRUE if confirmation/rejection accepted, FALSE if not + *************************************************************************/ +BOOL handleAutoLoadRequest( MESSAGE_T *message ) +{ + BOOL result = FALSE; + BOOL modeValid = FALSE; + TD_OP_MODE_T mode = getCurrentOperationMode(); + UI_RESPONSE_PAYLOAD_T response; + + response.rejectionReason = REQUEST_REJECT_REASON_NONE; + + if ( 0 == message->hdr.payloadLen ) + { + modeValid = ( ( MODE_PRET == mode ) || ( MODE_TREA == mode ) ? TRUE : FALSE ); + + if ( ( TRUE != modeValid ) || ( TUBE_SET_INSTALL_STATE_AWAIT_TUBE_SET_CONFIRMATION != currentInstallState ) ) + { + response.rejectionReason = REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE; + } + else + { + confirmTubesetPlaced = TRUE; + result = TRUE; + } + } + else + { + response.rejectionReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; + } + + response.accepted = result; + sendMessage( MSG_ID_TD_ADJUST_DISPOSABLES_CONFIRM_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08*)(&response), sizeof( UI_RESPONSE_PAYLOAD_T ) ); + + return result; +} + +/**@}*/ Fisheye: Tag 58d4338b0c453ac49d3fdc4ba391b47949406b98 refers to a dead (removed) revision in file `firmware/App/Services/FluidBolus.c'. Fisheye: No comparison available. Pass `N' to diff? Fisheye: Tag 58d4338b0c453ac49d3fdc4ba391b47949406b98 refers to a dead (removed) revision in file `firmware/App/Services/FluidBolus.h'. Fisheye: No comparison available. Pass `N' to diff?