Index: firmware/App/Modes/Dialysis.c =================================================================== diff -u -ra2bc96881a5fc3d8f779246b2abebf15a8de9384 -rdaefaef0d2d3e13ff563f11d9721ccc1d831cf06 --- firmware/App/Modes/Dialysis.c (.../Dialysis.c) (revision a2bc96881a5fc3d8f779246b2abebf15a8de9384) +++ firmware/App/Modes/Dialysis.c (.../Dialysis.c) (revision daefaef0d2d3e13ff563f11d9721ccc1d831cf06) @@ -20,7 +20,6 @@ #include "AirTrap.h" #include "BloodFlow.h" #include "Buttons.h" -#include "DGInterface.h" #include "Dialysis.h" #include "DialInFlow.h" #include "DialOutFlow.h" @@ -40,19 +39,19 @@ // ********** private definitions ********** #define MAX_UF_RATE_ML_PER_HOUR 2750.0 ///< Maximum ultrafiltration rate in mL/hour -#define MAX_UF_RATE_ACCURACY_ERROR_ML_HR 100.0 ///< Maximum ultrafiltration rate accuracy error in mL/hr over each hour of treatment. -#define MAX_UF_RATE_ACCURACY_ERROR_PCT 0.05 ///< Minimum ultrafilteration rate accuracy in percentage of set point (5%) over each hour of treatment. #define MAX_UF_ACCURACY_ERROR_ML 250.0 ///< Maximum ultrafiltration accuracy error in mL over the entire treatment. /// Saline bolus data broadcast interval (ms/task time) count. -#define SALINE_BOLUS_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) +static const U32 SALINE_BOLUS_DATA_PUB_INTERVAL = ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ); /// Ultrafiltration rate accuracy check interval count. -#define UF_ACCURACY_CHECK_INTERVAL ((1 * MIN_PER_HOUR * SEC_PER_MIN * MS_PER_SECOND) / TASK_GENERAL_INTERVAL) +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 MAX_ACTIVE_LOAD_CELL_CHANGE_G 50.0 ///< Maximum delta between new and previous measured UF volume. + // ********** private data ********** static DIALYSIS_STATE_T currentDialysisState; ///< Current state of the dialysis sub-mode state machine. @@ -72,7 +71,6 @@ static U32 setDialysateFlowRate; ///< Currently set dialysate flow rate (from prescription). static F32 maxUFVolumeML; ///< Currently set total ultrafiltration volume for treatment (from prescription). static F32 setUFRate; ///< Currently set ultrafiltration rate (from prescription). -static F32 maxUFRateAccuracyError_Ml_hr; ///< Minimum ultrafiltration rate accuracy over 1 hour duration (5% or 100 mL, whichever is greater). 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. @@ -126,8 +124,6 @@ refUFVolume = 0.0; measUFVolume = 0.0; - resFinalVolume[ DG_RESERVOIR_1 ] = 0.0; - resFinalVolume[ DG_RESERVOIR_2 ] = 0.0; measUFVolumeFromPriorReservoirs = 0.0; uFTimeMS = 0; @@ -137,7 +133,6 @@ setDialysateFlowRate = 0; maxUFVolumeML = 0.0; setUFRate = 0.0; - maxUFRateAccuracyError_Ml_hr = MAX_UF_RATE_ACCURACY_ERROR_ML_HR; salineBolusBroadcastTimerCtr = 0; totalSalineVolumeDelivered = 0.0; @@ -184,7 +179,38 @@ *************************************************************************/ void transitionToDialysis( void ) { - startAirTrapControl(); // TODO - do we need to start this sooner? After prime? + // Set last UF timestamp so UF ref is resumed from this time + lastUFTimeStamp = getMSTimerCount(); + // Send dialysate outlet pump latest UF volumes + setDialOutUFVolumes( refUFVolume, measUFVolume ); + + // Inform DG interface that dialysis has started/resumed + dialysisResumed(); + + // 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 ); +#endif + setDialOutPumpTargetRate( setDialysateFlowRate + (S32)setUFRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + // TODO - Heparin pump + // Tell DG to start heating dialysate + cmdStartDGTrimmerHeater(); + + startAirTrapControl(); + + // Set user alarm recovery actions allowed in this sub-mode + setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, TRUE ); + setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, TRUE ); + setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, TRUE ); } /*********************************************************************//** @@ -206,8 +232,6 @@ setDialysateFlowRate = dPFlow; maxUFVolumeML = maxUFVol; setUFRate = uFRate; - maxUFRateAccuracyError_Ml_hr = MAX_UF_RATE_ACCURACY_ERROR_PCT * uFRate * (F32)MIN_PER_HOUR; - maxUFRateAccuracyError_Ml_hr = MAX( maxUFRateAccuracyError_Ml_hr, MAX_UF_RATE_ACCURACY_ERROR_ML_HR ); if ( TREATMENT_DIALYSIS_STATE == getTreatmentState() ) { @@ -224,41 +248,6 @@ /*********************************************************************//** * @brief - * The startDialysis function starts/resumes dialysis. This function - * will be called by Treatment Mode when beginning or resuming dialysis - * treatment. - * @details Inputs: none - * @details Outputs: Dialysis module prepared for immediate resumption. - * @return none - *************************************************************************/ -void startDialysis( void ) -{ - // Set last UF timestamp so UF ref is resumed from this time - lastUFTimeStamp = getMSTimerCount(); - // Send dialysate outlet pump latest UF volumes - setDialOutUFVolumes( refUFVolume, measUFVolume ); - - // 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 ); -#endif - setDialOutPumpTargetRate( setDialysateFlowRate + (S32)setUFRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); - // TODO - Heparin pump - // Tell DG to start heating dialysate - cmdStartDGTrimmerHeater(); -} - -/*********************************************************************//** - * @brief * The stopDialysis function stops dialysis. This function will be called * by Treatment Mode when an alarm occurs or the user pressed the stop button. * Dialysis may be resumed later. @@ -578,17 +567,9 @@ currentUFState = handleUFRunningState( &result ); break; - case UF_OFF_STATE: - currentUFState = handleUFOffState( &result ); - break; - - case UF_COMPLETED_STATE: - currentUFState = handleUFCompletedState( &result ); - break; - default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_DIALYSIS_INVALID_UF_STATE, currentUFState ) - currentUFState = UF_COMPLETED_STATE; + currentUFState = UF_PAUSED_STATE; break; } @@ -648,16 +629,9 @@ { UF_STATE_T result; - if ( maxUFVolumeML < NEARLY_ZERO ) - { - result = UF_OFF_STATE; - } - else - { - lastUFTimeStamp = getMSTimerCount(); - uFTimeMS = 0; - result = UF_RUNNING_STATE; - } + lastUFTimeStamp = getMSTimerCount(); + uFTimeMS = 0; + result = UF_RUNNING_STATE; return result; } @@ -734,9 +708,9 @@ updateUFVolumes(); // If we have reached target UF volume, UF is complete - if ( measUFVolume >= maxUFVolumeML ) + if ( refUFVolume >= maxUFVolumeML ) { - result = UF_COMPLETED_STATE; + setUFRate = 0.0; } // Handle saline bolus start request from user else if ( TRUE == salineBolusStartRequested ) @@ -761,74 +735,6 @@ /*********************************************************************//** * @brief - * The handleUFCompletedOrOffState function handles the UF Off state - * of the ultrafiltration state machine. - * @details Inputs: none - * @details Outputs: UF volumes updated and provided to DPo pump controller. - * @param dialysisState next dialysis state - * @return next ultrafiltration state - *************************************************************************/ -static UF_STATE_T handleUFOffState( DIALYSIS_STATE_T *dialysisState ) -{ - UF_STATE_T result = UF_OFF_STATE; - - // Calculate UF volumes and provide to dialysate outlet pump controller - updateUFVolumes(); - - // Handle saline bolus start request from user - if ( TRUE == salineBolusStartRequested ) - { - salineBolusAutoResumeUF = FALSE; - // Go to saline bolus state - if ( SALINE_BOLUS_STATE_IDLE == currentSalineBolusState ) - { - *dialysisState = DIALYSIS_SALINE_BOLUS_STATE; - } - else - { - salineBolusStartRequested = FALSE; - } - } - - return result; -} - -/*********************************************************************//** - * @brief - * The handleUFCompletedState function handles the UF Completed - * state of the ultrafiltration state machine. This is a terminal state. - * @details Inputs: none - * @details Outputs: UF volumes updated and provided to DPo pump controller. - * @param dialysisState next dialysis state - * @return next ultrafiltration state - *************************************************************************/ -static UF_STATE_T handleUFCompletedState( DIALYSIS_STATE_T *dialysisState ) -{ - UF_STATE_T result = UF_COMPLETED_STATE; - - // Calculate UF volumes and provide to dialysate outlet pump controller - updateUFVolumes(); - - // Handle saline bolus start request from user - if ( TRUE == salineBolusStartRequested ) - { - salineBolusAutoResumeUF = FALSE; - // Go to saline bolus state - if ( SALINE_BOLUS_STATE_IDLE == currentSalineBolusState ) - { - *dialysisState = DIALYSIS_SALINE_BOLUS_STATE; - } - else - { - salineBolusStartRequested = FALSE; - } - } - - return result; -} - -/*********************************************************************//** - * @brief * The handleSalineBolusIdleState function handles the idle state of the * saline bolus state machine. * @details Inputs: none @@ -936,11 +842,12 @@ bolusSalineVolumeDelivered_Safety = ( (F32)bolusSalineMotorCount * VOLUME_PER_BP_MOTOR_REV_ML ); // TODO - include upstream pressure compensation to this calc #ifndef DISABLE_SALINE_BOLUS_CHECKS - // TODO - check for empty saline bag - if ( 0 ) + // Check for empty saline bag per arterial line pressure + if ( TRUE == isSalineBagEmpty() ) { - SET_ALARM_WITH_1_F32_DATA( ALARM_ID_EMPTY_SALINE_BAG, 0.0 ); // TODO - give data supporting empty bag detection + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_EMPTY_SALINE_BAG, getMeasuredArterialPressure() ); errorFound = TRUE; + result = SALINE_BOLUS_STATE_IDLE; } #endif @@ -1002,7 +909,7 @@ currentUFState = UF_RUNNING_STATE; } // Resume dialysis - startDialysis(); + transitionToDialysis(); } } @@ -1064,22 +971,19 @@ { F32 uFMeasRate = measUFVolume - lastUFVolumeChecked; // Volumes are at start/end of 1 hour period, so implied rate is per hour - // Check UF rate diff from target in last hour - if ( uFMeasRate > maxUFRateAccuracyError_Ml_hr ) + // 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 ); #endif } - // If actively performing ultrafiltration right now, increment timer and see if time to start another 1 hour check period - if ( UF_RUNNING_STATE == currentUFState ) + // Increment timer and see if time to start another 1 hour check period + if ( ++uFAccuracyCheckTimerCtr >= UF_ACCURACY_CHECK_INTERVAL ) { - if ( ++uFAccuracyCheckTimerCtr >= UF_ACCURACY_CHECK_INTERVAL ) - { - // Reset for next check interval - lastUFVolumeChecked = measUFVolume; - uFAccuracyCheckTimerCtr = 0; - } + // Reset for next check interval + lastUFVolumeChecked = measUFVolume; + uFAccuracyCheckTimerCtr = 0; } // Check total UF volume error @@ -1104,22 +1008,24 @@ static void updateUFVolumes( void ) { DG_RESERVOIR_ID_T activeRes = getDGActiveReservoir(); - F32 latestResVolume; + F32 latestResVolume = getReservoirWeightSmallFilter( activeRes ); +#ifndef DISABLE_UF_ALARMS + F32 deltaVolume = latestResVolume - resFinalVolume[ activeRes ]; - // Get volume of active reservoir - if ( DG_RESERVOIR_1 == 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 ) { - latestResVolume = getLoadCellWeightInGrams( LOAD_CELL_RESERVOIR_1_PRIMARY ); + 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 { - latestResVolume = getLoadCellWeightInGrams( LOAD_CELL_RESERVOIR_2_PRIMARY ); + // Calculate UF volumes and provide to dialysate outlet pump controller + measUFVolume = measUFVolumeFromPriorReservoirs + ( latestResVolume - resStartVolume[ activeRes ] ); + resFinalVolume[ activeRes ] = latestResVolume; + setDialOutUFVolumes( refUFVolume, measUFVolume ); } - - // Calculate UF volumes and provide to dialysate outlet pump controller - measUFVolume = measUFVolumeFromPriorReservoirs + ( latestResVolume - resStartVolume[ activeRes ] ); - resFinalVolume[ activeRes ] = latestResVolume; - setDialOutUFVolumes( refUFVolume, measUFVolume ); } /*********************************************************************//** @@ -1129,28 +1035,16 @@ * is the inactive reservoir) in order to get a more stable volume. * @details Inputs: active reservoir, load cell reading from inactive reservoir * @details Outputs: resStartVolume[] + * @param reservoirID reservoir ID to update the baseline volume. * @return none *************************************************************************/ -void setStartReservoirVolume( void ) +void setStartReservoirVolume( DG_RESERVOIR_ID_T reservoirID ) { - DG_RESERVOIR_ID_T activeRes = getDGActiveReservoir(); - DG_RESERVOIR_ID_T inactiveRes; - F32 resVolume; + F32 resVolume = getReservoirWeightLargeFilter( reservoirID ); - // Get volume of inactive reservoir - if ( DG_RESERVOIR_2 == activeRes ) - { - inactiveRes = DG_RESERVOIR_1; - resVolume = getLoadCellWeightInGrams( LOAD_CELL_RESERVOIR_1_PRIMARY ); - } - else - { - inactiveRes = DG_RESERVOIR_2; - resVolume = getLoadCellWeightInGrams( LOAD_CELL_RESERVOIR_2_PRIMARY ); - } - // Set starting baseline volume for next reservoir before we switch to it - resStartVolume[ inactiveRes ] = resVolume; + resStartVolume[ reservoirID ] = resVolume; + resFinalVolume[ reservoirID ] = resVolume; } /*********************************************************************//** @@ -1164,8 +1058,7 @@ *************************************************************************/ void signalReservoirsSwitched( void ) { - DG_RESERVOIR_ID_T activeRes = getDGActiveReservoir(); - DG_RESERVOIR_ID_T inactiveRes = ( activeRes == DG_RESERVOIR_1 ? DG_RESERVOIR_2 : DG_RESERVOIR_1 ); + DG_RESERVOIR_ID_T inactiveRes = getDGInactiveReservoir(); // Update UF volume from prior reservoirs per tentative res volume for last reservoir measUFVolumeFromPriorReservoirs += ( resFinalVolume[ inactiveRes ] - resStartVolume[ inactiveRes ] ); @@ -1174,30 +1067,17 @@ /*********************************************************************//** * @brief * The setFinalReservoirVolume function updates the UF volume from prior reservoirs - * with a more stable final reservoir volume of the prior reservoir after it's + * with a more stable final reservoir volume of the prior reservoir after it * had a moment to settle. * @details Inputs: active reservoir, load cell reading from inactive reservoir * @details Outputs: measUFVolumeFromPriorReservoirs, resFinalVolume[], resStartVolume[] * @return none *************************************************************************/ void setFinalReservoirVolume( void ) { - DG_RESERVOIR_ID_T activeRes = getDGActiveReservoir(); - DG_RESERVOIR_ID_T inactiveRes; - F32 resVolume; + DG_RESERVOIR_ID_T inactiveRes = getDGInactiveReservoir(); + F32 resVolume = getReservoirWeightLargeFilter( inactiveRes ); - // Get volume of inactive reservoir - if ( DG_RESERVOIR_2 == activeRes ) - { - inactiveRes = DG_RESERVOIR_1; - resVolume = getLoadCellWeightInGrams( LOAD_CELL_RESERVOIR_1_PRIMARY ); - } - else - { - inactiveRes = DG_RESERVOIR_2; - resVolume = getLoadCellWeightInGrams( LOAD_CELL_RESERVOIR_2_PRIMARY ); - } - // 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;