Index: firmware/App/Modes/Dialysis.c =================================================================== diff -u -rc84daa1f07003427fc5cdde8f5651434478f7313 -r43e59e505bbea87c76822c51a3273eec3f4addaa --- firmware/App/Modes/Dialysis.c (.../Dialysis.c) (revision c84daa1f07003427fc5cdde8f5651434478f7313) +++ firmware/App/Modes/Dialysis.c (.../Dialysis.c) (revision 43e59e505bbea87c76822c51a3273eec3f4addaa) @@ -1,30 +1,36 @@ /************************************************************************** * -* 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-Jun-2020 +* @author (last) Dara Navaei +* @date (last) 13-Jul-2022 * -* @author (original) Sean -* @date (original) 15-Jan-2020 +* @author (original) Sean +* @date (original) 15-Jan-2020 * ***************************************************************************/ +#include // Using fabs() function + +#include "AirTrap.h" #include "BloodFlow.h" #include "Buttons.h" -#include "DGInterface.h" #include "Dialysis.h" #include "DialInFlow.h" #include "DialOutFlow.h" +#include "ModeTreatment.h" +#include "ModeTreatmentParams.h" #include "OperationModes.h" +#include "Reservoirs.h" +#include "SystemCommMessages.h" #include "TaskGeneral.h" #include "Timers.h" -#include "ModeTreatment.h" +#include "Valves.h" /** * @addtogroup Dialysis @@ -33,20 +39,33 @@ // ********** private definitions ********** -#define MAX_UF_RATE_ML_PER_MIN 45.83 ///< Maximum ultrafiltration rate in mL/min (2750 mL/hr). -#define MAX_UF_ACCURACY_ERROR_ML 250.0 ///< Maximum ultrafiltration accuracy error in mL over the entire treatment. -#define UF_ACCURACY_CHECK_INTERVAL ((1 * SEC_PER_MIN * MS_PER_SECOND) / TASK_GENERAL_INTERVAL) ///< Ultrafiltration rate accuracy check interval count +#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 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. + // ********** 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 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 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). @@ -56,45 +75,62 @@ static F32 maxUFVolumeML; ///< 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 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 handleDialysisSolutionInfusionState( void ); +static DIALYSIS_STATE_T handleDialysisSalineBolusState( void ); -static UF_STATE_T handleUFStartState( void ); -static UF_STATE_T handleUFPausedState( void ); -static UF_STATE_T handleUFRunningState( void ); -static UF_STATE_T handleUFOffState( void ); -static UF_STATE_T handleUFCompletedState( 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 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 updateUFVolumes( void ); +static void publishSalineBolusData( void ); +static void checkLoadCellsStablePrimaryBackupDriftOutOfRange( DG_RESERVOIR_ID_T reservoirID ); + /*********************************************************************//** * @brief - * The initDialysis function initializes the Dialysis sub-mode module. \n - * Calling this function will reset dialysis and therefore should only \n + * The initDialysis function initializes the Dialysis sub-mode module. + * Calling this function will reset dialysis and therefore should only * be called when a new treatment is due to begin. - * @details - * Inputs : none - * Outputs : Dialysis sub-mode module initialized. + * @details Inputs: none + * @details Outputs: Dialysis sub-mode module initialized. * @return none *************************************************************************/ void initDialysis( void ) { + U16 i; + currentDialysisState = DIALYSIS_START_STATE; currentUFState = UF_START_STATE; + currentSalineBolusState = SALINE_BOLUS_STATE_IDLE; refUFVolume = 0.0; measUFVolume = 0.0; -// resStartVolume[ DG_RESERVOIR_1 ] = 0.0; -// resStartVolume[ DG_RESERVOIR_2 ] = 0.0; - resFinalVolume[ DG_RESERVOIR_1 ] = 0.0; - resFinalVolume[ DG_RESERVOIR_2 ] = 0.0; measUFVolumeFromPriorReservoirs = 0.0; + // Send reset UF volumes to dialysate outlet pump + setDialOutUFVolumes( refUFVolume, measUFVolume ); uFTimeMS = 0; lastUFTimeStamp = 0; @@ -104,39 +140,156 @@ maxUFVolumeML = 0.0; setUFRate = 0.0; + salineBolusBroadcastTimerCtr = 0; + totalSalineVolumeDelivered_mL = 0.0; + uFAccuracyCheckTimerCtr = 0; lastUFVolumeChecked = 0.0; + + for (i=0; i (F32)preStop ) && ( HEPARIN_STATE_STOPPED == currentHeparinState ) ) + { + if ( hepRate > 0.0 ) + { + startHeparinContinuous(); // TODO - check return status + } + else + { + setHeparinCompleted(); + } + } + else + { + stopSyringePump(); + } +} + +/*********************************************************************//** + * @brief + * The setDialysisParams function sets the dialysis treatment parameters. + * This function should be called prior to beginning dialysis treatment * and when the user changes one or more parameters during treatment. - * @details - * Inputs : none - * Outputs : dialysis treatment parameters are set. - * @param bPFlow : target blood pump flow rate (in mL/min). - * @param dPFlow : target dialysate inlet pump flow rate (in mL/min). - * @param maxUFVol : maximum ultrafiltration volume (in mL). - * @param uFRate : target ultrafiltration rate (in mL/min). + * @details Inputs: none + * @details Outputs: dialysis treatment parameters are set. + * @param bPFlow target blood pump flow rate (in mL/min) + * @param dPFlow target dialysate inlet pump flow rate (in mL/min) + * @param maxUFVol maximum ultrafiltration volume (in mL) + * @param uFRate target ultrafiltration rate (in mL/min) * @return none *************************************************************************/ void setDialysisParams( U32 bPFlow, U32 dPFlow, F32 maxUFVol, F32 uFRate ) @@ -146,55 +299,140 @@ maxUFVolumeML = maxUFVol; setUFRate = uFRate; - setBloodPumpTargetFlowRate( setBloodFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); // TODO - test code - remove later - setDialInPumpTargetFlowRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); -// setBloodPumpTargetFlowRate( setBloodFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); // TODO - restore these later -// setDialInPumpTargetFlowRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + // Make rate changes in real time if currently performing dialysis. + if ( ( TREATMENT_DIALYSIS_STATE == getTreatmentState() ) && ( getDialysisState() != DIALYSIS_SALINE_BOLUS_STATE ) ) + { + 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 ); + } } /*********************************************************************//** * @brief - * The startDialysis function starts/resumes dialysis. This function \n - * will be called by Treatment Mode when beginning or resuming dialysis \n - * treatment. - * @details - * Inputs : none - * Outputs : Dialysis module prepared for immediate resumption. + * 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. + * @details Inputs: none + * @details Outputs: Blood and dialysate pumps stopped. Heparin pump stopped. * @return none *************************************************************************/ -void startDialysis( void ) +void stopDialysis( void ) { - lastUFTimeStamp = getMSTimerCount(); - setDialOutUFVolumes( refUFVolume, measUFVolume ); - setBloodPumpTargetFlowRate( setBloodFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); - setDialInPumpTargetFlowRate( setDialysateFlowRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); - setDialOutPumpTargetRate( setDialysateFlowRate + (S32)setUFRate, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); - // TODO - Heparin pump + // Stop pumps + setBloodPumpTargetFlowRate( 0, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + setDialInPumpTargetFlowRate( 0, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + setDialOutPumpTargetRate( 0, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + stopSyringePump(); + // Tell DG to stop heating dialysate + cmdStopDGTrimmerHeater(); } /*********************************************************************//** * @brief - * The stopDialysis function stops dialysis. This function will be called \n - * by Treatment Mode when an alarm occurs or the user pressed the stop button. \n - * Dialysis may be resumed later. - * @details - * Inputs : none - * Outputs : Blood and dialysate pumps stopped. Heparin pump stopped. + * The signalStartSalineBolus function handles user request to initiate a + * saline bolus. + * @details Inputs: set bolus volume, current mode/sub-mode, bolus state + * @details Outputs: salineBolusStartRequested, response sent * @return none *************************************************************************/ -void stopDialysis( void ) +void signalStartSalineBolus( void ) { - setBloodPumpTargetFlowRate( 0, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); - setDialInPumpTargetFlowRate( 0, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); - setDialOutPumpTargetRate( 0, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + BOOL accept = FALSE; + REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NONE; + U32 salineBolusVolume = getTreatmentParameterU32( TREATMENT_PARAM_SALINE_BOLUS_VOLUME ); + HD_OP_MODE_T currOpMode = getCurrentOperationMode(); + TREATMENT_STATE_T currTreatSubMode = getTreatmentState(); + SALINE_BOLUS_STATE_T currSalineBolusState = getSalineBolusState(); + + // Must be in treatment mode, dialysis sub-mode, saline bolus in idle state in order to start a saline bolus + if ( currOpMode != MODE_TREA ) + { + rejReason = REQUEST_REJECT_REASON_NOT_IN_TREATMENT_MODE; + } + else if ( currTreatSubMode != TREATMENT_DIALYSIS_STATE ) + { + rejReason = REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE; + } + else if ( totalSalineVolumeDelivered_mL >= (F32)MAX_SALINE_VOLUME_DELIVERED ) + { + rejReason = REQUEST_REJECT_REASON_SALINE_MAX_VOLUME_REACHED; + } + else if ( currSalineBolusState != SALINE_BOLUS_STATE_IDLE ) + { + rejReason = REQUEST_REJECT_REASON_SALINE_BOLUS_IN_PROGRESS; + } + else + { + accept = TRUE; + salineBolusStartRequested = TRUE; + } + + // Send response + sendSalineBolusResponse( accept, rejReason, salineBolusVolume ); } /*********************************************************************//** * @brief + * The signalAbortSalineBolus function handles user request to abort a + * saline bolus. + * @details Inputs: set bolus volume, current mode/sub-mode, bolus state + * @details Outputs: salineBolusAbortRequested, response sent + * @return none + *************************************************************************/ +void signalAbortSalineBolus( void ) +{ + BOOL accept = FALSE; + REQUEST_REJECT_REASON_CODE_T rejReason = REQUEST_REJECT_REASON_NONE; + U32 salineBolusVolume = getTreatmentParameterU32( TREATMENT_PARAM_SALINE_BOLUS_VOLUME ); + HD_OP_MODE_T currOpMode = getCurrentOperationMode(); + TREATMENT_STATE_T currTreatSubMode = getTreatmentState(); + SALINE_BOLUS_STATE_T currSalineBolusState = getSalineBolusState(); + + // Must be in treatment mode, dialysis sub-mode, saline bolus in delivery state in order to abort a saline bolus + if ( currOpMode != MODE_TREA ) + { + rejReason = REQUEST_REJECT_REASON_NOT_IN_TREATMENT_MODE; + } + else if ( currTreatSubMode != TREATMENT_DIALYSIS_STATE ) + { + rejReason = REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE; + } + else if ( currSalineBolusState != SALINE_BOLUS_STATE_IN_PROGRESS ) + { + rejReason = REQUEST_REJECT_REASON_SALINE_BOLUS_NOT_IN_PROGRESS; + } + else + { + accept = TRUE; + salineBolusAbortRequested = TRUE; + } + + // Send response + sendSalineBolusResponse( accept, rejReason, salineBolusVolume ); +} + +/*********************************************************************//** + * @brief * The getDialysisState function gets the current dialysis state (sub-mode). - * @details - * Inputs : currentDialysisState - * Outputs : none + * @details Inputs: currentDialysisState + * @details Outputs: none * @return currentDialysisState *************************************************************************/ DIALYSIS_STATE_T getDialysisState( void ) @@ -205,9 +443,8 @@ /*********************************************************************//** * @brief * The getUltrafiltrationState function gets the current ultrafiltration state. - * @details - * Inputs : currentUFState - * Outputs : none + * @details Inputs: currentUFState + * @details Outputs: none * @return currentUFState *************************************************************************/ UF_STATE_T getUltrafiltrationState( void ) @@ -217,26 +454,60 @@ /*********************************************************************//** * @brief - * The getUltrafiltrationVolumeCollected function gets the current ultrafiltration \n + * The getSalineBolusState function gets the current saline bolus state. + * @details Inputs: currentSalineBolusState + * @details Outputs: none + * @return currentSalineBolusState + *************************************************************************/ +SALINE_BOLUS_STATE_T getSalineBolusState( void ) +{ + return currentSalineBolusState; +} + +/*********************************************************************//** + * @brief + * The getUltrafiltrationVolumeCollected function gets the current ultrafiltration * volume collected so far for current treatment. - * @details - * Inputs : measUFVolume, measUFVolumeFromPriorReservoirs - * Outputs : none - * @return currentUFState + * @details Inputs: measUFVolume + * @details Outputs: none + * @return measUFVolume *************************************************************************/ F32 getUltrafiltrationVolumeCollected( void ) { - F32 result = measUFVolume; + return measUFVolume; +} - return result; +/*********************************************************************//** + * @brief + * The getUltrafiltrationReferenceVolume function gets the current ultrafiltration + * reference volume. + * @details Inputs: refUFVolume + * @details Outputs: none + * @return refUFVolume + *************************************************************************/ +F32 getUltrafiltrationReferenceVolume( void ) +{ + return refUFVolume; } /*********************************************************************//** * @brief + * The getTotalSalineBolusVolumeDelivered function gets the current total + * saline volume delivered. + * @details Inputs: totalSalineVolumeDelivered + * @details Outputs: none + * @return totalSalineVolumeDelivered + *************************************************************************/ +F32 getTotalSalineBolusVolumeDelivered( void ) +{ + return totalSalineVolumeDelivered_mL; +} + +/*********************************************************************//** + * @brief * The pauseUF function pauses ultrafiltration. - * @details - * Inputs : currentDialysisState, currentUFState - * Outputs : currentUFState, outlet pump set point + * @details Inputs: currentDialysisState, currentUFState + * @details Outputs: currentUFState, outlet pump set point * @return TRUE if pause successful, FALSE if not *************************************************************************/ BOOL pauseUF( void ) @@ -250,10 +521,12 @@ ( DIALYSIS_UF_STATE == currentDialysisState ) && ( UF_RUNNING_STATE == currentUFState ) ) { result = TRUE; - // set outlet pump to dialysate rate + 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 + // Go to UF paused state currentUFState = UF_PAUSED_STATE; + sendTreatmentLogEventData( UF_START_RESUME_EVENT, UF_RUNNING_STATE, UF_PAUSED_STATE ); } else { @@ -275,17 +548,17 @@ } } - // TODO - send response w/ reason code if rejected + // Send response w/ reason code if rejected + sendUFPauseResumeResponse( result, rejectReason, currentUFState ); return result; } /*********************************************************************//** * @brief * The resumeUF function resumes ultrafiltration. - * @details - * Inputs : currentDialysisState, currentUFState - * Outputs : currentUFState, outlet pump set point + * @details Inputs: currentDialysisState, currentUFState + * @details Outputs: currentUFState, outlet pump set point * @return TRUE if resume successful, FALSE if not *************************************************************************/ BOOL resumeUF( void ) @@ -299,12 +572,14 @@ ( DIALYSIS_UF_STATE == currentDialysisState ) && ( UF_PAUSED_STATE == currentUFState ) ) { result = TRUE; - // set outlet pump to dialysate rate + set UF rate - setDialOutPumpTargetRate( setDialysateFlowRate + FLOAT_TO_INT_WITH_ROUND( setUFRate ), MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); - // restart UF time accumulation for reference volume calculation + 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 paused state + // Go to UF running state currentUFState = UF_RUNNING_STATE; + sendTreatmentLogEventData( UF_START_RESUME_EVENT, UF_PAUSED_STATE, UF_RUNNING_STATE ); } else { @@ -326,25 +601,27 @@ } } - // TODO - send response w/ reason code if rejected + // Send response w/ reason code if rejected + sendUFPauseResumeResponse( result, rejectReason, currentUFState ); + // Send state data immediately for UI update + broadcastTreatmentTimeAndState(); return result; } /*********************************************************************//** * @brief * The execDialysis function executes the Dialysis sub-mode state machine. - * @details - * Inputs : currentDialysisState - * Outputs : currentDialysisState + * @details Inputs: currentDialysisState + * @details Outputs: currentDialysisState * @return none *************************************************************************/ void execDialysis( void ) { - // check ultrafiltration max rate and accuracy during dialysis (even when ultrafiltration is paused). + // Check ultrafiltration max rate and accuracy during dialysis (even when ultrafiltration is paused). checkUFAccuracyAndVolume(); - // dialysis state machine + // Dialysis state machine switch ( currentDialysisState ) { case DIALYSIS_START_STATE: @@ -355,53 +632,63 @@ currentDialysisState = handleDialysisUltrafiltrationState(); break; - case DIALYSIS_SOLUTION_INFUSION_STATE: - currentDialysisState = handleDialysisSolutionInfusionState(); + case DIALYSIS_SALINE_BOLUS_STATE: + currentDialysisState = handleDialysisSalineBolusState(); break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_DIALYSIS_INVALID_STATE, currentDialysisState ) break; } + + // Publish saline bolus data at set interval (whether we are delivering one or not) + publishSalineBolusData(); } /*********************************************************************//** * @brief - * The handleDialysisUltrafiltrationState function handles the ultrafiltration \n + * The handleDialysisUltrafiltrationState function handles the ultrafiltration * state of the Dialysis state machine. - * @details - * Inputs : currentUFState - * Outputs : currentUFState + * @details Inputs: currentUFState + * @details Outputs: currentUFState * @return next Dialysis state. *************************************************************************/ static DIALYSIS_STATE_T handleDialysisUltrafiltrationState( void ) { DIALYSIS_STATE_T result = DIALYSIS_UF_STATE; + U32 preStop = getTreatmentParameterU32( TREATMENT_PARAM_HEPARIN_PRE_STOP_TIME ); + F32 minRem = (F32)getTreatmentTimeRemainingSecs() / (F32)SEC_PER_MIN; + // Stop Heparin delivery if we have reached Heparin pre-stop point + if ( minRem <= (F32)preStop ) + { + stopSyringePump(); + setHeparinCompleted(); + } + // TODO - find a better way to start continuous delivery after bolus completes + if ( HEPARIN_STATE_STOPPED == getHeparinState() ) + { + startHeparinPump(); + } + + // Handle current ultrafiltration state switch ( currentUFState ) { case UF_START_STATE: - currentUFState = handleUFStartState(); + currentUFState = handleUFStartState( &result ); break; case UF_PAUSED_STATE: - currentUFState = handleUFPausedState(); + currentUFState = handleUFPausedState( &result ); break; case UF_RUNNING_STATE: - currentUFState = handleUFRunningState(); + currentUFState = handleUFRunningState( &result ); break; - case UF_OFF_STATE: - currentUFState = handleUFOffState(); - break; - - case UF_COMPLETED_STATE: - currentUFState = handleUFCompletedState(); - break; - default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_DIALYSIS_INVALID_UF_STATE, currentUFState ) + currentUFState = UF_PAUSED_STATE; break; } @@ -410,308 +697,583 @@ /*********************************************************************//** * @brief - * The handleDialysisSolutionInfusionState function handles the solution \n + * The handleDialysisSolutionInfusionState function handles the solution * infustion state of the Dialysis state machine. - * @details - * Inputs : TBD - * Outputs : TBD + * @details Inputs: currentSalineBolusState + * @details Outputs: currentSalineBolusState * @return next Dialysis state. *************************************************************************/ -static DIALYSIS_STATE_T handleDialysisSolutionInfusionState( void ) +static DIALYSIS_STATE_T handleDialysisSalineBolusState( void ) { - DIALYSIS_STATE_T result = DIALYSIS_SOLUTION_INFUSION_STATE; + DIALYSIS_STATE_T result = DIALYSIS_SALINE_BOLUS_STATE; - // TODO - + switch ( currentSalineBolusState ) + { + case SALINE_BOLUS_STATE_IDLE: + currentSalineBolusState = handleSalineBolusIdleState( &result ); + break; + case SALINE_BOLUS_STATE_WAIT_FOR_PUMPS_STOP: + currentSalineBolusState = handleSalineBolusWait4Pumps2Stop( &result ); + break; + + case SALINE_BOLUS_STATE_IN_PROGRESS: + currentSalineBolusState = handleSalineBolusInProgressState( &result ); + break; + + case SALINE_BOLUS_STATE_MAX_DELIVERED: + currentSalineBolusState = handleSalineBolusMaxDeliveredState( &result ); + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_DIALYSIS_INVALID_SALINE_BOLUS_STATE, currentSalineBolusState ) + currentSalineBolusState = SALINE_BOLUS_STATE_MAX_DELIVERED; + break; + } + return result; } /*********************************************************************//** * @brief - * The handleUFStartState function handles the Start state of the \n + * The handleUFStartState function handles the Start state of the * ultrafiltration state machine. - * @details - * Inputs : maxUFVolumeML - * Outputs : if ultrafiltration prescribed, ultrafiltration time is + * @details Inputs: maxUFVolumeML + * @details Outputs: if ultrafiltration prescribed, ultrafiltration time is * initialized. + * @param dialysisState next dialysis state * @return next ultrafiltration state. *************************************************************************/ -static UF_STATE_T handleUFStartState( void ) +static UF_STATE_T handleUFStartState( DIALYSIS_STATE_T *dialysisState ) { - UF_STATE_T result; + lastUFTimeStamp = getMSTimerCount(); + uFTimeMS = 0; - if ( maxUFVolumeML < NEARLY_ZERO ) - { - result = UF_OFF_STATE; - } - else - { - lastUFTimeStamp = getMSTimerCount(); - uFTimeMS = 0; - result = UF_RUNNING_STATE; - } - - return result; + return UF_RUNNING_STATE; } /*********************************************************************//** * @brief - * The handleUFPausedState function handles the Paused state of the \n + * The handleUFPausedState function handles the Paused state of the * ultrafiltration state machine. - * @details - * Inputs : none - * Outputs : if ultrafiltration resumption requested, UF time is set to resume. + * @details Inputs: none + * @details Outputs: if ultrafiltration resumption requested, UF time is set to resume. + * @param dialysisState next dialysis state * @return next ultrafiltration state. *************************************************************************/ -static UF_STATE_T handleUFPausedState( void ) +static UF_STATE_T handleUFPausedState( DIALYSIS_STATE_T *dialysisState ) { UF_STATE_T result = UF_PAUSED_STATE; - // calculate UF volumes and provide to dialysate outlet pump controller + // Calculate UF volumes and provide to dialysate outlet pump controller updateUFVolumes(); - // TODO - test code - remove later - if ( TRUE == isStopButtonPressed() ) + // Handle saline bolus start request from user + if ( TRUE == salineBolusStartRequested ) { - resumeUF(); - result = UF_RUNNING_STATE; + salineBolusAutoResumeUF = FALSE; + // Go to saline bolus state if we can + if ( SALINE_BOLUS_STATE_IDLE == currentSalineBolusState ) + { + *dialysisState = DIALYSIS_SALINE_BOLUS_STATE; + } + else + { + salineBolusStartRequested = FALSE; + } } return result; } /*********************************************************************//** * @brief - * The handleUFRunningState function handles the Running state of the \n + * The handleUFRunningState function handles the Running state of the * ultrafiltration state machine. - * @details - * Inputs : ms timer, lastUFTimeStamp - * Outputs : UF timer incremented, UF volumes updated and provided to DPo \n + * @details Inputs: ms timer, lastUFTimeStamp + * @details Outputs: UF timer incremented, UF volumes updated and provided to DPo * pump controller. + * @param dialysisState next dialysis state * @return next ultrafiltration state. *************************************************************************/ -static UF_STATE_T handleUFRunningState( void ) +static UF_STATE_T handleUFRunningState( DIALYSIS_STATE_T *dialysisState ) { UF_STATE_T result = UF_RUNNING_STATE; U32 newTime = getMSTimerCount(); U32 msSinceLast = calcTimeBetween( lastUFTimeStamp, newTime ); - // update UF time + // Update UF time uFTimeMS += msSinceLast; lastUFTimeStamp = newTime; - // update UF ref volume in UF running state only + // Update UF ref volume in UF running state only refUFVolume += ( ( (F32)msSinceLast / MS_PER_SECOND ) / SEC_PER_MIN ) * setUFRate; - // calculate UF volumes and provide to dialysate outlet pump controller + // If we have reached target UF volume, UF is complete - set UF rate to zero for remainder of treatment + if ( refUFVolume >= maxUFVolumeML ) + { + setUFRate = 0.0; + } + + // Calculate UF volumes and provide to dialysate outlet pump controller updateUFVolumes(); - // if we've reached target UF volume, UF is complete - if ( measUFVolume >= maxUFVolumeML ) + // Handle saline bolus start request from user + if ( TRUE == salineBolusStartRequested ) { - result = UF_COMPLETED_STATE; + if ( SALINE_BOLUS_STATE_IDLE == currentSalineBolusState ) + { + // Since we were doing UF prior to saline bolus, we want to auto-resume when done + salineBolusAutoResumeUF = TRUE; + // Go to UF paused state + result = UF_PAUSED_STATE; + // Go to saline bolus state + *dialysisState = DIALYSIS_SALINE_BOLUS_STATE; + } + else + { + salineBolusStartRequested = FALSE; + } } - // TODO - test code - remove later - if ( TRUE == isStopButtonPressed() ) + return result; +} + +/*********************************************************************//** + * @brief + * The handleSalineBolusIdleState function handles the idle 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 handleSalineBolusIdleState( DIALYSIS_STATE_T *dialysisState ) +{ + SALINE_BOLUS_STATE_T result = SALINE_BOLUS_STATE_IDLE; + + // Handle saline bolus start request from user + if ( TRUE == salineBolusStartRequested ) { - pauseUF(); - result = UF_PAUSED_STATE; + salineBolusStartRequested = FALSE; + // Cmd all pumps to stop + setBloodPumpTargetFlowRate( 0, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + setDialInPumpTargetFlowRate( 0, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + setDialOutPumpTargetRate( 0, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); + stopSyringePump(); + // Begin saline bolus + result = SALINE_BOLUS_STATE_WAIT_FOR_PUMPS_STOP; } return result; } /*********************************************************************//** * @brief - * The handleUFCompletedOrOffState function handles the UF Off state \n - * of the ultrafiltration state machine. - * @details - * Inputs : none - * Outputs : UF volumes updated and provided to DPo pump controller. - * @return next ultrafiltration state + * The handleSalineBolusWait4Pumps2Stop function handles the wait for pumps + * 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 UF_STATE_T handleUFOffState( void ) +static SALINE_BOLUS_STATE_T handleSalineBolusWait4Pumps2Stop( DIALYSIS_STATE_T *dialysisState ) { - UF_STATE_T result = UF_OFF_STATE; + SALINE_BOLUS_STATE_T result = SALINE_BOLUS_STATE_WAIT_FOR_PUMPS_STOP; - // calculate UF volumes and provide to dialysate outlet pump controller - updateUFVolumes(); + if ( ( FALSE == isBloodPumpRunning() ) && ( FALSE == isDialInPumpRunning() ) && ( FALSE == isDialOutPumpRunning() ) ) + { + // Reset bolus data before we start + bolusSalineVolumeDelivered_mL = 0.0; + bolusSalineLastVolumeTimeStamp = getMSTimerCount(); + // Bypass dialyzer + setValvePosition( VDI, VALVE_POSITION_C_CLOSE ); + setValvePosition( VDO, VALVE_POSITION_C_CLOSE ); + // Switch to saline bag + setValvePosition( VBA, VALVE_POSITION_C_CLOSE ); + // Start blood pump at saline bolus rate +#ifndef RUN_PUMPS_OPEN_LOOP + setBloodPumpTargetFlowRate( SALINE_BOLUS_FLOW_RATE, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); +#else + setBloodPumpTargetFlowRate( SALINE_BOLUS_FLOW_RATE, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); +#endif + // Start dialysate inlet pump at re-circ rate +#ifndef RUN_PUMPS_OPEN_LOOP + setDialInPumpTargetFlowRate( DIALYSATE_FLOW_RATE_FOR_RECIRC, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_CLOSED_LOOP ); +#else + setDialInPumpTargetFlowRate( DIALYSATE_FLOW_RATE_FOR_RECIRC, MOTOR_DIR_FORWARD, PUMP_CONTROL_MODE_OPEN_LOOP ); +#endif + // Begin saline bolus + result = SALINE_BOLUS_STATE_IN_PROGRESS; + } + return result; } /*********************************************************************//** * @brief - * The handleUFCompletedState function handles the UF Completed \n - * state of the ultrafiltration state machine. This is a terminal state. - * @details - * Inputs : none - * Outputs : UF volumes updated and provided to DPo pump controller. - * @return next ultrafiltration state + * The handleSalineBolusInProgressState function handles the in-progress state of the + * saline bolus state machine. + * @details Inputs: none + * @details Outputs: + * @param dialysisState next dialysis state + * @return next saline bolus state *************************************************************************/ -static UF_STATE_T handleUFCompletedState( void ) +static SALINE_BOLUS_STATE_T handleSalineBolusInProgressState( DIALYSIS_STATE_T *dialysisState ) { - UF_STATE_T result = UF_COMPLETED_STATE; + SALINE_BOLUS_STATE_T result = SALINE_BOLUS_STATE_IN_PROGRESS; + 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; - // calculate UF volumes and provide to dialysate outlet pump controller - updateUFVolumes(); + // Update saline bolus volumes + 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() ); + errorFound = TRUE; + result = SALINE_BOLUS_STATE_IDLE; + } + + // Determine if we have reached maximum saline delivery volume + if ( ( totalSalineVolumeDelivered_mL >= (F32)MAX_SALINE_VOLUME_DELIVERED ) ) + { + result = SALINE_BOLUS_STATE_MAX_DELIVERED; + } + else + { + // Determine if bolus is complete + if ( bolusSalineVolumeDelivered_mL >= bolusTargetVolume ) + { + result = SALINE_BOLUS_STATE_IDLE; + } + // User is aborting saline bolus + else if ( TRUE == salineBolusAbortRequested ) + { + 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 + { + activateAlarmNoData( ALARM_ID_SALINE_BOLUS_VOLUME_CHECK_FAILURE ); + errorFound = TRUE; + result = SALINE_BOLUS_STATE_IDLE; + } + } + } + + // Are we stopping the bolus? + if ( result != SALINE_BOLUS_STATE_IN_PROGRESS ) + { + // Hard stop blood and dialysate pumps + signalBloodPumpHardStop(); + signalDialInPumpHardStop(); + // Send last saline bolus data + salineBolusBroadcastTimerCtr = SALINE_BOLUS_DATA_PUB_INTERVAL; + publishSalineBolusData(); + 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 + if ( FALSE == errorFound ) + { + // Resume UF if appropriate + if ( TRUE == salineBolusAutoResumeUF ) + { + salineBolusAutoResumeUF = FALSE; + currentUFState = UF_RUNNING_STATE; + } + // Resume dialysis + transitionToDialysis(); + } + } + return result; } /*********************************************************************//** * @brief - * The checkUF function checks ultrafiltration accuracy for the last \n - * minute and checks total UF volume. Triggers an alarm if out of spec. - * @details - * Inputs : uFAccuracyCheckTimerCtr, lastUFVolumeChecked, measUFVolume - * Outputs : uFAccuracyCheckTimerCtr, lastUFVolumeChecked + * The handleSalineBolusMaxDeliveredState function handles the max saline delivered + * state of the saline bolus state machine. This is a terminal state. + * @details Inputs: none + * @details Outputs: + * @param dialysisState next dialysis state + * @return next saline bolus state + *************************************************************************/ +static SALINE_BOLUS_STATE_T handleSalineBolusMaxDeliveredState( DIALYSIS_STATE_T *dialysisState ) +{ + SALINE_BOLUS_STATE_T result = SALINE_BOLUS_STATE_MAX_DELIVERED; + + // This is a terminal state for a given treatment - no more saline may be delivered to patient + // If we get here, pop back to UF + *dialysisState = DIALYSIS_UF_STATE; + + return result; +} + +/*********************************************************************//** + * @brief + * The publishSalineBolusData function handles the max saline delivered + * state of the saline bolus state machine. This is a terminal state. + * @details Inputs: none + * @details Outputs: + * @param dialysisState next dialysis state + * @return next saline bolus state + *************************************************************************/ +static void publishSalineBolusData( void ) +{ + if ( ++salineBolusBroadcastTimerCtr >= SALINE_BOLUS_DATA_PUB_INTERVAL ) + { + SALINE_BOLUS_DATA_PAYLOAD_T data; + + data.tgtSalineVolumeMl = getTreatmentParameterU32( TREATMENT_PARAM_SALINE_BOLUS_VOLUME ); + 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; + } +} + +/*********************************************************************//** + * @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 * @return none *************************************************************************/ static void checkUFAccuracyAndVolume( void ) { - // check UF accuracy at 1 minute intervals - if ( ++uFAccuracyCheckTimerCtr >= UF_ACCURACY_CHECK_INTERVAL ) - { - F32 uFMeasRate = measUFVolume - lastUFVolumeChecked; + F32 uFMeasRate = measUFVolume - lastUFVolumeChecked; // Volumes are at start/end of 1 hour period, so implied rate is per hour - // check UF max rate - if ( uFMeasRate > MAX_UF_RATE_ML_PER_MIN ) + // 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 { -#ifndef DISABLE_UF_ALARMS SET_ALARM_WITH_1_F32_DATA( ALARM_ID_UF_RATE_TOO_HIGH_ERROR, uFMeasRate ); -#endif } - // reset for next check + } + // 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 ) + // 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 ); + } } } /*********************************************************************//** * @brief - * The updateUFVolumes function updates the ultrafiltration volumes based on \n - * set UF rate, latest UF elapsed time, and the latest load cell weight for the \n - * currently used reservoir. Updated UF volumes are then sent to the dialysate \n + * 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 - * Outputs : refUFVolume, measUFVolume + * @details Inputs: setUFRate, uFTimeMS, load cell weight + * @details Outputs: refUFVolume, measUFVolume * @return none *************************************************************************/ static void updateUFVolumes( void ) { DG_RESERVOIR_ID_T activeRes = getDGActiveReservoir(); - F32 latestResVolume; + LOAD_CELL_ID_T loadCell = ( activeRes == DG_RESERVOIR_1 ? LOAD_CELL_RESERVOIR_1_PRIMARY : LOAD_CELL_RESERVOIR_2_PRIMARY ); + F32 latestResVolume = getLoadCellWeight( loadCell ); + F32 deltaVolume = latestResVolume - resLastVolume[ 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 ); +#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 { - 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 ); } /*********************************************************************//** * @brief - * The setStartReservoirVolume function updates the baseline volume of the \n - * next reservoir to be drawn from before it is switched to (i.e. while it \n + * The setStartReservoirVolume function updates the baseline volume of the + * next reservoir to be drawn from before it is switched to (i.e. while it * is the inactive reservoir) in order to get a more stable volume. - * @details - * Inputs : active reservoir, load cell reading from inactive reservoir - * Outputs : resStartVolume[] + * @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[ reservoirID ] = resVolume; + resFinalVolume[ reservoirID ] = resVolume; - // set starting baseline volume for next reservoir before we switch to it - resStartVolume[ inactiveRes ] = resVolume; + checkLoadCellsStablePrimaryBackupDriftOutOfRange( reservoirID ); } /*********************************************************************//** * @brief - * The signalReservoirsSwitched function informs this module that the \n - * reservoirs have been switched. The UF volume from prior reservoirs is \n + * The signalReservoirsSwitched function informs this module that the + * reservoirs have been switched. The UF volume from prior reservoirs is * tentatively added to with a load cell reading of the inactive reservoir. - * @details - * Inputs : resFinalVolume[], resStartVolume[] - * Outputs : measUFVolumeFromPriorReservoirs + * @details Inputs: resFinalVolume[], resStartVolume[] + * @details Outputs: measUFVolumeFromPriorReservoirs * @return none *************************************************************************/ 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 + // Update UF volume from prior reservoirs per tentative res volume for last reservoir measUFVolumeFromPriorReservoirs += ( resFinalVolume[ inactiveRes ] - resStartVolume[ inactiveRes ] ); } /*********************************************************************//** * @brief - * The setFinalReservoirVolume function updates the UF volume from prior reservoirs \n - * with a more stable final reservoir volume of the prior reservoir after it's \n + * The setFinalReservoirVolume function updates the UF volume from prior reservoirs + * 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 - * Outputs : measUFVolumeFromPriorReservoirs, resFinalVolume[], resStartVolume[] + * @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 ) + // 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 ] ); + + checkLoadCellsStablePrimaryBackupDriftOutOfRange( inactiveRes ); +} + +/*********************************************************************//** + * @brief + * The updateReservoirVolumes function updates the reservoir volumes based + * on new load cell readings. + * @details Inputs: none + * @details Outputs: resCurrVolume[], resLastVolume[] + * @param res1Vol new volume for reservoir 1 + * @param res2Vol new volume for reservoir 2 + * @return none + *************************************************************************/ +void updateReservoirVolumes( F32 res1Vol, F32 res2Vol ) +{ + resLastVolume[ DG_RESERVOIR_1 ] = resCurrVolume[ DG_RESERVOIR_1 ]; + resLastVolume[ DG_RESERVOIR_2 ] = resCurrVolume[ DG_RESERVOIR_2 ]; + resCurrVolume[ DG_RESERVOIR_1 ] = res1Vol; + 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 ] ); +} + +/*********************************************************************//** + * @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 ) { - inactiveRes = DG_RESERVOIR_1; - resVolume = getLoadCellWeightInGrams( LOAD_CELL_RESERVOIR_1_PRIMARY ); + lcPrimaryIndex = LOAD_CELL_RESERVOIR_1_PRIMARY; + lcBackupIndex = LOAD_CELL_RESERVOIR_1_BACKUP; } else { - inactiveRes = DG_RESERVOIR_2; - resVolume = getLoadCellWeightInGrams( LOAD_CELL_RESERVOIR_2_PRIMARY ); + lcPrimaryIndex = LOAD_CELL_RESERVOIR_2_PRIMARY; + lcBackupIndex = LOAD_CELL_RESERVOIR_2_BACKUP; } - // update UF volume from prior reservoirs per final res volume for last reservoir a bit after we've switched and reservoir has settled - measUFVolumeFromPriorReservoirs -= ( resFinalVolume[ inactiveRes ] - resStartVolume[ inactiveRes ] ); - resFinalVolume[ inactiveRes ] = resVolume; - measUFVolumeFromPriorReservoirs += ( resFinalVolume[ inactiveRes ] - resStartVolume[ inactiveRes ] ); + 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; + +#ifndef _RELEASE_ + if ( getSoftwareConfigStatus( SW_CONFIG_DISABLE_ULTRAFILTRATION_ALARMS ) != SW_CONFIG_ENABLE_VALUE ) +#endif + { + // 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 ) + } + } } /**@}*/