Index: firmware/App/Modes/Dialysis.c =================================================================== diff -u -re64816def7cd98e7dcb6d133b3a56c9fea835af3 -rb3724006e900d548c758aee54cd35386f19a7f1a --- firmware/App/Modes/Dialysis.c (.../Dialysis.c) (revision e64816def7cd98e7dcb6d133b3a56c9fea835af3) +++ firmware/App/Modes/Dialysis.c (.../Dialysis.c) (revision b3724006e900d548c758aee54cd35386f19a7f1a) @@ -1,17 +1,17 @@ /************************************************************************** * -* Copyright (c) 2019-2020 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 +* @file Dialysis.c * -* @author (last) Sean Nash -* @date (last) 24-Sep-2020 +* @author (last) Darren Cox +* @date (last) 13-May-2022 * -* @author (original) Sean -* @date (original) 15-Jan-2020 +* @author (original) Sean +* @date (original) 15-Jan-2020 * ***************************************************************************/ @@ -26,6 +26,7 @@ #include "ModeTreatment.h" #include "ModeTreatmentParams.h" #include "OperationModes.h" +#include "Reservoirs.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "Timers.h" @@ -46,9 +47,8 @@ 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 MIN_SALINE_BOLUS_VOLUME_PCT 0.8 ///< Minimum saline bolus volume measured by independent means (as % of target). -#define MAX_SALINE_BOLUS_VOLUME_PCT 1.2 ///< Maximum saline bolus volume measured by independent means (as % of target). +#define SALINE_BOLUS_RATE_ML_MIN 300 ///< 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 MAX_ACTIVE_LOAD_CELL_CHANGE_G 50.0 ///< Maximum delta between new and previous measured UF volume. @@ -78,11 +78,9 @@ 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 F32 totalSalineVolumeDelivered; ///< Volume (mL) in total of saline delivered so far (cumulative for all boluses including current one). -static F32 bolusSalineVolumeDelivered; ///< Volume (mL) of current bolus delivered so far. -static F32 bolusSalineVolumeDelivered_Safety; ///< Volume (mL) of current bolus delivered so far according to safety monitor. -static S32 bolusSalineMotorCount; ///< Blood pump motor rev count during saline bolus to calculate saline bolus volume independently for safety. -static U32 bolusSalineLastMotorCount; ///< Last blood pump count from last saline bolus volume update. +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. @@ -139,7 +137,7 @@ setUFRate = 0.0; salineBolusBroadcastTimerCtr = 0; - totalSalineVolumeDelivered = 0.0; + totalSalineVolumeDelivered_mL = 0.0; uFAccuracyCheckTimerCtr = 0; lastUFVolumeChecked = 0.0; @@ -160,9 +158,8 @@ { salineBolusStartRequested = FALSE; salineBolusAbortRequested = FALSE; - bolusSalineVolumeDelivered = 0.0; - bolusSalineVolumeDelivered_Safety = 0.0; - bolusSalineMotorCount = 0; + bolusSalineVolumeDelivered_mL = 0.0; + expectedSalineBolusVolume_mL = 0.0; if ( currentSalineBolusState != SALINE_BOLUS_STATE_MAX_DELIVERED ) { currentSalineBolusState = SALINE_BOLUS_STATE_IDLE; @@ -187,6 +184,8 @@ *************************************************************************/ void transitionToDialysis( void ) { + PUMP_CONTROL_MODE_T mode = PUMP_CONTROL_MODE_CLOSED_LOOP; + // Set last UF timestamp so UF ref is resumed from this time lastUFTimeStamp = getMSTimerCount(); // Send dialysate outlet pump latest UF volumes @@ -195,19 +194,33 @@ // Inform DG interface that dialysis has started/resumed dialysisResumed(); + resetReservoirsVariables(); + // Set valves for dialysis setValvePosition( VDI, VALVE_POSITION_B_OPEN ); setValvePosition( VDO, VALVE_POSITION_B_OPEN ); setValvePosition( VBA, VALVE_POSITION_B_OPEN ); setValvePosition( VBV, VALVE_POSITION_B_OPEN ); // Restart pumps -#ifndef RUN_PUMPS_OPEN_LOOP - setBloodPumpTargetFlowRate( setBloodFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); - setDialInPumpTargetFlowRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); -#else - setBloodPumpTargetFlowRate( setBloodFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); - setDialInPumpTargetFlowRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); + +#ifndef _RELEASE_ + if ( SW_CONFIG_ENABLE_VALUE == getSoftwareConfigStatus( SW_CONFIG_ENABLE_BLOOD_PUMP_OPEN_LOOP ) ) + { + mode = PUMP_CONTROL_MODE_OPEN_LOOP; + } #endif + + 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 + + setDialInPumpTargetFlowRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, mode ); + setDialOutPumpTargetRate( setDialysateFlowRate + (S32)setUFRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); // Start Heparin pump as appropriate startHeparinPump(); @@ -241,22 +254,13 @@ // Do not run syringe pump if no Heparin included in prescription or it was paused or if Heparin should be stopped at this stage of treatment if ( ( minRem > (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 + startHeparinContinuous(); // TODO - check return status } - // Otherwise, start/resume continuous delivery else { - if ( hepRate > 0.0 ) - { - startHeparinContinuous(); // TODO - check return status - } - else - { - setHeparinCompleted(); - } + setHeparinCompleted(); } } else @@ -288,13 +292,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 - setBloodPumpTargetFlowRate( setBloodFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); - setDialInPumpTargetFlowRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); -#else - setBloodPumpTargetFlowRate( setBloodFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); - setDialInPumpTargetFlowRate( setDialysateFlowRate, 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 + 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 + setDialInPumpTargetFlowRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, mode ); + setDialOutPumpTargetRate( setDialysateFlowRate + (S32)setUFRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); } } @@ -323,8 +338,8 @@ * @brief * The signalStartSalineBolus function handles user request to initiate a * saline bolus. - * @details Inputs: TBD - * @details Outputs: TBD + * @details Inputs: set bolus volume, current mode/sub-mode, bolus state + * @details Outputs: salineBolusStartRequested, response sent * @return none *************************************************************************/ void signalStartSalineBolus( void ) @@ -345,7 +360,7 @@ { rejReason = REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE; } - else if ( totalSalineVolumeDelivered >= (F32)MAX_SALINE_VOLUME_DELIVERED ) + else if ( totalSalineVolumeDelivered_mL >= (F32)MAX_SALINE_VOLUME_DELIVERED ) { rejReason = REQUEST_REJECT_REASON_SALINE_MAX_VOLUME_REACHED; } @@ -367,8 +382,8 @@ * @brief * The signalAbortSalineBolus function handles user request to abort a * saline bolus. - * @details Inputs: TBD - * @details Outputs: TBD + * @details Inputs: set bolus volume, current mode/sub-mode, bolus state + * @details Outputs: salineBolusAbortRequested, response sent * @return none *************************************************************************/ void signalAbortSalineBolus( void ) @@ -475,7 +490,7 @@ *************************************************************************/ F32 getTotalSalineBolusVolumeDelivered( void ) { - return totalSalineVolumeDelivered; + return totalSalineVolumeDelivered_mL; } /*********************************************************************//** @@ -500,7 +515,7 @@ 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 { @@ -552,7 +567,7 @@ 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 { @@ -857,10 +872,7 @@ if ( ( FALSE == isBloodPumpRunning() ) && ( FALSE == isDialInPumpRunning() ) && ( FALSE == isDialOutPumpRunning() ) ) { // Reset bolus data before we start - bolusSalineVolumeDelivered = 0.0; - bolusSalineVolumeDelivered_Safety = 0.0; - bolusSalineMotorCount = 0; - bolusSalineLastMotorCount = getBloodPumpMotorCount(); + bolusSalineVolumeDelivered_mL = 0.0; bolusSalineLastVolumeTimeStamp = getMSTimerCount(); // Bypass dialyzer @@ -902,15 +914,16 @@ 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(); // TODO - should I use raw flow instead of filtered here??? F32 volSinceLastUpdateMl = bldFlowRate * timeSinceLastVolumeUpdateMin; + F32 expVolSinceLastUpdateMl = (F32)getTargetBloodFlowRate() * timeSinceLastVolumeUpdateMin; // Update saline bolus volumes bolusSalineLastVolumeTimeStamp = getMSTimerCount(); - bolusSalineVolumeDelivered += volSinceLastUpdateMl; - totalSalineVolumeDelivered += volSinceLastUpdateMl; - bolusSalineMotorCount = u32BiDiffWithWrap( bolusSalineLastMotorCount, getBloodPumpMotorCount() ) / BP_HALL_EDGE_COUNTS_PER_REV; - bolusSalineVolumeDelivered_Safety = ( (F32)bolusSalineMotorCount * VOLUME_PER_BP_MOTOR_REV_ML ); // TODO - include upstream pressure compensation to this calc (from PBA). + bolusSalineVolumeDelivered_mL += volSinceLastUpdateMl; + totalSalineVolumeDelivered_mL += volSinceLastUpdateMl; + expectedSalineBolusVolume_mL += expVolSinceLastUpdateMl; // Check for empty saline bag per arterial line pressure if ( TRUE == isSalineBagEmpty() ) @@ -921,23 +934,15 @@ } // Determine if we have reached maximum saline delivery volume - if ( ( totalSalineVolumeDelivered >= (F32)MAX_SALINE_VOLUME_DELIVERED ) ) + if ( ( totalSalineVolumeDelivered_mL >= (F32)MAX_SALINE_VOLUME_DELIVERED ) ) { result = SALINE_BOLUS_STATE_MAX_DELIVERED; } else { // Determine if bolus is complete - if ( bolusSalineVolumeDelivered >= bolusTargetVolume ) + if ( bolusSalineVolumeDelivered_mL >= bolusTargetVolume ) { - // If safety thinks we have under-delivered the bolus, throw a fault - if ( bolusSalineVolumeDelivered_Safety < ( bolusTargetVolume * MIN_SALINE_BOLUS_VOLUME_PCT ) ) - { -#ifndef DISABLE_PUMP_FLOW_CHECKS - SET_ALARM_WITH_2_F32_DATA( ALARM_ID_SALINE_BOLUS_VOLUME_CHECK_FAILURE, bolusTargetVolume, bolusSalineVolumeDelivered_Safety ); - errorFound = TRUE; -#endif - } result = SALINE_BOLUS_STATE_IDLE; } // User is aborting saline bolus @@ -946,14 +951,13 @@ salineBolusAbortRequested = FALSE; result = SALINE_BOLUS_STATE_IDLE; } - // Determine if safety thinks we have over-delivered the bolus - else if ( bolusSalineVolumeDelivered_Safety > ( bolusTargetVolume * MAX_SALINE_BOLUS_VOLUME_PCT ) ) + // Determine if saline bolus is under/over delivering + else if ( ( fabs( expectedSalineBolusVolume_mL - bolusSalineVolumeDelivered_mL ) > maxBolusErrorMl ) && + ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_PUMP_FLOW_CHECKS ) != SW_CONFIG_ENABLE_VALUE ) ) { -#ifndef DISABLE_PUMP_FLOW_CHECKS - SET_ALARM_WITH_2_F32_DATA( ALARM_ID_SALINE_BOLUS_VOLUME_CHECK_FAILURE, bolusTargetVolume, bolusSalineVolumeDelivered_Safety ); + activateAlarmNoData( ALARM_ID_SALINE_BOLUS_VOLUME_CHECK_FAILURE ); errorFound = TRUE; result = SALINE_BOLUS_STATE_IDLE; -#endif } } @@ -966,8 +970,8 @@ // Send last saline bolus data salineBolusBroadcastTimerCtr = SALINE_BOLUS_DATA_PUB_INTERVAL; publishSalineBolusData(); - sendTreatmentLogEventData( SALINE_BOLUSES_CHANGE_EVENT, bolusSalineVolumeDelivered, totalSalineVolumeDelivered ); - bolusSalineVolumeDelivered = 0.0; + sendTreatmentLogEventData( SALINE_BOLUSES_CHANGE_EVENT, bolusSalineVolumeDelivered_mL, totalSalineVolumeDelivered_mL ); + bolusSalineVolumeDelivered_mL = 0.0; // Dialysis back to UF state *dialysisState = DIALYSIS_UF_STATE; // End dialyzer bypass and resume dialysis if no alarms triggered @@ -1023,9 +1027,9 @@ SALINE_BOLUS_DATA_PAYLOAD_T data; data.tgtSalineVolumeMl = getTreatmentParameterU32( TREATMENT_PARAM_SALINE_BOLUS_VOLUME ); - data.cumSalineVolumeMl = totalSalineVolumeDelivered; - data.bolSalineVolumeMl = bolusSalineVolumeDelivered; - broadcastSalineBolusData( data ); + data.cumSalineVolumeMl = totalSalineVolumeDelivered_mL; + data.bolSalineVolumeMl = bolusSalineVolumeDelivered_mL; + broadcastData( MSG_ID_SALINE_BOLUS_DATA, COMM_BUFFER_OUT_CAN_HD_BROADCAST, (U08*)&data, sizeof( SALINE_BOLUS_DATA_PAYLOAD_T ) ); salineBolusBroadcastTimerCtr = 0; } } @@ -1045,9 +1049,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 ) @@ -1060,9 +1067,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 ); + } } } @@ -1081,17 +1091,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 ] ); @@ -1174,4 +1188,18 @@ resCurrVolume[ DG_RESERVOIR_2 ] = res2Vol; } +/*********************************************************************//** + * @brief + * The getReservoirUltrafiltrationVol function returns the ultrafiltration + * volume of a reservoir. + * @details Inputs: none + * @details Outputs: resStartVolume[], resFinalVolume[] + * @param reservoirID reservoir ID to calculate the ultrafiltration volume + * @return ultrafiltration volume of the reservoir + *************************************************************************/ +F32 getReservoirUltrafiltrationVol( DG_RESERVOIR_ID_T reservoirID ) +{ + return ( resFinalVolume[ reservoirID ] - resStartVolume[ reservoirID ] ); +} + /**@}*/