Index: firmware/App/Modes/Dialysis.c =================================================================== diff -u -r68aefeff8890cdfa956c7bfdf0d4505b4ac25cb7 -r8b73263b38f449dacc0795c67a7cf6240cb79026 --- firmware/App/Modes/Dialysis.c (.../Dialysis.c) (revision 68aefeff8890cdfa956c7bfdf0d4505b4ac25cb7) +++ firmware/App/Modes/Dialysis.c (.../Dialysis.c) (revision 8b73263b38f449dacc0795c67a7cf6240cb79026) @@ -1,14 +1,14 @@ /************************************************************************** * -* Copyright (c) 2019-2022 Diality Inc. - All Rights Reserved. +* Copyright (c) 2020-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 Dialysis.c * -* @author (last) Sean Nash -* @date (last) 12-Nov-2021 +* @author (last) Dara Navaei +* @date (last) 15-Jul-2022 * * @author (original) Sean * @date (original) 15-Jan-2020 @@ -39,18 +39,18 @@ // ********** private definitions ********** -#define MAX_UF_RATE_ML_PER_HOUR 2750.0 ///< Maximum ultrafiltration rate in mL/hour -#define MAX_UF_ACCURACY_ERROR_ML 250.0 ///< Maximum ultrafiltration accuracy error in mL over the entire treatment. +#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. /// 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 150 ///< Fixed rate for saline bolus delivery. -#define MAX_BOLUS_ERROR_PCT 0.2 ///< Maximum error in saline bolus volume delivered (as a percentage of target). +#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.0 ///< Maximum delta between new and previous measured UF volume. +#define MAX_ACTIVE_LOAD_CELL_CHANGE_G 50.0F ///< Maximum delta between new and previous measured UF volume. // ********** private data ********** @@ -65,6 +65,7 @@ static F32 resCurrVolume[ NUM_OF_DG_RESERVOIRS ]; ///< Reservoir current volume. static F32 resLastVolume[ NUM_OF_DG_RESERVOIRS ]; ///< Reservoir previous volume. static F32 measUFVolumeFromPriorReservoirs; ///< Current total ultrafiltration volume from previous reservoirs in current treatment. +static F32 lcLastSteadyWeight[NUM_OF_LOAD_CELLS]; ///< Load Cell Last Steady Weight for drift test static U32 uFTimeMS; ///< Current elapsed ultrafiltration time (in ms). Used for calculating UF reference volume. static U32 lastUFTimeStamp; ///< HD timer value when we last took stock of ultrafiltration time (so we can determine how much time has elapsed since). @@ -106,6 +107,7 @@ static void updateUFVolumes( void ); static void publishSalineBolusData( void ); +static void checkLoadCellsStablePrimaryBackupDriftOutOfRange( DG_RESERVOIR_ID_T reservoirID ); /*********************************************************************//** * @brief @@ -118,6 +120,8 @@ *************************************************************************/ void initDialysis( void ) { + U16 i; + currentDialysisState = DIALYSIS_START_STATE; currentUFState = UF_START_STATE; currentSalineBolusState = SALINE_BOLUS_STATE_IDLE; @@ -142,6 +146,10 @@ uFAccuracyCheckTimerCtr = 0; lastUFVolumeChecked = 0.0; + for (i=0; i (F32)preStop ) && ( HEPARIN_STATE_STOPPED == currentHeparinState ) ) { - // If not done with bolus, start/resume bolus - if ( ( bolusVol > 0.0 ) && ( getSyringePumpVolumeDelivered() < bolusVol ) ) + if ( hepRate > 0.0 ) { - startHeparinBolus(); // TODO - check return status or have function trigger alarm if not successful + startHeparinContinuous(); // TODO - check return status } - // Otherwise, start/resume continuous delivery else { - if ( hepRate > 0.0 ) - { - startHeparinContinuous(); // TODO - check return status - } - else - { - setHeparinCompleted(); - } + setHeparinCompleted(); } } else @@ -295,21 +302,24 @@ // Make rate changes in real time if currently performing dialysis. if ( ( TREATMENT_DIALYSIS_STATE == getTreatmentState() ) && ( getDialysisState() != DIALYSIS_SALINE_BOLUS_STATE ) ) { -#ifndef RUN_PUMPS_OPEN_LOOP -#ifndef RUN_BP_OPEN_LOOP - setBloodPumpTargetFlowRate( setBloodFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); -#else - setBloodPumpTargetFlowRate( setBloodFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + PUMP_CONTROL_MODE_T mode = PUMP_CONTROL_MODE_CLOSED_LOOP; + +#ifndef _RELEASE_ + if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_ENABLE_BLOOD_PUMP_OPEN_LOOP ) ) + { + mode = PUMP_CONTROL_MODE_OPEN_LOOP; + } #endif -#ifndef RUN_DPI_OPEN_LOOP - setDialInPumpTargetFlowRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); -#else - setDialInPumpTargetFlowRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + setBloodPumpTargetFlowRate( setBloodFlowRate, MOTOR_DIR_FORWARD, mode ); + +#ifndef _RELEASE_ + if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_ENABLE_DIALYSATE_INLET_PUMP_OPEN_LOOP ) ) + { + mode = PUMP_CONTROL_MODE_OPEN_LOOP; + } #endif -#else - setBloodPumpTargetFlowRate( setBloodFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); - setDialInPumpTargetFlowRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); -#endif + setDialInPumpTargetFlowRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, mode ); + setDialOutPumpTargetRate( setDialysateFlowRate + (S32)setUFRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); } } @@ -511,11 +521,12 @@ ( DIALYSIS_UF_STATE == currentDialysisState ) && ( UF_RUNNING_STATE == currentUFState ) ) { result = TRUE; + sendTreatmentLogEventData( UF_PAUSE_EVENT, setUFRate, 0.0 ); // Set outlet pump to dialysate rate setDialOutPumpTargetRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); // Go to UF paused state currentUFState = UF_PAUSED_STATE; - sendTreatmentLogEventData( UF_PAUSE_RESUME_EVENT, UF_RUNNING_STATE, UF_PAUSED_STATE ); + sendTreatmentLogEventData( UF_START_RESUME_EVENT, UF_RUNNING_STATE, UF_PAUSED_STATE ); } else { @@ -561,13 +572,14 @@ ( DIALYSIS_UF_STATE == currentDialysisState ) && ( UF_PAUSED_STATE == currentUFState ) ) { result = TRUE; + sendTreatmentLogEventData( UF_START_RESUME_EVENT, 0.0, setUFRate ); // Set outlet pump to dialysate rate + set UF rate setDialOutPumpTargetRate( setDialysateFlowRate + (S32)setUFRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); // Restart UF time accumulation for reference volume calculation lastUFTimeStamp = getMSTimerCount(); // Go to UF running state currentUFState = UF_RUNNING_STATE; - sendTreatmentLogEventData( UF_PAUSE_RESUME_EVENT, UF_PAUSED_STATE, UF_RUNNING_STATE ); + sendTreatmentLogEventData( UF_START_RESUME_EVENT, UF_PAUSED_STATE, UF_RUNNING_STATE ); } else { @@ -951,15 +963,18 @@ salineBolusAbortRequested = FALSE; result = SALINE_BOLUS_STATE_IDLE; } -#ifndef DISABLE_PUMP_FLOW_CHECKS // Determine if saline bolus is under/over delivering else if ( fabs( expectedSalineBolusVolume_mL - bolusSalineVolumeDelivered_mL ) > maxBolusErrorMl ) { - activateAlarmNoData( ALARM_ID_SALINE_BOLUS_VOLUME_CHECK_FAILURE ); - errorFound = TRUE; - result = SALINE_BOLUS_STATE_IDLE; - } +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_PUMP_FLOW_CHECKS ) != SW_CONFIG_ENABLE_VALUE ) #endif + { + activateAlarmNoData( ALARM_ID_SALINE_BOLUS_VOLUME_CHECK_FAILURE ); + errorFound = TRUE; + result = SALINE_BOLUS_STATE_IDLE; + } + } } // Are we stopping the bolus? @@ -1050,9 +1065,12 @@ // Check UF rate over last hour if ( uFMeasRate > MAX_UF_RATE_ML_PER_HOUR ) { -#ifndef DISABLE_UF_ALARMS - SET_ALARM_WITH_1_F32_DATA( ALARM_ID_UF_RATE_TOO_HIGH_ERROR, uFMeasRate ); +#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 ) @@ -1065,9 +1083,12 @@ // Check total UF volume error if ( ( fabs( refUFVolume - measUFVolume ) ) >= (F32)MAX_UF_ACCURACY_ERROR_ML ) { -#ifndef DISABLE_UF_ALARMS - SET_ALARM_WITH_2_F32_DATA( ALARM_ID_UF_VOLUME_ACCURACY_ERROR, refUFVolume, measUFVolume ); +#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 ); + } } } @@ -1086,17 +1107,21 @@ DG_RESERVOIR_ID_T activeRes = getDGActiveReservoir(); LOAD_CELL_ID_T loadCell = ( activeRes == DG_RESERVOIR_1 ? LOAD_CELL_RESERVOIR_1_PRIMARY : LOAD_CELL_RESERVOIR_2_PRIMARY ); F32 latestResVolume = getLoadCellWeight( loadCell ); -#ifndef DISABLE_UF_ALARMS F32 deltaVolume = latestResVolume - resLastVolume[ activeRes ]; // ensure volume change is not too excessive - indication that load cell was impacted by some kind of shock if ( fabs(deltaVolume) > MAX_ACTIVE_LOAD_CELL_CHANGE_G ) { - ALARM_ID_T deltaAlarm = ( getDGActiveReservoir() == DG_RESERVOIR_1 ? ALARM_ID_HD_LOAD_CELL_ACCELERATION_RES_1_ALARM : ALARM_ID_HD_LOAD_CELL_ACCELERATION_RES_2_ALARM ); - SET_ALARM_WITH_1_F32_DATA( deltaAlarm, deltaVolume ); +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_ULTRAFILTRATION_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) +#endif + { + ALARM_ID_T deltaAlarm = ( getDGActiveReservoir() == DG_RESERVOIR_1 ? ALARM_ID_HD_LOAD_CELL_ACCELERATION_RES_1_ALARM : + ALARM_ID_HD_LOAD_CELL_ACCELERATION_RES_2_ALARM ); + SET_ALARM_WITH_1_F32_DATA( deltaAlarm, deltaVolume ); + } } else -#endif { // Calculate UF volumes and provide to dialysate outlet pump controller measUFVolume = measUFVolumeFromPriorReservoirs + ( latestResVolume - resStartVolume[ activeRes ] ); @@ -1122,6 +1147,8 @@ // Set starting baseline volume for next reservoir before we switch to it resStartVolume[ reservoirID ] = resVolume; resFinalVolume[ reservoirID ] = resVolume; + + checkLoadCellsStablePrimaryBackupDriftOutOfRange( reservoirID ); } /*********************************************************************//** @@ -1159,6 +1186,8 @@ measUFVolumeFromPriorReservoirs -= ( resFinalVolume[ inactiveRes ] - resStartVolume[ inactiveRes ] ); resFinalVolume[ inactiveRes ] = resVolume; measUFVolumeFromPriorReservoirs += ( resFinalVolume[ inactiveRes ] - resStartVolume[ inactiveRes ] ); + + checkLoadCellsStablePrimaryBackupDriftOutOfRange( inactiveRes ); } /*********************************************************************//** @@ -1193,4 +1222,53 @@ return ( resFinalVolume[ reservoirID ] - resStartVolume[ reservoirID ] ); } +/*********************************************************************//** + * @brief + * The checkLoadCellsStablePrimaryBackupDriftOutOfRange function checks the + * load cells' primary and backup drift when the reservoir level has been stable + * for greater than large filter time. + * @details Inputs: none + * @details Outputs: none + * @param reservoirID which is the reservoir ID that is range checked + * @return none + *************************************************************************/ +static void checkLoadCellsStablePrimaryBackupDriftOutOfRange( DG_RESERVOIR_ID_T reservoirID ) +{ + F32 loadCellPrimaryWeight, loadCellBackupWeight; + F32 loadCellPreviousDrift, loadCellCurrentDrift; + F32 driftDiff = 0.0; + U16 lcPrimaryIndex, lcBackupIndex; + + if ( DG_RESERVOIR_1 == reservoirID ) + { + lcPrimaryIndex = LOAD_CELL_RESERVOIR_1_PRIMARY; + lcBackupIndex = LOAD_CELL_RESERVOIR_1_BACKUP; + } + else + { + lcPrimaryIndex = LOAD_CELL_RESERVOIR_2_PRIMARY; + lcBackupIndex = LOAD_CELL_RESERVOIR_2_BACKUP; + } + + loadCellPrimaryWeight = getReservoirWeightLargeFilter( reservoirID ); + loadCellBackupWeight = getReservoirBackupWeightLargeFilter( reservoirID ); + loadCellCurrentDrift = fabs( loadCellPrimaryWeight - loadCellBackupWeight ); + + if ( lcLastSteadyWeight[lcPrimaryIndex] > (LOAD_CELL_ILLEGAL_WEIGHT_VALUE + 1) ) + { + // Weight has been previously saved, ok to test + loadCellPreviousDrift = lcLastSteadyWeight[lcPrimaryIndex] - lcLastSteadyWeight[lcBackupIndex]; + driftDiff = fabs ( loadCellCurrentDrift - loadCellPreviousDrift ); + } + // Save latest reading for next test time + lcLastSteadyWeight[lcPrimaryIndex] = loadCellPrimaryWeight; + lcLastSteadyWeight[lcBackupIndex] = loadCellBackupWeight; + + // Check for drift out of range + if ( driftDiff > LOAD_CELL_PRIMARY_BACKUP_MAX_ALLOWED_DRIFT_GRAMS ) + { + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_HD_LOAD_CELL_PRIMARY_BACKUP_DRIFT_OUT_OF_RANGE, loadCellCurrentDrift, loadCellPreviousDrift ) + } +} + /**@}*/