Index: firmware/App/Controllers/SyringePump.c =================================================================== diff -u -ra67030b162e759b0c9e350344112bbf26af794be -re482c9636c0e359386156d21a5518d59e727eb39 --- firmware/App/Controllers/SyringePump.c (.../SyringePump.c) (revision a67030b162e759b0c9e350344112bbf26af794be) +++ firmware/App/Controllers/SyringePump.c (.../SyringePump.c) (revision e482c9636c0e359386156d21a5518d59e727eb39) @@ -7,8 +7,8 @@ * * @file SyringePump.c * -* @author (last) Dara Navaei -* @date (last) 15-Jul-2022 +* @author (last) Darren Cox +* @date (last) 08-Nov-2022 * * @author (original) Sean Nash * @date (original) 04-Mar-2021 @@ -68,7 +68,6 @@ #define SYRINGE_PUMP_RATE_ALARM_PERSISTENCE 3000 ///< 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_ADC_READ_PERSISTENCE 100 ///< Syringe pump ADC stale read alarm persistence time (in ms). #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. @@ -83,6 +82,8 @@ #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_PRIME_VOLUME_ML 0.353F ///< Target syringe prime volume (in mL). +#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. @@ -154,12 +155,16 @@ #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. /// 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 @@ -206,8 +211,10 @@ static U32 syringePumpSpeedCalcTimerCounter; ///< Used to calculate measured rate from change in position over time. 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. @@ -218,16 +225,18 @@ 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 BOOL syringePumpDACVrefWriteInProgress; ///< Flag indicates DAC Vref write is in progress. static F32 syringePumpDACVref; ///< DAC Vref setting for force sensor. -static U08 lastSyringePumpADCReadCtr; ///< Remember last ADC read counter to check new reads are fresh. - static U32 syringePumpStallCtr; ///< Counts time when position is not changing during ramp. static U32 syringePumpStallRetryCount; ///< Counts pump ramp up stall retries. static HD_HEPARIN_FORCE_SENSOR_CAL_RECORD_T forceSensorCalRecord; ///< HD heparin force sensor calibration record. +static U32 syringePumpDACRetryCount; +static U32 syringePumpDACRetryTimer; + // ********** private function prototypes ********** static void resetSyringePumpRequestFlags( void ); @@ -247,6 +256,7 @@ 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 ); @@ -285,6 +295,7 @@ syringePumpVolumeStartPosition = 0; syringePumpHomePositionOffset = 0; syringePumpLastPosition = 0; + syringePumpVolumeRequired = 0.0; #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) @@ -303,10 +314,9 @@ syringePumpPlungerFound = FALSE; syringeVolumeAdequate = FALSE; syringePumpPrimeCompleted = FALSE; + syringePumpPreLoadCompleted = FALSE; syringePumpRampUpPct = 0.0; - lastSyringePumpADCReadCtr = 0; - // Zero pump position counts buffer syringePumpMotorSpeedCalcIdx = 0; for ( i = 0; i < SYRINGE_PUMP_SPEED_CALC_BUFFER_LEN; i++ ) @@ -316,15 +326,19 @@ syringePumpStallCtr = 0; syringePumpStallRetryCount = 0; + syringePumpDACRetryCount = 0; + syringePumpDACRetryTimer = 0; // Initialize persistent alarms - initPersistentAlarm( ALARM_ID_HD_SYRINGE_PUMP_ADC_ERROR, 0, SYRINGE_PUMP_ADC_READ_PERSISTENCE ); initPersistentAlarm( ALARM_ID_HD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, 0, SYRINGE_PUMP_DIR_ALARM_PERSISTENCE ); initPersistentAlarm( ALARM_ID_HD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR, 0, SYRINGE_PUMP_DIR_ALARM_PERSISTENCE ); initPersistentAlarm( ALARM_ID_HD_SYRINGE_PUMP_RUNNING_WHILE_BP_OFF_ERROR, 0, SYRINGE_PUMP_OFF_ALARM_PERSISTENCE ); initPersistentAlarm( ALARM_ID_HD_SYRINGE_PUMP_NOT_STOPPED_ERROR, 0, SYRINGE_PUMP_OFF_ALARM_PERSISTENCE ); initPersistentAlarm( ALARM_ID_HD_SYRINGE_PUMP_SPEED_ERROR, 0, SYRINGE_PUMP_RATE_ALARM_PERSISTENCE ); + initFPGAPersistentAlarm( FPGA_PERS_ERROR_SYRINGE_PUMP_ADC, ALARM_ID_HD_SYRINGE_PUMP_FPGA_ADC_FAULT, + SYRINGE_PUMP_ADC_FPGA_ERROR_TIMEOUT_MS, SYRINGE_PUMP_ADC_FPGA_ERROR_TIMEOUT_MS ); + // Reset request flags resetSyringePumpRequestFlags(); } @@ -340,6 +354,7 @@ { syringePumpRetractRequested = FALSE; syringePumpSeekRequested = FALSE; + syringePumpPreloadRequested = FALSE; syringePumpPrimeRequested = FALSE; syringePumpBolusRequested = FALSE; syringePumpContinuousRequested = FALSE; @@ -466,7 +481,7 @@ * @brief * The stopSyringePump function requests syringe pump be stopped. * @details Inputs: none - * @details Outputs: syringePumpSetRate, syringePumpRetractRequested, syringePumpState + * @details Outputs: syringePumpSetRate, syringePumpState, heparinDeliveryState * @return TRUE if request accepted, FALSE if not *************************************************************************/ void stopSyringePump( void ) @@ -484,7 +499,7 @@ * @brief * The retractSyringePump function requests retract operation. * @details Inputs: syringePumpState - * @details Outputs: syringePumpSetRate, syringePumpRetractRequested + * @details Outputs: syringePumpSetRate, syringePumpRetractRequested, heparinDeliveryState * @return TRUE if request accepted, FALSE if not *************************************************************************/ BOOL retractSyringePump( void ) @@ -501,6 +516,29 @@ /*********************************************************************//** * @brief + * The preloadSyringePlunger function requests plunger preload operation. + * @details Inputs: syringePumpState + * @details Outputs: syringePumpSetRate, syringePumpPreloadRequested + * @return TRUE if request accepted, FALSE if not + *************************************************************************/ +BOOL preloadSyringePlunger( void ) +{ + if ( MODE_SERV == getCurrentOperationMode() ) // Allow syringe pump operations in Service Mode. + { + heparinDeliveryState = HEPARIN_STATE_STOPPED; + } + if ( ( SYRINGE_PUMP_OFF_STATE == syringePumpState ) && + ( TRUE == syringePumpPositionKnown ) && ( heparinDeliveryState != HEPARIN_STATE_OFF ) ) + { + syringePumpSetRate = SYRINGE_PUMP_SEEK_RATE; + syringePumpPreloadRequested = TRUE; + } + + return syringePumpPreloadRequested; +} + +/*********************************************************************//** + * @brief * The seekSyringePlunger function requests plunger seek operation. * @details Inputs: syringePumpState * @details Outputs: syringePumpSetRate, syringePumpSeekRequested @@ -956,6 +994,19 @@ /*********************************************************************//** * @brief + * The isSyringePumpPreLoaded function determines whether the syringe pump + * is has been moved to preload position. + * @details Inputs: syringePumpPreLoadCompleted + * @details Outputs: none + * @return TRUE if syringe pump operation is in preload position, FALSE if not. + *************************************************************************/ +BOOL isSyringePumpPreLoaded( void ) +{ + return syringePumpPreLoadCompleted; +} + +/*********************************************************************//** + * @brief * The execSyringePumpMonitor function executes the syringe pump monitor. * @details Inputs: FPGA syringe pump readings * @details Outputs: Alarm(s) may be triggered @@ -1069,22 +1120,15 @@ } } - if ( syringePumpDACVrefWriteInProgress != TRUE ) + if ( ( FALSE == syringePumpDACVrefWriteInProgress ) && ( getCurrentOperationMode() > MODE_INIT ) ) { - // Check ADC read is fresh (takes FPGA a while to configure ADC so don't check until after init/POST mode - if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_ADC_ERROR, - ( ( getCurrentOperationMode() > MODE_INIT ) && ( lastSyringePumpADCReadCtr == getSyringePumpADCReadCounter() ) ) ) ) - { #ifndef _RELEASE_ - if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) #endif - { - activateAlarmNoData( ALARM_ID_HD_SYRINGE_PUMP_ADC_ERROR ); - } + { + checkFPGAPersistentAlarms( FPGA_PERS_ERROR_SYRINGE_PUMP_ADC, getSyringePumpADCandDACStatus(), getSyringePumpADCReadCounter() ); } - lastSyringePumpADCReadCtr = getSyringePumpADCReadCounter(); - // Check pump status if ( getSyringePumpStatus() != 0 ) { @@ -1111,6 +1155,11 @@ isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_NOT_STOPPED_ERROR, FALSE ); } + if ( syringePumpDACRetryCount > 0 ) + { + syringePumpDACRetryTimer++; + } + // Execute syringe pump control state machine switch ( syringePumpState ) { @@ -1126,6 +1175,10 @@ syringePumpState = handleSyringePumpRetractState(); break; + case SYRINGE_PUMP_PRELOAD_STATE: + syringePumpState = handleSyringePumpPreLoadState(); + break; + case SYRINGE_PUMP_SEEK_STATE: syringePumpState = handleSyringePumpSeekState(); break; @@ -1249,6 +1302,10 @@ { result = SYRINGE_PUMP_RETRACT_STATE; } + else if ( TRUE == syringePumpPreloadRequested ) + { + result = SYRINGE_PUMP_PRELOAD_STATE; + } else if ( TRUE == syringePumpSeekRequested ) { result = SYRINGE_PUMP_SEEK_STATE; @@ -1269,7 +1326,8 @@ sendTreatmentLogEventData( HEPARIN_START_RESUME_EVENT, 0.0, syringePumpSetRate ); result = SYRINGE_PUMP_HEP_CONTINUOUS_STATE; } - else if ( TRUE == syringePumpDACVrefSetRequested ) + 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 ); @@ -1338,6 +1396,8 @@ syringePumpPlungerFound = FALSE; syringeVolumeAdequate = FALSE; syringePumpPrimeCompleted = FALSE; + syringePumpPreLoadCompleted = FALSE; + syringePumpVolumeRequired = 0.0F; // Clear insufficient volume alarm condition in case we're retracting to allow user to resolve alarm clearAlarmCondition( ALARM_ID_HD_SYRINGE_PUMP_NOT_ENOUGH_HEPARIN_ALARM ); } @@ -1364,6 +1424,69 @@ /*********************************************************************//** * @brief + * The handleSyringePumpPreLoadState function handles the PreLoad plunger state + * of the syringe pump control state machine. Move to position that limits + * loadable Heparin volume to amount needed for treatment. + * @details Inputs: syringePumpMeasForce.data + * @details Outputs: syringePumpVolumeRequired, Syringe pump moved to preload position, alarm conditions checked + * @return next state + *************************************************************************/ +static SYRINGE_PUMP_STATE_T handleSyringePumpPreLoadState( void ) +{ + SYRINGE_PUMP_STATE_T result = SYRINGE_PUMP_PRELOAD_STATE; + BOOL stopPump = FALSE; + S32 pos = getSyringePumpPosition(); + F32 bolusVol = getTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_BOLUS_VOLUME ); + F32 contRate = getTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_DISPENSE_RATE ); + U32 preStop = getTreatmentParameterU32( TREATMENT_PARAM_HEPARIN_PRE_STOP_TIME ); + U32 setTxDur = getTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_DURATION ); + F32 hepDurHr = ( (F32)( setTxDur - preStop ) / (F32)MIN_PER_HOUR ); + F32 txVolumeReq = SYRINGE_PUMP_PRIME_VOLUME_ML + bolusVol + ( hepDurHr * contRate ); + F32 syringeVol = ( SYRINGE_PUMP_EMPTY_POS - (F32)pos ) / SYRINGE_ENCODER_COUNTS_PER_ML; + + syringePumpVolumeRequired = txVolumeReq; + txVolumeReq = txVolumeReq + SYRINGE_PUMP_PRELOAD_MARGIN_VOLUME_ML; + + // Is syringe loaded? + if ( TRUE == isSyringeDetected() ) + { + activateAlarmNoData( ALARM_ID_HD_SYRINGE_DETECTED ); + stopPump = TRUE; + } + else + { + // 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 max position > empty + 0.5 mL + stopPump = checkMaxTravel( stopPump, SYRINGE_PUMP_EMPTY_POS + SYRINGE_PUMP_EMPTY_POS_MARGIN ); + + // Check pump direction + stopPump = checkDirection( stopPump, MOTOR_DIR_FORWARD ); + + // If anything found that would require stopping the pump, stop pump and go to off state + if ( TRUE == stopPump ) + { + stopSyringePump(); + result = SYRINGE_PUMP_OFF_STATE; + } + + return result; +} + +/*********************************************************************//** + * @brief * The handleSyringePumpSeekState function handles the seek plunger state * of the syringe pump control state machine. * @details Inputs: syringePumpMeasForce.data @@ -1393,6 +1516,7 @@ syringePumpVolumeDelivered.data = 0.0; syringePumpSafetyVolumeDelivered = 0.0; syringePumpVolumeStartPosition = pos; + syringePumpPreLoadCompleted = FALSE; // Check estimated syringe volume needed for treatment vs. volume detected - if insufficient for treatment needs, alarm if ( syringeVol >= txVolume ) @@ -1454,6 +1578,7 @@ syringePumpVolumeDelivered.data = 0.0; syringePumpSafetyVolumeDelivered = 0.0; syringePumpVolumeStartPosition = syringePumpPosition.data; + syringePumpPreLoadCompleted = FALSE; } // Has syringe been removed? @@ -1576,28 +1701,46 @@ * The handleSyringePumpCalibrateForceSensorState function handles the * calibrate force sensor state of the syringe pump control state machine. * of the syringe pump control state machine. - * @details Inputs: DAC status + * @details Inputs: DAC status, syringePumpDACRetryCount * @details Outputs: syringePumpDACVrefWriteInProgress, ADC read mode restored + * syringePumpDACRetryCount, syringePumpDACRetryTimer * @return next state *************************************************************************/ static SYRINGE_PUMP_STATE_T handleSyringePumpCalibrateForceSensorState( void ) { SYRINGE_PUMP_STATE_T result = SYRINGE_PUMP_CONFIG_FORCE_SENSOR_STATE; U08 adcDACStatus = getSyringePumpADCandDACStatus(); - // Wait for DAC setting write to EEPROM to complete - if ( ( adcDACStatus & SYRINGE_PUMP_ADC_DAC_ERROR_COUNT_DAC_WR_DONE ) != 0 ) + // Check DAC write error + if ( ( adcDACStatus & SYRINGE_PUMP_DAC_WRITE_ERROR_BIT ) != 0 ) { + syringePumpDACRetryCount++; + + if ( syringePumpDACRetryCount > SYRINGE_PUMP_DAC_MAX_RETRIES ) + { + syringePumpDACRetryCount = 0; + activateAlarmNoData( ALARM_ID_HD_SYRINGE_PUMP_DAC_WRITE_ERROR ); + } + syringePumpDACVrefWriteInProgress = FALSE; // Switch back from DAC to ADC control setFPGASyringePumpADCandDACControlFlags( SYRINGE_PUMP_ADC_DAC_CONTROL_RD_DAC_ON_ADC | SYRINGE_PUMP_ADC_DAC_CONTROL_ENABLE_ADC ); // Back to off state result = SYRINGE_PUMP_OFF_STATE; } - // Check DAC write error - else if ( ( adcDACStatus & SYRINGE_PUMP_DAC_WRITE_ERROR_BIT ) != 0 ) + + // Wait for DAC setting write to EEPROM to complete + else if ( ( adcDACStatus & SYRINGE_PUMP_ADC_DAC_ERROR_COUNT_DAC_WR_DONE ) != 0 ) { - activateAlarmNoData( ALARM_ID_HD_SYRINGE_PUMP_DAC_WRITE_ERROR ); + // 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; @@ -1989,6 +2132,7 @@ ( (U32)getSyringePumpADCReadCounter() ); volData.syringePumpVolumeDelivered = getSyringePumpVolumeDelivered(); + volData.syringePumpVolumeRequired = syringePumpVolumeRequired; broadcastData( MSG_ID_HD_SYRINGE_PUMP_DATA, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&data, sizeof( SYRINGE_PUMP_DATA_PAYLOAD_T ) ); broadcastData( MSG_ID_HD_HEPARIN_DATA_BROADCAST, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&volData, sizeof( SYRINGE_PUMP_VOLUME_DATA_T ) );