Index: firmware/App/Modes/StateTxIsolatedUF.c =================================================================== diff -u --- firmware/App/Modes/StateTxIsolatedUF.c (revision 0) +++ firmware/App/Modes/StateTxIsolatedUF.c (revision 8a7e31d75c06444514b61870b61f8c8a1815a618) @@ -0,0 +1,424 @@ +/************************************************************************** +* +* 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 StateTxIsolatedUF.c +* +* @author (last) Jashwant Gantyada +* @date (last) 26-May-2026 +* +* @author (original) Jashwant Gantyada +* @date (original) 18-May-2026 +* +***************************************************************************/ + +#include "StateTxIsolatedUF.h" +#include "AirTrap.h" +#include "BloodFlow.h" +#include "DDInterface.h" +#include "Messaging.h" +#include "ModeTreatment.h" +#include "OperationModes.h" +#include "StateTxDialysis.h" +#include "TaskGeneral.h" +#include "Valves.h" + +/** + * @addtogroup StateTxIsolatedUF + * @{ + */ + +// ********** private definitions ********** + +// ********** private data ********** + +static ISOLATED_UF_STATE_T currentIsolatedUFState; ///< Current isolated UF sub-state. +static OVERRIDE_F32_T isolatedUFVolumeDrawnL; ///< Current isolated UF volume drawn from patient in L. +static BOOL isolatedUFActive; ///< Flag indicating isolated UF is active. +static BOOL isolatedUFCompleted; ///< Flag indicating isolated UF completed. +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 F32 setDialysateFlowMlMin; ///< Dialysate flow rate (Qd) for isolated UF. +static U32 isolatedUFElapsedTimerCtr; ///< Elapsed isolated UF time in general task intervals (50 ms per tick). + +// ********** private function prototypes ********** + +static void transitionToIsolatedUFState( ISOLATED_UF_STATE_T newState ); +static ISOLATED_UF_STATE_T handleIsolatedUFRunningState( void ); +static ISOLATED_UF_STATE_T handleIsolatedUFPausedState( void ); +static void updateIsolatedUFVolume( void ); + +/*********************************************************************//** + * @brief + * The initIsolatedUF function initializes the isolated UF sub-mode module. + * @details \b Inputs: none + * @details \b Outputs: isolated UF state initialized. + * @return none + *************************************************************************/ +void initIsolatedUF( void ) +{ + currentIsolatedUFState = ISOLATED_UF_RUNNING_STATE; + + isolatedUFVolumeDrawnL.data = 0.0F; + isolatedUFVolumeDrawnL.ovData = 0.0F; + isolatedUFVolumeDrawnL.ovInitData = 0.0F; + isolatedUFVolumeDrawnL.override = OVERRIDE_RESET; + + isolatedUFActive = FALSE; + isolatedUFCompleted = FALSE; + ufPauseRequested = FALSE; + ufResumeRequested = FALSE; + setDialysateFlowMlMin = 0.0F; + isolatedUFElapsedTimerCtr = 0U; +} + +/*********************************************************************//** + * @brief + * The transitionToIsolatedUF function prepares for transition to isolated UF. + * @details \b Inputs: treatment parameters, isolated UF settings + * @details \b Outputs: DD command, valves, blood pump, current sub-state + * @return none + *************************************************************************/ +void transitionToIsolatedUF( void ) +{ + F32 dialTemp = getTreatmentParameterF32( TREATMENT_PARAM_DIALYSATE_TEMPERATURE ); + F32 ufRateMlMin = getIsolatedUFRateMlMin(); + 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 ); + + 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 ); + + // Mid-treatment isolated UF keeps dialysate generation at the treatment rate + // while bypassing the dialyzer. Post-treatment and standalone isolated UF do not generate dialysate. + if ( TRUE == isIsolatedUFMidTreatment() ) + { + setDialysateFlowMlMin = (F32)getTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW ); + } + else + { + setDialysateFlowMlMin = 0.0F; + } + + cmdStartGenerateDialysate( setDialysateFlowMlMin, ufRateMlMin, dialTemp, TRUE, acidConvFactor, bicarbConvFactor, sodium, bicarbonate ); + transitionToIsolatedUFState( currentIsolatedUFState ); + + if ( FALSE == isolatedUFActive ) + { + isolatedUFElapsedTimerCtr = 0U; + isolatedUFCompleted = FALSE; + isolatedUFActive = TRUE; + } + + setCurrentSubState( (U32)currentIsolatedUFState ); +} + +/*********************************************************************//** + * @brief + * The transitionToIsolatedUFState function sets actuators for the isolated UF + * sub-state. + * @details \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if invalid state is given. + * @details \b Inputs: newState + * @details \b Outputs: blood pump, valves, air trap control, DD Quf + * @param newState Isolated UF state to transition to. + * @return none + *************************************************************************/ +static void transitionToIsolatedUFState( ISOLATED_UF_STATE_T newState ) +{ + switch ( newState ) + { + case ISOLATED_UF_RUNNING_STATE: + setValvePosition( H1_VALV, VALVE_POSITION_B_OPEN ); + setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); + setBloodPumpTargetFlowRate( getTreatmentParameterU32( TREATMENT_PARAM_BLOOD_FLOW ), MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + cmdChangeQuf( getIsolatedUFRateMlMin() ); + startAirTrapControl(); + break; + + case ISOLATED_UF_PAUSED_STATE: + setValvePosition( H1_VALV, VALVE_POSITION_B_OPEN ); + setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); + setBloodPumpTargetFlowRate( getTreatmentParameterU32( TREATMENT_PARAM_BLOOD_FLOW ), MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + cmdChangeQuf( 0.0F ); + startAirTrapControl(); + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_MODE_TREATMENT_INVALID_STATE, (U32)newState ) + break; + } +} + +/*********************************************************************//** + * @brief + * The getCurrentIsolatedUFState function gets the current isolated UF state. + * @details \b Inputs: currentIsolatedUFState + * @details \b Outputs: none + * @return currentIsolatedUFState + *************************************************************************/ +ISOLATED_UF_STATE_T getCurrentIsolatedUFState( void ) +{ + return currentIsolatedUFState; +} + +/*********************************************************************//** + * @brief + * The getIsolatedUFVolumeDrawn function gets the total isolated UF volume + * drawn from the patient so far. + * @details \b Inputs: isolatedUFVolumeDrawnL + * @details \b Outputs: none + * @return Isolated UF volume drawn in L. + *************************************************************************/ +F32 getIsolatedUFVolumeDrawn( void ) +{ + return getF32OverrideValue( &isolatedUFVolumeDrawnL ); +} + +/*********************************************************************//** + * @brief + * The isIsolatedUFCompleted function determines whether isolated UF completed. + * @details \b Inputs: isolatedUFCompleted + * @details \b Outputs: none + * @return TRUE if isolated UF completed, FALSE otherwise. + *************************************************************************/ +BOOL isIsolatedUFCompleted( void ) +{ + return isolatedUFCompleted; +} + +/*********************************************************************//** + * @brief + * The endIsolatedUFOnUserStop function ends an active isolated UF session. + * @details \b Inputs: none + * @details \b Outputs: isolatedUFActive, isolatedUFCompleted cleared + * @return none + *************************************************************************/ +void endIsolatedUFOnUserStop( void ) +{ + isolatedUFActive = FALSE; + isolatedUFCompleted = FALSE; +} + +/*********************************************************************//** + * @brief + * The handleUFPauseResumeRequest function routes a UI pause/resume UF request + * to the dialysis or isolated UF sub-mode handler. + * @details \b Inputs: getTreatmentState + * @details \b Outputs: none + * @param message Pause/resume request message from UI. + * @return TRUE if request was accepted, FALSE otherwise. + *************************************************************************/ +BOOL handleUFPauseResumeRequest( MESSAGE_T *message ) +{ + BOOL result; + + if ( TREATMENT_ISO_UF_STATE == getTreatmentState() ) + { + result = signalPauseResumeIsolatedUF( message ); + } + else + { + result = signalPauseResumeUF( message ); + } + + return result; +} + +/*********************************************************************//** + * @brief + * The signalPauseResumeIsolatedUF function handles a request to pause or resume + * isolated ultrafiltration. + * @details \b Message \b Sent: MSG_ID_TD_UF_PAUSE_RESUME_RESPONSE + * @details \b Inputs: currentIsolatedUFState + * @details \b Outputs: ufPauseRequested, ufResumeRequested + * @param message Message from UI which includes a flag indicating whether + * to pause or resume ultrafiltration. + * @return TRUE if request was accepted, FALSE otherwise. + *************************************************************************/ +BOOL signalPauseResumeIsolatedUF( 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; + + if ( sizeof( BOOL ) == message->hdr.payloadLen ) + { + BOOL payload; + + memcpy( &payload, message->payload, sizeof( BOOL ) ); + + if ( TRUE == payload ) + { + if ( ( MODE_TREA == currMode ) && ( TREATMENT_ISO_UF_STATE == trtState ) && + ( ISOLATED_UF_PAUSED_STATE == currentIsolatedUFState ) ) + { + ufResumeRequested = TRUE; + response.accepted = TRUE; + } + else + { + if ( MODE_TREA != currMode ) + { + response.rejectionReason = REQUEST_REJECT_REASON_NOT_IN_TREATMENT_MODE; + } + else if ( TREATMENT_ISO_UF_STATE != trtState ) + { + response.rejectionReason = REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE; + } + else + { + response.rejectionReason = REQUEST_REJECT_REASON_UF_NOT_PAUSED; + } + } + } + else + { + if ( ( MODE_TREA == currMode ) && ( TREATMENT_ISO_UF_STATE == trtState ) && + ( ISOLATED_UF_RUNNING_STATE == currentIsolatedUFState ) ) + { + ufPauseRequested = TRUE; + response.accepted = TRUE; + } + else + { + if ( MODE_TREA != currMode ) + { + response.rejectionReason = REQUEST_REJECT_REASON_NOT_IN_TREATMENT_MODE; + } + else if ( TREATMENT_ISO_UF_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; + } + + 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 execIsolatedUF function executes the isolated UF sub-mode state machine. + * @details \b Message \b Sent: TD_EVENT_SUB_STATE_CHANGE when state changes + * @details \b Inputs: currentIsolatedUFState + * @details \b Outputs: currentIsolatedUFState + * @return none + *************************************************************************/ +void execIsolatedUF( void ) +{ + ISOLATED_UF_STATE_T priorSubState = currentIsolatedUFState; + + switch ( currentIsolatedUFState ) + { + case ISOLATED_UF_RUNNING_STATE: + currentIsolatedUFState = handleIsolatedUFRunningState(); + break; + + case ISOLATED_UF_PAUSED_STATE: + currentIsolatedUFState = handleIsolatedUFPausedState(); + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_MODE_TREATMENT_INVALID_STATE, (U32)currentIsolatedUFState ) + break; + } + + updateIsolatedUFVolume(); + + if ( priorSubState != currentIsolatedUFState ) + { + setCurrentSubState( (U32)currentIsolatedUFState ); + SEND_EVENT_WITH_2_U32_DATA( TD_EVENT_SUB_STATE_CHANGE, priorSubState, currentIsolatedUFState ); + } +} + +/*********************************************************************//** + * @brief + * The handleIsolatedUFRunningState function handles isolated UF running state. + * @details \b Inputs: isolatedUFActive, ufPauseRequested, isolatedUFElapsedTimerCtr + * @details \b Outputs: isolatedUFElapsedTimerCtr, isolatedUFCompleted, isolatedUFActive + * @return current isolated UF state. + *************************************************************************/ +static ISOLATED_UF_STATE_T handleIsolatedUFRunningState( void ) +{ + ISOLATED_UF_STATE_T result = ISOLATED_UF_RUNNING_STATE; + U32 durationTimerLimit = ( getIsolatedUFDurationMin() * SEC_PER_MIN * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL; + F32 setVolumeL = getIsolatedUFSetVolumeMl() / (F32)ML_PER_LITER; + + if ( TRUE == ufPauseRequested ) + { + ufPauseRequested = FALSE; + transitionToIsolatedUFState( ISOLATED_UF_PAUSED_STATE ); + result = ISOLATED_UF_PAUSED_STATE; + } + else if ( TRUE == isolatedUFActive ) + { + isolatedUFElapsedTimerCtr++; + + if ( ( ( durationTimerLimit > 0U ) && ( isolatedUFElapsedTimerCtr >= durationTimerLimit ) ) || + ( ( setVolumeL > 0.0F ) && ( getIsolatedUFVolumeDrawn() >= setVolumeL ) ) ) + { + isolatedUFCompleted = TRUE; + isolatedUFActive = FALSE; + } + } + + return result; +} + +/*********************************************************************//** + * @brief + * The handleIsolatedUFPausedState function handles isolated UF paused state. + * @details \b Inputs: ufResumeRequested + * @details \b Outputs: ufResumeRequested + * @return current isolated UF state. + *************************************************************************/ +static ISOLATED_UF_STATE_T handleIsolatedUFPausedState( void ) +{ + ISOLATED_UF_STATE_T result = ISOLATED_UF_PAUSED_STATE; + + if ( TRUE == ufResumeRequested ) + { + ufResumeRequested = FALSE; + transitionToIsolatedUFState( ISOLATED_UF_RUNNING_STATE ); + result = ISOLATED_UF_RUNNING_STATE; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The updateIsolatedUFVolume function updates measured isolated UF volume. + * @details \b Inputs: none + * @details \b Outputs: isolatedUFVolumeDrawnL + * @return none + *************************************************************************/ +static void updateIsolatedUFVolume( void ) +{ + isolatedUFVolumeDrawnL.data = 0.0F; // TODO: update when DD reports delivered UF volume. +} + +/**@}*/ Index: firmware/App/Modes/StateTxIsolatedUF.h =================================================================== diff -u --- firmware/App/Modes/StateTxIsolatedUF.h (revision 0) +++ firmware/App/Modes/StateTxIsolatedUF.h (revision 8a7e31d75c06444514b61870b61f8c8a1815a618) @@ -0,0 +1,60 @@ +/************************************************************************** +* +* 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 StateTxIsolatedUF.h +* +* @author (last) Jashwant Gantyada +* @date (last) 26-May-2026 +* +* @author (original) Jashwant Gantyada +* @date (original) 18-May-2026 +* +***************************************************************************/ + +#ifndef __STATE_TX_ISOLATED_UF_H__ +#define __STATE_TX_ISOLATED_UF_H__ + +#include "MsgQueues.h" + +/** + * @defgroup StateTxIsolatedUF StateTxIsolatedUF + * @brief Treatment mode isolated ultrafiltration state unit. The isolated UF + * sub-mode removes fluid from the patient with the dialyzer bypassed. Dialysate + * generation depends on whether isolated UF is started mid-treatment (Qd at + * treatment rate), post-treatment (Qd = 0), or as a standalone prescription (Qd = 0). + * + * @addtogroup StateTxIsolatedUF + * @{ + */ + +// ********** public definitions ********** + +/// Isolated ultrafiltration sub-state enumeration. +typedef enum Isolated_UF_States +{ + ISOLATED_UF_RUNNING_STATE = 0, ///< Isolated UF is running. + ISOLATED_UF_PAUSED_STATE, ///< Isolated UF is paused (Quf = 0). + NUM_OF_ISOLATED_UF_STATES ///< Number of isolated UF states. +} ISOLATED_UF_STATE_T; + +// ********** public function prototypes ********** + +void initIsolatedUF( void ); // Initialize isolated UF unit +void transitionToIsolatedUF( void ); // Prepares for transition to isolated UF +void execIsolatedUF( void ); // Execute isolated UF state machine + +ISOLATED_UF_STATE_T getCurrentIsolatedUFState( void ); // Gets current isolated UF state +BOOL isIsolatedUFCompleted( void ); // Determines whether isolated UF has completed +void endIsolatedUFOnUserStop( void ); // Ends active isolated UF session +F32 getIsolatedUFVolumeDrawn( void ); // Gets current isolated UF volume drawn from patient + +BOOL handleUFPauseResumeRequest( MESSAGE_T *message ); // Routes UI pause/resume UF to dialysis or isolated UF +BOOL signalPauseResumeIsolatedUF( MESSAGE_T *message ); // Handles UI pause/resume UF request + +/**@}*/ + +#endif