/************************************************************************** * * Copyright (c) 2026-2026 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) Praneeth Bunne * @date (last) 16-Jul-2026 * * @author (original) Praneeth Bunne * @date (original) 16-Jul-2026 * ***************************************************************************/ #include // Used for fabs() functions #include "AirPump.h" #include "AirTrap.h" #include "BloodFlow.h" #include "DDInterface.h" #include "LevelSensors.h" #include "Pressures.h" #include "Prime.h" #include "Switches.h" #include "TaskGeneral.h" #include "Timers.h" #include "TxParams.h" #include "Valves.h" #include "Valve3Way.h" /** * @addtogroup Prime * @{ */ // ********** private definitions ********** /// Interval (ms/task time) at which the prime data is published on the CAN bus. #define PRIME_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) /// Overall time allowed to complete priming. #define PRIME_TOTAL_DURATION_INTERVAL ( 8U * 60U * MS_PER_SECOND ) /// Max time allowed paused before pause timeout alarms. #define PRIME_PAUSE_TIMEOUT_INTERVAL ( 5U * 60U * MS_PER_SECOND ) /// Air pump duty cycle used for active air trap raise/lower during blood circuit prime. #define AIR_PUMP_DUTY_CYCLE_PRIME_PCT 50.0F /// Venous pressure lower bound during active air removal. #define BC_NEG_VEN_PRES_LIMIT_MMHG -150.0F /// Duration limit for bc prime initial reverse state. #define BC_INITIAL_REV_PRIME_INTERVAL ( 40U * MS_PER_SECOND ) /// Duration of pause in bc prime initial reverse state. #define BC_INITIAL_REV_PRIME_PAUSE_INTERVAL ( 500U ) /// Number of neg pressure pause events allowed before alarming. #define BC_INITIAL_REV_MAX_RETRIES 2U /// Max purge time for bc initial dialyzer prime state #define BC_MAX_PURGE_INTERVAL ( 8U * MS_PER_SECOND ) /// Blood tubing set fill flow rate. #define INITIAL_SET_BLOOD_FLOW_RATE 150U /// Blood pump flow rate used while pinch valve is closed during clear stage pinch cycling. #define PINCH_SET_BLOOD_FLOW_RATE 50U /// Blood pump flow rate used during clear stage 3 saline purge. #define SALINE_PURGE_SET_BLOOD_FLOW_RATE 400U /// Blood pump flow rate used during post-prime recirculation. #define RECIRC_SET_BLOOD_FLOW_RATE 300U /// Max time allowed for each blood circuit clear stage. #define SLOW_PURGE_INTERVAL ( 20U * MS_PER_SECOND ) /// Duration the arterial pinch valve stays closed during each pinch cycle. #define PINCH_INTERVAL_MS ( 5U * MS_PER_SECOND ) ///< Target venous pressure during pinching #define TARGET_VEN_PRES_LIMIT_MMHG 200.0F /// Target dialysate flow rate used to calculate dialysate circuit initial prime duration. #define TARGET_DIALYSATE_FLOW_RATE 600U /// Venous pressure limit above which dialysate circuit prime pauses to depressurize. #define DC_PRIME_VEN_PRES_LIMIT_MMHG 250U /// Venous pressure limit below which dialysate circuit prime resumes after depressurizing. #define DC_PRIME_RESUME_VEN_PRES_LIMIT_MMHG 150U /// Max time allowed for venous pressure to stabilize during dialysate circuit depressurize. #define VEN_PRES_STABILIZATION_TIMEOUT_INTERVAL_MS ( 5U * MS_PER_SECOND ) /// Estimated blood tubing set cartridge volume, used to calculate prime discard duration. #define CARTRIDGE_VOLUME 123.8F /// Maximum ultrafiltration pump (D76) speed used during priming fluid discard and reverse prime. #define MAX_UF_RATE 200U /// Duration of each forward pass in the blood pump reversal sequence. #define BC2_FWD_RECIRC_INTERVAL ( 25U * MS_PER_SECOND ) /// Duration of the venous pinch pressure cycling window within each forward pass. #define BC2_PINCH_CYCLE_INTERVAL ( 15U * MS_PER_SECOND ) /// Duration of the reverse pass in the blood pump reversal sequence. #define BC2_REV_RECIRC_INTERVAL ( 10U * MS_PER_SECOND ) /// Blood pump stop duration between forward and reverse passes. #define BC2_PAUSE_INTERVAL ( 1U * MS_PER_SECOND ) /// Venous pressure limit above which the venous pinch valve closes during forward pass pinch cycling. #define BC2_MAX_VEN_PRES_LIMIT_MMHG 200U /// Venous pressure limit below which the venous pinch valve reopens during forward pass pinch cycling. #define BC2_STABLE_VEN_PRES_LIMIT_MMHG 100U // ********** private data ********** static TD_PRIME_STATE_T currentPrimeState; ///< Current state of the Prime state machine. static TD_PRIME_STATE_T prevPrimeState; ///< Previous state of the Prime state machine. static TD_PRIME_STATE_T nextStateAfterAirTrapLower; ///< Next state to transition to once air trap lowers. static U32 primeStartTime; ///< Priming start time (ms), used for elapsed time / pause adjustment. static U32 primePauseStartTime; ///< Time pause was entered (ms). static U32 primePublishTimerCtr; ///< Timer counter for next status broadcast. static U32 primeElapsedAtPauseMs; ///< Elapsed active prime time captured at pause entry. static U32 stateEntryTimerCtr; ///< Timer counter for state entry. static U32 bcRevPrimePauseTimerCtr; ///< Timer counter for bc initial rev prime pause. static U32 bcRevPrimeRetryCtr; ///< Counter for bc rev prime retry. static U32 pinchStartTime; ///< Timer counter for pinch valve cycle. static U32 dialyzerDialysateVolume; ///< Selected dialyzer jacket (dialysate) volume (mL). static U32 dilayzerBloodVolume; ///< Selected dialyzer core (blood) volume (mL). static U32 dcDepressurizeElapsedMs; ///< Timer counter for dc depressurize venous pressure stabilization. static U32 dcInitialPrimeInterval; ///< Calculated dc initial dialyzer prime duration (ms). static U32 dcDiscardDuration; ///< Calculated priming fluid discard duration (ms). static U32 dcReversePrimeDuration; ///< Calculated dialyzer reverse prime duration (ms). static U32 dcDiscardAndRevPrimeInterval; ///< Greater of dcDiscardDuration and dcReversePrimeDuration (ms). static BOOL revRecircVisited; ///< Flag indicates bc2 reverse recirc pass has been completed. static BOOL primeResumeRequested; ///< Flag indicates user requested resume from pause. // ********** private function prototypes ********** static TD_PRIME_STATE_T handlePrimeBCInitialReverseState( void ); static TD_PRIME_STATE_T handlePrimeBCWaitForAirTrapLowerState( void ); static TD_PRIME_STATE_T handlePrimeBCInitialDialyzerState( void ); static TD_PRIME_STATE_T handlePrimeBCFillState( void ); static TD_PRIME_STATE_T handlePrimeBCClearState1( void ); static TD_PRIME_STATE_T handlePrimeBCClearState2( void ); static TD_PRIME_STATE_T handlePrimeBCClearState3( void ); static TD_PRIME_STATE_T handlePrimeWaitForDialysateReadyState( void ); static TD_PRIME_STATE_T handlePrimeDCInitialDialyzerState( void ); static TD_PRIME_STATE_T handlePrimeDCDepressurizeState( void ); static TD_PRIME_STATE_T handleWaitForAirTrapFillState( void ); static TD_PRIME_STATE_T handleBC2ForwardRecircState( void ); static TD_PRIME_STATE_T handleBC2PauseToRotateOppDirState( void ); static TD_PRIME_STATE_T handleBC2ReverserRecircState( void ); static TD_PRIME_STATE_T handlePrimeDiscardAndReversePrimeState( void ); static TD_PRIME_STATE_T handlePrimePauseState( void ); static void resetPrimeFlags( void ); static void publishPrimeData( void ); /*********************************************************************//** * @brief * The initPrime function initializes the prime service module. * This function will reset anything required before the start of priming sequence. * @details \b Inputs: none * @details \b Outputs: Prime service initialized. * @return none *************************************************************************/ void initPrime( void ) { currentPrimeState = PRIME_START_STATE; prevPrimeState = PRIME_START_STATE; nextStateAfterAirTrapLower = PRIME_BC_INITIAL_DIALYZER_STATE; primeStartTime = 0; primePauseStartTime = 0; primePublishTimerCtr = PRIME_DATA_PUB_INTERVAL - 8; stateEntryTimerCtr = 0; bcRevPrimePauseTimerCtr = 0; bcRevPrimeRetryCtr = 0; dcDepressurizeElapsedMs = 0; revRecircVisited = FALSE; primeResumeRequested = FALSE; dialyzerDialysateVolume = getDialyzerDialysateVolume( getTreatmentParameterF32( TREATMENT_PARAM_DIALYZER_TYPE ) ); dilayzerBloodVolume = getDialyzerBloodVolume( getTreatmentParameterF32( TREATMENT_PARAM_DIALYZER_TYPE ) ); dcInitialPrimeInterval = (U32)( ( ( dialyzerDialysateVolume * 60.0F ) / (F32)TARGET_DIALYSATE_FLOW_RATE + 30.0F ) * (F32)MS_PER_SECOND ); dcDiscardDuration = (U32)( ( ( dilayzerBloodVolume + CARTRIDGE_VOLUME ) * 1.5F ) / MAX_UF_RATE ) * (F32)( SEC_PER_MIN * MS_PER_SECOND ) ; dcReversePrimeDuration = (U32)( ( ( dialyzerDialysateVolume * 0.5F * (F32)SEC_PER_MIN ) / (F32)MAX_UF_RATE ) + 30.0F ) * (F32)MS_PER_SECOND; dcDiscardAndRevPrimeInterval = ( dcDiscardDuration > dcReversePrimeDuration ) ? dcDiscardDuration : dcReversePrimeDuration; } /*********************************************************************//** * @brief * The setPrimeStateTransition function performs the actuator setup * (valves, blood pump, air pump/trap) required upon entry into the given * Prime state. Called both during normal forward state progression and * during pause/resume to deterministically re-establish actuator state. * @details \b Inputs: nextStateAfterAirTrapLower, revRecircVisited * @details \b Outputs: stateEntryTimerCtr, pinchStartTime, actuator commands * @param state target Prime state being transitioned into * @return none *************************************************************************/ static void setPrimeStateTransition( TD_PRIME_STATE_T state ) { switch ( state ) { case PRIME_BC_INITIAL_REVERSE_STATE: signalBloodPumpHardStop(); setValvePosition( H1_VALV, VALVE_POSITION_A_INSERT_EJECT ); setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); set3WayValveState( H20_VALV, VALVE_3WAY_COMMON_TO_OPEN_STATE ); setAirPumpState( AIR_PUMP_STATE_ON, AIR_PUMP_DUTY_CYCLE_PRIME_PCT ); stateEntryTimerCtr = getMSTimerCount(); bcRevPrimePauseTimerCtr = 0; bcRevPrimeRetryCtr = 0; break; case PRIME_BC_AIR_TRAP_LOWER_STATE: if ( PRIME_BC_INITIAL_DIALYZER_STATE == nextStateAfterAirTrapLower ) { // Leaving Venous Line state -- stop the H12/H20 reverse-prime drive. setAirPumpState( AIR_PUMP_STATE_OFF, AIR_PUMP_MOTOR_OFF ); } else { // Leaving Dialyzer Purge state -- stop the forward BP purge. signalBloodPumpHardStop(); setValvePosition( H1_VALV, VALVE_POSITION_B_OPEN ); } set3WayValveState( H20_VALV, VALVE_3WAY_COMMON_TO_CLOSED_STATE ); set3WayValveState( H13_VALV, VALVE_3WAY_COMMON_TO_OPEN_STATE ); setAirPumpState( AIR_PUMP_STATE_ON, AIR_PUMP_DUTY_CYCLE_PRIME_PCT ); break; case PRIME_BC_INITIAL_DIALYZER_STATE: setValvePosition( H1_VALV, VALVE_POSITION_C_CLOSE ); setBloodPumpTargetFlowRate( MAX_SET_BLOOD_FLOW_RATE, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); stateEntryTimerCtr = getMSTimerCount(); break; case PRIME_BC_FILL_STATE: setValvePosition( H1_VALV, VALVE_POSITION_C_CLOSE ); setBloodPumpTargetFlowRate( INITIAL_SET_BLOOD_FLOW_RATE, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); break; case PRIME_BC_CLEAR_STATE1: signalBloodPumpHardStop(); set3WayValveState( H20_VALV, VALVE_3WAY_COMMON_TO_CLOSED_STATE ); setValvePosition( H1_VALV, VALVE_POSITION_B_OPEN ); setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); setBloodPumpTargetFlowRate( INITIAL_SET_BLOOD_FLOW_RATE, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); stateEntryTimerCtr = getMSTimerCount(); pinchStartTime = getMSTimerCount(); break; case PRIME_BC_CLEAR_STATE2: setValvePosition( H1_VALV, VALVE_POSITION_B_OPEN ); setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); setBloodPumpTargetFlowRate( MAX_SET_BLOOD_FLOW_RATE, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); stateEntryTimerCtr = getMSTimerCount(); break; case PRIME_BC_CLEAR_STATE3: setValvePosition( H1_VALV, VALVE_POSITION_B_OPEN ); setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); setBloodPumpTargetFlowRate( SALINE_PURGE_SET_BLOOD_FLOW_RATE, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); stateEntryTimerCtr = getMSTimerCount(); pinchStartTime = getMSTimerCount(); break; case PRIME_WAIT_FOR_DIALYSATE_READY_STATE: signalBloodPumpHardStop(); setValvePosition( H1_VALV, VALVE_POSITION_B_OPEN ); setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); break; case PRIME_DC_INITIAL_DIALYSATE_STATE: cmdBypassDialyzer( FALSE ); stateEntryTimerCtr = getMSTimerCount(); dcDepressurizeElapsedMs = getMSTimerCount(); break; case PRIME_DC_DEPRESSURIZE_STATE: // TODO: Remove if not required in future. //cmdDDChamberHValveOpen( TRUE ); break; case PRIME_WAIT_FOR_AIR_TRAP_FILL_STATE: cmdBypassDialyzer( TRUE ); startAirTrapControl(); break; case PRIME_BC2_FORWARD_RECIRC_STATE: setValvePosition( H1_VALV, VALVE_POSITION_A_INSERT_EJECT ); setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); setBloodPumpTargetFlowRate( MAX_SET_BLOOD_FLOW_RATE, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); stateEntryTimerCtr = getMSTimerCount(); break; case PRIME_BC2_PAUSE_TO_ROTATE_OPP_DIR_STATE: signalBloodPumpHardStop(); stateEntryTimerCtr = getMSTimerCount(); break; case PRIME_BC2_REVERSE_RECIRC_STATE: revRecircVisited = TRUE; setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); setBloodPumpTargetFlowRate( MAX_SET_BLOOD_FLOW_RATE, MOTOR_DIR_REVERSE, PUMP_CONTROL_MODE_OPEN_LOOP ); stateEntryTimerCtr = getMSTimerCount(); break; case PRIME_DC_DISCARD_AND_REVERSE_PRIME_STATE: signalBloodPumpHardStop(); cmdDDprimeFluidDiscard( TRUE ); //cmdChangeQuf( MAX_UF_RATE ); setValvePosition( H1_VALV, VALVE_POSITION_A_INSERT_EJECT ); setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); setBloodPumpTargetFlowRate( RECIRC_SET_BLOOD_FLOW_RATE, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); stateEntryTimerCtr = getMSTimerCount(); break; case PRIME_PAUSE_STATE: signalBloodPumpHardStop(); setValvePosition( H1_VALV, VALVE_POSITION_C_CLOSE ); setValvePosition( H19_VALV, VALVE_POSITION_C_CLOSE ); setAirPumpState( AIR_PUMP_STATE_OFF, AIR_PUMP_MOTOR_OFF ); set3WayValveState( H13_VALV, VALVE_3WAY_COMMON_TO_CLOSED_STATE ); set3WayValveState( H20_VALV, VALVE_3WAY_COMMON_TO_CLOSED_STATE ); cmdBypassDialyzer( TRUE ); cmdDDprimeFluidDiscard( FALSE ); primePauseStartTime = getMSTimerCount(); prevPrimeState = currentPrimeState; break; case PRIME_COMPLETE_STATE: cmdDDprimeFluidDiscard( FALSE ); signalBloodPumpHardStop(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_MODE_PRE_TX_PRIME_INVALID_STATE, (U32)state ); break; } } /*********************************************************************//** * @brief * The execPrime function executes the prime service state machine. * @details \b Inputs: currentPrimeState * @details \b Outputs: currentPrimeState * @return none *************************************************************************/ void execPrime( void ) { TD_PRIME_STATE_T priorPrimeState = currentPrimeState; // Does alarm indicate stop? if ( TRUE == doesAlarmStatusIndicateStop() ) { setPrimeStateTransition( PRIME_PAUSE_STATE ); currentPrimeState = PRIME_PAUSE_STATE; } // execute prime service state machine switch ( currentPrimeState ) { case PRIME_START_STATE: setPrimeStateTransition ( PRIME_BC_INITIAL_REVERSE_STATE ); currentPrimeState = PRIME_BC_INITIAL_REVERSE_STATE; break; case PRIME_BC_INITIAL_REVERSE_STATE: currentPrimeState = handlePrimeBCInitialReverseState(); break; case PRIME_BC_AIR_TRAP_LOWER_STATE: currentPrimeState = handlePrimeBCWaitForAirTrapLowerState(); break; case PRIME_BC_INITIAL_DIALYZER_STATE: currentPrimeState = handlePrimeBCInitialDialyzerState(); break; case PRIME_BC_FILL_STATE: currentPrimeState = handlePrimeBCFillState(); break; case PRIME_BC_CLEAR_STATE1: currentPrimeState = handlePrimeBCClearState1(); break; case PRIME_BC_CLEAR_STATE2: currentPrimeState = handlePrimeBCClearState2(); break; case PRIME_BC_CLEAR_STATE3: currentPrimeState = handlePrimeBCClearState3(); break; case PRIME_WAIT_FOR_DIALYSATE_READY_STATE: currentPrimeState = handlePrimeWaitForDialysateReadyState(); break; case PRIME_DC_INITIAL_DIALYSATE_STATE: currentPrimeState = handlePrimeDCInitialDialyzerState(); break; case PRIME_DC_DEPRESSURIZE_STATE: currentPrimeState = handlePrimeDCDepressurizeState(); break; case PRIME_WAIT_FOR_AIR_TRAP_FILL_STATE: currentPrimeState = handleWaitForAirTrapFillState(); break; case PRIME_BC2_FORWARD_RECIRC_STATE: currentPrimeState = handleBC2ForwardRecircState(); break; case PRIME_BC2_PAUSE_TO_ROTATE_OPP_DIR_STATE: currentPrimeState = handleBC2PauseToRotateOppDirState(); break; case PRIME_BC2_REVERSE_RECIRC_STATE: currentPrimeState = handleBC2ReverserRecircState(); break; case PRIME_DC_DISCARD_AND_REVERSE_PRIME_STATE: currentPrimeState = handlePrimeDiscardAndReversePrimeState(); break; case PRIME_PAUSE_STATE: currentPrimeState = handlePrimePauseState(); break; case PRIME_COMPLETE_STATE: // ok, do nothing here. pre-treatment will move on to recirculate once we get here. break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SOFTWARE_FAULT, SW_FAULT_ID_MODE_PRE_TX_PRIME_INVALID_STATE, currentPrimeState ); break; } // Prime flags should be handled by now resetPrimeFlags(); if ( priorPrimeState != currentPrimeState ) { SEND_EVENT_WITH_2_U32_DATA( TD_EVENT_SUB_STATE_CHANGE, priorPrimeState, currentPrimeState ); } // Broadcast priming data publishPrimeData(); } /*********************************************************************//** * @brief * The getCurrentPrimeState function returns the current state of prime sub-mode. * @details \b Inputs: currentPrimeState * @details \b Outputs: none * @return current prime state *************************************************************************/ TD_PRIME_STATE_T getCurrentPrimeState( void ) { return currentPrimeState; } /*********************************************************************//** * @brief * The signalResumePrime function signals the prime service to resume * previous operation. * @details \b Inputs: none * @details \b Outputs: primeResumeRequested * @return none *************************************************************************/ void signalResumePrime( void ) { primeResumeRequested = TRUE; } /*********************************************************************//** * @brief * The resetPrimeFlags function resets all prime signal flags. * @details \b Inputs: none * @details \b Outputs: signal flags set to FALSE * @return none *************************************************************************/ static void resetPrimeFlags( void ) { primeResumeRequested = FALSE; } /*********************************************************************//** * @brief * The handlePrimeBcVenousLineState function handles prime blood circuit * initial reverse state of prime service. Primes the venous line and air trap * via active air removal until H17 reads fluid. * @details \b Inputs: stateEntryTimerCtr, bcRevPrimePauseTimerCtr, bcRevPrimeRetryCtr * @details \b Outputs: stateEntryTimerCtr, bcRevPrimePauseTimerCtr, bcRevPrimeRetryCtr * nextStateAfterAirTrapLower * @return next Prime state *************************************************************************/ static TD_PRIME_STATE_T handlePrimeBCInitialReverseState( void ) { TD_PRIME_STATE_T state = PRIME_BC_INITIAL_REVERSE_STATE; F32 venPres = getFilteredVenousPressure(); // Does h17 reads fluid? if ( AIR_TRAP_LEVEL_FLUID == getLevelSensorState( H17_LEVL ) ) { nextStateAfterAirTrapLower = PRIME_BC_INITIAL_DIALYZER_STATE; setPrimeStateTransition ( PRIME_BC_AIR_TRAP_LOWER_STATE ); state = PRIME_BC_AIR_TRAP_LOWER_STATE; } else if ( AIR_PUMP_STATE_ON == getAirPumpState() ) { // Currently running - keep pulling, unless negative pressure limit hit if ( venPres <= BC_NEG_VEN_PRES_LIMIT_MMHG ) { setAirPumpState( AIR_PUMP_STATE_OFF, AIR_PUMP_MOTOR_OFF ); set3WayValveState( H20_VALV, VALVE_3WAY_COMMON_TO_CLOSED_STATE ); bcRevPrimePauseTimerCtr = getMSTimerCount(); bcRevPrimeRetryCtr++; // Max retries exceeded? if ( bcRevPrimeRetryCtr >= BC_INITIAL_REV_MAX_RETRIES ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_TD_INITIAL_BC_REV_PRIME_LIMIT_REACHED, bcRevPrimeRetryCtr ); } } else { set3WayValveState( H20_VALV, VALVE_3WAY_COMMON_TO_OPEN_STATE ); setAirPumpState( AIR_PUMP_STATE_ON, AIR_PUMP_DUTY_CYCLE_PRIME_PCT ); } } // Pause time reached? else if ( TRUE == didTimeout( bcRevPrimePauseTimerCtr, BC_INITIAL_REV_PRIME_PAUSE_INTERVAL ) ) { // Paused, recheck delay elapsed - exclude the paused interval from the 40s timer, then resume stateEntryTimerCtr += calcTimeSince( bcRevPrimePauseTimerCtr ); setAirPumpState( AIR_PUMP_STATE_ON, AIR_PUMP_DUTY_CYCLE_PRIME_PCT ); set3WayValveState( H20_VALV, VALVE_3WAY_COMMON_TO_OPEN_STATE ); } else { // Paused, still waiting on the 0.5s recheck delay - no action. } // Max time exceeded for this state? if ( TRUE == didTimeout( stateEntryTimerCtr, BC_INITIAL_REV_PRIME_INTERVAL ) ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_TD_INITIAL_BC_REV_PRIME_TIMEOUT, BC_INITIAL_REV_PRIME_INTERVAL ); } return state; } /*********************************************************************//** * @brief * The handlePrimeBCAirTrapLowerState function handles wait for air trap lower * state of prime service. Actively lowers the air trap fluid level until H17 reads air, * then transitions to whichever state requested the lower operation. * @details Inputs: none * @details Outputs: none * @return next Prime state *************************************************************************/ static TD_PRIME_STATE_T handlePrimeBCWaitForAirTrapLowerState( void ) { TD_PRIME_STATE_T state = PRIME_BC_AIR_TRAP_LOWER_STATE; // Does h17 reads air? if ( AIR_TRAP_LEVEL_AIR == getLevelSensorState( H17_LEVL ) ) { setPrimeStateTransition ( nextStateAfterAirTrapLower ); state = nextStateAfterAirTrapLower; } return state; } /*********************************************************************//** * @brief * The handlePrimeBCInitialDialyzerState function handles the prime blood circuit * initial dialyzer state of prime service. Runs BP at maximum rate to purge * the residual air pocket from the blood pump tubing until H17 reads fluid * or the max purge interval elapses. * @details \b Inputs: stateEntryTimerCtr * @details \b Outputs: nextStateAfterAirTrapLower * @return next Prime state *************************************************************************/ static TD_PRIME_STATE_T handlePrimeBCInitialDialyzerState( void ) { TD_PRIME_STATE_T state = PRIME_BC_INITIAL_DIALYZER_STATE; // Does h17 reads fluid or max time reached? if ( ( AIR_TRAP_LEVEL_FLUID == getLevelSensorState( H17_LEVL ) ) || ( TRUE == didTimeout( stateEntryTimerCtr, BC_MAX_PURGE_INTERVAL ) ) ) { nextStateAfterAirTrapLower = PRIME_BC_FILL_STATE; setPrimeStateTransition ( PRIME_BC_AIR_TRAP_LOWER_STATE ); state = PRIME_BC_AIR_TRAP_LOWER_STATE; } return state; } /*********************************************************************//** * @brief * The handlePrimeBCFillState function handles the prime blood circuit * fill state of prime service. Fills the dialyzer core at the initial * blood flow rate until H17 reads fluid. * @details \b Inputs: none * @details \b Outputs: none * @return next Prime state *************************************************************************/ static TD_PRIME_STATE_T handlePrimeBCFillState( void ) { TD_PRIME_STATE_T state = PRIME_BC_FILL_STATE; // Does h17 reads fluid? if ( AIR_TRAP_LEVEL_FLUID == getLevelSensorState( H17_LEVL ) ) { setPrimeStateTransition( PRIME_BC_CLEAR_STATE1 ); state = PRIME_BC_CLEAR_STATE1; } return state; } /*********************************************************************//** * @brief * The handlePrimeBCClearState1 function handles prime blood circuit clear * stage 1 of prime service. Runs BP at the 150 mL/min, cycling the arterial * pinch valve every 5s to build venous pressure and clear residual air, * while maintaining the air trap level, until 20s elapses. * @details \b Inputs: stateEntryTimerCtr, pinchStartTime * @details \b Outputs: stateEntryTimerCtr, pinchStartTime * @return next Prime state *************************************************************************/ static TD_PRIME_STATE_T handlePrimeBCClearState1( void ) { TD_PRIME_STATE_T state = PRIME_BC_CLEAR_STATE1; // Max time for this state - 20 secs if ( TRUE == didTimeout( stateEntryTimerCtr, SLOW_PURGE_INTERVAL ) ) { setPrimeStateTransition ( PRIME_BC_CLEAR_STATE2 ); state = PRIME_BC_CLEAR_STATE2; } // Check pressure on each pinch else if ( VALVE_POSITION_C_CLOSE == getValvePosition( H1_VALV ) ) { if ( getFilteredVenousPressure() >= TARGET_VEN_PRES_LIMIT_MMHG ) { setValvePosition( H1_VALV, VALVE_POSITION_B_OPEN ); setBloodPumpTargetFlowRate( INITIAL_SET_BLOOD_FLOW_RATE, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); pinchStartTime = getMSTimerCount(); } } //Check if airtrap is filling else if ( TRUE == isAirTrapControlling() ) { if ( AIR_TRAP_LEVEL_FLUID == getLevelSensorState( H17_LEVL ) ) { endAirTrapControl(); stateEntryTimerCtr = getMSTimerCount(); pinchStartTime = getMSTimerCount(); } } // Air trap refill else if ( AIR_TRAP_LEVEL_AIR == getLevelSensorState( H17_LEVL ) ) { startAirTrapControl(); } // 5 secs pinch time check else if ( TRUE == didTimeout( pinchStartTime, PINCH_INTERVAL_MS ) ) { setValvePosition( H1_VALV, VALVE_POSITION_C_CLOSE ); setBloodPumpTargetFlowRate( INITIAL_SET_BLOOD_FLOW_RATE, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); } else { // No action required, continue for 5 secs } return state; } /*********************************************************************//** * @brief * The handlePrimeBCClearState2 function handles prime blood circuit clear * stage 2 of prime service. Runs BP at maximum rate for a fixed 20s, * maintaining air trap level, to clear residual air pockets. * @details \b Inputs: stateEntryTimerCtr * @details \b Outputs: stateEntryTimerCtr, pinchStartTime * @return next Prime state *************************************************************************/ static TD_PRIME_STATE_T handlePrimeBCClearState2( void ) { TD_PRIME_STATE_T state = PRIME_BC_CLEAR_STATE2; // Max time for this state - 20 secs if ( TRUE == didTimeout( stateEntryTimerCtr, SLOW_PURGE_INTERVAL ) ) { setPrimeStateTransition( PRIME_BC_CLEAR_STATE3 ); state = PRIME_BC_CLEAR_STATE3; } //Check if airtrap is filling else if ( TRUE == isAirTrapControlling() ) { if ( AIR_TRAP_LEVEL_FLUID == getLevelSensorState( H17_LEVL ) ) { endAirTrapControl(); stateEntryTimerCtr = getMSTimerCount(); pinchStartTime = getMSTimerCount(); } } // Air trap refill else if ( AIR_TRAP_LEVEL_AIR == getLevelSensorState( H17_LEVL ) ) { startAirTrapControl(); } else { // No action required, continue. } return state; } /*********************************************************************//** * @brief * The handlePrimeBCClearState3 function handles prime blood circuit clear * stage 3 of prime service. Runs BP at 400 mL/min, cycling the arterial * pinch valve to build venous pressure while maintaining air trap level, until 20s elapses. * @details \b Inputs: stateEntryTimerCtr, pinchStartTime * @details \b Outputs: stateEntryTimerCtr, pinchStartTime * @return next Prime state *************************************************************************/ static TD_PRIME_STATE_T handlePrimeBCClearState3( void ) { TD_PRIME_STATE_T state = PRIME_BC_CLEAR_STATE3; // Max time for this state - 20 secs if ( TRUE == didTimeout( stateEntryTimerCtr, SLOW_PURGE_INTERVAL ) ) { setPrimeStateTransition( PRIME_WAIT_FOR_DIALYSATE_READY_STATE ); state = PRIME_WAIT_FOR_DIALYSATE_READY_STATE; } // Check pressure on each pinch else if ( VALVE_POSITION_C_CLOSE == getValvePosition( H1_VALV ) ) { if ( getFilteredVenousPressure() >= TARGET_VEN_PRES_LIMIT_MMHG ) { setValvePosition( H1_VALV, VALVE_POSITION_B_OPEN ); setBloodPumpTargetFlowRate( SALINE_PURGE_SET_BLOOD_FLOW_RATE, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); pinchStartTime = getMSTimerCount(); } } //Check if airtrap is filling else if ( TRUE == isAirTrapControlling() ) { if ( AIR_TRAP_LEVEL_FLUID == getLevelSensorState( H17_LEVL ) ) { endAirTrapControl(); stateEntryTimerCtr = getMSTimerCount(); pinchStartTime = getMSTimerCount(); } } // Air trap refill else if ( AIR_TRAP_LEVEL_AIR == getLevelSensorState( H17_LEVL ) ) { startAirTrapControl(); } // 5 secs pinch time check else if ( TRUE == didTimeout( pinchStartTime, PINCH_INTERVAL_MS ) ) { setValvePosition( H1_VALV, VALVE_POSITION_C_CLOSE ); setBloodPumpTargetFlowRate( SALINE_PURGE_SET_BLOOD_FLOW_RATE, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); } else { // No action required, continue for 5 secs } return state; } /*********************************************************************//** * @brief * The handlePrimeWaitForDialysateReadyState function handles the Wait * for dilaysate circuit of prime service. Waits for the DD until it is * ready to deliver dialysate before starting dialysate circuit prime. * @details \b Inputs: none * @details \b Outputs: none * @return next Prime state *************************************************************************/ static TD_PRIME_STATE_T handlePrimeWaitForDialysateReadyState( void ) { TD_PRIME_STATE_T state = PRIME_WAIT_FOR_DIALYSATE_READY_STATE; if ( TRUE == getDialysateGoodToDeliverStatus() ) { setPrimeStateTransition( PRIME_DC_INITIAL_DIALYSATE_STATE ); state = PRIME_DC_INITIAL_DIALYSATE_STATE; } return state; } /*********************************************************************//** * @brief * The handlePrimeDCInitialDialyzerState function handles dialysate circuit * initial dialyzer state of prime prime service. Passes dialysate through * dialyzer. * @details \b Inputs: stateEntryTimerCtr, dcInitialPrimeInterval * @details \b Outputs: none * @return next Prime state *************************************************************************/ static TD_PRIME_STATE_T handlePrimeDCInitialDialyzerState( void ) { TD_PRIME_STATE_T state = PRIME_DC_INITIAL_DIALYSATE_STATE; F32 venPres = getFilteredVenousPressure(); if ( fabs( venPres ) > DC_PRIME_VEN_PRES_LIMIT_MMHG ) { setPrimeStateTransition( PRIME_DC_DEPRESSURIZE_STATE ); state = PRIME_DC_DEPRESSURIZE_STATE; } else if ( DD_GEND_DIALYSATE_DELIVERY_STATE == getCurrentGenDState() ) { if ( TRUE == didTimeout( stateEntryTimerCtr, dcInitialPrimeInterval ) ) { cmdBypassDialyzer( TRUE ); startAirTrapControl(); state = PRIME_WAIT_FOR_AIR_TRAP_FILL_STATE; } } else { // No action required. } return state; } /*********************************************************************//** * @brief * The handlePrimeDCDepressurizeState function handles dialysate circuit * depressurize state of prime service. Waits for pressure to return * within the resume limit, alarming if stabilization does not occur * within 5s. * @details \b Alarm: ALARM_ID_TD_VENOUS_PRESSURE_HIGH if venous pressure does * not stabilize within 5s. * @details \b Inputs: dcDepressurizeElapsedMs * @details \b Outputs: dcDepressurizeElapsedMs * @return next Prime state *************************************************************************/ static TD_PRIME_STATE_T handlePrimeDCDepressurizeState( void ) { TD_PRIME_STATE_T state = PRIME_DC_DEPRESSURIZE_STATE; F32 venPres = getFilteredVenousPressure(); if ( fabs( venPres ) <= DC_PRIME_RESUME_VEN_PRES_LIMIT_MMHG ) { //cmdDDChamberHValveOpen( FALSE ); setPrimeStateTransition ( PRIME_DC_INITIAL_DIALYSATE_STATE ); state = PRIME_DC_INITIAL_DIALYSATE_STATE; } else if ( ++dcDepressurizeElapsedMs >= VEN_PRES_STABILIZATION_TIMEOUT_INTERVAL_MS ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_TD_VENOUS_PRESSURE_HIGH, (U32)venPres ); } // else if ( DD_GEND_SPENT_CHAMBER_VENT_OPEN_STATE == getCurrentGenDState() ) // { // // if ( ++dcDepressurizeElapsedMs >= VEN_PRES_STABILIZATION_TIMEOUT_INTERVAL_MS ) // { // SET_ALARM_WITH_1_U32_DATA( ALARM_ID_TD_VENOUS_PRESSURE_HIGH, (U32)venPres ); // } // } else { // No action required. } return state; } /*********************************************************************//** * @brief * The handleWaitForAirTrapFillState function waits for the air trap to * fill to the upper level sensor (H16) before starting the blood pump * reversal sequence. * @details \b Inputs: none * @details \b Outputs: none * @return next Prime state *************************************************************************/ static TD_PRIME_STATE_T handleWaitForAirTrapFillState( void ) { TD_PRIME_STATE_T state = PRIME_WAIT_FOR_AIR_TRAP_FILL_STATE; if ( AIR_TRAP_LEVEL_FLUID == getLevelSensorState( H16_LEVL ) ) { setPrimeStateTransition( PRIME_BC2_FORWARD_RECIRC_STATE ); state = PRIME_BC2_FORWARD_RECIRC_STATE; } return state; } /*********************************************************************//** * @brief * The handleBC2ForwardRecircState function handles the prime forward recirc * state of prime service. Runs BP forward at maximum rate, cycling the * venous pinch valve against pressure limits for the first 15s, then holding * it open for 10s. * @details \b Inputs: stateEntryTimerCtr, revRecircVisited * @details \b Outputs: none * @return next Prime state *************************************************************************/ static TD_PRIME_STATE_T handleBC2ForwardRecircState( void ) { TD_PRIME_STATE_T state = PRIME_BC2_FORWARD_RECIRC_STATE; F32 venPres = getFilteredVenousPressure(); if ( TRUE == didTimeout( stateEntryTimerCtr, BC2_FWD_RECIRC_INTERVAL ) ) { if ( FALSE == revRecircVisited ) { setPrimeStateTransition( PRIME_BC2_PAUSE_TO_ROTATE_OPP_DIR_STATE ); state = PRIME_BC2_PAUSE_TO_ROTATE_OPP_DIR_STATE; } else { setPrimeStateTransition( PRIME_DC_DISCARD_AND_REVERSE_PRIME_STATE ); state = PRIME_DC_DISCARD_AND_REVERSE_PRIME_STATE; } } //First 15 sec else if ( FALSE == didTimeout( stateEntryTimerCtr, BC2_PINCH_CYCLE_INTERVAL ) ) { if ( venPres >= BC2_MAX_VEN_PRES_LIMIT_MMHG ) { setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); } else if ( venPres <= BC2_STABLE_VEN_PRES_LIMIT_MMHG ) { setValvePosition( H19_VALV, VALVE_POSITION_C_CLOSE ); } else { // No action required } } // Remaining 10 sec, open else { setValvePosition( H19_VALV, VALVE_POSITION_B_OPEN ); } return state; } /*********************************************************************//** * @brief * The handleBC2PauseToRotateOppDirState function handles the Pause to rotate * opp direction state of prime service. Pauses 1s between forward and * reverse passes of the blood pump reversal sequence. * @details \b Inputs: stateEntryTimerCtr, revRecircVisited * @details \b Outputs: none * @return next Prime state *************************************************************************/ static TD_PRIME_STATE_T handleBC2PauseToRotateOppDirState( void ) { TD_PRIME_STATE_T state = PRIME_BC2_PAUSE_TO_ROTATE_OPP_DIR_STATE; if ( TRUE == didTimeout( stateEntryTimerCtr, BC2_PAUSE_INTERVAL ) ) { if ( FALSE == revRecircVisited ) { setPrimeStateTransition( PRIME_BC2_REVERSE_RECIRC_STATE ); state = PRIME_BC2_REVERSE_RECIRC_STATE; } else { setPrimeStateTransition( PRIME_BC2_FORWARD_RECIRC_STATE ); state = PRIME_BC2_FORWARD_RECIRC_STATE; } } return state; } /*********************************************************************//** * @brief * The handleBC2ReverserRecircState function handles the prime forward recirc * state of prime service. Rotates BP in reverse at maximum rate for 10s * before pausing. * @details \b Inputs: stateEntryTimerCtr * @details \b Outputs: none * @return next Prime state *************************************************************************/ static TD_PRIME_STATE_T handleBC2ReverserRecircState( void ) { TD_PRIME_STATE_T state = PRIME_BC2_REVERSE_RECIRC_STATE; if ( TRUE == didTimeout( stateEntryTimerCtr, BC2_REV_RECIRC_INTERVAL ) ) { setPrimeStateTransition( PRIME_BC2_PAUSE_TO_ROTATE_OPP_DIR_STATE ); state = PRIME_BC2_PAUSE_TO_ROTATE_OPP_DIR_STATE; } return state; } /*********************************************************************//** * @brief * The handlePrimeDiscardAndReversePrimeState function handles the priming * fluid discard and dialyzer reverse prime state of prime service. Waits * for DD to confirm prime fluid discard is active, then runs for the * greater of the discard or reverse prime duration. * @details \b Inputs: stateEntryTimerCtr, dcDiscardAndRevPrimeInterval * @details \b Outputs: none * @return next Prime state *************************************************************************/ static TD_PRIME_STATE_T handlePrimeDiscardAndReversePrimeState( void ) { TD_PRIME_STATE_T state = PRIME_DC_DISCARD_AND_REVERSE_PRIME_STATE; if ( DD_GEND_PRIME_FLUID_DISCARD_STATE == getCurrentGenDState() ) { if ( TRUE == didTimeout( stateEntryTimerCtr, dcInitialPrimeInterval ) ) { //cmdChangeQuf( 0.0F ); setPrimeStateTransition( PRIME_COMPLETE_STATE ); state = PRIME_COMPLETE_STATE; } } return state; } /*********************************************************************//** * @brief * The handlePrimePauseState function handles the prime pause state of prime * service. * @details \b Inputs: prevPrimeState * @details \b Outputs: primeStartTime * @return next Prime state *************************************************************************/ static TD_PRIME_STATE_T handlePrimePauseState( void ) { TD_PRIME_STATE_T state = PRIME_PAUSE_STATE; if ( TRUE == signalPreTxResumeRequest() ) { setPrimeStateTransition( prevPrimeState ); state = prevPrimeState; } return state; } /*********************************************************************//** * @brief * The publishPrimeData function broadcasts prime data during priming. * @details \b Message \b Sent: MSG_ID_TD_PRIME_PROGRESS_DATA * @details \b Inputs: primePublishTimerCtr, currentPrimeState, primeStartTime, * primeElapsedAtPauseMs, primePauseStartTime * @details \b Outputs: primePublishTimerCtr * @return none *************************************************************************/ static void publishPrimeData( void ) { if ( ++primePublishTimerCtr >= PRIME_DATA_PUB_INTERVAL ) { PRIMING_DATA_PAYLOAD_T data; U32 elapsedMs; U32 remainingMs; U32 pauseElapsedMs; U32 pauseRemainingMs = 0; if ( PRIME_PAUSE_STATE == currentPrimeState ) { elapsedMs = primeElapsedAtPauseMs; pauseElapsedMs = calcTimeSince( primePauseStartTime ); pauseRemainingMs = ( pauseElapsedMs < PRIME_PAUSE_TIMEOUT_INTERVAL ) ? ( PRIME_PAUSE_TIMEOUT_INTERVAL - pauseElapsedMs ) : 0; } else { elapsedMs = calcTimeSince( primeStartTime ); } remainingMs = ( elapsedMs < PRIME_TOTAL_DURATION_INTERVAL ) ? ( PRIME_TOTAL_DURATION_INTERVAL - elapsedMs ) : 0; data.primeState = (U32)currentPrimeState; data.totalDurationSec = PRIME_TOTAL_DURATION_INTERVAL / MS_PER_SECOND; data.countDownSec = remainingMs / MS_PER_SECOND; data.pauseCountdownSec = pauseRemainingMs / MS_PER_SECOND; broadcastData( MSG_ID_TD_PRIME_PROGRESS_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)&data, sizeof( PRIMING_DATA_PAYLOAD_T ) ); primePublishTimerCtr = 0; } } /**@}*/