Index: firmware/App/Modes/ModeTreatment.c =================================================================== diff -u -rf88ce1aa5a00106b6ba51a625fe4c1d8f0408cfd -r8e7158d8231435496fcf1d5649e51babf859ccc7 --- firmware/App/Modes/ModeTreatment.c (.../ModeTreatment.c) (revision f88ce1aa5a00106b6ba51a625fe4c1d8f0408cfd) +++ firmware/App/Modes/ModeTreatment.c (.../ModeTreatment.c) (revision 8e7158d8231435496fcf1d5649e51babf859ccc7) @@ -1,42 +1,46 @@ /************************************************************************** * -* Copyright (c) 2019-2020 Diality Inc. - All Rights Reserved. +* Copyright (c) 2019-2021 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 +* @date (last) 01-Dec-2020 * * @author (original) Dara Navaei * @date (original) 05-Nov-2019 * ***************************************************************************/ +#include "AirTrap.h" #include "AlarmLamp.h" #include "BloodFlow.h" #include "Buttons.h" #include "DGInterface.h" #include "DialInFlow.h" #include "DialOutFlow.h" #include "Dialysis.h" -#include "TaskGeneral.h" +#include "ModeTreatment.h" +#include "ModeTreatmentParams.h" #include "OperationModes.h" #include "SystemCommMessages.h" +#include "TaskGeneral.h" #include "Timers.h" #include "TreatmentStop.h" -#include "ModeTreatment.h" +#include "Utilities.h" +#include "Valves.h" #ifdef RM46_EVAL_BOARD_TARGET #include "Timers.h" static U32 start; #endif - /** - * @addtogroup HDTreatmentMode - * @{ - */ +/** + * @addtogroup HDTreatmentMode + * @{ + */ // ********** private definitions ********** @@ -50,11 +54,14 @@ #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. +/// Interval (ms/task time) at which updated, valid treatment setting ranges are published on the CAN bus. +#define TREATMENT_SETTINGS_RANGES_PUB_INTERVAL ( ( 60 * MS_PER_SECOND ) / TASK_GENERAL_INTERVAL ) -#define CALC_ELAPSED_TREAT_TIME_IN_SECS() ( treatmentTimeMS / MS_PER_SECOND ) ///< Macro to calculate the elapsed treatment time in seconds. -#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. +#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 ********** @@ -70,8 +77,10 @@ static U32 lastTreatmentTimeStamp; ///< Last time elapsed treatment time was recorded (a timestamp in ms). static U32 treatmentTimeBroadcastTimerCtr; ///< Treatment data broadcast timer counter used to schedule when to transmit data. static U32 treatmentParamsRangesBroadcastTimerCtr; ///< Treatment parameter ranges broadcast timer counter used to schedule when to transmit updated ranges. +// TODO - test code - remove later +static BUTTON_STATE_T lastOffButtonState = BUTTON_STATE_RELEASED; -static BUTTON_STATE_T lastOffButtonState = BUTTON_STATE_RELEASED; // TODO - test code - remove later +static BOOL pendingUserEndTreatmentRequest; ///< Flag indicates user has requested treatment end. static U32 pendingParamChangesTimer; ///< User required to confirm UF volume change within 1 minute. static F32 pendingUFVolumeChange; ///< An ultrafiltration volume change (mL) is pending user confirmation. @@ -80,7 +89,6 @@ // ********** private function prototypes ********** -static void broadcastTreatmentTimeAndState( void ); static void broadcastTreatmentSettingsRanges( void ); static TREATMENT_STATE_T handleTreatmentStartState( void ); static TREATMENT_STATE_T handleTreatmentDialysisState( void ); @@ -89,9 +97,8 @@ /*********************************************************************//** * @brief * The initTreatmentMode function initializes the Treatment Mode module. - * @details - * Inputs : none - * Outputs : Treatment Mode module initialized. + * @details Inputs: none + * @details Outputs: Treatment Mode module initialized. * @return none *************************************************************************/ void initTreatmentMode( void ) @@ -109,6 +116,8 @@ presMaxUFVolumeML = 0.0; presUFRate = 0.0; + pendingUserEndTreatmentRequest = FALSE; + pendingParamChangesTimer = 0; pendingUFVolumeChange = 0.0; pendingUFRateChange = 0.0; @@ -118,9 +127,8 @@ /*********************************************************************//** * @brief * The transitionToTreatmentMode function prepares for transition to treatment mode. - * @details - * Inputs : none - * Outputs : + * @details Inputs: none + * @details Outputs: * @return none *************************************************************************/ void transitionToTreatmentMode( void ) @@ -131,12 +139,6 @@ // 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(); @@ -146,9 +148,8 @@ /*********************************************************************//** * @brief * The getTreatmentState function gets the current treatment mode state. - * @details - * Inputs : currentTreatmentState - * Outputs : none + * @details Inputs: currentTreatmentState + * @details Outputs: none * @return currentTreatmentState *************************************************************************/ TREATMENT_STATE_T getTreatmentState( void ) @@ -158,22 +159,43 @@ /*********************************************************************//** * @brief + * The userRequestEndTreatment function conveys a user request to end the + * treatment. + * @details Inputs: currentTreatmentState + * @details Outputs: response to user request sent + * @return TRUE if request accepted, FALSE if not + *************************************************************************/ +BOOL userRequestEndTreatment( void ) +{ + BOOL result = FALSE; + + if ( TREATMENT_STOP_STATE == currentTreatmentState ) + { + pendingUserEndTreatmentRequest = TRUE; + result = TRUE; + } + sendTreatmentEndResponseMsg( result ); + + return result; +} + +/*********************************************************************//** + * @brief * The execTreatmentMode function executes the Treatment Mode state machine. - * @details - * Inputs : none - * Outputs : + * @details Inputs: currentTreatmentState + * @details Outputs: currentTreatmentState * @return current state (sub-mode) *************************************************************************/ U32 execTreatmentMode( void ) { -#ifndef UF_TEST_ENABLED +#ifdef DISABLE_UI_TREATMENT_WORKFLOW BOOL stop = isStopButtonPressed(); if ( TRUE == stop ) { requestNewOperationMode( MODE_POST ); } -#else +#endif // treatment mode state machine switch ( currentTreatmentState ) { @@ -219,6 +241,7 @@ case TREATMENT_END_STATE: // TODO - implement + endAirTrapControl(); // TODO - move to appropriate place requestNewOperationMode( MODE_POST ); // TODO - test code - remove later break; @@ -227,12 +250,13 @@ currentTreatmentState = TREATMENT_END_STATE; break; } - + // broadcast treatment data broadcastTreatmentTimeAndState(); broadcastTreatmentSettingsRanges(); + // call various execs for treatment mode execTreatmentReservoirMgmt(); -#endif + execAirTrapMonitorTreatment(); #ifdef RM46_EVAL_BOARD_TARGET // TODO - temporary test code for eval board - move to next mode after 5 min if ( TRUE == didTimeout( start, 300000U ) ) @@ -246,11 +270,10 @@ /*********************************************************************//** * @brief - * The handleTreatmentStartState function handles the Start state of \n + * The handleTreatmentStartState function handles the Start state of * the Treatment Mode state machine. - * @details - * Inputs : none - * Outputs : none + * @details Inputs: none + * @details Outputs: treatmentTimeMS, lastTreatmentTimeStamp * @return next treatment mode state *************************************************************************/ static TREATMENT_STATE_T handleTreatmentStartState( void ) @@ -261,12 +284,20 @@ treatmentTimeMS = 0; lastTreatmentTimeStamp = getMSTimerCount(); - // get prescription settings TODO - hard-coded for now - presTreatmentTimeSecs = 3600; +#ifndef DISABLE_UI_TREATMENT_WORKFLOW + 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 ); +#else + // TODO - test code + presTreatmentTimeSecs = 14400; presBloodFlowRate = 300; presDialysateFlowRate = 300; - presMaxUFVolumeML = 600.0; - presUFRate = 10.0; + presMaxUFVolumeML = 2400.0; + presUFRate = 0.0; +#endif // kick dialysis sub-mode off setDialysisParams( presBloodFlowRate, presDialysateFlowRate, presMaxUFVolumeML, presUFRate ); @@ -277,11 +308,11 @@ /*********************************************************************//** * @brief - * The handleTreatmentDialysisState function handles the Dialysis state of \n + * The handleTreatmentDialysisState function handles the Dialysis state of * the Treatment Mode state machine. - * @details - * Inputs : none - * Outputs : none + * @details Inputs: none + * @details Outputs: treatmentTimeMS, lastTreatmentTimeStamp, dialysis sub-mode + * executed. * @return next treatment mode state *************************************************************************/ static TREATMENT_STATE_T handleTreatmentDialysisState( void ) @@ -297,13 +328,14 @@ } lastTreatmentTimeStamp = newTime; + // end treatment if treatment duration has been reached if ( CALC_ELAPSED_TREAT_TIME_IN_SECS() >= presTreatmentTimeSecs ) { result = TREATMENT_END_STATE; } + // otherwise, execute state machine for treatment dialysis sub-mode else { - // execute state machine for treatment dialysis sub-mode execDialysis(); } @@ -328,19 +360,26 @@ /*********************************************************************//** * @brief - * The handleTreatmentStopState function executes the Stop state of the \n + * The handleTreatmentStopState function executes the Stop state of the * Treatment Mode state machine. - * @details - * Inputs : none - * Outputs : none + * @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; - // execute state machine for treatment stop sub-mode - execTreatmentStop(); + // if user requests treatment end, end treatment + if ( TRUE == pendingUserEndTreatmentRequest ) + { + result = TREATMENT_END_STATE; + } + else + { + // execute state machine for treatment stop sub-mode + execTreatmentStop(); + } // TODO - test code - remove later if ( getOffButtonState() == BUTTON_STATE_PRESSED ) @@ -364,11 +403,10 @@ /*********************************************************************//** * @brief - * The verifyTreatmentDurationSettingChange function verifies and responds to \n + * The verifyTreatmentDurationSettingChange function verifies and responds to * the user treatment duration setting change request. - * @details - * Inputs : current operating mode, treatment states and parameters - * Outputs : response message sent + * @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. *************************************************************************/ @@ -437,17 +475,18 @@ 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 \n + * The verifyUFSettingsChange function verifies and responds to a new * ultrafiltration volume setting from the user. - * @details - * Inputs : current operating mode, treatment states and parameters - * Outputs : response message sent + * @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. *************************************************************************/ @@ -546,11 +585,10 @@ /*********************************************************************//** * @brief - * The verifyUFSettingsConfirmation function verifies the user confirmed \n + * 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 - * Outputs : response message sent + * @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. @@ -604,17 +642,18 @@ 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 \n + * The verifyBloodAndDialysateRateSettingsChange function verifies the * user blood & dialysate flow rate settings change. - * @details - * Inputs : current operating mode, treatment states and parameters - * Outputs : response message sent + * @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. @@ -670,14 +709,101 @@ /*********************************************************************//** * @brief - * The broadcastTreatmentTimeAndState function broadcasts treatment time and \n + * The verifyPressureLimitsChange function verifies a user change request + * for in-line pressure alarm limits. If valid, new limits are accepted + * and set. + * @details Inputs: none + * @details Outputs: new pressure limits set, response sent + * @param data payload record with new pressure limits + * @return TRUE if new pressure limits accepted, FALSE if not. + *************************************************************************/ +BOOL verifyPressureLimitsChange( PRESSURE_LIMIT_CHANGE_REQUEST_T *data ) +{ + BOOL result = TRUE; + CRITICAL_DATAS_T proposedNewArtLowLimit, proposedNewArtHighLimit; + CRITICAL_DATAS_T proposedNewVenLowLimit, proposedNewVenHighLimit; + PRESSURE_LIMIT_CHANGE_RESPONSE_T respRecord = { FALSE, 0, 0, 0, 0, 0 }; + + proposedNewArtLowLimit.sInt = data->artLowLimit; + proposedNewArtHighLimit.sInt = data->artHighLimit; + proposedNewVenLowLimit.sInt = data->venLowLimit; + proposedNewVenHighLimit.sInt = data->venHighLimit; + + // check ranges for changed limits + if ( FALSE == isTreatmentParamInRange( TREATMENT_PARAM_ART_PRESSURE_LOW_LIMIT, proposedNewArtLowLimit ) ) + { + respRecord.rejReasonCode = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + result = FALSE; + } + if ( FALSE == isTreatmentParamInRange( TREATMENT_PARAM_ART_PRESSURE_HIGH_LIMIT, proposedNewArtHighLimit ) ) + { + respRecord.rejReasonCode = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + result = FALSE; + } + if ( FALSE == isTreatmentParamInRange( TREATMENT_PARAM_VEN_PRESSURE_LOW_LIMIT, proposedNewVenLowLimit ) ) + { + respRecord.rejReasonCode = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + result = FALSE; + } + if ( FALSE == isTreatmentParamInRange( TREATMENT_PARAM_VEN_PRESSURE_HIGH_LIMIT, proposedNewVenHighLimit ) ) + { + respRecord.rejReasonCode = REQUEST_REJECT_REASON_PARAM_OUT_OF_RANGE; + result = FALSE; + } + + // if ranges ok, check separation between low/high limits + if ( TRUE == result ) + { + S32 arterialPresLimitDelta = data->artHighLimit - data->artLowLimit; + S32 venousPresLimitDelta = data->venHighLimit - data->venLowLimit; + + // check arterial alarm limits dependency + if ( arterialPresLimitDelta < MIN_PRESSURE_ALARM_LIMIT_DELTA_MMHG ) + { + respRecord.rejReasonCode = REQUEST_REJECT_REASON_ARTERIAL_PRESSURE_LOW_VS_HIGH; + result = FALSE; + } + + // check venous alarm limits dependency + if ( venousPresLimitDelta < MIN_PRESSURE_ALARM_LIMIT_DELTA_MMHG ) + { + respRecord.rejReasonCode = REQUEST_REJECT_REASON_VENOUS_PRESSURE_LOW_VS_HIGH; + result = FALSE; + } + } + + // set overall result - are changes accepted? + respRecord.accepted = result; + + // if changes accepted, set new pressure limits + if ( TRUE == result ) + { + setTreatmentParameterS32( TREATMENT_PARAM_ART_PRESSURE_LOW_LIMIT, data->artLowLimit ); + setTreatmentParameterS32( TREATMENT_PARAM_ART_PRESSURE_HIGH_LIMIT, data->artHighLimit ); + setTreatmentParameterS32( TREATMENT_PARAM_VEN_PRESSURE_LOW_LIMIT, data->venLowLimit ); + setTreatmentParameterS32( TREATMENT_PARAM_VEN_PRESSURE_HIGH_LIMIT, data->venHighLimit ); + } + + // read back limits for transmit to UI. + respRecord.artLowLimit = getTreatmentParameterS32( TREATMENT_PARAM_ART_PRESSURE_LOW_LIMIT ); + respRecord.artHighLimit = getTreatmentParameterS32( TREATMENT_PARAM_ART_PRESSURE_HIGH_LIMIT ); + respRecord.venLowLimit = getTreatmentParameterS32( TREATMENT_PARAM_VEN_PRESSURE_LOW_LIMIT ); + respRecord.venHighLimit = getTreatmentParameterS32( TREATMENT_PARAM_VEN_PRESSURE_HIGH_LIMIT ); + // send response + sendPressureLimitsChangeResponse( &respRecord ); + + return result; +} + +/*********************************************************************//** + * @brief + * The broadcastTreatmentTimeAndState function broadcasts treatment time and * state data during treatment. - * @details - * Inputs : treatment time and state data - * Outputs : treatment time and state messages sent on interval + * @details Inputs: treatment time and state data + * @details Outputs: treatment time and state messages sent on interval * @return none *************************************************************************/ -static void broadcastTreatmentTimeAndState( void ) +void broadcastTreatmentTimeAndState( void ) { U32 elapsedTreatmentTimeInSecs; @@ -695,7 +821,7 @@ U32 timeRemaining = presTreatmentTimeSecs - elapsedTreatmentTimeInSecs; DIALYSIS_STATE_T dialysisState = getDialysisState(); UF_STATE_T uFState = getUltrafiltrationState(); - BOOL salineBolusInProgress = ( dialysisState == DIALYSIS_SALINE_BOLUS_STATE ? TRUE : FALSE ); + SALINE_BOLUS_STATE_T salineBolusInProgress = getSalineBolusState(); broadcastTreatmentTime( presTreatmentTimeSecs, elapsedTreatmentTimeInSecs, timeRemaining ); broadcastTreatmentState( currentTreatmentState, uFState, salineBolusInProgress ); @@ -705,13 +831,12 @@ /*********************************************************************//** * @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 + * 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 - * Outputs : valid ranges message sent on interval + * @details Inputs: current operating mode, treatment states and parameters + * @details Outputs: valid ranges message sent on interval * @return none *************************************************************************/ static void broadcastTreatmentSettingsRanges( void )