Index: firmware/App/Controllers/SyringePump.c =================================================================== diff -u --- firmware/App/Controllers/SyringePump.c (revision 0) +++ firmware/App/Controllers/SyringePump.c (revision 22293ecac97c3f374f90bb20e3c4cc03fbd27f9d) @@ -0,0 +1,2829 @@ +/************************************************************************** +* +* Copyright (c) 2026-2026 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file SyringePump.c +* +* @author (last) Sean Nash +* @date (last) 05-Apr-2024 +* +* @author (original) Sean Nash +* @date (original) 04-Mar-2021 +* +***************************************************************************/ + +#include // Used for fabs() functions + +#include "AlarmMgmt.h" +#include "BloodFlow.h" +#include "CpldInterface.h" +#include "FPGA.h" +#include "Messaging.h" +#include "ModeTreatment.h" +#include "OperationModes.h" +#include "PersistentAlarm.h" +#include "StateTxDialysis.h" +#include "SyringePump.h" +#include "TaskPriority.h" +#include "Timers.h" +#include "TxParams.h" +#include "Utilities.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 TD_HEPARIN_FORCE_SENSOR_CAL_RECORD_T forceSensorCalRecord; ///< TD 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 \b Inputs: none + * @details \b 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_TD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, 0, SYRINGE_PUMP_DIR_ALARM_PERSISTENCE ); + initPersistentAlarm( ALARM_ID_TD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR, 0, SYRINGE_PUMP_DIR_ALARM_PERSISTENCE ); + initPersistentAlarm( ALARM_ID_TD_SYRINGE_PUMP_RUNNING_WHILE_BP_OFF_ERROR, 0, SYRINGE_PUMP_OFF_ALARM_PERSISTENCE ); + initPersistentAlarm( ALARM_ID_TD_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_TD_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 \b Inputs: none + * @details \b Out\b Outputs:quest 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b Outputs: heparinDeliveryState + * @return none + *************************************************************************/ +void setHeparinCompleted( void ) +{ + heparinDeliveryState = HEPARIN_STATE_COMPLETED; +} + +/*********************************************************************//** + * @brief + * The getHeparinState function returns the current Heparin state. + * @details \b Inputs: heparinDeliveryState + * @details \b Outputs: none + * @return heparinDeliveryState + *************************************************************************/ +HEPARIN_STATE_T getHeparinState( void ) +{ + return heparinDeliveryState; +} + +/*********************************************************************//** + * @brief + * The resetPreLoadStatus function resets the syringe pump pre-load status. + * @details \b Inputs: none + * @details \b Outputs: syringePumpPreLoadCompleted + * @return none + *************************************************************************/ +void resetPreLoadStatus( void ) +{ + syringePumpPreLoadCompleted = FALSE; +} + +/*********************************************************************//** + * @brief + * The stopSyringePump function requests syringe pump be stopped. + * @details \b Inputs: none + * @details \b 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 \b Inputs: syringePumpState + * @details \b 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 \b Inputs: syringePumpState + * @details \b 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 \b Inputs: syringePumpState + * @details \b 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 \b Inputs: syringePumpState + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: syringePumpState + * @details \b 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_TD_SOFTWARE_FAULT, (F32)SW_FAULT_ID_TD_SYRINGE_INVALID_BOLUS_CMD, syringePumpSetRate ) + } + } + + return syringePumpBolusRequested; +} + +/*********************************************************************//** + * @brief + * The startHeparinContinuous function requests Heparin continuous delivery. + * @details \b Inputs: syringePumpState + * @details \b 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_TD_SOFTWARE_FAULT, (F32)SW_FAULT_ID_TD_SYRINGE_INVALID_CONT_CMD, flowRate ) + } + } + + return syringePumpContinuousRequested; +} + +/*********************************************************************//** + * @brief + * The setSyringePumpDACVref function requests to set the DAC Vref setting. + * @details \b Inputs: none + * @details \b Outputs: syringePumpDACVrefSetRequested, syringePumpDACVref + * @return TRUE if request accepted, FALSE if not + *************************************************************************/ +BOOL setSyringePumpDACVref( void ) +{ + F32 vRef = forceSensorCalRecord.tdHeparinForceSensorDACVoltage; + + 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_TD_SOFTWARE_FAULT, (F32)SW_FAULT_ID_TD_SYRINGE_INVALID_VREF, vRef ) + } + + return syringePumpDACVrefSetRequested; +} + +/*********************************************************************//** + * @brief + * The syringeDetectionRequired function sets a flag indicating whether + * the syringe detection is required in current state. + * @details \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b Outputs: + * @return none + *************************************************************************/ +void syringePumpVerifyForceSensorDACCalibration( void ) +{ + F32 DACDeltaV = fabs( forceSensorCalRecord.tdHeparinForceSensorDACVoltage - getSyringePumpForceV() ); + + if ( DACDeltaV > SYRINGE_PUMP_DAC_VOLTAGE_MAX_ERROR ) + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_TD_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 \b Inputs: syringePumpVolumeDelivered + * @details \b 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 \b Inputs: syringePumpMeasRate + * @details \b 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 \b Inputs: syringePumpMeasSyringeDetectionSwitch + * @details \b 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 \b Inputs: syringePumpMeasHome + * @details \b 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 \b Inputs: syringePumpMeasForce + * @details \b 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 \b Inputs: getHeprinBolusTargetRate + * @details \b 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 \b Inputs: syringePumpPosition + * @details \b 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 \b Inputs: syringePumpStatus + * @details \b 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 \b Inputs: syringePumpEncoderStatus + * @details \b 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 \b Inputs: syringePumpADCReadCtr + * @details \b 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 \b Inputs: syringePumpADCandDACStatus + * @details \b 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 \b Inputs: syringePumpMeasSyringeDetectionSwitch + * @details \b 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 \b Inputs: syringePumpMeasHome + * @details \b 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 \b Inputs: syringePumpState + * @details \b 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 \b Inputs: syringePumpPlungerFound + * @details \b 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 \b Inputs: syringeVolumeAdequate + * @details \b 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 \b Inputs: syringePumpPrimeCompleted + * @details \b 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 \b Inputs: syringePumpState + * @details \b 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 \b Inputs: syringePumpPreLoadCompleted + * @details \b 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 \b Inputs: FPGA syringe pump readings + * @details \b 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( TD_HEPARIN_FORCE_SENSOR_CAL_RECORD_T ), + 0, ALARM_ID_TD_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_TD_SYRINGE_PUMP_SYRINGE_REMOVED ); + } + // If no syringe detected and detection is required, trigger alarm condition + else if ( FALSE == isSyringeDetected() ) + { + activateAlarmNoData( ALARM_ID_TD_SYRINGE_PUMP_SYRINGE_REMOVED ); + } + // On transition from detected to not detected - clear syringe detected alarm condition + if ( ( TRUE == prevSyringeDetected ) && ( isSyringeDetected() != TRUE ) ) + { + clearAlarmCondition( ALARM_ID_TD_SYRINGE_DETECTED ); + } + // If syringe detected and alarm active, maintain alarm condition + else if ( ( TRUE == isSyringeDetected() ) && ( TRUE == isAlarmActive( ALARM_ID_TD_SYRINGE_DETECTED ) ) ) + { + activateAlarmNoData( ALARM_ID_TD_SYRINGE_DETECTED ); + } + } + else + { + clearAlarmCondition( ALARM_ID_TD_SYRINGE_PUMP_SYRINGE_REMOVED ); + clearAlarmCondition( ALARM_ID_TD_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_TD_SYRINGE_PUMP_RUNNING_WHILE_BP_OFF_ERROR, runWhileOff ) ) + { + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_TD_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_TD_SYRINGE_PUMP_FAULT, (U32)getSyringePumpStatus() ); + } + } +} + +/*********************************************************************//** + * @brief + * The execSyringePump function executes the syringe pump control state machine. + * @details \b Inputs: syringePumpState + * @details \b 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_TD_SOFTWARE_FAULT, SW_FAULT_ID_TD_SYRINGE_INVALID_STATE, syringePumpState ) + break; + } + + if ( priorSyringeState != syringePumpState ) + { + SEND_EVENT_WITH_2_U32_DATA( TD_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 \b Inputs: none + * @details \b 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( TD_HEPARIN_FORCE_SENSOR_CAL_RECORD_T ), + 0, ALARM_ID_TD_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 \b Inputs: none + * @details \b 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 \b Inputs: syringePumpRetractRequested, syringePumpSeekRequested, + * syringePumpPrimeRequested, syringePumpBolusRequested, syringePumpContinuousRequested, + * syringePumpDACVrefSetRequested,syringePumpStopPositionTimerCounter,syringePumpStopLastPosition + * @details \b 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_TD_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_TD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, FALSE ); + isPersistentAlarmTriggered( ALARM_ID_TD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR, FALSE ); + isPersistentAlarmTriggered( ALARM_ID_TD_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 \b Inputs: syringePumpMeasHome.data + * @details \b 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_TD_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_TD_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 \b Inputs: syringePumpMeasForce.data + * @details \b 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 \b Inputs: syringePumpMeasForce.data + * @details \b 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_TD_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 \b Inputs: syringePumpVolumeDelivered, syringePumpStateStartTime + * @details \b 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_TD_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_TD_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 \b Inputs: set bolus volume, syringePumpVolumeDelivered.data + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: DAC status, syringePumpDACRetryCount + * @details \b 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_TD_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 \b Inputs: syringePumpMeasForce.data, syringePumpPosition.data + * @details \b 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_TD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, ( syringePumpEncoderMeasuredDirection != expDir ) ) ) + { + result = TRUE; + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, (U32)syringePumpEncoderMeasuredDirection, (U32)syringePumpState ); + } + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_TD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR, ( syringePumpControllerMeasuredDirection != expDir ) ) ) + { + result = TRUE; + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_TD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR, (U32)syringePumpControllerMeasuredDirection, (U32)syringePumpState ); + } + } + else + { + isPersistentAlarmTriggered( ALARM_ID_TD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, FALSE ); + isPersistentAlarmTriggered( ALARM_ID_TD_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 \b Inputs: syringePumpMeasForce.data, syringePumpPosition.data + * @details \b 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_TD_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 \b Inputs: syringePumpMeasForce.data, syringePumpPosition.data + * @details \b 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_TD_SYRINGE_PUMP_OCCLUSION, forceAtEndOfSeek, currentForceV ) + result = TRUE; + } + else + { + clearAlarmCondition( ALARM_ID_TD_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_TD_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 \b Inputs: syringePumpMeasSyringeDetectionSwitch.data + * @details \b 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_TD_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 \b Inputs: syringePumpPosition.data + * @details \b 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_TD_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 \b Inputs: syringePumpMeasRate.data, syringePumpSetRate + * @details \b 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_TD_SYRINGE_PUMP_SPEED_ERROR, ( ( error > pctMargin ) && ( delta > SYRINGE_PUMP_MAX_RATE_ERROR_ML_HR ) ) ) ) + { + result = TRUE; + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_TD_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 \b Inputs: syringePumpVolumeDelivered.data, syringePumpSafetyVolumeDelivered + * @details \b 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_TD_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 \b Inputs: syringePumpMeasRate.data, syringePumpStallCtr, syringePumpStallRetryCount + * @details \b 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_TD_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 \b Inputs: syringePumpRampTimerCtr, syringePumpSetToggleTime + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: syringePumpLastPositions[], syringePumpSpeedCalcTimerCounter + * @details \b 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 \b Inputs: syringePumpSetRate + * @details \b 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 \b Inputs: latest syringe pump data, syringePumpDataPublicationTimerCounter + * @details \b 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_TD_SYRINGE_PUMP_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)&data, sizeof( SYRINGE_PUMP_DATA_PAYLOAD_T ) ); + broadcastData( MSG_ID_TD_HEPARIN_DATA, COMM_BUFFER_OUT_CAN_TD_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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: heprinBolusTargetRate + * @details \b 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 \b Inputs: heprinBolusTargetRate + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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 \b Inputs: none + * @details \b 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; +} + +/**@}*/ Index: firmware/App/Controllers/SyringePump.h =================================================================== diff -u --- firmware/App/Controllers/SyringePump.h (revision 0) +++ firmware/App/Controllers/SyringePump.h (revision 22293ecac97c3f374f90bb20e3c4cc03fbd27f9d) @@ -0,0 +1,178 @@ +/************************************************************************** +* +* Copyright (c) 2026-2026 Diality Inc. - All Rights Reserved. +* +* THIS CODE MAY NOT BE COPIED OR REPRODUCED IN ANY FORM, IN PART OR IN +* WHOLE, WITHOUT THE EXPLICIT PERMISSION OF THE COPYRIGHT OWNER. +* +* @file SyringePump.h +* +* @author (last) Dara Navaei +* @date (last) 23-Jan-2024 +* +* @author (original) Sean Nash +* @date (original) 04-Mar-2021 +* +***************************************************************************/ + +#ifndef __SYRINGE_PUMP_H__ +#define __SYRINGE_PUMP_H__ + +#include "TDCommon.h" +#include "TDDefs.h" + +/** + * @defgroup SyringePump SyringePump + * @brief Syringe pump controller/monitor module. Controls the syringe pump + * and monitors status. + * + * @addtogroup SyringePump + * @{ + */ + +// ********** public definitions ********** + +// Begin TODO: Move to NVM +#ifndef _RELEASE_ +#define SW_CONFIG_ENABLE_VALUE 1 ///< Software configuration enable. +#define SW_CONFIG_DISABLE_VALUE 0 ///< Software configuration disable. +#endif +// End TODO: Move to NVM + +#define SYRINGE_PUMP_PRIME_VOLUME_ML 0.353F ///< Target syringe prime volume (in mL). +#define SYRINGE_PUMP_FILL_VOLUME_OFFSET_ML 0.8F ///< Advised fill volume offset due to HW variance. + +/// Enumeration of syringe pump operations. +typedef enum SyringePumpOperations +{ + SYRINGE_PUMP_OP_STOP = 0, ///< Syringe pump stopped - no operation. + SYRINGE_PUMP_OP_RETRACT = 1, ///< Syringe pump retract operation. Retract to home postion. + SYRINGE_PUMP_OP_SEEK = 2, ///< Syringe pump seek operation. Seek syringe plunger. + SYRINGE_PUMP_OP_PRIME = 3, ///< Syringe pump prime operation. Prime Heparin line. + SYRINGE_PUMP_OP_BOLUS = 4, ///< Syringe pump bolus operation. Deliver bolus volume of Heparin. + SYRINGE_PUMP_OP_CONTINUOUS = 5, ///< Syringe pump continuous dispense operation. Deliver Heparin at set rate. + NUM_OF_SYRINGE_PUMP_OPS = 6 ///< Number of syringe pump operation. +} SYRINGE_PUMP_OPERATION_T; + +/// Payload record structure for the syringe pump operation request message. +typedef struct +{ + U32 syringePumpOp; ///< Syringe pump operation. + F32 rate; ///< Syringe pump rate. + F32 volume; ///< Syringe pump volume. +} SYRINGE_PUMP_OP_PAYLOAD_T; + +/// Payload record structure for the syringe pump data message. +typedef struct +{ + U32 syringePumpState; ///< Syringe pump state. + U32 heparinDeliveryState; ///< Heparin delivery state. + F32 syringePumpSetRate; ///< Syringe pump set rate. + F32 syringePumpMeasRate; ///< Syringe pump measured rate. + S32 syringePumpPosition; ///< Syringe pump position. + F32 syringePumpVolumeDelivered; ///< Syringe pump volume delivered. + F32 syringePumpMeasHome; ///< Syringe pump measured home. + F32 syringePumpMeasSwitch; ///< Syringe pump measured switch. + F32 syringePumpMeasForce; ///< Syringe pump measured force. + F32 syringePumpSafetyVolume; ///< Syringe pump safety volume. + U32 syringePumpStatus; ///< Syringe pump status. +} SYRINGE_PUMP_DATA_PAYLOAD_T; + +/// Heparin publish data structure +typedef struct +{ + F32 syringePumpVolumeDelivered; ///< Syringe pump volume delivered. + F32 syringePumpVolumeRequired; ///< Syringe pump volume required for therapy. +} SYRINGE_PUMP_VOLUME_DATA_T; + +// Begin TODO: Move to NVM +/// TD heparin force sensor calibration record +typedef struct +{ + F32 tdHeparinForceSensorDACVoltage; ///< TD heparin force sensor calibration data. + U32 calibrationTime; ///< Calibration time. + U16 crc; ///< CRC for the TD heparin pump calibration payload. +} TD_HEPARIN_FORCE_SENSOR_CAL_RECORD_T; + +/// TD available NV data to get +typedef enum td_nv_commands +{ + GET_CAL_HEPARIN_FORCE_SENSOR, ///< Get heparin force sensor calibration data. + NUM_OF_NV_TD_DATA ///< Number of non-volatile data. +} NV_DATA_T; + +#ifndef _RELEASE_ +/// Software configuration enums +typedef enum software_configurations +{ + SW_CONFIG_DISABLE_SYRINGE_PUMP_ALARMS, ///< Software configuration disable syringe pump alarms. + SW_CONFIG_DISABLE_SYRINGE_PUMP, ///< Software configuration disable syringe pump. + NUM_OF_SW_CONFIGS ///< Number of software configurations. +} SOFTWARE_CONFIG_T; +#endif +// End TODO: Move to NVM + +// ********** public function prototypes ********** + +void initSyringePump( void ); +void execSyringePump( void ); + +SELF_TEST_STATUS_T execSyringePumpSelfTest( void ); + +BOOL userHeparinRequest( HEPARIN_CMD_T cmd ); +void setHeparinOff( void ); +void setHeparinStopped( void ); +void setHeparinCompleted( void ); +HEPARIN_STATE_T getHeparinState( void ); +void resetPreLoadStatus( void ); +void stopSyringePump( void ); +BOOL retractSyringePump( void ); +BOOL preloadSyringePlunger( void ); +BOOL seekSyringePlunger( void ); +BOOL primeSyringePump( void ); +BOOL checkForSyringeOcclusion( BOOL stopPump ); +BOOL startHeparinBolus( void ); +BOOL startHeparinContinuous( void ); +BOOL setSyringePumpDACVref( void ); +void syringeDetectionRequired( BOOL req ); +void syringePumpVerifyForceSensorDACCalibration( void ); + +BOOL isSyringeDetected( void ); +BOOL isSyringePumpHome( void ); +BOOL isSyringePumpStopped( void ); +BOOL isSyringePlungerFound( void ); +BOOL isSyringeVolumeAdequate( void ); +BOOL isSyringePumpPrimed( void ); +BOOL isSyringePumpRunning( void ); +BOOL isSyringePumpPreLoaded( void ); +F32 getSyringePumpVolumeDelivered( void ); + +BOOL testSetSyringePumpDataPublishIntervalOverride( U32 value ); +BOOL testResetSyringePumpDataPublishIntervalOverride( void ); +BOOL testSyringePumpOperationRequest( SYRINGE_PUMP_OP_PAYLOAD_T opParams ); +BOOL testSetSyringePumpMeasuredRateOverride( F32 value ); +BOOL testResetSyringePumpMeasuredRateOverride( void ); +BOOL testSetHeprinBolusTargetRateOverride( F32 value ); +BOOL testResetHeprinBolusTargetRateOverride( void ); +BOOL testSetSyringePumpMeasuredForceOverride( F32 value ); +BOOL testResetSyringePumpMeasuredForceOverride( void ); +BOOL testSetSyringePumpMeasuredSyringeDetectOverride( F32 value ); +BOOL testResetSyringePumpMeasuredSyringeDetectOverride( void ); +BOOL testSetSyringePumpMeasuredHomeOverride( F32 value ); +BOOL testResetSyringePumpMeasuredHomeOverride( void ); +BOOL testSetSyringePumpMeasuredPositionOverride( S32 value ); +BOOL testResetSyringePumpMeasuredPositionOverride( void ); +BOOL testSetSyringePumpMeasuredVolumeOverride( F32 value ); +BOOL testResetSyringePumpMeasuredVolumeOverride( void ); +BOOL testSetSyringePumpStatus( U32 status ); +BOOL testResetSyringePumpStatus( void ); +BOOL testSetSyringePumpEncoderStatus( U32 status ); +BOOL testResetSyringePumpEncoderStatus( void ); +BOOL testSetSyringePumpADCandDACStatus( U32 status ); +BOOL testResetSyringePumpADCandDACStatus( void ); +BOOL testSetSyringePumpADCReadCounter( U32 ctr ); +BOOL testResetSyringePumpADCReadCounter( void ); + +/**@}*/ + +#endif Index: firmware/App/Services/AlarmMgmtSWFaults.h =================================================================== diff -u -r6f02ff4686ec9dfc60247e9ed3fc9c5cc7771543 -r22293ecac97c3f374f90bb20e3c4cc03fbd27f9d --- firmware/App/Services/AlarmMgmtSWFaults.h (.../AlarmMgmtSWFaults.h) (revision 6f02ff4686ec9dfc60247e9ed3fc9c5cc7771543) +++ firmware/App/Services/AlarmMgmtSWFaults.h (.../AlarmMgmtSWFaults.h) (revision 22293ecac97c3f374f90bb20e3c4cc03fbd27f9d) @@ -183,6 +183,10 @@ SW_FAULT_ID_TD_VALVES_INVALID_FIND_DEENERGIZED_EDGE = 152, SW_FAULT_ID_BLOOD_PRIME_INVALID_STATE = 153, SW_FAULT_ID_MODE_PRE_TREATMENT_INVALID_STATE = 148, + SW_FAULT_ID_TD_SYRINGE_INVALID_BOLUS_CMD = 149, + SW_FAULT_ID_TD_SYRINGE_INVALID_CONT_CMD = 150, + SW_FAULT_ID_TD_SYRINGE_INVALID_STATE = 151, + SW_FAULT_ID_TD_SYRINGE_INVALID_VREF = 152, NUM_OF_SW_FAULT_IDS } SW_FAULT_ID_T;