/************************************************************************** * * 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 "AlarmLamp.h" #include "Buttons.h" #include "BloodFlow.h" #include "DialInFlow.h" #include "DialOutFlow.h" #include "DGInterface.h" #include "FPGA.h" #include "OperationModes.h" #include "ModePostTreat.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 300 ///< Dialysate inlet pump flow rate during flush in mL/min. #define LOAD_CELL_VOLUME_NOISE_TOLERANCE 0.05 ///< Allow 5% tolerance on load cell readings. #define LOAD_CELL_STEADY_VOLUME_TIME ( 10 * MS_PER_SECOND ) ///< Time load cell reading need to remain steady in ms. /// Post-Treatmen de-prime state machine. typedef enum De_Prime_States { DE_PRIME_FIRST_DRAIN_STATE = 0, ///< De-prime drain first reservoir state. DE_PRIME_FLUSH_BYPASS_STATE, ///< De-prime drain second reservoir while flushing bypass circuit state. DE_PRIME_DRAIN_BYPASS_STATE, ///< De-prime drain dialysate from bypass circuit state. DE_PRIME_COMPLETE_STATE, ///< De-prime complete state. NUM_OF_DE_PRIME_STATES ///< Number of de-prime states. } DE_PRIME_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 U32 previousLoadCellReading; ///< Previous load cell reading. static U32 loadcellSteadyVolumeStartTime; ///< Load cell steady volume starting time. static HD_POST_TREATMENT_STATE_T currentPostTreatmentState; ///< Current state of post-treatment mode state machine. static DE_PRIME_STATE_T currentDePrimeState; ///< Current de-prime sub-mode state. // ********** private function prototypes ********** static HD_POST_TREATMENT_STATE_T handlePostTreatmentPatientDisconnectionState( void ); static HD_POST_TREATMENT_STATE_T handlePostTreatmentDePrimeState( void ); static HD_POST_TREATMENT_STATE_T handlePostTreatmentUnloadState( void ); static HD_POST_TREATMENT_STATE_T handlePostTreatmentVerifyState( void ); static void execDePrime( void ); static BOOL switchReservoirCompleted( DG_RESERVOIR_ID_T activeRes ); static BOOL startDrainCompleted( void ); static DE_PRIME_STATE_T handleDePrimeFirstDrainState( void ); static DE_PRIME_STATE_T handleDePrimeFlushBypassState( void ); static DE_PRIME_STATE_T handleDePrimeDrainBypassState( 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; previousLoadCellReading = 0; loadcellSteadyVolumeStartTime = 0; currentPostTreatmentState = HD_POST_TREATMENT_PATIENT_DISCONNECTION_STATE; currentDePrimeState = DE_PRIME_FIRST_DRAIN_STATE; } /*********************************************************************//** * @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 // 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 ); 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 ); } /*********************************************************************//** * @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_DE_PRIME_STATE: currentPostTreatmentState = handlePostTreatmentDePrimeState(); break; case HD_POST_TREATMENT_UNLOAD_STATE: currentPostTreatmentState = handlePostTreatmentUnloadState(); break; case HD_POST_TREATMENT_VERIFY_STATE: currentPostTreatmentState = handlePostTreatmentVerifyState(); break; default: break; } requestNewOperationMode( MODE_STAN ); // TODO - implement post treatment mode return 0; // TODO - return current state } /*********************************************************************//** * @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 ) { HD_POST_TREATMENT_STATE_T state = HD_POST_TREATMENT_PATIENT_DISCONNECTION_STATE; if ( TRUE == patientDisconnectionConfirmed ) { patientDisconnectionConfirmed = FALSE; state = HD_POST_TREATMENT_DE_PRIME_STATE; } return state; } /*********************************************************************//** * @brief * The handlePostTreatmentDePrimeState function executes de-prime operation * sub-mode state machine and transition to next state once completed. * @details Inputs: currentDePrimeState * @details Outputs: executed de-prime operation state machine. * @return current state (sub-mode) *************************************************************************/ static HD_POST_TREATMENT_STATE_T handlePostTreatmentDePrimeState( void ) { HD_POST_TREATMENT_STATE_T state = HD_POST_TREATMENT_DE_PRIME_STATE; execDePrime(); if ( DE_PRIME_COMPLETE_STATE == currentDePrimeState ) { state = HD_POST_TREATMENT_UNLOAD_STATE; } return state; } /*********************************************************************//** * @brief * The handlePostTreatmentUnloadState function waits for user confirmation * of disposable removal and doors to be closed. * @details Inputs: disposableRemovalConfirmed, doors' status * @details Outputs: transition to verify state * @return current state (sub-mode) *************************************************************************/ static HD_POST_TREATMENT_STATE_T handlePostTreatmentUnloadState( void ) { HD_POST_TREATMENT_STATE_T state = HD_POST_TREATMENT_UNLOAD_STATE; if ( ( TRUE == disposableRemovalConfirmed ) && ( STATE_CLOSED == getFPGADoorState() ) ) { 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 ) { return HD_POST_TREATMENT_VERIFY_STATE; } /*********************************************************************//** * @brief * The execDePrime function executes the De-Prime sub-mode state machine. * @details Inputs: none * @details Outputs: none * @return current state *************************************************************************/ static void execDePrime( void ) { switch ( currentDePrimeState ) { case DE_PRIME_FIRST_DRAIN_STATE: currentDePrimeState = handleDePrimeFirstDrainState(); break; case DE_PRIME_FLUSH_BYPASS_STATE: currentDePrimeState = handleDePrimeFlushBypassState(); break; case DE_PRIME_DRAIN_BYPASS_STATE: currentDePrimeState = handleDePrimeDrainBypassState(); break; case DE_PRIME_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 isBypassCircuitFlushed function checks load cell steady reading as an * indicator for flushed bypass circuit. * @details Inputs: previousLoadCellReading * @details Outputs: processed DG drain command response * @return TRUE if start reservoir drain completed, otherwise FALSE *************************************************************************/ static BOOL isBypassCircuitFlushed( void ) { BOOL result = FALSE; DG_RESERVOIR_ID_T const activeRes = getDGActiveReservoir(); F32 const loadcellWeight = getReservoirWeightSmallFilter( activeRes ); F32 const weightChange = fabs( 1.0 - ( previousLoadCellReading / loadcellWeight ) ); if ( weightChange < LOAD_CELL_VOLUME_NOISE_TOLERANCE ) { result = didTimeout( loadcellSteadyVolumeStartTime, LOAD_CELL_STEADY_VOLUME_TIME ); } else { previousLoadCellReading = loadcellWeight; loadcellSteadyVolumeStartTime = getMSTimerCount(); } return result; } /*********************************************************************//** * @brief * The handleDePrimeFirstDrainState function sends command to DG to drain * reservoir one. * @details Inputs: none * @details Outputs: drained reservoir one * @return current state (sub-mode) *************************************************************************/ static DE_PRIME_STATE_T handleDePrimeFirstDrainState( void ) { DE_PRIME_STATE_T state = DE_PRIME_FIRST_DRAIN_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 = DE_PRIME_FLUSH_BYPASS_STATE; } return state; } /*********************************************************************//** * @brief * The handleDePrimeFlushBypassState function sends command to DG to drain * reservoir two while flushing dialysate from bypass circuit to reservoir one. * @details Inputs: none * @details Outputs: drained reservoir two, flushed dialysate from bypass circuit * @return current state (sub-mode) *************************************************************************/ static DE_PRIME_STATE_T handleDePrimeFlushBypassState( void ) { DE_PRIME_STATE_T state = DE_PRIME_FLUSH_BYPASS_STATE; if ( ( DG_MODE_CIRC == getDGOpMode() ) && ( DG_RECIRCULATE_MODE_STATE_RECIRC_WATER == getDGSubMode() ) ) { cmdSetDGActiveReservoir( DG_RESERVOIR_1 ); } if ( TRUE == switchReservoirCompleted( DG_RESERVOIR_1 ) ) { loadcellSteadyVolumeStartTime = getMSTimerCount(); setDialInPumpTargetFlowRate( DIP_FLUSH_FLOW_RATE_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); 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 == isBypassCircuitFlushed() ) && ( TRUE == isDrainStarted ) && ( DG_MODE_CIRC == getDGOpMode() ) ) { signalDialInPumpHardStop(); isDrainStarted = FALSE; state = DE_PRIME_DRAIN_BYPASS_STATE; } return state; } /*********************************************************************//** * @brief * The handleDePrimeDrainBypassState function sends command to DG to drain * reservoir one, which contains flushed dialysate from bypass circuit. * @details Inputs: none * @details Outputs: drained reservoir one * @return current state (sub-mode) *************************************************************************/ static DE_PRIME_STATE_T handleDePrimeDrainBypassState( void ) { DE_PRIME_STATE_T state = DE_PRIME_DRAIN_BYPASS_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 = DE_PRIME_COMPLETE_STATE; } return state; } /**@}*/