Index: firmware/App/Modes/TreatmentStop.c =================================================================== diff -u -r8bd1ae47aa13a843aa8abd6321ddc050deacb4a6 -r1f91b5a53bda942b0967817bbd5e68a499dbf816 --- firmware/App/Modes/TreatmentStop.c (.../TreatmentStop.c) (revision 8bd1ae47aa13a843aa8abd6321ddc050deacb4a6) +++ firmware/App/Modes/TreatmentStop.c (.../TreatmentStop.c) (revision 1f91b5a53bda942b0967817bbd5e68a499dbf816) @@ -1,14 +1,14 @@ /************************************************************************** * -* Copyright (c) 2019-2021 Diality Inc. - All Rights Reserved. +* 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) Sean Nash -* @date (last) 19-Aug-2021 +* @author (last) Michael Garthwaite +* @date (last) 19-Jan-2023 * * @author (original) Sean * @date (original) 15-Jan-2020 @@ -17,6 +17,7 @@ #include "AirTrap.h" #include "BloodFlow.h" +#include "BloodLeak.h" #include "DGInterface.h" #include "DialInFlow.h" #include "DialOutFlow.h" @@ -34,12 +35,10 @@ // ********** private definitions ********** -/// Maximum time in this mode before blood sitting alarm given (in general task intervals). -static const U32 MAX_TIME_BLOOD_SITTING = ( ( 5 * SEC_PER_MIN * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ); -/// Maximum time in this mode before blood sitting warning given (in general task intervals). -static const U32 WARN_TIME_BLOOD_SITTING = ( ( 4 * SEC_PER_MIN * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ); /// Treatment stop status broadcast interval. #define TREATMENT_STOP_DATA_PUBLISH_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) +/// Target flow rate for re-circulation of saline on blood-side circuit. +#define RECIRC_BP_FLOW_RATE_ML_MIN 100 // ********** private data ********** @@ -53,6 +52,12 @@ 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 ); @@ -85,6 +90,7 @@ // 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 ); @@ -93,24 +99,20 @@ { setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, TRUE ); } + setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, TRUE ); - // Ensure all pumps except DPi stopped - signalBloodPumpHardStop(); - signalDialOutPumpHardStop(); + // Ensure syringe pump is stopped stopSyringePump(); - // Start out assuming we re-circulate dialysate while stopped - setDialInPumpTargetFlowRate( DIALYSATE_FLOW_RATE_FOR_RECIRC, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); - - // Set valves to safe state + // Set Dialysate valves to bypass filter for recirculation setValvePosition( VDI, VALVE_POSITION_C_CLOSE ); setValvePosition( VDO, VALVE_POSITION_C_CLOSE ); - setValvePosition( VBA, VALVE_POSITION_C_CLOSE ); - setValvePosition( VBV, VALVE_POSITION_C_CLOSE ); - // Stop air trap control - endAirTrapControl(); + setupForBloodRecirculationState(); + + setupForDialysateRecirculationState(); + // Reset saline bolus state in case alarm interrupted one resetSalineBolus(); @@ -132,6 +134,82 @@ /*********************************************************************//** * @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 ) +{ + 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 re-circulate flow rate + setBloodPumpTargetFlowRate( RECIRC_BP_FLOW_RATE_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + // 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(); + // 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 @@ -140,19 +218,7 @@ *************************************************************************/ void execTreatmentStop( 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_BLOOD_SITTING_WARNING ); - } - if ( bloodSittingTimerCtr > MAX_TIME_BLOOD_SITTING ) - { - // Activate the alarm - activateAlarmNoData( ALARM_ID_TREATMENT_STOPPED_NO_RINSEBACK ); - } - } + TREATMENT_STOP_STATE_T priorSubState = currentTxStopState; // Execute treatment stop sub-mode state machine switch ( currentTxStopState ) @@ -161,6 +227,14 @@ 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; @@ -170,14 +244,71 @@ break; } + if ( priorSubState != 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 - * state operations. + * and blood state operations. * @details Inputs: active alarms * @details Outputs: dialysate re-circulation stopped if active alarm blocks * @return next treatment stop state @@ -186,19 +317,49 @@ { TREATMENT_STOP_STATE_T result = TREATMENT_STOP_RECIRC_STATE; - // if any active alarm(s) blocks dialysate re-circulation, stop it - if ( TRUE == isDialysateRecircBlocked() ) - { - signalDialInPumpHardStop(); - cmdStopDGTrimmerHeater(); - result = TREATMENT_STOP_NO_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 @@ -209,13 +370,46 @@ { TREATMENT_STOP_STATE_T result = TREATMENT_STOP_NO_RECIRC_STATE; - // nothing to do in this state - alarm user response will take us to appropriate next sub-mode + 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_BLOOD_SITTING_WARNING ); + } + if ( bloodSittingTimerCtr > MAX_TIME_BLOOD_SITTING ) + { + // Activate the alarm + activateAlarmNoData( ALARM_ID_TREATMENT_STOPPED_NO_RINSEBACK ); + } + } + else + { + bloodSittingTimerCtr = 0; + } +} +/*********************************************************************//** + * @brief * The getCurrentTreatmentStopState function returns the current state of the * treatment stop sub-mode. * @details Inputs: currentTxStopState @@ -241,15 +435,18 @@ { TREATMENT_STOP_PAYLOAD_T data; - data.timeout = MAX_TIME_BLOOD_SITTING / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ); + // Set to zero so UI will not update unless needed below + data.timeout = 0; + data.countdown = 0; + if ( getRinsebackCompleted() != TRUE ) { - data.countdown = ( bloodSittingTimerCtr >= MAX_TIME_BLOOD_SITTING ? 0 : ( MAX_TIME_BLOOD_SITTING - bloodSittingTimerCtr ) / ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ); + 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 ) ); + } } - else - { - data.countdown = 0; - } stopPublishTimerCtr = 0; broadcastData( MSG_ID_HD_TREATMENT_STOP_TIMER_DATA, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&data, sizeof( TREATMENT_STOP_PAYLOAD_T ) ); }