Index: firmware/App/Controllers/SyringePump.c =================================================================== diff -u -r9055124b17389e04131b44c00915c33c72db3ae6 -rf01452c199716de698eaf90e692b9bb847d739c3 --- firmware/App/Controllers/SyringePump.c (.../SyringePump.c) (revision 9055124b17389e04131b44c00915c33c72db3ae6) +++ firmware/App/Controllers/SyringePump.c (.../SyringePump.c) (revision f01452c199716de698eaf90e692b9bb847d739c3) @@ -45,7 +45,8 @@ #define SYRINGE_STEPS_PER_REV 200.0 ///< Number of steps per revolution. #define MICRO_SECONDS_PER_SECOND 1000000.0 ///< Microseconds per second conversion. #define SYRINGE_TOGGLES_PER_STEP 2.0 ///< Stepper motor driver toggles per step or microstep. -#define SYRINGE_MICRO_STEPS_PER_STEP 32.0 ///< Micro-steps per step. +#define SYRINGE_MICRO_STEPS_PER_STEP 32.0 ///< 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. @@ -79,6 +80,10 @@ #define SYRINGE_PUMP_SYRINGE_DETECT_THRESHOLD_V 2.0 ///< Syringe pump syringe detected threshold (in V). #define SYRINGE_PUMP_HOME_DETECT_THRESHOLD_V 0.25 ///< Syringe pump home detected threshold (in V). #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 TEN_PCT_OVER_ALLOWANCE 1.1 ///< Allow ten percent over target before alarming on over travel. +#define FIVE_PCT_OVER_ALLOWANCE 1.05 ///< Allow five 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 ) @@ -141,7 +146,7 @@ NUM_OF_SYRINGE_PUMP_STATES ///< Number of syringe pump control states } SYRINGE_PUMP_STATE_T; -/// Defined states for the pressure and occlusion self-test state machine. +/// Defined states for the syringe pump self-test state machine. typedef enum Syringe_Pump_Self_Test_States { SYRINGE_PUMP_SELF_TEST_STATE_START = 0, ///< Self test start state. @@ -174,6 +179,7 @@ static S32 syringePumpVolumeStartPosition; ///< Start position for the current volume calculation. static S32 syringePumpHomePositionOffset; ///< FPGA reported position when at home postion. static S32 syringePumpPosition1SecAgo; ///< Position recorded at last 1 Hz speed check. +static MOTOR_DIR_T syringePumpMeasuredDirection; ///< Measured direction of syringe pump. static BOOL syringePumpRetractRequested; ///< Flag indicates a retract operation is requested. static BOOL syringePumpSeekRequested; ///< Flag indicates a plunger seek operation is requested. @@ -242,6 +248,7 @@ syringePumpVolumeStartPosition = 0; syringePumpHomePositionOffset = 0; syringePumpPosition1SecAgo = 0; + syringePumpMeasuredDirection = MOTOR_DIR_FORWARD; syringePumpDataPublicationTimerCounter = 0; syringePumpSelfTestTimerCount = 0; @@ -280,7 +287,7 @@ * The userHeparinRequest function handles a command request from the user * to pause or resume Heparin delivery. * @details Inputs: none - * @details Outputs: + * @details Outputs: heparinDeliveryState, syringePumpState, syringePumpContinuousRequested * @param cmd command from user * @return TRUE if request accepted, FALSE if not *************************************************************************/ @@ -291,7 +298,7 @@ if ( MODE_TREA == getCurrentOperationMode() ) { - if ( TREATMENT_DIALYSIS_STATE == getTreatmentState() ) + if ( ( TREATMENT_DIALYSIS_STATE == getTreatmentState() ) && ( getDialysisState() != DIALYSIS_SALINE_BOLUS_STATE ) ) { if ( HEPARIN_CMD_PAUSE == cmd ) { @@ -311,7 +318,6 @@ if ( HEPARIN_STATE_PAUSED == heparinDeliveryState ) { accepted = TRUE; - heparinDeliveryState = HEPARIN_STATE_DISPENSING; startHeparinContinuous(); } else @@ -417,7 +423,7 @@ *************************************************************************/ BOOL retractSyringePump( void ) { - if ( ( SYRINGE_PUMP_OFF_STATE == syringePumpState ) && ( FALSE == isSyringeDetected() ) && ( heparinDeliveryState != HEPARIN_STATE_OFF ) ) + if ( ( SYRINGE_PUMP_OFF_STATE == syringePumpState ) && ( heparinDeliveryState != HEPARIN_STATE_OFF ) ) { syringePumpSetRate = SYRINGE_PUMP_RETRACT_RATE; syringePumpRetractRequested = TRUE; @@ -512,7 +518,7 @@ if ( ( flowRate >= MIN_HEPARIN_CONTINUOUS_RATE ) && ( flowRate <= MAX_HEPARIN_CONTINUOUS_RATE ) && ( FALSE == isSyringePumpHome() ) ) { if ( ( TRUE == isSyringeDetected() ) && ( SYRINGE_PUMP_OFF_STATE == syringePumpState ) && - ( ( ( HEPARIN_STATE_STOPPED == heparinDeliveryState ) || ( HEPARIN_STATE_PAUSED == heparinDeliveryState ) ) ) ) + ( ( HEPARIN_STATE_STOPPED == heparinDeliveryState ) || ( HEPARIN_STATE_PAUSED == heparinDeliveryState ) ) ) { syringePumpSetRate = flowRate; syringePumpContinuousRequested = TRUE; @@ -805,6 +811,8 @@ calcSafetyVolumeDelivered(); // Calculate measured rate (mL/hr) calcMeasRate(); + // Get measured direction + syringePumpMeasuredDirection = ( ( encStatus & 0x80 ) != 0 ? MOTOR_DIR_REVERSE : MOTOR_DIR_FORWARD ); if ( syringePumpDACVrefWriteInProgress != TRUE ) { @@ -814,6 +822,18 @@ // activateAlarmNoData( ALARM_ID_HD_SYRINGE_PUMP_ADC_ERROR ); // TODO - restore when issue with read counter resolved } lastSyringePumpADCReadCtr = adcReadCtr; + + // Check encoder direction error. // TODO check direction in states + if ( ( encStatus & 0x3F ) != 0) + { + // TODO - alarm? + } + + // Check pump status + if ( pmpStatus != 0 ) + { + // TODO - pump failure fault + } } } @@ -974,8 +994,8 @@ * @brief * The handleSyringePumpRetractState function handles the retract state * of the syringe pump control state machine. - * @details Inputs: TBD - * @details Outputs: TBD + * @details Inputs: syringePumpMeasHome.data + * @details Outputs: Syringe pump ramped up to retract rate, alarm conditions checked * @return next state *************************************************************************/ static SYRINGE_PUMP_STATE_T handleSyringePumpRetractState( void ) @@ -989,22 +1009,24 @@ if ( TRUE == isSyringePumpHome() ) { stopSyringePump(); + // 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; syringePumpPrimeCompleted = FALSE; result = SYRINGE_PUMP_OFF_STATE; } - // Stop retract if syringe is detected (should not have a syringe loaded during retract) - else if ( TRUE == isSyringeDetected() ) + else if ( syringePumpMeasuredDirection != MOTOR_DIR_REVERSE ) { - stopSyringePump(); - activateAlarmNoData( ALARM_ID_HD_SYRINGE_PUMP_SYRINGE_DETECTED_IN_RETRACT ); - result = SYRINGE_PUMP_OFF_STATE; + // TODO - alarm w/ some persistence } + // TODO - if position know from prior retract, ensure we don't go lower than -TBD position return result; } @@ -1013,8 +1035,8 @@ * @brief * The handleSyringePumpSeekState function handles the seek plunger state * of the syringe pump control state machine. - * @details Inputs: TBD - * @details Outputs: TBD + * @details Inputs: syringePumpMeasForce.data + * @details Outputs: Syringe pump ramped up to seek rate, alarm conditions checked * @return next state *************************************************************************/ static SYRINGE_PUMP_STATE_T handleSyringePumpSeekState( void ) @@ -1031,6 +1053,7 @@ stopPump = TRUE; syringePumpPlungerFound = TRUE; syringePumpVolumeDelivered.data = 0.0; + syringePumpSafetyVolumeDelivered = 0.0; syringePumpVolumeStartPosition = syringePumpPosition.data; } @@ -1040,6 +1063,10 @@ // Check for occlusion stopPump = checkOcclusionOrEmpty( stopPump ); + // TODO - check max position > TBD + + // TODO - calc estimate syringe volume - if insufficient for treatment needs, alarm + // If anything found that would require stopping the pump, stop pump and go to off state if ( TRUE == stopPump ) { @@ -1054,8 +1081,8 @@ * @brief * The handleSyringePumpPrimeState function handles the prime state * of the syringe pump control state machine. - * @details Inputs: TBD - * @details Outputs: TBD + * @details Inputs: syringePumpVolumeDelivered.data + * @details Outputs: Syringe pump ramped up to prime rate, alarm conditions checked * @return next state *************************************************************************/ static SYRINGE_PUMP_STATE_T handleSyringePumpPrimeState( void ) @@ -1072,6 +1099,7 @@ stopPump = TRUE; syringePumpPrimeCompleted = TRUE; syringePumpVolumeDelivered.data = 0.0; + syringePumpSafetyVolumeDelivered = 0.0; syringePumpVolumeStartPosition = syringePumpPosition.data; } @@ -1082,7 +1110,7 @@ stopPump = checkOcclusionOrEmpty( stopPump ); // Check position > max travel - stopPump = checkMaxTravel( stopPump, syringePumpVolumeStartPosition * ( SYRINGE_PUMP_PRIME_VOLUME_ML * SYRINGE_ENCODER_COUNTS_PER_ML ) ); + stopPump = checkMaxTravel( stopPump, syringePumpVolumeStartPosition + ( SYRINGE_PUMP_PRIME_VOLUME_ML * TEN_PCT_OVER_ALLOWANCE * SYRINGE_ENCODER_COUNTS_PER_ML ) ); // If anything found that would require stopping the pump, stop pump and go to off state if ( TRUE == stopPump ) @@ -1097,8 +1125,8 @@ * @brief * The handleSyringePumpBolusState function handles the bolus delivery state * of the syringe pump control state machine. - * @details Inputs: TBD - * @details Outputs: TBD + * @details Inputs: set bolus volume, syringePumpVolumeDelivered.data + * @details Outputs: Syringe pump ramped up to set bolus rate, alarm conditions checked * @return next state *************************************************************************/ static SYRINGE_PUMP_STATE_T handleSyringePumpBolusState( void ) @@ -1129,7 +1157,7 @@ stopPump = checkMaxMeasRate( stopPump, SYRINGE_PUMP_RATE_CHECK_MARGIN ); // Check position > max travel - stopPump = checkMaxTravel( stopPump, syringePumpVolumeStartPosition * ( SYRINGE_PUMP_PRIME_VOLUME_ML * SYRINGE_ENCODER_COUNTS_PER_ML ) ); + stopPump = checkMaxTravel( stopPump, syringePumpVolumeStartPosition + ( bolusVol * FIVE_PCT_OVER_ALLOWANCE * SYRINGE_ENCODER_COUNTS_PER_ML ) ); // If anything found that would require stopping the pump, stop pump and go to off state if ( TRUE == stopPump ) @@ -1145,8 +1173,8 @@ * @brief * The handleSyringePumpContinuousState function handles the continuous state * of the syringe pump control state machine. - * @details Inputs: TBD - * @details Outputs: TBD + * @details Inputs: none + * @details Outputs: Syringe pump ramped up to set rate, alarm conditions checked * @return next state *************************************************************************/ static SYRINGE_PUMP_STATE_T handleSyringePumpContinuousState( void ) @@ -1163,9 +1191,6 @@ // Check for occlusion stopPump = checkOcclusionOrEmpty( stopPump ); - // Check if time to stop Heparin delivery - // TODO - implement pre-stop check - // Check position > max travel stopPump = checkMaxTravel( stopPump, SYRINGE_PUMP_EMPTY_POS + SYRINGE_PUMP_EMPTY_POS_MARGIN ); @@ -1215,8 +1240,8 @@ * @brief * The checkOcclusionOrEmpty function checks the force sensor for excessive * pressure. Would indicate occlusion or jam or empty syringe. - * @details Inputs: TBD - * @details Outputs: TBD + * @details Inputs: syringePumpMeasForce.data, syringePumpPosition.data + * @details Outputs: alarm triggered if max force detected * @param stopPump flag passed in by caller indicating whether pump should be stopped * @return TRUE if pump should be stopped, FALSE if not *************************************************************************/ @@ -1249,8 +1274,8 @@ * The checkSyringeRemoved function checks whether the syringe has been removed. * This function should only be called from states that require the syringe to * be installed. - * @details Inputs: TBD - * @details Outputs: TBD + * @details Inputs: syringePumpMeasSyringeDetectionSwitch.data + * @details Outputs: alarm triggered if syringe not detected * @param stopPump flag passed in by caller indicating whether pump should be stopped * @return TRUE if pump should be stopped, FALSE if not *************************************************************************/ @@ -1272,8 +1297,8 @@ * The checkMaxTravel function checks whether a maximum travel (position) * has been exceeded. This threshold is state dependent so the calling function * must provide the maximum position to apply. - * @details Inputs: TBD - * @details Outputs: TBD + * @details Inputs: syringePumpPosition.data + * @details Outputs: alarm triggered if beyond given max position * @param stopPump flag passed in by caller indicating whether pump should be stopped * @param maxPos maximum position allowed in current state * @return TRUE if pump should be stopped, FALSE if not @@ -1302,8 +1327,8 @@ * The checkMaxMeasRate function checks whether the measured rate is exceeding * a maximum rate (in mL/hr). This threshold is state dependent so the calling * function must provide the maximum rate to apply. - * @details Inputs: TBD - * @details Outputs: TBD + * @details Inputs: syringePumpMeasRate.data, syringePumpSetRate + * @details Outputs: alarm triggered if measured and set rates deviate too much * @param stopPump flag passed in by caller indicating whether pump should be stopped * @param pctMargin percent tolerance allowed between set and measured rate * @return TRUE if pump should be stopped, FALSE if not @@ -1312,14 +1337,17 @@ { BOOL result = stopPump; F32 rate = getSyringePumpMeasRate(); - F32 delta = fabs( syringePumpSetRate - rate ); F32 max = MAX( rate, syringePumpSetRate ); - F32 error = ( max > 0.0 ? ( 1.0 - ( delta / max ) ) : 0.0 ); + F32 min = MIN( rate, syringePumpSetRate ); + F32 delta = max - min; + F32 error = ( max > 0.0 ? ( 1.0 - fabs( min / max ) ) : 0.0 ); - if ( error > pctMargin ) + // 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 ) ) { - result = TRUE; - SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_SYRINGE_PUMP_SPEED_ERROR, syringePumpSetRate, rate ) + // TODO - needs persistence +// result = TRUE; +// SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_SYRINGE_PUMP_SPEED_ERROR, syringePumpSetRate, rate ) } return result; @@ -1331,8 +1359,8 @@ * and the safety volume delivered have diverged too much. The threshold * is state dependent so the calling function must provide the tolerance (in +/- %) * to apply. - * @details Inputs: TBD - * @details Outputs: TBD + * @details Inputs: syringePumpVolumeDelivered.data, syringePumpSafetyVolumeDelivered + * @details Outputs: alarm triggered if measured and expected volume deviate too much * @param stopPump flag passed in by caller indicating whether pump should be stopped * @param pctMargin percent tolerance allowed between volume and safety volume * @return TRUE if pump should be stopped, FALSE if not @@ -1341,11 +1369,13 @@ { BOOL result = stopPump; F32 vol = getSyringePumpVolumeDelivered(); - F32 delta = fabs( vol - syringePumpSafetyVolumeDelivered ); F32 max = MAX( vol, syringePumpSafetyVolumeDelivered ); - F32 error = ( fabs( max ) < NEARLY_ZERO ? 0.0 : ( 1.0 - ( delta / max ) ) ); + F32 min = MIN( vol, syringePumpSafetyVolumeDelivered ); + F32 delta = max - min; + F32 error = ( fabs( max ) < NEARLY_ZERO ? 0.0 : ( 1.0 - fabs( min / max ) ) ); - if ( error > pctMargin ) + // Alarm if volume delivered off by more than 5% or 0.1 mL, whichever is greater + if ( ( error > pctMargin ) && ( delta > SYRINGE_PUMP_MAX_VOL_ERROR_ML ) ) { result = TRUE; SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_SYRINGE_PUMP_VOLUME_ERROR, vol, syringePumpSafetyVolumeDelivered ) @@ -1416,11 +1446,12 @@ { if ( ++syringePumpSpeedCalcTimerCounter > SYRINGE_PUMP_SPEED_CHECK_INTERVAL ) { - S32 countsPerSec = syringePumpMeasRate.data - syringePumpPosition1SecAgo; + S32 countsPerSec = syringePumpPosition.data - syringePumpPosition1SecAgo; S32 countsPerHr = countsPerSec * ( MIN_PER_HOUR * SEC_PER_MIN); F32 mLPerHr = (F32)countsPerHr / SYRINGE_ENCODER_COUNTS_PER_ML; syringePumpMeasRate.data = mLPerHr; + syringePumpPosition1SecAgo = syringePumpPosition.data; syringePumpSpeedCalcTimerCounter = 0; } } @@ -1462,6 +1493,7 @@ data.syringePumpState = (U32)syringePumpState; data.heparinDeliveryState = (U32)heparinDeliveryState; data.syringePumpVolumeDelivered = getSyringePumpVolumeDelivered(); + data.syringePumpSafetyVolume = syringePumpSafetyVolumeDelivered; broadcastSyringePumpData( data ); broadcastHeparinData( data.syringePumpVolumeDelivered );