Index: firmware/App/Modes/ModeTreatment.c =================================================================== diff -u -reb877ae36c28eb83553ee11ccccf42e2c4a5b4d2 -rcbb126cfd3a02c08436a66ffecb2bdf926263705 --- firmware/App/Modes/ModeTreatment.c (.../ModeTreatment.c) (revision eb877ae36c28eb83553ee11ccccf42e2c4a5b4d2) +++ firmware/App/Modes/ModeTreatment.c (.../ModeTreatment.c) (revision cbb126cfd3a02c08436a66ffecb2bdf926263705) @@ -1,73 +1,1679 @@ /************************************************************************** - * - * 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) 12-Oct-2020 +* +* @author (original) Dara Navaei +* @date (original) 05-Nov-2019 +* +***************************************************************************/ -#include -#include -#include "Common.h" -#include "OperationModes.h" +#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 "Rinseback.h" +#include "RTC.h" +#include "SystemCommMessages.h" +#include "TaskGeneral.h" +#include "Timers.h" +#include "TreatmentEnd.h" +#include "TreatmentRecirc.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). +#ifndef ALLOW_1_MIN_TREATMENT_DURATION +#define MIN_TREATMENT_TIME_MINUTES ( 1 * MIN_PER_HOUR ) ///< Minimum treatment time (in minutes). +#else +#define MIN_TREATMENT_TIME_MINUTES ( 1 ) ///< Minimum treatment time (in minutes). +#endif +#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). + +/// Interval (ms/task time) at which the treatment time data is published on the CAN bus. +#define TREATMENT_TIME_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) +/// Interval (ms/task time) at which the treatment state data is published on the CAN bus. +#define TREATMENT_STATE_DATA_PUB_INTERVAL ( 250 / TASK_GENERAL_INTERVAL ) +/// 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 ) +/// Interval (ms) at which the treatment periodic data is published on the CAN bus. +static const U32 TREATMENT_PERIODIC_DATA_PUB_INTERVAL = ( 30 * SEC_PER_MIN * MS_PER_SECOND ); +/// Interval (ms) at which the treatment periodic data is logged. +static const U32 TREATMENT_PERIODIC_DATA_LOG_INTERVAL = ( MS_PER_SECOND ); + +#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 BOOL bloodIsPrimed; ///< Flag indicates whether blood-side circuit has been primed with blood. Set FALSE at init and start of rinseback. Set TRUE at end of blood prime. +static BOOL rinsebackDone; ///< Flag indicates whether a rinseback has been completed. Set FALSE at start of blood prime. Set TRUE at init and end of rinseback. +static BOOL treatmentCompleted; ///< Flag indicates whether the treatment has completed. + +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 time data broadcast timer counter used to schedule when to transmit data. +static U32 treatmentStateBroadcastTimerCtr; ///< Treatment state 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. +/// Interval (in task intervals) at which to publish alarm status to CAN bus. +static OVERRIDE_U32_T treatmentTimePublishInterval = { TREATMENT_TIME_DATA_PUB_INTERVAL, TREATMENT_TIME_DATA_PUB_INTERVAL, TREATMENT_TIME_DATA_PUB_INTERVAL, 0 }; +/// Interval (in task intervals) at which to publish alarm status to CAN bus. +static OVERRIDE_U32_T treatmentStatePublishInterval = { TREATMENT_STATE_DATA_PUB_INTERVAL, TREATMENT_STATE_DATA_PUB_INTERVAL, TREATMENT_STATE_DATA_PUB_INTERVAL, 0 }; +/// Interval (in task intervals) at which to publish alarm status to CAN bus. +static OVERRIDE_U32_T treatmentParamRangesPublishInterval = { TREATMENT_SETTINGS_RANGES_PUB_INTERVAL, TREATMENT_SETTINGS_RANGES_PUB_INTERVAL, TREATMENT_SETTINGS_RANGES_PUB_INTERVAL, 0 }; + +static BOOL resumeTreatmentAlarmResponseRequest; ///< Flag indicates user has requested treatment resume. +static BOOL initiateRinsebackAlarmResponseRequest; ///< Flag indicates user has requested rinseback. +static BOOL endTreatmentAlarmResponseRequest; ///< Flag indicates user has requested treatment end. +static BOOL rinsebackToStoppedRequest; ///< Flag indicates user has requested return to treatment from rinseback workflow. +static BOOL endTreatmentRequest; ///< Flag indicates user has requested end of treatment from rinseback workflow. +static BOOL bloodPrimeToDialysisRequest; ///< Flag indicates blood prime has completed and time to start dialysis. +static BOOL rinsebackToRecircRequest; ///< Flag indicates user has requested to proceed to re-circulate sub-mode. +static BOOL treatmentEndToRinsebackRequest; ///< Flag indicates user has requested final rinseback. + +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. + +static U32 lastTreatmentPeriodicDataPublishTimeStamp; ///< Last treatment 30 minutes periodic data publish time stamp. +static U32 lastTreatmentPeriodicDataCollectTimeStamp; ///< Last treatment periodic data collect time stamp. +static F32 lastUltraFiltrationVolume_mL; ///< Last 30 minutes ultra filtration volume in mL. +static F32 bloodFlowRateSum_mL_min; ///< Blood flow rate sum logged every second. +static F32 dialysateFlowRateSum_mL_min; ///< Dialysate flow rate sum logged every second. +static F32 arterialPressureSum_mmHg; ///< Arterial pressure sum logged every second. +static F32 venousPressureSum_mmHg; ///< Venous pressure sum logged every second. + +static F32 treatmentBloodFlowRateTotal_mL_min; ///< Blood flow rate total per treatment logged every second. +static F32 treatmentDialysateFlowRateTotal_mL_min; ///< Dialysate flow rate total per treatment logged every second. +static F32 treatmentDialysateTempTotal_degree_C; ///< Dialysate temperature total per treatment logged every second. +static F32 treatmentArterialPressureTotal_mmHg; ///< Arterial pressure total per treatment logged every second. +static F32 treatmentVenousPressureTotal_mmHg; ///< Venous pressure total per treatment logged every second. +static BOOL sendLastTreatmentPeriodicData; ///< Flag determines if HD needs to send the last treatment periodic data. + +static U32 treatmentStartTimeStamp; ///< Treatment start timestampt for logging purpose. +static U32 treatmentEndTimeStamp; ///< Treatment end timestampt for logging purpose. + // ********** private function prototypes ********** -/************************************************************************* - * @brief initTreatmentMode +static void resetSignalFlags( void ); +static void resetAlarmSignalFlags( void ); +static void broadcastTreatmentSettingsRanges( void ); +static void broadcastTreatmentPeriodicData(); +static TREATMENT_STATE_T handleTreatmentStartState( void ); +static TREATMENT_STATE_T handleTreatmentBloodPrimeState( void ); +static TREATMENT_STATE_T handleTreatmentDialysisState( void ); +static TREATMENT_STATE_T handleTreatmentStopState( void ); +static TREATMENT_STATE_T handleTreatmentRinsebackState( void ); +static TREATMENT_STATE_T handleTreatmentRecircState( void ); +static TREATMENT_STATE_T handleTreatmentEndState( void ); + +/*********************************************************************//** + * @brief * The initTreatmentMode function initializes the Treatment Mode module. - * @details - * Inputs : none - * Outputs : Treatment Mode module initialized. - * @param none + * @details Inputs: none + * @details Outputs: Treatment Mode module initialized. * @return none *************************************************************************/ void initTreatmentMode( void ) { + currentTreatmentState = TREATMENT_START_STATE; + + bloodIsPrimed = FALSE; + treatmentCompleted = FALSE; + rinsebackDone = FALSE; + + treatmentTimeMS = 0; + lastTreatmentTimeStamp = 0; + treatmentTimeBroadcastTimerCtr = TREATMENT_TIME_DATA_PUB_INTERVAL; // So we send time data immediately when we begin treatment mode + treatmentStateBroadcastTimerCtr = TREATMENT_STATE_DATA_PUB_INTERVAL; // So we send state data immediately when we begin treatment mode + treatmentParamsRangesBroadcastTimerCtr = TREATMENT_SETTINGS_RANGES_PUB_INTERVAL; // So we send ranges immediately when we begin treatment mode + + presTreatmentTimeSecs = 0; + presBloodFlowRate = 0; + presDialysateFlowRate = 0; + presMaxUFVolumeML = 0.0; + presUFRate = 0.0; + + resetSignalFlags(); + resetAlarmSignalFlags(); + + pendingParamChangesTimer = 0; + pendingUFVolumeChange = 0.0; + pendingUFRateChange = 0.0; + pendingTreatmentTimeChange = 0; + + lastTreatmentPeriodicDataPublishTimeStamp = 0; + lastTreatmentPeriodicDataCollectTimeStamp = 0; + lastUltraFiltrationVolume_mL = 0.0; + bloodFlowRateSum_mL_min = 0.0; + dialysateFlowRateSum_mL_min = 0.0; + arterialPressureSum_mmHg = 0.0; + venousPressureSum_mmHg = 0.0; + treatmentBloodFlowRateTotal_mL_min = 0.0; + treatmentDialysateFlowRateTotal_mL_min = 0.0; + treatmentDialysateTempTotal_degree_C = 0.0; + treatmentArterialPressureTotal_mmHg = 0.0; + treatmentVenousPressureTotal_mmHg = 0.0; + sendLastTreatmentPeriodicData = FALSE; + + treatmentStartTimeStamp = getRTCTimestamp(); + treatmentEndTimeStamp = 0; } -/************************************************************************* - * @brief transitionToTreatmentMode +/*********************************************************************//** + * @brief + * The resetSignalFlags function resets all non-alarm signal flags. + * @details Inputs: none + * @details Outputs: non-alarm signal flags set to FALSE + * @return none + *************************************************************************/ +static void resetSignalFlags( void ) +{ + rinsebackToStoppedRequest = FALSE; + endTreatmentRequest = FALSE; + rinsebackToRecircRequest = FALSE; + bloodPrimeToDialysisRequest = FALSE; + treatmentEndToRinsebackRequest = FALSE; +} + +/*********************************************************************//** + * @brief + * The resetAlarmSignalFlags function resets all alarm signal flags. + * @details Inputs: none + * @details Outputs: alarm signal flags set to FALSE + * @return none + *************************************************************************/ +static void resetAlarmSignalFlags( void ) +{ + resumeTreatmentAlarmResponseRequest = FALSE; + initiateRinsebackAlarmResponseRequest = FALSE; + endTreatmentAlarmResponseRequest = FALSE; +} + +/*********************************************************************//** + * @brief * The transitionToTreatmentMode function prepares for transition to treatment mode. - * @details - * Inputs : none - * Outputs : - * @param none + * @details Inputs: none + * @details Outputs: * @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 + initBloodPrime(); + initDialysis(); + initTreatmentStop(); + initRinseback(); + initTreatmentRecirc(); + initTreatmentEnd(); } -/************************************************************************* - * @brief execTreatmentMode - * The execTreatmentMode function executes the Treatment Mode state machine. - * @details - * Inputs : none - * Outputs : - * @param none +/*********************************************************************//** + * @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 isTreatmentCompleted function determines whether the treatment has + * completed (indicating treatment duration has elapsed). + * @details Inputs: treatmentCompleted + * @details Outputs: none + * @return treatmentCompleted + *************************************************************************/ +BOOL isTreatmentCompleted( void ) +{ + return treatmentCompleted; +} + +/*********************************************************************//** + * @brief + * The getTreatmentTimeRemainingSecs function determines the number of seconds + * remaining in the treatment. + * @details Inputs: presTreatmentTimeSecs, treatmentTimeMS + * @details Outputs: none + * @return The number of seconds remaining in the treatment. + *************************************************************************/ +U32 getTreatmentTimeRemainingSecs( void ) +{ + U32 result = CALC_TREAT_TIME_REMAINING_IN_SECS(); + + return result; +} + +/*********************************************************************//** + * @brief + * The getActualTreatmentTimeSecs function determines the actual treatment + * duration in seconds. + * @details Inputs: treatmentTimeMS + * @details Outputs: none + * @return The actual treatment duration in seconds. + *************************************************************************/ +U32 getActualTreatmentTimeSecs( void ) +{ + return ( treatmentTimeMS / MS_PER_SECOND ); +} + +/*********************************************************************//** + * @brief + * The getRinsebackCompleted function determines whether a rinseback has been + * completed (indicating blood-side circuit should be primarily saline). + * @details Inputs: rinsebackDone + * @details Outputs: none + * @return rinsebackDone + *************************************************************************/ +BOOL getRinsebackCompleted( void ) +{ + return rinsebackDone; +} + +/*********************************************************************//** + * @brief + * The setRinsebackIsCompleted function sets that flag indicating whether a + * rinseback has been completed. Call this function with TRUE when rinseback + * operation is completed. Call this function with FALSE at start of blood + * prime operation. + * @details Inputs: none + * @details Outputs: rinsebackDone + * @param flag TRUE if rinseback has been completed, FALSE if not * @return none *************************************************************************/ -void execTreatmentMode( void ) +void setRinsebackIsCompleted( BOOL flag ) { + rinsebackDone = flag; +} + +/*********************************************************************//** + * @brief + * The getBloodIsPrimed function determines whether the blood-side circuit + * has been primed with blood (indicating blood-side circuit should be primarily blood). + * @details Inputs: bloodIsPrimed + * @details Outputs: none + * @return bloodIsPrimed + *************************************************************************/ +BOOL getBloodIsPrimed( void ) +{ + return bloodIsPrimed; +} + +/*********************************************************************//** + * @brief + * The setBloodIsPrimed function sets that flag indicating whether the + * blood-side circuit has been primed with blood. Call this function with + * TRUE when blood prime operation is completed. Call this function with + * FALSE at start of treatment or start of rinseback operation. + * @details Inputs: none + * @details Outputs: bloodIsPrimed + * @param flag TRUE if blood side circuit is primed with blood, FALSE if not + * @return none + *************************************************************************/ +void setBloodIsPrimed( BOOL flag ) +{ + bloodIsPrimed = flag; +} + +/*********************************************************************//** + * @brief + * The getTreatmentAvgBloodFlowRate function returns the average blood flow + * rate collected through out the treatment. + * @details Inputs: treatmentTimeMS, treatmentBloodFlowRateTotal_mL_min + * @details Outputs: none + * @return the average blood flow rate + *************************************************************************/ +F32 getTreatmentAvgBloodFlowRate( void ) +{ + U32 const numberOfDataPoint = ( treatmentTimeMS / TREATMENT_PERIODIC_DATA_LOG_INTERVAL ); + F32 result = 0.0; + + if ( numberOfDataPoint > 0 ) + { + result = ( treatmentBloodFlowRateTotal_mL_min / numberOfDataPoint ); + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getTreatmentAvgDialysateFlowRate function returns the average dialysate + * flow rate collected through out the treatment. + * @details Inputs: treatmentTimeMS, treatmentDialysateFlowRateTotal_mL_min + * @details Outputs: none + * @return the average dialysate flow rate + *************************************************************************/ +F32 getTreatmentAvgDialysateFlowRate( void ) +{ + U32 const numberOfDataPoint = ( treatmentTimeMS / TREATMENT_PERIODIC_DATA_LOG_INTERVAL ); + F32 result = 0.0; + + if ( numberOfDataPoint > 0 ) + { + result = ( treatmentDialysateFlowRateTotal_mL_min / numberOfDataPoint ); + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getTreatmentAvgDialysateTemp function returns the average dialysate + * temperature collected through out the treatment. + * @details Inputs: treatmentTimeMS, treatmentDialysateTempTotal_degree_C + * @details Outputs: none + * @return the average dialysate temperature + *************************************************************************/ +F32 getTreatmentAvgDialysateTemp( void ) +{ + U32 const numberOfDataPoint = ( treatmentTimeMS / TREATMENT_PERIODIC_DATA_LOG_INTERVAL ); + F32 result = 0.0; + + if ( numberOfDataPoint > 0 ) + { + result = ( treatmentDialysateTempTotal_degree_C / numberOfDataPoint ); + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getTreatmentAvgArterialPressure function returns the average arterial + * pressure collected through out the treatment. + * @details Inputs: treatmentTimeMS, treatmentArterialPressureTotal_mmHg + * @details Outputs: none + * @return the average arterial pressure + *************************************************************************/ +F32 getTreatmentAvgArterialPressure( void ) +{ + U32 const numberOfDataPoint = ( treatmentTimeMS / TREATMENT_PERIODIC_DATA_LOG_INTERVAL ); + F32 result = 0.0; + + if ( numberOfDataPoint > 0 ) + { + result = ( treatmentArterialPressureTotal_mmHg / numberOfDataPoint ); + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getTreatmentAvgVenousPressure function returns the average venous + * pressure collected through out the treatment. + * @details Inputs: treatmentTimeMS, treatmentVenousPressureTotal_mmHg + * @details Outputs: none + * @return the average venous pressure + *************************************************************************/ +F32 getTreatmentAvgVenousPressure( void ) +{ + U32 const numberOfDataPoint = ( treatmentTimeMS / TREATMENT_PERIODIC_DATA_LOG_INTERVAL ); + F32 result = 0.0; + + if ( numberOfDataPoint > 0 ) + { + result = ( treatmentVenousPressureTotal_mmHg / numberOfDataPoint ); + } + + return result; +} + +/*********************************************************************//** + * @brief + * The getTreatmentStartTimeStamp function returns the treatment start + * time stamp. + * @details Inputs: treatmentStartTimeStamp + * @details Outputs: none + * @return the treatment start time stamp + *************************************************************************/ +U32 getTreatmentStartTimeStamp( void ) +{ + return treatmentStartTimeStamp; +} + +/*********************************************************************//** + * @brief + * The getTreatmentEndTimeStamp function returns the treatment end + * time stamp. + * @details Inputs: treatmentEndTimeStamp + * @details Outputs: none + * @return the treatment end time stamp + *************************************************************************/ +U32 getTreatmentEndTimeStamp( void ) +{ + return treatmentEndTimeStamp; +} + +/*********************************************************************//** + * @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: + // Stop signal actively polled by mode/sub-mode/state + break; + + case ALARM_ACTION_RESUME: + resumeTreatmentAlarmResponseRequest = TRUE; + break; + + case ALARM_ACTION_RINSEBACK: + initiateRinsebackAlarmResponseRequest = TRUE; + break; + + case ALARM_ACTION_END_TREATMENT: + endTreatmentAlarmResponseRequest = TRUE; + 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 signalRinsebackToTreatmentStopped function signals that user wants to return + * to treatment from rinseback workflow. + * @details Inputs: none + * @details Outputs: rinsebackToStoppedRequest + * @return none + *************************************************************************/ +void signalGoToTreatmentStopped( void ) +{ + rinsebackToStoppedRequest = TRUE; +} + +/*********************************************************************//** + * @brief + * The signalGoToRinseback function signals that user wants to perform + * a rinseback operation. + * @details Inputs: none + * @details Outputs: rinsebackRequested + * @return none + *************************************************************************/ +void signalGoToRinseback( void ) +{ + treatmentEndToRinsebackRequest = TRUE; +} + +/*********************************************************************//** + * @brief + * The signalRinsebackToTreatmentEnd function signals that user wants to end the + * treatment from rinseback workflow. + * @details Inputs: none + * @details Outputs: rinsebackToEndTreatmentRequest + * @return none + *************************************************************************/ +void signalEndTreatment( void ) +{ + endTreatmentRequest = TRUE; +} + +/*********************************************************************//** + * @brief + * The signalBloodPrimeToDialysis function signals that blood prime has + * completed and it is time to move on to dialysis. + * @details Inputs: none + * @details Outputs: bloodPrimeToDialysisRequest + * @return none + *************************************************************************/ +void signalBloodPrimeToDialysis( void ) +{ + bloodPrimeToDialysisRequest = TRUE; +} + +/*********************************************************************//** + * @brief + * The signalRinsebackToRecirc function signals that user wants to continue + * on to re-circulate portion of rinseback workflow. + * @details Inputs: none + * @details Outputs: rinsebackToRecircRequest + * @return none + *************************************************************************/ +void signalRinsebackToRecirc( void ) +{ + rinsebackToRecircRequest = TRUE; +} + +/*********************************************************************//** + * @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_BLOOD_PRIME_STATE: + currentTreatmentState = handleTreatmentBloodPrimeState(); + break; + + case TREATMENT_DIALYSIS_STATE: + currentTreatmentState = handleTreatmentDialysisState(); + break; + + case TREATMENT_STOP_STATE: + currentTreatmentState = handleTreatmentStopState(); + break; + + case TREATMENT_RINSEBACK_STATE: + currentTreatmentState = handleTreatmentRinsebackState(); + break; + + case TREATMENT_RECIRC_STATE: + currentTreatmentState = handleTreatmentRecircState(); + break; + + case TREATMENT_END_STATE: + currentTreatmentState = handleTreatmentEndState(); + break; + + default: + SET_ALARM_WITH_2_U32_DATA( ALARM_ID_HD_SOFTWARE_FAULT, SW_FAULT_ID_MODE_TREATMENT_INVALID_STATE, currentTreatmentState ); + break; + } + + // Alarm response request flags should be handled at this point, reset in case not handled in current state + resetAlarmSignalFlags(); + // clear signal flags from sub-modes before calling sub-mode executives + resetSignalFlags(); + + // Broadcast treatment data + broadcastTreatmentTimeAndState(); + broadcastTreatmentSettingsRanges(); + broadcastTreatmentPeriodicData(); + + // Manage air trap control + execAirTrapMonitorTreatment(); + + return currentTreatmentState; +} + +/*********************************************************************//** + * @brief + * The handleTreatmentStartState function handles the Start state of + * the Treatment Mode state machine. Should only pass through this state + * once at very beginning of treatment. + * @details Inputs: none + * @details Outputs: treatmentTimeMS, lastTreatmentTimeStamp + * @return next treatment mode state + *************************************************************************/ +static TREATMENT_STATE_T handleTreatmentStartState( void ) +{ + TREATMENT_STATE_T result = TREATMENT_BLOOD_PRIME_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 ); + + transitionToBloodPrime(); + + return result; +} + +/*********************************************************************//** + * @brief + * The handleTreatmentBloodPrimeState function handles the blood prime state of + * the Treatment Mode state machine. + * @details Inputs: none + * @details Outputs: none + * @return next treatment mode state + *************************************************************************/ +static TREATMENT_STATE_T handleTreatmentBloodPrimeState( void ) +{ + TREATMENT_STATE_T result = TREATMENT_BLOOD_PRIME_STATE; + + setRinsebackIsCompleted( FALSE ); + + // Handle alarm stoppage + if ( TRUE == doesAlarmStatusIndicateStop() ) + { + transitionToTreatmentStop(); + result = TREATMENT_STOP_STATE; + } + else + { + execBloodPrime(); + + // Handle signals from blood prime sub-mode + if ( TRUE == bloodPrimeToDialysisRequest ) + { + lastTreatmentTimeStamp = getMSTimerCount(); + // Kick dialysis sub-mode off + setDialysisParams( presBloodFlowRate, presDialysateFlowRate, presMaxUFVolumeML, presUFRate ); + transitionToDialysis(); + result = TREATMENT_DIALYSIS_STATE; + } + } + + 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 ) + { + treatmentCompleted = TRUE; + sendLastTreatmentPeriodicData = TRUE; + treatmentEndTimeStamp = getRTCTimestamp(); + stopDialysis(); + transitionToTreatmentEnd(); + SET_ALARM_WITH_1_U32_DATA( ALARM_ID_END_OF_TREATMENT_WARNING, presTreatmentTimeSecs ); + result = TREATMENT_END_STATE; + } + // Otherwise, execute state machine for treatment dialysis sub-mode + else + { + execTreatmentReservoirMgmt(); + execDialysis(); + // Handle alarm stoppage + if ( TRUE == doesAlarmStatusIndicateStop() ) + { + stopDialysis(); + transitionToTreatmentStop(); + result = TREATMENT_STOP_STATE; + } + } + + 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 resumption of treatment, resume treatment + if ( TRUE == resumeTreatmentAlarmResponseRequest ) + { + resumeTreatmentAlarmResponseRequest = FALSE; + if ( TRUE == getBloodIsPrimed() ) + { + lastTreatmentTimeStamp = getMSTimerCount(); + transitionToDialysis(); + result = TREATMENT_DIALYSIS_STATE; + } + else + { + transitionToBloodPrime(); + result = TREATMENT_BLOOD_PRIME_STATE; + } + } + // If user requests rinseback, go to rinseback + else if ( TRUE == initiateRinsebackAlarmResponseRequest ) + { + // TODO - should we block rinseback request if no blood in line (i.e. already did a rinseback)? + transitionToRinseback(); + result = TREATMENT_RINSEBACK_STATE; + } + // If user requests treatment end, end treatment + else if ( TRUE == endTreatmentAlarmResponseRequest ) + { + sendLastTreatmentPeriodicData = TRUE; requestNewOperationMode( MODE_POST ); } + // Otherwise execute state machine for treatment stop sub-mode + else + { + execTreatmentReservoirMgmt(); + execTreatmentStop(); + } + + return result; } +/*********************************************************************//** + * @brief + * The handleTreatmentRinsebackState function executes the rinseback state of the + * Treatment Mode state machine. + * @details Inputs: none + * @details Outputs: treatment rinseback sub-mode executed. + * @return next treatment mode state + *************************************************************************/ +static TREATMENT_STATE_T handleTreatmentRinsebackState( void ) +{ + TREATMENT_STATE_T result = TREATMENT_RINSEBACK_STATE; + + // Execute treatment rinseback sub-mode + execRinseback(); + + // Handle signals from rinseback sub-mode + if ( TRUE == rinsebackToRecircRequest ) + { + transitionToTreatmentRecirc(); + result = TREATMENT_RECIRC_STATE; + } + else if ( TRUE == rinsebackToStoppedRequest ) + { + transitionToTreatmentStop(); + result = TREATMENT_STOP_STATE; + } + else if ( TRUE == endTreatmentRequest ) + { + requestNewOperationMode( MODE_POST ); + } + + return result; +} + +/*********************************************************************//** + * @brief + * The handleTreatmentRecircState function executes the re-circulate state of the + * Treatment Mode state machine. + * @details Inputs: none + * @details Outputs: treatment re-circulate sub-mode executed. + * @return next treatment mode state + *************************************************************************/ +static TREATMENT_STATE_T handleTreatmentRecircState( void ) +{ + TREATMENT_STATE_T result = TREATMENT_RECIRC_STATE; + + // Execute treatment re-circ sub-mode + execTreatmentRecirc(); + + // Handle signals from treatment re-circ sub-mode + if ( TRUE == rinsebackToStoppedRequest ) + { + transitionToTreatmentStop(); + result = TREATMENT_STOP_STATE; + } + else if ( TRUE == endTreatmentRequest ) + { + requestNewOperationMode( MODE_POST ); + } + + return result; +} + +/*********************************************************************//** + * @brief + * The handleTreatmentEndState function executes the end state of the + * Treatment Mode state machine. + * @details Inputs: none + * @details Outputs: treatment end sub-mode executed. + * @return next treatment mode state + *************************************************************************/ +static TREATMENT_STATE_T handleTreatmentEndState( void ) +{ + TREATMENT_STATE_T result = TREATMENT_END_STATE; + + // Handle final rinseback alarm response from user + if ( TRUE == initiateRinsebackAlarmResponseRequest ) + { + signalTreatmentEndAlarmRinsebackUserAction(); + } + // Handle alarm response from user to end treatment w/o rinseback + else if ( TRUE == endTreatmentAlarmResponseRequest ) + { + signalTreatmentEndAlarmEndTxUserAction(); + } + // Handle alarm response from user to resume slow blood flow while waiting for rinseback request + else if ( TRUE == resumeTreatmentAlarmResponseRequest ) + { + signalTreatmentEndAlarmResumeUserAction(); + } + + // Execute treatment end sub-mode + execTreatmentEnd(); + + // Handle signals from treatment end sub-mode + if ( TRUE == treatmentEndToRinsebackRequest ) + { + transitionToRinseback(); + result = TREATMENT_RINSEBACK_STATE; + } + else if ( TRUE == endTreatmentRequest ) + { + requestNewOperationMode( MODE_POST ); + } + + 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_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 ) + getUltrafiltrationReferenceVolume(); + if ( ( treatmentTime <= MAX_TREATMENT_TIME_MINUTES ) && + ( dialVolume <= MAX_DIALYSATE_VOLUME_ML ) && + ( uFVolume <= MAX_UF_VOLUME_ML ) ) + { + result = TRUE; + sendTreatmentLogEventData( TREATMENT_DURATION_CHANGE_EVENT, ( presTreatmentTimeSecs / SEC_PER_MIN ), treatmentTime ); + 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_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_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_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(); + + sendTreatmentLogEventData( UF_VOLUME_CHANGE_EVENT, presMaxUFVolumeML, pendingUFVolumeChange ); + 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_SET_BLOOD_FLOW_RATE ) && ( bloodRate <= MAX_SET_BLOOD_FLOW_RATE ) && + ( dialRate >= MIN_DIAL_IN_FLOW_RATE ) && ( dialRate <= MAX_DIAL_IN_FLOW_RATE ) && + ( dialVolume <= MAX_DIALYSATE_VOLUME_ML ) ) + { + result = TRUE; + sendTreatmentLogEventData( BLOOD_FLOW_RATE_CHANGE_EVENT, presBloodFlowRate, bloodRate ); + sendTreatmentLogEventData( DIALYSATE_FLOW_RATE_CHANGE_EVENT, presDialysateFlowRate, dialRate ); + // Set to new rates + presBloodFlowRate = bloodRate; + presDialysateFlowRate = dialRate; + setDialysisParams( presBloodFlowRate, presDialysateFlowRate, presMaxUFVolumeML, presUFRate ); + } + else + { + if ( ( bloodRate < MIN_SET_BLOOD_FLOW_RATE ) || ( bloodRate > MAX_SET_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 ) + { + S32 const artLowLimit = getTreatmentParameterS32( TREATMENT_PARAM_ART_PRESSURE_LOW_LIMIT ); + S32 const artHighLimit = getTreatmentParameterS32( TREATMENT_PARAM_ART_PRESSURE_HIGH_LIMIT ); + S32 const venLowLimit = getTreatmentParameterS32( TREATMENT_PARAM_VEN_PRESSURE_LOW_LIMIT ); + S32 const venHighLimit = getTreatmentParameterS32( TREATMENT_PARAM_VEN_PRESSURE_HIGH_LIMIT ); + + 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 ); + + if ( abs( artLowLimit - data->artLowLimit ) > 0 ) + { + sendTreatmentLogEventData( ARTERIAL_PRESSURE_LOWER_LIMIT_CHANGE_EVENT, (F32)artLowLimit, (F32)data->artLowLimit ); + } + if ( abs( artHighLimit - data->artHighLimit ) > 0 ) + { + sendTreatmentLogEventData( ARTERIAL_PRESSURE_UPPER_LIMIT_CHANGE_EVENT, (F32)artHighLimit, (F32)data->artHighLimit ); + } + if ( abs( venLowLimit - data->venLowLimit ) > 0 ) + { + sendTreatmentLogEventData( VENOUS_PRESSURE_LOWER_LIMIT_CHANGE_EVENT, (F32)venLowLimit, (F32)data->venLowLimit ); + } + if ( abs( venHighLimit - data->venHighLimit ) > 0 ) + { + sendTreatmentLogEventData( VENOUS_PRESSURE_UPPER_LIMIT_CHANGE_EVENT, (F32)venHighLimit, (F32)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 = CALC_ELAPSED_TREAT_TIME_IN_SECS(); + // Broadcast treatment time data at interval + if ( ++treatmentTimeBroadcastTimerCtr >= getU32OverrideValue( &treatmentTimePublishInterval ) ) + { + if ( isTreatmentCompleted() != TRUE ) + { + U32 timeRemaining = presTreatmentTimeSecs - elapsedTreatmentTimeInSecs; + + broadcastTreatmentTime( presTreatmentTimeSecs, elapsedTreatmentTimeInSecs, timeRemaining ); + } + else // If treatment is completed, send zeroes to UI + { + broadcastTreatmentTime( 0, 0, 0 ); + } + treatmentTimeBroadcastTimerCtr = 0; + } + // Broadcast treatment state data at interval + if ( ++treatmentStateBroadcastTimerCtr >= getU32OverrideValue( &treatmentStatePublishInterval ) ) + { + TREATMENT_STATE_DATA_T payload; + + payload.treatmentSubMode = (U32)currentTreatmentState; + payload.uFState = getUltrafiltrationState(); + payload.salineBolusState = getSalineBolusState(); + payload.txStopState = getCurrentTreatmentStopState(); + payload.bldPrimeState = getCurrentBloodPrimeState(); + payload.rinsebackState = getCurrentRinsebackState(); + payload.txRecircState = getCurrentTreatmentRecircState(); + payload.txEndState = getCurrentTreatmentEndState(); + payload.heparinState = getHeparinState(); + payload.dialysisState = getDialysisState(); + + broadcastTreatmentState( payload ); + treatmentStateBroadcastTimerCtr = 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 >= getU32OverrideValue( &treatmentParamRangesPublishInterval ) ) + { + // 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)getUltrafiltrationReferenceVolume() ) / ( 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 = getUltrafiltrationReferenceVolume() + 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; + } +} + +/*********************************************************************//** + * @brief + * The broadcastTreatmentPeriodicData function computes and broadcasts + * periodic treatment data during treatment. + * @details Inputs: treatmentPeriodDataBroadcastTimerCtr + * @details Outputs: collected and sent average treatment data over 30 minutes + * @return none + *************************************************************************/ +static void broadcastTreatmentPeriodicData( void ) +{ + U32 const timeElapsedSinceLastCollect_ms = calcTimeBetween( lastTreatmentPeriodicDataCollectTimeStamp, treatmentTimeMS ); + U32 const timeElapsedSinceLastPublish_ms = calcTimeBetween( lastTreatmentPeriodicDataPublishTimeStamp, treatmentTimeMS ); + + if ( timeElapsedSinceLastCollect_ms >= TREATMENT_PERIODIC_DATA_LOG_INTERVAL ) + { + F32 const arterialPres = getFilteredArterialPressure(); + F32 const venousPres = getFilteredVenousPressure(); + + lastTreatmentPeriodicDataCollectTimeStamp = treatmentTimeMS; + bloodFlowRateSum_mL_min += getMeasuredBloodFlowRate(); + dialysateFlowRateSum_mL_min += getMeasuredDialInFlowRate(); + arterialPressureSum_mmHg += arterialPres; + venousPressureSum_mmHg += venousPres; + + treatmentBloodFlowRateTotal_mL_min += getMeasuredBloodFlowRate(); + treatmentDialysateFlowRateTotal_mL_min += getMeasuredDialInFlowRate(); + treatmentDialysateTempTotal_degree_C += getDialysateTemperature(); + treatmentArterialPressureTotal_mmHg += arterialPres; + treatmentVenousPressureTotal_mmHg += venousPres; + } + + if ( ( timeElapsedSinceLastPublish_ms >= TREATMENT_PERIODIC_DATA_PUB_INTERVAL ) || ( TRUE == sendLastTreatmentPeriodicData ) ) + { + TREATMENT_LOG_DATA_PERIODIC_T periodTreatmentData; + U32 const numberOfDataPoint = timeElapsedSinceLastPublish_ms / TREATMENT_PERIODIC_DATA_LOG_INTERVAL; + + periodTreatmentData.avgUFRate = ( getUltrafiltrationVolumeCollected() - lastUltraFiltrationVolume_mL ) / ( numberOfDataPoint / SEC_PER_MIN ); + periodTreatmentData.avgBloodFlowRate = bloodFlowRateSum_mL_min / numberOfDataPoint; + periodTreatmentData.avgDialysateFlowRate = dialysateFlowRateSum_mL_min / numberOfDataPoint; + periodTreatmentData.avgArterialPressure = arterialPressureSum_mmHg / numberOfDataPoint; + periodTreatmentData.avgVenousPressure = venousPressureSum_mmHg / numberOfDataPoint; + + sendLastTreatmentPeriodicData = FALSE; + lastTreatmentPeriodicDataPublishTimeStamp = treatmentTimeMS; + lastUltraFiltrationVolume_mL = getUltrafiltrationVolumeCollected(); + bloodFlowRateSum_mL_min = 0.0; + dialysateFlowRateSum_mL_min = 0.0; + arterialPressureSum_mmHg = 0.0; + venousPressureSum_mmHg = 0.0; + sendTreatmentPeriodicDataToUI( &periodTreatmentData ); + } +} + + +/************************************************************************* + * TEST SUPPORT FUNCTIONS + *************************************************************************/ + + +/*********************************************************************//** + * @brief + * The testSetTreatmentTimeRemainingOverride function overrides the + * treatment time remaining. + * @details Inputs: presTreatmentTimeSecs, currentTreatmentState + * @details Outputs: treatmentTimeMS + * @param value override treatment time remaining overridden to this (in sec) + * @return TRUE if override successful, FALSE if not + *************************************************************************/ +BOOL testSetTreatmentTimeRemainingOverride( U32 value ) +{ + BOOL result = FALSE; + + if ( ( TRUE == isTestingActivated() ) && ( value < presTreatmentTimeSecs ) ) + { + if ( ( MODE_TREA == getCurrentOperationMode() ) && ( currentTreatmentState < TREATMENT_END_STATE ) ) + { + result = TRUE; + treatmentTimeMS = ( presTreatmentTimeSecs - value ) * MS_PER_SECOND; + } + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testSetTreatmentTimePublishIntervalOverride function sets the override of the + * treatment time data publication interval. + * @details Inputs: none + * @details Outputs: treatmentTimePublishInterval + * @param ms milliseconds between treatment time broadcasts + * @return TRUE if override set successful, FALSE if not + *************************************************************************/ +BOOL testSetTreatmentTimePublishIntervalOverride( U32 ms ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + U32 intvl = ms / TASK_GENERAL_INTERVAL; + + result = TRUE; + treatmentTimePublishInterval.ovData = intvl; + treatmentTimePublishInterval.override = OVERRIDE_KEY; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetTreatmentTimePublishIntervalOverride function resets the override of the + * treatment time data publication interval. + * @details Inputs: none + * @details Outputs: treatmentTimePublishInterval + * @return TRUE if override reset successful, FALSE if not + *************************************************************************/ +BOOL testResetTreatmentTimePublishIntervalOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + treatmentTimePublishInterval.override = OVERRIDE_RESET; + treatmentTimePublishInterval.ovData = treatmentTimePublishInterval.ovInitData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testSetTreatmentStatePublishIntervalOverride function sets the override of the + * treatment state data publication interval. + * @details Inputs: none + * @details Outputs: treatmentStatePublishInterval + * @param ms milliseconds between treatment state broadcasts + * @return TRUE if override set successful, FALSE if not + *************************************************************************/ +BOOL testSetTreatmentStatePublishIntervalOverride( U32 ms ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + U32 intvl = ms / TASK_GENERAL_INTERVAL; + + result = TRUE; + treatmentStatePublishInterval.ovData = intvl; + treatmentStatePublishInterval.override = OVERRIDE_KEY; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetTreatmentStatePublishIntervalOverride function resets the override of the + * treatment state data publication interval. + * @details Inputs: none + * @details Outputs: treatmentStatePublishInterval + * @return TRUE if override reset successful, FALSE if not + *************************************************************************/ +BOOL testResetTreatmentStatePublishIntervalOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + treatmentStatePublishInterval.override = OVERRIDE_RESET; + treatmentStatePublishInterval.ovData = treatmentStatePublishInterval.ovInitData; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testSetTreatmentParamRangesPublishIntervalOverride function sets the override of the + * treatment param ranges data publication interval. + * @details Inputs: none + * @details Outputs: treatmentParamRangesPublishInterval + * @param ms milliseconds between treatment param range broadcasts + * @return TRUE if override set successful, FALSE if not + *************************************************************************/ +BOOL testSetTreatmentParamRangesPublishIntervalOverride( U32 ms ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + U32 intvl = ms / TASK_GENERAL_INTERVAL; + + result = TRUE; + treatmentParamRangesPublishInterval.ovData = intvl; + treatmentParamRangesPublishInterval.override = OVERRIDE_KEY; + } + + return result; +} + +/*********************************************************************//** + * @brief + * The testResetTreatmentParamRangesPublishIntervalOverride function resets the override of the + * treatment param ranges data publication interval. + * @details Inputs: none + * @details Outputs: treatmentParamRangesPublishInterval + * @return TRUE if override reset successful, FALSE if not + *************************************************************************/ +BOOL testResetTreatmentParamRangesPublishIntervalOverride( void ) +{ + BOOL result = FALSE; + + if ( TRUE == isTestingActivated() ) + { + result = TRUE; + treatmentParamRangesPublishInterval.override = OVERRIDE_RESET; + treatmentParamRangesPublishInterval.ovData = treatmentParamRangesPublishInterval.ovInitData; + } + + return result; +} + +/**@}*/