/************************************************************************** * * Copyright (c) 2019-2021 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.c * * @author (last) Sean Nash * @date (last) 12-Nov-2021 * * @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 "Prime.h" #include "SelfTests.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "Timers.h" #include "Valves.h" /** * @addtogroup Prime * @{ */ // ********** private definitions ********** #define MAX_PRIME_TIME ( 10 * 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 400 ///< 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_FLOW_RATE_CIRC_BLOOD_CIRCUIT_ML_MIN 300 ///< Blood pump flow rate during prime recirculate blood circuit state. #define DIALYSATE_PUMP_PRIME_FLOW_RATE_ML_MIN 300 ///< Dialysate pump flow rate during priming fluid path. #define LOAD_CELL_VOLUME_NOISE_TOLERANCE 5.00 ///< Allow 5 mL tolerance on load cell readings. #define PRIME_SALINE_DIALYZER_STOP_WEIGHT 50.0 ///< Load cell readings of 10 grams ( 10 mL ) used in prime saline dialyzer state ///< Currently, we use 50.0 mL for testing. Remove this comment and replace 50.0 with 10.0 before merge to develop branch #define NO_AIR_DETECTED_COUNT ( 20 * MS_PER_SECOND ) ///< No air detected time period count. #define PURGE_AIR_TIME_OUT_COUNT ( 60 * MS_PER_SECOND ) ///< Time period count for purge air 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_DIALYZER_TIME_LIMIT ( 120 * MS_PER_SECOND ) ///< Time limit for priming dialysate dialyzer circuit. #define PRIME_DIALYSATE_BYPASS_TIME_LIMIT ( 120 * MS_PER_SECOND ) ///< Time limit for priming dialysate bypass circuit. #define PRIME_SALINE_DIALYZER_TIME_LIMIT ( 300 * MS_PER_SECOND ) ///< Five minutes time limit for priming saline dialyzer path. #define CIRC_BUF_SIZE 10 ///< Size of the circular buffer. Note: the size is not 2^N where N = 1, 2 , 3, 4, 5 ... #define MAX_BUF_INDEX ( CIRC_BUF_SIZE - 1 ) ///< Use to calculate write index of the circular buffer. #define PRIME_DIALYZER_BYPASS_EXPECTED_VOLUME 1000.0 ///< Prime dialysate dialyzer bypass state expected steady state volume in mL. #define PRIME_DIALYZER_EXPECTED_VOLUME 1200.0 ///< Prime dialysate dialyzer state expected steady state volume in mL. #define PRIME_EXPECTED_VOLUME_TOLERANCE 50.0 ///< Prime expected steady state volume tolerance in mL. /// 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; // ********** private data ********** 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 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 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 primeDialysateDialyzerStartTime; ///< Starting time of priming dialysate dialyzer circuit. static U32 primeDialysateBypassStartTime; ///< Starting time of priming dialysate bypass circuit. static U32 primeSalineDializerInitialLoadCellReading; ///< Initial load cell reading in the prime saline dialyzer state. static U32 loadcellSteadyVolumeStartTime; ///< Load cell steady volume starting time. static BOOL runBloodCircuitPrimeAgain; ///< Flag indicates HD should run blood circuit prime once more time. static F32 loadcellReadings[ CIRC_BUF_SIZE ]; ///< Storage for 10 seconds of load cell weight readings. static U32 currentBufIndex; ///< Use to calculate the write and the read index of the circular buffer. // ********** private function prototypes ********** static void resetPrimeFlags(); static void setupForPrimePause( void ); static void broadcastPrimingStatus( void ); static void purgeAirDialyzerBloodPumpControl( 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 handlePrimeReservoirTwoFillCompleteState( void ); static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeDialysateBypassState( 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 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 ) { transitionToPrime(); } /*********************************************************************//** * @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 ) { currentPrimeState = HD_PRIME_START_STATE; currentReservoirMgmtState = PRIME_RESERVOIR_MGMT_START_STATE; primeStartTime = getMSTimerCount(); primePauseStartTime = 0; primeStatusBroadcastTimerCounter = 0; runBloodCircuitPrimeAgain = TRUE; setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, TRUE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, FALSE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, TRUE ); 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: #ifdef SKIP_PRIMING currentPrimeState = HD_PRIME_RESERVOIR_ONE_FILL_COMPLETE_STATE; #else currentPrimeState = HD_PRIME_WAIT_FOR_USER_START_STATE; #endif break; case HD_PRIME_WAIT_FOR_USER_START_STATE: currentPrimeState = handlePrimeWaitForUserStartState(); break; case HD_PRIME_SALINE_DIALYZER_SETUP_STATE: currentPrimeState = handlePrimeSalineDialyzerSetupState(); break; case HD_PRIME_SALINE_DIALYZER_STATE: currentPrimeState = handlePrimeSalineDialyzerState(); 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_BYPASS_STATE: currentPrimeState = handlePrimeDialysateBypassState(); break; case HD_PRIME_RESERVOIR_TWO_FILL_COMPLETE_STATE: currentPrimeState = handlePrimeReservoirTwoFillCompleteState(); break; case HD_PRIME_DIALYSATE_DIALYZER_STATE: currentPrimeState = handlePrimeDialysateDialyzerState(); 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(); } /*********************************************************************//** * @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 ) { return (U32)currentPrimeState; } /*********************************************************************//** * @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 = TRUE; REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NONE; if ( currentPrimeState != HD_PRIME_WAIT_FOR_USER_START_STATE ) { accepted = FALSE; rejReason = REQUEST_REJECT_REASON_NOT_ALLOWED_IN_CURRENT_MODE; } else if ( getReservoirFillStatus( DG_RESERVOIR_1 ) != TRUE ) { accepted = FALSE; rejReason = REQUEST_REJECT_REASON_RESERVOIR_ONE_IS_NOT_READY; } else { primeStartRequested = TRUE; } 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 purgeAirDialyzerBloodPumpControl function controls valves and blood pump * to purge air by priming the saline dialyzer fluid path. * @details Inputs: none * @details Outputs: run blood pump, close VDI, VDO, VBA and VBV valves, open VBT valve * @return current state (sub-mode) *************************************************************************/ static void purgeAirDialyzerBloodPumpControl( void ) { 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_SALINE_FLOW_RATE_PURGE_AIR_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); } /*********************************************************************//** * @brief * The purgeAirValvesBloodPumpControl function controls valves and blood pump * to purge air. * @details Inputs: none * @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(); setBloodPumpTargetFlowRate( BLOOD_PUMP_FAST_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(); #ifdef SKIP_UI_INTERACTION primeStartRequested = TRUE; #endif if ( TRUE == primeStartRequested ) { primeStartRequested = FALSE; state = HD_PRIME_SALINE_DIALYZER_SETUP_STATE; } 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; primeSalineDializerInitialLoadCellReading = getLoadCellWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); primeSalineDialyzerStartTime = getMSTimerCount(); purgeAirDialyzerBloodPumpControl(); 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; F32 const loadcellWeight = getLoadCellWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); if ( (loadcellWeight - primeSalineDializerInitialLoadCellReading) > PRIME_SALINE_DIALYZER_STOP_WEIGHT ) { state = HD_PRIME_SALINE_SETUP_STATE; } if ( TRUE == didTimeout( primeSalineDialyzerStartTime, PRIME_SALINE_DIALYZER_TIME_LIMIT ) ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_PRIME_SALINE_DIALYZER_TIME_OUT, PRIME_SALINE_DIALYZER_TIME_LIMIT ); } if ( TRUE == doesAlarmStatusIndicateStop() ) { setupForPrimePause(); state = HD_PRIME_PAUSE; } return state; } /*********************************************************************//** * @brief * The handlePrimeSalineSetupState function setups the Pre-Treatment Prime * Saline Purge Air state. * @details Inputs: state * @details Outputs: control valves to purge air * @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(); if ( TRUE == doesAlarmStatusIndicateStop() ) { setupForPrimePause(); state = HD_PRIME_PAUSE; } return state; } /*********************************************************************//** * @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 * @details Outputs: runs blood pump, control valves to trap air * @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 ) ) { activateAlarmNoData( ALARM_ID_HD_PRIME_PURGE_AIR_TIME_OUT ); } if ( AIR_TRAP_LEVEL_FLUID == getAirTrapLevel( AIR_TRAP_LEVEL_SENSOR_LOWER ) ) { setBloodPumpTargetFlowRate( BLOOD_PUMP_SLOW_FLOW_RATE_PURGE_AIR_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); } 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_FLOW_RATE_CIRC_BLOOD_CIRCUIT_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); noAirDetectedStartTime = getMSTimerCount(); 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 ) ) { cmdSetDGActiveReservoir( DG_RESERVOIR_1 ); if ( TRUE == hasDGCompletedReservoirSwitch() ) { 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 ); U32 bufIndex; for ( bufIndex = 0; bufIndex < CIRC_BUF_SIZE; bufIndex++ ) { loadcellReadings[ bufIndex ] = getLoadCellWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); } currentBufIndex = 0; // initialize circular buffer read/write index primeDialysateBypassStartTime = getMSTimerCount(); loadcellSteadyVolumeStartTime = getMSTimerCount(); #ifdef SKIP_PRIMING state = HD_PRIME_WET_SELF_TESTS_STATE; #else state = HD_PRIME_DIALYSATE_BYPASS_STATE; #endif } } 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 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( loadcellSteadyVolumeStartTime, LOAD_CELL_STEADY_VOLUME_SAMPLING_TIME ) ) { F32 const currentLoadCellValue = getLoadCellWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); F32 const previousLoadCellValue = loadcellReadings[ currentBufIndex % CIRC_BUF_SIZE ]; F32 const loadcellDelta = fabs( currentLoadCellValue - previousLoadCellValue ); // delta of the current (now) and the previous (10 seconds ago) loadcellReadings[ currentBufIndex % CIRC_BUF_SIZE ] = currentLoadCellValue; currentBufIndex++; if ( ( loadcellDelta < LOAD_CELL_VOLUME_NOISE_TOLERANCE ) && ( currentLoadCellValue < ( PRIME_DIALYZER_BYPASS_EXPECTED_VOLUME + PRIME_EXPECTED_VOLUME_TOLERANCE ) ) && // check the steady state volume of R1 ( currentLoadCellValue > ( PRIME_DIALYZER_BYPASS_EXPECTED_VOLUME - PRIME_EXPECTED_VOLUME_TOLERANCE ) ) ) { #ifdef RUN_BLOOD_CIRCUIT_PRIME_AGAIN if ( TRUE == runBloodCircuitPrimeAgain ) { runBloodCircuitPrimeAgain = FALSE; state = HD_PRIME_SALINE_SETUP_STATE; } else { state = HD_PRIME_RESERVOIR_TWO_FILL_COMPLETE_STATE; } #else state = HD_PRIME_RESERVOIR_TWO_FILL_COMPLETE_STATE; #endif } loadcellSteadyVolumeStartTime = getMSTimerCount(); // re-armed the timer for the next iteration } if ( TRUE == didTimeout( primeDialysateBypassStartTime, PRIME_DIALYSATE_BYPASS_TIME_LIMIT ) ) { 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 handlePrimeReservoirTwoFillCompleteState function waits for DG to finish * filling reservoir 2 before moving to pre-treatment dialysate dialyzer state. * @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 ) ) { cmdSetDGActiveReservoir( DG_RESERVOIR_2 ); if ( TRUE == hasDGCompletedReservoirSwitch() ) { 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 ); setDialInPumpTargetFlowRate( DIALYSATE_PUMP_PRIME_FLOW_RATE_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); setDialOutPumpTargetRate( DIALYSATE_PUMP_PRIME_FLOW_RATE_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); U32 bufIndex; for ( bufIndex = 0; bufIndex < CIRC_BUF_SIZE; bufIndex++ ) { loadcellReadings[ bufIndex ] = getLoadCellWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); } currentBufIndex = 0; // initialize circular buffer read/write index loadcellSteadyVolumeStartTime = getMSTimerCount(); primeDialysateDialyzerStartTime = getMSTimerCount(); 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 2 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( loadcellSteadyVolumeStartTime, LOAD_CELL_STEADY_VOLUME_SAMPLING_TIME ) ) { F32 const currentLoadCellValue = getLoadCellWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); F32 const previousLoadCellValue = loadcellReadings[ currentBufIndex % CIRC_BUF_SIZE ]; F32 const loadcellDelta = fabs( currentLoadCellValue - previousLoadCellValue ); loadcellReadings[ currentBufIndex % CIRC_BUF_SIZE ] = currentLoadCellValue; currentBufIndex++; if ( ( loadcellDelta < LOAD_CELL_VOLUME_NOISE_TOLERANCE ) && ( currentLoadCellValue < ( PRIME_DIALYZER_EXPECTED_VOLUME + PRIME_EXPECTED_VOLUME_TOLERANCE ) ) && // check the steady state volume of R2 ( currentLoadCellValue > ( PRIME_DIALYZER_EXPECTED_VOLUME - PRIME_EXPECTED_VOLUME_TOLERANCE ) ) ) { signalDialOutPumpHardStop(); transitionToWetSelfTests(); state = HD_PRIME_WET_SELF_TESTS_STATE; } loadcellSteadyVolumeStartTime = getMSTimerCount(); // re-armed the timer for the next iteration } if ( TRUE == didTimeout( primeDialysateDialyzerStartTime, PRIME_DIALYSATE_DIALYZER_TIME_LIMIT ) ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_PRIME_DIALYSATE_DIALYZER_TIME_OUT, PRIME_DIALYSATE_DIALYZER_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(); } #ifndef SKIP_WET_SELF_TESTS execWetSelfTests(); if ( TRUE == isWetSelfTestsPassed() ) #endif { 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_SALINE_DIALYZER_SETUP_STATE: case HD_PRIME_SALINE_DIALYZER_STATE: state = HD_PRIME_SALINE_DIALYZER_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_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; } /**@}*/