Index: firmware/App/Modes/Prime.c =================================================================== diff -u -r45803ac5779e6345d2a52584bbf7a7817d2f54f7 -r2d354e7ca9b55c5bb4fe679c8dd43fcbafd53d42 --- firmware/App/Modes/Prime.c (.../Prime.c) (revision 45803ac5779e6345d2a52584bbf7a7817d2f54f7) +++ firmware/App/Modes/Prime.c (.../Prime.c) (revision 2d354e7ca9b55c5bb4fe679c8dd43fcbafd53d42) @@ -44,17 +44,18 @@ #define BLOOD_PUMP_FLOW_RATE_CIRC_BLOOD_CIRCUIT_ML_MIN 300 ///< Blood pump flow rate during prime recirculate blood circuit state. #define DIALYSATE_PUMP_PRIME_FLOW_RATE_ML_MIN 300 ///< Dialysate pump flow rate during priming fluid path. -#define LOAD_CELL_VOLUME_NOISE_TOLERANCE 0.05 ///< Allow 5% tolerance on load cell readings. -#define PRIME_SALINE_DIALYZER_STOP_WEIGHT 50.0 // 10.0 try 200.0 11/19/2021 Sean ///< Load cell readings of 10 grams ( 10 mL ) used in prime saline dialyzer state - +#define LOAD_CELL_VOLUME_NOISE_TOLERANCE 5.00 ///< Allow 5 mL tolerance on load cell readings. +#define PRIME_SALINE_DIALYZER_STOP_WEIGHT 50.0 ///< Load cell readings of 10 grams ( 10 mL ) used in prime saline dialyzer state + ///< Currently, we use 50.0 mL for testing. Remove this comment and replace 50.0 with 10.0 before merge to develop branch #define NO_AIR_DETECTED_COUNT ( 20 * MS_PER_SECOND ) ///< No air detected time period count. #define PURGE_AIR_TIME_OUT_COUNT ( 60 * MS_PER_SECOND ) ///< Time period count for purge air time out. -#define MIN_LOAD_CELL_STEADY_VOLUME_TIME ( 10 * MS_PER_SECOND ) ///< Minimum time load cell reading need to remain steady in ms. +#define LOAD_CELL_STEADY_VOLUME_SAMPLING_TIME ( 1 * MS_PER_SECOND ) ///< Time load cell reading steady state detection sampling time in seconds. #define PRIME_DIALYSATE_DIALYZER_TIME_LIMIT ( 120 * MS_PER_SECOND ) ///< Time limit for priming dialysate dialyzer circuit. #define PRIME_DIALYSATE_BYPASS_TIME_LIMIT ( 120 * MS_PER_SECOND ) ///< Time limit for priming dialysate bypass circuit. #define PRIME_SALINE_DIALYZER_TIME_LIMIT ( 300 * MS_PER_SECOND ) ///< Five minutes time limit for priming saline dialyzer path. +#define CIRC_BUF_SIZE 10 ///< Size of the circular buffer. Note: the size is not 2^N where N = 1, 2 , 3, 4, 5 ... +#define MAX_BUF_INDEX ( CIRC_BUF_SIZE - 1 ) ///< Use to calculate write index of the circular buffer. - /// States of the treatment reservoir management state machine. typedef enum PrimeReservoirMgmt_States { @@ -88,10 +89,11 @@ static U32 primeSalineDialyzerStartTime; ///< Starting time of priming saline dialyzer circuit. static U32 primeDialysateDialyzerStartTime; ///< Starting time of priming dialysate dialyzer circuit. static U32 primeDialysateBypassStartTime; ///< Starting time of priming dialysate bypass circuit. -static U32 previousLoadCellReading; ///< Previous load cell reading. static U32 primeSalineDializerInitialLoadCellReading; ///< Initial load cell reading in the prime saline dialyzer state. static U32 loadcellSteadyVolumeStartTime; ///< Load cell steady volume starting time. static BOOL runBloodCircuitPrimeAgain; ///< Flag indicates HD should run blood circuit prime once more time. +static F32 loadcellReadings[ CIRC_BUF_SIZE ]; ///< Storage for 10 seconds of load cell weight readings. +static U32 iteration; ///< Use to calculate the write and the read index of the circular buffer. // ********** private function prototypes ********** @@ -478,9 +480,9 @@ /*********************************************************************//** * @brief - * The handlePrimeSalineSetupState function checks user's request to start - * priming. - * @details Inputs: primeStartReqReceived + * The handlePrimeSalineSetupState function setups the Pre-Treatment Prime + * Saline Purge Air state. + * @details Inputs: state * @details Outputs: control valves to purge air * @return current state *************************************************************************/ @@ -605,7 +607,13 @@ setValvePosition( VBV, VALVE_POSITION_C_CLOSE ); setValveAirTrap( STATE_CLOSED ); - previousLoadCellReading = 0; + iteration = 0; // initialize to zero before use + U32 bufIndex; + for ( bufIndex = 0; bufIndex < CIRC_BUF_SIZE; bufIndex++ ) + { + loadcellReadings[ bufIndex ] = getLoadCellWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); + } + primeDialysateBypassStartTime = getMSTimerCount(); loadcellSteadyVolumeStartTime = getMSTimerCount(); @@ -630,25 +638,29 @@ * @brief * The handlePrimeDialysateDialyzerState function handles priming for * dialysate dialyzer fluid path. - * @details Inputs: reservoir 2 filtered weight + * @details Inputs: reservoir 1 filtered weight * @details Outputs: primed dialysate bypass fluid path * @return current state *************************************************************************/ static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeDialysateBypassState( void ) { HD_PRE_TREATMENT_PRIME_STATE_T state = HD_PRIME_DIALYSATE_BYPASS_STATE; - F32 const loadcellWeight = getLoadCellWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); - F32 const weightChange = fabs( 1.0 - ( previousLoadCellReading / loadcellWeight ) ); - if ( weightChange < LOAD_CELL_VOLUME_NOISE_TOLERANCE ) + if ( TRUE == didTimeout( loadcellSteadyVolumeStartTime, LOAD_CELL_STEADY_VOLUME_SAMPLING_TIME ) ) { - if ( TRUE == didTimeout( loadcellSteadyVolumeStartTime, MIN_LOAD_CELL_STEADY_VOLUME_TIME ) ) + F32 const currentLoadCellValue = getLoadCellWeight( LOAD_CELL_RESERVOIR_1_PRIMARY ); + F32 const previousLoadCellValue = loadcellReadings[ iteration % CIRC_BUF_SIZE ]; + F32 const loadcellDelta = fabs( currentLoadCellValue - previousLoadCellValue ); // delta of the current (now) and the previous (10 seconds ago) + loadcellReadings[ ( iteration + MAX_BUF_INDEX ) % CIRC_BUF_SIZE ] = currentLoadCellValue; // see Circular Buffer.xlsx in X:\Engineering\Denali\06- Software Design\Story\HD\HD Pre-Treatment Prime Update + iteration++; // iteration will not roll over in 136 years at one second increment + + if ( loadcellDelta < LOAD_CELL_VOLUME_NOISE_TOLERANCE ) // TODO: also check for the expected reservoir volume here { #ifdef RUN_BLOOD_CIRCUIT_PRIME_AGAIN - if ( TRUE == runBloodCircuitPrimeAgain ) // TODO: Determine if we still need to go to HD_PRIME_SALINE_SETUP_STATE the first time - { // since a new state HD_PRIME_SALINE_DIALYZER_STATE has been added + if ( TRUE == runBloodCircuitPrimeAgain ) + { runBloodCircuitPrimeAgain = FALSE; - state = HD_PRIME_SALINE_SETUP_STATE; // should we go to HD_PRIME_SALINE_SETUP_STATE or HD_PRIME_SALINE_DIALYZER_SETUP_STATE? + state = HD_PRIME_SALINE_SETUP_STATE; } else { @@ -658,12 +670,9 @@ state = HD_PRIME_RESERVOIR_TWO_FILL_COMPLETE_STATE; #endif } + + loadcellSteadyVolumeStartTime = getMSTimerCount(); // re-armed the timer for the next iteration } - else - { - previousLoadCellReading = loadcellWeight; - loadcellSteadyVolumeStartTime = getMSTimerCount(); - } if ( TRUE == didTimeout( primeDialysateBypassStartTime, PRIME_DIALYSATE_BYPASS_TIME_LIMIT ) ) { @@ -706,10 +715,15 @@ setDialInPumpTargetFlowRate( DIALYSATE_PUMP_PRIME_FLOW_RATE_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); setDialOutPumpTargetRate( DIALYSATE_PUMP_PRIME_FLOW_RATE_ML_MIN, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); - previousLoadCellReading = 0; - loadcellSteadyVolumeStartTime = getMSTimerCount(); - primeDialysateDialyzerStartTime = getMSTimerCount(); + iteration = 0; // initialize to zero before use + U32 bufIndex; + for ( bufIndex = 0; bufIndex < CIRC_BUF_SIZE; bufIndex++ ) + { + loadcellReadings[ bufIndex ] = getLoadCellWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); + } + loadcellSteadyVolumeStartTime = getMSTimerCount(); + primeDialysateDialyzerStartTime = getMSTimerCount(); state = HD_PRIME_DIALYSATE_DIALYZER_STATE; } } @@ -727,46 +741,31 @@ * @brief * The handlePrimeDialysateDialyzerState function handles priming for * dialysate dialyzer fluid path. - * @details Inputs: reservoir 1 filtered weight + * @details Inputs: reservoir 2 filtered weight * @details Outputs: primed dialysate dialyzer fluid path * @return current state *************************************************************************/ static HD_PRE_TREATMENT_PRIME_STATE_T handlePrimeDialysateDialyzerState( void ) { HD_PRE_TREATMENT_PRIME_STATE_T state = HD_PRIME_DIALYSATE_DIALYZER_STATE; - F32 const loadcellWeight = getLoadCellWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); - - F32 const weightChange = fabs( 1.0 - ( previousLoadCellReading / loadcellWeight ) ); - - if ( weightChange < LOAD_CELL_VOLUME_NOISE_TOLERANCE ) + if ( TRUE == didTimeout( loadcellSteadyVolumeStartTime, LOAD_CELL_STEADY_VOLUME_SAMPLING_TIME ) ) { - if ( TRUE == didTimeout( loadcellSteadyVolumeStartTime, MIN_LOAD_CELL_STEADY_VOLUME_TIME ) ) + F32 const currentLoadCellValue = getLoadCellWeight( LOAD_CELL_RESERVOIR_2_PRIMARY ); + F32 const previousLoadCellValue = loadcellReadings[ iteration % CIRC_BUF_SIZE ]; + F32 const loadcellDelta = fabs( currentLoadCellValue - previousLoadCellValue ); + loadcellReadings[ ( iteration + MAX_BUF_INDEX ) % CIRC_BUF_SIZE ] = currentLoadCellValue; + iteration++; + + if ( loadcellDelta < LOAD_CELL_VOLUME_NOISE_TOLERANCE ) // TODO: also check for the expected reservoir volume here { - signalDialOutPumpHardStop(); // this is needed since dialyzer and dialyzer bypass states were swapped + signalDialOutPumpHardStop(); transitionToWetSelfTests(); state = HD_PRIME_WET_SELF_TESTS_STATE; - - /* - if ( TRUE == runBloodCircuitPrimeAgain ) - { - runBloodCircuitPrimeAgain = FALSE; - state = HD_PRIME_SALINE_SETUP_STATE; - } - else - { - //state = HD_PRIME_RESERVOIR_TWO_FILL_COMPLETE_STATE; - transitionToWetSelfTests(); - state = HD_PRIME_WET_SELF_TESTS_STATE; - } - */ } + + loadcellSteadyVolumeStartTime = getMSTimerCount(); // re-armed the timer for the next iteration } - else - { - previousLoadCellReading = loadcellWeight; - loadcellSteadyVolumeStartTime = getMSTimerCount(); - } if ( TRUE == didTimeout( primeDialysateDialyzerStartTime, PRIME_DIALYSATE_DIALYZER_TIME_LIMIT ) ) {