Index: firmware/App/Modes/Dialysis.c =================================================================== diff -u -ra15b3ca05344fb3aeb49dc41c384345ec2651ce0 -r5f64bac99df0abbff5a30490da054e4ee7f6fa1b --- firmware/App/Modes/Dialysis.c (.../Dialysis.c) (revision a15b3ca05344fb3aeb49dc41c384345ec2651ce0) +++ firmware/App/Modes/Dialysis.c (.../Dialysis.c) (revision 5f64bac99df0abbff5a30490da054e4ee7f6fa1b) @@ -7,8 +7,8 @@ * * @file Dialysis.c * -* @author (last) Michael Garthwaite -* @date (last) 19-Jan-2023 +* @author (last) Sean Nash +* @date (last) 11-Jul-2023 * * @author (original) Sean * @date (original) 15-Jan-2020 @@ -26,6 +26,7 @@ #include "ModeTreatment.h" #include "ModeTreatmentParams.h" #include "OperationModes.h" +#include "PresOccl.h" #include "Reservoirs.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" @@ -39,16 +40,14 @@ // ********** private definitions ********** -#define MAX_UF_RATE_ML_PER_HOUR 2750.0F ///< Maximum ultrafiltration rate in mL/hour -#define MAX_UF_ACCURACY_ERROR_ML 250.0F ///< Maximum ultrafiltration accuracy error in mL over the entire treatment. +#define MAX_UF_VOLUME_ACCURACY_ERROR_ML 100.0F ///< Maximum ultrafiltration volume accuracy error in mL over the entire treatment. +#define MAX_UF_RATE_ACCURACY_ERROR_ML_MIN 10.0F ///< Maximum ultrafiltration rate accuracy error in mL/min over use of single reservoir during treatment. + /// Saline bolus data broadcast interval (ms/task time) count. static const U32 SALINE_BOLUS_DATA_PUB_INTERVAL = ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ); -/// Ultrafiltration rate accuracy check interval count. -static const U32 UF_ACCURACY_CHECK_INTERVAL = ((1 * MIN_PER_HOUR * SEC_PER_MIN * MS_PER_SECOND) / TASK_GENERAL_INTERVAL); #define MAX_SALINE_VOLUME_DELIVERED 800 ///< Maximum saline volume delivered for a treatment. #define SALINE_BOLUS_RATE_ML_MIN 300 ///< Fixed rate for saline bolus delivery. -#define MAX_BOLUS_ERROR_PCT 0.2F ///< Maximum error in saline bolus volume delivered (as a percentage of target). #define MAX_ACTIVE_LOAD_CELL_CHANGE_G 50.0F ///< Maximum delta between new and previous measured UF volume. @@ -62,16 +61,17 @@ // ********** private data ********** -static DIALYSIS_STATE_T currentDialysisState; ///< Current state of the dialysis sub-mode state machine. -static UF_STATE_T currentUFState; ///< Current state of the ultrafiltration state machine. -static SALINE_BOLUS_STATE_T currentSalineBolusState; ///< Current state of the saline bolus state machine. - +static DIALYSIS_STATE_T currentDialysisState; ///< Current state of the dialysis sub-mode state machine. +static UF_STATE_T currentUFState; ///< Current state of the ultrafiltration state machine. +static SALINE_BOLUS_STATE_T currentSalineBolusState; ///< Current state of the saline bolus state machine. static F32 refUFVolume; ///< Current reference volume for ultrafiltration (Where should we be w/r/t ultrafiltration). static F32 measUFVolume; ///< Current total measured volume for ultrafiltration (Where are we w/r/t ultrafiltration). static F32 resStartVolume[ NUM_OF_DG_RESERVOIRS ]; ///< Reservoir start volume for ultrafiltration (i.e. where did we start with each reservoir). static F32 resFinalVolume[ NUM_OF_DG_RESERVOIRS ]; ///< Reservoir final volume for ultrafiltration (i.e. where did we end after switch with each reservoir). +static F32 resStartRefVolume[ NUM_OF_DG_RESERVOIRS ]; ///< Starting ultrafiltration reference volume for reservoirs. +static F32 resFinalRefVolume[ NUM_OF_DG_RESERVOIRS ]; ///< Final ultrafiltration reference volume for reservoirs. static F32 resCurrVolume[ NUM_OF_DG_RESERVOIRS ]; ///< Reservoir current volume. -static F32 resLastVolume[ NUM_OF_DG_RESERVOIRS ]; ///< Reservoir previous volume. +static F32 resLastVolume[ NUM_OF_DG_RESERVOIRS ]; ///< Previous reading of reservoir volume. static F32 measUFVolumeFromPriorReservoirs; ///< Current total ultrafiltration volume from previous reservoirs in current treatment. static F32 lcLastSteadyWeight[NUM_OF_RESERVOIR_STEADY_CYCLES][NUM_OF_LOAD_CELLS]; ///< Load Cell Last Steady Weight for drift test Start/Final cycle @@ -80,38 +80,31 @@ static U32 setBloodFlowRate; ///< Currently set blood flow rate (from prescription). static U32 setDialysateFlowRate; ///< Currently set dialysate flow rate (from prescription). -static F32 maxUFVolumeML; ///< Currently set total ultrafiltration volume for treatment (from prescription). +static F32 setUFVolumeML; ///< Currently set total ultrafiltration volume for treatment (from prescription). static F32 setUFRate; ///< Currently set ultrafiltration rate (from prescription). static U32 salineBolusBroadcastTimerCtr; ///< Saline bolus data broadcast timer counter used to schedule when to transmit data. static BOOL salineBolusStartRequested; ///< Flag indicates a saline bolus start has been requested by user. static BOOL salineBolusAbortRequested; ///< Flag indicates a salien bolus abort has been requested by user. -static BOOL salineBolusAutoResumeUF = FALSE; ///< Flag indicates UF should be auto-resumed after saline bolus completes. +static BOOL autoResumeUF; ///< Flag indicates UF should be auto-resumed after saline bolus completes. static F32 totalSalineVolumeDelivered_mL; ///< Volume (mL) in total of saline delivered so far (cumulative for all boluses including current one). static F32 bolusSalineVolumeDelivered_mL; ///< Volume (mL) of current bolus delivered so far (calculated from measured blood flow rate). -static F32 expectedSalineBolusVolume_mL; ///< Volume (mL) of current bolus delivered so far (calculated from target blood flow rate). static U32 bolusSalineLastVolumeTimeStamp; ///< Time stamp for last saline volume update. -static U32 uFAccuracyCheckTimerCtr; ///< Timer counter to determine when next to check ultrafiltration accuracy. -static F32 lastUFVolumeChecked; ///< Starting ultrafiltration volume for accuracy check. - // ********** private function prototypes ********** static DIALYSIS_STATE_T handleDialysisUltrafiltrationState( void ); static DIALYSIS_STATE_T handleDialysisSalineBolusState( void ); -static UF_STATE_T handleUFStartState( DIALYSIS_STATE_T *dialysisState ); static UF_STATE_T handleUFPausedState( DIALYSIS_STATE_T *dialysisState ); static UF_STATE_T handleUFRunningState( DIALYSIS_STATE_T *dialysisState ); -static SALINE_BOLUS_STATE_T handleSalineBolusIdleState( DIALYSIS_STATE_T *dialysisState ); -static SALINE_BOLUS_STATE_T handleSalineBolusWait4Pumps2Stop( DIALYSIS_STATE_T *dialysisState ); +static SALINE_BOLUS_STATE_T handleSalineBolusIdleState( void ); +static SALINE_BOLUS_STATE_T handleSalineBolusWait4Pumps2Stop( void ); static SALINE_BOLUS_STATE_T handleSalineBolusInProgressState( DIALYSIS_STATE_T *dialysisState ); static SALINE_BOLUS_STATE_T handleSalineBolusMaxDeliveredState( DIALYSIS_STATE_T *dialysisState ); -static void startHeparinPump( void ); - -static void checkUFAccuracyAndVolume( void ); +static void checkUFControl( void ); static void updateUFVolumes( void ); static void publishSalineBolusData( void ); @@ -130,35 +123,46 @@ { U16 i; - currentDialysisState = DIALYSIS_START_STATE; - currentUFState = UF_START_STATE; + currentDialysisState = DIALYSIS_UF_STATE; + currentUFState = UF_RUNNING_STATE; currentSalineBolusState = SALINE_BOLUS_STATE_IDLE; + lastUFTimeStamp = getMSTimerCount(); + uFTimeMS = 0; + refUFVolume = 0.0; measUFVolume = 0.0; measUFVolumeFromPriorReservoirs = 0.0; - // Send reset UF volumes to dialysate outlet pump - setDialOutUFVolumes( refUFVolume, measUFVolume ); + setDialOutUFVolumes( refUFVolume, measUFVolume ); // Send reset UF volumes to dialysate outlet pump driver uFTimeMS = 0; lastUFTimeStamp = 0; setBloodFlowRate = 0; setDialysateFlowRate = 0; - maxUFVolumeML = 0.0; + setUFVolumeML = 0.0; setUFRate = 0.0; + resetDialOutRateOffset(); salineBolusBroadcastTimerCtr = 0; totalSalineVolumeDelivered_mL = 0.0; + autoResumeUF = FALSE; - uFAccuracyCheckTimerCtr = 0; - lastUFVolumeChecked = 0.0; - - for (i=0; i= maxUFVolumeML ) + if ( refUFVolume >= setUFVolumeML ) // TODO - is this something we want to do or should we just let UF continue? { setUFRate = 0.0; } - // Calculate UF volumes and provide to dialysate outlet pump controller - updateUFVolumes(); - // Handle saline bolus start request from user if ( TRUE == salineBolusStartRequested ) { if ( SALINE_BOLUS_STATE_IDLE == currentSalineBolusState ) { // Since we were doing UF prior to saline bolus, we want to auto-resume when done - salineBolusAutoResumeUF = TRUE; + autoResumeUF = TRUE; // Go to UF paused state result = UF_PAUSED_STATE; // Go to saline bolus state *dialysisState = DIALYSIS_SALINE_BOLUS_STATE; + setCurrent4thLevelState( (U32)currentSalineBolusState ); } else { @@ -864,10 +849,9 @@ * saline bolus state machine. * @details Inputs: none * @details Outputs: - * @param dialysisState next dialysis state * @return next saline bolus state *************************************************************************/ -static SALINE_BOLUS_STATE_T handleSalineBolusIdleState( DIALYSIS_STATE_T *dialysisState ) +static SALINE_BOLUS_STATE_T handleSalineBolusIdleState( void ) { SALINE_BOLUS_STATE_T result = SALINE_BOLUS_STATE_IDLE; @@ -893,18 +877,16 @@ * to stop state of the saline bolus state machine. * @details Inputs: none * @details Outputs: - * @param dialysisState next dialysis state * @return next saline bolus state *************************************************************************/ -static SALINE_BOLUS_STATE_T handleSalineBolusWait4Pumps2Stop( DIALYSIS_STATE_T *dialysisState ) +static SALINE_BOLUS_STATE_T handleSalineBolusWait4Pumps2Stop( void ) { SALINE_BOLUS_STATE_T result = SALINE_BOLUS_STATE_WAIT_FOR_PUMPS_STOP; if ( ( FALSE == isBloodPumpRunning() ) && ( FALSE == isDialInPumpRunning() ) && ( FALSE == isDialOutPumpRunning() ) ) { // Reset bolus data before we start bolusSalineVolumeDelivered_mL = 0.0; - expectedSalineBolusVolume_mL = 0.0; bolusSalineLastVolumeTimeStamp = getMSTimerCount(); // Bypass dialyzer @@ -938,7 +920,6 @@ BOOL errorFound = FALSE; F32 timeSinceLastVolumeUpdateMin = (F32)calcTimeSince( bolusSalineLastVolumeTimeStamp ) / (F32)( MS_PER_SECOND * SEC_PER_MIN ); F32 bolusTargetVolume = (F32)getTreatmentParameterU32( TREATMENT_PARAM_SALINE_BOLUS_VOLUME ); - F32 maxBolusErrorMl = bolusTargetVolume * MAX_BOLUS_ERROR_PCT; F32 bldFlowRate = getMeasuredBloodFlowRate(); F32 volSinceLastUpdateMl = bldFlowRate * timeSinceLastVolumeUpdateMin; F32 expVolSinceLastUpdateMl = (F32)getTargetBloodFlowRate() * timeSinceLastVolumeUpdateMin; @@ -947,12 +928,11 @@ bolusSalineLastVolumeTimeStamp = getMSTimerCount(); bolusSalineVolumeDelivered_mL += volSinceLastUpdateMl; totalSalineVolumeDelivered_mL += volSinceLastUpdateMl; - expectedSalineBolusVolume_mL += expVolSinceLastUpdateMl; // Check for empty saline bag per arterial line pressure if ( TRUE == isSalineBagEmpty() ) { - SET_ALARM_WITH_1_F32_DATA( ALARM_ID_EMPTY_SALINE_BAG, getMeasuredArterialPressure() ); + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_HD_EMPTY_SALINE_BAG, getMeasuredArterialPressure() ); errorFound = TRUE; result = SALINE_BOLUS_STATE_IDLE; } @@ -961,6 +941,8 @@ if ( ( totalSalineVolumeDelivered_mL >= (F32)MAX_SALINE_VOLUME_DELIVERED ) ) { result = SALINE_BOLUS_STATE_MAX_DELIVERED; + setCurrent4thLevelState( (U32)result ); + sendOperationStatusEvent(); } else { @@ -975,18 +957,6 @@ salineBolusAbortRequested = FALSE; result = SALINE_BOLUS_STATE_IDLE; } - // Determine if saline bolus is under/over delivering - else if ( fabs( expectedSalineBolusVolume_mL - bolusSalineVolumeDelivered_mL ) > maxBolusErrorMl ) - { -#ifndef _RELEASE_ - if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_PUMP_FLOW_CHECKS ) != SW_CONFIG_ENABLE_VALUE ) -#endif - { - SET_ALARM_WITH_2_F32_DATA( ALARM_ID_SALINE_BOLUS_VOLUME_CHECK_FAILURE, bolusSalineVolumeDelivered_mL, expectedSalineBolusVolume_mL ); - errorFound = TRUE; - result = SALINE_BOLUS_STATE_IDLE; - } - } } // Are we stopping the bolus? @@ -995,6 +965,8 @@ // Hard stop blood and dialysate pumps signalBloodPumpHardStop(); signalDialInPumpHardStop(); + // Tell DG to stop heating dialysate + cmdStopDGTrimmerHeater(); // Send last saline bolus data salineBolusBroadcastTimerCtr = SALINE_BOLUS_DATA_PUB_INTERVAL; publishSalineBolusData(); @@ -1006,11 +978,17 @@ if ( FALSE == errorFound ) { // Resume UF if appropriate - if ( TRUE == salineBolusAutoResumeUF ) + if ( TRUE == autoResumeUF ) { - salineBolusAutoResumeUF = FALSE; + autoResumeUF = FALSE; currentUFState = UF_RUNNING_STATE; + //Set substate for event + setCurrentSubState( (U32)DIALYSIS_UF_STATE ); + setCurrent4thLevelState( (U32)currentUFState ); + sendOperationStatusEvent(); + } + signalInitiatePressureStabilization(); // Resume dialysis transitionToDialysis(); } @@ -1064,53 +1042,33 @@ /*********************************************************************//** * @brief - * The checkUF function checks ultrafiltration accuracy for the last - * hour and checks total UF volume. Triggers an alarm if out of spec. - * @details Inputs: uFAccuracyCheckTimerCtr, lastUFVolumeChecked, measUFVolume - * @details Outputs: uFAccuracyCheckTimerCtr, lastUFVolumeChecked + * The checkUFControl function checks ultrafiltration control to ensure measured + * UF volume does not deviate too far from the UF reference volume (indicating + * poor UF control). + * @details Inputs: none + * @details Outputs: none * @return none *************************************************************************/ -static void checkUFAccuracyAndVolume( void ) +static void checkUFControl( void ) { - F32 uFMeasRate = measUFVolume - lastUFVolumeChecked; // Volumes are at start/end of 1 hour period, so implied rate is per hour - - // Check UF rate over last hour - if ( uFMeasRate > MAX_UF_RATE_ML_PER_HOUR ) - { -#ifndef _RELEASE_ - if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_ULTRAFILTRATION_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) -#endif - { - SET_ALARM_WITH_1_F32_DATA( ALARM_ID_UF_RATE_TOO_HIGH_ERROR, uFMeasRate ); - } - } - // Increment timer and see if time to start another 1 hour check period - if ( ++uFAccuracyCheckTimerCtr >= UF_ACCURACY_CHECK_INTERVAL ) - { - // Reset for next check interval - lastUFVolumeChecked = measUFVolume; - uFAccuracyCheckTimerCtr = 0; - } - // Check total UF volume error - if ( ( fabs( refUFVolume - measUFVolume ) ) >= (F32)MAX_UF_ACCURACY_ERROR_ML ) + if ( ( fabs( refUFVolume - measUFVolume ) ) > MAX_UF_VOLUME_ACCURACY_ERROR_ML ) { #ifndef _RELEASE_ if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_ULTRAFILTRATION_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) #endif { - SET_ALARM_WITH_2_F32_DATA( ALARM_ID_UF_VOLUME_ACCURACY_ERROR, refUFVolume, measUFVolume ); + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_UF_VOLUME_ACCURACY_ERROR, refUFVolume, measUFVolume ); } } } /*********************************************************************//** * @brief - * The updateUFVolumes function updates the ultrafiltration volumes based on - * set UF rate, latest UF elapsed time, and the latest load cell weight for the - * currently used reservoir. Updated UF volumes are then sent to the dialysate - * outlet pump controller. - * @details Inputs: setUFRate, uFTimeMS, load cell weight + * The updateUFVolumes function updates the measured ultrafiltration volume based on + * the latest load cell weight for the currently used reservoir. Updated UF volumes + * are then sent to the dialysate outlet pump controller. + * @details Inputs: measUFVolumeFromPriorReservoirs, resStartVolume[], load cell weight * @details Outputs: refUFVolume, measUFVolume * @return none *************************************************************************/ @@ -1159,6 +1117,7 @@ // Set starting baseline volume for next reservoir before we switch to it resStartVolume[ reservoirID ] = resVolume; resFinalVolume[ reservoirID ] = resVolume; + resStartRefVolume[ reservoirID ] = refUFVolume; checkLoadCellsStablePrimaryBackupDriftOutOfRange( reservoirID, RESERVOIR_STEADY_CYCLE_START ); } @@ -1178,6 +1137,9 @@ // Update UF volume from prior reservoirs per tentative res volume for last reservoir measUFVolumeFromPriorReservoirs += ( resFinalVolume[ inactiveRes ] - resStartVolume[ inactiveRes ] ); + + // Record final UF ref volume for the spent reservoir + resFinalRefVolume[ inactiveRes ] = refUFVolume; } /*********************************************************************//** @@ -1193,12 +1155,25 @@ { DG_RESERVOIR_ID_T inactiveRes = getDGInactiveReservoir(); F32 resVolume = getReservoirWeightLargeFilter( inactiveRes ); + F32 resUFTimeInMs = (F32)getLastReservoirUFTimeInMs(); + F32 resUFTimeInMin = ( resUFTimeInMs / (F32)( SEC_PER_MIN * MS_PER_SECOND ) ); + F32 resExpUFVolumeInMl = resFinalRefVolume[ inactiveRes ] - resStartRefVolume[ inactiveRes ]; + F32 resExpUFRate = resExpUFVolumeInMl / resUFTimeInMin; + F32 uFResVolumeInMl; + F32 uFMeasRate; // Update UF volume from prior reservoirs per final res volume for last reservoir a bit after we have switched and reservoir has settled measUFVolumeFromPriorReservoirs -= ( resFinalVolume[ inactiveRes ] - resStartVolume[ inactiveRes ] ); resFinalVolume[ inactiveRes ] = resVolume; - measUFVolumeFromPriorReservoirs += ( resFinalVolume[ inactiveRes ] - resStartVolume[ inactiveRes ] ); + uFResVolumeInMl = ( resFinalVolume[ inactiveRes ] - resStartVolume[ inactiveRes ] ); + measUFVolumeFromPriorReservoirs += uFResVolumeInMl; + // Calc UF rate from reservoir weight change + uFMeasRate = uFResVolumeInMl / resUFTimeInMin; + SEND_EVENT_WITH_2_F32_DATA( HD_EVENT_RSRVR_UF_VOLUME_AND_TIME, uFResVolumeInMl, resUFTimeInMin ) + SEND_EVENT_WITH_2_F32_DATA( HD_EVENT_RSRVR_UF_RATE, uFMeasRate, resExpUFRate ) + + // Check redundant load cells checkLoadCellsStablePrimaryBackupDriftOutOfRange( inactiveRes, RESERVOIR_STEADY_CYCLE_FINAL ); }