Index: firmware/App/Modes/Dialysis.c =================================================================== diff -u -rd91a24c730aeb5cd7e3eba9ef4eca78e442911f8 -r1a685471524555a374854c0c9ec8e208e71fe2df --- firmware/App/Modes/Dialysis.c (.../Dialysis.c) (revision d91a24c730aeb5cd7e3eba9ef4eca78e442911f8) +++ firmware/App/Modes/Dialysis.c (.../Dialysis.c) (revision 1a685471524555a374854c0c9ec8e208e71fe2df) @@ -8,7 +8,7 @@ * @file Dialysis.c * * @author (last) Sean Nash -* @date (last) 03-Sep-2020 +* @date (last) 24-Sep-2020 * * @author (original) Sean * @date (original) 15-Jan-2020 @@ -17,16 +17,20 @@ #include +#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 "SystemCommMessages.h" #include "TaskGeneral.h" #include "Timers.h" -#include "ModeTreatment.h" +#include "Valves.h" /** * @addtogroup Dialysis @@ -39,8 +43,16 @@ #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. -#define UF_ACCURACY_CHECK_INTERVAL ((1 * MIN_PER_HOUR * SEC_PER_MIN * MS_PER_SECOND) / TASK_GENERAL_INTERVAL) ///< Ultrafiltration rate accuracy check interval count +/// Saline bolus data broadcast interval (ms/task time) count. +#define 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) +#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). + // ********** private data ********** static DIALYSIS_STATE_T currentDialysisState; ///< Current state of the dialysis sub-mode state machine. @@ -60,8 +72,19 @@ 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 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. +static BOOL salineBolusAbortRequested; ///< Flag indicates a salien bolus abort has been requested by user. +static BOOL salineBolusAutoResumeUF; ///< 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 U32 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 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. @@ -70,23 +93,29 @@ static DIALYSIS_STATE_T handleDialysisUltrafiltrationState( 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 UF_STATE_T handleUFOffState( DIALYSIS_STATE_T *dialysisState ); +static UF_STATE_T handleUFCompletedState( 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 checkUFAccuracyAndVolume( void ); static void updateUFVolumes( void ); +static void publishSalineBolusData( void ); + /*********************************************************************//** * @brief * 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 ) @@ -97,8 +126,6 @@ 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; @@ -112,35 +139,61 @@ setUFRate = 0.0; maxUFRateAccuracyError_Ml_hr = MAX_UF_RATE_ACCURACY_ERROR_ML_HR; + salineBolusBroadcastTimerCtr = 0; + totalSalineVolumeDelivered = 0.0; + uFAccuracyCheckTimerCtr = 0; lastUFVolumeChecked = 0.0; + + resetSalineBolus(); } /*********************************************************************//** * @brief + * The resetSalineBolus function initializes the saline bolus variables + * at start of treatment or after stopping a bolus (e.g. alarm). Total + * saline bolus volume delivered will not be affected by this function. + * @details Inputs: none + * @details Outputs: Dialysis sub-mode module initialized. + * @return none + *************************************************************************/ +void resetSalineBolus( void ) +{ + salineBolusStartRequested = FALSE; + salineBolusAbortRequested = FALSE; + salineBolusAutoResumeUF = FALSE; + bolusSalineVolumeDelivered = 0.0; + bolusSalineVolumeDelivered_Safety = 0.0; + bolusSalineMotorCount = 0; + if ( currentSalineBolusState != SALINE_BOLUS_STATE_MAX_DELIVERED ) + { + currentSalineBolusState = SALINE_BOLUS_STATE_IDLE; + } +} + +/*********************************************************************//** + * @brief * The transitionToDialysis function prepares for transition to dialysis sub-mode. * This function will reset anything required for resuming dialysis in a * treatment that has already begun. It does not reset everything as dialysis * may be stopped and resumed multiple times due to alarms or user intervention * and we don't want to start the treatment all over again. - * @details - * Inputs : none - * Outputs : none + * @details Inputs: none + * @details Outputs: none * @return none *************************************************************************/ void transitionToDialysis( void ) { - // TODO - anything needed here? + startAirTrapControl(); // TODO - do we need to start this sooner? After prime? } /*********************************************************************//** * @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. + * @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) @@ -174,9 +227,8 @@ * The startDialysis function starts/resumes dialysis. This function * will be called by Treatment Mode when beginning or resuming dialysis * treatment. - * @details - * Inputs : none - * Outputs : Dialysis module prepared for immediate resumption. + * @details Inputs: none + * @details Outputs: Dialysis module prepared for immediate resumption. * @return none *************************************************************************/ void startDialysis( void ) @@ -185,6 +237,12 @@ 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 ); @@ -204,9 +262,8 @@ * 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 - * Outputs : Blood and dialysate pumps stopped. Heparin pump stopped. + * @details Inputs: none + * @details Outputs: Blood and dialysate pumps stopped. Heparin pump stopped. * @return none *************************************************************************/ void stopDialysis( void ) @@ -222,10 +279,93 @@ /*********************************************************************//** * @brief + * The signalStartSalineBolus function handles user request to initiate a + * saline bolus. + * @details Inputs: TBD + * @details Outputs: TBD + * @return none + *************************************************************************/ +void signalStartSalineBolus( 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 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 ( currSalineBolusState != SALINE_BOLUS_STATE_IDLE ) + { + rejReason = REQUEST_REJECT_REASON_SALINE_BOLUS_IN_PROGRESS; + } + else if ( totalSalineVolumeDelivered >= (F32)MAX_SALINE_VOLUME_DELIVERED ) + { + rejReason = REQUEST_REJECT_REASON_SALINE_MAX_VOLUME_REACHED; + } + 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: TBD + * @details Outputs: TBD + * @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 ) @@ -236,9 +376,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 ) @@ -249,9 +388,8 @@ /*********************************************************************//** * @brief * The getSalineBolusState function gets the current saline bolus state. - * @details - * Inputs : currentSalineBolusState - * Outputs : none + * @details Inputs: currentSalineBolusState + * @details Outputs: none * @return currentSalineBolusState *************************************************************************/ SALINE_BOLUS_STATE_T getSalineBolusState( void ) @@ -263,9 +401,8 @@ * @brief * The getUltrafiltrationVolumeCollected function gets the current ultrafiltration * volume collected so far for current treatment. - * @details - * Inputs : measUFVolume, measUFVolumeFromPriorReservoirs - * Outputs : none + * @details Inputs: measUFVolume, measUFVolumeFromPriorReservoirs + * @details Outputs: none * @return currentUFState *************************************************************************/ F32 getUltrafiltrationVolumeCollected( void ) @@ -278,9 +415,8 @@ /*********************************************************************//** * @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 ) @@ -319,17 +455,19 @@ } } - // 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 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 ) @@ -370,17 +508,19 @@ } } - // 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 ) @@ -407,15 +547,17 @@ 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're delivering one or not) + publishSalineBolusData(); } /*********************************************************************//** * @brief * 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 ) @@ -425,27 +567,28 @@ 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(); + currentUFState = handleUFOffState( &result ); break; case UF_COMPLETED_STATE: - currentUFState = handleUFCompletedState(); + 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; break; } @@ -456,31 +599,52 @@ * @brief * The handleDialysisSolutionInfusionState function handles the solution * infustion state of the Dialysis state machine. - * @details - * Inputs : currentSalineBolusState - * Outputs : currentSalineBolusState + * @details Inputs: currentSalineBolusState + * @details Outputs: currentSalineBolusState * @return next Dialysis state. *************************************************************************/ static DIALYSIS_STATE_T handleDialysisSalineBolusState( void ) { 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 * 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; @@ -502,18 +666,44 @@ * @brief * 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 updateUFVolumes(); + // handle saline bolus start request from user + if ( TRUE == salineBolusStartRequested ) + { + salineBolusAutoResumeUF = FALSE; + // go to saline bolus state if we can + if ( SALINE_BOLUS_STATE_IDLE == currentSalineBolusState ) + { + *dialysisState = DIALYSIS_SALINE_BOLUS_STATE; + } + else + { + salineBolusStartRequested = FALSE; + } + } + // handle auto-resume after saline bolus + else if ( TRUE == salineBolusAutoResumeUF ) + { + salineBolusAutoResumeUF = FALSE; + // 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 + lastUFTimeStamp = getMSTimerCount(); + // resume UF + result = UF_RUNNING_STATE; + } + // TODO - test code - remove later if ( TRUE == isStopButtonPressed() ) { @@ -528,13 +718,13 @@ * @brief * 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 + * @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(); @@ -555,6 +745,23 @@ { result = UF_COMPLETED_STATE; } + // handle saline bolus start request from user + else if ( TRUE == salineBolusStartRequested ) + { + if ( SALINE_BOLUS_STATE_IDLE == currentSalineBolusState ) + { + // since we were doing UF prior to saline bolus, we want to auto-resume when done + salineBolusAutoResumeUF = TRUE; + // 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() ) @@ -570,47 +777,301 @@ * @brief * The handleUFCompletedOrOffState function handles the UF Off state * of the ultrafiltration state machine. - * @details - * Inputs : none - * Outputs : UF volumes updated and provided to DPo pump controller. + * @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( void ) +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 - * Outputs : UF volumes updated and provided to DPo pump controller. + * @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( void ) +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 + * @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 ) + { + salineBolusStartRequested = FALSE; + // cmd all pumps to stop +#ifndef RUN_PUMPS_OPEN_LOOP + 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 ); +#else + 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_OPEN_LOOP ); +#endif + // begin saline bolus + result = SALINE_BOLUS_STATE_WAIT_FOR_PUMPS_STOP; + } + + return result; +} + +/*********************************************************************//** + * @brief + * 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 SALINE_BOLUS_STATE_T handleSalineBolusWait4Pumps2Stop( DIALYSIS_STATE_T *dialysisState ) +{ + SALINE_BOLUS_STATE_T result = SALINE_BOLUS_STATE_WAIT_FOR_PUMPS_STOP; + + if ( ( FALSE == isBloodPumpRunning() ) && ( FALSE == isDialInPumpRunning() ) && ( FALSE == isDialOutPumpRunning() ) ) + { + // reset bolus data before we start + bolusSalineVolumeDelivered = 0.0; + bolusSalineVolumeDelivered_Safety = 0.0; + bolusSalineMotorCount = 0; + bolusSalineLastMotorCount = getBloodPumpMotorCount(); + 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 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 SALINE_BOLUS_STATE_T handleSalineBolusInProgressState( DIALYSIS_STATE_T *dialysisState ) +{ + 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 bldFlowRate = getMeasuredBloodFlowRate(); // TODO - should I use raw flow instead of filtered here??? + F32 volSinceLastUpdateMl = bldFlowRate * timeSinceLastVolumeUpdateMin; + U32 bldPumpMotorCount = getBloodPumpMotorCount(); + U32 bldPumpMotorDelta = u32DiffWithWrap( bolusSalineLastMotorCount, bldPumpMotorCount ); + + // update saline bolus volumes + bolusSalineLastVolumeTimeStamp = getMSTimerCount(); + bolusSalineVolumeDelivered += volSinceLastUpdateMl; + totalSalineVolumeDelivered += volSinceLastUpdateMl; + bolusSalineMotorCount += bldPumpMotorDelta; + bolusSalineLastMotorCount = bldPumpMotorCount; + 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 ) + { + SET_ALARM_WITH_1_F32_DATA( ALARM_ID_EMPTY_SALINE_BAG, 0.0 ); // TODO - give data supporting empty bag detection + errorFound = TRUE; + } +#endif + + // determine if we've reached maximum saline delivery volume + if ( ( totalSalineVolumeDelivered >= (F32)MAX_SALINE_VOLUME_DELIVERED ) ) + { + result = SALINE_BOLUS_STATE_MAX_DELIVERED; + } + else + { + // determine if bolus is complete + if ( bolusSalineVolumeDelivered >= bolusTargetVolume ) + { + // if safety thinks we've under-delivered the bolus, throw a fault + if ( bolusSalineVolumeDelivered_Safety < ( bolusTargetVolume * MIN_SALINE_BOLUS_VOLUME_PCT ) ) + { +#ifndef DISABLE_SALINE_BOLUS_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 + else if ( TRUE == salineBolusAbortRequested ) + { + salineBolusAbortRequested = FALSE; + result = SALINE_BOLUS_STATE_IDLE; + } + // determine if safety thinks we've over-delivered the bolus + else if ( bolusSalineVolumeDelivered_Safety > ( bolusTargetVolume * MAX_SALINE_BOLUS_VOLUME_PCT ) ) + { +#ifndef DISABLE_SALINE_BOLUS_CHECKS + SET_ALARM_WITH_2_F32_DATA( ALARM_ID_SALINE_BOLUS_VOLUME_CHECK_FAILURE, bolusTargetVolume, bolusSalineVolumeDelivered_Safety ); + errorFound = TRUE; + result = SALINE_BOLUS_STATE_IDLE; +#endif + } + } + + // 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 + bolusSalineVolumeDelivered = 0.0; + salineBolusBroadcastTimerCtr = SALINE_BOLUS_DATA_PUB_INTERVAL; + publishSalineBolusData(); + // 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 ) + { + currentUFState = UF_RUNNING_STATE; + } + // resume dialysis + startDialysis(); + } + } + + return result; +} + +/*********************************************************************//** + * @brief + * 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.maxSalineVolumeMl = MAX_SALINE_VOLUME_DELIVERED; + data.cumSalineVolumeMl = totalSalineVolumeDelivered; + data.bolSalineVolumeMl = bolusSalineVolumeDelivered; + broadcastSalineBolusData( data ); + 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 - * Outputs : uFAccuracyCheckTimerCtr, lastUFVolumeChecked + * @details Inputs: uFAccuracyCheckTimerCtr, lastUFVolumeChecked, measUFVolume + * @details Outputs: uFAccuracyCheckTimerCtr, lastUFVolumeChecked * @return none *************************************************************************/ static void checkUFAccuracyAndVolume( void ) @@ -646,7 +1107,9 @@ // 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 ); +#endif } } @@ -656,9 +1119,8 @@ * 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 ) @@ -687,9 +1149,8 @@ * 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[] * @return none *************************************************************************/ void setStartReservoirVolume( void ) @@ -719,9 +1180,8 @@ * 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 ) @@ -738,9 +1198,8 @@ * The setFinalReservoirVolume function updates the UF volume from prior reservoirs * with a more stable final reservoir volume of the prior reservoir after it's * 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 )