/************************************************************************** * * Copyright (c) 2020-2023 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 TreatmentStop.c * * @author (last) Michael Garthwaite * @date (last) 15-Jun-2023 * * @author (original) Sean * @date (original) 15-Jan-2020 * ***************************************************************************/ #include "AirTrap.h" #include "BloodFlow.h" #include "BloodLeak.h" #include "DGInterface.h" #include "DialInFlow.h" #include "DialOutFlow.h" #include "ModeTreatment.h" #include "OperationModes.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "TreatmentStop.h" #include "Valves.h" /** * @addtogroup TreatmentStop * @{ */ // ********** private definitions ********** /// Treatment stop status broadcast interval. #define TREATMENT_STOP_DATA_PUBLISH_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) // ********** private data ********** static TREATMENT_STOP_STATE_T currentTxStopState; ///< Current treatment stop state. static U32 bloodSittingTimerCtr; ///< Timer counter tracks time in this mode while blood is sitting. static U32 stopPublishTimerCtr; ///< Timer counter (in GP task intervals) counts time to next status broadcast. /// Interval (in task intervals) at which to publish treatment stop sub-mode data to CAN bus. static OVERRIDE_U32_T treatmentStopPublishInterval = { TREATMENT_STOP_DATA_PUBLISH_INTERVAL, TREATMENT_STOP_DATA_PUBLISH_INTERVAL, TREATMENT_STOP_DATA_PUBLISH_INTERVAL, 0 }; // ********** private function prototypes ********** static TREATMENT_STOP_STATE_T handleTreatmentStopRecircState( void ); static TREATMENT_STOP_STATE_T handleTreatmentStopNoRecircState( void ); static void setupForDialysateRecirculationState( void ); static void setupForBloodRecirculationState( void ); static TREATMENT_STOP_STATE_T handleTreatmentStopAlarmsAndSignals( TREATMENT_STOP_STATE_T state ); static TREATMENT_STOP_STATE_T handleTreatmentStopDialysateRecircState( void ); static TREATMENT_STOP_STATE_T handleTreatmentStopBloodRecircState( void ); static void handleTreatmentStopBloodSittingTimer( void ); static void publishTreatmentStopData( void ); /*********************************************************************//** * @brief * The initTreatmentStop function initializes the Treatment Stop sub-mode * module. * @details Inputs: none * @details Outputs: Treatment Stop sub-mode module initialized. * @return none *************************************************************************/ void initTreatmentStop( void ) { currentTxStopState = TREATMENT_STOP_RECIRC_STATE; // Assume blood and dialysate will recirculate initially - will stop pump(s) as appropriate in state machine bloodSittingTimerCtr = 0; stopPublishTimerCtr = TREATMENT_STOP_DATA_PUBLISH_INTERVAL; } /*********************************************************************//** * @brief * The transitionToTreatmentStop function prepares for transition to treatment * stop sub-mode. * @details Inputs: none * @details Outputs: none * @return none *************************************************************************/ void transitionToTreatmentStop( void ) { initTreatmentStop(); setCurrentSubState( (U32)currentTxStopState ); setCurrent4thLevelState( NO_SUB_STATE ); // Set user alarm recovery actions allowed in this sub-mode setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, TRUE ); if ( TRUE == getRinsebackCompleted() ) { // block rinseback action if already done setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, FALSE ); } else { setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, TRUE ); } setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, TRUE ); // Set Dialysate valves to bypass filter for recirculation setValvePosition( VDI, VALVE_POSITION_C_CLOSE ); setValvePosition( VDO, VALVE_POSITION_C_CLOSE ); setupForBloodRecirculationState(); setupForDialysateRecirculationState(); signalUserRateChange(); // so pressure limits re-stabilize // Reset saline bolus state in case alarm interrupted one resetSalineBolus(); // Enable venous bubble detection (in case returning from mid-treatment rinseback) setVenousBubbleDetectionEnabled( TRUE ); // Should always have an alarm active in treatment stop sub-mode so that user can take action if ( FALSE == isAnyAlarmActive() ) { if ( TRUE == getRinsebackCompleted() ) { activateAlarmNoData( ALARM_ID_HD_TREATMENT_STOPPED_AFTER_RINSEBACK ); // No escalation after rinseback because no blood in blood line } else { activateAlarmNoData( ALARM_ID_HD_TREATMENT_STOPPED_BY_USER ); } // coming back to stop state via non-alarm path, so no audio - just want alarm for its options signalAlarmSilence( ALARM_SILENCE_CMD_CANCEL ); } } /*********************************************************************//** * @brief * The setupForDialysateRecirculationState function sets actuators appropriately * for Dialysate recirculation state. * @details Inputs: none * @details Outputs: arterial and venous lines opened, blood pump started, * and air trap leveling control is started. * @return none *************************************************************************/ static void setupForDialysateRecirculationState( void ) { // Re-circulate dialysate side of dialyzer w/ heating to maintain temperature doorClosedRequired( TRUE, TRUE ); setDialInPumpTargetFlowRate( DIALYSATE_FLOW_RATE_FOR_RECIRC, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); cmdStartDGTrimmerHeater(); } /*********************************************************************//** * @brief * The setupForBloodRecirculationState function sets actuators appropriately * for Blood recirculation state. * @details Inputs: none * @details Outputs: arterial and venous lines closed, blood pump started, * and air trap leveling control is started. * @return none *************************************************************************/ static void setupForBloodRecirculationState( void ) { PUMP_CONTROL_MODE_T mode = PUMP_CONTROL_MODE_CLOSED_LOOP; #ifndef _RELEASE_ if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_ENABLE_BLOOD_PUMP_OPEN_LOOP ) ) { mode = PUMP_CONTROL_MODE_OPEN_LOOP; } #endif doorClosedRequired( TRUE, TRUE ); // Open VBA and VBV valves to patient for blood recirculation setValvePosition( VBA, VALVE_POSITION_B_OPEN ); setValvePosition( VBV, VALVE_POSITION_B_OPEN ); // Start blood pump at prescribed flow rate setBloodPumpTargetFlowRate( getTreatmentParameterU32( TREATMENT_PARAM_BLOOD_FLOW ), MOTOR_DIR_FORWARD, mode ); // Start Heparin pump as appropriate startHeparinPump(); // Start air trap leveling control startAirTrapControl(); // Reset blood sitting timer, it is now circulating bloodSittingTimerCtr = 0; } /*********************************************************************//** * @brief * The setupForDialysateRecirculationStopState function sets actuators appropriately * for dialysate re-circulation stopped state. * @details Inputs: none * @details Outputs: Blood pump stopped, arterial and venous lines closed, * and air trap leveling control is stopped. * @return none *************************************************************************/ static void setupForDialysateRecirculationStopState( void ) { // Stop dialysate side of dialyzer and heating signalDialInPumpHardStop(); cmdStopDGTrimmerHeater(); } /*********************************************************************//** * @brief * The setupForBloodRecirculationStopState function sets actuators appropriately * for blood re-circulation stopped state. * @details Inputs: none * @details Outputs: Blood pump stopped, arterial and venous lines closed, * and air trap leveling control is stopped. * @return none *************************************************************************/ static void setupForBloodRecirculationStopState( void ) { // Stop blood pump signalBloodPumpHardStop(); // Stop syringe pump stopSyringePump(); // 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 execTreatmentStop function executes the Treatment Stop sub-mode * state machine. * @details Inputs: none * @details Outputs: none * @return none *************************************************************************/ void execTreatmentStop( void ) { TREATMENT_STOP_STATE_T priorSubState = currentTxStopState; switch ( currentTxStopState ) { case TREATMENT_STOP_RECIRC_STATE: currentTxStopState = handleTreatmentStopRecircState(); break; case TREATMENT_STOP_RECIRC_DIALYSATE_ONLY_STATE: currentTxStopState = handleTreatmentStopDialysateRecircState(); break; case TREATMENT_STOP_RECIRC_BLOOD_ONLY_STATE: currentTxStopState = handleTreatmentStopBloodRecircState(); break; case TREATMENT_STOP_NO_RECIRC_STATE: currentTxStopState = handleTreatmentStopNoRecircState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_TREATMENT_STOP_INVALID_STATE, currentTxStopState ); break; } if ( priorSubState != currentTxStopState ) { setCurrentSubState( (U32)currentTxStopState ); SEND_EVENT_WITH_2_U32_DATA( HD_EVENT_SUB_STATE_CHANGE, priorSubState, currentTxStopState ); } // Broadcast treatment stop status publishTreatmentStopData(); } /*********************************************************************//** * @brief * The handleTreatmentStopAlarmsAndSignals function handles the Alarms and User Signals * operations to set new states. * @details Inputs: state, flags * @details Outputs: flags handled * @return next treatment re-circulation state *************************************************************************/ static TREATMENT_STOP_STATE_T handleTreatmentStopAlarmsAndSignals( TREATMENT_STOP_STATE_T state ) { TREATMENT_STOP_STATE_T result = state; BOOL bloodRecircBlocked = isBloodRecircBlocked(); BOOL dialysateRecircBlocked = isDialysateRecircBlocked(); // Both unblocked and not in recirculate both state if ( ( TREATMENT_STOP_RECIRC_STATE != state ) && ( FALSE == dialysateRecircBlocked ) && ( FALSE == bloodRecircBlocked ) ) { setupForBloodRecirculationState(); setupForDialysateRecirculationState(); result = TREATMENT_STOP_RECIRC_STATE; } // Both blocked and not in stopped state if ( ( TREATMENT_STOP_NO_RECIRC_STATE != state ) && ( TRUE == dialysateRecircBlocked ) && ( TRUE == bloodRecircBlocked ) ) { setupForBloodRecirculationStopState(); setupForDialysateRecirculationStopState(); result = TREATMENT_STOP_NO_RECIRC_STATE; } // Dialysate recirculation blocked and not in blood recirc state if ( ( TREATMENT_STOP_RECIRC_BLOOD_ONLY_STATE != state ) && ( TRUE == dialysateRecircBlocked ) && ( FALSE == bloodRecircBlocked ) ) { setupForDialysateRecirculationStopState(); result = TREATMENT_STOP_RECIRC_BLOOD_ONLY_STATE; } // Blood recirculation blocked and not in dialysate recirc state if ( ( TREATMENT_STOP_RECIRC_DIALYSATE_ONLY_STATE != state ) && ( TRUE == bloodRecircBlocked ) && ( FALSE == dialysateRecircBlocked ) ) { setupForBloodRecirculationStopState(); result = TREATMENT_STOP_RECIRC_DIALYSATE_ONLY_STATE; } return result; } /*********************************************************************//** * @brief * The handleTreatmentStopRecircState function handles the re-circ dialysate * and blood state operations. * @details Inputs: active alarms * @details Outputs: dialysate re-circulation stopped if active alarm blocks * @return next treatment stop state *************************************************************************/ static TREATMENT_STOP_STATE_T handleTreatmentStopRecircState( void ) { TREATMENT_STOP_STATE_T result = TREATMENT_STOP_RECIRC_STATE; result = handleTreatmentStopAlarmsAndSignals( result ); return result; } /*********************************************************************//** * @brief * The handleTreatmentStopDialysateRecircState function handles the Dialysate only * re-circulating state operations. * @details Inputs: flags * @details Outputs: flags handled * @return next treatment re-circulation state *************************************************************************/ static TREATMENT_STOP_STATE_T handleTreatmentStopDialysateRecircState( void ) { TREATMENT_STOP_STATE_T result = TREATMENT_STOP_RECIRC_DIALYSATE_ONLY_STATE; handleTreatmentStopBloodSittingTimer(); result = handleTreatmentStopAlarmsAndSignals(result); return result; } /*********************************************************************//** * @brief * The handleTreatmentStopBloodRecircState function handles the Blood only * re-circulating state operations. * @details Inputs: flags * @details Outputs: flags handled * @return next treatment re-circulation state *************************************************************************/ static TREATMENT_STOP_STATE_T handleTreatmentStopBloodRecircState( void ) { TREATMENT_STOP_STATE_T result = TREATMENT_STOP_RECIRC_BLOOD_ONLY_STATE; result = handleTreatmentStopAlarmsAndSignals(result); return result; } /*********************************************************************//** * @brief * The handleTreatmentStopNoRecircState function handles the no re-circ dialysate * state operations. * @details Inputs: none * @details Outputs: none * @return next treatment stop state *************************************************************************/ static TREATMENT_STOP_STATE_T handleTreatmentStopNoRecircState( void ) { TREATMENT_STOP_STATE_T result = TREATMENT_STOP_NO_RECIRC_STATE; doorClosedRequired( FALSE, FALSE ); handleTreatmentStopBloodSittingTimer(); result = handleTreatmentStopAlarmsAndSignals( result ); return result; } /*********************************************************************//** * @brief * The handleTreatmentStopBloodSittingTimer function handles the no re-circ * blood timer. It should only be called when Blood is NOT circulating. * Increments and checks for warning and alarm timeouts. * @details Inputs: none * @details Outputs: none * @return next treatment stop state *************************************************************************/ static void handleTreatmentStopBloodSittingTimer( void ) { // Ensure we do not sit in stopped state for too long (if blood in line) if ( getRinsebackCompleted() != TRUE ) { if ( ++bloodSittingTimerCtr > WARN_TIME_BLOOD_SITTING ) { activateAlarmNoData( ALARM_ID_HD_BLOOD_SITTING_WARNING ); } if ( bloodSittingTimerCtr > MAX_TIME_BLOOD_SITTING ) { // Activate the alarm activateAlarmNoData( ALARM_ID_HD_TREATMENT_STOPPED_NO_RINSEBACK ); } } else { bloodSittingTimerCtr = 0; } } /*********************************************************************//** * @brief * The getCurrentTreatmentStopState function returns the current state of the * treatment stop sub-mode. * @details Inputs: currentTxStopState * @details Outputs: none * @return currentTxStopState *************************************************************************/ TREATMENT_STOP_STATE_T getCurrentTreatmentStopState( void ) { return currentTxStopState; } /*********************************************************************//** * @brief * The publishTreatmentStopData function publishes treatment stop progress to UI * at 1 Hz interval. * @details Inputs: stopPublishTimerCtr, bloodSittingTimerCtr * @details Outputs: treatment stop data published * @return none *************************************************************************/ static void publishTreatmentStopData( void ) { if ( ++stopPublishTimerCtr >= getU32OverrideValue( &treatmentStopPublishInterval ) ) { TREATMENT_STOP_PAYLOAD_T data; // Set to zero so UI will not update unless needed below data.timeout = 0; data.countdown = 0; if ( ( getRinsebackCompleted() != TRUE ) && ( isAlarmActive( ALARM_ID_HD_TREATMENT_RINSEBACK_TIMEOUT_ALARM ) != TRUE ) && ( isAlarmActive( ALARM_ID_HD_TREATMENT_STOPPED_NO_RINSEBACK ) != TRUE ) ) { if ( bloodSittingTimerCtr > 0 ) { data.timeout = MAX_TIME_BLOOD_SITTING / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ); data.countdown = ( bloodSittingTimerCtr >= MAX_TIME_BLOOD_SITTING ? 0 : ( MAX_TIME_BLOOD_SITTING - bloodSittingTimerCtr ) / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ); } } stopPublishTimerCtr = 0; broadcastData( MSG_ID_HD_TREATMENT_STOP_TIMER_DATA, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&data, sizeof( TREATMENT_STOP_PAYLOAD_T ) ); } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetTreatmentStopPublishIntervalOverride function sets the override of the * treatment stop sub-mode data publication interval. * @details Inputs: none * @details Outputs: treatmentStopPublishInterval * @param ms milliseconds between treatment stop sub-mode broadcasts * @return TRUE if override set successful, FALSE if not *************************************************************************/ BOOL testSetTreatmentStopPublishIntervalOverride( U32 ms ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = ms / TASK_GENERAL_INTERVAL; result = TRUE; treatmentStopPublishInterval.ovData = intvl; treatmentStopPublishInterval.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetTreatmentStopPublishIntervalOverride function resets the override of the * treatment stop sub-mode data publication interval. * @details Inputs: none * @details Outputs: treatmentStopPublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetTreatmentStopPublishIntervalOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; treatmentStopPublishInterval.override = OVERRIDE_RESET; treatmentStopPublishInterval.ovData = treatmentStopPublishInterval.ovInitData; } return result; } /**@}*/