/************************************************************************** * * 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 TubeSetAutoEject.c * * @author (last) Praneeth Bunne * @date (last) 29-Apr-2026 * * @author (original) Praneeth Bunne * @date (original) 29-Apr-2026 * ***************************************************************************/ #include "BloodFlow.h" #include "Ejector.h" #include "Messaging.h" #include "OperationModes.h" #include "PeristalticPump.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 ejectComplete; ///< True once the service has finished successfully 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 ); /*********************************************************************//** * @brief * The initTubeSetAutoEject function initializes the Tube Set Eject service. * @details Inputs: none * @details Outputs: Resets the flags, counters and sets the initial * Tube Set Eject sub-state. * @return none **************************************************************************/ void initTubeSetAutoEject( void ) { currentAutoEjectState = TUBE_SET_AUTO_EJECT_STATE_AWAIT_CONFIRMATION; autoEjectReqReceived = FALSE; autoEjectTimerCounter = 0; ejectComplete = FALSE; } /*********************************************************************//** * @brief * The execTubeSetEject function executes the Tube Set Eject * state machine. * @details Inputs: currentAutoEjectState * @details Outputs: currentAutoEjectState, ejectComplete. Advances the * Tube Set Eject sub-state * @return none *************************************************************************/ void execTubeSetAutoEject( void ) { if( TRUE == ejectComplete ) { return; } 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; 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; } } /*********************************************************************//** * @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 Inputs: autoEjectReqReceived * @details Outputs: autoEjectReqReceived * @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 Inputs: autoEjectTimerCounter, isPeristalticPumpHome() * @details 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 == isPeristalticPumpHome() ) { extendEjector(); autoEjectTimerCounter = 0; state = TUBE_SET_AUTO_EJECT_STATE_EXTENDING_EJECTOR; } 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 Inputs: autoEjectTimerCounter, getEjectorState() * @details 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 ); autoEjectTimerCounter = 0; state = TUBE_SET_AUTO_EJECT_STATE_EJECTING; } 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. * @details Inputs: autoEjectTimerCounter, isPeristalticPumpHome() * @details Outputs: autoEjectTimerCounter * @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; 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(); retractEjector(); autoEjectTimerCounter = 0; state = TUBE_SET_AUTO_EJECT_STATE_RETRACTING_EJECTOR; } 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. * @details Inputs: autoEjectTimerCounter, getEjectorState() * @details Outputs: autoEjectTimerCounter, ejectComplete * @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; ejectComplete = TRUE; state = NUM_OF_TUBE_SET_AUTO_EJECT_SUB_STATES; } return state; } /*********************************************************************//** * @brief * The isTubeSetEjectComplete function reports whether the eject * service has finished successfully. * @details \b Inputs: ejectComplete * @details \b Outputs: none * @return TRUE if auto-eject completed and ejector is retracted, * FALSE otherwise. *************************************************************************/ BOOL isTubeSetAutoEjectComplete( void ) { return ejectComplete; } /*********************************************************************//** * @brief * The handleAutoEjectRequest function handles a UI request to remove the * disposable tubeset. * @details Inputs: none * @details 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 { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_MESSAGE_PAYLOAD_LENGTH, (U32)message->hdr.payloadLen ); } 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; } /**@}*/