/************************************************************************** * * 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 StateTxHDF.c * * @author (last) Praneeth Bunne * @date (last) 03-Jun-2026 * * @author (original) Sean Nash * @date (original) 21-Apr-2025 * ***************************************************************************/ #include "TDCommon.h" #include "AirTrap.h" #include "BloodFlow.h" #include "Buttons.h" #include "DDInterface.h" #include "HemoDiaFiltration.h" #include "FluidBolus.h" #include "Messaging.h" #include "ModeTreatment.h" //#include "NVDataMgmt.h" #include "OperationModes.h" #include "StateTxHDF.h" #include "Switches.h" #include "TaskGeneral.h" #include "TxParams.h" #include "Valves.h" /** * @addtogroup StateTxHDF * @{ */ // ********** private definitions ********** // ********** private data ********** static HDF_STATE_T currentHDFState; ///< Current state of the HDF sub-mode state machine. static HDF_STATE_T HDFResumeState; ///< HDF sub-state prior to fluid bolus. static U32 setBloodFlowRate; ///< Currently set blood flow rate (from prescription). static U32 setDialysateFlowRate; ///< Currently set dialysate flow rate (from prescription). static U32 setHDFFlowRate; ///< Currently set HDF flow rate. static F32 setUFVolumeL; ///< Currently set total ultrafiltration volume (in L) for treatment (from prescription). static F32 setUFRateMlMin; ///< Currently set ultrafiltration rate (in mL/min from prescription). static OVERRIDE_F32_T measUFVolumeL; ///< Current total measured volume (in L) for ultrafiltration . static F32 measHDFVolumeL; ///< Current total measured volume (in L) for HDF. static BOOL ufPauseRequested; ///< Flag indicates UF pause has been requested by user. static BOOL ufResumeRequested; ///< Flag indicates UF resume has been requested by user. static BOOL autoResumeUF; ///< Flag indicates UF should be auto-resumed after saline bolus completes. static BOOL fluidBolusRequested; ///< Flag indicating fluid bolus has been requested by user. // ********** private function prototypes ********** static void transitionToHDFState( HDF_STATE_T newState ); static HDF_STATE_T handleHDFUltrafiltrationState( void ); static HDF_STATE_T handleHDFUltrafiltrationPausedState( void ); static HDF_STATE_T handleHDFFluidBolusState( void ); static void updateUFVolume( void ); /*********************************************************************//** * @brief * The initHDF function initializes the HDF sub-mode module. * Calling this function will reset HDF and therefore should only * be called when a new treatment is due to begin. * @details \b Inputs: none * @details \b Outputs: HDF sub-mode module initialized. * @return none *************************************************************************/ void initHDF( void ) { currentHDFState = HDF_UF_STATE; HDFResumeState = HDF_UF_STATE; measUFVolumeL.data = 0.0F; measUFVolumeL.ovData = 0.0F; measUFVolumeL.ovInitData = 0.0F; measUFVolumeL.override = OVERRIDE_RESET; setBloodFlowRate = 0; setDialysateFlowRate = 0; setUFVolumeL = 0.0F; setUFRateMlMin = 0.0F; ufPauseRequested = FALSE; ufResumeRequested = FALSE; autoResumeUF = FALSE; fluidBolusRequested = FALSE; } /*********************************************************************//** * @brief * The transitionToHDF function prepares for transition to HDF sub-mode. * This function will reset anything required for resuming HDF in a * treatment that has already begun. It does not reset everything as HDF * may be stopped and resumed multiple times due to alarms or user intervention * and we don't want to start the treatment all over again. * @details \b Inputs: none * @details \b Outputs: none * @return none *************************************************************************/ void transitionToHDF( void ) { F32 dialTemp = getTreatmentParameterF32( TREATMENT_PARAM_DIALYSATE_TEMPERATURE ); F32 acidConvFactor = getTreatmentParameterF32( TREATMENT_PARAM_ACID_CONCENTRATE_CONV_FACTOR ); F32 bicarbConvFactor = BICARBONATE_CONVERSION_FACTOR; U32 sodium = getTreatmentParameterU32( TREATMENT_PARAM_SODIUM ); U32 bicarbonate = getTreatmentParameterU32( TREATMENT_PARAM_BICARBONATE ); F32 hdfRate = getCurrentHDFRate(); doorClosedRequired( TRUE ); // Set user alarm recovery actions allowed in this sub-mode setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, TRUE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, TRUE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, TRUE ); // Set actuators as appropriate for state cmdStartGenerateDialysate( setDialysateFlowRate, setUFRateMlMin, hdfRate, dialTemp, FALSE, acidConvFactor, bicarbConvFactor, sodium, bicarbonate ); transitionToHDFState( currentHDFState ); if ( hdfRate > 0.0F ) { StartHemodiafiltration(); } // Set substate for event setCurrentSubState( (U32)currentHDFState ); } /*********************************************************************//** * @brief * The transitionToHDFState function sets all actuators as appropriate * go the given new HDF state. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if invalid state is given * @details \b Inputs: none * @details \b Outputs: none * @param newState The new state that we are transitioning to. * @return none *************************************************************************/ static void transitionToHDFState( HDF_STATE_T newState ) { switch ( newState ) { case HDF_UF_STATE: setValvePosition( H1_VALV, VALVE_POSITION_B_OPEN ); // set arterial valve to pump blood from patient setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); // set venous valve to open, allowing blood to return to patient setBloodPumpTargetFlowRate( setBloodFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); cmdChangeQuf( setUFRateMlMin ); // Start auto-control of air trap valve startAirTrapControl(); break; case HDF_UF_PAUSED_STATE: // Set valves for HDF setValvePosition( H1_VALV, VALVE_POSITION_B_OPEN ); // set arterial valve to pump blood from patient setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); // set venous valve to open, allowing blood to return to patient // Restart pumps setBloodPumpTargetFlowRate( setBloodFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); cmdChangeQuf( 0.0F ); // Start auto-control of air trap valve startAirTrapControl(); break; case HDF_UF_FLUID_BOLUS_STATE: // State prior to bolus HDFResumeState = currentHDFState; cmdBypassDialyzer( TRUE ); cmdChangeQuf( 0.0F ); // Start bolus and transition to bolus state signalStartFluidBolus( setBloodFlowRate ); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_TX_HDF_INVALID_STATE1, (U32)newState ) break; } } /*********************************************************************//** * @brief * The getHDFState function gets the current HDF state. * @details \b Inputs: currentHDFState * @details \b Outputs: none * @return currentHDFState *************************************************************************/ HDF_STATE_T getHDFState( void ) { return currentHDFState; } /*********************************************************************//** * @brief * The getUltrafiltrationVolumeDrawn function gets the current total * ultrafiltration volume (in L) drawn from the patient so far. * @details \b Inputs: measUFVolumeL * @details \b Outputs: none * @return Total ultrafiltration volume (in L) drawn from the patient so far. *************************************************************************/ F32 getHDFUltrafiltrationVolumeDrawn( void ) { F32 result = getF32OverrideValue( &measUFVolumeL ); return result; } /*********************************************************************//** * @brief * The setHDFBloodPumpFlowRate function sets the blood pump flow rate parameter. * This function should be called prior to beginning HDF treatment * and when the user changes the blood flow rate during treatment. * @details \b Inputs: none * @details \b Outputs: setBloodFlowRate * @param bPFlow target blood pump flow rate (in mL/min) * @return none *************************************************************************/ void setHDFBloodPumpFlowRate( U32 bPFlow ) { setBloodFlowRate = bPFlow; // Make rate changes in real time if currently performing HDF. if ( TREATMENT_HDF_STATE == getTreatmentState() ) { setBloodPumpTargetFlowRate( setBloodFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); } } /*********************************************************************//** * @brief * The setHDFDDParams function sets the dialysate rate and ultrafiltration * volume and rate parameters. * @details \b Inputs: none * @details \b Outputs: setDialysateFlowRate, setUFVolumeL, setUFRateMlMin * @param qd target dialysate flow rate (in mL/min) * @param ufVol target ultrafiltration volume (in mL) * @param quf target ultrafiltration flow rate (in mL/min) * @return none *************************************************************************/ void setHDFDDParams( U32 qd, F32 ufVol, F32 quf ) { setDialysateFlowRate = qd; setUFVolumeL = ufVol; setUFRateMlMin = quf; } /*********************************************************************//** * @brief * The signalHDFPauseResumeUF function handles a request to pause or resume * ultrafiltration. * @details \b Message \b Sent: MSG_ID_TD_UF_PAUSE_RESUME_RESPONSE * @details \b Message \b Sent: UF_START_PAUSE_EVENT / UF_START_RESUME_EVENT * @details \b Inputs: currentHDFState, setUFRateMlMin * @details \b Outputs: ufPauseRequested, ufResumeRequested * @param message Message from UI which includes a flag indicating whether * to pause or resume ultrafiltration. * @return TRUE if pause *************************************************************************/ BOOL signalHDFPauseResumeUF( MESSAGE_T *message ) { TREATMENT_STATE_T trtState = getTreatmentState(); TD_OP_MODE_T currMode = getCurrentOperationMode(); UI_RESPONSE_PAYLOAD_T response; response.accepted = FALSE; response.rejectionReason = REQUEST_REJECT_REASON_NONE; // Verify payload length is valid if ( sizeof( BOOL ) == message->hdr.payloadLen ) { BOOL payload; memcpy( &payload, message->payload, sizeof( BOOL ) ); // Handle request to resume ultrafiltration if ( TRUE == payload ) { if ( ( MODE_TREA == currMode ) && ( TREATMENT_HDF_STATE == trtState ) && ( HDF_UF_PAUSED_STATE == currentHDFState ) ) { ufResumeRequested = TRUE; response.accepted = TRUE; if ( setUFRateMlMin > 0.0 ) { //sendTreatmentLogEventData( UF_START_RESUME_EVENT, 0.0, setUFRateMlMin ); } } else { if ( MODE_TREA != currMode ) { response.rejectionReason = REQUEST_REJECT_REASON_NOT_IN_TREATMENT_MODE; } else if ( TREATMENT_HDF_STATE != trtState ) { response.rejectionReason = REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE; } else { response.rejectionReason = REQUEST_REJECT_REASON_UF_NOT_PAUSED; } } } // Handle request to pause ultrafiltration else { if ( ( MODE_TREA == currMode ) && ( TREATMENT_HDF_STATE == trtState ) && ( HDF_UF_STATE == currentHDFState ) ) { ufPauseRequested = TRUE; response.accepted = TRUE; if ( setUFRateMlMin > 0.0 ) { //sendTreatmentLogEventData( UF_PAUSE_EVENT, setUFRateMlMin, 0.0 ); } } else { if ( MODE_TREA != currMode ) { response.rejectionReason = REQUEST_REJECT_REASON_NOT_IN_TREATMENT_MODE; } else if ( TREATMENT_HDF_STATE != trtState ) { response.rejectionReason = REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE; } else { response.rejectionReason = REQUEST_REJECT_REASON_UF_NOT_IN_PROGESS; } } } } else { response.rejectionReason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; } // Send response w/ reason code if rejected sendMessage( MSG_ID_TD_UF_PAUSE_RESUME_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08*)(&response), sizeof( UI_RESPONSE_PAYLOAD_T ) ); return response.accepted; } /*********************************************************************//** * @brief * The pauseHDF function pauses HDF. This function will be called * by Treatment Mode when an alarm occurs or the user pressed the stop button. * HDF may be resumed later. * @details Inputs: none * @details Outputs: Pumps stopped. DD commanded to bypass. * @return none *************************************************************************/ void pauseHDF( void ) { // Stop pumps signalBloodPumpHardStop(); // Tell DD to bypass dialyzer and stop HDF while alarm has HDF paused cmdBypassDialyzer( TRUE ); StopHemodiafiltration(); } /*********************************************************************//** * @brief * The execHDF function executes the HDF sub-mode state machine. * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if state is invalid * @details \b Message \b Sent: TD_EVENT_SUB_STATE_CHANGE when state changes * @details \b Inputs: currentHDFState * @details \b Outputs: currentHDFState * @return none *************************************************************************/ void execHDF( void ) { HDF_STATE_T priorSubState = currentHDFState; // HDF state machine switch ( currentHDFState ) { case HDF_UF_STATE: currentHDFState = handleHDFUltrafiltrationState(); break; case HDF_UF_PAUSED_STATE: currentHDFState = handleHDFUltrafiltrationPausedState(); break; case HDF_UF_FLUID_BOLUS_STATE: currentHDFState = handleHDFFluidBolusState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_TX_HDF_INVALID_STATE1, (U32)currentHDFState ) break; } // Update total UF volume drawn updateUFVolume(); if ( priorSubState != currentHDFState ) { setCurrentSubState( (U32)currentHDFState ); SEND_EVENT_WITH_2_U32_DATA( TD_EVENT_SUB_STATE_CHANGE, priorSubState, currentHDFState ); } } /*********************************************************************//** * @brief * The handleHDFUltrafiltrationState function handles the ultrafiltration * state of the HDF state machine. * @details \b Inputs: ufPauseRequested, fluidBolusRequested * @details \b Outputs: ufPauseRequested, fluidBolusRequested * @return next HDF state. *************************************************************************/ static HDF_STATE_T handleHDFUltrafiltrationState( void ) { HDF_STATE_T result = HDF_UF_STATE; if ( TRUE == ufPauseRequested ) { ufPauseRequested = FALSE; transitionToHDFState( HDF_UF_PAUSED_STATE ); result = HDF_UF_PAUSED_STATE; } // When UI requests fluid bolus else if ( TRUE == fluidBolusRequested ) { fluidBolusRequested = FALSE; transitionToHDFState( HDF_UF_FLUID_BOLUS_STATE ); result = HDF_UF_FLUID_BOLUS_STATE; } else { // No action required. } return result; } /*********************************************************************//** * @brief * The handleHDFUltrafiltrationPausedState function handles the * ultrafiltration paused state of the HDF state machine. * @details \b Inputs: ufResumeRequested, fluidBolusRequested * @details \b Outputs: ufResumeRequested, fluidBolusRequested * @return next HDF state. *************************************************************************/ static HDF_STATE_T handleHDFUltrafiltrationPausedState( void ) { HDF_STATE_T result = HDF_UF_PAUSED_STATE; if ( TRUE == ufResumeRequested ) { ufResumeRequested = FALSE; transitionToHDFState( HDF_UF_STATE ); result = HDF_UF_STATE; } // When UI requests fluid bolus else if ( TRUE == fluidBolusRequested ) { fluidBolusRequested = FALSE; transitionToHDFState( HDF_UF_FLUID_BOLUS_STATE ); result = HDF_UF_FLUID_BOLUS_STATE; } else { // No action required. } return result; } /*********************************************************************//** * @brief * The handleHDFFluidBolusState function handles the fluid bolus * sub-state of the HDF state machine. Signals the fluid bolus * service to start. * @details \b Inputs: HDFResumeState * @details \b Outputs: none * @return next HDF state. *************************************************************************/ static HDF_STATE_T handleHDFFluidBolusState( void ) { HDF_STATE_T result = HDF_UF_FLUID_BOLUS_STATE; // Restore actuators and return to pre-bolus sub-state upon bolus complete or abort if ( FALSE == isFluidBolusActive() ) { cmdBypassDialyzer( FALSE ); transitionToHDFState( HDFResumeState ); result = HDFResumeState; } return result; } /*********************************************************************//** * @brief * The updateUFVolume function updates the total measured ultrafiltration * volume based on set rate and whether or not DD is currently running * the ultrafiltration pump. * @details \b Inputs: none * @details \b Outputs: measUFVolumeL * @return none *************************************************************************/ static void updateUFVolume( void ) { // Update total UF volume drawn measUFVolumeL.data = 0.0F; // TODO } /*********************************************************************//** * @brief * The signalHDFFluidBolusRequest function signals a fluid bolus * request to the HDF sub-mode. * @details \b Inputs: none * @details \b Outputs: fluidBolusRequested * @return TRUE if request is accepted, FALSE if rejected. *************************************************************************/ BOOL signalHDFFluidBolusRequest( void ) { BOOL result = FALSE; // Allow from both UF & UF pause states fluidBolusRequested = TRUE; result = TRUE; return result; } /**@}*/