Index: firmware/App/Modes/ModePostTreat.c =================================================================== diff -u -r8c00197ce69e80f1967aa3f2eb36beb3573f36f3 -r5bf62ff1373f2e9eea858244a97167323bff33f8 --- firmware/App/Modes/ModePostTreat.c (.../ModePostTreat.c) (revision 8c00197ce69e80f1967aa3f2eb36beb3573f36f3) +++ firmware/App/Modes/ModePostTreat.c (.../ModePostTreat.c) (revision 5bf62ff1373f2e9eea858244a97167323bff33f8) @@ -15,23 +15,77 @@ * ***************************************************************************/ +#include // For memset() + #include "AlarmLamp.h" +#include "Buttons.h" #include "BloodFlow.h" #include "DialInFlow.h" #include "DialOutFlow.h" -#include "Buttons.h" +#include "DGInterface.h" +#include "FPGA.h" #include "OperationModes.h" +#include "PresOccl.h" #include "ModePostTreat.h" +#include "ModeTreatmentParams.h" +#include "SystemCommMessages.h" +#include "TaskGeneral.h" +#include "Timers.h" +#include "Valves.h" /** * @addtogroup HDPostTreatmentMode * @{ */ +// ********** private definitions ********** + +/// Interval (ms/task time) at which the post-treatment state data is published on the CAN bus. +#define POST_TREATMENT_DATA_PUB_INTERVAL ( 250 / TASK_GENERAL_INTERVAL ) + +#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_FIRST_RESERVOIR_START_STATE = 0, ///< Drain first reservoir start state. + DRAIN_FIRST_RESERVOIR_STATE, ///< Drain first reservoir state. + DRAIN_SWITCH_RESERVOIR_STATE, ///< Switch reservoir state. + DRAIN_SECOND_RESERVOIR_STATE, ///< Drain second reservoir 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 BOOL isDrainCompleted = FALSE; + +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 U32 postTreatmentPublishTimerCounter; ///< Timer counter used to schedule post-treatment data broadcast. + +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 isReservoirDrainStarted( void ); +static DRAIN_STATE_T handleDrainFirstReservoirStartState( void ); +static DRAIN_STATE_T handleDrainFirstReservoirState( void ); +static DRAIN_STATE_T handleDrainSwitchReservoirState( void ); +static DRAIN_STATE_T handleDrainSecondReservoirState( void ); + /*********************************************************************//** * @brief * The initPostTreatmentMode function initializes the Post-Treatment Mode module. @@ -41,6 +95,15 @@ *************************************************************************/ void initPostTreatmentMode( void ) { + patientDisconnectionConfirmed = FALSE; + disposableRemovalConfirmed = FALSE; + isDrainStarted = FALSE; + isDrainCompleted = FALSE; + currentPostTreatmentState = HD_POST_TREATMENT_PATIENT_DISCONNECTION_STATE; + currentDrainReservoirState = DRAIN_FIRST_RESERVOIR_START_STATE; + postTreatmentPublishTimerCounter = 0; + + memset( &treatmentLogData, 0x0, sizeof( TREATMENT_LOG_DATA_PAYLOAD_T ) ); } /*********************************************************************//** @@ -53,12 +116,28 @@ *************************************************************************/ void transitionToPostTreatmentMode( void ) { - // TODO - stop any DG fill that may be in progress from an aborted treatment + initPostTreatmentMode(); + // 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 ); + + 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 ); + + stopSyringePump(); + signalBloodPumpHardStop(); + signalDialOutPumpHardStop(); + signalDialInPumpHardStop(); } /*********************************************************************//** @@ -72,13 +151,90 @@ { BOOL stop = isStopButtonPressed(); - requestNewOperationMode( MODE_STAN ); // TODO - implement post treatment mode + switch ( currentPostTreatmentState ) + { + case HD_POST_TREATMENT_PATIENT_DISCONNECTION_STATE: + currentPostTreatmentState = handlePostTreatmentPatientDisconnectionState(); + break; - return 0; // TODO - return current state + case HD_POST_TREATMENT_DRAIN_RESERVOIRS_STATE: + currentPostTreatmentState = handlePostTreatmentDrainReservoirsState(); + break; + + case HD_POST_TREATMENT_VERIFY_STATE: + currentPostTreatmentState = handlePostTreatmentVerifyState(); + break; + + default: + currentPostTreatmentState = HD_POST_TREATMENT_PATIENT_DISCONNECTION_STATE; + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_MODE_POST_TREATMENT_INVALID_STATE, currentPostTreatmentState ); + break; + } + + if ( ++postTreatmentPublishTimerCounter >= POST_TREATMENT_DATA_PUB_INTERVAL ) + { + broadcastPostTreatmentState( currentPostTreatmentState ); + postTreatmentPublishTimerCounter = 0; + } + + return currentPostTreatmentState; } /*********************************************************************//** * @brief + * The handleTreatmentLogDataRequest sends treatment log data to UI upon + * UI requests. + * @details Inputs: treatmentLogData + * @details Outputs: Sent treatment log data to UI + * @return none + *************************************************************************/ +void handleTreatmentLogDataRequest( void ) +{ + BOOL accepted = TRUE; + REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NONE; + + // Send treatment log data to UI + sendTreatmentLogData( accepted, rejReason, &treatmentLogData ); +} + +/*********************************************************************//** + * @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 ) +{ + BOOL accepted = FALSE; + REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_DRAIN_NOT_COMPLETE; + + if ( DRAIN_COMPLETE_STATE == currentDrainReservoirState ) + { + disposableRemovalConfirmed = TRUE; + accepted = TRUE; + rejReason = REQUEST_REJECT_REASON_NONE; + } + + sendDisposableRemovalConfirmResponse( accepted, rejReason ); +} + +/*********************************************************************//** + * @brief * The signalAlarmActionToPostTreatmentMode function executes the given alarm action * as appropriate while in PostTreatment Mode. * @details Inputs: none @@ -91,4 +247,309 @@ } +/*********************************************************************//** + * @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() ) + { +#ifndef SKIP_UI_INTERACTION + if ( TRUE == patientDisconnectionConfirmed ) +#endif + { + 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 ) ) + { + 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 ) + { +#ifndef SKIP_UI_INTERACTION + if ( TRUE == disposableRemovalConfirmed ) +#endif + { + 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 ) +{ +#ifndef SKIP_EMPTY_RES_CHECK + 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; +#else + BOOL const isReservoirOneEmpty = TRUE; + BOOL const isReservoirTwoEmpty = TRUE; +#endif + +#ifndef SKIP_CARTRIDGE_REMOVAL + BOOL isCartridgeRemoved = isCartridgeUnloaded(); +#else + BOOL isCartridgeRemoved = TRUE; +#endif + + if ( STATE_CLOSED == getFPGADoorState() ) + { + if ( FALSE == isCartridgeRemoved ) + { + activateAlarmNoData( ALARM_ID_CARTRIDGE_REMOVAL_FAILURE ); + } + + if ( TRUE == isSyringeDetected() ) + { + activateAlarmNoData( ALARM_ID_HD_SYRINGE_DETECTED ); + } + + if ( ( TRUE == isReservoirOneEmpty ) && ( TRUE == isReservoirTwoEmpty ) && + ( TRUE == isCartridgeRemoved ) && ( FALSE == isSyringeDetected() ) ) + { + 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_FIRST_RESERVOIR_START_STATE: + currentDrainReservoirState = handleDrainFirstReservoirStartState(); + break; + + case DRAIN_FIRST_RESERVOIR_STATE: + currentDrainReservoirState = handleDrainFirstReservoirState(); + break; + + case DRAIN_SWITCH_RESERVOIR_STATE: + currentDrainReservoirState = handleDrainSwitchReservoirState(); + break; + + case DRAIN_SECOND_RESERVOIR_STATE: + currentDrainReservoirState = handleDrainSecondReservoirState(); + break; + + case DRAIN_COMPLETE_STATE: + break; + + default: + currentDrainReservoirState = DRAIN_FIRST_RESERVOIR_START_STATE; + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_POST_TREATMENT_RESERVOIR_MGMT_INVALID_STATE, currentDrainReservoirState ); + break; + } +} + +/*********************************************************************//** + * @brief + * The isReservoirDrainStarted function checks if reservoir drain has started. + * @details Inputs: none + * @details Outputs: processed drain command response + * @return TRUE if reservoir drain has started, otherwise FALSE + *************************************************************************/ +static BOOL isReservoirDrainStarted( void ) +{ + DG_CMD_RESPONSE_T dgCmdResp; + BOOL result = FALSE; + + if ( TRUE == getDGCommandResponse( DG_CMD_START_DRAIN, &dgCmdResp ) ) + { + if ( DG_CMD_REQUEST_REJECT_REASON_NONE == dgCmdResp.rejectCode ) + { + result = TRUE; + } + else + { + cmdStartDGDrain( EMPTY_RESERVOIR_VOLUME_ML, FALSE ); + } + } + + return result; +} + +/*********************************************************************//** + * @brief + * The handleDrainReservoirOneState function sends command to DG to + * drain first reservoir. + * @details Inputs: none + * @details Outputs: drained reservoir one + * @return next state + *************************************************************************/ +static DRAIN_STATE_T handleDrainFirstReservoirStartState( void ) +{ + DRAIN_STATE_T state = DRAIN_FIRST_RESERVOIR_START_STATE; + + if ( ( DG_MODE_CIRC == getDGOpMode() ) && ( DG_RECIRCULATE_MODE_STATE_RECIRC_WATER == getDGSubMode() ) ) + { + state = DRAIN_FIRST_RESERVOIR_STATE; + cmdStartDGDrain( EMPTY_RESERVOIR_VOLUME_ML, FALSE ); + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleDrainFirstReservoirState function waits for DG to finish draining + * first reservoir and sends command to switch reservoir. + * @details Inputs: none + * @details Outputs: drained first reservoir + * @return next state + *************************************************************************/ +static DRAIN_STATE_T handleDrainFirstReservoirState( void ) +{ + DRAIN_STATE_T state = DRAIN_FIRST_RESERVOIR_STATE; + + if ( TRUE == isReservoirDrainStarted() ) + { + isDrainStarted = TRUE; + } + + // Drain has started and DG goes to re-circ mode flush lines state means drain completed + if ( ( TRUE == isDrainStarted ) && ( DG_RECIRCULATE_MODE_STATE_FLUSH_LINES == getDGSubMode() ) ) + { + isDrainCompleted = TRUE; + } + + if ( TRUE == isDrainCompleted ) + { + if ( ( DG_MODE_CIRC == getDGOpMode() ) && ( DG_RECIRCULATE_MODE_STATE_RECIRC_WATER == getDGSubMode() ) ) + { + isDrainStarted = FALSE; + isDrainCompleted = FALSE; + state = DRAIN_SWITCH_RESERVOIR_STATE; + cmdSetDGActiveReservoir( getDGInactiveReservoir() ); + } + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleDrainSwitchReservoirState function waits for DG to complete + * reservoir switch and sends drain command. + * @details Inputs: none + * @details Outputs: proceed to next state after DG switched reservoir + * @return next state + *************************************************************************/ +static DRAIN_STATE_T handleDrainSwitchReservoirState( void ) +{ + DRAIN_STATE_T state = DRAIN_SWITCH_RESERVOIR_STATE; + + if ( TRUE == hasDGCompletedReservoirSwitch() ) + { + state = DRAIN_SECOND_RESERVOIR_STATE; + cmdStartDGDrain( EMPTY_RESERVOIR_VOLUME_ML, FALSE ); + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handleDrainReservoirTwoState function waits for DG to finish draining + * second reservoir. + * @details Inputs: none + * @details Outputs: drained second reservoir + * @return next state + *************************************************************************/ +static DRAIN_STATE_T handleDrainSecondReservoirState( void ) +{ + DRAIN_STATE_T state = DRAIN_SECOND_RESERVOIR_STATE; + + if ( TRUE == isReservoirDrainStarted() ) + { + isDrainStarted = TRUE; + } + + // Drain has started and DG goes to re-circ mode flush lines state means drain completed + if ( ( TRUE == isDrainStarted ) && ( DG_RECIRCULATE_MODE_STATE_FLUSH_LINES == getDGSubMode() ) ) + { + isDrainCompleted = TRUE; + } + + if ( TRUE == isDrainCompleted ) + { + if ( ( DG_MODE_CIRC == getDGOpMode() ) && ( DG_RECIRCULATE_MODE_STATE_RECIRC_WATER == getDGSubMode() ) ) + { + isDrainStarted = FALSE; + isDrainCompleted = FALSE; + state = DRAIN_COMPLETE_STATE; + } + } + + return state; +} + /**@}*/