Index: firmware/App/Controllers/SyringePump.c =================================================================== diff -u -rf499e4d718aaf99432e4ed4300a0b48e402feb60 -rfaaa80bfef7fdcfea8c03163db14c83300d64a23 --- firmware/App/Controllers/SyringePump.c (.../SyringePump.c) (revision f499e4d718aaf99432e4ed4300a0b48e402feb60) +++ firmware/App/Controllers/SyringePump.c (.../SyringePump.c) (revision faaa80bfef7fdcfea8c03163db14c83300d64a23) @@ -67,9 +67,9 @@ #define SYRINGE_PUMP_PRIME_RATE 635.0 ///< 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.0 ///< Maximum rate of the syringe pump (in mL/hr). -#define SYRINGE_PUMP_RATE_ALARM_PERSISTENCE ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Alarm persistence period for syringe pump speed check alarms. +#define SYRINGE_PUMP_RATE_ALARM_PERSISTENCE ( MS_PER_SECOND * 3 / TASK_PRIORITY_INTERVAL ) ///< Alarm persistence period for syringe pump speed check alarms. #define SYRINGE_PUMP_DIR_ALARM_PERSISTENCE ( MS_PER_SECOND / TASK_PRIORITY_INTERVAL ) ///< Alarm persistence period for syringe pump direction check alarms. -#define SYRINGE_PUMP_OFF_ALARM_PERSISTENCE ( 1100 / TASK_PRIORITY_INTERVAL ) ///< Alarm persistence period for syringe pump off check alarms. +#define SYRINGE_PUMP_OFF_ALARM_PERSISTENCE ( 500 / TASK_PRIORITY_INTERVAL ) ///< Alarm persistence period for syringe pump off check alarms. #define SYRINGE_PUMP_OCCLUSION_ALARM_PERSISTENCE ( 30 / TASK_PRIORITY_INTERVAL ) ///< Alarm persistence period for syringe pump occlusion alarms. #define SYRINGE_PUMP_ADC_READ_PERSISTENCE ( 100 / TASK_PRIORITY_INTERVAL ) ///< Syringe pump ADC stale read alarm persistence time (100 ms). @@ -87,12 +87,11 @@ #define SYRINGE_PUMP_PRIME_VOLUME_ML 0.353 ///< Target syringe prime volume (in mL). #define SYRINGE_PUMP_MAX_VOL_ERROR_ML 0.1 ///< Maximum Heparin volume error (in mL). #define SYRINGE_PUMP_MAX_RATE_ERROR_ML_HR 0.1 ///< Maximum Heparin delivery rate error (in mL/hr). -#define SYRINGE_PUMP_MAX_RATE_WHILE_OFF_ML_HR 0.001 ///< Maximum Heparin rate measured while in off state (in mL/hr). #define TEN_PCT_OVER_ALLOWANCE 1.1 ///< Allow 10 percent over target before alarming on over travel. #define FIVE_PCT_OVER_ALLOWANCE 1.05 ///< Allow 5 percent over target before alarming on over travel. /// Expected position of empty in relation to home postion. -#define SYRINGE_PUMP_EMPTY_POS ( SYRINGE_ENCODER_COUNTS_PER_ML * 11.0 ) +#define SYRINGE_PUMP_EMPTY_POS ( SYRINGE_ENCODER_COUNTS_PER_ML * 11.0 ) // TODO - get syringe volume from home to empty (11 mL is placeholder) /// Margin of error for empty position determination. #define SYRINGE_PUMP_EMPTY_POS_MARGIN ( SYRINGE_ENCODER_COUNTS_PER_ML * 0.5 ) /// Minimum retract position. @@ -181,6 +180,7 @@ 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 syringePumpPosition1SecAgo; ///< Position recorded at last 1 Hz speed check. 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. @@ -194,6 +194,7 @@ 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 syringePumpDACVrefWriteInProgress; ///< Flag indicates DAC Vref write is in progress. @@ -230,6 +231,7 @@ static void calcStepperToggleTimeForTargetRate( F32 rate ); static void calcMeasRate( void ); static void calcSafetyVolumeDelivered( void ); +static BOOL checkDirection( BOOL stopPump, MOTOR_DIR_T expDir ); static BOOL checkOcclusionOrEmpty( BOOL stopPump ); static BOOL checkSyringeRemoved( BOOL stopPump ); static BOOL checkMaxTravel( BOOL stopPump, S32 maxPos ); @@ -254,6 +256,7 @@ syringePumpSafetyVolumeDelivered = 0.0; syringePumpVolumeStartPosition = 0; syringePumpHomePositionOffset = 0; + syringePumpLastPosition = 0; syringePumpPosition1SecAgo = 0; syringePumpControllerMeasuredDirection = MOTOR_DIR_FORWARD; syringePumpEncoderMeasuredDirection = MOTOR_DIR_FORWARD; @@ -264,6 +267,7 @@ syringePumpPositionKnown = FALSE; syringePumpPlungerFound = FALSE; + syringeVolumeAdequate = FALSE; syringePumpPrimeCompleted = FALSE; lastSyringePumpADCReadCtr = 0; @@ -283,7 +287,7 @@ * @brief * The resetSyringePumpRequestFlags function resets request flags. * @details Inputs: none - * @details Outputs: syringePumpSetRate, syringePumpRetractRequested + * @details Outputs: request flags reset * @return TRUE if request accepted, FALSE if not *************************************************************************/ static void resetSyringePumpRequestFlags( void ) @@ -793,6 +797,20 @@ /*********************************************************************//** * @brief + * The isSyringeVolumeAdequate function determines whether the syringe has + * sufficient Heparin volume to complete the treatment per user settings. + * Call this function after seek operation. + * @details Inputs: syringeVolumeAdequate + * @details Outputs: none + * @return TRUE if syringe volume is sufficient to complete the treatment, FALSE if not. + *************************************************************************/ +BOOL isSyringeVolumeAdequate( void ) +{ + return syringeVolumeAdequate; +} + +/*********************************************************************//** + * @brief * The isSyringePumpPrimed function determines whether the syringe pump * prime operation was completed. * @details Inputs: syringePumpPrimeCompleted @@ -801,7 +819,7 @@ *************************************************************************/ BOOL isSyringePumpPrimed( void ) { - return syringePumpPrimeCompleted;; + return syringePumpPrimeCompleted; } /*********************************************************************//** @@ -823,6 +841,7 @@ syringePumpMeasSyringeDetectionSwitch.data = ( (F32)getFPGASyringePumpADCChannel1() * SYRINGE_PUMP_ADC_REF_V ) / SYRINGE_PUMP_ADC_FULL_SCALE_BITS; syringePumpMeasForce.data = ( (F32)getFPGASyringePumpADCChannel0() * SYRINGE_PUMP_ADC_REF_V ) / SYRINGE_PUMP_ADC_FULL_SCALE_BITS; // Apply home offset to encoder position + syringePumpLastPosition = getSyringePumpPosition(); syringePumpPosition.data = encPosition - syringePumpHomePositionOffset; // Calculate volume delivered from position syringePumpVolumeDelivered.data = (F32)(syringePumpPosition.data - syringePumpVolumeStartPosition) / SYRINGE_ENCODER_COUNTS_PER_ML; @@ -831,10 +850,22 @@ calcMeasRate(); // Get measured direction syringePumpControllerMeasuredDirection = ( ( encStatus & SYRINGE_PUMP_ENCODER_DIRECTION_BIT ) != 0 ? MOTOR_DIR_REVERSE : MOTOR_DIR_FORWARD ); - // TODO - calc direction from encoder pos relative to last - // syringePumpEncoderMeasuredDirection = TBD; + // Calculate direction from encoder position relative to last + syringePumpEncoderMeasuredDirection = ( getSyringePumpPosition() - syringePumpLastPosition >= 0 ? MOTOR_DIR_FORWARD : MOTOR_DIR_REVERSE ); - // TODO - check if syringe pump is on while BP is off (w/ a little persistence). + // Check if syringe pump is on while BP is off + if ( ( ( SYRINGE_PUMP_HEP_BOLUS_STATE == syringePumpState ) || ( SYRINGE_PUMP_HEP_CONTINUOUS_STATE == syringePumpState ) ) && + ( isBloodPumpRunning() != TRUE ) ) + { + if ( ++syringePumpRunningWhileBPOffErrorCtr > SYRINGE_PUMP_OFF_ALARM_PERSISTENCE ) + { + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_HD_SYRINGE_PUMP_RUNNING_WHILE_BP_OFF_ERROR, (U32)syringePumpState ); + } + } + else + { + syringePumpRunningWhileBPOffErrorCtr = 0; + } if ( syringePumpDACVrefWriteInProgress != TRUE ) { @@ -854,12 +885,6 @@ #endif lastSyringePumpADCReadCtr = adcReadCtr; - // Check encoder direction error. // TODO check direction in states - if ( ( encStatus & SYRINGE_PUMP_ENCODER_DIRECTION_ERROR_BITS ) != 0) - { - // TODO - alarm? - } - // Check pump status if ( pmpStatus != 0 ) { @@ -961,7 +986,7 @@ SYRINGE_PUMP_STATE_T result = SYRINGE_PUMP_OFF_STATE; // Check position is not changing while stopped - if ( getSyringePumpMeasRate() > SYRINGE_PUMP_MAX_RATE_WHILE_OFF_ML_HR ) // TODO - add persistence + if ( syringePumpLastPosition != getSyringePumpPosition() ) { if ( ++syringePumpRunningWhileOffErrorCtr > SYRINGE_PUMP_OFF_ALARM_PERSISTENCE ) { @@ -1042,14 +1067,15 @@ 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() ) { - stopSyringePump(); + stopPump = TRUE; // Set position to zero after retract reaches home position syringePumpHomePositionOffset = getFPGASyringePumpEncoderPosition(); syringePumpPosition.data = 0; @@ -1060,25 +1086,27 @@ syringePumpSafetyVolumeDelivered = 0.0; // Reset flags syringePumpPlungerFound = FALSE; + syringeVolumeAdequate = FALSE; syringePumpPrimeCompleted = FALSE; - result = SYRINGE_PUMP_OFF_STATE; + // 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 ); } - else if ( syringePumpControllerMeasuredDirection != MOTOR_DIR_REVERSE ) - { - // TODO - alarm w/ some persistence -// SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR, (U32)syringePumpControllerMeasuredDirection, (U32)SYRINGE_PUMP_RETRACT_STATE ); - } - else if ( syringePumpEncoderMeasuredDirection != MOTOR_DIR_REVERSE ) - { - // TODO - alarm w/ some persistence -// SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, (U32)syringePumpEncoderMeasuredDirection, (U32)SYRINGE_PUMP_RETRACT_STATE ); - } + // If position known from prior retract, ensure we don't retract beyond minimum position else if ( ( TRUE == syringePumpPositionKnown ) && ( getSyringePumpPosition() < SYRINGE_PUMP_RETRACT_POS_MIN ) ) { + stopPump = TRUE; SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SYRINGE_PUMP_OVER_TRAVEL_ERROR, (U32)getSyringePumpPosition(), (U32)SYRINGE_PUMP_RETRACT_STATE ); } - // TODO - if position known from prior retract, ensure we don't go lower than -TBD position + 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; } @@ -1101,11 +1129,30 @@ // Is plunger contact detected? if ( getSyringePumpForceV() >= SYRINGE_FORCE_PLUNGER_THRESHOLD_V ) { + 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 ) - HEPARIN_BOLUS_TIME_HR; + F32 txVolume = bolusVol + ( hepDurHr * contRate ); + F32 syringeVol = ( SYRINGE_PUMP_EMPTY_POS - (F32)pos ) / SYRINGE_MICRO_STEPS_PER_ML; + stopPump = TRUE; syringePumpPlungerFound = TRUE; syringePumpVolumeDelivered.data = 0.0; syringePumpSafetyVolumeDelivered = 0.0; - syringePumpVolumeStartPosition = syringePumpPosition.data; + syringePumpVolumeStartPosition = pos; + + // Check estimated syringe volume needed for treatment vs. volume detected - if insufficient for treatment needs, alarm + if ( syringeVol >= txVolume ) + { + syringeVolumeAdequate = TRUE; + } + else + { + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_SYRINGE_PUMP_NOT_ENOUGH_HEPARIN_ALARM, syringeVol, txVolume ); + } } // Has syringe been removed? @@ -1117,7 +1164,8 @@ // Check max position > empty + 0.5 mL stopPump = checkMaxTravel( stopPump, SYRINGE_PUMP_EMPTY_POS + SYRINGE_PUMP_EMPTY_POS_MARGIN ); - // TODO - calc estimate syringe volume - if insufficient for treatment needs, alarm + // 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 ) @@ -1164,6 +1212,9 @@ // 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 and go to off state if ( TRUE == stopPump ) { @@ -1211,6 +1262,9 @@ // 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 ) { @@ -1252,6 +1306,9 @@ // 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 ) { @@ -1288,14 +1345,52 @@ // Check DAC write error else if ( ( adcDACStatus & SYRINGE_PUMP_DAC_WRITE_ERROR_BIT ) != 0 ) { - // TODO - alarm + activateAlarmNoData( ALARM_ID_HD_SYRINGE_PUMP_DAC_WRITE_ERROR ); } return result; } /*********************************************************************//** * @brief + * The checkDirection function checks the measured direction of the syringe + * pump vs. the given expected direction. + * @details Inputs: syringePumpMeasForce.data, syringePumpPosition.data + * @details Outputs: alarm triggered if max force detected + * @param stopPump flag passed in by caller indicating whether pump should be stopped + * @param expDir expected direction of syringe pump + * @return TRUE if pump should be stopped, FALSE if not + *************************************************************************/ +static BOOL checkDirection( BOOL stopPump, MOTOR_DIR_T expDir ) +{ + BOOL result = stopPump; + + if ( syringePumpEncoderMeasuredDirection != expDir ) + { + if ( ++syringePumpEncoderDirectionErrorCtr > SYRINGE_PUMP_DIR_ALARM_PERSISTENCE ) + { + result = TRUE; + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, (U32)syringePumpEncoderMeasuredDirection, (U32)syringePumpState ); + } + } + else if ( syringePumpControllerMeasuredDirection != expDir ) + { + if ( ++syringePumpControllerDirectionErrorCtr > SYRINGE_PUMP_DIR_ALARM_PERSISTENCE ) + { + result = TRUE; + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR, (U32)syringePumpControllerMeasuredDirection, (U32)syringePumpState ); + } + } + else + { + syringePumpControllerDirectionErrorCtr = 0; + } + + return result; +} + +/*********************************************************************//** + * @brief * The checkOcclusionOrEmpty function checks the force sensor for excessive * pressure. Would indicate occlusion or jam or empty syringe. * @details Inputs: syringePumpMeasForce.data, syringePumpPosition.data @@ -1325,11 +1420,11 @@ } result = TRUE; } - else - { - syringePumpOcclusionErrorCtr = 0; - } } + else + { + syringePumpOcclusionErrorCtr = 0; + } return result; } @@ -1413,10 +1508,16 @@ // Alarm on rate if off by more than 5% or 0.1 mL/hr, whichever is greater if ( ( error > pctMargin ) && ( delta > SYRINGE_PUMP_MAX_RATE_ERROR_ML_HR ) ) { - // TODO - needs persistence -// result = TRUE; -// SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_SYRINGE_PUMP_SPEED_ERROR, syringePumpSetRate, rate ) + if ( ++syringePumpRateErrorCtr > SYRINGE_PUMP_RATE_ALARM_PERSISTENCE ) + { + result = TRUE; + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_SYRINGE_PUMP_SPEED_ERROR, syringePumpSetRate, rate ) + } } + else + { + syringePumpRateErrorCtr = 0; + } return result; }