Index: firmware/App/Modes/Prime.c =================================================================== diff -u -r4d2dcd2d5ee3c7684be139b7e560b16d8c3abc06 -rc634d11307431c42a5219c7ca5000093ee3ee1f6 --- firmware/App/Modes/Prime.c (.../Prime.c) (revision 4d2dcd2d5ee3c7684be139b7e560b16d8c3abc06) +++ firmware/App/Modes/Prime.c (.../Prime.c) (revision c634d11307431c42a5219c7ca5000093ee3ee1f6) @@ -1,21 +1,33 @@ /************************************************************************** * -* Copyright (c) 2019-2020 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 Prime.h +* @file Prime.c * -* @author (last) Quang Nguyen -* @date (last) 08-Dec-2020 +* @author (last) Dara Navaei +* @date (last) 22-Dec-2022 * -* @author (original) Quang Nguyen -* @date (original) 08-Dec-2020 +* @author (original) Quang Nguyen +* @date (original) 08-Dec-2020 * ***************************************************************************/ +#include "AirTrap.h" +#include "AlarmMgmt.h" +#include "BloodFlow.h" +#include "DialInFlow.h" +#include "DialOutFlow.h" +#include "DGInterface.h" +#include "ModeTreatmentParams.h" #include "Prime.h" +#include "SelfTests.h" +#include "SystemCommMessages.h" +#include "TaskGeneral.h" +#include "Timers.h" +#include "Valves.h" /** * @addtogroup Prime @@ -24,31 +36,1012 @@ // ********** private definitions ********** +#define MAX_PRIME_TIME ( 30 * SEC_PER_MIN ) ///< Maximum prime time (in seconds). +#define PRIME_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the prime data is published on the CAN bus. + +#define BLOOD_PUMP_FAST_FLOW_RATE_PURGE_AIR_ML_MIN 300 ///< Blood pump fast flow rate to fill fluid. +#define BLOOD_PUMP_SLOW_FLOW_RATE_PURGE_AIR_ML_MIN 150 ///< Blood pump slow flow rate after fluid reach lower level of air trap sensor. +#define BLOOD_PUMP_SALINE_FLOW_RATE_PURGE_AIR_ML_MIN 200 ///< Blood pump very slow flow rate during prime saline dialyzer state +#define BLOOD_PUMP_SLOW_FLOW_RATE_CIRC_BLOOD_CIRCUIT_ML_MIN 150 ///< Blood pump slow flow rate during prime recirculate blood circuit state. +#define BLOOD_PUMP_FAST_FLOW_RATE_CIRC_BLOOD_CIRCUIT_ML_MIN 300 ///< Blood pump fast flow rate during prime recirculate blood circuit state. +#define BLOOD_PUMP_FLOW_RATE_SALINE_DIALYZER_ML_MIN 300 ///< Blood pump flow rate during prime the saline dialyzer dialysate state. +#define DIALYSATE_PUMP_PRIME_FLOW_RATE_ML_MIN 300 ///< Dialysate pump flow rate during priming fluid path. +#define DPO_PUMP_PRIME_FLOW_RATE_ML_MIN 225 ///< Dialysate outlet pump flow rate needed to match PWM with dialysate inlet pump @ 300 mL/min in open loop mode. + +#define DIALYSATE_DIALYZER_TUBE_VOLUME_ML 115 ///< This total tube volume is used to calculate the Dpi & Dpo time out in the dialysate dialyzer state. +#define DIALYSATE_DIALYZER_BYPASS_TUBE_VOLUME_ML 75 ///< This volume is used to calculate the DPi pump time out in the dialyzer bypass state. +#define DIALYZER_DVI_PATH_VOLUME_ML 17 ///< Path volume from the dialyzer to the VDI valve in mL. +#define DIALYZER_VOLUME_SCALE_FACTOR 0.5F ///< Half of the dialyzer total volume. + +#define NO_AIR_DETECTED_COUNT ( 40 * MS_PER_SECOND ) ///< No air detected time period count. +#define PURGE_AIR_TIME_OUT_COUNT ( 240 * MS_PER_SECOND ) ///< Time period count for purge air time out. +#define PRIME_SALINE_DIALYZER_TIME_OUT_COUNT ( 60 * MS_PER_SECOND ) ///< Time period count for prime saline dialyzer time out. +#define LOAD_CELL_STEADY_VOLUME_SAMPLING_TIME ( 1 * MS_PER_SECOND ) ///< Time load cell reading steady state detection sampling time in seconds. +#define PRIME_DIALYSATE_BYPASS_TIME_LIMIT ( 15 * MS_PER_SECOND ) ///< Time limit for priming dialysate bypass circuit. +#define STEADY_VOLUME_COUNT_SEC ( 10000 / LOAD_CELL_STEADY_VOLUME_SAMPLING_TIME ) ///< Counter must be greater than 10 seconds before steady volume is true. +#define STEADY_VOLUME_TIME_DEADLINE_MS ( 55 * MS_PER_SECOND ) ///< Time in msec for the steady volume deadline time out. + +#define VENOUS_PRESSURE_BUBBLE_CLEAR_MAX_MMHG ( 200.0F ) ///< Maximum venous pressure reading (in mmHg) for bubble clear. +#define BUBBLE_CLEAR_WAIT_TIME_INITIAL_MS ( 10 * MS_PER_SECOND ) ///< Time in msec to wait for initial bubble clear pressure. +#define BUBBLE_CLEAR_WAIT_TIME_MS ( 2 * MS_PER_SECOND ) ///< Time in msec to wait for bubble clear pressure. +#define BUBBLE_CLEAR_MAX_TIME_MS ( 10 * MS_PER_SECOND ) ///< Time in msec maximum for bubble clear pressure. +#define BUBBLE_CLEAR_VALVE_WAIT_TIME_MS ( 90 ) ///< Time in msec between VDi and VBT valve open + +/// States of the treatment reservoir management state machine. +typedef enum PrimeReservoirMgmt_States +{ + PRIME_RESERVOIR_MGMT_START_STATE = 0, ///< If DG not already in re-circ mode, try to get it there. + PRIME_RESERVOIR_MGMT_FLUSH_DG_LINES_STATE, ///< In DG re-circ, wait for lines to flush - then start draining inactive reservoir. + PRIME_RESERVOIR_MGMT_WAIT_FOR_DRAIN_CMD_RESP, ///< After sending drain command, wait for DG to response back. + PRIME_RESERVOIR_MGMT_DRAIN_STATE, ///< Wait for drain to complete. + PRIME_RESERVOIR_MGMT_START_FILL_STATE, ///< Command DG to start filling reservoir. + PRIME_RESERVOIR_MGMT_WAIT_FOR_FILL_CMD_RESP, ///< After sending fill command, wait for DG to response back. + PRIME_RESERVOIR_MGMT_FILL_STATE, ///< Wait for fill to complete. + PRIME_RESERVOIR_MGMT_FILL_COMPLETE_STATE, ///< Reservoir fill has completed. + PRIME_RESERVOIR_MGMT_WAIT_RESERVOIR_TWO_INACTIVE, ///< Wait for reservoir 2 become inactive. + NUM_OF_PRIME_RESERVOIR_MGMT_STATES ///< Number of prime reservoir mgmt. states. +} PRIME_RESERVOIR_MGMT_STATE_T; + +/// States of the prime dialyzer bubble clear state machine. +typedef enum PrimeSalineDialyzerBubbleClear_States +{ + PRIME_BUBBLE_CLEAR_READY_STATE = 0, ///< Bubble clear initial / ready state. + PRIME_BUBBLE_CLEAR_PRESSURE_STATE, ///< Bubble clear pressurized / active state. + PRIME_BUBBLE_CLEAR_VENT_STATE, ///< BUbble clear vent to reservoir state. + PRIME_BUBBLE_CLEAR_FLOW_STATE, ///< Bubble clear flow state. + PRIME_BUBBLE_CLEAR_COMPLETE_STATE, ///< Bubble clear complete. + NUM_OF_PRIME_BUBBLE_CLEAR_STATES ///< Number of bubble clear states. +} PRIME_BUBBLE_CLEAR_STATE_T; + +typedef struct { + U32 bloodVolume; ///< Blood volume of the dialyzer in mL. + U32 dialysateVolume; ///< Dialysate volume of the dialyzer in mL. +} DIALYZER_VOLUME_DATA_T; + // ********** private data ********** -static BOOL primeStatus; +/// Dialyzer volumes (blood and dialysate) table in mL for each type of dialyzer. +static DIALYZER_VOLUME_DATA_T dialyzerVolumeTable[ ] = + { { 82, 170 }, // DIALYZER_TYPE_BBRAUN_PRO_13H + { 100, 200 }, // DIALYZER_TYPE_BBRAUN_PRO_16H + { 120, 257 }, // DIALYZER_TYPE_BBRAUN_PRO_19H + { 87, 233 }, // DIALYZER_TYPE_FRESENIUS_OPTIFLUX_F160NRE + { 102, 280 }, // DIALYZER_TYPE_FRESENIUS_OPTIFLUX_F180NRE + { 113, 348 } }; // DIALYZER_TYPE_FRESENIUS_OPTIFLUX_F200NRE +static U32 primeDialysateDialyzerTimeLimit; ///< Time limit in msec for priming dialysate dialyzer path. +static U32 primeSalineDialyzerTimeLimit; ///< Time limit in msec for priming saline dialyzer path. + +static HD_PRE_TREATMENT_PRIME_STATE_T currentPrimeState; ///< Current state of the prime sub-mode state machine. +static HD_PRE_TREATMENT_PRIME_STATE_T previousPrimeState; ///< Previous state of the prime sub-mode, to use when resuming from pause. +static PRIME_RESERVOIR_MGMT_STATE_T currentReservoirMgmtState; ///< Current reservoir management state. +static PRIME_BUBBLE_CLEAR_STATE_T primeDialyzerBubbleClearState;///< Priming saline dialyzer bubble clear state. + +static U32 primeStartTime; ///< Starting time of priming (in ms). +static U32 primePauseStartTime; ///< Priming pause start time (in ms). +static U32 primeStatusBroadcastTimerCounter; ///< Prime status data broadcast timer counter used to schedule when to transmit data. + +static BOOL primeStartRequested; ///< Flag indicates user requesting to start prime. +static BOOL primeResumeRequested; ///< Flag indicates user requesting prime resume. +static BOOL primeFirstPurgePass; ///< Flag indicates to transition to a faster purge speed. + +static U32 noAirDetectedStartTime; ///< starting time when detecting no air. +static U32 purgeAirTimeOutStartTime; ///< Starting time for purge air state time out. +static U32 primeSalineDialyzerStartTime; ///< Starting time of priming saline dialyzer circuit. +static U32 primeSalineDialyzerBubbleClearStartTime; ///< Starting time of priming saline dialyzer bubble clear. +static U32 primeDialysateDialyzerStartTime; ///< Starting time of priming dialysate dialyzer circuit. +static U32 primeDialysateBypassStartTime; ///< Starting time of priming dialysate bypass circuit. +static U32 steadyVolumeSamplingStartTime; ///< Load cell steady volume sampling interval starting time. +static F32 minimumReservoirVolume; ///< Minimum reservoir volume in mL. +static U32 steadyVolumeCount; ///< Use to keep track the number of dVolume/dt < Threshold + // ********** private function prototypes ********** +static void resetPrimeFlags(); +static void setupForPrimePause( void ); +static void broadcastPrimingStatus( void ); +static void purgeAirValvesBloodPumpControl( void ); +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeWaitForUserStartState( void ); +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeSalineDialyzerSetupState( void ); +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeSalineDialyzerState( void ); +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeSalineSetupState( void ); +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimePurgeAirState( void ); +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeCircBloodCircuitState( void ); +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeReservoirOneFillCompleteState( void ); +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeDialysateDialyzerState( void ); +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeReservoirTwoFillCompleteState( void ); +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeDialysateBypassState( void ); +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeWetSelfTestsState( void ); +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimePause( void ); + +/*********************************************************************//** + * @brief + * The initPrime function initializes the prime sub-mode module. + * This function will reset anything required before the start of priming sequence. + * @details Inputs: none + * @details Outputs: Prime sub-mode module initialized. + * @return none + *************************************************************************/ void initPrime( void ) { - primeStatus = FALSE; + currentPrimeState = HD_PRIME_START_STATE; + currentReservoirMgmtState = PRIME_RESERVOIR_MGMT_START_STATE; + primeStartTime = 0; + primePauseStartTime = 0; + primeStatusBroadcastTimerCounter = 0; + } +/*********************************************************************//** + * @brief + * The transitionToPrime function prepares for transition to prime sub-mode. + * This function will reset anything required before the start of priming sequence. + * @details Inputs: none + * @details Outputs: currentPrimeState, wetSelfTestsResult, primeStartReqReceived, reservoirFilledStatus[] + * @return none + *************************************************************************/ void transitionToPrime( void ) { + primeStartTime = getMSTimerCount(); + primeFirstPurgePass = TRUE; + primeDialyzerBubbleClearState = PRIME_BUBBLE_CLEAR_READY_STATE; + + setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, TRUE ); + setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, FALSE ); + setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, TRUE ); + + doorClosedRequired( TRUE, TRUE ); + + // Pumps should be off + signalBloodPumpHardStop(); + signalDialInPumpHardStop(); + signalDialOutPumpHardStop(); + stopSyringePump(); + + // Set valves to default positions + setValveAirTrap( STATE_CLOSED ); + setValvePosition( VDI, VALVE_POSITION_C_CLOSE ); + setValvePosition( VDO, VALVE_POSITION_C_CLOSE ); + setValvePosition( VBA, VALVE_POSITION_C_CLOSE ); + setValvePosition( VBV, VALVE_POSITION_C_CLOSE ); + + resetPrimeFlags(); } +/*********************************************************************//** + * @brief + * The execPrime function executes the prime sub-mode state machine. + * @details Inputs: currentPrimeState + * @details Outputs: currentPrimeState + * @return none + *************************************************************************/ void execPrime( void ) { + // execute prime sub-mode state machine + switch ( currentPrimeState ) + { + case HD_PRIME_START_STATE: +#ifndef _RELEASE_ + if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_DISABLE_PRIMING ) ) + { + currentPrimeState = HD_PRIME_RESERVOIR_ONE_FILL_COMPLETE_STATE; + } + else +#endif + { + currentPrimeState = HD_PRIME_WAIT_FOR_USER_START_STATE; + } + break; + + case HD_PRIME_WAIT_FOR_USER_START_STATE: + currentPrimeState = handlePrimeWaitForUserStartState(); + break; + + case HD_PRIME_SALINE_SETUP_STATE: + currentPrimeState = handlePrimeSalineSetupState(); + break; + + case HD_PRIME_SALINE_PURGE_AIR_STATE: + currentPrimeState = handlePrimePurgeAirState(); + break; + + case HD_PRIME_SALINE_CIRC_BLOOD_CIRCUIT_STATE: + currentPrimeState = handlePrimeCircBloodCircuitState(); + break; + + case HD_PRIME_RESERVOIR_ONE_FILL_COMPLETE_STATE: + currentPrimeState = handlePrimeReservoirOneFillCompleteState(); + break; + + case HD_PRIME_DIALYSATE_DIALYZER_STATE: + currentPrimeState = handlePrimeDialysateDialyzerState(); + break; + + case HD_PRIME_SALINE_DIALYZER_SETUP_STATE: + currentPrimeState = handlePrimeSalineDialyzerSetupState(); + break; + + case HD_PRIME_SALINE_DIALYZER_STATE: + currentPrimeState = handlePrimeSalineDialyzerState(); + break; + + case HD_PRIME_RESERVOIR_TWO_FILL_COMPLETE_STATE: + currentPrimeState = handlePrimeReservoirTwoFillCompleteState(); + break; + + case HD_PRIME_DIALYSATE_BYPASS_STATE: + currentPrimeState = handlePrimeDialysateBypassState(); + break; + + case HD_PRIME_WET_SELF_TESTS_STATE: + currentPrimeState = handlePrimeWetSelfTestsState(); + break; + + case HD_PRIME_COMPLETE: + break; + + case HD_PRIME_PAUSE: + currentPrimeState = handlePrimePause(); + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_MODE_PRE_TREATMENT_PRIME_INVALID_STATE, currentReservoirMgmtState ); + break; + } + + // Prime flags should be handled by now + resetPrimeFlags(); + + // Broadcast priming data + broadcastPrimingStatus(); } -BOOL isPrimingPassed( void ) +/*********************************************************************//** + * @brief + * The getPrimeState function returns the current state of prime sub-mode. + * @details Inputs: currentPrimeState + * @details Outputs: none + * @return current prime state + *************************************************************************/ +U32 getPrimeState( void ) { - primeStatus = TRUE; - return primeStatus; + return (U32)currentPrimeState; } +/*********************************************************************//** + * @brief + * The getDialyzerBloodVolume function returns the blood volume of + * the selected dialyzer. + * @details Inputs: dialyzer volume table + * @details Outputs: none + * @return blood volume capacity of the selected dialyzer type in mL + *************************************************************************/ +U32 getDialyzerBloodVolume( void ) +{ + U32 dialyzerType = getTreatmentParameterU32( TREATMENT_PARAM_DIALYZER_TYPE ); + return dialyzerVolumeTable[ dialyzerType ].bloodVolume; +} + +/*********************************************************************//** + * @brief + * The getDialyzerDialysateVolume function returns the dialysate volume + * of the selected dialyzer. + * @details Inputs: dialyzer volume table + * @details Outputs: none + * @return dialysate volume capacity of the selected dialyzer type in mL + *************************************************************************/ +U32 getDialyzerDialysateVolume( void ) +{ + U32 dialyzerType = getTreatmentParameterU32( TREATMENT_PARAM_DIALYZER_TYPE ); + + return dialyzerVolumeTable[ dialyzerType ].dialysateVolume; +} + +/*********************************************************************//** + * @brief + * The signalStartPrime function signals the prime sub-mode the user requested + * to start priming operation. + * @details Inputs: none + * @details Outputs: primeStartRequested + * @return none + *************************************************************************/ +void signalStartPrime( void ) +{ + BOOL accepted = FALSE; + REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE; + + if ( HD_PRIME_WAIT_FOR_USER_START_STATE == currentPrimeState ) + { + primeStartRequested = TRUE; + accepted = TRUE; + rejReason = REQUEST_REJECT_REASON_NONE; + } + + sendStartPrimeCmdResponse( accepted, rejReason ); +} + +/*********************************************************************//** + * @brief + * The signalResumePrime function signals the prime sub-mode to resume + * previous operation. + * @details Inputs: none + * @details Outputs: primeResumeRequested + * @return none + *************************************************************************/ +void signalResumePrime( void ) +{ + primeResumeRequested = TRUE; +} + +/*********************************************************************//** + * @brief + * The resetPrimeFlags function resets all prime signal flags. + * @details Inputs: none + * @details Outputs: signal flags set to FALSE + * @return none + *************************************************************************/ +static void resetPrimeFlags( void ) +{ + primeStartRequested = FALSE; + primeResumeRequested = FALSE; +} + +/*********************************************************************//** + * @brief + * The setupForPrimePause function stop pumps handles prime pause request. + * @details Inputs: none + * @details Outputs: stop all pumps, switch to prime pause state. + * @return none + *************************************************************************/ +static void setupForPrimePause( void ) +{ + signalDialOutPumpHardStop(); + signalDialInPumpHardStop(); + signalBloodPumpHardStop(); + + primePauseStartTime = getMSTimerCount(); + previousPrimeState = currentPrimeState; +} + +/*********************************************************************//** + * @brief + * The broadcastPrimeTimeAndStatus function broadcasts prime status + * data during priming. + * @details Inputs: pre-treatment time and state data + * @details Outputs: pre-treatment time and state messages sent on interval + * @return none + *************************************************************************/ +static void broadcastPrimingStatus( void ) +{ + U32 const elapsedPrimeTimeInSecs = calcTimeSince( primeStartTime ) / MS_PER_SECOND; + + if ( elapsedPrimeTimeInSecs > MAX_PRIME_TIME ) + { + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_PRIME_OUT_OF_TIME, elapsedPrimeTimeInSecs, MAX_PRIME_TIME ); + } + + if ( ++primeStatusBroadcastTimerCounter >= PRIME_DATA_PUB_INTERVAL ) + { + PRIMING_DATA_PAYLOAD_T primeData; + primeData.totalTime = MAX_PRIME_TIME; + primeData.remainingTime = MAX_PRIME_TIME - elapsedPrimeTimeInSecs; + + broadcastData( MSG_ID_HD_PRIMING_STATUS_DATA, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&primeData, sizeof( PRIMING_DATA_PAYLOAD_T ) ); + primeStatusBroadcastTimerCounter = 0; + } +} + +/*********************************************************************//** + * @brief + * The purgeAirValvesBloodPumpControl function controls valves and blood pump + * to purge air. + * @details Inputs: primeFirstPurgePass + * @details Outputs: run blood pump, close VDI, VDO, VBA and VBV valves, open VBT valve + * @return current state (sub-mode) + *************************************************************************/ +static void purgeAirValvesBloodPumpControl( void ) +{ + 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_OPEN ); + + signalDialOutPumpHardStop(); + signalDialInPumpHardStop(); + if ( FALSE == primeFirstPurgePass ) + { + setBloodPumpTargetFlowRate( BLOOD_PUMP_FAST_FLOW_RATE_PURGE_AIR_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + } + else + { + setBloodPumpTargetFlowRate( BLOOD_PUMP_SLOW_FLOW_RATE_PURGE_AIR_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + } +} + +/*********************************************************************//** + * @brief + * The handlePrimeSalineSetupState function checks user's request to start + * priming. + * @details Inputs: primeStartReqReceived + * @details Outputs: control valves to purge air + * @return current state + *************************************************************************/ +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeWaitForUserStartState( void ) +{ + HD_PRE_TREATMENT_PRIME_STATE_T state = HD_PRIME_WAIT_FOR_USER_START_STATE; + + // Keep updating start time until the user requested priming + primeStartTime = getMSTimerCount(); + +#ifndef _RELEASE_ + if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_DISABLE_UI_INTERACTION ) || SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_DISABLE_PRIMING ) ) + { + primeStartRequested = TRUE; + } +#endif + + if ( TRUE == primeStartRequested ) + { + primeStartRequested = FALSE; + state = HD_PRIME_SALINE_SETUP_STATE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handlePrimeSalineSetupState function prepares the entry conditions + * before start priming the saline dialyzer fluid path. + * priming. + * @details Inputs: primeStartReqReceived + * @details Outputs: control valves to purge air, primeDialyzerBubbleClearState, + * primeSalineDialyzerBubbleClearStartTime, purgeAirTimeOutStartTime + * @return current state + *************************************************************************/ +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeSalineSetupState( void ) +{ + HD_PRE_TREATMENT_PRIME_STATE_T state = HD_PRIME_SALINE_PURGE_AIR_STATE; + + purgeAirValvesBloodPumpControl(); + purgeAirTimeOutStartTime = getMSTimerCount(); + primeSalineDialyzerBubbleClearStartTime = getMSTimerCount(); + primeDialyzerBubbleClearState = PRIME_BUBBLE_CLEAR_READY_STATE; + + if ( TRUE == doesAlarmStatusIndicateStop() ) + { + setupForPrimePause(); + state = HD_PRIME_PAUSE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handlePrimeBubbleClear function handles bubble clear pressurizing + * of dialyzer. + * @details Inputs: air trap levels, primeDialyzerBubbleClearState, primeSalineDialyzerBubbleClearStartTime + * @details Outputs: control valves to pressurize, primeDialyzerBubbleClearState, primeSalineDialyzerBubbleClearStartTime + * @return none + *************************************************************************/ +static void handlePrimeBubbleClear( void ) +{ + static BOOL bubble_clear_ended = FALSE; + U32 timeout; + + switch ( primeDialyzerBubbleClearState ) + { + case PRIME_BUBBLE_CLEAR_READY_STATE: + case PRIME_BUBBLE_CLEAR_FLOW_STATE: + if ( PRIME_BUBBLE_CLEAR_READY_STATE == primeDialyzerBubbleClearState ) + { + timeout = BUBBLE_CLEAR_WAIT_TIME_INITIAL_MS; + } + else + { + timeout = BUBBLE_CLEAR_WAIT_TIME_MS; + } + + if ( TRUE == didTimeout( primeSalineDialyzerBubbleClearStartTime, timeout ) ) + { + // Close valve to create pressure to clear Dialyzer bubbles + primeSalineDialyzerBubbleClearStartTime = getMSTimerCount(); + if ( AIR_TRAP_LEVEL_AIR == getAirTrapLevel( AIR_TRAP_LEVEL_SENSOR_LOWER ) ) + { + setValvePosition( VDI, VALVE_POSITION_C_CLOSE ); + setValveAirTrap( STATE_CLOSED ); + primeDialyzerBubbleClearState = PRIME_BUBBLE_CLEAR_PRESSURE_STATE; + } + } + break; + + case PRIME_BUBBLE_CLEAR_PRESSURE_STATE: + case PRIME_BUBBLE_CLEAR_VENT_STATE: + if ( ( getMeasuredVenousPressure() > VENOUS_PRESSURE_BUBBLE_CLEAR_MAX_MMHG ) || + ( TRUE == didTimeout( primeSalineDialyzerBubbleClearStartTime, BUBBLE_CLEAR_MAX_TIME_MS ) ) ) + { + // Pressure max reached. or timeout, release pressure + setValvePosition( VDI, VALVE_POSITION_A_INSERT_EJECT ); + primeSalineDialyzerBubbleClearStartTime = getMSTimerCount(); + bubble_clear_ended = TRUE; + primeDialyzerBubbleClearState = PRIME_BUBBLE_CLEAR_VENT_STATE; + } + else if ( ( TRUE == bubble_clear_ended ) && + ( TRUE == didTimeout( primeSalineDialyzerBubbleClearStartTime, BUBBLE_CLEAR_VALVE_WAIT_TIME_MS ) ) ) + { + setValvePosition( VDI, VALVE_POSITION_C_CLOSE ); + setValveAirTrap( STATE_OPEN ); + bubble_clear_ended = FALSE; + primeDialyzerBubbleClearState = PRIME_BUBBLE_CLEAR_FLOW_STATE; + } + break; + + case PRIME_BUBBLE_CLEAR_COMPLETE_STATE: + default: + // do nothing + break; + + } // end switch +} + +/*********************************************************************//** + * @brief + * The handlePrimePurgeAirState function checks for air trap level and moves + * to blood circuit circulation state if fluid is detected at upper sensor. + * @details Inputs: air trap levels, primeFirstPurgePass, purgeAirTimeOutStartTime + * @details Outputs: runs blood pump, control valves to trap air, primeFirstPurgePass + * noAirDetectedStartTime + * @return current state + *************************************************************************/ +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimePurgeAirState( void ) +{ + HD_PRE_TREATMENT_PRIME_STATE_T state = HD_PRIME_SALINE_PURGE_AIR_STATE; + + if ( TRUE == didTimeout( purgeAirTimeOutStartTime, PURGE_AIR_TIME_OUT_COUNT ) ) + { + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_PRIME_SALINE_PURGE_AIR_TIME_OUT, PURGE_AIR_TIME_OUT_COUNT ); + } + + handlePrimeBubbleClear(); + + if ( AIR_TRAP_LEVEL_FLUID == getAirTrapLevel( AIR_TRAP_LEVEL_SENSOR_UPPER ) ) + { + setValvePosition( VDI, VALVE_POSITION_C_CLOSE ); + setValvePosition( VDO, VALVE_POSITION_C_CLOSE ); + setValvePosition( VBA, VALVE_POSITION_B_OPEN ); + setValvePosition( VBV, VALVE_POSITION_B_OPEN ); + setValveAirTrap( STATE_CLOSED ); + + setBloodPumpTargetFlowRate( BLOOD_PUMP_FAST_FLOW_RATE_CIRC_BLOOD_CIRCUIT_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + + // Once the dialyzer is no longer dry from the first pass of purge air, we can set flow to the fast rate + // until we are in a new priming state. + primeFirstPurgePass = FALSE; + noAirDetectedStartTime = getMSTimerCount(); + primeDialyzerBubbleClearState = PRIME_BUBBLE_CLEAR_COMPLETE_STATE; + state = HD_PRIME_SALINE_CIRC_BLOOD_CIRCUIT_STATE; + } + + if ( TRUE == doesAlarmStatusIndicateStop() ) + { + setupForPrimePause(); + state = HD_PRIME_PAUSE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handlePrimeCircBloodCircuitState function checks for air trap level and + * return to purge air state if air is detected at lower sensor. If no air + * detected for a period of time, the blood pump is stopped. + * @details Inputs: air trap levels + * @details Outputs: stop blood pump, control valves to purge air + * @return current state + *************************************************************************/ +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeCircBloodCircuitState( void ) +{ + HD_PRE_TREATMENT_PRIME_STATE_T state = HD_PRIME_SALINE_CIRC_BLOOD_CIRCUIT_STATE; + + if ( AIR_TRAP_LEVEL_AIR == getAirTrapLevel( AIR_TRAP_LEVEL_SENSOR_LOWER ) ) + { + purgeAirValvesBloodPumpControl(); + purgeAirTimeOutStartTime = getMSTimerCount(); + state = HD_PRIME_SALINE_PURGE_AIR_STATE; + } + + if ( TRUE == didTimeout( noAirDetectedStartTime, NO_AIR_DETECTED_COUNT ) ) + { + signalBloodPumpHardStop(); + state = HD_PRIME_RESERVOIR_ONE_FILL_COMPLETE_STATE; + } + + if ( TRUE == doesAlarmStatusIndicateStop() ) + { + setupForPrimePause(); + state = HD_PRIME_PAUSE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handlePrimeReservoirOneFillCompleteState function waits for DG to finish + * filling reservoir 1 before moving to priming dialyzer. + * @details Inputs: reservoirFilledStatus[] + * @details Outputs: update valves and pumps configuration on state change + * @return current state + *************************************************************************/ +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeReservoirOneFillCompleteState( void ) +{ + HD_PRE_TREATMENT_PRIME_STATE_T state = HD_PRIME_RESERVOIR_ONE_FILL_COMPLETE_STATE; + + if ( TRUE == getReservoirFillStatus( DG_RESERVOIR_1 ) ) + { + DG_SWITCH_RSRVRS_CMD_T rsrvrCmd; + + rsrvrCmd.reservoirID = (U32)DG_RESERVOIR_1; + rsrvrCmd.useLastTrimmerHeaterDC = FALSE; + + if ( TRUE == hasDGCompletedReservoirSwitch() ) + { + U32 dialyzerDialysateVolume = getDialyzerDialysateVolume(); + + setValvePosition( VDI, VALVE_POSITION_B_OPEN ); + setValvePosition( VDO, VALVE_POSITION_B_OPEN ); + setValvePosition( VBA, VALVE_POSITION_C_CLOSE ); + setValvePosition( VBV, VALVE_POSITION_C_CLOSE ); + setValveAirTrap( STATE_CLOSED ); + + signalBloodPumpHardStop(); + setDialInPumpTargetFlowRate( DIALYSATE_PUMP_PRIME_FLOW_RATE_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + setDialOutPumpTargetRate( DPO_PUMP_PRIME_FLOW_RATE_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + + // Calculate the time out value that must passed prior to checking for the steady state volume in the reservoir + primeDialysateDialyzerTimeLimit = (U32)( ( ( DIALYSATE_DIALYZER_TUBE_VOLUME_ML + dialyzerDialysateVolume ) * SEC_PER_MIN * MS_PER_SECOND ) / DIALYSATE_PUMP_PRIME_FLOW_RATE_ML_MIN ); + minimumReservoirVolume = getLoadCellWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); + steadyVolumeCount = 0; + steadyVolumeSamplingStartTime = getMSTimerCount(); + primeDialysateDialyzerStartTime = getMSTimerCount(); + +#ifndef _RELEASE_ + if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_DISABLE_PRIMING ) ) + { + signalDialOutPumpHardStop(); + signalDialInPumpHardStop(); + state = HD_PRIME_WET_SELF_TESTS_STATE; + } + else +#endif + { + state = HD_PRIME_DIALYSATE_DIALYZER_STATE; + } + } + } + + if ( TRUE == doesAlarmStatusIndicateStop() ) + { + setupForPrimePause(); + state = HD_PRIME_PAUSE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handlePrimeDialysateDialyzerState function handles priming for + * dialysate dialyzer fluid path. + * @details Inputs: reservoir 1 filtered weight + * @details Outputs: primed dialysate dialyzer fluid path + * @return current state + *************************************************************************/ +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeDialysateDialyzerState( void ) +{ + HD_PRE_TREATMENT_PRIME_STATE_T state = HD_PRIME_DIALYSATE_DIALYZER_STATE; + + if ( TRUE == didTimeout( primeDialysateDialyzerStartTime, primeDialysateDialyzerTimeLimit ) ) + { + // check for volume steady state every second after the DVi and DVo pumps have been on for primeDialysateDialyzerTimeLimit seconds + if ( TRUE == didTimeout( steadyVolumeSamplingStartTime, LOAD_CELL_STEADY_VOLUME_SAMPLING_TIME ) ) + { + F32 const currentReservoirVolume = getLoadCellWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); + + if ( currentReservoirVolume >= minimumReservoirVolume ) + { + if ( ++steadyVolumeCount >= STEADY_VOLUME_COUNT_SEC ) + { + state = HD_PRIME_SALINE_DIALYZER_SETUP_STATE; + } + } + else + { + minimumReservoirVolume = currentReservoirVolume; + steadyVolumeCount = 0; // required 10 seconds continuous steady volume to transition to next state + } + + steadyVolumeSamplingStartTime = getMSTimerCount(); // re-armed the timer for the next dVolume/dt check + } + } + else + { + steadyVolumeSamplingStartTime = getMSTimerCount(); + } + + if ( TRUE == didTimeout( primeDialysateDialyzerStartTime, primeDialysateDialyzerTimeLimit + STEADY_VOLUME_TIME_DEADLINE_MS ) ) + { + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_PRIME_DIALYSATE_DIALYZER_TIME_OUT, primeDialysateDialyzerTimeLimit ); + } + + if ( TRUE == doesAlarmStatusIndicateStop() ) + { + setupForPrimePause(); + state = HD_PRIME_PAUSE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handlePrimeSalineDialyzerSetupState function implements the setup + * prior to transition to the prime the saline dialyzer state. + * @details Inputs: primeStartReqReceived + * @details Outputs: primeSalineDializerInitialLoadCellReading, primeSalineDialyzerStartTime + * @return current state + *************************************************************************/ +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeSalineDialyzerSetupState( void ) +{ + HD_PRE_TREATMENT_PRIME_STATE_T state = HD_PRIME_SALINE_DIALYZER_STATE; + U32 dialyzerDialysateVolume = getDialyzerDialysateVolume(); + + setValvePosition( VDI, VALVE_POSITION_A_INSERT_EJECT ); + setValvePosition( VDO, VALVE_POSITION_C_CLOSE ); + setValvePosition( VBA, VALVE_POSITION_C_CLOSE ); + setValvePosition( VBV, VALVE_POSITION_C_CLOSE ); + setValveAirTrap( STATE_CLOSED ); + + signalDialOutPumpHardStop(); + signalDialInPumpHardStop(); + setBloodPumpTargetFlowRate( BLOOD_PUMP_FLOW_RATE_SALINE_DIALYZER_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + + primeSalineDialyzerTimeLimit = (U32)( ( ( dialyzerDialysateVolume * DIALYZER_VOLUME_SCALE_FACTOR + DIALYZER_DVI_PATH_VOLUME_ML ) * SEC_PER_MIN * MS_PER_SECOND ) / BLOOD_PUMP_FLOW_RATE_SALINE_DIALYZER_ML_MIN ); + primeSalineDialyzerStartTime = getMSTimerCount(); + + if ( TRUE == doesAlarmStatusIndicateStop() ) + { + setupForPrimePause(); + state = HD_PRIME_PAUSE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handlePrimeSalineDialyzerState function handles priming for saline + * dialyzer fluid path. + * @details Inputs: reservoir 2 filtered weight + * @details Outputs: primed saline dialyzer fluid path + * @return current state + *************************************************************************/ +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeSalineDialyzerState( void ) +{ + HD_PRE_TREATMENT_PRIME_STATE_T state = HD_PRIME_SALINE_DIALYZER_STATE; + + if ( TRUE == didTimeout( primeSalineDialyzerStartTime, primeSalineDialyzerTimeLimit ) ) + { + state = HD_PRIME_RESERVOIR_TWO_FILL_COMPLETE_STATE; + } + // TODO: Rework alarm and timeout logic here. PRIME_SALINE_DIALYZER_TIME_OUT_COUNT may not be reached. + if ( TRUE == didTimeout( primeSalineDialyzerStartTime, PRIME_SALINE_DIALYZER_TIME_OUT_COUNT ) ) + { + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_PRIME_SALINE_DIALYZER_TIME_OUT, PRIME_SALINE_DIALYZER_TIME_OUT_COUNT ); // Trigger HD prime saline dialyzer time out alarm. + } + + if ( TRUE == doesAlarmStatusIndicateStop() ) + { + setupForPrimePause(); + state = HD_PRIME_PAUSE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handlePrimeReservoirTwoFillCompleteState function waits for DG to finish + * filling reservoir 2 before moving to pre-treatment re-circulation. + * @details Inputs: reservoirFilledStatus[] + * @details Outputs: update valves and pumps configuration on state change + * @return current state + *************************************************************************/ +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeReservoirTwoFillCompleteState( void ) +{ + HD_PRE_TREATMENT_PRIME_STATE_T state = HD_PRIME_RESERVOIR_TWO_FILL_COMPLETE_STATE; + + if ( TRUE == getReservoirFillStatus( DG_RESERVOIR_2 ) ) + { + DG_SWITCH_RSRVRS_CMD_T rsrvrCmd; + + rsrvrCmd.reservoirID = (U32)DG_RESERVOIR_1; + rsrvrCmd.useLastTrimmerHeaterDC = FALSE; + + cmdSetDGActiveReservoir( &rsrvrCmd ); + + if ( TRUE == hasDGCompletedReservoirSwitch() ) + { + signalBloodPumpHardStop(); + setDialInPumpTargetFlowRate( DIALYSATE_PUMP_PRIME_FLOW_RATE_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + + 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 ); + + minimumReservoirVolume = getLoadCellWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); + steadyVolumeCount = 0; + primeDialysateBypassStartTime = getMSTimerCount(); + steadyVolumeSamplingStartTime = getMSTimerCount(); + state = HD_PRIME_DIALYSATE_BYPASS_STATE; + } + } + + if ( TRUE == doesAlarmStatusIndicateStop() ) + { + setupForPrimePause(); + state = HD_PRIME_PAUSE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handlePrimeDialysateBypassState function handles priming for + * dialysate dialyzer bypass fluid path. + * @details Inputs: reservoir 2 filtered weight + * @details Outputs: primed dialysate bypass fluid path + * @return current state + *************************************************************************/ +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeDialysateBypassState( void ) +{ + HD_PRE_TREATMENT_PRIME_STATE_T state = HD_PRIME_DIALYSATE_BYPASS_STATE; + + if ( TRUE == didTimeout( primeDialysateBypassStartTime, PRIME_DIALYSATE_BYPASS_TIME_LIMIT ) ) + { + // check for steady volume every second after the DVi pump has been on for PRIME_DIALYSATE_BYPASS_TIME_LIMIT seconds + if ( TRUE == didTimeout( steadyVolumeSamplingStartTime, LOAD_CELL_STEADY_VOLUME_SAMPLING_TIME ) ) + { + F32 const currentReservoirVolume = getLoadCellWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); + + if ( currentReservoirVolume >= minimumReservoirVolume ) + { + if ( ++steadyVolumeCount >= STEADY_VOLUME_COUNT_SEC ) + { + transitionToWetSelfTests(); + state = HD_PRIME_WET_SELF_TESTS_STATE; + } + } + else + { + minimumReservoirVolume = currentReservoirVolume; + steadyVolumeCount = 0; + } + + steadyVolumeSamplingStartTime = getMSTimerCount(); // re-armed the timer for the next 1 second iteration + } + } + else + { + steadyVolumeSamplingStartTime = getMSTimerCount(); + } + + if ( TRUE == didTimeout( primeDialysateBypassStartTime, PRIME_DIALYSATE_BYPASS_TIME_LIMIT + STEADY_VOLUME_TIME_DEADLINE_MS ) ) // allocate 55 seconds for the steady state logic to do its job before timing out + { + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_PRIME_DIALYSATE_BYPASS_TIME_OUT, PRIME_DIALYSATE_BYPASS_TIME_LIMIT ); + } + + if ( TRUE == doesAlarmStatusIndicateStop() ) + { + setupForPrimePause(); + state = HD_PRIME_PAUSE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handlePrimeWetSelfTestsState function perform wet self-tests after + * priming is completed and collect the result. + * @details Inputs: none + * @details Outputs: isWetSelfTestsPassed + * @return current state + *************************************************************************/ +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeWetSelfTestsState( void ) +{ + HD_PRE_TREATMENT_PRIME_STATE_T state = HD_PRIME_WET_SELF_TESTS_STATE; + + if ( TRUE == primeResumeRequested ) + { + signalResumeSelfTests(); + } + + execWetSelfTests(); + + if ( TRUE == isWetSelfTestsPassed() ) + { + state = HD_PRIME_COMPLETE; + } + + if ( TRUE == doesAlarmStatusIndicateStop() ) + { + setupForPrimePause(); + state = HD_PRIME_PAUSE; + } + + return state; +} + +/*********************************************************************//** + * @brief + * The handlePrimePause function handles prime pause state. + * @details Inputs: previousPrimeState + * @details Outputs: primeStartTime, primeResumeReqReceived + * @return current state + *************************************************************************/ +static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimePause( void ) +{ + HD_PRE_TREATMENT_PRIME_STATE_T state = HD_PRIME_PAUSE; + + if ( TRUE == primeResumeRequested ) + { + primeResumeRequested = FALSE; + primeStartTime += calcTimeSince( primePauseStartTime ); // Adjust prime start time so we don't count pause time in prime duration calculation. + + switch ( previousPrimeState ) + { + case HD_PRIME_SALINE_SETUP_STATE: + case HD_PRIME_SALINE_PURGE_AIR_STATE: + case HD_PRIME_SALINE_CIRC_BLOOD_CIRCUIT_STATE: + state = HD_PRIME_SALINE_SETUP_STATE; + break; + + case HD_PRIME_RESERVOIR_ONE_FILL_COMPLETE_STATE: + case HD_PRIME_DIALYSATE_DIALYZER_STATE: + state = HD_PRIME_RESERVOIR_ONE_FILL_COMPLETE_STATE; + break; + + case HD_PRIME_SALINE_DIALYZER_SETUP_STATE: + case HD_PRIME_SALINE_DIALYZER_STATE: + state = HD_PRIME_SALINE_DIALYZER_SETUP_STATE; + break; + + case HD_PRIME_RESERVOIR_TWO_FILL_COMPLETE_STATE: + case HD_PRIME_DIALYSATE_BYPASS_STATE: + state = HD_PRIME_RESERVOIR_TWO_FILL_COMPLETE_STATE; + break; + + case HD_PRIME_WET_SELF_TESTS_STATE: + signalResumeSelfTests(); + state = HD_PRIME_WET_SELF_TESTS_STATE; + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_MODE_PRE_TREATMENT_PRIME_INVALID_STATE, (U32)previousPrimeState ); + break; + } + } + + return state; +} + /**@}*/