/************************************************************************** * * Copyright (c) 2021-2024 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 Rinseback.c * * @author (last) Dara Navaei * @date (last) 05-Mar-2024 * * @author (original) Sean Nash * @date (original) 20-Jan-2021 * ***************************************************************************/ #include "AirTrap.h" #include "BloodFlow.h" #include "BloodPrime.h" #include "DGInterface.h" #include "DialInFlow.h" #include "DialOutFlow.h" #include "ModeTreatment.h" #include "ModeTreatmentParams.h" #include "OperationModes.h" #include "PresOccl.h" #include "Rinseback.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "TreatmentStop.h" #include "Valves.h" /** * @addtogroup Rinseback * @{ */ // ********** private definitions ********** #define DEFAULT_RINSEBACK_VOLUME_ML 350.0F ///< Default rinseback volume (in mL). #define MAX_TOTAL_ADDITIONAL_RINSEBACK_VOLUME_ML 300.0F ///< Maximum total additional rinseback volume allowed : all additionals (in mL). #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 25 ///< Adjustment amount (in mL/min) to apply when user requests increase/decrease in flow rate. #define DEFAULT_RINSEBACK_FLOW_RATE_ML_MIN 200 ///< Default rinseback flow rate (in mL/min). #define MIN_RINSEBACK_FLOW_RATE_ML_MIN 100 ///< Minimum rinseback flow rate (in mL/min). #define MAX_RINSEBACK_FLOW_RATE_ML_MIN 300 ///< 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. static const U32 MAX_RINSEBACK_TIME = ( 5 * SEC_PER_MIN * ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ); /// Maximum time allowed for each additional rinseback volume delivery. static const U32 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. static const F32 RINSEBACK_FLOW_INTEGRATOR = 1.0F / (F32)( SEC_PER_MIN * ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ); // ********** private data ********** static RINSEBACK_STATE_T rinsebackState; ///< Current state of the rinseback sub-mode. static U32 rinsebackRate_mL_min; ///< Rinseback rate to use/adjust for this current rinseback only. static U32 rinsebackTimerCtr; ///< Timer counter for time spent in rinseback sub-mode. static F32 rinsebackTargetVolume_mL; ///< Calculated target rinseback volume (based on selected dialyzer and fixed tubing volume). static OVERRIDE_F32_T cumulativeRinsebackVolume_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. static F32 totalAdditionalRinsebackVolume_mL; ///< Total accumulated volume (in mL) delivered so far for all additional volumes combined. 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. /// 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 indicates user requesting rinseback start (confirming saline bag move to arterial line end). static BOOL incrRinsebackFlowRateRequested; ///< Flag indicates user requesting rinseback flow rate be increased. static BOOL decrRinsebackFlowRateRequested; ///< Flag indicates user requesting rinseback flow rate be decreased. static BOOL pauseRinsebackRequested; ///< Flag indicates user requesting rinseback pause. static BOOL resumeRinsebackRequested; ///< Flag indicates user requesting rinseback resume. static BOOL endRinsebackRequested; ///< Flag indicates user requesting to end rinseback operation. static BOOL additionalRinsebackRequested; ///< Flag indicates user requesting a little more rinseback volume. static BOOL recircRequested; ///< Flag indicates user requesting to begin re-circulation (confirming disconnection and shunt). static BOOL backToTreatmentRequested; ///< Flag indicates user requesting to go back to treatment (confirming still connected). static BOOL endTreatmentRequested; ///< Flag indicates user requesting to end treatment. // ********** private function prototypes ********** static void resetRinsebackFlags( void ); static void setupForRinsebackDelivery( U32 rate ); static void setupForRinsebackStopOrPause( void ); static F32 getRinsebackVolume( 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 handleRinsebackRunAdditionalState( void ); static RINSEBACK_STATE_T handleRinsebackReconnectPatientState( 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 handleEndRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); static BOOL handleAdditionalRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); static BOOL handleToRecircUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); static BOOL handleBackToTreatmentUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ); static void publishRinsebackData( void ); static U32 getPublishRinsebackInterval( void ); static void handleDialysateRecircOnOff( 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 ) { rinsebackState = RINSEBACK_STOP_INIT_STATE; rinsebackRate_mL_min = getTreatmentParameterU32( TREATMENT_PARAM_RINSEBACK_FLOW_RATE ); rinsebackTargetVolume_mL = DEFAULT_RINSEBACK_VOLUME_ML; targetRinsebackVolumePlusAdditional_mL = rinsebackTargetVolume_mL; rinsebackTimerCtr = 0; cumulativeRinsebackVolume_mL.data = 0.0; additionalRinsebackVolume_mL = 0.0; totalAdditionalRinsebackVolume_mL = 0.0; 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; endRinsebackRequested = FALSE; additionalRinsebackRequested = FALSE; recircRequested = FALSE; backToTreatmentRequested = FALSE; endTreatmentRequested = 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)rinsebackState ); doorClosedRequired( TRUE, TRUE ); // Set user alarm recovery actions allowed in this sub-mode setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, FALSE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, FALSE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, FALSE ); // Set valves to safe state setValvePosition( VDI, VALVE_POSITION_C_CLOSE ); setValvePosition( VDO, VALVE_POSITION_C_CLOSE ); setValvePosition( VBA, VALVE_POSITION_C_CLOSE ); setValvePosition( VBV, VALVE_POSITION_C_CLOSE ); // Ensure all pumps except DPi stopped signalBloodPumpHardStop(); signalDialOutPumpHardStop(); stopSyringePump(); endAirTrapControl(); // if DG restarted for some reason, lets make sure we set the heating parameters first before commanding to // turn on the trimmer heater and start the DG as well. if ( DG_MODE_STAN == getDGOpMode() ) { // set the trimmer heating parameters setDefaultTreatmentHeatingParams(); // start the DG cmdStartDG(); } // Re-circulate dialysate side of dialyzer w/ heating to maintain temperature setDialInPumpTargetFlowRate( DIALYSATE_FLOW_RATE_FOR_RECIRC, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); cmdStartDGTrimmerHeater(); } /*********************************************************************//** * @brief * The setupForRinsebackDelivery function sets actuators appropriately * for rinseback states where delivery is in progress. * @details Inputs: none * @details Outputs: arterial and venous lines opened, blood pump started, * and air trap levelin control is started. * @param rate rinseback rate to use for blood pump * @return none *************************************************************************/ static void setupForRinsebackDelivery( U32 rate ) { // Open VBA and VBV valves to allow flow from saline bag and to patient venous line setValvePosition( VBA, VALVE_POSITION_B_OPEN ); // patient will have removed arterial line from fistula and connected it to RB port to saline - so we will get saline from arterial line by opening setValvePosition( VBV, VALVE_POSITION_B_OPEN ); // return to patient // Start blood pump at rinseback flow rate setBloodPumpTargetFlowRate( rate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); // Start air trap leveling control startAirTrapControl(); // Re-circulate dialysate side of dialyzer w/ heating to maintain temperature setDialInPumpTargetFlowRate( DIALYSATE_FLOW_RATE_FOR_RECIRC, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); cmdStartDGTrimmerHeater(); } /*********************************************************************//** * @brief * The setupForRinsebackStopOrPause function sets actuators appropriately * for rinseback states where delivery is paused or stopped. * @details Inputs: none * @details Outputs: Blood pump stopped, arterial and venous lines closed, * and air trap leveling control is stopped. * @return none *************************************************************************/ static void setupForRinsebackStopOrPause( void ) { // Stop blood pump signalBloodPumpHardStop(); // Close arterial and venous lines setValvePosition( VBA, VALVE_POSITION_C_CLOSE ); setValvePosition( VBV, VALVE_POSITION_C_CLOSE ); // Stop air trap leveling control endAirTrapControl(); } /*********************************************************************//** * @brief * The getRinsebackVolume function gets the calculated blood prime volume * delivered. * @details Inputs: cumulativeRinsebackVolume_mL * @details Outputs: none * @return the current rinseback volume delivered (in mL). *************************************************************************/ static F32 getRinsebackVolume( void ) { F32 result = cumulativeRinsebackVolume_mL.data; if ( OVERRIDE_KEY == cumulativeRinsebackVolume_mL.override ) { result = cumulativeRinsebackVolume_mL.ovData; } return result; } /*********************************************************************//** * @brief * The getCurrentRinsebackState function returns the current state of the * rinseback sub-mode. * @details Inputs: rinsebackState * @details Outputs: none * @return rinsebackState *************************************************************************/ RINSEBACK_STATE_T getCurrentRinsebackState( void ) { return rinsebackState; } /*********************************************************************//** * @brief * The execRinseback function executes the Rinseback sub-mode state machine. * @details Inputs: rinsebackState * @details Outputs: rinsebackState, rinsebackTimerCtr, endTreatmentRequested, flags * @return none *************************************************************************/ void execRinseback( void ) { RINSEBACK_STATE_T priorSubState = rinsebackState; rinsebackTimerCtr++; switch ( rinsebackState ) { case RINSEBACK_STOP_INIT_STATE: rinsebackState = handleRinsebackStopInitState(); break; case RINSEBACK_RUN_STATE: rinsebackState = handleRinsebackRunState(); break; case RINSEBACK_PAUSED_STATE: rinsebackState = handleRinsebackPausedState(); break; case RINSEBACK_STOP_STATE: rinsebackState = handleRinsebackStoppedState(); break; case RINSEBACK_RUN_ADDITIONAL_STATE: rinsebackState = handleRinsebackRunAdditionalState(); break; case RINSEBACK_RECONNECT_PATIENT_STATE: rinsebackState = handleRinsebackReconnectPatientState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_RINSEBACK_INVALID_STATE, rinsebackState ); break; } // Start/stop dialysate recirculation during rinseback mode based on active alarms that would block it handleDialysateRecircOnOff(); if ( priorSubState != rinsebackState ) { setCurrentSubState( (U32)rinsebackState ); SEND_EVENT_WITH_2_U32_DATA( HD_EVENT_SUB_STATE_CHANGE, priorSubState, rinsebackState ); } // 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 Inputs: flags * @details Outputs: flags handled * @return next rinseback state *************************************************************************/ static RINSEBACK_STATE_T handleRinsebackStopInitState( void ) { RINSEBACK_STATE_T result = RINSEBACK_STOP_INIT_STATE; // Has user requested rinseback start? if ( TRUE == startRinsebackRequested ) { setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, TRUE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, FALSE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, TRUE ); setupForRinsebackDelivery( rinsebackRate_mL_min ); // From moment we start rinseback, we consider blood side of dialyzer no longer fully primed setBloodIsPrimed( FALSE ); result = RINSEBACK_RUN_STATE; } // Has user requested to return to treatment? else if ( TRUE == backToTreatmentRequested ) { signalGoToTreatmentStopped(); } // Has user requested to end the treatment? else if ( TRUE == endTreatmentRequested ) { signalEndTreatment(); } // Has rinseback operation exceeded max time? else if ( rinsebackTimerCtr > MAX_RINSEBACK_TIME ) { signalGoToTreatmentStopped(); activateAlarmNoData( ALARM_ID_HD_TREATMENT_RINSEBACK_TIMEOUT_ALARM ); } return result; } /*********************************************************************//** * @brief * The handleRinsebackRunState function handles the rinseback run state * operations. * @details Inputs: flags * @details Outputs: cumulativeRinsebackVolume_mL, flags handled * @return next rinseback state *************************************************************************/ static RINSEBACK_STATE_T handleRinsebackRunState( void ) { RINSEBACK_STATE_T result = RINSEBACK_RUN_STATE; // Reset rinseback t/o rinsebackTimerCtr = 0; // Update rinseback volume delivered so far cumulativeRinsebackVolume_mL.data += ( getMeasuredBloodFlowRate() * RINSEBACK_FLOW_INTEGRATOR ); // Has user requested to end rinseback? if ( TRUE == endRinsebackRequested ) { setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, FALSE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, FALSE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, FALSE ); setupForRinsebackStopOrPause(); targetRinsebackVolumePlusAdditional_mL = getRinsebackVolume(); result = RINSEBACK_STOP_STATE; } // Has rinseback completed? else if ( getRinsebackVolume() >= rinsebackTargetVolume_mL ) { setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, FALSE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, FALSE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, FALSE ); setRinsebackIsCompleted( TRUE ); setupForRinsebackStopOrPause(); targetRinsebackVolumePlusAdditional_mL = getRinsebackVolume(); result = RINSEBACK_STOP_STATE; } // Check for empty saline bag else if ( TRUE == isSalineBagEmpty() ) { SET_ALARM_WITH_1_F32_DATA( ALARM_ID_HD_EMPTY_SALINE_BAG, getMeasuredArterialPressure() ); setupForRinsebackStopOrPause(); result = RINSEBACK_PAUSED_STATE; } // Has user or alarm requested rinseback pause? else if ( ( TRUE == pauseRinsebackRequested ) || ( TRUE == doesAlarmStatusIndicateStop() ) ) { setupForRinsebackStopOrPause(); result = RINSEBACK_PAUSED_STATE; } // Otherwise, continue rinseback else { // Has user requested rate change? if ( ( TRUE == incrRinsebackFlowRateRequested ) || ( TRUE == decrRinsebackFlowRateRequested ) ) { U32 rbRate = rinsebackRate_mL_min; if ( TRUE == incrRinsebackFlowRateRequested ) { rbRate += RINSEBACK_FLOW_RATE_ADJ_ML_MIN; } else { rbRate -= RINSEBACK_FLOW_RATE_ADJ_ML_MIN; } incrRinsebackFlowRateRequested = FALSE; decrRinsebackFlowRateRequested = FALSE; if ( ( rbRate >= MIN_RINSEBACK_FLOW_RATE_ML_MIN ) && ( rbRate <= MAX_RINSEBACK_FLOW_RATE_ML_MIN ) ) { rinsebackRate_mL_min = rbRate; setBloodPumpTargetFlowRate( rinsebackRate_mL_min, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); } } } return result; } /*********************************************************************//** * @brief * The handleRinsebackPausedState function handles the rinseback paused * state operations. * @details Inputs: rinsebackTimerCtr, resumeRinsebackRequested, * endTreatmentRequested, endRinsebackRequested, rinsebackRate_mL_min * @details Outputs: targetRinsebackVolumePlusAdditional_mL * @return next rinseback state *************************************************************************/ static RINSEBACK_STATE_T handleRinsebackPausedState( void ) { RINSEBACK_STATE_T result = RINSEBACK_PAUSED_STATE; // Has rinseback operation exceeded max time? if ( rinsebackTimerCtr > MAX_RINSEBACK_TIME ) { signalGoToTreatmentStopped(); activateAlarmNoData( ALARM_ID_HD_TREATMENT_RINSEBACK_TIMEOUT_ALARM ); } else if ( TRUE == resumeRinsebackRequested ) { setupForRinsebackDelivery( rinsebackRate_mL_min ); result = RINSEBACK_RUN_STATE; } else if ( TRUE == endTreatmentRequested ) { signalEndTreatment(); } // Has user requested to end rinseback? else if ( TRUE == endRinsebackRequested ) { setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, FALSE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, FALSE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, FALSE ); targetRinsebackVolumePlusAdditional_mL = getRinsebackVolume(); result = RINSEBACK_STOP_STATE; } return result; } /*********************************************************************//** * @brief * The handleRinsebackStoppedState function handles the stopped rinseback * state operations. * @details Inputs: rinsebackTimerCtr, recircRequested, additionalRinsebackRequested, * backToTreatmentRequested, endTreatmentRequested * @details Outputs: additionalRinsebackRequested, rinsebackAdditionalTimerCtr, * additionalRinsebackVolume_mL, targetRinsebackVolumePlusAdditional_mL * @return next rinseback state *************************************************************************/ static RINSEBACK_STATE_T handleRinsebackStoppedState( void ) { RINSEBACK_STATE_T result = 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 ) && ( getRinsebackVolume() < rinsebackTargetVolume_mL ) ) { signalGoToTreatmentStopped(); SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_TREATMENT_RINSEBACK_TIMEOUT_ALARM, rinsebackTimerCtr, MAX_RINSEBACK_TIME ); } else if ( TRUE == recircRequested ) { signalRinsebackToRecirc(); } else if ( TRUE == additionalRinsebackRequested ) { additionalRinsebackRequested = FALSE; // deliver additional rinseback volume only if max time not reached or max volume has been reached (i.e. no more blood in line) if ( ( rinsebackTimerCtr < MAX_RINSEBACK_TIME ) || ( getRinsebackVolume() >= rinsebackTargetVolume_mL ) ) { rinsebackAdditionalTimerCtr = 0; additionalRinsebackVolume_mL = 0.0; targetRinsebackVolumePlusAdditional_mL += TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML; rinsebackRate_mL_min = DEFAULT_RINSEBACK_FLOW_RATE_ML_MIN; setupForRinsebackDelivery( rinsebackRate_mL_min ); result = RINSEBACK_RUN_ADDITIONAL_STATE; } } else if ( TRUE == backToTreatmentRequested ) { result = RINSEBACK_RECONNECT_PATIENT_STATE; } else if ( TRUE == endTreatmentRequested ) { signalEndTreatment(); } return result; } /*********************************************************************//** * @brief * The handleRinsebackRunAdditionalState function handles the rinseback additional * state operations. * @details Inputs: flags * @details Outputs: flags handled * @return next rinseback state *************************************************************************/ static RINSEBACK_STATE_T handleRinsebackRunAdditionalState( void ) { RINSEBACK_STATE_T result = RINSEBACK_RUN_ADDITIONAL_STATE; F32 rinsebackVolumeSinceLast = ( getMeasuredBloodFlowRate() * RINSEBACK_FLOW_INTEGRATOR ); // Reset rinseback t/o rinsebackTimerCtr = 0; // update additional rinseback volume delivered so far additionalRinsebackVolume_mL += rinsebackVolumeSinceLast; totalAdditionalRinsebackVolume_mL += rinsebackVolumeSinceLast; cumulativeRinsebackVolume_mL.data += rinsebackVolumeSinceLast; rinsebackAdditionalTimerCtr++; // Check for empty saline bag if ( TRUE == isSalineBagEmpty() ) { SET_ALARM_WITH_1_F32_DATA( ALARM_ID_HD_EMPTY_SALINE_BAG, getMeasuredArterialPressure() ); targetRinsebackVolumePlusAdditional_mL = getRinsebackVolume(); result = RINSEBACK_STOP_STATE; } // Has additional rinseback completed or max additional volume reached else if ( ( additionalRinsebackVolume_mL >= TARGET_ADDITIONAL_RINSEBACK_VOLUME_ML ) || ( totalAdditionalRinsebackVolume_mL >= MAX_TOTAL_ADDITIONAL_RINSEBACK_VOLUME_ML ) ) { result = RINSEBACK_STOP_STATE; } // Has additional rinseback timed out? else if ( rinsebackAdditionalTimerCtr >= MAX_RINSEBACK_ADDITIONAL_TIME ) { targetRinsebackVolumePlusAdditional_mL = getRinsebackVolume(); result = RINSEBACK_STOP_STATE; } // Has alarm requested stop? else if ( TRUE == doesAlarmStatusIndicateStop() ) { targetRinsebackVolumePlusAdditional_mL = getRinsebackVolume(); result = RINSEBACK_STOP_STATE; } // Setup for rinseback stop state if additional rinseback completed or ending. if ( RINSEBACK_STOP_STATE == result ) { setupForRinsebackStopOrPause(); // If this additional rinseback volume has brought us to "full" rinseback volume, set flag if ( getRinsebackVolume() >= rinsebackTargetVolume_mL ) { setRinsebackIsCompleted( TRUE ); } } return result; } /*********************************************************************//** * @brief * The handleRinsebackReconnectPatientState function handles the rinseback * reconnect patient state operations. * @details Inputs: flags * @details Outputs: flags handled * @return next rinseback state *************************************************************************/ static RINSEBACK_STATE_T handleRinsebackReconnectPatientState( void ) { RINSEBACK_STATE_T result = RINSEBACK_RECONNECT_PATIENT_STATE; if ( TRUE == resumeRinsebackRequested ) { result = RINSEBACK_STOP_STATE; } else if ( TRUE == backToTreatmentRequested ) { signalGoToTreatmentStopped(); } else if ( TRUE == endTreatmentRequested ) { signalEndTreatment(); } return result; } /*********************************************************************//** * @brief * The signalRinsebackAlarmResumeUserAction function signals the rinseback * sub-mode to resume per user alarm action. * @details Inputs: none * @details Outputs: resumeRinsebackRequested * @return none *************************************************************************/ void signalRinsebackAlarmResumeUserAction( void ) { resumeRinsebackRequested = TRUE; } /*********************************************************************//** * @brief * The signalRinsebackUserAction function signals a rinseback user action * has been requested. The request is handled and responded to. * @details Inputs: none * @details Outputs: action handled, request responded to * @param action User action requested * @return none *************************************************************************/ void signalRinsebackUserAction( REQUESTED_RINSEBACK_USER_ACTIONS_T action ) { BOOL accepted = FALSE; REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NONE; // Reject user action requests if any alarm is currently active. User must clear alarm first. if ( FALSE == isAnyAlarmActive() ) { switch ( action ) { case REQUESTED_USER_ACTION_RINSEBACK_CONFIRM_START: accepted = handleStartRinsebackUserAction( &rejReason ); break; case REQUESTED_USER_ACTION_RINSEBACK_INCREASE_RATE: accepted = handleIncrRinsebackUserAction( &rejReason ); break; case REQUESTED_USER_ACTION_RINSEBACK_DECREASE_RATE: accepted = handleDecrRinsebackUserAction( &rejReason ); break; case REQUESTED_USER_ACTION_RINSEBACK_PAUSE: accepted = handlePauseRinsebackUserAction( &rejReason ); break; case REQUESTED_USER_ACTION_RINSEBACK_RESUME: accepted = handleResumeRinsebackUserAction( &rejReason ); break; case REQUESTED_USER_ACTION_RINSEBACK_END: accepted = handleEndRinsebackUserAction( &rejReason ); break; case REQUESTED_USER_ACTION_RINSEBACK_ADDITIONAL: accepted = handleAdditionalRinsebackUserAction( &rejReason ); break; case REQUESTED_USER_ACTION_RINSEBACK_CONFIRM_DISCONNECT: accepted = handleToRecircUserAction( &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, 0 ); accepted = TRUE; break; case REQUESTED_USER_ACTION_RINSEBACK_BACK_TO_TREATMENT: accepted = handleBackToTreatmentUserAction( &rejReason ); break; default: rejReason = REQUEST_REJECT_REASON_INVALID_COMMAND; break; } } else { rejReason = REQUEST_REJECT_REASON_ALARM_IS_ACTIVE; } // Respond to user action request sendRinsebackCmdResponse( accepted, (U32)rejReason ); } /*********************************************************************//** * @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: rinsebackState * @details Outputs: rinseback started if appropriate * @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 == rinsebackState ) { 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: rinsebackState * @details Outputs: rinseback rate increased if appropriate * @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 ( ( rinsebackRate_mL_min + RINSEBACK_FLOW_RATE_ADJ_ML_MIN ) <= MAX_RINSEBACK_FLOW_RATE_ML_MIN ) { if ( RINSEBACK_RUN_STATE == rinsebackState ) { 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: rinsebackState * @details Outputs: rinseback decreased if appropriate * @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 ( ( rinsebackRate_mL_min - RINSEBACK_FLOW_RATE_ADJ_ML_MIN ) >= MIN_RINSEBACK_FLOW_RATE_ML_MIN ) { if ( RINSEBACK_RUN_STATE == rinsebackState ) { 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: rinsebackState * @details Outputs: rinseback paused if appropriate * @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 == rinsebackState ) { 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: rinsebackState * @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 == rinsebackState ) { result = TRUE; resumeRinsebackRequested = TRUE; } else if ( RINSEBACK_RECONNECT_PATIENT_STATE == rinsebackState ) { result = TRUE; resumeRinsebackRequested = TRUE; } else { *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; } return result; } /*********************************************************************//** * @brief * The handleEndRinsebackUserAction function handles an end rinseback * user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. * @details Inputs: rinsebackState * @details Outputs: rinseback ended if appropriate * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ static BOOL handleEndRinsebackUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) { BOOL result = FALSE; if ( ( RINSEBACK_RUN_STATE == rinsebackState ) || ( RINSEBACK_PAUSED_STATE == rinsebackState ) ) { result = TRUE; endRinsebackRequested = 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: rinsebackState * @details Outputs: additional rinseback volume started if appropriate * @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 == rinsebackState ) { if ( totalAdditionalRinsebackVolume_mL < MAX_TOTAL_ADDITIONAL_RINSEBACK_VOLUME_ML ) { result = TRUE; additionalRinsebackRequested = TRUE; } else { *rejReason = REQUEST_REJECT_REASON_ADDL_RINSEBACK_MAX_VOLUME_REACHED; } } 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: rinsebackState * @details Outputs: transition to re-circulate if appropriate * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ static BOOL handleToRecircUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) { BOOL result = FALSE; if ( TRUE == isTreatmentCompleted() ) { *rejReason = REQUEST_REJECT_REASON_TREATMENT_IS_COMPLETED; } else if ( rinsebackState != RINSEBACK_STOP_STATE ) { *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; } else if ( getRinsebackVolume() < rinsebackTargetVolume_mL ) { *rejReason = REQUEST_REJECT_REASON_RINSEBACK_NOT_COMPLETED; } else { result = TRUE; recircRequested = TRUE; } return result; } /*********************************************************************//** * @brief * The handleBackToTreatmentUserAction function handles a back to treatment * user action request. It is assumed that the calling function will set * the reject reason parameter to None beforehand. * @details Inputs: rinsebackState * @details Outputs: return to treatment if appropriate * @param rejReason Code indicating reason for rejection * @return TRUE if user action accepted, FALSE if not *************************************************************************/ static BOOL handleBackToTreatmentUserAction( REQUEST_REJECT_REASON_CODE_T *rejReason ) { BOOL result = FALSE; if ( TRUE == isTreatmentCompleted() ) { *rejReason = REQUEST_REJECT_REASON_TREATMENT_IS_COMPLETED; } else if ( TRUE == isTreatmentResumeBlocked() ) { *rejReason = REQUEST_REJECT_REASON_TREATMENT_CANNOT_BE_RESUMED; } else if ( ( RINSEBACK_STOP_INIT_STATE != rinsebackState ) && ( RINSEBACK_STOP_STATE != rinsebackState ) && ( RINSEBACK_RECONNECT_PATIENT_STATE != rinsebackState ) ) { *rejReason = REQUEST_REJECT_REASON_ACTION_DISABLED_IN_CURRENT_STATE; } else { result = TRUE; backToTreatmentRequested = TRUE; } return result; } /*********************************************************************//** * @brief * The publishRinsebackData function publishes rinseback progress to UI * at 1 Hz interval. * @details Inputs: rinsebackPublishTimerCtr, rinsebackTimerCtr, rinsebackState, * cumulativeRinsebackVolume_mL, rinsebackRate_mL_min * @details Outputs: rinseback data published * @return none *************************************************************************/ static void publishRinsebackData( void ) { if ( ++rinsebackPublishTimerCtr >= getPublishRinsebackInterval() ) { RINSEBACK_DATA_PAYLOAD_T data; TREATMENT_STOP_PAYLOAD_T sdata; U32 timeout = MAX_RINSEBACK_TIME / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ); U32 countdown = ( getRinsebackVolume() >= rinsebackTargetVolume_mL ? 0 : MAX_RINSEBACK_TIME ); U32 stopTO = ( getRinsebackVolume() >= rinsebackTargetVolume_mL ? 0 : MAX_RINSEBACK_TIME ); countdown -= rinsebackTimerCtr; // Handle countdown past zero if ( countdown > MAX_RINSEBACK_TIME ) { countdown = 0; } // Scale to seconds countdown /= ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ); data.isCompleted = FALSE; data.targetRinsebackVolumeMl = rinsebackTargetVolume_mL; rinsebackPublishTimerCtr = 0; // If we have completed rinseback, timeout is no longer in force - indicate by zeroing timeout. Also include any additionals to target. data.timeout = 0; data.countdown = 0; if ( ( rinsebackState > RINSEBACK_PAUSED_STATE ) && ( getRinsebackVolume() >= rinsebackTargetVolume_mL ) ) { data.targetRinsebackVolumeMl = targetRinsebackVolumePlusAdditional_mL; } data.deliveredRinsebackVolumeMl = getRinsebackVolume(); data.rinsebackFlowRateMlMin = rinsebackRate_mL_min; if ( RINSEBACK_RUN_ADDITIONAL_STATE == rinsebackState ) { data.rinsebackFlowRateMlMin = DEFAULT_RINSEBACK_FLOW_RATE_ML_MIN; } if ( getRinsebackCompleted() != TRUE ) { if ( rinsebackTimerCtr > 0 ) // Timer set to zero when BP is running { data.timeout = timeout; data.countdown = countdown; } } if ( data.deliveredRinsebackVolumeMl >= rinsebackTargetVolume_mL ) { data.isCompleted = TRUE; } broadcastData( MSG_ID_HD_RINSEBACK_PROGRESS, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&data, sizeof( RINSEBACK_DATA_PAYLOAD_T ) ); // Send blood sitting count down to UI for alarm dialog sdata.timeout = stopTO / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ); sdata.countdown = 0; if ( getRinsebackCompleted() != TRUE ) { if ( rinsebackTimerCtr > 0 ) { sdata.countdown = ( rinsebackTimerCtr > MAX_TIME_BLOOD_SITTING ? 0 : ( MAX_TIME_BLOOD_SITTING - rinsebackTimerCtr ) / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ); } } broadcastData( MSG_ID_HD_TREATMENT_STOP_TIMER_DATA, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&sdata, sizeof( TREATMENT_STOP_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; } /*********************************************************************//** * @brief * The handleDialysateRecircOnOff function starts/stops dialysate recirculation * during rinseback sub-mode based on active alarm properties. * @details Inputs: none * @details Outputs: dialysate recirculation started or stopped as appropriate * @return none *************************************************************************/ static void handleDialysateRecircOnOff( void ) { // Stop dialysate recirc if blocked by alarm if ( ( TRUE == isDialysateRecircBlocked() ) && ( TRUE == isDialInPumpRunning() ) ) { signalDialInPumpHardStop(); cmdStopDGTrimmerHeater(); } else if ( ( isDialysateRecircBlocked() != TRUE ) && ( isDialInPumpRunning() != TRUE ) ) { setDialInPumpTargetFlowRate( DIALYSATE_FLOW_RATE_FOR_RECIRC, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); cmdStartDGTrimmerHeater(); } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetRinsebackVolumeOverride function overrides the calculated * rinseback volume delivered. * @details Inputs: none * @details Outputs: cumulativeRinsebackVolume_mL * @param vol override calculated rinseback volume (in mL) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetRinsebackVolumeOverride( F32 vol ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; cumulativeRinsebackVolume_mL.ovData = vol; cumulativeRinsebackVolume_mL.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetRinsebackVolumeOverride function resets the override of the * calculated rinseback volume. * @details Inputs: none * @details Outputs: cumulativeRinsebackVolume_mL * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetRinsebackVolumeOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; cumulativeRinsebackVolume_mL.override = OVERRIDE_RESET; cumulativeRinsebackVolume_mL.ovData = cumulativeRinsebackVolume_mL.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetRinsebackPublishIntervalOverride function sets the override of the * rinseback data publication interval. * @details Inputs: none * @details Outputs: rinsebackPublishInterval * @param ms milliseconds between rinseback broadcasts * @return TRUE if override set successful, FALSE if not *************************************************************************/ BOOL testSetRinsebackPublishIntervalOverride( U32 ms ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = ms / TASK_GENERAL_INTERVAL; result = TRUE; rinsebackPublishInterval.ovData = intvl; rinsebackPublishInterval.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetRinsebackPublishIntervalOverride function resets the override of the * rinseback data publication interval. * @details Inputs: none * @details Outputs: rinsebackPublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetRinsebackPublishIntervalOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; rinsebackPublishInterval.override = OVERRIDE_RESET; rinsebackPublishInterval.ovData = rinsebackPublishInterval.ovInitData; } return result; } /**@}*/