/************************************************************************** * * Copyright (c) 2025-2025 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 StateTxPaused.c * * @author (last) Sean * @date (last) 18-Apr-2025 * * @author (original) Sean * @date (original) 18-Apr-2025 * ***************************************************************************/ #include "AirPump.h" #include "BloodFlow.h" #include "Buttons.h" #include "Common.h" #include "DDInterface.h" #include "Messaging.h" #include "ModeTreatment.h" #include "ModeTxParams.h" //#include "NVDataMgmt.h" #include "OperationModes.h" #include "RotaryValve.h" #include "StateTxPaused.h" #include "Switches.h" #include "TaskGeneral.h" #include "Valves.h" #include "Valve3Way.h" /** * @addtogroup StateTxPaused * @{ */ // ********** private definitions ********** /// Treatment paused status broadcast interval. #define TREATMENT_PAUSED_DATA_PUBLISH_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) // ********** private data ********** static TREATMENT_PAUSED_STATE_T currentTxPausedState; ///< Current treatment paused state. static U32 bloodSittingTimerCtr; ///< Timer counter tracks time in this mode while blood is sitting. static U32 treatmentPausedPublishTimerCtr; ///< Timer counter (in GP task intervals) counts time to next treatment paused status broadcast. static OVERRIDE_U32_T treatmentPausedPublishInterval; ///< Interval (in task intervals) at which to publish treatment paused state data to CAN bus. // ********** private function prototypes ********** static TREATMENT_PAUSED_STATE_T handleTreatmentPausedRecircState( void ); static TREATMENT_PAUSED_STATE_T handleTreatmentPausedDialysateRecircState( void ); static TREATMENT_PAUSED_STATE_T handleTreatmentPausedBloodRecircState( void ); static TREATMENT_PAUSED_STATE_T handleTreatmentPausedNoRecircState( void ); static TREATMENT_PAUSED_STATE_T handleTreatmentPausedRecoverBloodDetectState( void ); static void handleTreatmentPausedBloodSittingTimer( void ); static TREATMENT_PAUSED_STATE_T handleTreatmentPausedAlarmsAndSignals( TREATMENT_PAUSED_STATE_T state ); static void transitionToTxPausedState( TREATMENT_PAUSED_STATE_T newState ); static void publishTreatmentPausedData( void ); /*********************************************************************//** * @brief * The initTreatmentPaused function initializes the Treatment Paused State unit. * @details \b Inputs: none * @details \b Outputs: Treatment Paused State unit initialized. * @return none *************************************************************************/ void initTreatmentPaused( void ) { currentTxPausedState = TREATMENT_PAUSED_RECIRC_STATE; // Assume blood and dialysate will recirculate initially - will stop pump(s) as appropriate in state machine bloodSittingTimerCtr = 0; treatmentPausedPublishTimerCtr = TREATMENT_PAUSED_DATA_PUBLISH_INTERVAL; treatmentPausedPublishInterval.data = TREATMENT_PAUSED_DATA_PUBLISH_INTERVAL; treatmentPausedPublishInterval.ovData = TREATMENT_PAUSED_DATA_PUBLISH_INTERVAL; treatmentPausedPublishInterval.ovInitData = TREATMENT_PAUSED_DATA_PUBLISH_INTERVAL; treatmentPausedPublishInterval.override = OVERRIDE_RESET; } /*********************************************************************//** * @brief * The transitionToTreatmentPaused function prepares for transition to * Treatment Paused state. * @details \b Inputs: currentTxPausedState * @details \b Outputs: none * @return none *************************************************************************/ void transitionToTreatmentPaused( void ) { initTreatmentPaused(); setCurrentSubState( (U32)currentTxPausedState ); setCurrent4thLevelState( NO_SUB_STATE ); // Set user alarm recovery actions allowed in this sub-mode setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, TRUE ); if ( FALSE ) // TODO ( 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 ); transitionToTxPausedState( currentTxPausedState ); // TODO figure out the code // signalInitiatePressureStabilization( USE_NORMAL_STABILIZATION_PERIOD ); // Reset saline bolus state in case alarm interrupted one // resetSalineBolus(); // Reset blood leak zeroing params // resetBloodLeakZeroingVariables(); // 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_TD_TREATMENT_STOPPED_AFTER_RINSEBACK ); // No escalation after rinseback because no blood in blood line // } // else { activateAlarmNoData( ALARM_ID_TD_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 execTreatmentPaused function executes the Treatment Paused state machine. * @details \b Alarm: ALARM_ID_HD_SOFTWARE_FAULT if state is invalid * @details \b Message \b Sent: TD_EVENT_SUB_STATE_CHANGE when state changes * @details \b Inputs: currentTxPausedState * @details \b Outputs: currentTxPausedState * @return none *************************************************************************/ void execTreatmentPaused( void ) { TREATMENT_PAUSED_STATE_T priorSubState = currentTxPausedState; switch ( currentTxPausedState ) { case TREATMENT_PAUSED_RECIRC_STATE: currentTxPausedState = handleTreatmentPausedRecircState(); break; case TREATMENT_PAUSED_RECIRC_DIALYSATE_ONLY_STATE: currentTxPausedState = handleTreatmentPausedDialysateRecircState(); break; case TREATMENT_PAUSED_RECIRC_BLOOD_ONLY_STATE: currentTxPausedState = handleTreatmentPausedBloodRecircState(); break; case TREATMENT_PAUSED_NO_RECIRC_STATE: currentTxPausedState = handleTreatmentPausedNoRecircState(); break; case TREATMENT_PAUSED_RECOVER_BLOOD_DETECT_STATE: currentTxPausedState = handleTreatmentPausedRecoverBloodDetectState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_TREATMENT_STOP_INVALID_STATE, currentTxPausedState ); break; } if ( priorSubState != currentTxPausedState ) { setCurrentSubState( (U32)currentTxPausedState ); SEND_EVENT_WITH_2_U32_DATA( TD_EVENT_SUB_STATE_CHANGE, priorSubState, currentTxPausedState ); } // Broadcast treatment paused status publishTreatmentPausedData(); } /*********************************************************************//** * @brief * The getCurrentTreatmentPausedState function returns the current state of the * treatment paused sub-mode. * @details \b Inputs: currentTxPausedState * @details \b Outputs: none * @return currentTxPausedState *************************************************************************/ TREATMENT_PAUSED_STATE_T getCurrentTreatmentPausedState( void ) { return currentTxPausedState; } /*********************************************************************//** * @brief * The handleTreatmentPausedRecircState 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 paused state *************************************************************************/ static TREATMENT_PAUSED_STATE_T handleTreatmentPausedRecircState( void ) { TREATMENT_PAUSED_STATE_T result = TREATMENT_PAUSED_RECIRC_STATE; result = handleTreatmentPausedAlarmsAndSignals( result ); return result; } /*********************************************************************//** * @brief * The handleTreatmentPausedDialysateRecircState function handles the Dialysate only * re-circulating state operations. * @details Inputs: flags * @details Outputs: flags handled * @return next treatment paused state *************************************************************************/ static TREATMENT_PAUSED_STATE_T handleTreatmentPausedDialysateRecircState( void ) { TREATMENT_PAUSED_STATE_T result = TREATMENT_PAUSED_RECIRC_DIALYSATE_ONLY_STATE; handleTreatmentPausedBloodSittingTimer(); result = handleTreatmentPausedAlarmsAndSignals( result ); return result; } /*********************************************************************//** * @brief * The handleTreatmentPausedBloodRecircState function handles the Blood only * re-circulating state operations. * @details Inputs: flags * @details Outputs: flags handled * @return next treatment paused state *************************************************************************/ static TREATMENT_PAUSED_STATE_T handleTreatmentPausedBloodRecircState( void ) { TREATMENT_PAUSED_STATE_T result = TREATMENT_PAUSED_RECIRC_BLOOD_ONLY_STATE; result = handleTreatmentPausedAlarmsAndSignals( result ); return result; } /*********************************************************************//** * @brief * The handleTreatmentStopNoRecircState function handles the no re-circ dialysate * state operations. * @details Inputs: none * @details Outputs: none * @return next treatment paused state *************************************************************************/ static TREATMENT_PAUSED_STATE_T handleTreatmentPausedNoRecircState( void ) { TREATMENT_PAUSED_STATE_T result = TREATMENT_PAUSED_NO_RECIRC_STATE; handleTreatmentPausedBloodSittingTimer(); result = handleTreatmentPausedAlarmsAndSignals( result ); return result; } /*********************************************************************//** * @brief * The handleTreatmentPausedRecoverBloodDetectState function handles the recovering * for blood detect state in treatment paused. * @details Inputs: none * @details Outputs: none * @return next treatment paused state *************************************************************************/ static TREATMENT_PAUSED_STATE_T handleTreatmentPausedRecoverBloodDetectState( void ) { TREATMENT_PAUSED_STATE_T result = TREATMENT_PAUSED_RECOVER_BLOOD_DETECT_STATE; // TODO uncomment //execBloodLeakZeroing(); //// Keep reseting the blood sitting timer handleTreatmentPausedBloodSittingTimer(); result = handleTreatmentPausedAlarmsAndSignals( result ); return result; } /*********************************************************************//** * @brief * The handleTreatmentPausedBloodSittingTimer 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: bloodSittingTimerCtr * @details Outputs: bloodSittingTimerCtr * @return next treatment paused state *************************************************************************/ static void handleTreatmentPausedBloodSittingTimer( void ) { // Ensure we do not sit in stopped state for too long (if blood in line) //if ( getRinsebackCompleted() != TRUE ) // TODO rinseback if ( TRUE ) { bloodSittingTimerCtr++; if ( FALSE == doesAlarmStatusIndicateEndTxOnly() ) // Alarms appropriate only if we are not already at an alarm stop, end Tx only { if ( bloodSittingTimerCtr > WARN_TIME_BLOOD_SITTING ) { activateAlarmNoData( ALARM_ID_TD_BLOOD_SITTING_WARNING ); } if ( bloodSittingTimerCtr > MAX_TIME_BLOOD_SITTING ) { // Activate the alarm activateAlarmNoData( ALARM_ID_TD_BLOOD_SITTING_TOO_LONG ); } } } else { bloodSittingTimerCtr = 0; } } /*********************************************************************//** * @brief * The handleTreatmentPausedAlarmsAndSignals function handles the Alarms and User Signals * operations to set new states. * @details Inputs: state, flags * @details Outputs: flags handled * @param state the treatment paused state * @return next treatment re-circulation state *************************************************************************/ static TREATMENT_PAUSED_STATE_T handleTreatmentPausedAlarmsAndSignals( TREATMENT_PAUSED_STATE_T state ) { TREATMENT_PAUSED_STATE_T result = state; BOOL bloodRecircBlocked = isBloodRecircBlocked(); BOOL dialysateRecircBlocked = isDialysateRecircBlocked(); BOOL bldAlarmsStatus = FALSE; //( ( TRUE == isAlarmActive( ALARM_ID_HD_BLOOD_LEAK_RECOVERING_PLEASE_WAIT ) ) && // ( FALSE == isAlarmActive( ALARM_ID_HD_BLOOD_LEAK_DETECTED ) ? TRUE : FALSE ) ); // TODO un-comment when BLD is coming up if ( TRUE == bldAlarmsStatus ) { if ( ( TREATMENT_PAUSED_RECOVER_BLOOD_DETECT_STATE != state ) && ( dialysateRecircBlocked != TRUE ) ) { result = TREATMENT_PAUSED_RECOVER_BLOOD_DETECT_STATE; transitionToTxPausedState( result ); } } else { // Both unblocked and not in recirculate both state if ( ( TREATMENT_PAUSED_NO_RECIRC_STATE != state ) && ( FALSE == dialysateRecircBlocked ) && ( FALSE == bloodRecircBlocked ) ) { result = TREATMENT_PAUSED_RECIRC_STATE; transitionToTxPausedState( result ); } // Both blocked and not in stopped state else if ( ( TREATMENT_PAUSED_NO_RECIRC_STATE != state ) && ( TRUE == dialysateRecircBlocked ) && ( TRUE == bloodRecircBlocked ) ) { result = TREATMENT_PAUSED_NO_RECIRC_STATE; transitionToTxPausedState( result ); } // Dialysate recirculation blocked and not in blood recirc state else if ( ( TREATMENT_PAUSED_RECIRC_BLOOD_ONLY_STATE != state ) && ( TRUE == dialysateRecircBlocked ) && ( FALSE == bloodRecircBlocked ) ) { result = TREATMENT_PAUSED_RECIRC_BLOOD_ONLY_STATE; transitionToTxPausedState( result ); } // Blood recirculation blocked and not in dialysate recirc state else if ( ( TREATMENT_PAUSED_RECIRC_DIALYSATE_ONLY_STATE != state ) && ( TRUE == bloodRecircBlocked ) && ( FALSE == dialysateRecircBlocked ) ) { result = TREATMENT_PAUSED_RECIRC_DIALYSATE_ONLY_STATE; transitionToTxPausedState( result ); } } return result; } /*********************************************************************//** * @brief * The transitionToTxPausedState function handles the transition to treatment * paused state by setting the actuators. * @details Inputs: none * @details Outputs: none * @param newState the targeted state that the transition is going to go * @return none *************************************************************************/ static void transitionToTxPausedState( TREATMENT_PAUSED_STATE_T newState ) { switch ( newState ) { case TREATMENT_PAUSED_RECIRC_STATE: { U32 targetBloodFlowMLPM = getTreatmentParameterU32( TREATMENT_PARAM_BLOOD_FLOW ); U32 targetDialysateFlowMLPM = (F32)getTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW ); setBloodPumpTargetFlowRate( targetBloodFlowMLPM, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); setValvePosition( H1_VALV, VALVE_POSITION_C_CLOSE ); setValvePosition( H19_VALV, VALVE_POSITION_C_CLOSE ); setAirPumpState( AIR_PUMP_STATE_OFF, 0 ); set3WayValveState( H13_VALV, VALVE_3WAY_COMMON_TO_CLOSED_STATE ); set3WayValveState( H20_VALV, VALVE_3WAY_COMMON_TO_CLOSED_STATE ); cmdBypassDialyzer( TRUE ); cmdChangeQd( targetDialysateFlowMLPM ); cmdChangeQuf( 0.0F ); doorClosedRequired( TRUE ); } break; case TREATMENT_PAUSED_RECIRC_DIALYSATE_ONLY_STATE: { U32 targetDialysateFlowMLPM = (F32)getTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW ); signalBloodPumpHardStop(); setValvePosition( H1_VALV, VALVE_POSITION_C_CLOSE ); setValvePosition( H19_VALV, VALVE_POSITION_C_CLOSE ); setAirPumpState( AIR_PUMP_STATE_OFF, 0 ); set3WayValveState( H13_VALV, VALVE_3WAY_COMMON_TO_CLOSED_STATE ); set3WayValveState( H20_VALV, VALVE_3WAY_COMMON_TO_CLOSED_STATE ); cmdBypassDialyzer( TRUE ); cmdChangeQd( targetDialysateFlowMLPM ); cmdChangeQuf( 0.0F ); doorClosedRequired( FALSE ); } break; case TREATMENT_PAUSED_RECIRC_BLOOD_ONLY_STATE: { U32 targetBloodFlowMLPM = getTreatmentParameterU32( TREATMENT_PARAM_BLOOD_FLOW ); setBloodPumpTargetFlowRate( targetBloodFlowMLPM, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); setValvePosition( H1_VALV, VALVE_POSITION_B_OPEN ); setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); setAirPumpState( AIR_PUMP_STATE_ON, AIR_PUMP_MOTOR_LOWER_PWM ); // TODO is the PWM Correct? set3WayValveState( H13_VALV, VALVE_3WAY_COMMON_TO_OPEN_STATE ); set3WayValveState( H20_VALV, VALVE_3WAY_COMMON_TO_OPEN_STATE ); cmdBypassDialyzer( TRUE ); cmdChangeQd( 0.0F ); cmdChangeQuf( 0.0F ); doorClosedRequired( TRUE ); } break; case TREATMENT_PAUSED_NO_RECIRC_STATE: signalBloodPumpHardStop(); setValvePosition( H1_VALV, VALVE_POSITION_C_CLOSE ); setValvePosition( H19_VALV, VALVE_POSITION_C_CLOSE ); setAirPumpState( AIR_PUMP_STATE_OFF, 0 ); set3WayValveState( H13_VALV, VALVE_3WAY_COMMON_TO_CLOSED_STATE ); set3WayValveState( H20_VALV, VALVE_3WAY_COMMON_TO_CLOSED_STATE ); cmdBypassDialyzer( TRUE ); cmdChangeQd( 0.0F ); cmdChangeQuf( 0.0F ); doorClosedRequired( FALSE ); break; case TREATMENT_PAUSED_RECOVER_BLOOD_DETECT_STATE: // TODO fill up later break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_TREATMENT_STOP_INVALID_STATE, (U32)newState ) break; } } /*********************************************************************//** * @brief * The publishTreatmentPausedData function publishes treatment paused progress. * @details Inputs: stopPublishTimerCtr, bloodSittingTimerCtr * @details Outputs: treatment stop data published * @return none *************************************************************************/ static void publishTreatmentPausedData( void ) { if ( ++treatmentPausedPublishTimerCtr >= getU32OverrideValue( &treatmentPausedPublishInterval ) ) { TREATMENT_PAUSED_PAYLOAD_T data; // Set to zero so UI will not update unless needed below data.timeout = 0; data.countdown = 0; // TODO figure out the alarms //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 ) ); } } treatmentPausedPublishTimerCtr = 0; broadcastData( MSG_ID_TD_TREATMENT_PAUSED_TIMER_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)&data, sizeof( TREATMENT_PAUSED_PAYLOAD_T ) ); } } /**@}*/