/************************************************************************** * * Copyright (c) 2019-2020 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 ModeTreatment.c * * @author (last) Sean Nash * @date (last) 12-Oct-2020 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 * ***************************************************************************/ #include "AirTrap.h" #include "AlarmLamp.h" #include "BloodFlow.h" #include "Buttons.h" #include "DGInterface.h" #include "DialInFlow.h" #include "DialOutFlow.h" #include "Dialysis.h" #include "ModeTreatment.h" #include "ModeTreatmentParams.h" #include "OperationModes.h" #include "SystemCommMessages.h" #include "TaskGeneral.h" #include "Timers.h" #include "TreatmentStop.h" #include "Utilities.h" #include "Valves.h" /** * @addtogroup HDTreatmentMode * @{ */ // ********** private definitions ********** #define MAX_TREATMENT_TIME_MINUTES ( 8 * MIN_PER_HOUR ) ///< Maximum treatment time (in minutes). #define MIN_TREATMENT_TIME_MINUTES ( 1 * MIN_PER_HOUR ) ///< Minimum treatment time (in minutes). #define MAX_UF_RATE_ML_MIN ( (F32)2500 / (F32)MIN_PER_HOUR ) ///< Maximum ultrafiltration rate (in mL/min). #define MAX_UF_VOLUME_ML ( 8 * ML_PER_LITER ) ///< Maximum ultrafiltration volume (in mL). #define MAX_DIALYSATE_VOLUME_ML ( 150 * ML_PER_LITER ) ///< Maximum dialysate volume (in mL). #define USER_CONFIRM_CHANGE_TIMEOUT_MS ( 60 * MS_PER_SECOND ) ///< Require user to confirm UF volume change within this time. #define PREVENT_UF_VOL_CHANGE_IF_NEARLY_DONE_SEC ( 10 * SEC_PER_MIN ) ///< Prevent UF volume change if treatment within this much time from end of treatment (in seconds). #define TREATMENT_TIME_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which the treatment time & state data is published on the CAN bus. /// Interval (ms/task time) at which updated, valid treatment setting ranges are published on the CAN bus. #define TREATMENT_SETTINGS_RANGES_PUB_INTERVAL ( ( 60 * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ) #define CALC_ELAPSED_TREAT_TIME_IN_SECS() ( treatmentTimeMS / MS_PER_SECOND ) ///< Macro to calculate the elapsed treatment time in seconds. /// Macro to calculate the elapsed treatment time in minutes. #define CALC_ELAPSED_TREAT_TIME_IN_MIN() ( ( treatmentTimeMS / MS_PER_SECOND ) / SEC_PER_MIN ) /// Macro to calculate the remaining treatment time in seconds. #define CALC_TREAT_TIME_REMAINING_IN_SECS() ( (S32)presTreatmentTimeSecs - (S32)( treatmentTimeMS / MS_PER_SECOND ) ) // ********** private data ********** static TREATMENT_STATE_T currentTreatmentState; ///< Current state (sub-mode) of treatment mode. static U32 presTreatmentTimeSecs; ///< Prescribed treatment time (in minutes). static U32 presBloodFlowRate; ///< Prescribed blood flow rate (in mL/min). static U32 presDialysateFlowRate; ///< Prescribed dialysate flow rate (in mL/min). static F32 presMaxUFVolumeML; ///< Prescribed ultrafiltration volume (in mL). static F32 presUFRate; ///< Prescribed ultrafiltration rate (in mL/min). static U32 treatmentTimeMS; ///< Elapsed treatment time (in ms). static U32 lastTreatmentTimeStamp; ///< Last time elapsed treatment time was recorded (a timestamp in ms). static U32 treatmentTimeBroadcastTimerCtr; ///< Treatment data broadcast timer counter used to schedule when to transmit data. static U32 treatmentParamsRangesBroadcastTimerCtr; ///< Treatment parameter ranges broadcast timer counter used to schedule when to transmit updated ranges. // TODO - test code - remove later static BUTTON_STATE_T lastOffButtonState = BUTTON_STATE_RELEASED; static BOOL pendingUserEndTreatmentRequest; ///< Flag indicates user has requested treatment end. static U32 pendingParamChangesTimer; ///< User required to confirm UF volume change within 1 minute. static F32 pendingUFVolumeChange; ///< An ultrafiltration volume change (mL) is pending user confirmation. static F32 pendingUFRateChange; ///< An ultrafiltration rate change (mL/min) is pending user confirmation. static U32 pendingTreatmentTimeChange; ///< A treatment time change (min) is pending user confirmation. // ********** private function prototypes ********** static void broadcastTreatmentSettingsRanges( void ); static TREATMENT_STATE_T handleTreatmentStartState( void ); static TREATMENT_STATE_T handleTreatmentDialysisState( void ); static TREATMENT_STATE_T handleTreatmentStopState( void ); /*********************************************************************//** * @brief * The initTreatmentMode function initializes the Treatment Mode module. * @details Inputs: none * @details Outputs: Treatment Mode module initialized. * @return none *************************************************************************/ void initTreatmentMode( void ) { currentTreatmentState = TREATMENT_START_STATE; treatmentTimeMS = 0; lastTreatmentTimeStamp = 0; treatmentTimeBroadcastTimerCtr = 0; treatmentParamsRangesBroadcastTimerCtr = TREATMENT_SETTINGS_RANGES_PUB_INTERVAL; // So we send ranges immediately presTreatmentTimeSecs = 0; presBloodFlowRate = 0; presDialysateFlowRate = 0; presMaxUFVolumeML = 0.0; presUFRate = 0.0; pendingUserEndTreatmentRequest = FALSE; pendingParamChangesTimer = 0; pendingUFVolumeChange = 0.0; pendingUFRateChange = 0.0; pendingTreatmentTimeChange = 0; } /*********************************************************************//** * @brief * The transitionToTreatmentMode function prepares for transition to treatment mode. * @details Inputs: none * @details Outputs: * @return none *************************************************************************/ void transitionToTreatmentMode( void ) { // Initialize treatment mode each time we transition to it initTreatmentMode(); initTreatmentReservoirMgmt(); // Initialize treatment sub-modes each time we transition to treatment mode initDialysis(); initTreatmentStop(); // Set user alarm recovery actions allowed in this mode setAlarmUserActionEnabled( ALARM_USER_ACTION_RESUME, TRUE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_RINSEBACK, TRUE ); setAlarmUserActionEnabled( ALARM_USER_ACTION_END_TREATMENT, TRUE ); } /*********************************************************************//** * @brief * The getTreatmentState function gets the current treatment mode state. * @details Inputs: currentTreatmentState * @details Outputs: none * @return currentTreatmentState *************************************************************************/ TREATMENT_STATE_T getTreatmentState( void ) { return currentTreatmentState; } /*********************************************************************//** * @brief * The userRequestEndTreatment function conveys a user request to end the * treatment. * @details Inputs: currentTreatmentState * @details Outputs: response to user request sent * @return TRUE if request accepted, FALSE if not *************************************************************************/ BOOL userRequestEndTreatment( void ) { BOOL result = FALSE; if ( TREATMENT_STOP_STATE == currentTreatmentState ) { pendingUserEndTreatmentRequest = TRUE; result = TRUE; } sendTreatmentEndResponseMsg( result ); return result; } /*********************************************************************//** * @brief * The signalAlarmActionToTreatmentMode function executes the given alarm action * as appropriate while in Treatment Mode. * @details Inputs: none * @details Outputs: given alarm action executed * @param action ID of alarm action to execute * @return none *************************************************************************/ void signalAlarmActionToTreatmentMode( ALARM_ACTION_T action ) { switch( action ) { case ALARM_ACTION_STOP: switch ( currentTreatmentState ) { case TREATMENT_START_STATE: case TREATMENT_DIALYSIS_STATE: stopDialysis(); transitionToTreatmentStop(); currentTreatmentState = TREATMENT_STOP_STATE; break; case TREATMENT_RINSEBACK_STATE: // TODO - implement break; case TREATMENT_RECIRC_STATE: // TODO - implement break; default: // Ignore break; } break; case ALARM_ACTION_RESUME: switch ( currentTreatmentState ) { case TREATMENT_STOP_STATE: lastTreatmentTimeStamp = getMSTimerCount(); startDialysis(); transitionToDialysis(); currentTreatmentState = TREATMENT_DIALYSIS_STATE; break; default: // Ignore break; } break; case ALARM_ACTION_RINSEBACK: // TODO - implement break; case ALARM_ACTION_END_TREATMENT: // TODO - temporary code - implement currentTreatmentState = TREATMENT_END_STATE; break; case ALARM_ACTION_ACK: // Nothing to be done here break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_MODE_TREATMENT_INVALID_ALARM_ACTION, (U32)action ) break; } } /*********************************************************************//** * @brief * The execTreatmentMode function executes the Treatment Mode state machine. * @details Inputs: currentTreatmentState * @details Outputs: currentTreatmentState * @return current state (sub-mode) *************************************************************************/ U32 execTreatmentMode( void ) { BOOL stop = isStopButtonPressed(); if ( TRUE == stop ) { activateAlarmNoData( ALARM_ID_TREATMENT_STOPPED_BY_USER ); } // Treatment mode state machine switch ( currentTreatmentState ) { case TREATMENT_START_STATE: currentTreatmentState = handleTreatmentStartState(); break; case TREATMENT_DIALYSIS_STATE: currentTreatmentState = handleTreatmentDialysisState(); break; case TREATMENT_STOP_STATE: currentTreatmentState = handleTreatmentStopState(); break; case TREATMENT_RINSEBACK_STATE: // TODO - implement break; case TREATMENT_RECIRC_STATE: // TODO - implement break; case TREATMENT_DIALYSIS_END_STATE: // TODO - implement break; case TREATMENT_END_STATE: // TODO - implement endAirTrapControl(); // TODO - move to appropriate place requestNewOperationMode( MODE_POST ); // TODO - test code - remove later break; default: SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_MODE_TREATMENT_INVALID_STATE, currentTreatmentState ); currentTreatmentState = TREATMENT_END_STATE; break; } // Broadcast treatment data broadcastTreatmentTimeAndState(); broadcastTreatmentSettingsRanges(); // Call various execs for treatment mode execTreatmentReservoirMgmt(); execAirTrapMonitorTreatment(); return currentTreatmentState; } /*********************************************************************//** * @brief * The handleTreatmentStartState function handles the Start state of * the Treatment Mode state machine. * @details Inputs: none * @details Outputs: treatmentTimeMS, lastTreatmentTimeStamp * @return next treatment mode state *************************************************************************/ static TREATMENT_STATE_T handleTreatmentStartState( void ) { TREATMENT_STATE_T result = TREATMENT_DIALYSIS_STATE; // Initialize treatment time treatmentTimeMS = 0; lastTreatmentTimeStamp = getMSTimerCount(); presTreatmentTimeSecs = SEC_PER_MIN * getTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_DURATION ); presBloodFlowRate = getTreatmentParameterU32( TREATMENT_PARAM_BLOOD_FLOW ); presDialysateFlowRate = getTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW ); presMaxUFVolumeML = getTreatmentParameterF32( TREATMENT_PARAM_UF_VOLUME ) * (F32)ML_PER_LITER; presUFRate = presMaxUFVolumeML / (F32)getTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_DURATION ); // Kick dialysis sub-mode off setDialysisParams( presBloodFlowRate, presDialysateFlowRate, presMaxUFVolumeML, presUFRate ); startDialysis(); return result; } /*********************************************************************//** * @brief * The handleTreatmentDialysisState function handles the Dialysis state of * the Treatment Mode state machine. * @details Inputs: none * @details Outputs: treatmentTimeMS, lastTreatmentTimeStamp, dialysis sub-mode * executed. * @return next treatment mode state *************************************************************************/ static TREATMENT_STATE_T handleTreatmentDialysisState( void ) { TREATMENT_STATE_T result = TREATMENT_DIALYSIS_STATE; U32 newTime = getMSTimerCount(); U32 msSinceLast = calcTimeBetween( lastTreatmentTimeStamp, newTime ); // Update treatment time (unless delivering a saline bolus) if ( getDialysisState() != DIALYSIS_SALINE_BOLUS_STATE ) { treatmentTimeMS += msSinceLast; } lastTreatmentTimeStamp = newTime; // End treatment if treatment duration has been reached if ( CALC_ELAPSED_TREAT_TIME_IN_SECS() >= presTreatmentTimeSecs ) { result = TREATMENT_END_STATE; } // Otherwise, execute state machine for treatment dialysis sub-mode else { execDialysis(); } // TODO - test code - remove later if ( getOffButtonState() == BUTTON_STATE_PRESSED ) { if ( lastOffButtonState == BUTTON_STATE_RELEASED ) { lastOffButtonState = BUTTON_STATE_PRESSED; stopDialysis(); transitionToTreatmentStop(); result = TREATMENT_STOP_STATE; } } else { lastOffButtonState = BUTTON_STATE_RELEASED; } return result; } /*********************************************************************//** * @brief * The handleTreatmentStopState function executes the Stop state of the * Treatment Mode state machine. * @details Inputs: none * @details Outputs: treatment stop sub-mode executed. * @return next treatment mode state *************************************************************************/ static TREATMENT_STATE_T handleTreatmentStopState( void ) { TREATMENT_STATE_T result = TREATMENT_STOP_STATE; // If user requests treatment end, end treatment if ( TRUE == pendingUserEndTreatmentRequest ) { result = TREATMENT_END_STATE; } else { // Execute state machine for treatment stop sub-mode execTreatmentStop(); } // TODO - test code - remove later if ( getOffButtonState() == BUTTON_STATE_PRESSED ) { if ( lastOffButtonState == BUTTON_STATE_RELEASED ) { lastOffButtonState = BUTTON_STATE_PRESSED; lastTreatmentTimeStamp = getMSTimerCount(); startDialysis(); transitionToDialysis(); result = TREATMENT_DIALYSIS_STATE; } } else { lastOffButtonState = BUTTON_STATE_RELEASED; } return result; } /*********************************************************************//** * @brief * The verifyTreatmentDurationSettingChange function verifies and responds to * the user treatment duration setting change request. * @details Inputs: current operating mode, treatment states and parameters * @details Outputs: response message sent * @param treatmentTime Proposed new treatment duration (in min) * @return TRUE if new treatment duration setting valid, FALSE if not. *************************************************************************/ BOOL verifyTreatmentDurationSettingChange( U32 treatmentTime ) { BOOL result = FALSE; REQUEST_REJECT_REASON_CODE_T rejectReason = REQUEST_REJECT_REASON_NONE; HD_OP_MODE_T currMode = getCurrentOperationMode(); // Check if we are in an appropriate treatment state for settings adjustment if ( ( MODE_TREA == currMode ) && ( currentTreatmentState > TREATMENT_START_STATE ) && ( currentTreatmentState < TREATMENT_DIALYSIS_END_STATE ) && ( CALC_ELAPSED_TREAT_TIME_IN_MIN() < treatmentTime ) && ( treatmentTime >= MIN_TREATMENT_TIME_MINUTES ) ) { F32 uFVolume; U32 dialVolume = presDialysateFlowRate * treatmentTime; // In mL // Always adjust UF volume to accommodate treatment time change (not UF rate) uFVolume = ( (F32)( treatmentTime - CALC_ELAPSED_TREAT_TIME_IN_MIN() ) * presUFRate ) + getUltrafiltrationVolumeCollected(); if ( ( treatmentTime <= MAX_TREATMENT_TIME_MINUTES ) && ( dialVolume <= MAX_DIALYSATE_VOLUME_ML ) && ( uFVolume <= MAX_UF_VOLUME_ML ) ) { result = TRUE; presMaxUFVolumeML = uFVolume; presTreatmentTimeSecs = treatmentTime * SEC_PER_MIN; setDialysisParams( presBloodFlowRate, presDialysateFlowRate, presMaxUFVolumeML, presUFRate ); } else { if ( treatmentTime > MAX_TREATMENT_TIME_MINUTES ) { rejectReason = REQUEST_REJECT_REASON_TREATMENT_TIME_OUT_OF_RANGE; } else if ( uFVolume > MAX_UF_VOLUME_ML ) { rejectReason = REQUEST_REJECT_REASON_UF_VOLUME_OUT_OF_RANGE; } else { rejectReason = REQUEST_REJECT_REASON_DIAL_VOLUME_OUT_OF_RANGE; } } } else { if ( MODE_TREA != currMode ) { rejectReason = REQUEST_REJECT_REASON_NOT_IN_TREATMENT_MODE; } else if ( ( currentTreatmentState <= TREATMENT_START_STATE ) || ( currentTreatmentState >= TREATMENT_DIALYSIS_END_STATE ) ) { rejectReason = REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE; } else if ( treatmentTime < MIN_TREATMENT_TIME_MINUTES ) { rejectReason = REQUEST_REJECT_REASON_TREATMENT_TIME_LESS_THAN_MINIMUM; } else { rejectReason = REQUEST_REJECT_REASON_TREATMENT_TIME_LESS_THAN_CURRENT; } } // Send response to request sendChangeTreatmentDurationResponse( result, rejectReason, presTreatmentTimeSecs / SEC_PER_MIN, presMaxUFVolumeML ); // Send new ranges for settings broadcastTreatmentSettingsRanges(); // Send time/state data immediately for UI update broadcastTreatmentTimeAndState(); return result; } /*********************************************************************//** * @brief * The verifyUFSettingsChange function verifies and responds to a new * ultrafiltration volume setting from the user. * @details Inputs: current operating mode, treatment states and parameters * @details Outputs: response message sent * @param uFVolume New ultrafiltration volume requested by the user * @return TRUE if new UF voluem is valid, FALSE if not. *************************************************************************/ BOOL verifyUFSettingsChange( F32 uFVolume ) { BOOL result = FALSE; REQUEST_REJECT_REASON_CODE_T rejectReason = REQUEST_REJECT_REASON_NONE; S32 timeDiff = 0; F32 rateDiff = 0.0; HD_OP_MODE_T currMode = getCurrentOperationMode(); // Reset pending UF/time settings changes to current values in case request is rejected pendingUFVolumeChange = presMaxUFVolumeML; pendingUFRateChange = presUFRate; pendingTreatmentTimeChange = presTreatmentTimeSecs / SEC_PER_MIN; // Check if we are in an appropriate treatment state for settings adjustment if ( ( MODE_TREA == currMode ) && ( currentTreatmentState > TREATMENT_START_STATE ) && ( currentTreatmentState < TREATMENT_DIALYSIS_END_STATE ) && ( uFVolume <= MAX_UF_VOLUME_ML ) && ( CALC_TREAT_TIME_REMAINING_IN_SECS() >= PREVENT_UF_VOL_CHANGE_IF_NEARLY_DONE_SEC ) ) { DIALYSIS_STATE_T currDialysisState = getDialysisState(); UF_STATE_T currUFState = getUltrafiltrationState(); F32 uFRate = uFVolume / ((F32)presTreatmentTimeSecs / (F32)SEC_PER_MIN); // What UF rate would be if user selected to adjust it U32 trtTime = (S32)( uFVolume / presUFRate ) + 1; // What the treatment duration would be if user selected to adjust it U32 dialVolume = presDialysateFlowRate * trtTime; // What dialysate volume would be if user selected to adjust time // UF should already be paused but let's make sure. if ( ( TREATMENT_DIALYSIS_STATE == currentTreatmentState ) && ( DIALYSIS_UF_STATE == currDialysisState ) && ( UF_RUNNING_STATE == currUFState ) ) { pauseUF(); } // Start t/o timer - user must confirm UF changes within 1 minute from now pendingParamChangesTimer = getMSTimerCount(); // Verify UF rate change would be valid (leave zero if not valid - UI will disable option) if ( uFRate <= (F32)MAX_UF_RATE_ML_MIN ) { result = TRUE; pendingUFVolumeChange = uFVolume; pendingUFRateChange = uFRate; rateDiff = ( uFRate - presUFRate ); } else { pendingUFRateChange = 0.0; } // Verify treatment duration change would be valid (leave zero if not valid - UI will disable option) if ( ( trtTime <= MAX_TREATMENT_TIME_MINUTES ) && ( trtTime >= MIN_TREATMENT_TIME_MINUTES ) && ( dialVolume <= MAX_DIALYSATE_VOLUME_ML ) ) { result = TRUE; pendingUFVolumeChange = uFVolume; pendingTreatmentTimeChange = trtTime; timeDiff = trtTime - ( (U32)( (F32)presTreatmentTimeSecs / (F32)SEC_PER_MIN ) + 1 ); } else { pendingTreatmentTimeChange = 0; } // If neither option works, reject for UF rate if ( FALSE == result ) { rejectReason = REQUEST_REJECT_REASON_UF_RATE_OUT_OF_RANGE; } } else { if ( MODE_TREA != currMode ) { rejectReason = REQUEST_REJECT_REASON_TREATMENT_TIME_OUT_OF_RANGE; } else if ( ( currentTreatmentState <= TREATMENT_START_STATE ) || ( currentTreatmentState >= TREATMENT_DIALYSIS_END_STATE ) ) { rejectReason = REQUEST_REJECT_REASON_INVALID_TREATMENT_STATE; } else if ( uFVolume > MAX_UF_VOLUME_ML ) { rejectReason = REQUEST_REJECT_REASON_UF_VOLUME_OUT_OF_RANGE; } else { rejectReason = REQUEST_REJECT_REASON_TREATMENT_TOO_CLOSE_TO_FINISHED; } } // Respond to UF settings change request sendChangeUFSettingsResponse( result, rejectReason, pendingUFVolumeChange, pendingTreatmentTimeChange, pendingUFRateChange, timeDiff, rateDiff, presUFRate ); return result; } /*********************************************************************//** * @brief * The verifyUFSettingsConfirmation function verifies the user confirmed * ultrafiltration settings change(s) and, if valid, accepts the new settings. * @details Inputs: current operating mode, treatment states and parameters * @details Outputs: response message sent * @param uFVolume New ultrafiltration volume confirmed by the user * @param adjustment The adjustment selected by the user * @return TRUE if new UF settings accepted, FALSE if not. *************************************************************************/ BOOL verifyUFSettingsConfirmation( F32 uFVolume, U32 adjustment ) { BOOL result = FALSE; REQUEST_REJECT_REASON_CODE_T rejectReason = REQUEST_REJECT_REASON_NONE; HD_OP_MODE_T currMode = getCurrentOperationMode(); // User confirmed UF settings change(s)? if ( ( MODE_TREA == currMode ) && ( FALSE == didTimeout( pendingParamChangesTimer, USER_CONFIRM_CHANGE_TIMEOUT_MS ) ) ) { DIALYSIS_STATE_T currDialysisState = getDialysisState(); UF_STATE_T currUFState = getUltrafiltrationState(); result = TRUE; presMaxUFVolumeML = pendingUFVolumeChange; // Which setting does user want to adjust to accommodate the UF volume change? (treatment time or UF rate) if ( UF_ADJ_TREATMENT_TIME == adjustment ) { presTreatmentTimeSecs = pendingTreatmentTimeChange * SEC_PER_MIN; } else // Must be adjusting UF rate then { presUFRate = pendingUFRateChange; } setDialysisParams( presBloodFlowRate, presDialysateFlowRate, presMaxUFVolumeML, presUFRate ); // If UF paused, resume with new settings if ( ( TREATMENT_DIALYSIS_STATE == currentTreatmentState ) && ( DIALYSIS_UF_STATE == currDialysisState ) && ( UF_PAUSED_STATE == currUFState ) ) { resumeUF(); } } else { if ( currMode != MODE_TREA ) { rejectReason = REQUEST_REJECT_REASON_NOT_IN_TREATMENT_MODE; } else { rejectReason = REQUEST_REJECT_REASON_TIMEOUT_WAITING_FOR_USER_CONFIRM; } } // Respond to UF settings change confirmation sendChangeUFSettingsOptionResponse( result, rejectReason, presMaxUFVolumeML, presTreatmentTimeSecs / SEC_PER_MIN, presUFRate ); // Send new ranges for settings broadcastTreatmentSettingsRanges(); // Send time/state data immediately for UI update broadcastTreatmentTimeAndState(); return result; } /*********************************************************************//** * @brief * The verifyBloodAndDialysateRateSettingsChange function verifies the * user blood & dialysate flow rate settings change. * @details Inputs: current operating mode, treatment states and parameters * @details Outputs: response message sent * @param bloodRate Proposed new blood flow rate (in mL/min) * @param dialRate Proposed new dialysate flow rate (in mL/min) * @return TRUE if new blood & dialysate rate settings are valid, FALSE if not. *************************************************************************/ BOOL verifyBloodAndDialysateRateSettingsChange( U32 bloodRate, U32 dialRate ) { BOOL result = FALSE; REQUEST_REJECT_REASON_CODE_T rejectReason = REQUEST_REJECT_REASON_NONE; HD_OP_MODE_T currMode = getCurrentOperationMode(); // Check if we are in treatment mode for settings change if ( MODE_TREA == currMode ) { U32 dialVolume = dialRate * ( (U32)( (F32)presTreatmentTimeSecs / (F32)SEC_PER_MIN ) + 1 ); // In mL // Validate new rates if ( ( bloodRate >= MIN_BLOOD_FLOW_RATE ) && ( bloodRate <= MAX_BLOOD_FLOW_RATE ) && ( dialRate >= MIN_DIAL_IN_FLOW_RATE ) && ( dialRate <= MAX_DIAL_IN_FLOW_RATE ) && ( dialVolume <= MAX_DIALYSATE_VOLUME_ML ) ) { result = TRUE; // Set to new rates presBloodFlowRate = bloodRate; presDialysateFlowRate = dialRate; setDialysisParams( presBloodFlowRate, presDialysateFlowRate, presMaxUFVolumeML, presUFRate ); } else { if ( ( bloodRate < MIN_BLOOD_FLOW_RATE ) || ( bloodRate > MAX_BLOOD_FLOW_RATE ) ) { rejectReason = REQUEST_REJECT_REASON_BLOOD_FLOW_OUT_OF_RANGE; } else if ( ( dialRate < MIN_DIAL_IN_FLOW_RATE ) || ( dialRate > MAX_DIAL_IN_FLOW_RATE ) ) { rejectReason = REQUEST_REJECT_REASON_DIAL_FLOW_OUT_OF_RANGE; } else { rejectReason = REQUEST_REJECT_REASON_DIAL_VOLUME_OUT_OF_RANGE; } } } else { rejectReason = REQUEST_REJECT_REASON_NOT_IN_TREATMENT_MODE; } sendChangeBloodDialysateRateChangeResponse( result, (U32)rejectReason, presBloodFlowRate, presDialysateFlowRate ); // Send new ranges for settings broadcastTreatmentSettingsRanges(); return result; } /*********************************************************************//** * @brief * The verifyPressureLimitsChange function verifies a user change request * for in-line pressure alarm limits. If valid, new limits are accepted * and set. * @details Inputs: none * @details Outputs: new pressure limits set, response sent * @param data payload record with new pressure limits * @return TRUE if new pressure limits accepted, FALSE if not. *************************************************************************/ BOOL verifyPressureLimitsChange( PRESSURE_LIMIT_CHANGE_REQUEST_T *data ) { BOOL result = TRUE; CRITICAL_DATAS_T proposedNewArtLowLimit, proposedNewArtHighLimit; CRITICAL_DATAS_T proposedNewVenLowLimit, proposedNewVenHighLimit; PRESSURE_LIMIT_CHANGE_RESPONSE_T respRecord = { FALSE, 0, 0, 0, 0, 0 }; proposedNewArtLowLimit.sInt = data->artLowLimit; proposedNewArtHighLimit.sInt = data->artHighLimit; proposedNewVenLowLimit.sInt = data->venLowLimit; proposedNewVenHighLimit.sInt = data->venHighLimit; // Check ranges for changed limits if ( FALSE == isTreatmentParamInRange( TREATMENT_PARAM_ART_PRESSURE_LOW_LIMIT, proposedNewArtLowLimit ) ) { respRecord.rejReasonCode = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; result = FALSE; } if ( FALSE == isTreatmentParamInRange( TREATMENT_PARAM_ART_PRESSURE_HIGH_LIMIT, proposedNewArtHighLimit ) ) { respRecord.rejReasonCode = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; result = FALSE; } if ( FALSE == isTreatmentParamInRange( TREATMENT_PARAM_VEN_PRESSURE_LOW_LIMIT, proposedNewVenLowLimit ) ) { respRecord.rejReasonCode = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; result = FALSE; } if ( FALSE == isTreatmentParamInRange( TREATMENT_PARAM_VEN_PRESSURE_HIGH_LIMIT, proposedNewVenHighLimit ) ) { respRecord.rejReasonCode = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; result = FALSE; } // If ranges ok, check separation between low/high limits if ( TRUE == result ) { S32 arterialPresLimitDelta = data->artHighLimit - data->artLowLimit; S32 venousPresLimitDelta = data->venHighLimit - data->venLowLimit; // Check arterial alarm limits dependency if ( arterialPresLimitDelta < MIN_PRESSURE_ALARM_LIMIT_DELTA_MMHG ) { respRecord.rejReasonCode = REQUEST_REJECT_REASON_ARTERIAL_PRESSURE_LOW_VS_HIGH; result = FALSE; } // Check venous alarm limits dependency if ( venousPresLimitDelta < MIN_PRESSURE_ALARM_LIMIT_DELTA_MMHG ) { respRecord.rejReasonCode = REQUEST_REJECT_REASON_VENOUS_PRESSURE_LOW_VS_HIGH; result = FALSE; } } // Set overall result - are changes accepted? respRecord.accepted = result; // If changes accepted, set new pressure limits if ( TRUE == result ) { setTreatmentParameterS32( TREATMENT_PARAM_ART_PRESSURE_LOW_LIMIT, data->artLowLimit ); setTreatmentParameterS32( TREATMENT_PARAM_ART_PRESSURE_HIGH_LIMIT, data->artHighLimit ); setTreatmentParameterS32( TREATMENT_PARAM_VEN_PRESSURE_LOW_LIMIT, data->venLowLimit ); setTreatmentParameterS32( TREATMENT_PARAM_VEN_PRESSURE_HIGH_LIMIT, data->venHighLimit ); } // Read back limits for transmit to UI. respRecord.artLowLimit = getTreatmentParameterS32( TREATMENT_PARAM_ART_PRESSURE_LOW_LIMIT ); respRecord.artHighLimit = getTreatmentParameterS32( TREATMENT_PARAM_ART_PRESSURE_HIGH_LIMIT ); respRecord.venLowLimit = getTreatmentParameterS32( TREATMENT_PARAM_VEN_PRESSURE_LOW_LIMIT ); respRecord.venHighLimit = getTreatmentParameterS32( TREATMENT_PARAM_VEN_PRESSURE_HIGH_LIMIT ); // Send response sendPressureLimitsChangeResponse( &respRecord ); return result; } /*********************************************************************//** * @brief * The broadcastTreatmentTimeAndState function broadcasts treatment time and * state data during treatment. * @details Inputs: treatment time and state data * @details Outputs: treatment time and state messages sent on interval * @return none *************************************************************************/ void broadcastTreatmentTimeAndState( void ) { U32 elapsedTreatmentTimeInSecs; // Update treatment time stats and broadcast - end treatment if time elapsedTreatmentTimeInSecs = treatmentTimeMS / MS_PER_SECOND; if ( elapsedTreatmentTimeInSecs >= presTreatmentTimeSecs ) { stopDialysis(); elapsedTreatmentTimeInSecs = presTreatmentTimeSecs; currentTreatmentState = TREATMENT_DIALYSIS_END_STATE; } // Broadcast treatment time and state data at interval if ( ++treatmentTimeBroadcastTimerCtr >= TREATMENT_TIME_DATA_PUB_INTERVAL ) { U32 timeRemaining = presTreatmentTimeSecs - elapsedTreatmentTimeInSecs; DIALYSIS_STATE_T dialysisState = getDialysisState(); UF_STATE_T uFState = getUltrafiltrationState(); SALINE_BOLUS_STATE_T salineBolusInProgress = getSalineBolusState(); broadcastTreatmentTime( presTreatmentTimeSecs, elapsedTreatmentTimeInSecs, timeRemaining ); broadcastTreatmentState( currentTreatmentState, uFState, salineBolusInProgress ); treatmentTimeBroadcastTimerCtr = 0; } } /*********************************************************************//** * @brief * The broadcastTreatmentSettingsRanges function computes and broadcasts * updated treatment parameter ranges that the user may change during treatment. * It is assumed that prescription settings have already been set prior to calling * this function. * @details Inputs: current operating mode, treatment states and parameters * @details Outputs: valid ranges message sent on interval * @return none *************************************************************************/ static void broadcastTreatmentSettingsRanges( void ) { if ( ++treatmentParamsRangesBroadcastTimerCtr >= TREATMENT_SETTINGS_RANGES_PUB_INTERVAL ) { // Compute minimum treatment duration U32 presTime = ( presTreatmentTimeSecs / SEC_PER_MIN ); U32 elapseTime = CALC_ELAPSED_TREAT_TIME_IN_MIN(); U32 minTime = MAX( (elapseTime + 2), MIN_TREATMENT_TIME_MINUTES ); // Treatment duration cannot be < 1 hour. add two minutes to cover rounding and ensure it is valid for next minute // Compute maximum treatment duration (from both UF and dialysate volume perspectives) U32 maxTimeRem = ( MAX_UF_VOLUME_ML - (U32)getUltrafiltrationVolumeCollected() ) / ( presUFRate > 0.0 ? (U32)presUFRate : 1 ); U32 maxTime1 = minTime + maxTimeRem; U32 maxTime2 = MAX_DIALYSATE_VOLUME_ML / presDialysateFlowRate; U32 maxTime = MAX( maxTime1, maxTime2 ); // Compute minimum UF volume F32 minUFVol = getUltrafiltrationVolumeCollected() + presUFRate; // Compute maximum UF volume (considering from adjustment of UF rate and time perspectives) F32 maxUFVol1 = minUFVol + ( (F32)( presTime - elapseTime ) * MAX_UF_RATE_ML_MIN ); F32 maxUFVol2 = ( presUFRate > 0.0 ? minUFVol + ( (F32)( MAX_TREATMENT_TIME_MINUTES - elapseTime - 1 ) * presUFRate ) : minUFVol ); F32 maxUFVol = MAX( maxUFVol1, maxUFVol2 ); // Compute minimum dialysate flow rate U32 minDialRate = MIN_DIAL_IN_FLOW_RATE; // Compute maximum dialysate flow rate from max dialysate volume perspective U32 maxDialRate = MAX_DIALYSATE_VOLUME_ML / presTime; // Now ensure maximums do not exceed the literal maximums maxTime = MIN( maxTime, MAX_TREATMENT_TIME_MINUTES ); maxUFVol = MIN( maxUFVol, (F32)MAX_UF_VOLUME_ML ); maxDialRate = MIN( maxDialRate, MAX_DIAL_IN_FLOW_RATE ); // Send updated treatment parameter ranges to UI sendTreatmentParamsRangesToUI( minTime, maxTime, minUFVol, maxUFVol, minDialRate, maxDialRate ); treatmentParamsRangesBroadcastTimerCtr = 0; } }