Index: firmware/App/Controllers/SyringePump.c =================================================================== diff -u -r98eba98a613c20101826036dcdaf5b0296265c1e -r647f8950c1a9f936e16a7b51e36f582c3d03a40e --- firmware/App/Controllers/SyringePump.c (.../SyringePump.c) (revision 98eba98a613c20101826036dcdaf5b0296265c1e) +++ firmware/App/Controllers/SyringePump.c (.../SyringePump.c) (revision 647f8950c1a9f936e16a7b51e36f582c3d03a40e) @@ -8,7 +8,7 @@ * @file SyringePump.c * * @author (last) Dara Navaei -* @date (last) 23-May-2022 +* @date (last) 15-Jul-2022 * * @author (original) Sean Nash * @date (original) 04-Mar-2021 @@ -82,11 +82,13 @@ #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_PRIME_VOLUME_ML 0.353F + 0.2F ///< Target syringe prime volume (in mL). Heparin line volume + volume to coat blood circuit/dialyzer. +#define SYRINGE_PUMP_PRIME_VOLUME_ML 0.353F ///< Target syringe prime volume (in mL). #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. /// Expected position of empty in relation to home position. #define SYRINGE_PUMP_EMPTY_POS ( SYRINGE_ENCODER_COUNTS_PER_ML * 10.84F ) @@ -189,6 +191,8 @@ 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 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). @@ -272,10 +276,9 @@ void initSyringePump( void ) { U32 i; - syringePumpState = SYRINGE_PUMP_INIT_STATE; heparinDeliveryState = HEPARIN_STATE_OFF; - + requireSyringeDetection = FALSE; syringePumpSetRate = 0.0; syringePumpSetToggleTime = 0; syringePumpSafetyVolumeDelivered = 0.0; @@ -486,27 +489,12 @@ *************************************************************************/ BOOL retractSyringePump( void ) { -#ifndef _RELEASE_ - if ( getSoftwareConfigStatus( SW_CONFIG_ENABLE_SYRINGE_PUMP_CMDS ) == SW_CONFIG_ENABLE_VALUE ) + if ( SYRINGE_PUMP_OFF_STATE == syringePumpState ) { - heparinDeliveryState = HEPARIN_STATE_STOPPED; + heparinDeliveryState = HEPARIN_STATE_STOPPED; + syringePumpSetRate = SYRINGE_PUMP_RETRACT_RATE; + syringePumpRetractRequested = TRUE; } -#endif - if ( MODE_SERV == getCurrentOperationMode() ) // Allow syringe pump operations in Service Mode. - { - heparinDeliveryState = HEPARIN_STATE_STOPPED; - } - if ( ( SYRINGE_PUMP_OFF_STATE == syringePumpState ) && ( heparinDeliveryState != HEPARIN_STATE_OFF ) ) - { - { - heparinDeliveryState = HEPARIN_STATE_STOPPED; - if ( ( SYRINGE_PUMP_OFF_STATE == syringePumpState ) ) - { - syringePumpSetRate = SYRINGE_PUMP_RETRACT_RATE; - syringePumpRetractRequested = TRUE; - } - } - } return syringePumpRetractRequested; } @@ -605,7 +593,6 @@ { SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, (F32)SW_FAULT_ID_HD_SYRINGE_INVALID_BOLUS_CMD, syringePumpSetRate ) } - } return syringePumpBolusRequested; @@ -653,14 +640,15 @@ * The setSyringePumpDACVref function requests to set the DAC Vref setting. * @details Inputs: none * @details Outputs: syringePumpDACVrefSetRequested, syringePumpDACVref - * @param vRef voltage to set DAC Vref to (in V) * @return TRUE if request accepted, FALSE if not *************************************************************************/ -BOOL setSyringePumpDACVref( F32 vRef ) +BOOL setSyringePumpDACVref( void ) { - if ( ( vRef >= 0.0 ) && ( vRef <= 3.3 ) ) - { - if ( SYRINGE_PUMP_OFF_STATE == syringePumpState ) + F32 vRef = forceSensorCalRecord.hdHeparinForceSensorDACVoltage; + + if ( ( vRef >= 0.0 ) && ( 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; @@ -676,6 +664,42 @@ /*********************************************************************//** * @brief + * The syringeDetectionRequired function sets a flag indicating whether + * the syringe detection is required in current state. + * @details Inputs: none + * @details Outputs: requireSyringeDetection + * @param req True if syringe detection is required in the current state + * @return none + *************************************************************************/ +void syringeDetectionRequired( BOOL req ) +{ + requireSyringeDetection = req; +} + +/*********************************************************************//** + * @brief + * The syringePumpVerifyForceSensorDACCalibration function verifies that the + * syringe pump force sensor DAC has been calibrated and is reporting no force + * as would be expected when fully retracted (caller should not call this function + * until the syringe pump is homed first). + * @details Inputs: none + * @details Outputs: + * @return none + *************************************************************************/ +void syringePumpVerifyForceSensorDACCalibration( void ) +{ + F32 DACDeltaV = fabs( forceSensorCalRecord.hdHeparinForceSensorDACVoltage - getSyringePumpForceV() ); + + if ( DACDeltaV > SYRINGE_PUMP_DAC_VOLTAGE_MAX_ERROR ) + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_HD_SYRINGE_PUMP_SELF_TEST_FAILURE, DACDeltaV ) + // In case DAC not set yet, try to set DAC + setSyringePumpDACVref(); + } +} + +/*********************************************************************//** + * @brief * The getSyringePumpVolumeDelivered function gets the current syringe pump * volume delivered. * @details Inputs: syringePumpVolumeDelivered @@ -939,7 +963,6 @@ *************************************************************************/ static void execSyringePumpMonitor( void ) { - F32 forceSensorBeforCal = 0.0; BOOL prevSyringeDetected = isSyringeDetected(); // Check if a new calibration is available @@ -960,46 +983,68 @@ // Get latest ADC data and convert to V syringePumpMeasHome.data = ( (F32)getFPGASyringePumpADCChannel2() * SYRINGE_PUMP_ADC_REF_V ) / SYRINGE_PUMP_ADC_FULL_SCALE_BITS; - syringePumpMeasSyringeDetectionSwitch.data = ( (F32)getFPGASyringePumpADCChannel1() * 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; + } + // Log syringe detect switch changes + if ( prevSyringeDetected != isSyringeDetected() ) + { + sendTreatmentLogEventData( SYRINGE_DETECTION_SWITCH_CHANGED_EVENT, (F32)prevSyringeDetected, (F32)isSyringeDetected() ); + } + // Handle syringe detect alarm management #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) #endif { - // On transition from not detected to detected - clear syringe removed alarm condition - if ( ( prevSyringeDetected != TRUE ) && ( TRUE == isSyringeDetected() ) ) + 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 ) ) { - clearAlarmCondition( ALARM_ID_HD_SYRINGE_PUMP_SYRINGE_REMOVED ); + // On transition from not detected to detected or if syringe detection not required - clear syringe removed alarm condition + if ( ( ( prevSyringeDetected != TRUE ) && ( TRUE == isSyringeDetected() ) ) || ( requireSyringeDetection != TRUE ) ) + { + clearAlarmCondition( ALARM_ID_HD_SYRINGE_PUMP_SYRINGE_REMOVED ); + } + // If no syringe detected and detection is required, trigger alarm condition + else if ( ( FALSE == isSyringeDetected() ) && ( TRUE == requireSyringeDetection ) ) + { + activateAlarmNoData( ALARM_ID_HD_SYRINGE_PUMP_SYRINGE_REMOVED ); + } + // On transition from detected to not detected - clear syringe detected alarm condition + if ( ( TRUE == prevSyringeDetected ) && ( isSyringeDetected() != TRUE ) ) + { + clearAlarmCondition( ALARM_ID_HD_SYRINGE_DETECTED ); + } + // If syringe detected and alarm active, maintain alarm condition + else if ( ( TRUE == isSyringeDetected() ) && ( TRUE == isAlarmActive( ALARM_ID_HD_SYRINGE_DETECTED ) ) ) + { + activateAlarmNoData( ALARM_ID_HD_SYRINGE_DETECTED ); + } } - // If no syringe detected while syringe removed alarm is active, maintain alarm condition - else if ( ( FALSE == isSyringeDetected() ) && ( TRUE == isAlarmActive( ALARM_ID_HD_SYRINGE_PUMP_SYRINGE_REMOVED ) ) ) + else { - activateAlarmNoData( ALARM_ID_HD_SYRINGE_PUMP_SYRINGE_REMOVED ); - } - // On transition from detected to not detected - clear syringe detected alarm condition - if ( ( TRUE == prevSyringeDetected ) && ( isSyringeDetected() != TRUE ) ) - { + clearAlarmCondition( ALARM_ID_HD_SYRINGE_PUMP_SYRINGE_REMOVED ); clearAlarmCondition( ALARM_ID_HD_SYRINGE_DETECTED ); } - // If syringe detected while syringe detected alarm is active, maintain alarm condition - else if ( ( TRUE == isSyringeDetected() ) && ( TRUE == isAlarmActive( ALARM_ID_HD_SYRINGE_DETECTED ) ) ) - { - activateAlarmNoData( ALARM_ID_HD_SYRINGE_DETECTED ); - } } - forceSensorBeforCal = ( (F32)getFPGASyringePumpADCChannel0() * SYRINGE_PUMP_ADC_REF_V ) / SYRINGE_PUMP_ADC_FULL_SCALE_BITS; - syringePumpMeasForce.data = pow(forceSensorBeforCal, 4) * forceSensorCalRecord.hdHeparinForceSensor.fourthOrderCoeff + - pow(forceSensorBeforCal, 3) * forceSensorCalRecord.hdHeparinForceSensor.thirdOrderCoeff + - pow(forceSensorBeforCal, 2) * forceSensorCalRecord.hdHeparinForceSensor.secondOrderCoeff + - forceSensorBeforCal * forceSensorCalRecord.hdHeparinForceSensor.gain + - forceSensorCalRecord.hdHeparinForceSensor.offset; + 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; + syringePumpVolumeDelivered.data = (F32)( syringePumpPosition.data - syringePumpVolumeStartPosition ) / SYRINGE_ENCODER_COUNTS_PER_ML; calcSafetyVolumeDelivered(); // Calculate measured rate (mL/hr) calcMeasRate(); @@ -1122,20 +1167,24 @@ *************************************************************************/ SELF_TEST_STATUS_T execSyringePumpSelfTest( void ) { - SELF_TEST_STATUS_T result = SELF_TEST_STATUS_IN_PROGRESS; - + SELF_TEST_STATUS_T result = SELF_TEST_STATUS_PASSED; + // Read/check force sensor calibration BOOL calStatus = getNVRecord2Driver( GET_CAL_HEPARIN_FORCE_SENSOR, (U08*)&forceSensorCalRecord, sizeof( HD_HEPARIN_FORCE_SENSOR_CAL_RECORD_T ), 0, ALARM_ID_HD_HEPARIN_FORCE_SENSOR_INVALID_CAL_RECORD ); - if ( TRUE == calStatus ) +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) +#endif { - result = SELF_TEST_STATUS_PASSED; + if ( TRUE == calStatus ) + { + result = SELF_TEST_STATUS_PASSED; + } + else + { + result = SELF_TEST_STATUS_FAILED; + } } - else - { - result = SELF_TEST_STATUS_FAILED; - activateAlarmNoData( ALARM_ID_HD_SYRINGE_PUMP_SELF_TEST_FAILURE ); - } return result; } @@ -1572,15 +1621,24 @@ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) #endif { - if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, ( syringePumpEncoderMeasuredDirection != expDir ) ) ) + // Check direction if rate >= minimum for alarm + if ( getSyringePumpMeasRate() >= MIN_SYRINGE_PUMP_RATE_FOR_DIR_ALARM ) { - result = TRUE; - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, (U32)syringePumpEncoderMeasuredDirection, (U32)syringePumpState ); + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, ( syringePumpEncoderMeasuredDirection != expDir ) ) ) + { + result = TRUE; + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, (U32)syringePumpEncoderMeasuredDirection, (U32)syringePumpState ); + } + if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR, ( syringePumpControllerMeasuredDirection != expDir ) ) ) + { + result = TRUE; + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR, (U32)syringePumpControllerMeasuredDirection, (U32)syringePumpState ); + } } - if ( TRUE == isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR, ( syringePumpControllerMeasuredDirection != expDir ) ) ) + else { - result = TRUE; - SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR, (U32)syringePumpControllerMeasuredDirection, (U32)syringePumpState ); + isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_ENCODER_DIRECTION_ERROR, FALSE ); + isPersistentAlarmTriggered( ALARM_ID_HD_SYRINGE_PUMP_CONTROLLER_DIRECTION_ERROR, FALSE ); } } @@ -1814,9 +1872,9 @@ syringePumpRampTimerCtr++; if ( syringePumpRampUpToggleTime > syringePumpSetToggleTime ) { - syringePumpRampUpToggleTime = (U32)((F32)SYRINGE_PUMP_START_RAMP_SPEED / - (F32)( ( syringePumpRampTimerCtr * syringePumpRampTimerCtr * syringePumpRampTimerCtr ) / SYRINGE_PUMP_RAMP_DIVISOR ) ); - syringePumpRampUpPct = syringePumpSetToggleTime / syringePumpRampUpToggleTime; + 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 ); @@ -1938,13 +1996,14 @@ } } - -/************************************************************************* - * TEST SUPPORT FUNCTIONS - *************************************************************************/ +/************************************************************************* + * TEST SUPPORT FUNCTIONS + *************************************************************************/ + + /*********************************************************************//** * @brief * The testSetSyringePumpDataPublishIntervalOverride function overrides the