/************************************************************************** * * Copyright (c) 2019-2020 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 ModePostTreat.c * * @author (last) Sean Nash * @date (last) 08-Oct-2020 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 * ***************************************************************************/ #include // For memset() #include "AlarmLamp.h" #include "Buttons.h" #include "BloodFlow.h" #include "DialInFlow.h" #include "DialOutFlow.h" #include "DGInterface.h" #include "FPGA.h" #include "OperationModes.h" #include "PresOccl.h" #include "ModePostTreat.h" #include "ModeTreatmentParams.h" #include "SystemCommMessages.h" #include "Timers.h" #include "Valves.h" /** * @addtogroup HDPostTreatmentMode * @{ */ // ********** private definitions ********** #define EMPTY_RESERVOIR_VOLUME_ML 0 ///< Empty reservoir volume in ml. #define DIP_FLUSH_FLOW_RATE_ML_MIN 150 ///< Dialysate inlet pump flow rate during flush in mL/min. #define LOAD_CELL_VOLUME_NOISE_TOLERANCE_GRAMS 10.0 ///< Allowed tolerance on load cell readings when empty in grams. /// Post-Treatment drain reservoirs state machine. typedef enum Drain_States { DRAIN_RESERVOIR_ONE_STATE = 0, ///< Drain reservoir one state. DRAIN_RESERVOIR_TWO_STATE, ///< Drain reservoir two state. DRAIN_COMPLETE_STATE, ///< Drain complete state. NUM_OF_DRAIN_STATES ///< Number of drain states. } DRAIN_STATE_T; // ********** private data ********** static BOOL patientDisconnectionConfirmed = FALSE; ///< Flag indicates user confirms patient disconnection. static BOOL disposableRemovalConfirmed = FALSE; ///< Flag indicates user confirms disposable removal. static BOOL isDrainStarted = FALSE; ///< Flag indicates a drain operation has been started. static HD_POST_TREATMENT_STATE_T currentPostTreatmentState; ///< Current state of post-treatment mode state machine. static DRAIN_STATE_T currentDrainReservoirState; ///< Current drain reservoir state. static TREATMENT_LOG_DATA_PAYLOAD_T treatmentLogData; ///< Treatment parameters record for logging. // ********** private function prototypes ********** static HD_POST_TREATMENT_STATE_T handlePostTreatmentPatientDisconnectionState( void ); static HD_POST_TREATMENT_STATE_T handlePostTreatmentDrainReservoirsState( void ); static HD_POST_TREATMENT_STATE_T handlePostTreatmentVerifyState( void ); static void execDrainReservoirs( void ); static BOOL switchReservoirCompleted( DG_RESERVOIR_ID_T activeRes ); static BOOL startDrainCompleted( void ); static DRAIN_STATE_T handleDrainReservoirOneState( void ); static DRAIN_STATE_T handleDrainReservoirTwoState( void ); /*********************************************************************//** * @brief * The initPostTreatmentMode function initializes the Post-Treatment Mode module. * @details Inputs: none * @details Outputs: Post-Treatment Mode module initialized. * @return none *************************************************************************/ void initPostTreatmentMode( void ) { patientDisconnectionConfirmed = FALSE; disposableRemovalConfirmed = FALSE; isDrainStarted = FALSE; currentPostTreatmentState = HD_POST_TREATMENT_PATIENT_DISCONNECTION_STATE; currentDrainReservoirState = DRAIN_RESERVOIR_ONE_STATE; memset( &treatmentLogData, 0x0, sizeof( TREATMENT_LOG_DATA_PAYLOAD_T ) ); } /*********************************************************************//** * @brief * The transitionToPostTreatmentMode function prepares for transition to * post-treatment mode. * @details Inputs: none * @details Outputs: none * @return none *************************************************************************/ void transitionToPostTreatmentMode( void ) { initPostTreatmentMode(); // TODO - stop any DG fill that may be in progress from an aborted treatment // cmdStopDGFill(); // Set user alarm recovery actions allowed in this mode setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, TRUE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, FALSE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, FALSE ); cmdSetDGActiveReservoir( DG_RESERVOIR_2 ); cmdStopDGTrimmerHeater(); setValvePosition( VDI, VALVE_POSITION_C_CLOSE ); setValvePosition( VDO, VALVE_POSITION_C_CLOSE ); setValvePosition( VBA, VALVE_POSITION_C_CLOSE ); setValvePosition( VBV, VALVE_POSITION_C_CLOSE ); setValveAirTrap( STATE_CLOSED ); signalBloodPumpHardStop(); signalDialOutPumpHardStop(); signalDialInPumpHardStop(); // TODO: Enable when have syringe pump driver // stopSyringePump(); // Send treatment log data to UI sendTreatmentLogData( &treatmentLogData ); } /*********************************************************************//** * @brief * The execPostTreatmentMode function executes the Post-Treatment Mode state machine. * @details Inputs: none * @details Outputs: none * @return current state (sub-mode) *************************************************************************/ U32 execPostTreatmentMode( void ) { BOOL stop = isStopButtonPressed(); switch ( currentPostTreatmentState ) { case HD_POST_TREATMENT_PATIENT_DISCONNECTION_STATE: currentPostTreatmentState = handlePostTreatmentPatientDisconnectionState(); break; case HD_POST_TREATMENT_DRAIN_RESERVOIRS_STATE: currentPostTreatmentState = handlePostTreatmentDrainReservoirsState(); break; case HD_POST_TREATMENT_VERIFY_STATE: currentPostTreatmentState = handlePostTreatmentVerifyState(); break; default: break; } return currentPostTreatmentState; } /*********************************************************************//** * @brief * The signalUserConfirmPatientDisconnection signals post-treatment mode * user has confirmed patient disconnection. * @details Inputs: none * @details Outputs: patientDisconnectedConfirm * @return none *************************************************************************/ void signalUserConfirmPatientDisconnection( void ) { patientDisconnectionConfirmed = TRUE; } /*********************************************************************//** * @brief * The signalUserConfirmDisposableRemoval signals post-treatment mode * user has confirmed disposable removal. * @details Inputs: none * @details Outputs: disposableRemovalConfirmed * @return none *************************************************************************/ void signalUserConfirmDisposableRemoval( void ) { disposableRemovalConfirmed = TRUE; } /*********************************************************************//** * @brief * The signalAlarmActionToPostTreatmentMode function executes the given alarm action * as appropriate while in PostTreatment Mode. * @details Inputs: none * @details Outputs: given alarm action executed * @param action ID of alarm action to execute * @return none *************************************************************************/ void signalAlarmActionToPostTreatmentMode( ALARM_ACTION_T action ) { } /*********************************************************************//** * @brief * The handlePostTreatmentPatientConnectionState function waits for user * confirms patient disconnection. * @details Inputs: none * @details Outputs: processed patient disconnection confirmation * @return current state (sub-mode) *************************************************************************/ static HD_POST_TREATMENT_STATE_T handlePostTreatmentPatientDisconnectionState( void ) { F32 const bolusVol = getTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_BOLUS_VOLUME ); F32 const hepRate = getTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_BOLUS_VOLUME ); HD_POST_TREATMENT_STATE_T state = HD_POST_TREATMENT_PATIENT_DISCONNECTION_STATE; VALVE_T valve; if ( STATE_CLOSED == getFPGADoorState() ) { if ( TRUE == patientDisconnectionConfirmed ) { patientDisconnectionConfirmed = FALSE; state = HD_POST_TREATMENT_DRAIN_RESERVOIRS_STATE; for ( valve = VDI; valve < NUM_OF_VALVES; ++valve ) { homeValve( valve ); } homeBloodPump(); homeDialInPump(); homeDialOutPump(); if ( ( bolusVol > 0.0 ) || ( hepRate > 0.0 ) ) { // TODO: Enable when have syringe pump driver // retractSyringePump(); } } } else { activateAlarmNoData( ALARM_ID_CARTRIDGE_DOOR_OPENED ); } return state; } /*********************************************************************//** * @brief * The handlePostTreatmentDrainReservoirsState function executes drain reservoirs * operation state machine and transition to next state once completed. * @details Inputs: currentDrainReservoirState * @details Outputs: executed drain reservoirs operation state machine. * @return current state (sub-mode) *************************************************************************/ static HD_POST_TREATMENT_STATE_T handlePostTreatmentDrainReservoirsState( void ) { HD_POST_TREATMENT_STATE_T state = HD_POST_TREATMENT_DRAIN_RESERVOIRS_STATE; execDrainReservoirs(); if ( DRAIN_COMPLETE_STATE == currentDrainReservoirState ) { if ( TRUE == disposableRemovalConfirmed ) { disposableRemovalConfirmed = FALSE; state = HD_POST_TREATMENT_VERIFY_STATE; } } return state; } /*********************************************************************//** * @brief * The handlePostTreatmentVerifyState function verifies cartridge removed, * syringe removed, and doors are closed before transition to standby mode. * @details Inputs: doors' status, syringe status, occlusion sensors value * @details Outputs: requested transition to standby mode * @return current state (sub-mode) *************************************************************************/ static HD_POST_TREATMENT_STATE_T handlePostTreatmentVerifyState( void ) { BOOL const isReservoirOneEmpty = fabs( getReservoirWeightLargeFilter( DG_RESERVOIR_1 ) ) <= LOAD_CELL_VOLUME_NOISE_TOLERANCE_GRAMS; BOOL const isReservoirTwoEmpty = fabs( getReservoirWeightLargeFilter( DG_RESERVOIR_2 ) ) <= LOAD_CELL_VOLUME_NOISE_TOLERANCE_GRAMS; if ( STATE_CLOSED == getFPGADoorState() ) { if ( FALSE == isCartridgeUnloaded() ) { activateAlarmNoData( ALARM_ID_CARTRIDGE_REMOVAL_FAILURE ); } // TODO: Enable when have syringe pump driver // if ( TRUE == isSyringeDetected() ) // { // // TODO: alarm // } // TODO: Add checks for syringe removed if ( ( TRUE == isReservoirOneEmpty ) && ( TRUE == isReservoirTwoEmpty ) && ( TRUE == isCartridgeUnloaded() ) ) { cmdStopDG(); requestNewOperationMode( MODE_STAN ); } } else { activateAlarmNoData( ALARM_ID_CARTRIDGE_DOOR_OPENED ); } return HD_POST_TREATMENT_VERIFY_STATE; } /*********************************************************************//** * @brief * The execDrainReservoirs function executes the drain reservoirs operation. * @details Inputs: currentDrainReservoirState * @details Outputs: currentDrainReservoirState * @return current state *************************************************************************/ static void execDrainReservoirs( void ) { switch ( currentDrainReservoirState ) { case DRAIN_RESERVOIR_ONE_STATE: currentDrainReservoirState = handleDrainReservoirOneState(); break; case DRAIN_RESERVOIR_TWO_STATE: currentDrainReservoirState = handleDrainReservoirTwoState(); break; case DRAIN_COMPLETE_STATE: break; default: break; } } /*********************************************************************//** * @brief * The switchReservoirCompleted function waits for switch reservoir response * from DG and re-send switch reservoir command if DG rejected previous one. * @details Inputs: none * @details Outputs: processed DG switch reservoir command response * @return TRUE if switch reservoir completed, otherwise FALSE *************************************************************************/ static BOOL switchReservoirCompleted( DG_RESERVOIR_ID_T activeRes ) { BOOL result = FALSE; DG_CMD_RESPONSE_T dgCmdResp; if ( TRUE == getDGCommandResponse( DG_CMD_SWITCH_RESERVOIR, &dgCmdResp ) ) { if ( TRUE == dgCmdResp.rejected ) { cmdSetDGActiveReservoir( activeRes ); } else { result = TRUE; } } return result; } /*********************************************************************//** * @brief * The startDrainCompleted function waits for drain command response from DG * and re-send drain command if DG rejected previous one. * @details Inputs: none * @details Outputs: processed DG drain command response * @return TRUE if start reservoir drain completed, otherwise FALSE *************************************************************************/ static BOOL startDrainCompleted( void ) { BOOL result = FALSE; DG_CMD_RESPONSE_T dgCmdResp; if ( TRUE == getDGCommandResponse( DG_CMD_START_DRAIN, &dgCmdResp ) ) { if ( TRUE == dgCmdResp.rejected ) { cmdStartDGDrain( EMPTY_RESERVOIR_VOLUME_ML, FALSE ); } else { result = TRUE; } } return result; } /*********************************************************************//** * @brief * The handleDrainReservoirOneState function sends command to DG to * drain reservoir one. * @details Inputs: none * @details Outputs: drained reservoir one * @return current state (sub-mode) *************************************************************************/ static DRAIN_STATE_T handleDrainReservoirOneState( void ) { DRAIN_STATE_T state = DRAIN_RESERVOIR_ONE_STATE; if ( ( DG_MODE_CIRC == getDGOpMode() ) && ( DG_RECIRCULATE_MODE_STATE_RECIRC_WATER == getDGSubMode() ) ) { cmdSetDGActiveReservoir( DG_RESERVOIR_2 ); } if ( TRUE == switchReservoirCompleted( DG_RESERVOIR_2 ) ) { cmdStartDGDrain( EMPTY_RESERVOIR_VOLUME_ML, FALSE ); } if ( TRUE == startDrainCompleted() ) { isDrainStarted = TRUE; } // Drain has started and DG goes to re-circ mode means drain completed if ( ( TRUE == isDrainStarted ) && ( DG_MODE_CIRC == getDGOpMode() ) ) { isDrainStarted = FALSE; state = DRAIN_RESERVOIR_TWO_STATE; } return state; } /*********************************************************************//** * @brief * The handleDrainReservoirTwoState function sends command to DG to drain * reservoir two. * @details Inputs: none * @details Outputs: drained reservoir two * @return current state (sub-mode) *************************************************************************/ static DRAIN_STATE_T handleDrainReservoirTwoState( void ) { DRAIN_STATE_T state = DRAIN_RESERVOIR_TWO_STATE; if ( ( DG_MODE_CIRC == getDGOpMode() ) && ( DG_RECIRCULATE_MODE_STATE_RECIRC_WATER == getDGSubMode() ) ) { cmdSetDGActiveReservoir( DG_RESERVOIR_1 ); } if ( TRUE == switchReservoirCompleted( DG_RESERVOIR_1 ) ) { cmdStartDGDrain( EMPTY_RESERVOIR_VOLUME_ML, FALSE ); } if ( TRUE == startDrainCompleted() ) { isDrainStarted = TRUE; } // Drain has started and DG goes to re-circ mode means drain completed if ( ( TRUE == isDrainStarted ) && ( DG_MODE_CIRC == getDGOpMode() ) ) { isDrainStarted = FALSE; state = DRAIN_COMPLETE_STATE; } return state; } /**@}*/