/************************************************************************** * * 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 HemoDiaFiltration.c * * @author (last) Steve Jarpe * @date (last) 23-May-2026 * * @author (original) Steve Jarpe * @date (original) 23-May-2026 * ***************************************************************************/ #include "SubstitutionPump.h" #include "PIControllers.h" #include "Pressure.h" #include "Messaging.h" #include "OperationModes.h" #include "TaskGeneral.h" #include "TDInterface.h" #include "TestSupport.h" #include "Hemodiafiltration.h" /** * @addtogroup Hemodiafiltration * @{ */ // ********** private definitions ********** #define HDF_DATA_PUBLISH_INTERVAL ( 1000 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the hemodiafiltration data published. #define SECONDS_PER_MINUTE 60 ///< seconds per minute #define HDF_CONTROL_PERIOD_SECONDS 2 ///< Interval at which the hemodiafiltration flow rate adjustment is executed in seconds #define HDF_CONTROL_PERIOD ( HDF_CONTROL_PERIOD_SECONDS * MS_PER_SECOND ) ///< Interval at which the hemodiafiltration flow rate adjustment is executed in milliseconds #define HDF_CONTROL_INTERVAL ( HDF_CONTROL_PERIOD / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the peroidic hemodiafiltration flow rate is calculated. #define HDF_MAX_FILTRATION_FRACTION 0.35F ///< HDF maximum filtration fraction (Qs+Quf)/Qb #define HDF_MAX_TMP 400.0F ///< HDF maximum TMP - trans-membrane pressure = (Pb - Pd) #define HDF_MAX_RATE 200.0F ///< Maximum substitution flow rate #define ZERO_RATE 0.0F ///< Zero value. #define SUB_PUMP_PRESSURE_CONTROL_P_COEFFICIENT 0.5F ///< P term for Sub pump pressure control. #define SUB_PUMP_PRESSURE_CONTROL_I_COEFFICIENT 5.0F ///< I term for Sub pump pressure control. // ********** private data ********** static HDF_EXEC_STATE_T hdfExecState; ///< Current hemodiafiltration executive state. static BOOL isHemodiafiltrationRequested; ///< Flag indicating hemodiafiltration request. static U32 hdfControlTimerCounter; ///< Counter (in task interval) to initiate the periodic hemodiafiltration rate recalculation. static F32 currentHDFrate; ///< current HDF rate in ml/min static F32 TMP_target; static F32 currentHDFvolume; ///< current HDF delivered substitution volume static U32 hdfDataPublicationTimerCounter; ///< Used to schedule hemodiafiltration data publication to CAN bus. static BOOL isHDFRateUpdated; ///< flag indicating to update HDF rate needed. static OVERRIDE_U32_T hdfDataPublishInterval; ///< Hemodiafiltration data publish interval. // ********** private function prototypes ********** static HDF_EXEC_STATE_T handleHDFRunningState( void ); static HDF_EXEC_STATE_T handleHDFPausedState( void ); static void updateHDFRequest( void ); static void UpdateHDFCompensation( void ); static void publishHemodiafiltrationData( void ); /*********************************************************************//** * @brief * The initHemodiafiltration function initializes the hemodiafiltration unit. * @details \b Inputs: none * @details \b Outputs: unit variables initialized * @return none *************************************************************************/ void initHemodiafiltration( void ) { // Initialize substitution pump PI controller to target pressure initializePIController( PI_CONTROLLER_ID_SUB_PUMP_PRES, ZERO_RATE, SUB_PUMP_PRESSURE_CONTROL_P_COEFFICIENT, SUB_PUMP_PRESSURE_CONTROL_I_COEFFICIENT, ZERO_RATE, HDF_MAX_RATE, FALSE, 0 ); hdfExecState = DD_HDF_PAUSED; hdfDataPublishInterval.data = HDF_DATA_PUBLISH_INTERVAL; hdfDataPublishInterval.ovData = HDF_DATA_PUBLISH_INTERVAL; hdfDataPublishInterval.ovInitData = 0; hdfDataPublishInterval.override = OVERRIDE_RESET; isHemodiafiltrationRequested = FALSE; currentHDFCompCounter = 0; currentHDFrate = 0.0; currentHDFvolume = 0.0; hdfDataPublicationTimerCounter = 0; isHDFRateUpdated = FALSE; } /*********************************************************************//** * @brief * The transitionToHemodiafiltration function prepares for transition to * hemodiafiltration. * @details \b Inputs: none * @details \b Outputs: isHemodiafiltrationRequested,hdfDataPublicationTimerCounter * @return none *************************************************************************/ void transitionToHemodiafiltration( void ) { initHemodiafiltration(); } /*********************************************************************//** * @brief * The execHDFControl function executes the hemodiafiltration state machine. * @details \b Inputs: hdfExecState * @details \b Outputs: hdfExecState * @details \b Alarm: ALARM_ID_DD_SOFTWARE_FAULT when wrong hemodiafiltration * state invoked. * @details Warning: The compensated HDF should be caclulated first and then * call 'updateHDFRequest' function. * @return current state. *************************************************************************/ U32 execHDFControl( void ) { else if ( TRUE == isHDFRateUpdated ) { //get updated HDF rate compHDFrate = getTDHDFRate(); // Update HDF rate setConcentratePumpTargetSpeed( D76_PUMP, compHDFrate, DOSING_CONT_VOLUME ); isHDFRateUpdated = FALSE; } // Calculate HDF volume and determine HDF pause/run updateHDFRequest(); // execute current hemodiafiltration exec state switch ( hdfExecState ) { case DD_HDF_PAUSED: hdfExecState = handleHDFPausedState(); break; case DD_HDF_RUNNING: hdfExecState = handleHDFRunningState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_HDF_INVALID_EXEC_STATE, hdfExecState ) hdfExecState = DD_HDF_PAUSED; break; } //Publish hemodiafiltration data publishHemodiafiltrationData(); return hdfExecState; } /*********************************************************************//** * @brief * The handleHDFPausedState function handles the hemodiafiltration * paused state. * @details \b Inputs:isHemodiafiltrationRequested * @details \b Outputs: HDF state * @return next HDF state. *************************************************************************/ static HDF_EXEC_STATE_T handleHDFPausedState( void ) { HDF_EXEC_STATE_T state = DD_HDF_PAUSED; if ( TRUE == isHemodiafiltrationRequested ) { setSubstitutionPumpTargetSpeed( D92_PUMP, currentHDFrate ); requestSubstitutionPumpOn( D92_PUMP ); resetPIDController( PI_CONTROLLER_ID_SUB_PUMP_PRES, currentHDFrate, 0.0F ); hdfControlTimerCounter = 0; //Tranistion to run state state = DD_HDF_RUNNING; } return state; } /*********************************************************************//** * @brief * The handleHDFPausedState function handles the hemodiafiltration * running state. * @details \b Inputs:isHemodiafiltrationRequested * @details \b Outputs: HDF state * @return next HDF state. *************************************************************************/ static HDF_EXEC_STATE_T handleHDFRunningState( void ) { HDF_EXEC_STATE_T state = DD_HDF_RUNNING; if ( TRUE != isHemodiafiltrationRequested ) { requestSubstitutionPumpOff( D92_PUMP); state = DD_HDF_PAUSED; } else { UpdateHDFRateAndVolume(); } return state; } /*********************************************************************//** * @brief * The updateHDFRequest function updates the hemodiafiltration requested * flag to true or false based on the calculated HDF rate. * @details \b Inputs: compensated HDF * @details \b Outputs: isHemodiafiltrationRequested * @return none. *************************************************************************/ static void updateHDFRequest( void ) //need to understand this... { // update latest HDF run/pause request if ( currentHDFrate > ZERO_RATE ) { isHemodiafiltrationRequested = TRUE; } else { isHemodiafiltrationRequested = FALSE; } } /*********************************************************************//** * @brief * The signalHDFRateUpdate function sets the flag to udpate the * HDF rate. * @details \b Inputs: none * @details \b Outputs: isHDFRateUpdated * @return none. *************************************************************************/ void signalHDFRateUpdate( void ) { isHDFRateUpdated = TRUE; } /*********************************************************************//** * @brief * The getTMP function gets the TMP (trans-membrane pressure) * is within the (limits) * @details \b Inputs: Inputs: D41 and H14 pressure * @details \b Outputs: Calculated TMP * @return none. *************************************************************************/ static F32 getTMP( void ) { F32 d41Pressure = getFilteredPressure( D41_PRES ); F32 h14Pressure = ; //figure out how to get TD pressures return h14Pressure - d41Pressure; } /*********************************************************************//** * @brief * The CheckHDFRate function checks whether the hemodiafiltration rate * is within the (limits) * @details \b Inputs: proposed HDF rate * @details \b Outputs: updated HDF rate * @return none. *************************************************************************/ static F32 CheckHDFRate( F32 proposed_rate ) { F32 Qb = ; //get blood flow rate F32 Quf = getTDUFRate(); F32 max_sub_rate = HDF_MAX_FILTRATION_FRACTION * Qb - Quf; if (max_sub_rate > HDF_MAX_RATE) { max_sub_rate = HDF_MAX_RATE; } if (proposed_rate > max_sub_rate) { return max_sub_rate; } else { return proposed_rate; } } /*********************************************************************//** * @brief * The UpdateHDFCompensation function updates the hemodiafiltration rate * based on the trans-membrane-pressure * @details \b Inputs: D41 and H14 temperature * @details \b Outputs: updated HDF rate * @return none. *************************************************************************/ static void UpdateHDFRateAndVolume( void ) { if ( ( ++hdfControlTimerCounter >= HDF_CONTROL_INTERVAL ) || ( TRUE == isHDFRateUpdated ) ) // get current TMP currentHDFrate = runPIController( PI_CONTROLLER_ID_SUB_PUMP_PRES, TMP_target, TMP_current_value ); // Update HDF rate setSubstitutionPumpTargetSpeed( D92_PUMP, currentHDFrate ) hdfControlTimerCounter = 0; currentHDFvolume += ((F32) HDF_CONTROL_PERIOD_SECONDS / (F32) SECONDS_PER_MINUTE) * currentHDFrate; //ml hdfControlTimerCounter = 0; isHDFRateUpdated = FALSE; } } /*********************************************************************//** * @brief * The getCurrentHDFExecState function returns the current state * of the hemodiafiltration. * @details \b Inputs: hdfExecState * @details \b Outputs: none * @return the current state of HDF execution state. *************************************************************************/ HDF_EXEC_STATE_T getCurrentHDFExecState( void ) { return hdfExecState; } /*********************************************************************//** * @brief * The publishHemodiafiltrationData function broadcasts the hemodiafiltration * data at defined interval. * @details \b Inputs: hdfDataPublicationTimerCounter * @details \b Outputs: DD hemodiafiltration data broadcast message sent * @details \b Message \Sent: MSG_ID_DD_HDF_DATA to publish the hemodiafiltration * data. * @return none *************************************************************************/ static void publishHemodiafiltrationData( void ) { if ( ++hdfDataPublicationTimerCounter >= getU32OverrideValue( &hdfDataPublishInterval ) ) { HDF_DATA_T data; data.hdfExecState = (U32)hdfExecState; data.hdfRate = getTDHDFRate(); data.hdfcurrentrate = currentHDFrate; data.isHDFRequested = (U32)isHemodiafiltrationRequested; data.hdfRequestedVolume; = getTDHDFVolume(); data.hdfCurrentVolume; = CurrentHDFvolume; broadcastData( MSG_ID_DD_HDF_DATA, COMM_BUFFER_OUT_CAN_DD_BROADCAST, (U08*)&data, sizeof( HDF_DATA_T ) ); hdfDataPublicationTimerCounter = 0; } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testDDHDFDataPublishIntervalOverride function overrides the * DD hemodiafiltration data publish interval. * @details \b Inputs: hdfDataPublishInterval * @details \b Outputs: hdfDataPublishInterval * @param Override message from Dialin which includes the interval * (in ms) to override the DD hemodiafiltration data publish interval to. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testDDHDFDataPublishIntervalOverride( MESSAGE_T *message ) { BOOL result = u32BroadcastIntervalOverride( message, &hdfDataPublishInterval, TASK_GENERAL_INTERVAL ); return result; } /**@}*/