/************************************************************************** * * Copyright (c) 2021-2024 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 SyringePump.c * * @author (last) Dara Navaei * @date (last) 05-Mar-2024 * * @author (original) Sean Nash * @date (original) 04-Mar-2021 * ***************************************************************************/ #include // Used for fabs() functions #include "AlarmMgmt.h" #include "FPGA.h" #include "ModeTreatmentParams.h" #include "NVDataMgmt.h" #include "OperationModes.h" #include "PersistentAlarm.h" #include "SafetyShutdown.h" #include "SyringePump.h" #include "SystemCommMessages.h" #include "TaskPriority.h" #include "Timers.h" /** * @addtogroup SyringePump * @{ */ // ********** private definitions ********** /// Default publication interval for syringe pump data. #define SYRINGE_PUMP_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) #define BD_SYRINGE_ID_RADIUS_CM ( 1.4427F / 2.0F ) ///< Radius from inner diameter (in cm) of supported BD syringe. /// Milliliters per mm of syringe plunger travel. #define SYRINGE_ML_PER_MM ( ( BD_SYRINGE_ID_RADIUS_CM * BD_SYRINGE_ID_RADIUS_CM ) * PI * 0.1F ) #define SYRINGE_MM_PER_REV 0.635F ///< Travel (in mm) per revolution. #define SYRINGE_ENCODER_COUNTS_PER_REV 4000.0F ///< Number of encoder counts per revolution. #define SYRINGE_STEPS_PER_REV 200.0F ///< Number of steps per revolution. #define MICRO_SECONDS_PER_SECOND 1000000.0F ///< Microseconds per second conversion. #define SYRINGE_TOGGLES_PER_STEP 2.0F ///< Stepper motor driver toggles per step or microstep. #define SYRINGE_MICRO_STEPS_PER_STEP 32.0F ///< Number of micro-steps per step. /// Number of micro steps per revolution. #define SYRINGE_MICRO_STEPS_PER_REV ( SYRINGE_STEPS_PER_REV * SYRINGE_MICRO_STEPS_PER_STEP ) /// Number of encoder counts per mm. #define SYRINGE_ENCODER_COUNTS_PER_MM ( SYRINGE_ENCODER_COUNTS_PER_REV / SYRINGE_MM_PER_REV ) /// Number of encoder counts per mL. #define SYRINGE_ENCODER_COUNTS_PER_ML ( SYRINGE_ENCODER_COUNTS_PER_MM / SYRINGE_ML_PER_MM ) /// Number of micro steps per mL. #define SYRINGE_MICRO_STEPS_PER_ML ( ( SYRINGE_MICRO_STEPS_PER_REV / SYRINGE_MM_PER_REV ) / SYRINGE_ML_PER_MM ) #define MIN_HEPARIN_CONTINUOUS_RATE 0.2F ///< Minimum continuous Heparin flow rate (in mL/hr). #define MAX_HEPARIN_CONTINUOUS_RATE 1.0F ///< Maximum continuous Heparin flow rate (in mL/hr). #define HEPARIN_BOLUS_TARGET_RATE 720.0F ///< Heparin bolus target rate (in ml/hr); #define SYRINGE_PUMP_RETRACT_RATE 3600.0F ///< Retract rate is 5 mL/ 5 s = 1 mL/s = 3,600 mL/hr. #define SYRINGE_PUMP_SEEK_RATE 3600.0F ///< Seek plunger rate is 5 mL/ 5 s = 1 mL/s = 3,600 mL/hr. #define SYRINGE_PUMP_PRIME_RATE 635.0F ///< Prime rate is 0.5 mm ^ 2 x PI x 450 mm = 0.353 mL / 2s = 635 mL/hr. #define SYRINGE_PUMP_MAX_RATE 11000.0F ///< Maximum rate of the syringe pump (in mL/hr). #define SYRINGE_PUMP_RATE_ALARM_PERSISTENCE 6000 ///< Alarm persistence period (in ms) for syringe pump speed check alarms. #define SYRINGE_PUMP_DIR_ALARM_PERSISTENCE 3000 ///< Alarm persistence period (in ms) for syringe pump direction check alarms. #define SYRINGE_PUMP_OFF_ALARM_PERSISTENCE 1000 ///< Alarm persistence period (in ms) for syringe pump off check alarms. #define SYRINGE_PUMP_OFF_ERROR_MAX_CNT 5 ///< Maximum number of syringe pump not stopped errors within time window before alarm triggered. Do not exceed MAX_TIME_WINDOWED_COUNT. #define SYRINGE_PUMP_OFF_ERROR_TIME_WIN_MS ( 1 * MS_PER_SECOND ) ///< Time window for Syringe Pump not stopped error. #define SYRINGE_PUMP_PRIMING_TIMEOUT_MS ( 5 * MS_PER_SECOND ) ///< Timeout for syringe pump prime operation. #define STEPS_TO_MICROSTEPS( s ) ( (s) * 32.0F ) ///< Macro conversion from steps to microsteps. #define MICROSTEPS_TO_STEPS( m ) ( (m) / 32.0F ) ///< Macro conversion from microsteps to steps. #define SYRINGE_PUMP_ADC_REF_V 3.3F ///< Syringe pump ADC reference voltage. #define SYRINGE_PUMP_ADC_FULL_SCALE_BITS 1024.0F ///< Syringe pump ADC has 1024 full scale counts (10-bit) per channel. #define SYRINGE_PUMP_DAC_FULL_SCALE_BITS 4096.0F ///< Syringe pump DAC has has 4096 full scale counts (12-bit). #define SYRINGE_FORCE_OCCLUSION_THRESHOLD_V 2.5F ///< Force sensor threshold (in V) above which an occlusion is detected. #define SYRINGE_FORCE_OCCLUSION_DIFF_V 0.5F ///< Force sensor difference (in V) which an occlusion alarm is triggered. #define SYRINGE_FORCE_PLUNGER_THRESHOLD_V 0.25F ///< Force sensor threshold (in V) above which we have engaged with plunger. #define SYRINGE_PUMP_SYRINGE_DETECT_THRESHOLD_V 2.0F ///< Syringe pump syringe detected threshold (in V). #define SYRINGE_PUMP_HOME_DETECT_THRESHOLD_V 0.25F ///< Syringe pump home detected threshold (in V). #define SYRINGE_PUMP_EMPTY_THRESHOLD_V 2.5F ///< Syringe pump empty detected threshold (in V). #define SYRINGE_PUMP_PRELOAD_MARGIN_VOLUME_ML 1.0F ///< Target syringe preload margin volume (in mL). Total to deliver plus this give syringe load position. #define SYRINGE_PUMP_PRELOAD_MAX_VOLUME_ML 10.0F ///< Target syringe preload max volume (in mL). Leave at home position above this value. #define SYRINGE_PUMP_MAX_VOL_ERROR_ML 0.1F ///< Maximum Heparin volume error (in mL). #define SYRINGE_PUMP_MAX_RATE_ERROR_ML_HR 0.1F ///< Maximum Heparin delivery rate error (in mL/hr). #define TEN_PCT_OVER_ALLOWANCE 1.1F ///< Allow 10 percent over target before alarming on over travel. #define FIVE_PCT_OVER_ALLOWANCE 1.05F ///< Allow 5 percent over target before alarming on over travel. #define SYRINGE_PUMP_DAC_VOLTAGE_MAX_ERROR 0.05F ///< Force sensor POST check for DAC voltage - max delta. #define MIN_SYRINGE_PUMP_RATE_FOR_DIR_ALARM 0.5F ///< Minimum measured rate (in mL/hr) required before enforcing direction alarm. /// HW volume variance in the syringe pump in mL. #define SYRINGE_PUMP_HW_TOLERANCE_ML 0.65F /// HW volume variance in the syringe pump in encoder counts. #define SYRINGE_PUMP_HW_TOLERANCE_POS ( SYRINGE_ENCODER_COUNTS_PER_ML * SYRINGE_PUMP_HW_TOLERANCE_ML ) /// Expected position of empty in relation to home position. #define SYRINGE_PUMP_EMPTY_POS ( SYRINGE_ENCODER_COUNTS_PER_ML * 10.755F ) /// Over-travel (past empty) allowance for alarm. #define SYRINGE_PUMP_EMPTY_POS_MARGIN ( SYRINGE_ENCODER_COUNTS_PER_ML * 0.5F ) /// Minimum retract position. #define SYRINGE_PUMP_RETRACT_POS_MIN ( SYRINGE_ENCODER_COUNTS_PER_ML * -0.5F ) #define SYRINGE_PUMP_START_RAMP_SPEED 300000 ///< Starting speed for all syringe pump operations to ramp up from. #define SYRINGE_PUMP_RAMP_DIVISOR 5 ///< Used for ramping profile. #define SYRINGE_PUMP_RATE_CHECK_MARGIN 0.05F ///< 5 pct margin on commanded vs. measured rate check. #define SYRINGE_PUMP_VOLUME_CHECK_MARGIN 0.05F ///< 5 pct margin on commanded vs. encoder based volume check. // Bit definitions for syringe pump control register #define SYRINGE_PUMP_CONTROL_SLEEP_OFF 0x40 ///< Syringe pump control register bit for sleep mode (active low). #define SYRINGE_PUMP_CONTROL_NOT_RESET 0x20 ///< Syringe pump control register bit for resetting stepper motor (active low). #define SYRINGE_PUMP_CONTROL_ENABLE 0x00 ///< Syringe pump control register bit mask for enable. #define SYRINGE_PUMP_CONTROL_DISABLE 0x10 ///< Syringe pump control register bit for enable (active low). #define SYRINGE_PUMP_CONTROL_REVERSE_DIR 0x08 ///< Syringe pump control register bit for direction (0=fwd, 1=rev). #define SYRINGE_PUMP_CONTROL_FORWARD_DIR 0x00 ///< Syringe pump control register bit mask for forward direction. #define SYRINGE_PUMP_CONTROL_32TH_STEP 0x03 ///< Syringe pump control register bits for 1/32 micro-stepping mode. // Bit definitions for syringe pump ADC/DAC control register #define SYRINGE_PUMP_ADC_DAC_CONTROL_WR_ADC_SETUP 0x04 ///< Syringe pump ADC/DAC control bit for re-write ADC setup registers to ADC. #define SYRINGE_PUMP_ADC_DAC_CONTROL_RD_DAC_ON_ADC 0x02 ///< Syringe pump ADC/DAC control bit for reading (once) DAC setting from device. #define SYRINGE_PUMP_ADC_DAC_CONTROL_ENABLE_DAC 0x01 ///< Syringe pump ADC/DAC control bit for write DAC setting to device (ADC reads are off). #define SYRINGE_PUMP_ADC_DAC_CONTROL_ENABLE_ADC 0x00 ///< Syringe pump ADC/DAC control bit mask for enable ADC reads. // Bit definitions for syringe pump ADC/DAC status register #define SYRINGE_PUMP_ADC_DAC_ERROR_COUNT_DAC_WR_DONE 0x80 ///< Syringe pump ADC/DAC error status bit for DAC write done. #define SYRINGE_PUMP_ADC_DAC_ERROR_COUNT_DAC_WR_ERROR 0x40 ///< Syringe pump ADC/DAC error status bit for DAC write error. #define SYRINGE_PUMP_ADC_DAC_ERROR_COUNT_MASK 0x3F ///< Syringe pump ADC/DAC error status count bit-mask. // Bit definitions for syringe pump encoder status register #define SYRINGE_PUMP_ENCODER_STATUS_DIRECTION 0x80 ///< Syringe pump encoder status direction bit (0-Fwd, 1=Rev). #define SYRINGE_PUMP_ENCODER_STATUS_ERROR_COUNT_MASK 0x3F ///< Syringe pump encoder status error count bit-mask. // Stepper motor toggle time for zero speed (stopped) #define SYRINGE_PUMP_MICROSTEP_TOGGLE_TIME_FOR_STOP 0xFFFFFFFF ///< Syringe pump microstep toggle time setting to indicate zero speed (stopped). #define DATA_PUBLISH_COUNTER_START_COUNT 10 ///< Data publish counter start count. /// Control bits to run syringe pump in reverse direction static const U08 SYRINGE_PUMP_CONTROL_RUN_REVERSE = SYRINGE_PUMP_CONTROL_SLEEP_OFF | SYRINGE_PUMP_CONTROL_NOT_RESET | SYRINGE_PUMP_CONTROL_ENABLE | SYRINGE_PUMP_CONTROL_REVERSE_DIR | SYRINGE_PUMP_CONTROL_32TH_STEP; /// Control bits to run syringe pump in forward direction static const U08 SYRINGE_PUMP_CONTROL_RUN_FORWARD = SYRINGE_PUMP_CONTROL_SLEEP_OFF | SYRINGE_PUMP_CONTROL_NOT_RESET | SYRINGE_PUMP_CONTROL_ENABLE | SYRINGE_PUMP_CONTROL_FORWARD_DIR | SYRINGE_PUMP_CONTROL_32TH_STEP; #define SYRINGE_PUMP_DAC_WRITE_ERROR_BIT 0x40 ///< Syringe pump DAC write error bit flag in FPGA register. #define SYRINGE_PUMP_ENCODER_DIRECTION_ERROR_BITS 0x3F ///< Syringe pump encoder direction error counter bits in FPGA register. #define SYRINGE_PUMP_ENCODER_DIRECTION_BIT 0x80 ///< Syringe pump encoder direction bit in FPGA register. /// Interval (ms/task time) at which the syringe pump speed is calculated (every 40 ms). #define SYRINGE_PUMP_SPEED_CALC_INTERVAL ( 40 / TASK_PRIORITY_INTERVAL ) /// Interval (ms/task time) at which the syringe pump position is checked when pump off state (every 50 ms). #define SYRINGE_PUMP_STOP_POSITION_CHECK_INTERVAL ( 50 / TASK_PRIORITY_INTERVAL ) /// Number of syringe pump encoder positions kept in buffer to hold last 1 second of position data. #define SYRINGE_PUMP_SPEED_CALC_BUFFER_LEN ( MS_PER_SECOND / SYRINGE_PUMP_SPEED_CALC_INTERVAL / TASK_PRIORITY_INTERVAL ) #define SYRINGE_PUMP_RAMP_STALL_TIME ( 500 / TASK_PRIORITY_INTERVAL ) ///< Syringe pump ramp stall timeout. #define SYRINGE_PUMP_RAMP_STALL_RETRIES 3 ///< Syringe pump ramp stall retries allowed. #define SYRINGE_PUMP_STALL_SPEED_THRESHOLD 0.05F ///< Minimum syringe pump speed to be considered not stalled. #define SYRINGE_PUMP_ADC_FPGA_ERROR_TIMEOUT_MS ( 2 * MS_PER_SECOND ) ///< Syringe pump ADC FPGA error timeout in milliseconds. #define SYRINGE_PUMP_DAC_MAX_RETRIES 5 ///< Syringe pump DAC retries to write. #define SYRINGE_PUMP_DAC_TIMER ( 200 / TASK_PRIORITY_INTERVAL ) ///< Syringe pump DAC timer between retries. #define SYRINGE_PUMP_OCCLUSION_PERSISTENCE 50 ///< Syringe pump occlusion persistence timer in milliseconds. #define SYRINGE_PUMP_EMPTY_FORCE_COUNT 5 ///< Syringe pump empty force voltage count persistence. /// Defined states for the syringe pump control state machine. typedef enum SyringePump_States { SYRINGE_PUMP_INIT_STATE = 0, ///< Syringe pump initialize state SYRINGE_PUMP_OFF_STATE, ///< Syringe pump off state SYRINGE_PUMP_RETRACT_STATE, ///< Syringe pump retract state SYRINGE_PUMP_PRELOAD_STATE, ///< Syringe pump preload state SYRINGE_PUMP_SEEK_STATE, ///< Syringe pump seek plunger state SYRINGE_PUMP_PRIME_STATE, ///< Syringe pump prime state SYRINGE_PUMP_HEP_BOLUS_STATE, ///< Syringe pump bolus delivery of Heparin state SYRINGE_PUMP_HEP_CONTINUOUS_STATE, ///< Syringe pump continuous delivery of Heparin state SYRINGE_PUMP_CONFIG_FORCE_SENSOR_STATE, ///< Syringe pump configure DAC gain for force sensor state NUM_OF_SYRINGE_PUMP_STATES ///< Number of syringe pump control states } SYRINGE_PUMP_STATE_T; // ********** private data ********** static SYRINGE_PUMP_STATE_T syringePumpState; ///< Current state of syringe pump control state machine. static U32 syringePumpDataPublicationTimerCounter; ///< Used to schedule syringe pump data publication to CAN bus. static U32 syringePumpRampTimerCtr; ///< Used to track ramp up time. static HEPARIN_STATE_T heparinDeliveryState; ///< Current state of Heparin delivery. /// Interval (in ms) at which to publish syringe pump data to CAN bus. static OVERRIDE_U32_T syringePumpDataPublishInterval = { SYRINGE_PUMP_DATA_PUB_INTERVAL, SYRINGE_PUMP_DATA_PUB_INTERVAL, 0, 0 }; static OVERRIDE_F32_T syringePumpMeasRate = { 0.0, 0.0, 0.0, 0 }; ///< Measured rate for syringe pump (in mL/hr). static OVERRIDE_F32_T syringePumpMeasForce = { 0.0, 0.0, 0.0, 0 }; ///< Measured driver force (in V). static OVERRIDE_F32_T syringePumpMeasSyringeDetectionSwitch = { 0, 0, 0, 0 }; ///< Measured syringe detect switch (in V). static OVERRIDE_F32_T syringePumpMeasHome = { 0.0, 0.0, 0.0, 0 }; ///< Measured optical home (in V). static OVERRIDE_S32_T syringePumpPosition = { 0, 0, 0, 0 }; ///< Encoder based position (in steps). static OVERRIDE_F32_T syringePumpVolumeDelivered = { 0.0, 0.0, 0.0, 0 }; ///< Measured volume delivered (in mL). static OVERRIDE_F32_T heparinBolusTargetRate = { HEPARIN_BOLUS_TARGET_RATE, 0.0, HEPARIN_BOLUS_TARGET_RATE, 0 }; ///< Target rate of the heprin bolus in mL/hour static OVERRIDE_U32_T syringePumpStatus = {0, 0, 0, 0}; ///< Syringe pump status reported by FPGA. static OVERRIDE_U32_T syringePumpEncoderStatus = {0, 0, 0, 0}; ///< Syringe pump encoder status reported by FPGA. static OVERRIDE_U32_T syringePumpADCandDACStatus = {0, 0, 0, 0}; ///< Syringe pump ADC and DAC status reported by FPGA. static OVERRIDE_U32_T syringePumpADCReadCtr = {0, 0, 0, 0}; ///< Syringe pump ADC read counter reported by FPGA. static BOOL requireSyringeDetection; ///< Flag indicating whether syringe detection is required in the current state. static BOOL prevSyringeDetected; ///< Flag saving last state of syringe detection static F32 syringePumpSetRate; ///< Set rate for syringe pump (in mL/hr). static F32 forceAtEndOfSeek; ///< Force sensor reading in Volts at the end of seek. static U32 syringePumpSetToggleTime; ///< Set rate for syringe pump (in uSec/toggle). static U32 syringePumpRampUpToggleTime; ///< Current ramp rate for syringe pump (in uSec/toggle). static F32 syringePumpRampUpPct; ///< Percentage progress of ramp to target rate. static F32 syringePumpSafetyVolumeDelivered; ///< Calculated volume (in mL) (from set rate over time). static S32 syringePumpVolumeStartPosition; ///< Start position for the current volume calculation. static S32 syringePumpHomePositionOffset; ///< FPGA reported position when at home postion. static S32 syringePumpLastPosition; ///< Position previously recorded. static S32 syringePumpStopLastPosition; ///< Syringe pump stop position previously recorded. static S32 syringePumpLastPositions[ SYRINGE_PUMP_SPEED_CALC_BUFFER_LEN ]; ///< Last encoder positions for the syringe pump. static U32 syringePumpMotorSpeedCalcIdx; ///< Index into 1 second buffer of syringe pump encoder positions. static U32 syringePumpSpeedCalcTimerCounter; ///< Used to calculate measured rate from change in position over time. static U32 syringePumpStopPositionTimerCounter; ///< Used to check change in encoder position when pump stopped. static MOTOR_DIR_T syringePumpControllerMeasuredDirection; ///< Measured direction of syringe pump per controller. static MOTOR_DIR_T syringePumpEncoderMeasuredDirection; ///< Measured direction of syringe pump per encoder position relative to previous. static F32 syringePumpVolumeRequired; ///< Volume required for complete therapy (in mL). static BOOL syringePumpRetractRequested; ///< Flag indicates a retract operation is requested. static BOOL syringePumpPreloadRequested; ///< Flag indicates a plunger preload operation is requested. static BOOL syringePumpSeekRequested; ///< Flag indicates a plunger seek operation is requested. static BOOL syringePumpPrimeRequested; ///< Flag indicates a Heparin prime operation is requested. static BOOL syringePumpBolusRequested; ///< Flag indicates start or resume of a Heparin bolus operation is requested. static BOOL syringePumpContinuousRequested; ///< Flag indicates start or resume of a Heparin continuous delivery operation is requested. static BOOL syringePumpDACVrefSetRequested; ///< Flag indicates request to set DAC Vref for force sensor. static BOOL syringePumpPositionKnown; ///< Flag indicates we know position from a prior retract to home. static BOOL syringePumpPlungerFound; ///< Flag indicates plunger was found. static BOOL syringeVolumeAdequate; ///< Flag indicates whether Heparin volume is sufficient to complete treatment. static BOOL syringePumpPrimeCompleted; ///< Flag indicates prime operation was completed. static BOOL syringePumpPreLoadCompleted; ///< Flag indicates preload operation was completed. static U32 syringePumpStateStartTime; ///< Time current syringe pump state started. static BOOL syringePumpDACVrefWriteInProgress; ///< Flag indicates DAC Vref write is in progress. static F32 syringePumpDACVref; ///< DAC Vref setting for force sensor. static U32 syringePumpStallCtr; ///< Counts time when position is not changing during ramp. static U32 syringePumpStallRetryCount; ///< Counts pump ramp up stall retries. static HD_HEPARIN_FORCE_SENSOR_CAL_RECORD_T forceSensorCalRecord; ///< HD heparin force sensor calibration record. static U32 syringePumpDACRetryCount; ///< Counter for DAC set retries. static U32 syringePumpDACRetryTimer; ///< Timer for DAC set retries. static U32 syringePumpEmptyForceCount; ///< Counter for empty syringe detection. // ********** private function prototypes ********** static void resetSyringePumpRequestFlags( void ); static void resetHeparinVolumeDelivered( void ); static void execSyringePumpMonitor( void ); static S32 getSyringePumpPosition( void ); static F32 getSyringePumpMeasRate( void ); static F32 getSyringePumpSyringeDetectorV( void ); static F32 getSyringePumpHomeDetectorV( void ); static F32 getSyringePumpForceV( void ); static F32 getHeprinBolusTargetRate( void ); static U08 getSyringePumpStatus( void ); static U08 getSyringePumpEncoderStatus( void ); static U08 getSyringePumpADCReadCounter( void ); static U08 getSyringePumpADCandDACStatus( void ); static SYRINGE_PUMP_STATE_T handleSyringePumpInitState( void ); static SYRINGE_PUMP_STATE_T handleSyringePumpOffState( void ); static SYRINGE_PUMP_STATE_T handleSyringePumpRetractState( void ); static SYRINGE_PUMP_STATE_T handleSyringePumpPreLoadState( void ); static SYRINGE_PUMP_STATE_T handleSyringePumpSeekState( void ); static SYRINGE_PUMP_STATE_T handleSyringePumpPrimeState( void ); static SYRINGE_PUMP_STATE_T handleSyringePumpBolusState( void ); static SYRINGE_PUMP_STATE_T handleSyringePumpContinuousState( void ); static SYRINGE_PUMP_STATE_T handleSyringePumpCalibrateForceSensorState( void ); static void rampSyringePump( void ); static void calcStepperToggleTimeForTargetRate( F32 rate ); static void calcMeasRate( void ); static void calcSafetyVolumeDelivered( void ); static BOOL checkDirection( BOOL stopPump, MOTOR_DIR_T expDir ); static BOOL checkSyringeEmpty( BOOL stopPump ); static BOOL checkSyringeRemoved( BOOL stopPump ); static BOOL checkMaxTravel( BOOL stopPump, S32 maxPos ); static BOOL checkMeasRate( BOOL stopPump, F32 pctMargin ); static BOOL checkVolumeVsSafetyVolume( BOOL stopPump, F32 pctMargin ); static BOOL checkForStall( BOOL stopPump ); static void publishSyringePumpData( void ); /*********************************************************************//** * @brief * The initSyringePump function initializes the syringe pump module. * @details Inputs: none * @details Outputs: syringe pump module initialized. * @return none *************************************************************************/ void initSyringePump( void ) { U32 i; syringePumpState = SYRINGE_PUMP_INIT_STATE; heparinDeliveryState = HEPARIN_STATE_OFF; requireSyringeDetection = FALSE; prevSyringeDetected = FALSE; syringePumpSetRate = 0.0; syringePumpSetToggleTime = 0; syringePumpSafetyVolumeDelivered = 0.0; syringePumpVolumeStartPosition = 0; syringePumpHomePositionOffset = 0; syringePumpLastPosition = 0; syringePumpStopLastPosition = 0; syringePumpVolumeRequired = 0.0; syringePumpControllerMeasuredDirection = MOTOR_DIR_FORWARD; syringePumpEncoderMeasuredDirection = MOTOR_DIR_FORWARD; syringePumpDataPublicationTimerCounter = DATA_PUBLISH_COUNTER_START_COUNT; syringePumpSpeedCalcTimerCounter = 0; syringePumpRampTimerCtr = 0; syringePumpStopPositionTimerCounter = 0; syringePumpSafetyVolumeDelivered = 0.0; syringePumpStateStartTime = getMSTimerCount(); syringePumpPositionKnown = FALSE; syringePumpPlungerFound = FALSE; syringeVolumeAdequate = FALSE; syringePumpPrimeCompleted = FALSE; syringePumpPreLoadCompleted = FALSE; syringePumpRampUpPct = 0.0; syringePumpEmptyForceCount = 0; // Zero pump position counts buffer syringePumpMotorSpeedCalcIdx = 0; for ( i = 0; i < SYRINGE_PUMP_SPEED_CALC_BUFFER_LEN; i++ ) { syringePumpLastPositions[ i ] = 0; } syringePumpStallCtr = 0; syringePumpStallRetryCount = 0; syringePumpDACRetryCount = 0; syringePumpDACRetryTimer = 0; // Initialize persistent alarms initPersistentAlarm( ALARM_ID_HD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, 0, SYRINGE_PUMP_DIR_ALARM_PERSISTENCE ); initPersistentAlarm( ALARM_ID_HD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR, 0, SYRINGE_PUMP_DIR_ALARM_PERSISTENCE ); initPersistentAlarm( ALARM_ID_HD_SYRINGE_PUMP_RUNNING_WHILE_BP_OFF_ERROR, 0, SYRINGE_PUMP_OFF_ALARM_PERSISTENCE ); initPersistentAlarm( ALARM_ID_HD_SYRINGE_PUMP_SPEED_ERROR, 0, SYRINGE_PUMP_RATE_ALARM_PERSISTENCE ); initTimeWindowedCount( TIME_WINDOWED_COUNT_SYRINGE_PUMP_OFF_ERROR, SYRINGE_PUMP_OFF_ERROR_MAX_CNT, SYRINGE_PUMP_OFF_ERROR_TIME_WIN_MS ); initFPGAPersistentAlarm( FPGA_PERS_ERROR_SYRINGE_PUMP_ADC, ALARM_ID_HD_SYRINGE_PUMP_FPGA_ADC_FAULT, SYRINGE_PUMP_ADC_FPGA_ERROR_TIMEOUT_MS, SYRINGE_PUMP_ADC_FPGA_ERROR_TIMEOUT_MS ); // Reset request flags resetSyringePumpRequestFlags(); } /*********************************************************************//** * @brief * The resetSyringePumpRequestFlags function resets request flags. * @details Inputs: none * @details Outputs: request flags reset * @return TRUE if request accepted, FALSE if not *************************************************************************/ static void resetSyringePumpRequestFlags( void ) { syringePumpRetractRequested = FALSE; syringePumpSeekRequested = FALSE; syringePumpPreloadRequested = FALSE; syringePumpPrimeRequested = FALSE; syringePumpBolusRequested = FALSE; syringePumpContinuousRequested = FALSE; syringePumpDACVrefSetRequested = FALSE; } /*********************************************************************//** * @brief * The userHeparinRequest function handles a command request from the user * to pause or resume Heparin delivery. * @details Inputs: none * @details Outputs: heparinDeliveryState, syringePumpState, syringePumpContinuousRequested * @param cmd command from user * @return TRUE if request accepted, FALSE if not *************************************************************************/ BOOL userHeparinRequest( HEPARIN_CMD_T cmd ) { BOOL accepted = FALSE; REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NONE; if ( MODE_TREA == getCurrentOperationMode() ) { if ( ( TREATMENT_DIALYSIS_STATE == getTreatmentState() ) && ( getDialysisState() != DIALYSIS_SALINE_BOLUS_STATE ) ) { if ( HEPARIN_CMD_PAUSE == cmd ) { if ( HEPARIN_STATE_DISPENSING == heparinDeliveryState ) { stopSyringePump(); accepted = TRUE; heparinDeliveryState = HEPARIN_STATE_PAUSED; } else { rejReason = REQUEST_REJECT_REASON_HEPARIN_PAUSE_INVALID_IN_THIS_STATE; } } else if ( HEPARIN_CMD_RESUME == cmd ) { if ( HEPARIN_STATE_PAUSED == heparinDeliveryState ) { accepted = TRUE; startHeparinContinuous(); } else { rejReason = REQUEST_REJECT_REASON_HEPARIN_NOT_PAUSED; } } else { rejReason = REQUEST_REJECT_REASON_INVALID_COMMAND; } } else { rejReason = REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE; } } else { rejReason = REQUEST_REJECT_REASON_NOT_IN_TREATMENT_MODE; } sendHeparinCommandResponse( (U32)accepted, (U32)rejReason ); return accepted; } /*********************************************************************//** * @brief * The setHeparinOff function requests Heparin state be set to off - * indicates syringe pump will not be needed in this treatment. * @details Inputs: none * @details Outputs: heparinDeliveryState * @return none *************************************************************************/ void setHeparinOff( void ) { heparinDeliveryState = HEPARIN_STATE_OFF; } /*********************************************************************//** * @brief * The setHeparinStopped function requests Heparin state be set to stopped - * indicates syringe pump will be needed in this treatment. * @details Inputs: none * @details Outputs: heparinDeliveryState * @return none *************************************************************************/ void setHeparinStopped( void ) { heparinDeliveryState = HEPARIN_STATE_STOPPED; } /*********************************************************************//** * @brief * The setHeparinCompleted function requests Heparin state be set to completed - * indicates we have entered the Heparin pre-stop period of this treatment. * @details Inputs: none * @details Outputs: heparinDeliveryState * @return none *************************************************************************/ void setHeparinCompleted( void ) { heparinDeliveryState = HEPARIN_STATE_COMPLETED; } /*********************************************************************//** * @brief * The getHeparinState function returns the current Heparin state. * @details Inputs: heparinDeliveryState * @details Outputs: none * @return heparinDeliveryState *************************************************************************/ HEPARIN_STATE_T getHeparinState( void ) { return heparinDeliveryState; } /*********************************************************************//** * @brief * The resetPreLoadStatus function resets the syringe pump pre-load status. * @details Inputs: none * @details Outputs: syringePumpPreLoadCompleted * @return none *************************************************************************/ void resetPreLoadStatus( void ) { syringePumpPreLoadCompleted = FALSE; } /*********************************************************************//** * @brief * The stopSyringePump function requests syringe pump be stopped. * @details Inputs: none * @details Outputs: syringePumpSetRate, syringePumpState, heparinDeliveryState * @return TRUE if request accepted, FALSE if not *************************************************************************/ void stopSyringePump( void ) { setFPGASyringePumpStepToggleTime( SYRINGE_PUMP_MICROSTEP_TOGGLE_TIME_FOR_STOP ); if ( ( syringePumpState != SYRINGE_PUMP_CONFIG_FORCE_SENSOR_STATE ) && ( syringePumpState != SYRINGE_PUMP_OFF_STATE ) ) { syringePumpState = SYRINGE_PUMP_OFF_STATE; syringePumpSetRate = 0.0; } if ( ( HEPARIN_STATE_INITIAL_BOLUS == heparinDeliveryState ) || ( HEPARIN_STATE_DISPENSING == heparinDeliveryState ) ) { if ( HEPARIN_STATE_INITIAL_BOLUS == heparinDeliveryState ) { sendTreatmentLogEventData( HEPARIN_BOLUS_END_EVENT, syringePumpSetRate, 0.0 ); } else { sendTreatmentLogEventData( HEPARIN_STOP_PAUSE_EVENT, syringePumpSetRate, 0.0 ); } heparinDeliveryState = HEPARIN_STATE_STOPPED; syringePumpSetRate = 0.0; } } /*********************************************************************//** * @brief * The retractSyringePump function requests retract operation. * @details Inputs: syringePumpState * @details Outputs: syringePumpSetRate, syringePumpRetractRequested, heparinDeliveryState * @return TRUE if request accepted, FALSE if not *************************************************************************/ BOOL retractSyringePump( void ) { if ( SYRINGE_PUMP_OFF_STATE == syringePumpState ) { heparinDeliveryState = HEPARIN_STATE_STOPPED; syringePumpSetRate = SYRINGE_PUMP_RETRACT_RATE; syringePumpRetractRequested = TRUE; } return syringePumpRetractRequested; } /*********************************************************************//** * @brief * The preloadSyringePlunger function requests plunger preload operation. * @details Inputs: syringePumpState * @details Outputs: syringePumpSetRate, syringePumpPreloadRequested * @return TRUE if request accepted, FALSE if not *************************************************************************/ BOOL preloadSyringePlunger( void ) { if ( MODE_SERV == getCurrentOperationMode() ) // Allow syringe pump operations in Service Mode. { heparinDeliveryState = HEPARIN_STATE_STOPPED; } if ( ( SYRINGE_PUMP_OFF_STATE == syringePumpState ) && ( TRUE == syringePumpPositionKnown ) && ( heparinDeliveryState != HEPARIN_STATE_OFF ) ) { syringePumpSetRate = SYRINGE_PUMP_SEEK_RATE; syringePumpPreloadRequested = TRUE; } return syringePumpPreloadRequested; } /*********************************************************************//** * @brief * The seekSyringePlunger function requests plunger seek operation. * @details Inputs: syringePumpState * @details Outputs: syringePumpSetRate, syringePumpSeekRequested * @return TRUE if request accepted, FALSE if not *************************************************************************/ BOOL seekSyringePlunger( void ) { if ( MODE_SERV == getCurrentOperationMode() ) // Allow syringe pump operations in Service Mode. { heparinDeliveryState = HEPARIN_STATE_STOPPED; } if ( ( SYRINGE_PUMP_OFF_STATE == syringePumpState ) && ( TRUE == isSyringeDetected() ) && ( TRUE == syringePumpPositionKnown ) && ( heparinDeliveryState != HEPARIN_STATE_OFF ) ) { syringePumpSetRate = SYRINGE_PUMP_SEEK_RATE; syringePumpSeekRequested = TRUE; } return syringePumpSeekRequested; } /*********************************************************************//** * @brief * The primeSyringePump function requests a prime operation. * @details Inputs: syringePumpState * @details Outputs: syringePumpSetRate, syringePumpPrimeRequested * @return TRUE if request accepted, FALSE if not *************************************************************************/ BOOL primeSyringePump( void ) { if ( MODE_SERV == getCurrentOperationMode() ) // Allow syringe pump operations in Service Mode. { heparinDeliveryState = HEPARIN_STATE_STOPPED; } if ( ( SYRINGE_PUMP_OFF_STATE == syringePumpState ) && ( TRUE == isSyringeDetected() ) && ( heparinDeliveryState != HEPARIN_STATE_OFF ) && ( TRUE == syringePumpPlungerFound ) ) { forceAtEndOfSeek = getSyringePumpForceV(); // Get the force sensor reading when syringe pump prime has been requested (after seek) syringePumpSetRate = SYRINGE_PUMP_PRIME_RATE; syringePumpPrimeRequested = TRUE; resetHeparinVolumeDelivered(); } return syringePumpPrimeRequested; } /*********************************************************************//** * @brief * The resetHeparinVolumeDelivered function resets the Heparin volume delivered. * @details Inputs: none * @details Outputs: syringePumpVolumeDelivered, syringePumpSafetyVolumeDelivered, * syringePumpVolumeStartPosition * @return none *************************************************************************/ static void resetHeparinVolumeDelivered( void ) { syringePumpVolumeDelivered.data = 0.0; syringePumpSafetyVolumeDelivered = 0.0; syringePumpVolumeStartPosition = getSyringePumpPosition(); } /*********************************************************************//** * @brief * The startHeparinBolus function requests Heparin bolus delivery. * @details Inputs: syringePumpState * @details Outputs: syringePumpSetRate, syringePumpBolusRequested * @return TRUE if request accepted, FALSE if not *************************************************************************/ BOOL startHeparinBolus( void ) { F32 bolusVol = getTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_BOLUS_VOLUME ); // If valid to start a bolus, kick it off if ( FALSE == isSyringePumpHome() ) { if ( MODE_SERV == getCurrentOperationMode() ) // Allow syringe pump operations in Service Mode. { heparinDeliveryState = HEPARIN_STATE_STOPPED; } if ( ( TRUE == isSyringeDetected() ) && ( TRUE == syringePumpPrimeCompleted ) && ( SYRINGE_PUMP_OFF_STATE == syringePumpState ) && ( HEPARIN_STATE_STOPPED == heparinDeliveryState ) ) { // if user set a Heparin bolus volume and not complete, start now if ( ( bolusVol > 0.0F ) && ( getSyringePumpVolumeDelivered() < bolusVol ) ) { syringePumpSetRate = getHeprinBolusTargetRate(); syringePumpBolusRequested = TRUE; } } } else { #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP ) != SW_CONFIG_ENABLE_VALUE ) #endif { SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, (F32)SW_FAULT_ID_HD_SYRINGE_INVALID_BOLUS_CMD, syringePumpSetRate ) } } return syringePumpBolusRequested; } /*********************************************************************//** * @brief * The startHeparinContinuous function requests Heparin continuous delivery. * @details Inputs: syringePumpState * @details Outputs: syringePumpSetRate, syringePumpContinuousRequested * @return TRUE if request accepted, FALSE if not *************************************************************************/ BOOL startHeparinContinuous( void ) { F32 flowRate = getTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_DISPENSE_RATE ); if ( ( flowRate >= MIN_HEPARIN_CONTINUOUS_RATE ) && ( flowRate <= MAX_HEPARIN_CONTINUOUS_RATE ) && ( FALSE == isSyringePumpHome() ) ) { if ( MODE_SERV == getCurrentOperationMode() ) // Allow syringe pump operations in Service Mode. { heparinDeliveryState = HEPARIN_STATE_STOPPED; } if ( ( TRUE == isSyringeDetected() ) && ( SYRINGE_PUMP_OFF_STATE == syringePumpState ) && ( ( HEPARIN_STATE_STOPPED == heparinDeliveryState ) || ( HEPARIN_STATE_PAUSED == heparinDeliveryState ) ) ) { syringePumpSetRate = flowRate; syringePumpContinuousRequested = TRUE; } } else { #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP ) != SW_CONFIG_ENABLE_VALUE ) #endif { SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, (F32)SW_FAULT_ID_HD_SYRINGE_INVALID_CONT_CMD, flowRate ) } } return syringePumpContinuousRequested; } /*********************************************************************//** * @brief * The setSyringePumpDACVref function requests to set the DAC Vref setting. * @details Inputs: none * @details Outputs: syringePumpDACVrefSetRequested, syringePumpDACVref * @return TRUE if request accepted, FALSE if not *************************************************************************/ BOOL setSyringePumpDACVref( void ) { F32 vRef = forceSensorCalRecord.hdHeparinForceSensorDACVoltage; if ( ( vRef >= 0.0F ) && ( vRef <= SYRINGE_PUMP_ADC_REF_V ) ) { // Cannot set DAC value if pump is busy or calibration values have not been provided yet if ( ( SYRINGE_PUMP_OFF_STATE == syringePumpState ) && ( forceSensorCalRecord.calibrationTime != 0 ) ) { syringePumpDACVref = vRef; syringePumpDACVrefSetRequested = TRUE; } } else { SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, (F32)SW_FAULT_ID_HD_SYRINGE_INVALID_VREF, vRef ) } return syringePumpDACVrefSetRequested; } /*********************************************************************//** * @brief * The syringeDetectionRequired function sets a flag indicating whether * the syringe detection is required in current state. * @details Inputs: none * @details Outputs: requireSyringeDetection * @param req True if syringe detection is required in the current state * @return none *************************************************************************/ void syringeDetectionRequired( BOOL req ) { requireSyringeDetection = req; } /*********************************************************************//** * @brief * The syringePumpVerifyForceSensorDACCalibration function verifies that the * syringe pump force sensor DAC has been calibrated and is reporting no force * as would be expected when fully retracted (caller should not call this function * until the syringe pump is homed first). * @details Inputs: none * @details Outputs: * @return none *************************************************************************/ void syringePumpVerifyForceSensorDACCalibration( void ) { F32 DACDeltaV = fabs( forceSensorCalRecord.hdHeparinForceSensorDACVoltage - getSyringePumpForceV() ); if ( DACDeltaV > SYRINGE_PUMP_DAC_VOLTAGE_MAX_ERROR ) { SET_ALARM_WITH_1_F32_DATA( ALARM_ID_HD_SYRINGE_PUMP_SELF_TEST_FAILURE, DACDeltaV ) // In case DAC not set yet, try to set DAC setSyringePumpDACVref(); } } /*********************************************************************//** * @brief * The getSyringePumpVolumeDelivered function gets the current syringe pump * volume delivered. * @details Inputs: syringePumpVolumeDelivered * @details Outputs: none * @return the current syringe pump volume delivered (in mL). *************************************************************************/ F32 getSyringePumpVolumeDelivered( void ) { return getF32OverrideValue( &syringePumpVolumeDelivered ); } /*********************************************************************//** * @brief * The getSyringePumpMeasRate function gets the current syringe pump measured * rate. * @details Inputs: syringePumpMeasRate * @details Outputs: none * @return the current syringe pump measured rate (in mL/hr). *************************************************************************/ static F32 getSyringePumpMeasRate( void ) { return getF32OverrideValue( &syringePumpMeasRate ); } /*********************************************************************//** * @brief * The getSyringePumpSyringeDetectorV function gets the current syringe pump * syringe detect signal. * @details Inputs: syringePumpMeasSyringeDetectionSwitch * @details Outputs: none * @return the current syringe pump syringe detect signal (in V). *************************************************************************/ static F32 getSyringePumpSyringeDetectorV( void ) { return getF32OverrideValue( &syringePumpMeasSyringeDetectionSwitch ); } /*********************************************************************//** * @brief * The getSyringePumpHomeDetectorV function gets the current syringe pump * home detect signal. * @details Inputs: syringePumpMeasHome * @details Outputs: none * @return the current syringe pump home detect signal (in V). *************************************************************************/ static F32 getSyringePumpHomeDetectorV( void ) { return getF32OverrideValue( &syringePumpMeasHome ); } /*********************************************************************//** * @brief * The getSyringePumpForceV function gets the current syringe pump force. * @details Inputs: syringePumpMeasForce * @details Outputs: none * @return the current syringe pump force (in V). *************************************************************************/ static F32 getSyringePumpForceV( void ) { return getF32OverrideValue( &syringePumpMeasForce ); } /*********************************************************************//** * @brief * The getHeprinBolusTargetRate function gets the heprin bolus target * flow rate. * @details Inputs: getHeprinBolusTargetRate * @details Outputs: getHeprinBolusTargetRate * @return the current target heprin bolus flow rate (in mL/hour). *************************************************************************/ static F32 getHeprinBolusTargetRate( void ) { return getF32OverrideValue( &heparinBolusTargetRate ); } /*********************************************************************//** * @brief * The getSyringePumpPosition function gets the current syringe pump * position. * @details Inputs: syringePumpPosition * @details Outputs: none * @return the current syringe pump syringe position (in steps). *************************************************************************/ static S32 getSyringePumpPosition( void ) { return getS32OverrideValue( &syringePumpPosition ); } /*********************************************************************//** * @brief * The getSyringePumpStatus function gets the current syringe pump status. * @details Inputs: syringePumpStatus * @details Outputs: none * @return the current syringe pump status. *************************************************************************/ static U08 getSyringePumpStatus() { return getU08OverrideValue( &syringePumpStatus ); } /*********************************************************************//** * @brief * The getSyringePumpEncoderStatus function gets the current syringe pump * encoder status. * @details Inputs: syringePumpEncoderStatus * @details Outputs: none * @return the current syringe pump encoder status. *************************************************************************/ static U08 getSyringePumpEncoderStatus() { return getU08OverrideValue( &syringePumpEncoderStatus ); } /*********************************************************************//** * @brief * The getSyringePumpADCReadCounter function gets the current syringe pump * ADC read counter. * @details Inputs: syringePumpADCReadCtr * @details Outputs: none * @return the current syringe pump ADC read counter. *************************************************************************/ static U08 getSyringePumpADCReadCounter() { return getU08OverrideValue( &syringePumpADCReadCtr ); } /*********************************************************************//** * @brief * The getSyringePumpADCandDACStatus function gets the current syringe pump * ADC and DAC status reported by the FPGA. * @details Inputs: syringePumpADCandDACStatus * @details Outputs: none * @return the current syringe pump ADC and DAC status. *************************************************************************/ static U08 getSyringePumpADCandDACStatus() { return getU08OverrideValue( &syringePumpADCandDACStatus ); } /*********************************************************************//** * @brief * The isSyringeDetected function determines whether a syringe is currently * detected. * @details Inputs: syringePumpMeasSyringeDetectionSwitch * @details Outputs: none * @return TRUE if syringe detected, FALSE if not *************************************************************************/ BOOL isSyringeDetected( void ) { BOOL result = FALSE; if ( getSyringePumpSyringeDetectorV() >= SYRINGE_PUMP_SYRINGE_DETECT_THRESHOLD_V ) { result = TRUE; } return result; } /*********************************************************************//** * @brief * The isSyringePumpHome function determines whether the syringe drive position * is at the home position. * @details Inputs: syringePumpMeasHome * @details Outputs: none * @return TRUE if home detected, FALSE if not *************************************************************************/ BOOL isSyringePumpHome( void ) { BOOL result = FALSE; if ( getSyringePumpHomeDetectorV() <= SYRINGE_PUMP_HOME_DETECT_THRESHOLD_V ) { result = TRUE; } return result; } /*********************************************************************//** * @brief * The isSyringePumpStopped function determines whether the syringe pump * is currently stopped. * @details Inputs: syringePumpState * @details Outputs: none * @return TRUE if syringe pump is currently stopped, FALSE if running. *************************************************************************/ BOOL isSyringePumpStopped( void ) { BOOL result = FALSE; if ( ( syringePumpState <= SYRINGE_PUMP_OFF_STATE ) || ( syringePumpState >= SYRINGE_PUMP_CONFIG_FORCE_SENSOR_STATE ) ) { result = TRUE; } return result; } /*********************************************************************//** * @brief * The isSyringePlungerFound function determines whether the syringe pump * plunger seek operation was completed. * @details Inputs: syringePumpPlungerFound * @details Outputs: none * @return TRUE if syringe plunger was found, FALSE if not. *************************************************************************/ BOOL isSyringePlungerFound( void ) { return syringePumpPlungerFound; } /*********************************************************************//** * @brief * The isSyringeVolumeAdequate function determines whether the syringe has * sufficient Heparin volume to complete the treatment per user settings. * Call this function after seek operation. * @details Inputs: syringeVolumeAdequate * @details Outputs: none * @return TRUE if syringe volume is sufficient to complete the treatment, FALSE if not. *************************************************************************/ BOOL isSyringeVolumeAdequate( void ) { return syringeVolumeAdequate; } /*********************************************************************//** * @brief * The isSyringePumpPrimed function determines whether the syringe pump * prime operation was completed. * @details Inputs: syringePumpPrimeCompleted * @details Outputs: none * @return TRUE if syringe pump prime is completed, FALSE if not. *************************************************************************/ BOOL isSyringePumpPrimed( void ) { return syringePumpPrimeCompleted; } /*********************************************************************//** * @brief * The isSyringePumpRunning function determines whether the syringe pump * is currently running an operation. * @details Inputs: syringePumpState * @details Outputs: none * @return TRUE if syringe pump operation is currently running, FALSE if not. *************************************************************************/ BOOL isSyringePumpRunning( void ) { BOOL result = ( syringePumpState > SYRINGE_PUMP_OFF_STATE ? TRUE : FALSE ); return result; } /*********************************************************************//** * @brief * The isSyringePumpPreLoaded function determines whether the syringe pump * is has been moved to preload position. * @details Inputs: syringePumpPreLoadCompleted * @details Outputs: none * @return TRUE if syringe pump operation is in preload position, FALSE if not. *************************************************************************/ BOOL isSyringePumpPreLoaded( void ) { return syringePumpPreLoadCompleted; } /*********************************************************************//** * @brief * The execSyringePumpMonitor function executes the syringe pump monitor. * @details Inputs: FPGA syringe pump readings * @details Outputs: Alarm(s) may be triggered * @return none *************************************************************************/ static void execSyringePumpMonitor( void ) { // Check if a new calibration is available if ( TRUE == isNewCalibrationRecordAvailable() ) { // Get the new calibration data and check its validity getNVRecord2Driver( GET_CAL_HEPARIN_FORCE_SENSOR, (U08*)&forceSensorCalRecord, sizeof( HD_HEPARIN_FORCE_SENSOR_CAL_RECORD_T ), 0, ALARM_ID_HD_HEPARIN_FORCE_SENSOR_INVALID_CAL_RECORD ); } S32 encPosition = getFPGASyringePumpEncoderPosition(); // Get latest FPGA status syringePumpStatus.data = (U32)getFPGASyringePumpStatus(); syringePumpEncoderStatus.data = (U32)getFPGASyringePumpEncoderStatus(); syringePumpADCandDACStatus.data = (U32)getFPGASyringePumpADCandDACStatus(); syringePumpADCReadCtr.data = (U32)getFPGASyringePumpADCReadCounter(); // Get latest ADC data and convert to V syringePumpMeasHome.data = ( (F32)getFPGASyringePumpADCChannel2() * SYRINGE_PUMP_ADC_REF_V ) / SYRINGE_PUMP_ADC_FULL_SCALE_BITS; #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP ) != SW_CONFIG_DISABLE_VALUE ) { syringePumpMeasSyringeDetectionSwitch.data = 0.0; // if syringe pump disabled, always report no detection } else #endif { syringePumpMeasSyringeDetectionSwitch.data = ( (F32)getFPGASyringePumpADCChannel1() * SYRINGE_PUMP_ADC_REF_V ) / SYRINGE_PUMP_ADC_FULL_SCALE_BITS; } // Handle syringe detect alarm management #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) #endif { F32 bolusVol = getTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_BOLUS_VOLUME ); F32 hepRate = getTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_DISPENSE_RATE ); // Syringe detection not required if not using Heparin in treatment if ( ( bolusVol > NEARLY_ZERO ) || ( hepRate > NEARLY_ZERO ) ) { // On transition from not detected to detected or if syringe detection not required - clear syringe removed alarm condition if ( ( ( prevSyringeDetected != TRUE ) && ( TRUE == isSyringeDetected() ) ) || ( requireSyringeDetection != TRUE ) ) { clearAlarmCondition( ALARM_ID_HD_SYRINGE_PUMP_SYRINGE_REMOVED ); } // If no syringe detected and detection is required, trigger alarm condition else if ( FALSE == isSyringeDetected() ) { activateAlarmNoData( ALARM_ID_HD_SYRINGE_PUMP_SYRINGE_REMOVED ); } // On transition from detected to not detected - clear syringe detected alarm condition if ( ( TRUE == prevSyringeDetected ) && ( isSyringeDetected() != TRUE ) ) { clearAlarmCondition( ALARM_ID_HD_SYRINGE_DETECTED ); } // If syringe detected and alarm active, maintain alarm condition else if ( ( TRUE == isSyringeDetected() ) && ( TRUE == isAlarmActive( ALARM_ID_HD_SYRINGE_DETECTED ) ) ) { activateAlarmNoData( ALARM_ID_HD_SYRINGE_DETECTED ); } } else { clearAlarmCondition( ALARM_ID_HD_SYRINGE_PUMP_SYRINGE_REMOVED ); clearAlarmCondition( ALARM_ID_HD_SYRINGE_DETECTED ); } } syringePumpMeasForce.data = ( (F32)getFPGASyringePumpADCChannel0() * SYRINGE_PUMP_ADC_REF_V ) / SYRINGE_PUMP_ADC_FULL_SCALE_BITS; // Apply home offset to encoder position syringePumpPosition.data = encPosition - syringePumpHomePositionOffset; // Calculate volume delivered from position syringePumpVolumeDelivered.data = (F32)( syringePumpPosition.data - syringePumpVolumeStartPosition ) / SYRINGE_ENCODER_COUNTS_PER_ML; syringePumpVolumeDelivered.data = MAX( syringePumpVolumeDelivered.data, 0.0F ); calcSafetyVolumeDelivered(); // Calculate measured rate (mL/hr) calcMeasRate(); #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) #endif { // Get measured direction syringePumpControllerMeasuredDirection = ( ( getSyringePumpEncoderStatus() & SYRINGE_PUMP_ENCODER_DIRECTION_BIT ) != 0 ? MOTOR_DIR_REVERSE : MOTOR_DIR_FORWARD ); // Calculate direction from encoder position relative to last syringePumpEncoderMeasuredDirection = ( getSyringePumpPosition() - syringePumpLastPosition >= 0 ? MOTOR_DIR_FORWARD : MOTOR_DIR_REVERSE ); } // Check if syringe pump is on while BP is off { BOOL runWhileOff = ( ( SYRINGE_PUMP_HEP_CONTINUOUS_STATE == syringePumpState ) && ( isBloodPumpRunning() != TRUE ) ); if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_RUNNING_WHILE_BP_OFF_ERROR, runWhileOff ) ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_SYRINGE_PUMP_RUNNING_WHILE_BP_OFF_ERROR, (U32)syringePumpState ); } } if ( ( FALSE == syringePumpDACVrefWriteInProgress ) && ( getCurrentOperationMode() > MODE_INIT ) ) { #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) #endif { checkFPGAPersistentAlarms( FPGA_PERS_ERROR_SYRINGE_PUMP_ADC, getSyringePumpADCReadCounter() ); } // Check pump status if ( getSyringePumpStatus() != 0 ) { SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_SYRINGE_PUMP_FAULT, (U32)getSyringePumpStatus() ); } } } /*********************************************************************//** * @brief * The execSyringePump function executes the syringe pump control state machine. * @details Inputs: syringePumpState * @details Outputs: syringePumpState * @return none *************************************************************************/ void execSyringePump( void ) { SYRINGE_PUMP_STATE_T priorSyringeState = syringePumpState; // Execute syringe pump monitor execSyringePumpMonitor(); if ( syringePumpDACRetryCount > 0 ) { syringePumpDACRetryTimer++; } // Execute syringe pump control state machine switch ( syringePumpState ) { case SYRINGE_PUMP_INIT_STATE: syringePumpState = handleSyringePumpInitState(); break; case SYRINGE_PUMP_OFF_STATE: syringePumpState = handleSyringePumpOffState(); break; case SYRINGE_PUMP_RETRACT_STATE: syringePumpState = handleSyringePumpRetractState(); break; case SYRINGE_PUMP_PRELOAD_STATE: syringePumpState = handleSyringePumpPreLoadState(); break; case SYRINGE_PUMP_SEEK_STATE: syringePumpState = handleSyringePumpSeekState(); break; case SYRINGE_PUMP_PRIME_STATE: syringePumpState = handleSyringePumpPrimeState(); break; case SYRINGE_PUMP_HEP_BOLUS_STATE: syringePumpState = handleSyringePumpBolusState(); break; case SYRINGE_PUMP_HEP_CONTINUOUS_STATE: syringePumpState = handleSyringePumpContinuousState(); break; case SYRINGE_PUMP_CONFIG_FORCE_SENSOR_STATE: syringePumpState = handleSyringePumpCalibrateForceSensorState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_HD_SYRINGE_INVALID_STATE, syringePumpState ) break; } if ( priorSyringeState != syringePumpState ) { SEND_EVENT_WITH_2_U32_DATA( HD_EVENT_SYRINGE_PUMP_STATE, priorSyringeState, syringePumpState ); } resetSyringePumpRequestFlags(); // Save syringe pump info for next time syringePumpLastPosition = getSyringePumpPosition(); prevSyringeDetected = isSyringeDetected(); // Publish syringe pump data on interval publishSyringePumpData(); } /*********************************************************************//** * @brief * The execSyringePumpSelfTest function executes the state machine for the * syringe pump self-test. * @details Inputs: none * @details Outputs: forceSensorCalRecord * @return TRUE if the self test passed otherwise, FALSE *************************************************************************/ SELF_TEST_STATUS_T execSyringePumpSelfTest( void ) { SELF_TEST_STATUS_T result = SELF_TEST_STATUS_PASSED; // Read/check force sensor calibration BOOL calStatus = getNVRecord2Driver( GET_CAL_HEPARIN_FORCE_SENSOR, (U08*)&forceSensorCalRecord, sizeof( HD_HEPARIN_FORCE_SENSOR_CAL_RECORD_T ), 0, ALARM_ID_HD_HEPARIN_FORCE_SENSOR_INVALID_CAL_RECORD ); #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) #endif { if ( TRUE == calStatus ) { result = SELF_TEST_STATUS_PASSED; } else { result = SELF_TEST_STATUS_FAILED; } } return result; } /*********************************************************************//** * @brief * The handleSyringePumpInitState function handles the initialize state * of the syringe pump control state machine. * @details Inputs: none * @details Outputs: pump speed set to zero, ADC enabled, direction set to reverse * @return next state *************************************************************************/ static SYRINGE_PUMP_STATE_T handleSyringePumpInitState( void ) { SYRINGE_PUMP_STATE_T result = SYRINGE_PUMP_OFF_STATE; // Set FPGA rate to zero setFPGASyringePumpStepToggleTime( SYRINGE_PUMP_MICROSTEP_TOGGLE_TIME_FOR_STOP ); // Set FPGA control bits for ADC/DAC setFPGASyringePumpADCandDACControlFlags( SYRINGE_PUMP_ADC_DAC_CONTROL_RD_DAC_ON_ADC | SYRINGE_PUMP_ADC_DAC_CONTROL_ENABLE_ADC ); // Set FPGA control bits for stepper motor (enable, no sleep, dir=FWD, ...) setFPGASyringePumpControlFlags( SYRINGE_PUMP_CONTROL_RUN_REVERSE ); return result; } /*********************************************************************//** * @brief * The handleSyringePumpOffState function handles the off state * of the syringe pump control state machine. * @details Inputs: syringePumpRetractRequested, syringePumpSeekRequested, * syringePumpPrimeRequested, syringePumpBolusRequested, syringePumpContinuousRequested, * syringePumpDACVrefSetRequested,syringePumpStopPositionTimerCounter,syringePumpStopLastPosition * @details Outputs: Pump speed and direction set if pump command initiated * @return next state *************************************************************************/ static SYRINGE_PUMP_STATE_T handleSyringePumpOffState( void ) { SYRINGE_PUMP_STATE_T result = SYRINGE_PUMP_OFF_STATE; // Check the encoder position every 50ms interval after pump off state transition. if ( ++syringePumpStopPositionTimerCounter >= SYRINGE_PUMP_STOP_POSITION_CHECK_INTERVAL ) { // Check position is not changing while stopped if ( ( syringePumpStopLastPosition != getSyringePumpPosition() ) && ( TRUE == incTimeWindowedCount( TIME_WINDOWED_COUNT_SYRINGE_PUMP_OFF_ERROR ) ) ) { #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) #endif { activateAlarmNoData( ALARM_ID_HD_SYRINGE_PUMP_NOT_STOPPED_ERROR ); activateSafetyShutdown(); } } // last pump stop position value syringePumpStopLastPosition = getSyringePumpPosition(); syringePumpStopPositionTimerCounter = 0; } // Reset persistence for direction alarms while pump is off isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, FALSE ); isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR, FALSE ); isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_SPEED_ERROR, FALSE ); // Reset ramp % complete syringePumpRampUpPct = 0.0; // Check for request flags if ( TRUE == syringePumpRetractRequested ) { result = SYRINGE_PUMP_RETRACT_STATE; } else if ( TRUE == syringePumpPreloadRequested ) { result = SYRINGE_PUMP_PRELOAD_STATE; } else if ( TRUE == syringePumpSeekRequested ) { result = SYRINGE_PUMP_SEEK_STATE; } else if ( TRUE == syringePumpPrimeRequested ) { result = SYRINGE_PUMP_PRIME_STATE; } else if ( TRUE == syringePumpBolusRequested ) { heparinDeliveryState = HEPARIN_STATE_INITIAL_BOLUS; sendTreatmentLogEventData( HEPARIN_BOLUS_START_EVENT, 0.0, syringePumpSetRate ); result = SYRINGE_PUMP_HEP_BOLUS_STATE; } else if ( TRUE == syringePumpContinuousRequested ) { heparinDeliveryState = HEPARIN_STATE_DISPENSING; sendTreatmentLogEventData( HEPARIN_START_RESUME_EVENT, 0.0, syringePumpSetRate ); result = SYRINGE_PUMP_HEP_CONTINUOUS_STATE; } else if ( ( TRUE == syringePumpDACVrefSetRequested ) || ( ( syringePumpDACRetryCount > 0 ) && ( syringePumpDACRetryTimer > SYRINGE_PUMP_DAC_TIMER ) ) ) { U16 vRef = (U16)( ( syringePumpDACVref / SYRINGE_PUMP_ADC_REF_V ) * SYRINGE_PUMP_DAC_FULL_SCALE_BITS ); setFPGASyringePumpDACOutputLevel( vRef ); setFPGASyringePumpADCandDACControlFlags( SYRINGE_PUMP_ADC_DAC_CONTROL_ENABLE_DAC ); syringePumpDACVrefWriteInProgress = TRUE; result = SYRINGE_PUMP_CONFIG_FORCE_SENSOR_STATE; } // If we are starting an active pump state, set direction and calculate target toggle time to achieve desired rate if ( ( result != SYRINGE_PUMP_OFF_STATE ) && ( result != SYRINGE_PUMP_CONFIG_FORCE_SENSOR_STATE ) ) { syringePumpStateStartTime = getMSTimerCount(); if ( SYRINGE_PUMP_RETRACT_STATE == result ) { // Set fpga direction to reverse setFPGASyringePumpControlFlags( SYRINGE_PUMP_CONTROL_RUN_REVERSE ); } else { // Set fpga direction to forward setFPGASyringePumpControlFlags( SYRINGE_PUMP_CONTROL_RUN_FORWARD ); } // Calculate target FPGA rate from set rate in mL/hr converted to microstep toggle interval in uSec calcStepperToggleTimeForTargetRate( syringePumpSetRate ); setFPGASyringePumpStepToggleTime( syringePumpRampUpToggleTime ); // Reset stall detect variables syringePumpStallRetryCount = 0; syringePumpStallCtr = 0; } // Reset ramp up timer syringePumpRampTimerCtr = 1; return result; } /*********************************************************************//** * @brief * The handleSyringePumpRetractState function handles the retract state * of the syringe pump control state machine. * @details Inputs: syringePumpMeasHome.data * @details Outputs: Syringe pump ramped up to retract rate, alarm conditions checked * @return next state *************************************************************************/ static SYRINGE_PUMP_STATE_T handleSyringePumpRetractState( void ) { SYRINGE_PUMP_STATE_T result = SYRINGE_PUMP_RETRACT_STATE; BOOL stopPump = FALSE; // Handle ramp up rampSyringePump(); // Stop retract when home position is detected if ( TRUE == isSyringePumpHome() ) { stopPump = TRUE; // Set position to zero after retract reaches home position syringePumpHomePositionOffset = getFPGASyringePumpEncoderPosition(); syringePumpPosition.data = 0; syringePumpVolumeStartPosition = 0; syringePumpPositionKnown = TRUE; // Zero volumes after retract syringePumpVolumeDelivered.data = 0.0; syringePumpSafetyVolumeDelivered = 0.0; // Reset flags syringePumpPlungerFound = FALSE; syringeVolumeAdequate = FALSE; syringePumpPrimeCompleted = FALSE; syringePumpPreLoadCompleted = FALSE; syringePumpVolumeRequired = 0.0F; // Clear insufficient volume alarm condition in case we're retracting to allow user to resolve alarm clearAlarmCondition( ALARM_ID_HD_SYRINGE_PUMP_NOT_ENOUGH_HEPARIN_ALARM ); } // If position known from prior retract, ensure we don't retract beyond minimum position else if ( ( TRUE == syringePumpPositionKnown ) && ( getSyringePumpPosition() < SYRINGE_PUMP_RETRACT_POS_MIN ) ) { stopPump = TRUE; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SYRINGE_PUMP_OVER_TRAVEL_ERROR, (U32)getSyringePumpPosition(), (U32)SYRINGE_PUMP_RETRACT_STATE ); } // Check for stall stopPump = checkForStall( stopPump ); // Check direction stopPump = checkDirection( stopPump, MOTOR_DIR_REVERSE ); // If anything found that would require stopping the pump, stop pump and go to off state if ( TRUE == stopPump ) { stopSyringePump(); result = SYRINGE_PUMP_OFF_STATE; } return result; } /*********************************************************************//** * @brief * The handleSyringePumpPreLoadState function handles the PreLoad plunger state * of the syringe pump control state machine. Move to position that limits * loadable Heparin volume to amount needed for treatment. * @details Inputs: syringePumpMeasForce.data * @details Outputs: syringePumpVolumeRequired, Syringe pump moved to preload position, alarm conditions checked * @return next state *************************************************************************/ static SYRINGE_PUMP_STATE_T handleSyringePumpPreLoadState( void ) { SYRINGE_PUMP_STATE_T result = SYRINGE_PUMP_PRELOAD_STATE; BOOL stopPump = FALSE; S32 pos = getSyringePumpPosition(); F32 bolusVol = getTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_BOLUS_VOLUME ); F32 contRate = getTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_DISPENSE_RATE ); U32 preStop = getTreatmentParameterU32( TREATMENT_PARAM_HEPARIN_PRE_STOP_TIME ); U32 setTxDur = getTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_DURATION ); F32 hepDurHr = ( (F32)( setTxDur - preStop ) / (F32)MIN_PER_HOUR ); F32 txVolumeReq = SYRINGE_PUMP_PRIME_VOLUME_ML + bolusVol + ( hepDurHr * contRate ); F32 syringeVol = ( SYRINGE_PUMP_EMPTY_POS - (F32)pos ) / SYRINGE_ENCODER_COUNTS_PER_ML; syringePumpVolumeRequired = txVolumeReq + SYRINGE_PUMP_FILL_VOLUME_OFFSET_ML; txVolumeReq = txVolumeReq + SYRINGE_PUMP_FILL_VOLUME_OFFSET_ML + SYRINGE_PUMP_PRELOAD_MARGIN_VOLUME_ML; // Handle ramp up rampSyringePump(); // Is plunger Heparin volume position detected? or volume requiring full syringe. if ( ( syringeVol <= txVolumeReq ) || ( SYRINGE_PUMP_PRELOAD_MAX_VOLUME_ML < txVolumeReq ) ) { stopPump = TRUE; syringePumpVolumeDelivered.data = 0.0; syringePumpSafetyVolumeDelivered = 0.0; syringePumpVolumeStartPosition = pos; syringePumpPreLoadCompleted = TRUE; } // Check for stall stopPump = checkForStall( stopPump ); // Check max position > empty + 0.5 mL stopPump = checkMaxTravel( stopPump, SYRINGE_PUMP_EMPTY_POS + SYRINGE_PUMP_EMPTY_POS_MARGIN ); // Check pump direction stopPump = checkDirection( stopPump, MOTOR_DIR_FORWARD ); // If anything found that would require stopping the pump, stop pump and go to off state if ( TRUE == stopPump ) { stopSyringePump(); result = SYRINGE_PUMP_OFF_STATE; } return result; } /*********************************************************************//** * @brief * The handleSyringePumpSeekState function handles the seek plunger state * of the syringe pump control state machine. * @details Inputs: syringePumpMeasForce.data * @details Outputs: Syringe pump ramped up to seek rate, alarm conditions checked * @return next state *************************************************************************/ static SYRINGE_PUMP_STATE_T handleSyringePumpSeekState( void ) { SYRINGE_PUMP_STATE_T result = SYRINGE_PUMP_SEEK_STATE; BOOL stopPump = FALSE; S32 pos = getSyringePumpPosition(); F32 bolusVol = getTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_BOLUS_VOLUME ); F32 contRate = getTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_DISPENSE_RATE ); U32 preStop = getTreatmentParameterU32( TREATMENT_PARAM_HEPARIN_PRE_STOP_TIME ); U32 setTxDur = getTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_DURATION ); F32 hepDurHr = ( (F32)( setTxDur - preStop ) / (F32)MIN_PER_HOUR ); F32 txVolume = SYRINGE_PUMP_PRIME_VOLUME_ML + bolusVol + ( hepDurHr * contRate ); F32 syringeVol = ( SYRINGE_PUMP_EMPTY_POS - (F32)pos ) / SYRINGE_ENCODER_COUNTS_PER_ML; // Handle ramp up rampSyringePump(); // Is plunger contact detected or insufficient Heparin volume detected? if ( ( getSyringePumpForceV() >= SYRINGE_FORCE_PLUNGER_THRESHOLD_V ) || ( syringeVol < txVolume ) ) { stopPump = TRUE; syringePumpVolumeDelivered.data = 0.0; syringePumpSafetyVolumeDelivered = 0.0; syringePumpVolumeStartPosition = pos; // Check estimated syringe volume needed for treatment vs. volume detected - if insufficient for treatment needs, alarm (check only applies in pre-tx mode) if ( ( syringeVol >= txVolume ) || ( getCurrentOperationMode() != MODE_PRET ) ) { syringePumpPlungerFound = TRUE; syringeVolumeAdequate = TRUE; } else { #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) #endif { SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_SYRINGE_PUMP_NOT_ENOUGH_HEPARIN_ALARM, syringeVol, txVolume ); } } } // Check for stall stopPump = checkForStall( stopPump ); // Has syringe been removed? stopPump = checkSyringeRemoved( stopPump ); // Check max position > empty + 0.5 mL stopPump = checkMaxTravel( stopPump, SYRINGE_PUMP_EMPTY_POS + SYRINGE_PUMP_EMPTY_POS_MARGIN ); // Check pump direction stopPump = checkDirection( stopPump, MOTOR_DIR_FORWARD ); // If anything found that would require stopping the pump, stop pump and go to off state if ( TRUE == stopPump ) { stopSyringePump(); result = SYRINGE_PUMP_OFF_STATE; } return result; } /*********************************************************************//** * @brief * The handleSyringePumpPrimeState function handles the prime state * of the syringe pump control state machine. * @details Inputs: syringePumpVolumeDelivered, syringePumpStateStartTime * @details Outputs: Syringe pump ramped up to prime rate, alarm conditions checked * @return next state *************************************************************************/ static SYRINGE_PUMP_STATE_T handleSyringePumpPrimeState( void ) { SYRINGE_PUMP_STATE_T result = SYRINGE_PUMP_PRIME_STATE; BOOL stopPump = FALSE; // Handle ramp up rampSyringePump(); // Has prime volume been delivered? if ( getSyringePumpVolumeDelivered() >= SYRINGE_PUMP_PRIME_VOLUME_ML ) { stopPump = TRUE; syringePumpPrimeCompleted = TRUE; syringePumpVolumeDelivered.data = 0.0; syringePumpSafetyVolumeDelivered = 0.0; syringePumpVolumeStartPosition = syringePumpPosition.data; resetPersistentAlarmTimer( ALARM_ID_HD_SYRINGE_PUMP_OCCLUSION ); // reset persistence after prime so ensured fresh start before continuous state } // Check for timeout if ( TRUE == didTimeout( syringePumpStateStartTime, SYRINGE_PUMP_PRIMING_TIMEOUT_MS ) ) { stopPump = TRUE; SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_SYRINGE_PUMP_PRIME_TIMEOUT, (F32)SYRINGE_PUMP_PRIMING_TIMEOUT_MS, 0.0F ) } // Check for stall stopPump = checkForStall( stopPump ); // Has syringe been removed? stopPump = checkSyringeRemoved( stopPump ); // Check position > max travel stopPump = checkMaxTravel( stopPump, syringePumpVolumeStartPosition + ( SYRINGE_PUMP_PRIME_VOLUME_ML * TEN_PCT_OVER_ALLOWANCE * SYRINGE_ENCODER_COUNTS_PER_ML ) ); // Check pump direction stopPump = checkDirection( stopPump, MOTOR_DIR_FORWARD ); // If anything found that would require stopping the pump, stop pump if ( TRUE == stopPump ) { stopSyringePump(); result = SYRINGE_PUMP_OFF_STATE; } return result; } /*********************************************************************//** * @brief * The handleSyringePumpBolusState function handles the bolus delivery state * of the syringe pump control state machine. * @details Inputs: set bolus volume, syringePumpVolumeDelivered.data * @details Outputs: Syringe pump ramped up to set bolus rate, alarm conditions checked * @return next state *************************************************************************/ static SYRINGE_PUMP_STATE_T handleSyringePumpBolusState( void ) { SYRINGE_PUMP_STATE_T result = SYRINGE_PUMP_HEP_BOLUS_STATE; BOOL stopPump = FALSE; F32 bolusVol = getTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_BOLUS_VOLUME ); // Handle ramp up rampSyringePump(); // Is bolus completed? if ( getSyringePumpVolumeDelivered() >= bolusVol ) { stopPump = TRUE; } // Has syringe been removed? stopPump = checkSyringeRemoved( stopPump ); // Check volume vs. safety volume stopPump = checkVolumeVsSafetyVolume( stopPump, SYRINGE_PUMP_VOLUME_CHECK_MARGIN ); // Check for syringe pump empty stopPump = checkSyringeEmpty( stopPump ); // Check for commanded vs. meas. rate stopPump = checkMeasRate( stopPump, SYRINGE_PUMP_RATE_CHECK_MARGIN ); // Check position > max travel stopPump = checkMaxTravel( stopPump, syringePumpVolumeStartPosition + ( bolusVol * FIVE_PCT_OVER_ALLOWANCE * SYRINGE_ENCODER_COUNTS_PER_ML ) ); // Check pump direction stopPump = checkDirection( stopPump, MOTOR_DIR_FORWARD ); // If anything found that would require stopping the pump, stop pump and go to off state if ( TRUE == stopPump ) { stopSyringePump(); result = SYRINGE_PUMP_OFF_STATE; } return result; } /*********************************************************************//** * @brief * The handleSyringePumpContinuousState function handles the continuous state * of the syringe pump control state machine. * @details Inputs: none * @details Outputs: Syringe pump ramped up to set rate, alarm conditions checked * @return next state *************************************************************************/ static SYRINGE_PUMP_STATE_T handleSyringePumpContinuousState( void ) { SYRINGE_PUMP_STATE_T result = SYRINGE_PUMP_HEP_CONTINUOUS_STATE; BOOL stopPump = FALSE; // Handle ramp up rampSyringePump(); // Has syringe been removed? stopPump = checkSyringeRemoved( stopPump ); // Check for syringe pump empty stopPump = checkSyringeEmpty( stopPump ); // Check for occlusion stopPump = checkForSyringeOcclusion( stopPump ); // Check position > empty + 0.5 mL stopPump = checkMaxTravel( stopPump, SYRINGE_PUMP_EMPTY_POS + SYRINGE_PUMP_EMPTY_POS_MARGIN ); // Check for commanded vs. meas. rate stopPump = checkMeasRate( stopPump, SYRINGE_PUMP_RATE_CHECK_MARGIN ); // Check volume vs. safety volume stopPump = checkVolumeVsSafetyVolume( stopPump, SYRINGE_PUMP_VOLUME_CHECK_MARGIN ); // Check pump direction stopPump = checkDirection( stopPump, MOTOR_DIR_FORWARD ); // If anything found that would require stopping the pump, stop pump and go to off state if ( TRUE == stopPump ) { stopSyringePump(); result = SYRINGE_PUMP_OFF_STATE; } return result; } /*********************************************************************//** * @brief * The handleSyringePumpCalibrateForceSensorState function handles the * calibrate force sensor state of the syringe pump control state machine. * of the syringe pump control state machine. * @details Inputs: DAC status, syringePumpDACRetryCount * @details Outputs: syringePumpDACVrefWriteInProgress, ADC read mode restored * syringePumpDACRetryCount, syringePumpDACRetryTimer * @return next state *************************************************************************/ static SYRINGE_PUMP_STATE_T handleSyringePumpCalibrateForceSensorState( void ) { SYRINGE_PUMP_STATE_T result = SYRINGE_PUMP_CONFIG_FORCE_SENSOR_STATE; U08 adcDACStatus = getSyringePumpADCandDACStatus(); // Check DAC write error if ( ( adcDACStatus & SYRINGE_PUMP_DAC_WRITE_ERROR_BIT ) != 0 ) { syringePumpDACRetryCount++; if ( syringePumpDACRetryCount > SYRINGE_PUMP_DAC_MAX_RETRIES ) { syringePumpDACRetryCount = 0; activateAlarmNoData( ALARM_ID_HD_SYRINGE_PUMP_DAC_WRITE_ERROR ); } syringePumpDACVrefWriteInProgress = FALSE; // Switch back from DAC to ADC control setFPGASyringePumpADCandDACControlFlags( SYRINGE_PUMP_ADC_DAC_CONTROL_RD_DAC_ON_ADC | SYRINGE_PUMP_ADC_DAC_CONTROL_ENABLE_ADC ); // Back to off state result = SYRINGE_PUMP_OFF_STATE; } // Wait for DAC setting write to EEPROM to complete else if ( ( adcDACStatus & SYRINGE_PUMP_ADC_DAC_ERROR_COUNT_DAC_WR_DONE ) != 0 ) { // We're done and no error bit found. // Clear retry attempts. syringePumpDACRetryCount = 0; syringePumpDACRetryTimer = 0; syringePumpDACVrefWriteInProgress = FALSE; // Switch back from DAC to ADC control setFPGASyringePumpADCandDACControlFlags( SYRINGE_PUMP_ADC_DAC_CONTROL_RD_DAC_ON_ADC | SYRINGE_PUMP_ADC_DAC_CONTROL_ENABLE_ADC ); // Back to off state result = SYRINGE_PUMP_OFF_STATE; } return result; } /*********************************************************************//** * @brief * The checkDirection function checks the measured direction of the syringe * pump vs. the given expected direction. * @details Inputs: syringePumpMeasForce.data, syringePumpPosition.data * @details Outputs: alarm triggered if max force detected * @param stopPump flag passed in by caller indicating whether pump should be stopped * @param expDir expected direction of syringe pump * @return TRUE if pump should be stopped, FALSE if not *************************************************************************/ static BOOL checkDirection( BOOL stopPump, MOTOR_DIR_T expDir ) { BOOL result = stopPump; #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) #endif { // Check direction if rate >= minimum for alarm if ( getSyringePumpMeasRate() >= MIN_SYRINGE_PUMP_RATE_FOR_DIR_ALARM ) { if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, ( syringePumpEncoderMeasuredDirection != expDir ) ) ) { result = TRUE; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, (U32)syringePumpEncoderMeasuredDirection, (U32)syringePumpState ); } if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR, ( syringePumpControllerMeasuredDirection != expDir ) ) ) { result = TRUE; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR, (U32)syringePumpControllerMeasuredDirection, (U32)syringePumpState ); } } else { isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, FALSE ); isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR, FALSE ); } } return result; } /*********************************************************************//** * @brief * The checkSyringeEmpty function checks the force sensor position * to determine if the syringe pump is empty. * @details Inputs: syringePumpMeasForce.data, syringePumpPosition.data * @details Outputs: alarm triggered if position is empty. * @param stopPump flag passed in by caller indicating whether pump should be stopped * @return TRUE if pump should be stopped, FALSE if not *************************************************************************/ static BOOL checkSyringeEmpty( BOOL stopPump ) { BOOL result = stopPump; F32 force = getSyringePumpForceV(); S32 pos = getSyringePumpPosition(); if ( force >= SYRINGE_PUMP_EMPTY_THRESHOLD_V ) { syringePumpEmptyForceCount++; } else { syringePumpEmptyForceCount = 0; } // If near empty position, assume syringe is empty // Check for force threshold over some time and within SYRINGE_PUMP_HW_TOLERANCE_POS of empty if ( ( pos >= SYRINGE_PUMP_EMPTY_POS ) || ( ( syringePumpEmptyForceCount >= SYRINGE_PUMP_EMPTY_FORCE_COUNT ) && ( pos >= ( SYRINGE_PUMP_EMPTY_POS - SYRINGE_PUMP_HW_TOLERANCE_POS ) ) ) ) { heparinDeliveryState = HEPARIN_STATE_EMPTY; SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_SYRINGE_PUMP_SYRINGE_EMPTY, (F32)pos, force ) result = TRUE; } return result; } /*********************************************************************//** * @brief * The checkForPrimeOcclusion function checks the force sensor for excessive * pressure. Would indicate occlusion or jam or empty syringe. * @details Inputs: syringePumpMeasForce.data, syringePumpPosition.data * @details Outputs: alarm triggered if max force detected * @param stopPump flag passed in by caller indicating whether pump should be stopped * @return TRUE if pump should be stopped, FALSE if not *************************************************************************/ BOOL checkForSyringeOcclusion( BOOL stopPump ) { BOOL result = stopPump; F32 currentForceV = getSyringePumpForceV(); // Read the force sensor at the end of the priming F32 forceDelta = currentForceV - forceAtEndOfSeek; // Occlusion is detected if force at end of prime is > than force at end of seek by 0.5 volts or more BOOL occlusionDetected = FALSE; // Checking for occlusion in dry self tests without persistence if ( MODE_PRET == getCurrentOperationMode() ) { occlusionDetected = ( forceDelta >= SYRINGE_FORCE_OCCLUSION_DIFF_V ? TRUE : FALSE ); if ( TRUE == occlusionDetected ) { SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_SYRINGE_PUMP_OCCLUSION, forceAtEndOfSeek, currentForceV ) result = TRUE; } else { clearAlarmCondition( ALARM_ID_HD_SYRINGE_PUMP_OCCLUSION ); } } // Check with persistence during continuous state if ( SYRINGE_PUMP_OP_CONTINUOUS == syringePumpState ) { occlusionDetected = ( currentForceV >= SYRINGE_FORCE_OCCLUSION_THRESHOLD_V ? TRUE : FALSE ); checkPersistentAlarm( ALARM_ID_HD_SYRINGE_PUMP_OCCLUSION, occlusionDetected, forceAtEndOfSeek, currentForceV ); } return result; } /*********************************************************************//** * @brief * The checkSyringeRemoved function checks whether the syringe has been removed. * This function should only be called from states that require the syringe to * be installed. * @details Inputs: syringePumpMeasSyringeDetectionSwitch.data * @details Outputs: alarm triggered if syringe not detected * @param stopPump flag passed in by caller indicating whether pump should be stopped * @return TRUE if pump should be stopped, FALSE if not *************************************************************************/ static BOOL checkSyringeRemoved( BOOL stopPump ) { BOOL result = stopPump; if ( FALSE == isSyringeDetected() ) { result = TRUE; SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_SYRINGE_PUMP_SYRINGE_REMOVED, (U32)syringePumpState ) } return result; } /*********************************************************************//** * @brief * The checkMaxTravel function checks whether a maximum travel (position) * has been exceeded. This threshold is state dependent so the calling function * must provide the maximum position to apply. * @details Inputs: syringePumpPosition.data * @details Outputs: alarm triggered if beyond given max position * @param stopPump flag passed in by caller indicating whether pump should be stopped * @param maxPos maximum position allowed in current state * @return TRUE if pump should be stopped, FALSE if not *************************************************************************/ static BOOL checkMaxTravel( BOOL stopPump, S32 maxPos ) { BOOL result = stopPump; S32 pos = getSyringePumpPosition(); if ( pos >= maxPos ) { result = TRUE; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SYRINGE_PUMP_OVER_TRAVEL_ERROR, (U32)pos, (U32)syringePumpState ); } return result; } /*********************************************************************//** * @brief * The checkMeasRate function checks whether the measured rate is within a * given margin of the set rate (in mL/hr). * @details Inputs: syringePumpMeasRate.data, syringePumpSetRate * @details Outputs: alarm triggered if measured and set rates deviate too much * @param stopPump flag passed in by caller indicating whether pump should be stopped * @param pctMargin percent tolerance allowed between set and measured rate * @return TRUE if pump should be stopped, FALSE if not *************************************************************************/ static BOOL checkMeasRate( BOOL stopPump, F32 pctMargin ) { BOOL result = stopPump; #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) #endif { F32 rate = getSyringePumpMeasRate(); F32 max = MAX( rate, syringePumpSetRate ); F32 min = MIN( rate, syringePumpSetRate ); F32 error = ( max > 0.0 ? ( 1.0 - fabs( min / max ) ) : 0.0 ); F32 delta = max - min; // Alarm on rate if off by more than 5% or 0.1 mL/hr, whichever is greater if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_SPEED_ERROR, ( ( error > pctMargin ) && ( delta > SYRINGE_PUMP_MAX_RATE_ERROR_ML_HR ) ) ) ) { result = TRUE; SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_SYRINGE_PUMP_SPEED_ERROR, syringePumpSetRate, rate ) } } return result; } /*********************************************************************//** * @brief * The checkVolumeVsSafetyVolume function checks whether the volume delivered * and the safety volume delivered have diverged too much. The threshold * is state dependent so the calling function must provide the tolerance (in +/- %) * to apply. * @details Inputs: syringePumpVolumeDelivered.data, syringePumpSafetyVolumeDelivered * @details Outputs: alarm triggered if measured and expected volume deviate too much * @param stopPump flag passed in by caller indicating whether pump should be stopped * @param pctMargin percent tolerance allowed between volume and safety volume * @return TRUE if pump should be stopped, FALSE if not *************************************************************************/ static BOOL checkVolumeVsSafetyVolume( BOOL stopPump, F32 pctMargin ) { BOOL result = stopPump; F32 vol = getSyringePumpVolumeDelivered(); F32 max = MAX( vol, syringePumpSafetyVolumeDelivered ); F32 min = MIN( vol, syringePumpSafetyVolumeDelivered ); F32 delta = max - min; F32 error = ( fabs( max ) < NEARLY_ZERO ? 0.0 : ( 1.0 - fabs( min / max ) ) ); // Alarm if volume delivered off by more than 5% or 0.1 mL, whichever is greater if ( ( error > pctMargin ) && ( delta > SYRINGE_PUMP_MAX_VOL_ERROR_ML ) ) { result = TRUE; SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_SYRINGE_PUMP_VOLUME_ERROR, vol, syringePumpSafetyVolumeDelivered ) } return result; } /*********************************************************************//** * @brief * The checkForStall function checks whether the syringe pump has stalled. * If stall detected, the ramp up will be restarted up to 3 times. * If cannot resolve the stall within 3 retries, a stall fault is triggered. * @details Inputs: syringePumpMeasRate.data, syringePumpStallCtr, syringePumpStallRetryCount * @details Outputs: syringePumpStallCtr, syringePumpStallRetryCount * @param stopPump flag passed in by caller indicating whether pump should be stopped * @return TRUE if pump should be stopped, FALSE if not *************************************************************************/ static BOOL checkForStall( BOOL stopPump ) { BOOL result = stopPump; // Check for stall if ( fabs( getSyringePumpMeasRate() ) < SYRINGE_PUMP_STALL_SPEED_THRESHOLD ) { if ( ++syringePumpStallCtr >= SYRINGE_PUMP_RAMP_STALL_TIME ) { if ( ++syringePumpStallRetryCount <= SYRINGE_PUMP_RAMP_STALL_RETRIES ) { syringePumpSetToggleTime++; // lower target rate (by increasing time between steps) syringePumpRampTimerCtr = 0; // restart ramp syringePumpRampUpToggleTime = SYRINGE_PUMP_START_RAMP_SPEED; syringePumpStallCtr = 0; // reset stall counter } else { result = TRUE; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SYRINGE_PUMP_STALL, (U32)getSyringePumpPosition(), (U32)syringePumpState ); } } } else { syringePumpStallCtr = 0; } return result; } /*********************************************************************//** * @brief * The rampSyringePump function handles the ramp-up for the syringe pump. * @details Inputs: syringePumpRampTimerCtr, syringePumpSetToggleTime * @details Outputs: syringePumpRampUpToggleTime * @return none *************************************************************************/ static void rampSyringePump( void ) { // Ramp up syringe pump toward target speed syringePumpRampTimerCtr++; if ( syringePumpRampUpToggleTime > syringePumpSetToggleTime ) { syringePumpRampUpToggleTime = (U32)( (F32)SYRINGE_PUMP_START_RAMP_SPEED / ( (F32)( syringePumpRampTimerCtr * syringePumpRampTimerCtr * syringePumpRampTimerCtr ) / (F32)SYRINGE_PUMP_RAMP_DIVISOR ) ); syringePumpRampUpPct = (F32)syringePumpSetToggleTime / (F32)syringePumpRampUpToggleTime; if ( syringePumpRampUpToggleTime > syringePumpSetToggleTime ) { setFPGASyringePumpStepToggleTime( syringePumpRampUpToggleTime ); } else { syringePumpRampUpPct = 1.0; // set to 100% ramp completion setFPGASyringePumpStepToggleTime( syringePumpSetToggleTime ); } } } /*********************************************************************//** * @brief * The calcStepperToggleTimeForTargetRate function calculates the stepper * toggle period for a given rate. * @details Inputs: none * @details Outputs: syringePumpSetToggleTime, syringePumpRampUpToggleTime * @param rate the rate (in mL/hr) to calculate stepper toggle period for * @return none *************************************************************************/ static void calcStepperToggleTimeForTargetRate( F32 rate ) { F64 temp; F32 conv; // Convert given rate to stepper toggle period temp = (F64)rate * SYRINGE_MICRO_STEPS_PER_ML; // = uSteps/hr temp /= (F64)( MIN_PER_HOUR * SEC_PER_MIN); // = uSteps/sec temp /= MICRO_SECONDS_PER_SECOND; // = uSteps/uSec conv = (F32)temp * SYRINGE_TOGGLES_PER_STEP; // = toggles/uSec conv = 1.0 / conv; // = uSec/toggle // Set stepper toggle time to calculated value syringePumpSetToggleTime = FLOAT_TO_INT_WITH_ROUND( conv ); // Set stepper ramp toggle time to initial ramp up speed initially syringePumpRampUpToggleTime = SYRINGE_PUMP_START_RAMP_SPEED; // Reset ramp stall timer and retry counters syringePumpStallCtr = 0; syringePumpStallRetryCount = 0; } /*********************************************************************//** * @brief * The calcMeasRate function calculates the measured rate from a given delta * position in last 1 second. * @details Inputs: syringePumpLastPositions[], syringePumpSpeedCalcTimerCounter * @details Outputs: syringePumpMeasRate, syringePumpSpeedCalcTimerCounter * @return none *************************************************************************/ static void calcMeasRate( void ) { if ( ++syringePumpSpeedCalcTimerCounter >= SYRINGE_PUMP_SPEED_CALC_INTERVAL ) { S32 pos = getSyringePumpPosition(); U32 nextIdx = INC_WRAP( syringePumpMotorSpeedCalcIdx, 0, SYRINGE_PUMP_SPEED_CALC_BUFFER_LEN - 1 ); S32 countsPerSec = pos - syringePumpLastPositions[ syringePumpMotorSpeedCalcIdx ]; // Calc delta between pos 1 second ago and pos now S32 countsPerHr = countsPerSec * ( MIN_PER_HOUR * SEC_PER_MIN); F32 mLPerHr = (F32)((F64)countsPerHr / (F64)SYRINGE_ENCODER_COUNTS_PER_ML); // Set latest measured rate syringePumpMeasRate.data = mLPerHr; // Update last position for next time syringePumpLastPositions[ syringePumpMotorSpeedCalcIdx ] = pos; syringePumpMotorSpeedCalcIdx = nextIdx; syringePumpSpeedCalcTimerCounter = 0; } } /*********************************************************************//** * @brief * The calcSafetyVolumeDelivered function calculates the safety volume from * commanded rate over time (last 10 ms). * @details Inputs: syringePumpSetRate * @details Outputs: syringePumpSafetyVolumeDelivered * @return none *************************************************************************/ static void calcSafetyVolumeDelivered( void ) { syringePumpSafetyVolumeDelivered += ( syringePumpRampUpPct * ( syringePumpSetRate / (F32)( MIN_PER_HOUR * SEC_PER_MIN * ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ) ) ); } /*********************************************************************//** * @brief * The publishSyringePumpData function publishes syringe pump data at the * set interval. * @details Inputs: latest syringe pump data, syringePumpDataPublicationTimerCounter * @details Outputs: syringePumpDataPublicationTimerCounter * @return none *************************************************************************/ static void publishSyringePumpData( void ) { // Publish syringe pump data on interval if ( ++syringePumpDataPublicationTimerCounter >= getU32OverrideValue( &syringePumpDataPublishInterval ) ) { SYRINGE_PUMP_DATA_PAYLOAD_T data; SYRINGE_PUMP_VOLUME_DATA_T volData; data.syringePumpMeasForce = getSyringePumpForceV(); data.syringePumpMeasHome = getSyringePumpHomeDetectorV(); data.syringePumpMeasRate = getSyringePumpMeasRate(); data.syringePumpMeasSwitch = getSyringePumpSyringeDetectorV(); data.syringePumpPosition = getSyringePumpPosition(); data.syringePumpSetRate = syringePumpSetRate; data.syringePumpState = (U32)syringePumpState; data.heparinDeliveryState = (U32)heparinDeliveryState; data.syringePumpVolumeDelivered = getSyringePumpVolumeDelivered(); data.syringePumpSafetyVolume = syringePumpSafetyVolumeDelivered; data.syringePumpStatus = ( (U32)getSyringePumpStatus() << SHIFT_24_BITS ) | ( (U32)getSyringePumpEncoderStatus() << SHIFT_16_BITS_FOR_WORD_SHIFT ) | ( (U32)getSyringePumpADCandDACStatus() << SHIFT_8_BITS_FOR_BYTE_SHIFT ) | ( (U32)getSyringePumpADCReadCounter() ); volData.syringePumpVolumeDelivered = getSyringePumpVolumeDelivered(); volData.syringePumpVolumeRequired = syringePumpVolumeRequired; broadcastData( MSG_ID_HD_SYRINGE_PUMP_DATA, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&data, sizeof( SYRINGE_PUMP_DATA_PAYLOAD_T ) ); broadcastData( MSG_ID_HD_HEPARIN_DATA, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&volData, sizeof( SYRINGE_PUMP_VOLUME_DATA_T ) ); syringePumpDataPublicationTimerCounter = 0; } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /*********************************************************************//** * @brief * The testSetSyringePumpDataPublishIntervalOverride function overrides the * syringe pump data publish interval. * @details Inputs: none * @details Outputs: syringePumpDataPublishInterval * @param value override syringe pump data publish interval with (in ms) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetSyringePumpDataPublishIntervalOverride( U32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { U32 intvl = value / TASK_PRIORITY_INTERVAL; result = TRUE; syringePumpDataPublishInterval.ovData = intvl; syringePumpDataPublishInterval.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetSyringePumpDataPublishIntervalOverride function resets the override * of the syringe pump data publish interval. * @details Inputs: none * @details Outputs: syringePumpDataPublishInterval * @return TRUE if override reset successful, FALSE if not *************************************************************************/ BOOL testResetSyringePumpDataPublishIntervalOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; syringePumpDataPublishInterval.override = OVERRIDE_RESET; syringePumpDataPublishInterval.ovData = syringePumpDataPublishInterval.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSyringePumpOperationRequest function requests a given syringe pump * operation. * @details Inputs: none * @details Outputs: pump operation request is handled * @param opParams record containing the requested operation and its parameters * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSyringePumpOperationRequest( SYRINGE_PUMP_OP_PAYLOAD_T opParams ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { SYRINGE_PUMP_OPERATION_T op = (SYRINGE_PUMP_OPERATION_T)(opParams.syringePumpOp); switch ( op ) { case SYRINGE_PUMP_OP_STOP: stopSyringePump(); result = TRUE; break; case SYRINGE_PUMP_OP_RETRACT: result = retractSyringePump(); break; case SYRINGE_PUMP_OP_SEEK: result = seekSyringePlunger(); break; case SYRINGE_PUMP_OP_PRIME: result = primeSyringePump(); break; case SYRINGE_PUMP_OP_BOLUS: setTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_BOLUS_VOLUME, opParams.volume ); result = startHeparinBolus(); break; case SYRINGE_PUMP_OP_CONTINUOUS: setTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_DISPENSE_RATE, opParams.rate ); result = startHeparinContinuous(); break; default: // Ok, just ignore and we will return FALSE rejecting request break; } } return result; } /*********************************************************************//** * @brief * The testSetSyringePumpMeasuredRateOverride function overrides the measured * rate of the syringe pump. * @details Inputs: none * @details Outputs: syringePumpMeasRate * @param value override syringe pump measured rate with this value (in mL/hr) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetSyringePumpMeasuredRateOverride( F32 value ) { BOOL result = FALSE; if ( ( TRUE == isTestingActivated() ) && ( value <= SYRINGE_PUMP_MAX_RATE ) ) { result = TRUE; syringePumpMeasRate.ovData = value; syringePumpMeasRate.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetSyringePumpMeasuredRateOverride function resets the override * of the syringe pump measured rate. * @details Inputs: none * @details Outputs: syringePumpMeasRate * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetSyringePumpMeasuredRateOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; syringePumpMeasRate.override = OVERRIDE_RESET; syringePumpMeasRate.ovData = syringePumpMeasRate.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetSyringePumpMeasuredForceOverride function overrides the measured * force analog signal of the syringe pump. * @details Inputs: none * @details Outputs: syringePumpMeasForce * @param value override syringe pump measured force signal with this value (in V) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetSyringePumpMeasuredForceOverride( F32 value ) { BOOL result = FALSE; if ( ( TRUE == isTestingActivated() ) && ( value >= 0.0 ) && ( value <= SYRINGE_PUMP_ADC_REF_V ) ) { result = TRUE; syringePumpMeasForce.ovData = value; syringePumpMeasForce.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetSyringePumpMeasuredForceOverride function resets the override * of the syringe pump measured force analog signal. * @details Inputs: none * @details Outputs: syringePumpMeasForce * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetSyringePumpMeasuredForceOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; syringePumpMeasForce.override = OVERRIDE_RESET; syringePumpMeasForce.ovData = syringePumpMeasForce.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetSyringePumpMeasuredSyringeDetectOverride function overrides the measured * syringe detect analog signal of the syringe pump. * @details Inputs: none * @details Outputs: syringePumpMeasSyringeDetectionSwitch * @param value override syringe pump measured syringe detection signal with this value (in V) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetSyringePumpMeasuredSyringeDetectOverride( F32 value ) { BOOL result = FALSE; if ( ( TRUE == isTestingActivated() ) && ( value >= 0.0 ) && ( value <= SYRINGE_PUMP_ADC_REF_V ) ) { result = TRUE; syringePumpMeasSyringeDetectionSwitch.ovData = value; syringePumpMeasSyringeDetectionSwitch.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetSyringePumpMeasuredSyringeDetectOverride function resets the override * of the syringe pump measured syringe detect analog signal. * @details Inputs: none * @details Outputs: syringePumpMeasSyringeDetectionSwitch * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetSyringePumpMeasuredSyringeDetectOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; syringePumpMeasSyringeDetectionSwitch.override = OVERRIDE_RESET; syringePumpMeasSyringeDetectionSwitch.ovData = syringePumpMeasSyringeDetectionSwitch.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetSyringePumpMeasuredHomeOverride function overrides the measured * home analog signal of the syringe pump. * @details Inputs: none * @details Outputs: syringePumpMeasHome * @param value override syringe pump measured home signal with this value (in V) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetSyringePumpMeasuredHomeOverride( F32 value ) { BOOL result = FALSE; if ( ( TRUE == isTestingActivated() ) && ( value >= 0.0 ) && ( value <= SYRINGE_PUMP_ADC_REF_V ) ) { result = TRUE; syringePumpMeasHome.ovData = value; syringePumpMeasHome.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetSyringePumpMeasuredHomeOverride function resets the override * of the syringe pump measured home analog signal. * @details Inputs: none * @details Outputs: syringePumpMeasHome * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetSyringePumpMeasuredHomeOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; syringePumpMeasHome.override = OVERRIDE_RESET; syringePumpMeasHome.ovData = syringePumpMeasHome.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetSyringePumpMeasuredPositionOverride function overrides the measured * position of the syringe pump. * @details Inputs: none * @details Outputs: syringePumpPosition * @param value override syringe pump measured position with this value (in encoder counts) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetSyringePumpMeasuredPositionOverride( S32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; syringePumpPosition.ovData = value; syringePumpPosition.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetSyringePumpMeasuredPositionOverride function resets the override * of the syringe pump measured position. * @details Inputs: none * @details Outputs: syringePumpPosition * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetSyringePumpMeasuredPositionOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; syringePumpPosition.override = OVERRIDE_RESET; syringePumpPosition.ovData = syringePumpPosition.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetSyringePumpMeasuredVolumeOverride function overrides the measured * rate of the syringe pump measured volume delivered. * @details Inputs: none * @details Outputs: syringePumpVolumeDelivered * @param value override syringe pump measured volume with this value (in mL) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetSyringePumpMeasuredVolumeOverride( F32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; syringePumpVolumeDelivered.ovData = value; syringePumpVolumeDelivered.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetSyringePumpMeasuredVolumeOverride function resets the override * of the syringe pump measured volume delivered. * @details Inputs: none * @details Outputs: syringePumpVolumeDelivered * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetSyringePumpMeasuredVolumeOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; syringePumpVolumeDelivered.override = OVERRIDE_RESET; syringePumpVolumeDelivered.ovData = syringePumpVolumeDelivered.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetHeprinBolusTargetRateOverride function overrides the * heprine bolus target rate. * @details Inputs: heprinBolusTargetRate * @details Outputs: heprinBolusTargetRate * @param: value : override heprinBolusTargetRate (in mL/hour) * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetHeprinBolusTargetRateOverride( F32 value ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { heparinBolusTargetRate.ovInitData = heparinBolusTargetRate.data; heparinBolusTargetRate.ovData = value; heparinBolusTargetRate.override = OVERRIDE_KEY; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testResetHeprinBolusTargetRateOverride function resets the override * of the heprin bolus target rate. * @details Inputs: heprinBolusTargetRate * @details Outputs: heprinBolusTargetRate * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testResetHeprinBolusTargetRateOverride( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { heparinBolusTargetRate.override = OVERRIDE_RESET; heparinBolusTargetRate.ovData = heparinBolusTargetRate.ovInitData; heparinBolusTargetRate.ovInitData = 0.0; result = TRUE; } return result; } /*********************************************************************//** * @brief * The testSetSyringePumpStatus function overrides the syringe pump * status. * @details Inputs: none * @details Outputs: syringePumpStatus * @param status override syringe pump status with this value * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetSyringePumpStatus( U32 status ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; syringePumpStatus.ovData = status; syringePumpStatus.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetSyringePumpStatus function resets the override of the * syringe pump status. * @details Inputs: none * @details Outputs: syringePumpStatus * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetSyringePumpStatus( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; syringePumpStatus.override = OVERRIDE_RESET; syringePumpStatus.ovData = syringePumpStatus.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetSyringePumpEncoderStatus function overrides the syringe * pump encoder status. * @details Inputs: none * @details Outputs: syringePumpEncoderStatus * @param status override syringe pump encoder status with this value * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetSyringePumpEncoderStatus( U32 status ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; syringePumpEncoderStatus.ovData = status; syringePumpEncoderStatus.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetSyringePumpEncoderStatus function resets the override * of the syringe pump encoder status. * @details Inputs: none * @details Outputs: syringePumpEncoderStatus * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetSyringePumpEncoderStatus( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; syringePumpEncoderStatus.override = OVERRIDE_RESET; syringePumpEncoderStatus.ovData = syringePumpEncoderStatus.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetSyringePumpADCandDACStatus function overrides the syringe * pump ADC and DAC status. * @details Inputs: none * @details Outputs: syringePumpADCandDACStatus * @param status override syringe pump ADC and DAC status with this value * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetSyringePumpADCandDACStatus( U32 status ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; syringePumpADCandDACStatus.ovData = status; syringePumpADCandDACStatus.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetSyringePumpADCandDACStatus function resets the override * of the syringe pump ADC and DAC status. * @details Inputs: none * @details Outputs: syringePumpADCandDACStatus * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetSyringePumpADCandDACStatus( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; syringePumpADCandDACStatus.override = OVERRIDE_RESET; syringePumpADCandDACStatus.ovData = syringePumpADCandDACStatus.ovInitData; } return result; } /*********************************************************************//** * @brief * The testSetSyringePumpADCReadCounter function overrides the syringe * pump ADC read counter. * @details Inputs: none * @details Outputs: syringePumpADCReadCtr * @param ctr override syringe pump ADC read counter with this value * @return TRUE if override successful, FALSE if not *************************************************************************/ BOOL testSetSyringePumpADCReadCounter( U32 ctr ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; syringePumpADCReadCtr.ovData = ctr; syringePumpADCReadCtr.override = OVERRIDE_KEY; } return result; } /*********************************************************************//** * @brief * The testResetSyringePumpADCReadCounter function resets the override * of the syringe pump ADC read counter. * @details Inputs: none * @details Outputs: syringePumpADCReadCtr * @return TRUE if reset successful, FALSE if not *************************************************************************/ BOOL testResetSyringePumpADCReadCounter( void ) { BOOL result = FALSE; if ( TRUE == isTestingActivated() ) { result = TRUE; syringePumpADCReadCtr.override = OVERRIDE_RESET; syringePumpADCReadCtr.ovData = syringePumpADCReadCtr.ovInitData; } return result; } /**@}*/