/************************************************************************** * * 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 StateTxRinseback.c * * @author (last) Praneeth Bunne * @date (last) 15-Jun-2026 * * @author (original) Praneeth Bunne * @date (original) 15-Jun-2026 * ***************************************************************************/ #include "AirTrap.h" #include "BloodFlow.h" #include "DDInterface.h" #include "Messaging.h" #include "ModeStandby.h" #include "ModeTreatment.h" #include "OperationModes.h" #include "Pressures.h" #include "StateTxPaused.h" #include "StateTxRinseback.h" #include "SyringePump.h" #include "TaskGeneral.h" #include "Timers.h" #include "TxParams.h" #include "Valves.h" /** * @addtogroup StateTxRinseback * @{ */ // ********** private definitions ********** #define TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML 50.0F ///< Target rinseback volume for an additional volume request (in mL). #define RINSEBACK_FLOW_RATE_ADJ_ML_MIN 10U ///< Adjustment amount (in mL/min) to apply when user requests increase/decrease #define DEFAULT_RINSEBACK_FLOW_RATE_ML_MIN 200U ///< Default rinseback flow rate (in mL/min). #define MIN_RINSEBACK_FLOW_RATE_ML_MIN 100U ///< Minimum rinseback flow rate (in mL/min). #define MAX_RINSEBACK_FLOW_RATE_ML_MIN 500U ///< Maximum rinseback flow rate (in mL/min). /// Interval at which rinseback progress is to be published to UI. #define RINSEBACK_DATA_PUBLISH_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) /// Maximum time allowed for rinseback operation until full volume is delivered. Timer is reset whenever BP is running. #define MAX_RINSEBACK_TIME ( 5 * SEC_PER_MIN * ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ) /// Maximum time allowed for each additional rinseback volume delivery. #define MAX_RINSEBACK_ADDITIONAL_TIME ( 25 * MS_PER_SECOND / TASK_GENERAL_INTERVAL ) /// Multiplier to convert flow (mL/min) into volume (mL) for period of general task interval. #define RINSEBACK_FLOW_INTEGRATOR 1.0F / (F32)( SEC_PER_MIN * ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ) // ********** private data ********** static RINSEBACK_STATE_T currentRinsebackState; ///< Current state of the rinseback sub-mode. static FLUID_TYPE_T fluidType; ///< Fluid Type to rinseback static U32 rinsebackTimerCtr; ///< Timer counter for time spent in rinseback sub-mode. static U32 rinsebackAdditionalTimerCtr; ///< Timer counter for duration of an additional rinseback delivery. static U32 rinsebackPublishTimerCtr; ///< Timer counter for determining interval for rinseback status to be published. static U32 rinsebackTargetVolume_mL; ///< Calculated target rinseback volume (based on selected dialyzer and fixed tubing volume). static OVERRIDE_F32_T cummulativeRinsebackVolume_mL = { 0.0, 0.0, 0.0, 0 }; ///< Total cumulative rinseback volume (in mL) from measured blood flow rate. static F32 targetRinsebackVolumePlusAdditional_mL; ///< Target rinseback volume w/ additional volume(s) added (in mL). static F32 additionalRinsebackVolume_mL; ///< Total volume (in mL) delivered so far for additional volume request. /// Interval (in task intervals) at which to publish rinseback data to CAN bus. static OVERRIDE_U32_T rinsebackPublishInterval = { RINSEBACK_DATA_PUBLISH_INTERVAL, RINSEBACK_DATA_PUBLISH_INTERVAL, RINSEBACK_DATA_PUBLISH_INTERVAL, 0}; static BOOL startRinsebackRequested; ///< Flag indicating user requesting rinseback start (confirming saline bag move to arterial line end). static BOOL incrRinsebackFlowRateRequested; ///< Flag indicating user requesting rinseback flow rate be increased. static BOOL decrRinsebackFlowRateRequested; ///< Flag indicating user requesting rinseback flow rate be decreased. static BOOL pauseRinsebackRequested; ///< Flag indicating user requesting rinseback pause. static BOOL resumeRinsebackRequested; ///< Flag indicating user requesting rinseback resume. static BOOL additionalRinsebackRequested; ///< Flag indicating user requesting a little more rinseback volume. static BOOL recircRequested; ///< Flag indicating user requesting to begin re-circulation. // ********** private function prototypes ********** static void resetRinsebackFlags( void ); static void setupForRinsebackDelivery( void ); static void setupForRinsebackStopOrPause( void ); static RINSEBACK_STATE_T handleRinsebackStopInitState( void ); static RINSEBACK_STATE_T handleRinsebackRunState( void ); static RINSEBACK_STATE_T handleRinsebackPausedState( void ); static RINSEBACK_STATE_T handleRinsebackStoppedState( void ); static RINSEBACK_STATE_T handleRinsebackAdditionalState( void ); static BOOL handleStartRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); static BOOL handleIncrRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); static BOOL handleDecrRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); static BOOL handlePauseRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); static BOOL handleResumeRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); static BOOL handleAdditionalRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); static BOOL handleRecircUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); static void publishRinsebackData( void ); static U32 getPublishRinsebackInterval( void ); /*********************************************************************//** * @brief * The initRinseback function initializes the Rinseback sub-mode module. * @details Inputs: none * @details Outputs: Rinseback sub-mode module initialized. * @return none *************************************************************************/ void initRinseback( void ) { currentRinsebackState = RINSEBACK_STOP_INIT_STATE; fluidType = FLUID_TYPE_SALINE; //getFluidType(); setSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE, (U32)DEFAULT_RINSEBACK_FLOW_RATE_ML_MIN ); // Set to default rate before rinseback start rinsebackTargetVolume_mL = getTreatmentParameterU32( TREATMENT_PARAM_PRIME_RINSEBACK_VOLUME ); targetRinsebackVolumePlusAdditional_mL = rinsebackTargetVolume_mL; rinsebackTimerCtr = 0; cummulativeRinsebackVolume_mL.data = 0.0F; additionalRinsebackVolume_mL = 0.0F; rinsebackAdditionalTimerCtr = 0; rinsebackPublishTimerCtr = 0; resetRinsebackFlags(); } /*********************************************************************//** * @brief * The resetRinsebackFlags function resets the Rinseback request flags. * @details Inputs: none * @details Outputs: Rinseback request flags reset to FALSE. * @return none *************************************************************************/ static void resetRinsebackFlags( void ) { startRinsebackRequested = FALSE; incrRinsebackFlowRateRequested = FALSE; decrRinsebackFlowRateRequested = FALSE; pauseRinsebackRequested = FALSE; resumeRinsebackRequested = FALSE; additionalRinsebackRequested = FALSE; recircRequested = FALSE; } /*********************************************************************//** * @brief * The transitionToRinseback function prepares for transition to Rinseback * sub-mode. * @details Inputs: none * @details Outputs: Rinseback sub-mode setup to begin * @return none *************************************************************************/ void transitionToRinseback( void ) { initRinseback(); setCurrentSubState( (U32)currentRinsebackState ); // Set user alarm recovery actions allowed in this sub-mode setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, TRUE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_TEMPORARY_BREAK, FALSE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, FALSE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, TRUE ); // Set actuators setupForRinsebackStopOrPause(); // Bypass dialyzer cmdBypassDialyzer( TRUE ); // Stop syringe pump stopSyringePump(); } /*********************************************************************//** * @brief * The setupForRinsebackDelivery function sets actuators appropriately * for rinseback states where delivery is in progress. * @details Inputs: none * @details Outputs: none * @param rate rinseback rate to use for blood pump and HDF pump * @return none *************************************************************************/ static void setupForRinsebackDelivery( void ) { // Open arterial & venous valves to return blood. setValvePosition( H1_VALV, VALVE_POSITION_B_OPEN ); setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); // Select the rinseback fluid source based fluid medium, which is based on modality. if ( FLUID_TYPE_SALINE == fluidType ) { setBloodPumpTargetFlowRate( getSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ), MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); } else { cmdSubstitutionRate( (F32)getSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ) ); } // Start air trap leveling control. startAirTrapControl(); } /*********************************************************************//** * @brief * The setupForRinsebackStopOrPause function sets actuators appropriately * for rinseback states where delivery is paused or stopped. * @details Inputs: none * @details Outputs: none * @return none *************************************************************************/ static void setupForRinsebackStopOrPause( void ) { // Stop pumps signalBloodPumpHardStop(); cmdSubstitutionRate( 0.0F ); // Switch arterial & venous valves to safe state setValvePosition( H1_VALV, VALVE_POSITION_C_CLOSE ); setValvePosition( H19_VALV, VALVE_POSITION_C_CLOSE ); // Stop air trap leveling control endAirTrapControl(); } /*********************************************************************//** * @brief * The getDeliveredRinsebackVolume function gets the calculated rinseback volume * delivered. * @details Inputs: cumulativeRinsebackVolume_mL * @details Outputs: none * @return the current rinseback volume delivered (in mL). *************************************************************************/ F32 getDeliveredRinsebackVolume( void ) { F32 result = cummulativeRinsebackVolume_mL.data; if ( OVERRIDE_KEY == cummulativeRinsebackVolume_mL.override ) { result = cummulativeRinsebackVolume_mL.ovData; } return result; } /*********************************************************************//** * @brief * The getCurrentRinsebackState function returns the current state of the * rinseback sub-mode. * @details Inputs: currentRinsebackState * @details Outputs: none * @return currentRinsebackState *************************************************************************/ RINSEBACK_STATE_T getCurrentRinsebackState( void ) { return currentRinsebackState; } /*********************************************************************//** * @brief * The execRinseback function executes the Rinseback sub-mode state machine. * @details Inputs: currentRinsebackState * @details Outputs: currentRinsebackState, rinsebackTimerCtr * @return none *************************************************************************/ void execRinseback( void ) { RINSEBACK_STATE_T priorSubState = currentRinsebackState; rinsebackTimerCtr++; switch ( currentRinsebackState ) { case RINSEBACK_STOP_INIT_STATE: currentRinsebackState = handleRinsebackStopInitState(); break; case RINSEBACK_RUN_STATE: currentRinsebackState = handleRinsebackRunState(); break; case RINSEBACK_PAUSED_STATE: currentRinsebackState = handleRinsebackPausedState(); break; case RINSEBACK_STOP_STATE: currentRinsebackState = handleRinsebackStoppedState(); break; case RINSEBACK_RUN_ADDITIONAL_STATE: currentRinsebackState = handleRinsebackAdditionalState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_INVALID_RINSEBACK_STATE, currentRinsebackState ); break; } if ( priorSubState != currentRinsebackState ) { setCurrentSubState( (U32)currentRinsebackState ); SEND_EVENT_WITH_2_U32_DATA( TD_EVENT_SUB_STATE_CHANGE, (U32)priorSubState, (U32)currentRinsebackState ); } // Rinseback flags should be handled by now - reset in case not handled by current state resetRinsebackFlags(); // Publish rinseback progress while in rinseback sub-mode publishRinsebackData(); } /*********************************************************************//** * @brief * The handleRinsebackStopInitState function handles the stopped initial * rinseback state operations. * @details \b Alarms: ALARM_ID_TD_RINSEBACK_TIMEOUT_ALARM if rinseback * timeout * @details Inputs: startRinsebackRequested * @details Outputs: none * @return next rinseback state *************************************************************************/ static RINSEBACK_STATE_T handleRinsebackStopInitState( void ) { RINSEBACK_STATE_T state = RINSEBACK_STOP_INIT_STATE; // Have we been in this state for too long? if ( rinsebackTimerCtr > MAX_RINSEBACK_TIME ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_RINSEBACK_TIMEOUT_ALARM, (U32)rinsebackTimerCtr, (U32)MAX_RINSEBACK_TIME ); } // User requested rinseback start else if ( TRUE == startRinsebackRequested ) { setBloodIsPrimed( FALSE ); setupForRinsebackDelivery(); state = RINSEBACK_RUN_STATE; } else { // No action required. } return state; } /*********************************************************************//** * @brief * The handleRinsebackRunState function handles the rinseback run state * operations. * @details \b Alarms: ALARM_ID_TD_RINSEBACK_TIMEOUT_ALARM if rinseback * timeout, ALARM_ID_TD_EMPTY_SALINE_BAG if saline bag is empty or * saline line is clamped * @details Inputs: rinsebackTargetVolume_mL, pauseRinsebackRequestd, * incrRinsebackFlowRateRequested, decrRinsebackFlowRateRequested * @details Outputs: rinsebackTimerCtr, cumulativeRinsebackVolume_mL, * rinsebackRate_mL_min, incrRinsebackFlowRateRequested, decrRinsebackFlowRateRequested * @return next rinseback state *************************************************************************/ static RINSEBACK_STATE_T handleRinsebackRunState( void ) { RINSEBACK_STATE_T state = RINSEBACK_RUN_STATE; // Reset rinseback timeout rinsebackTimerCtr = 0; // Update rinseback volume delivered so far cummulativeRinsebackVolume_mL.data += ( getSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ) * RINSEBACK_FLOW_INTEGRATOR ); // Check for empty saline bag if we are using saline to rinseback. if ( ( FLUID_TYPE_SALINE == fluidType ) && ( TRUE == isSalineBagEmpty() ) ) { SET_ALARM_WITH_1_F32_DATA( ALARM_ID_TD_EMPTY_SALINE_BAG, getFilteredArterialPressure() ); setupForRinsebackStopOrPause(); state = RINSEBACK_PAUSED_STATE; } // Rinseback complete ? else if ( getDeliveredRinsebackVolume() >= rinsebackTargetVolume_mL ) { setRinsebackIsCompleted( TRUE ); setupForRinsebackStopOrPause(); targetRinsebackVolumePlusAdditional_mL = getDeliveredRinsebackVolume(); state = RINSEBACK_STOP_STATE; } // Has user or alarm requested stop ? else if ( ( TRUE == pauseRinsebackRequested ) || TRUE == doesAlarmStatusIndicateStop() ) { setupForRinsebackStopOrPause(); state = RINSEBACK_PAUSED_STATE; } // Otherwise, continue else { if ( ( TRUE == incrRinsebackFlowRateRequested ) || ( TRUE == decrRinsebackFlowRateRequested ) ) { U32 newRate = getSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ); if ( TRUE == incrRinsebackFlowRateRequested ) { newRate += RINSEBACK_FLOW_RATE_ADJ_ML_MIN; } else { newRate -= RINSEBACK_FLOW_RATE_ADJ_ML_MIN; } incrRinsebackFlowRateRequested = FALSE; decrRinsebackFlowRateRequested = FALSE; if ( ( newRate >= MIN_RINSEBACK_FLOW_RATE_ML_MIN ) && ( newRate <= MAX_RINSEBACK_FLOW_RATE_ML_MIN ) ) { // Set the rinseabck flow rate with the new rate setSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE, newRate ); setupForRinsebackDelivery(); } } } return state; } /*********************************************************************//** * @brief * The handleRinsebackPausedState function handles the rinseback paused * state operations. * @details \b Alarms: ALARM_ID_TD_RINSEBACK_TIMEOUT_ALARM if rinseback * timeout * @details Inputs: rinsebackTimerCtr, resumeRinsebackRequested * @details Outputs: targetRinsebackVolumePlusAdditional_mL * @return next rinseback state *************************************************************************/ static RINSEBACK_STATE_T handleRinsebackPausedState( void ) { RINSEBACK_STATE_T state = RINSEBACK_PAUSED_STATE; // Have we been in this state for too long? if ( rinsebackTimerCtr > MAX_RINSEBACK_TIME ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_RINSEBACK_TIMEOUT_ALARM, (U32)rinsebackTimerCtr, (U32)MAX_RINSEBACK_TIME ); } // User requested to resume else if ( TRUE == resumeRinsebackRequested ) { setupForRinsebackDelivery(); state = RINSEBACK_RUN_STATE; } else { // No action required } return state; } /*********************************************************************//** * @brief * The handleRinsebackStoppedState function handles the stopped rinseback * state operations. * @details \b Alarms: ALARM_ID_TD_RINSEBACK_TIMEOUT_ALARM if rinseback * timeout * @details Inputs: rinsebackTimerCtr, rinsebackTargetVolume_mL, recircRequested, * additionalRinsebackRequested * @details Outputs: additionalRinsebackRequested, * rinsebackAdditionalTimerCtr, additionalRinsebackVolume_mL * @return next rinseback state *************************************************************************/ static RINSEBACK_STATE_T handleRinsebackStoppedState( void ) { RINSEBACK_STATE_T state = RINSEBACK_STOP_STATE; // Have we been in this stopped state for too long w/o having delivered full blood volume back to patient? if ( ( rinsebackTimerCtr > MAX_RINSEBACK_TIME ) && ( getDeliveredRinsebackVolume() < rinsebackTargetVolume_mL ) ) { SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_RINSEBACK_TIMEOUT_ALARM, (U32)rinsebackTimerCtr, (U32)MAX_RINSEBACK_TIME ); } // User requested to recirculate else if ( TRUE == recircRequested ) { if ( TRUE == isRecircAllowed() ) { signalRinsebackToRecirc(); } } else if ( TRUE == additionalRinsebackRequested ) { additionalRinsebackRequested = FALSE; if ( ( rinsebackTimerCtr < MAX_RINSEBACK_TIME ) || ( getDeliveredRinsebackVolume() >= rinsebackTargetVolume_mL ) ) { rinsebackAdditionalTimerCtr = 0; additionalRinsebackVolume_mL = 0.0F; targetRinsebackVolumePlusAdditional_mL += TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML; setSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE, DEFAULT_RINSEBACK_FLOW_RATE_ML_MIN ); setupForRinsebackDelivery(); state = RINSEBACK_RUN_ADDITIONAL_STATE; } } else { // No action required. } return state; } /*********************************************************************//** * @brief * The handleRinsebackRunAdditionalState function handles the rinseback additional * state operations. * @details \b Alarms: ALARM_ID_TD_EMPTY_SALINE_BAG if saline bag is empty or * saline line is clamped. * @details Inputs: additionalRinsebackVolume_mL, rinsebackAdditionalTimerCtr * @details Outputs: rinsebackTimerCtr, additionalRinsebackVolume_mL, cumulativeRinsebackVolume_mL, * rinsebackAdditionalTimerCtr, targetRinsebackVolumePlusAdditional_mL * @return next rinseback state *************************************************************************/ static RINSEBACK_STATE_T handleRinsebackAdditionalState( void ) { RINSEBACK_STATE_T state = RINSEBACK_RUN_ADDITIONAL_STATE; F32 rinsebackVolumeSinceLast = ( getSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ) * RINSEBACK_FLOW_INTEGRATOR ); // Reset rinseback t/o rinsebackTimerCtr = 0; // update additional rinseback volume delivered so far additionalRinsebackVolume_mL += rinsebackVolumeSinceLast; cummulativeRinsebackVolume_mL.data += rinsebackVolumeSinceLast; rinsebackAdditionalTimerCtr++; // Check for empty saline bag if we are using saline to rinseback. if ( ( FLUID_TYPE_SALINE == fluidType ) && ( TRUE == isSalineBagEmpty() ) ) { SET_ALARM_WITH_1_F32_DATA( ALARM_ID_TD_EMPTY_SALINE_BAG, getFilteredArterialPressure() ); setupForRinsebackStopOrPause(); targetRinsebackVolumePlusAdditional_mL = getDeliveredRinsebackVolume(); state = RINSEBACK_STOP_STATE; } // Has additional volume been delivered? else if ( additionalRinsebackVolume_mL >= TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML ) { setupForRinsebackStopOrPause(); state = RINSEBACK_STOP_STATE; } // Has alarm requested stop or timed out? else if ( ( TRUE == doesAlarmStatusIndicateStop() ) || ( rinsebackAdditionalTimerCtr >= MAX_RINSEBACK_ADDITIONAL_TIME ) ) { setupForRinsebackStopOrPause(); targetRinsebackVolumePlusAdditional_mL = getDeliveredRinsebackVolume(); state = RINSEBACK_STOP_STATE; } else { // No action required. } return state; } /*********************************************************************//** * @brief * The signalRinsebackAlarmResumeUserAction function signals the rinseback * sub-mode to resume per user alarm action. * @details Inputs: currentRinsebackState * @details Outputs: resumeRinsebackRequested * @return none *************************************************************************/ void signalRinsebackAlarmResumeUserAction( void ) { if ( RINSEBACK_PAUSED_STATE == currentRinsebackState ) { resumeRinsebackRequested = TRUE; } } /*********************************************************************//** * @brief * The signalRinsebackUserAction function signals a rinseback user action * has been requested. * @details Inputs: none * @details Outputs: action handled, request responded to * @param message UI message which has requested user action * @return TRUE if request is accepted, FALSE if rejected. *************************************************************************/ BOOL signalRinsebackUserAction( MESSAGE_T *message ) { BOOL result = FALSE; U32 cmd = 0; REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NONE; UI_RESPONSE_PAYLOAD_T response; if ( sizeof(U32) == message->hdr.payloadLen ) { memcpy( &cmd, message->payload, sizeof(U32) ); // Reject user action requests if any alarm is currently active. User must clear alarm first. if ( FALSE == isAnyAlarmActive() ) { switch ( cmd ) { case REQUESTED_USER_ACTION_RINSEBACK_CONFIRM_START: result = handleStartRinsebackUserAction( &rejReason ); break; case REQUESTED_USER_ACTION_RINSEBACK_INCREASE_RATE: result = handleIncrRinsebackUserAction( &rejReason ); break; case REQUESTED_USER_ACTION_RINSEBACK_DECREASE_RATE: result = handleDecrRinsebackUserAction( &rejReason ); break; case REQUESTED_USER_ACTION_RINSEBACK_PAUSE: result = handlePauseRinsebackUserAction( &rejReason ); break; case REQUESTED_USER_ACTION_RINSEBACK_RESUME: result = handleResumeRinsebackUserAction( &rejReason ); break; case REQUESTED_USER_ACTION_RINSEBACK_ADDITIONAL: result = handleAdditionalRinsebackUserAction( &rejReason ); break; case REQUESTED_USER_ACTION_RINSEBACK_CONFIRM_RECIRCULATE: result = handleRecircUserAction( &rejReason ); break; case REQUESTED_USER_ACTION_RINSEBACK_END_TREATMENT: // Send message to UI to get user confirmation to end treatment - action initiated only upon receipt of user confirmation from UI addConfirmationRequest( GENERIC_CONFIRM_ID_TREATMENT_END, GENERIC_CONFIRM_CMD_REQUEST_OPEN, 0U ); result = TRUE; break; default: rejReason = REQUEST_REJECT_REASON_INVALID_COMMAND; break; } } else { rejReason = REQUEST_REJECT_REASON_ALARM_IS_ACTIVE; } } else { rejReason = REQUEST_REJECT_REASON_INVALID_REQUEST_FORMAT; } // Respond to user action request response.accepted = result; response.rejectionReason = rejReason; sendMessage( MSG_ID_TD_RINSEBACK_CMD_RESPONSE, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08*)&response, sizeof( UI_RESPONSE_PAYLOAD_T ) ); return result; } /*********************************************************************//** * @brief * The handleStartRinsebackUserAction function handles a start rinseback * user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. * @details Inputs: currentRinsebackState * @details Outputs: startRinsebackRequested * @param rejReason code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ static BOOL handleStartRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) { BOOL result = FALSE; if ( RINSEBACK_STOP_INIT_STATE == currentRinsebackState ) { result = TRUE; startRinsebackRequested = TRUE; } else { *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; } return result; } /*********************************************************************//** * @brief * The handleIncrRinsebackUserAction function handles an increase rinseback * flow rate user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. * @details Inputs: currentRinsebackState * @details Outputs: incrRinsebackFlowRateRequested * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ static BOOL handleIncrRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) { BOOL result = FALSE; if ( ( getSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ) + RINSEBACK_FLOW_RATE_ADJ_ML_MIN ) <= MAX_RINSEBACK_FLOW_RATE_ML_MIN ) { if ( RINSEBACK_RUN_STATE == currentRinsebackState ) { result = TRUE; incrRinsebackFlowRateRequested = TRUE; rinsebackPublishTimerCtr = getPublishRinsebackInterval(); } else { *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; } } else { *rejReason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; } return result; } /*********************************************************************//** * @brief * The handleDecrRinsebackUserAction function handles a decrease rinseback * flow rte user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. * @details Inputs: currentRinsebackState * @details Outputs: decrRinsebackFlowRateRequested * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ static BOOL handleDecrRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) { BOOL result = FALSE; if ( ( getSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ) - RINSEBACK_FLOW_RATE_ADJ_ML_MIN ) >= MIN_RINSEBACK_FLOW_RATE_ML_MIN ) { if ( RINSEBACK_RUN_STATE == currentRinsebackState ) { result = TRUE; decrRinsebackFlowRateRequested = TRUE; rinsebackPublishTimerCtr = getPublishRinsebackInterval(); } else { *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; } } else { *rejReason = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; } return result; } /*********************************************************************//** * @brief * The handlePauseRinsebackUserAction function handles a pause rinseback * user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. * @details Inputs: currentRinsebackState * @details Outputs: pauseRinsebackRequested * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ static BOOL handlePauseRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) { BOOL result = FALSE; if ( RINSEBACK_RUN_STATE == currentRinsebackState ) { result = TRUE; pauseRinsebackRequested = TRUE; } else { *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; } return result; } /*********************************************************************//** * @brief * The handleResumeRinsebackUserAction function handles a resume rinseback * user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. * @details Inputs: currentRinsebackState * @details Outputs: resumeRinsebackRequested * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ static BOOL handleResumeRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) { BOOL result = FALSE; if ( RINSEBACK_PAUSED_STATE == currentRinsebackState ) { result = TRUE; resumeRinsebackRequested = TRUE; } else { *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; } return result; } /*********************************************************************//** * @brief * The handleAdditionalRinsebackUserAction function handles an additional rinseback * user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. * @details Inputs: currentRinsebackState * @details Outputs: additionalRinsebackRequested * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ static BOOL handleAdditionalRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) { BOOL result = FALSE; if ( RINSEBACK_STOP_STATE == currentRinsebackState ) { result = TRUE; additionalRinsebackRequested = TRUE; } else { *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; } return result; } /*********************************************************************//** * @brief * The handleToRecircUserAction function handles a go to re-circulate * user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. * @details Inputs: currentRinsebackState * @details Outputs:recircRequested * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ static BOOL handleRecircUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) { BOOL result = FALSE; if ( TRUE == isTreatmentCompleted() ) { *rejReason = REQUEST_REJECT_REASON_TREATMENT_IS_COMPLETED; } else if ( currentRinsebackState != RINSEBACK_STOP_STATE ) { *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; } else if ( TRUE != isRecircAllowed() ) { *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; } else { result = TRUE; recircRequested = TRUE; } return result; } /*********************************************************************//** * @brief * The publishRinsebackData function publishes rinseback progress to UI * at 1 Hz interval. * @details Inputs: rinsebackPublishTimerCtr, rinsebackTimerCtr, targetRinsebackVolumePlusAdditional_mL * rinsebackRate_mL_min * @details Outputs: none * @return none *************************************************************************/ static void publishRinsebackData( void ) { if ( ++rinsebackPublishTimerCtr >= getPublishRinsebackInterval() ) { RINSEBACK_DATA_PAYLOAD_T data; BOOL rinsebackComplete = getRinsebackIsCompleted(); rinsebackPublishTimerCtr = 0; data.targetRinsebackVolumeMl = targetRinsebackVolumePlusAdditional_mL; data.deliveredRinsebackVolumeMl = getDeliveredRinsebackVolume(); data.rinsebackFlowRateMlMin = getSysConfigTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ); data.isCompleted = rinsebackComplete; data.isRecircAllowed = isRecircAllowed(); // Report blood-sitting timeout countdown only while rinseback not completed and blood pump is stopped if ( ( TRUE == rinsebackComplete ) || ( 0 == rinsebackTimerCtr ) ) { data.timeout = 0; data.countdown = 0; } else { U32 countdown = ( rinsebackTimerCtr <= MAX_RINSEBACK_TIME ) ? ( MAX_RINSEBACK_TIME - rinsebackTimerCtr ) : 0; data.timeout = MAX_RINSEBACK_TIME / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ); data.countdown = countdown / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ); } broadcastData( MSG_ID_TD_RINSEBACK_PROGRESS, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)&data, sizeof( RINSEBACK_DATA_PAYLOAD_T ) ); } } /*********************************************************************//** * @brief * The getPublishRinsebackInterval function gets the rinseback data * publication interval. * @details Inputs: rinsebackPublishInterval * @details Outputs: none * @return the current rinseback publication interval (in task intervals). *************************************************************************/ static U32 getPublishRinsebackInterval( void ) { U32 result = rinsebackPublishInterval.data; if ( OVERRIDE_KEY == rinsebackPublishInterval.override ) { result = rinsebackPublishInterval.ovData; } return result; } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testRinsebackVolumeOverride function overrides or resets the cumulative * rinseback volume delivered. * @details \b Inputs: none * @details \b Outputs: cummulativeRinsebackVolume_mL * @param message Override message from Dialin which includes the rinseback * volume (in mL) to override to, or reset command. * @return TRUE if override or reset successful, FALSE if not *************************************************************************/ BOOL testRinsebackVolumeOverride( MESSAGE_T *message ) { BOOL result = FALSE; result = f32Override( message, &cummulativeRinsebackVolume_mL ); return result; } /*********************************************************************//** * @brief * The testRinsebackPublishIntervalOverride function overrides or resets the * rinseback data publication interval. * @details \b Inputs: message * @details \b Outputs: rinsebackPublishInterval * @param message Override message from Dialin which includes the interval * (in ms) to override the rinseback broadcast interval to, or reset command. * @return TRUE if override or reset successful, FALSE if not *************************************************************************/ BOOL testRinsebackPublishIntervalOverride( MESSAGE_T *message ) { BOOL result = FALSE; result = u32BroadcastIntervalOverride( message, &rinsebackPublishInterval, TASK_GENERAL_INTERVAL ); return result; } /**@}*/