/************************************************************************** * * Copyright (c) 2024-2025 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 Ultrafiltration.c * * @author (last) Vinayakam Mani * @date (last) 23-Jul-2025 * * @author (original) Vinayakam Mani * @date (original) 23-Jul-2025 * ***************************************************************************/ #include "ConcentratePumps.h" #include "Messaging.h" #include "OperationModes.h" #include "TaskGeneral.h" #include "Temperature.h" #include "TDInterface.h" #include "TestSupport.h" #include "Ultrafiltration.h" /** * @addtogroup Ultrafiltration * @{ */ // ********** private definitions ********** #define UF_DATA_PUBLISH_INTERVAL ( 1000 / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the ultrafiltration data published. #define UF_COMPENSATION_PERIOD ( 2 * SEC_PER_MIN * MS_PER_SECOND ) ///< Interval at which the ultrafiltration compenstaion executed. #define UF_COMP_INTERVAL ( UF_COMPENSATION_PERIOD / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the peroidic ultrafiltration compensation calculated. #define COMP_SLOPE -0.000376F ///< UF Temperature compensation slope factor #define COMP_INTERCEPT 1.007269F ///< UF temperature compensation intercept factor #define ZERO_RATE 0.0F ///< Zero value. // ********** private data ********** static UF_EXEC_STATE_T ufExecState; ///< Current ultrafiltration executive state. static BOOL isUltrafiltrationRequested; ///< Flag indicating ultrafiltration request. static U32 currentUFCompCounter; ///< Counter (in task interval) to initiate the periodic ultrafiltration temperature compensation. static F32 compUFrate; ///< compensated UF rate static U32 ufDataPublicationTimerCounter; ///< Used to schedule ultrafiltration data publication to CAN bus. static BOOL isUFRateUpdated; ///< flag indicating to update UF rate needed. static OVERRIDE_U32_T ufDataPublishInterval; ///< Ultrafiltration data publish interval. // ********** private function prototypes ********** static UF_EXEC_STATE_T handleUFRunningState( void ); static UF_EXEC_STATE_T handleUFPausedState( void ); static void updateUFRequest( void ); static void UpdateUFCompensation( void ); static void publishUltrafiltrationData( void ); /*********************************************************************//** * @brief * The initUltrafiltration function initializes the ultrafiltration unit. * @details \b Inputs: none * @details \b Outputs: unit variables initialized * @return none *************************************************************************/ void initUltrafiltration( void ) { ufExecState = DD_UF_PAUSED; ufDataPublishInterval.data = UF_DATA_PUBLISH_INTERVAL; ufDataPublishInterval.ovData = UF_DATA_PUBLISH_INTERVAL; ufDataPublishInterval.ovInitData = 0; ufDataPublishInterval.override = OVERRIDE_RESET; isUltrafiltrationRequested = FALSE; currentUFCompCounter = 0; compUFrate = getTDUFRate(); ufDataPublicationTimerCounter = 0; isUFRateUpdated = FALSE; } /*********************************************************************//** * @brief * The transitionToUltrafiltration function prepares for transition to * ultrafiltration. * @details \b Inputs: none * @details \b Outputs: isUltrafiltrationRequested,ufDataPublicationTimerCounter * @return none *************************************************************************/ void transitionToUltrafiltration( void ) { initUltrafiltration(); } /*********************************************************************//** * @brief * The execUFControl function executes the ultrafiltration state machine. * @details \b Inputs: ufExecState * @details \b Outputs: ufExecState * @details \b Alarm: ALARM_ID_DD_SOFTWARE_FAULT when wrong ultrafiltration * state invoked. * @return current state. *************************************************************************/ U32 execUFControl( void ) { // Calculate UF volume and determine UF pause/run updateUFRequest(); // Compensate balancing error at defined interval UpdateUFCompensation(); // execute current ultrafiltration exec state switch ( ufExecState ) { case DD_UF_PAUSED: ufExecState = handleUFPausedState(); break; case DD_UF_RUNNING: ufExecState = handleUFRunningState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_DD_SOFTWARE_FAULT, SW_FAULT_ID_UF_INVALID_EXEC_STATE, ufExecState ) ufExecState = DD_UF_PAUSED; break; } //Publish ultrafiltration data publishUltrafiltrationData(); return ufExecState; } /*********************************************************************//** * @brief * The handleUFPausedState function handles the ultrafiltration * paused state. * @details \b Inputs:isUltrafiltrationRequested * @details \b Outputs: UF state * @return next UF state. *************************************************************************/ static UF_EXEC_STATE_T handleUFPausedState( void ) { UF_EXEC_STATE_T state = DD_UF_PAUSED; if ( TRUE == isUltrafiltrationRequested ) { // start with TD UF rate compUFrate = getTDUFRate(); currentUFCompCounter = 0; setConcentratePumpTargetSpeed( D76_PUMP, compUFrate, DOSING_CONT_VOLUME ); requestConcentratePumpOn( D76_PUMP ); //Tranistion to run state state = DD_UF_RUNNING; } return state; } /*********************************************************************//** * @brief * The handleUFPausedState function handles the ultrafiltration * running state. * @details \b Inputs:isUltrafiltrationRequested * @details \b Outputs: UF state * @return next UF state. *************************************************************************/ static UF_EXEC_STATE_T handleUFRunningState( void ) { UF_EXEC_STATE_T state = DD_UF_RUNNING; if ( TRUE != isUltrafiltrationRequested ) { requestConcentratePumpOff( D76_PUMP, FALSE ); state = DD_UF_PAUSED; } return state; } /*********************************************************************//** * @brief * The updateUFRequest function updates the ultrafiltration rate per iteration * and of the ultrafiltration. * @details \b Inputs: TD Uf rate, TD Dialysate flow rate and bypass flag * @details \b Outputs: ufVolumeperIteration , isUltrafiltrationRequested * @return none. *************************************************************************/ static void updateUFRequest( void ) { // update latest UF run/pause request if ( ( getTDUFRate() > ZERO_RATE ) && ( TRUE != getTDDialyzerBypass() ) && ( getTDDialysateFlowrate() > ZERO_RATE ) ) { isUltrafiltrationRequested = TRUE; } else { isUltrafiltrationRequested = FALSE; } } /*********************************************************************//** * @brief * The signalUFRateUpdate function sets the flag to udpate the * UF rate. * @details \b Inputs: none * @details \b Outputs: isUFRateUpdated * @return none. *************************************************************************/ void signalUFRateUpdate( void ) { isUFRateUpdated = TRUE; } /*********************************************************************//** * @brief * The UpdateUFCompensation function updates the ultrafiltration rate * based on the dialysate temperature compensation. * @details \b Inputs: D4 and D50 temperature * @details \b Outputs: updated UF rate * @return none. *************************************************************************/ static void UpdateUFCompensation( void ) { if ( ( ++currentUFCompCounter >= UF_COMP_INTERVAL ) || ( TRUE == isUFRateUpdated ) ) { F32 freshDensity = ( COMP_SLOPE * getD4AverageTemperature() ) + COMP_INTERCEPT; // Fresh side dialysate density F32 spentDensity = ( COMP_SLOPE * getD50AverageTemperature() ) + COMP_INTERCEPT; // spent side dialysate density F32 compFreshFlowrate = getTDDialysateFlowrate() * freshDensity; // Qd * fresh density F32 compSpentFlowrate = getTDDialysateFlowrate() * spentDensity; // Qd * spent density F32 balancingError = compFreshFlowrate - compSpentFlowrate; // Error in g/min //Update compensate UF rate with the balancing chamber error compUFrate = getTDUFRate() + balancingError; // Update UF rate setConcentratePumpTargetSpeed( D76_PUMP, compUFrate, DOSING_CONT_VOLUME ); currentUFCompCounter = 0; isUFRateUpdated = FALSE; } } /*********************************************************************//** * @brief * The getCurrentUFExecState function returns the current state * of the ultrafiltration. * @details \b Inputs: ufExecState * @details \b Outputs: none * @return the current state of UF execution state. *************************************************************************/ UF_EXEC_STATE_T getCurrentUFExecState( void ) { return ufExecState; } /*********************************************************************//** * @brief * The publishUltrafiltrationData function broadcasts the ultrafiltration * data at defined interval. * @details \b Inputs: ufDataPublicationTimerCounter * @details \b Outputs: DD ultrafiltration data broadcast message sent * @details \b Message \Sent: MSG_ID_DD_UF_DATA to publish the ultrafiltration * data. * @return none *************************************************************************/ static void publishUltrafiltrationData( void ) { if ( ++ufDataPublicationTimerCounter >= getU32OverrideValue( &ufDataPublishInterval ) ) { UF_DATA_T data; data.ufExecState = (U32)ufExecState; data.ufRate = getTDUFRate(); data.compUFrate = compUFrate; data.isUFRequested = (U32)isUltrafiltrationRequested; broadcastData( MSG_ID_DD_UF_DATA, COMM_BUFFER_OUT_CAN_DD_BROADCAST, (U08*)&data, sizeof( UF_DATA_T ) ); ufDataPublicationTimerCounter = 0; } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testDDUFDataPublishIntervalOverride function overrides the * DD ultrafiltration data publish interval. * @details \b Inputs: ufDataPublishInterval * @details \b Outputs: ufDataPublishInterval * @param Override message from Dialin which includes the interval * (in ms) to override the DD ultrafiltration data publish interval to. * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testDDUFDataPublishIntervalOverride( MESSAGE_T *message ) { BOOL result = u32BroadcastIntervalOverride( message, &ufDataPublishInterval, TASK_GENERAL_INTERVAL ); return result; } /**@}*/