Index: firmware/App/Modes/ModeTreatment.c =================================================================== diff -u -reb877ae36c28eb83553ee11ccccf42e2c4a5b4d2 -rf88ce1aa5a00106b6ba51a625fe4c1d8f0408cfd --- firmware/App/Modes/ModeTreatment.c (.../ModeTreatment.c) (revision eb877ae36c28eb83553ee11ccccf42e2c4a5b4d2) +++ firmware/App/Modes/ModeTreatment.c (.../ModeTreatment.c) (revision f88ce1aa5a00106b6ba51a625fe4c1d8f0408cfd) @@ -1,73 +1,749 @@ /************************************************************************** - * - * Copyright (c) 2019-2019 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 - * - * @date 19-Sep-2019 - * @author S. Nash - * - * @brief Top-level state machine for the treatment mode. - * - **************************************************************************/ +* +* 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) 02-Jul-2020 +* +* @author (original) Dara Navaei +* @date (original) 05-Nov-2019 +* +***************************************************************************/ -#include -#include -#include "Common.h" +#include "AlarmLamp.h" +#include "BloodFlow.h" +#include "Buttons.h" +#include "DGInterface.h" +#include "DialInFlow.h" +#include "DialOutFlow.h" +#include "Dialysis.h" +#include "TaskGeneral.h" #include "OperationModes.h" +#include "SystemCommMessages.h" +#include "Timers.h" +#include "TreatmentStop.h" #include "ModeTreatment.h" +#ifdef RM46_EVAL_BOARD_TARGET + #include "Timers.h" + static U32 start; +#endif + /** + * @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. +#define TREATMENT_SETTINGS_RANGES_PUB_INTERVAL ( ( 60 * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ) ///< Interval (ms/task time) at which updated, valid treatment setting ranges are published on the CAN bus. + +#define CALC_ELAPSED_TREAT_TIME_IN_SECS() ( treatmentTimeMS / MS_PER_SECOND ) ///< Macro to calculate the elapsed treatment time in seconds. +#define CALC_ELAPSED_TREAT_TIME_IN_MIN() ( ( treatmentTimeMS / MS_PER_SECOND ) / SEC_PER_MIN ) ///< Macro to calculate the elapsed treatment time in minutes. +#define CALC_TREAT_TIME_REMAINING_IN_SECS() ( (S32)presTreatmentTimeSecs - (S32)( treatmentTimeMS / MS_PER_SECOND ) ) ///< Macro to calculate the remaining treatment time in seconds. + // ********** 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. + +static BUTTON_STATE_T lastOffButtonState = BUTTON_STATE_RELEASED; // TODO - test code - remove later + +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 ********** -/************************************************************************* - * @brief initTreatmentMode +static void broadcastTreatmentTimeAndState( void ); +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 * Outputs : Treatment Mode module initialized. - * @param none * @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; + + pendingParamChangesTimer = 0; + pendingUFVolumeChange = 0.0; + pendingUFRateChange = 0.0; + pendingTreatmentTimeChange = 0; } -/************************************************************************* - * @brief transitionToTreatmentMode +/*********************************************************************//** + * @brief * The transitionToTreatmentMode function prepares for transition to treatment mode. * @details * Inputs : none * Outputs : - * @param none * @return none *************************************************************************/ void transitionToTreatmentMode( void ) { - // temporary test code - alarm lamp medium alarm - requestAlarmLampPattern( LAMP_PATTERN_MED_ALARM ); + // initialize treatment mode each time we transition to it + initTreatmentMode(); + initTreatmentReservoirMgmt(); + // initialize treatment sub-modes each time we transition to treatment mode + initDialysis(); + initTreatmentStop(); + // temporary test code. TODO - remove later +#ifndef UF_TEST_ENABLED + setBloodPumpTargetFlowRate( 400, MOTOR_DIR_REVERSE, PUMP_CONTROL_MODE_OPEN_LOOP ); + setDialInPumpTargetFlowRate( 400, MOTOR_DIR_REVERSE, PUMP_CONTROL_MODE_OPEN_LOOP ); + setDialOutPumpTargetRate( 400, MOTOR_DIR_REVERSE, PUMP_CONTROL_MODE_OPEN_LOOP ); +#endif +#ifdef RM46_EVAL_BOARD_TARGET + // TODO - temporary test code for eval board + start = getMSTimerCount(); +#endif } -/************************************************************************* - * @brief execTreatmentMode +/*********************************************************************//** + * @brief + * The getTreatmentState function gets the current treatment mode state. + * @details + * Inputs : currentTreatmentState + * Outputs : none + * @return currentTreatmentState + *************************************************************************/ +TREATMENT_STATE_T getTreatmentState( void ) +{ + return currentTreatmentState; +} + +/*********************************************************************//** + * @brief * The execTreatmentMode function executes the Treatment Mode state machine. * @details * Inputs : none * Outputs : - * @param none - * @return none + * @return current state (sub-mode) *************************************************************************/ -void execTreatmentMode( void ) +U32 execTreatmentMode( void ) { +#ifndef UF_TEST_ENABLED BOOL stop = isStopButtonPressed(); if ( TRUE == stop ) { requestNewOperationMode( MODE_POST ); } +#else + // 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_RINSEBACK_PAUSE_STATE: + // TODO - implement + break; + + case TREATMENT_RECIRC_SETUP_STATE: + // TODO - implement + break; + + case TREATMENT_RECIRC_STATE: + // TODO - implement + break; + + case TREATMENT_RECIRC_PAUSE_STATE: + // TODO - implement + break; + + case TREATMENT_RECIRC_STOP_STATE: + // TODO - implement + break; + + case TREATMENT_DIALYSIS_END_STATE: + // TODO - implement + break; + + case TREATMENT_END_STATE: + // TODO - implement + 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; + } + + broadcastTreatmentTimeAndState(); + broadcastTreatmentSettingsRanges(); + + execTreatmentReservoirMgmt(); +#endif +#ifdef RM46_EVAL_BOARD_TARGET + // TODO - temporary test code for eval board - move to next mode after 5 min + if ( TRUE == didTimeout( start, 300000U ) ) + { + requestNewOperationMode( MODE_POST ); + } +#endif + + return currentTreatmentState; } +/*********************************************************************//** + * @brief + * The handleTreatmentStartState function handles the Start state of \n + * the Treatment Mode state machine. + * @details + * Inputs : none + * Outputs : none + * @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(); + + // get prescription settings TODO - hard-coded for now + presTreatmentTimeSecs = 3600; + presBloodFlowRate = 300; + presDialysateFlowRate = 300; + presMaxUFVolumeML = 600.0; + presUFRate = 10.0; + + // kick dialysis sub-mode off + setDialysisParams( presBloodFlowRate, presDialysateFlowRate, presMaxUFVolumeML, presUFRate ); + startDialysis(); + + return result; +} + +/*********************************************************************//** + * @brief + * The handleTreatmentDialysisState function handles the Dialysis state of \n + * the Treatment Mode state machine. + * @details + * Inputs : none + * Outputs : none + * @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; + + if ( CALC_ELAPSED_TREAT_TIME_IN_SECS() >= presTreatmentTimeSecs ) + { + result = TREATMENT_END_STATE; + } + else + { + // execute state machine for treatment dialysis sub-mode + 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 \n + * Treatment Mode state machine. + * @details + * Inputs : none + * Outputs : none + * @return next treatment mode state + *************************************************************************/ +static TREATMENT_STATE_T handleTreatmentStopState( void ) +{ + TREATMENT_STATE_T result = TREATMENT_STOP_STATE; + + // 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 \n + * the user treatment duration setting change request. + * @details + * Inputs : current operating mode, treatment states and parameters + * 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(); + + return result; +} + +/*********************************************************************//** + * @brief + * The verifyUFSettingsChange function verifies and responds to a new \n + * ultrafiltration volume setting from the user. + * @details + * Inputs : current operating mode, treatment states and parameters + * 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 \n + * ultrafiltration settings change(s) and, if valid, accepts the new settings. + * @details + * Inputs : current operating mode, treatment states and parameters + * 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(); + + return result; +} + +/*********************************************************************//** + * @brief + * The verifyBloodAndDialysateRateSettingsChange function verifies the \n + * user blood & dialysate flow rate settings change. + * @details + * Inputs : current operating mode, treatment states and parameters + * 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 broadcastTreatmentTimeAndState function broadcasts treatment time and \n + * state data during treatment. + * @details + * Inputs : treatment time and state data + * Outputs : treatment time and state messages sent on interval + * @return none + *************************************************************************/ +static 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(); + BOOL salineBolusInProgress = ( dialysisState == DIALYSIS_SALINE_BOLUS_STATE ? TRUE : FALSE ); + + broadcastTreatmentTime( presTreatmentTimeSecs, elapsedTreatmentTimeInSecs, timeRemaining ); + broadcastTreatmentState( currentTreatmentState, uFState, salineBolusInProgress ); + treatmentTimeBroadcastTimerCtr = 0; + } +} + +/*********************************************************************//** + * @brief + * The broadcastTreatmentSettingsRanges function computes and broadcasts \n + * updated treatment parameter ranges that the user may change during treatment. \n + * It is assumed that prescription settings have already been set prior to calling \n + * this function. + * @details + * Inputs : current operating mode, treatment states and parameters + * 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's 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; + } +}