Index: firmware/App/Modes/SelfTests.c =================================================================== diff -u -r8bd1ae47aa13a843aa8abd6321ddc050deacb4a6 -r40e8c3a97babf6910cc6e9602d691f8e56691c17 --- firmware/App/Modes/SelfTests.c (.../SelfTests.c) (revision 8bd1ae47aa13a843aa8abd6321ddc050deacb4a6) +++ firmware/App/Modes/SelfTests.c (.../SelfTests.c) (revision 40e8c3a97babf6910cc6e9602d691f8e56691c17) @@ -1,14 +1,14 @@ /************************************************************************** * -* Copyright (c) 2019-2021 Diality Inc. - All Rights Reserved. +* Copyright (c) 2021-2022 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 SelfTests.c * -* @author (last) Quang Nguyen -* @date (last) 09-Aug-2021 +* @author (last) Dara Navaei +* @date (last) 31-Mar-2022 * * @author (original) Quang Nguyen * @date (original) 28-Jan-2021 @@ -41,14 +41,15 @@ #define PUMP_RUN_SELF_TEST_TIME_MS ( 15 * MS_PER_SECOND ) ///< Self-test time to run pumps in ms. #define PUMP_SELF_TEST_FLOW_RATE_ML_MIN 100 ///< Self-test pump flow rate in mL/min. +#define SYRINGE_PUMP_OCCLUSION_CHECK_DELAY ( 3 * MS_PER_SECOND ) ///< Delay 3 seconds then check for syringe pump prime occlusion. #define BLOOD_PUMP_RUN_TIME_PRESSURE_SELF_TEST ( 5 * MS_PER_SECOND ) ///< Pressure self-test time to run blood pump in ms. -#define NORMALIZED_PRESSURE_SELF_TEST_TIME ( 2 * MS_PER_SECOND ) ///< Time to wait for pressure to normalize in ms. +#define NORMALIZED_PRESSURE_SELF_TEST_TIME ( 4 * MS_PER_SECOND ) ///< Time to wait for pressure to normalize in ms. #define ARTERIAL_PRESSURE_SELF_TEST_LOW_LIMIT_MMHG -50.0 ///< Arterial pressure low limit after running blood pump. #define VENOUS_PRESSURE_SELF_TEST_HIGH_LIMIT_MMHG 400 ///< Venous pressure high limit after running blood pump. -#define NORMAL_PRESSURE_DIFF_TOLERANCE_MMHG 5.0 ///< Difference in pressure readings after return to normal state tolerance (in mmHg). +#define NORMAL_PRESSURE_DIFF_TOLERANCE_MMHG 10.0 ///< Difference in pressure readings after return to normal state tolerance (in mmHg). #define DIP_FLOW_RATE_SETUP_ML_MIN 150 ///< Dialysate inlet pump flow rate during the setup for wet self-test. #define DIP_FLOW_RATE_FIRST_DISPLACEMENT_ML_MIN 100 ///< Dialysate inlet pump flow rate during the first displacement in wet self-test. @@ -58,37 +59,19 @@ #define WET_SELF_TEST_FIRST_DISPLACEMENT_TARGET_VOLUME_ML 100.0 ///< Target of first displacement volume in ml. #define WET_SELF_TEST_SECOND_DISPLACEMENT_TARGET_VOLUME_ML 600.0 ///< Target of second displacement volume in ml. #define WET_SELF_TEST_INTEGRATED_VOLUME_TOLERANCE 5.0 ///< Tolerance on integrated volume in percentage. -#define WET_SELF_TEST_DISPLACEMENT_TOLERANCE_G 1.5 ///< Tolerance in the load cell readings of the displacement in grams. +#define WET_SELF_TEST_DISPLACEMENT_TOLERANCE_G 12.0 ///< Tolerance in the load cell readings of the displacement in grams (2%). #define WET_SELF_TEST_DISPLACEMENT_TIME_MS ( SEC_PER_MIN * MS_PER_SECOND ) ///< Time to displace dialysate in wet self-test in ms. #define RESERVOIR_SETTLE_TIME_MS ( 4 * MS_PER_SECOND ) ///< Time allotted for reservoir to settle in ms. -#define MAX_NO_CARTRIDGE_SELF_TEST_TIME 30 ///< Maximum no cartridge self-test time (in seconds). +#define MAX_NO_CARTRIDGE_SELF_TEST_TIME 30 ///< Maximum no cartridge self-test time (in seconds). #define MAX_DRY_SELF_TEST_TIME ( 3 * SEC_PER_MIN ) ///< Maximum dry self-test time (in seconds). +#define CARTRIDGE_INSERT_PRESSURE_SETTLE_TIME_MS ( 10 * MS_PER_SECOND ) ///< Time (in ms) required to wait for occlusion pressure to settle after cartridge insertion. #define SELF_TEST_TIME_DATA_PUB_INTERVAL ( MS_PER_SECOND ) ///< Interval (ms/task time) at which self-test time data is published on the CAN bus. /// Multiplier to conver flow (mL/min) into volume (mL) for period of general task interval. static const F32 SELF_TEST_FLOW_INTEGRATOR = ( ( 1.0 * TASK_GENERAL_INTERVAL ) / ( SEC_PER_MIN * MS_PER_SECOND ) ); -/// Wet self-tests state machine. -typedef enum Wet_Self_Tests_State -{ - WET_SELF_TESTS_START_STATE = 0, ///< Wet self-tests starting state. - WET_SELF_TESTS_SETUP_STATE, ///< Setup reservoirs for wet self-tests. - WET_SELF_TESTS_BUBBLES_STATE, ///< Wet self-test air bubble detectors state. - WET_SELF_TESTS_PRIME_CHECK_STATE, ///< Prime check wet self-tests state, checks for primed patient lines. - WET_SELF_TESTS_BLOOD_LEAK_DETECTOR_STATE, ///< Blood leak detector self-test state. - WET_SELF_TESTS_FIRST_DISPLACEMENT_SETUP_STATE, ///< Setup valve and start dialysate pump for first displacement. - WET_SELF_TESTS_FIRST_DISPLACEMENT_STATE, ///< Fill reservoir 2 with 100 mL of dialysate from reservoir 1 state. - WET_SELF_TESTS_FIRST_DISPLACEMENT_VERIFY_STATE, ///< After first displacement completed verify state. - WET_SELF_TESTS_SECOND_DISPLACEMENT_SETUP_STATE, ///< Setup valve and start dialysate pump for second displacement. - WET_SELF_TESTS_SECOND_DISPLACEMENT_STATE, ///< Fill reservoir 1 with 300 mL of dialysate from reservoir 2 state. - WET_SELF_TESTS_SECOND_DISPLACEMENT_VERIFY_STATE, ///< After first displacement completed verify state. - WET_SELF_TESTS_STOPPED_STATE, ///< Wet self-test stopped state. - WET_SELF_TESTS_COMPLETE_STATE, ///< Wet self-test complete state. - NUM_OF_WET_SELF_TESTS_STATES ///< Number of wet self-tests states. -} WET_SELF_TESTS_STATE_T; - // ********** private data ********** static NO_CART_SELF_TESTS_STATE_T currentNoCartSelfTestsState; ///< Current state of the no cartridge self-tests state machine. @@ -112,6 +95,7 @@ static U32 selfTestStartTime; ///< Starting time of self-test (in ms). static U32 selfTestPreviousPublishDataTime; ///< Last time self-test time data is being published (in ms). +static U32 syringeOcclusionDelayStartTime; ///< Used to calculate the 1 second delay time before check for prime occlusion. static BOOL useHeparin; ///< Flag indicates the user of heparin. @@ -135,6 +119,7 @@ static DRY_SELF_TESTS_STATE_T handleDrySelfTestPressureSensorsState( SELF_TEST_STATUS_T *result ); static DRY_SELF_TESTS_STATE_T handleDrySelfTestPressureSensorsNormalState( SELF_TEST_STATUS_T *result ); static DRY_SELF_TESTS_STATE_T handleDrySelfTestSyringePumpPrimeState( void ); +static DRY_SELF_TESTS_STATE_T handleDrySelfTestSyringePumpOcclusionDetectionState( void ); static DRY_SELF_TESTS_STATE_T handleDrySelfTestStoppedState( void ); static WET_SELF_TESTS_STATE_T handleWetSelfTestStartState( void ); @@ -161,6 +146,7 @@ { selfTestStartTime = 0; selfTestPreviousPublishDataTime = 0; + syringeOcclusionDelayStartTime = 0; } /*********************************************************************//** @@ -201,7 +187,16 @@ signalBloodPumpHardStop(); signalDialInPumpHardStop(); signalDialOutPumpHardStop(); - stopSyringePump(); + + if ( TRUE == isAlarmActive( ALARM_ID_HD_SYRINGE_PUMP_NOT_ENOUGH_HEPARIN_ALARM ) ) + { + retractSyringePump(); + } + else + { + stopSyringePump(); + } + setValvePosition( VDI, VALVE_POSITION_A_INSERT_EJECT ); setValvePosition( VDO, VALVE_POSITION_A_INSERT_EJECT ); setValvePosition( VBA, VALVE_POSITION_A_INSERT_EJECT ); @@ -221,11 +216,15 @@ F32 const bolusVol = getTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_BOLUS_VOLUME ); F32 const hepRate = getTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_DISPENSE_RATE ); -#ifndef DISABLE_SYRINGE_PUMP - useHeparin = ( ( bolusVol > 0.0 ) || ( hepRate > 0.0 ) ? TRUE : FALSE ); -#else - useHeparin = FALSE; -#endif + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SYRINGE_PUMP ) != SW_CONFIG_ENABLE_VALUE ) + { + useHeparin = ( ( bolusVol > 0.0 ) || ( hepRate > 0.0 ) ? TRUE : FALSE ); + } + else + { + useHeparin = FALSE; + } + currentNoCartSelfTestsState = NO_CART_SELF_TESTS_START_STATE; runPumpStartTime = 0; havePumpsStarted = FALSE; @@ -267,7 +266,7 @@ break; case NO_CART_SELF_TESTS_DIALYSATE_FLOW_METERS_STATE: - execDialInFlowTest(); + // TODO as of now, the dialysate flow self test only gets the calibration record so it is only called in POST currentNoCartSelfTestsState = NO_CART_SELF_TESTS_BOARD_TEMPERATURE_STATE; break; @@ -378,12 +377,19 @@ switch ( currentDrySelfTestsState ) { case DRY_SELF_TESTS_START_STATE: -#ifdef SKIP_DRY_SELF_TESTS - // TODO: Remove once dry self-test is ready to use - currentDrySelfTestsState = DRY_SELF_TESTS_SYRINGE_PUMP_PRIME_STATE; -#else - currentDrySelfTestsState = DRY_SELF_TESTS_WAIT_FOR_DOOR_CLOSE_STATE; -#endif + // Ensure occlusion sensor has time to settle after cartridge insertion before starting dry self-tests + if ( TRUE == didTimeout( selfTestStartTime, CARTRIDGE_INSERT_PRESSURE_SETTLE_TIME_MS ) ) + { + setOcclusionInstallLevel(); // Record occlusion pressure level after a new cartridge is installed + if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_DISABLE_DRY_SELF_TESTS ) ) + { + currentDrySelfTestsState = DRY_SELF_TESTS_SYRINGE_PUMP_PRIME_STATE; + } + else + { + currentDrySelfTestsState = DRY_SELF_TESTS_WAIT_FOR_DOOR_CLOSE_STATE; + } + } break; case DRY_SELF_TESTS_WAIT_FOR_DOOR_CLOSE_STATE: @@ -422,6 +428,10 @@ currentDrySelfTestsState = handleDrySelfTestSyringePumpPrimeState(); break; + case DRY_SELF_TESTS_SYRINGE_PUMP_OCCLUSION_DETECTION_STATE: + currentDrySelfTestsState = handleDrySelfTestSyringePumpOcclusionDetectionState(); + break; + case DRY_SELF_TESTS_STOPPED_STATE: currentDrySelfTestsState = handleDrySelfTestStoppedState(); break; @@ -486,6 +496,7 @@ *************************************************************************/ void transitionToWetSelfTests() { + signalDialInPumpHardStop(); // turn off DPi that was on in previous dialysate bypass state wetSelfTestsResult = FALSE; currentWetSelfTestsState = WET_SELF_TESTS_START_STATE; settleStartTime = 0; @@ -608,7 +619,7 @@ selfTestStartTime = getMSTimerCount(); // TODO: Use appropriate sensor driver - if ( STATE_CLOSED == getFPGADoorState() ) + if (( STATE_CLOSED == getSwitchStatus( FRONT_DOOR ) ) && (STATE_CLOSED == getSwitchStatus( PUMP_TRACK_SWITCH ) )) { state = NO_CART_SELF_TESTS_OCCLUSION_SENSORS_STATE; } @@ -792,7 +803,7 @@ selfTestStartTime = getMSTimerCount(); // TODO: Use appropriate sensor driver - if ( STATE_CLOSED == getFPGADoorState() ) + if (( STATE_CLOSED == getSwitchStatus( FRONT_DOOR ) ) && (STATE_CLOSED == getSwitchStatus( PUMP_TRACK_SWITCH ) )) { state = DRY_SELF_TESTS_USED_CARTRIDGE_CHECK_STATE; setValvePosition( VDI, VALVE_POSITION_C_CLOSE ); @@ -821,11 +832,9 @@ static DRY_SELF_TESTS_STATE_T handleDrySelfTestUsedCartridgeCheckState( void ) { DRY_SELF_TESTS_STATE_T state = DRY_SELF_TESTS_USED_CARTRIDGE_CHECK_STATE; - - BUBBLE_STATUS_T const ADABubbleStatus = getBubbleStatus( ADA ); BUBBLE_STATUS_T const ADVBubbleStatus = getBubbleStatus( ADV ); - if ( ( BUBBLE_DETECTED == ADABubbleStatus ) && ( BUBBLE_DETECTED == ADVBubbleStatus ) && + if ( ( BUBBLE_DETECTED == ADVBubbleStatus ) && ( AIR_TRAP_LEVEL_AIR == getAirTrapLevel( AIR_TRAP_LEVEL_SENSOR_LOWER ) ) && ( AIR_TRAP_LEVEL_AIR == getAirTrapLevel( AIR_TRAP_LEVEL_SENSOR_UPPER ) ) ) { @@ -973,21 +982,28 @@ if ( TRUE == useHeparin ) { - if ( TRUE == isSyringePlungerFound() ) + if ( FALSE == isSyringePumpRunning() ) { - if ( TRUE == isSyringePumpPrimed() ) + if ( TRUE == isSyringePlungerFound() ) { - state = DRY_SELF_TESTS_COMPLETE_STATE; + if ( TRUE == isSyringePumpPrimed() ) + { + state = DRY_SELF_TESTS_SYRINGE_PUMP_OCCLUSION_DETECTION_STATE; + syringeOcclusionDelayStartTime = getMSTimerCount(); // Get the current time to check for occlusion after 3 seconds has elapsed + } + else + { + primeSyringePump(); + } } else { - primeSyringePump(); + if ( TRUE == isSyringePumpHome() ) + { + seekSyringePlunger(); + } } } - else - { - seekSyringePlunger(); - } } else { @@ -1005,6 +1021,37 @@ /*********************************************************************//** * @brief + * The handleDrySelfTestSyringePumpOcclusionDetectionState function handles + * occlusion detection after prime has completed. + * @details Inputs: none + * @details Outputs: none + * @return the next state of dry self-tests state machine + *************************************************************************/ +static DRY_SELF_TESTS_STATE_T handleDrySelfTestSyringePumpOcclusionDetectionState( void ) +{ + DRY_SELF_TESTS_STATE_T state = DRY_SELF_TESTS_SYRINGE_PUMP_OCCLUSION_DETECTION_STATE; + + if ( TRUE == didTimeout( syringeOcclusionDelayStartTime, SYRINGE_PUMP_OCCLUSION_CHECK_DELAY ) ) + { + if ( FALSE == checkForPrimeOcclusion() ) // transition to complete state only when occlusion is removed + { + // clear the occlusion alarm condition to allow user to resume + clearAlarmCondition( ALARM_ID_HD_SYRINGE_PUMP_OCCLUSION ); // Resume option will appear + state = DRY_SELF_TESTS_COMPLETE_STATE; + } + } + + if ( ( TRUE == doesAlarmStatusIndicateStop() ) && ( FALSE == isAlarmActive( ALARM_ID_HD_SYRINGE_PUMP_OCCLUSION ) ) ) + { + state = DRY_SELF_TESTS_STOPPED_STATE; + setupForSelfTestsStop(); + } + + return state; +} + +/*********************************************************************//** + * @brief * The handleDrySelfTestStoppedState function handles the stopped dry self-tests * operation. * @details Inputs: none @@ -1021,11 +1068,14 @@ if ( TRUE == selfTestsResumeRequested ) { selfTestsResumeRequested = FALSE; -#ifndef SKIP_DRY_SELF_TESTS - state = DRY_SELF_TESTS_WAIT_FOR_DOOR_CLOSE_STATE; -#else - state = DRY_SELF_TESTS_SYRINGE_PUMP_PRIME_STATE; -#endif + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_DRY_SELF_TESTS ) != SW_CONFIG_ENABLE_VALUE ) + { + state = DRY_SELF_TESTS_WAIT_FOR_DOOR_CLOSE_STATE; + } + else + { + state = DRY_SELF_TESTS_SYRINGE_PUMP_PRIME_STATE; + } } return state; @@ -1070,8 +1120,16 @@ if ( FALSE == cmdResp.rejected ) { + F32 bolusVol = getTreatmentParameterF32( TREATMENT_PARAM_HEPARIN_BOLUS_VOLUME ); + setDialInPumpTargetFlowRate( DIP_FLOW_RATE_SETUP_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); fmdIntegratedVolume = 0.0; + + if ( ( bolusVol > 0.0 ) && ( getSyringePumpVolumeDelivered() < bolusVol ) ) + { + startHeparinBolus(); // moved here from startHeparinPump() in Dialysis.c + } + state = WET_SELF_TESTS_SETUP_STATE; } } @@ -1102,7 +1160,6 @@ if ( setupDisplacementVolume <= 0 ) { signalDialInPumpHardStop(); - selfTestBubble( ADA ); selfTestBubble( ADV ); state = WET_SELF_TESTS_BUBBLES_STATE; } @@ -1128,7 +1185,7 @@ { WET_SELF_TESTS_STATE_T state = WET_SELF_TESTS_BUBBLES_STATE; - if ( ( SELF_TEST_STATUS_PASSED == getBubbleSelfTestStatus( ADA ) ) && ( SELF_TEST_STATUS_PASSED == getBubbleSelfTestStatus( ADV ) ) ) + if ( SELF_TEST_STATUS_PASSED == getBubbleSelfTestStatus( ADV ) ) { state = WET_SELF_TESTS_PRIME_CHECK_STATE; } @@ -1156,14 +1213,20 @@ WET_SELF_TESTS_STATE_T state = WET_SELF_TESTS_PRIME_CHECK_STATE; *result = SELF_TEST_STATUS_FAILED; - BUBBLE_STATUS_T const ADABubbleStatus = getBubbleStatus( ADA ); BUBBLE_STATUS_T const ADVBubbleStatus = getBubbleStatus( ADV ); -#ifndef SKIP_AIR_BUBBLE_CHECK - if ( ( BUBBLE_NOT_DETECTED == ADABubbleStatus ) && ( BUBBLE_NOT_DETECTED == ADVBubbleStatus ) ) -#endif + + if ( BUBBLE_NOT_DETECTED == ADVBubbleStatus ) { - zeroBloodLeak(); +#ifndef _RELEASE_ + // TODO do we need both of these? + if ( ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_BLOOD_LEAK_SELF_TEST ) != SW_CONFIG_ENABLE_VALUE ) && + ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_SELF_TESTS_AIR_BUBBLE_CHECK ) != SW_CONFIG_ENABLE_VALUE ) ) +#endif + { + zeroBloodLeak(); + } + state = WET_SELF_TESTS_BLOOD_LEAK_DETECTOR_STATE; *result = SELF_TEST_STATUS_PASSED; } @@ -1189,7 +1252,8 @@ { WET_SELF_TESTS_STATE_T state = WET_SELF_TESTS_BLOOD_LEAK_DETECTOR_STATE; - if ( SELF_TEST_STATUS_PASSED == getBloodLeakSelfTestStatus() ) + if ( ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_BLOOD_LEAK_SELF_TEST ) != SW_CONFIG_ENABLE_VALUE ) || + ( SELF_TEST_STATUS_PASSED == getBloodLeakSelfTestStatus() ) ) { settleStartTime = getMSTimerCount(); state = WET_SELF_TESTS_FIRST_DISPLACEMENT_SETUP_STATE;