/************************************************************************** * * 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 * * @date 19-Sep-2019 * @author S. Nash * * @brief Top-level state machine for the treatment mode. * **************************************************************************/ #include "AlarmLamp.h" #include "BloodFlow.h" #include "Buttons.h" #include "DialInFlow.h" #include "DialOutFlow.h" #include "Dialysis.h" #include "Buttons.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 // ********** private definitions ********** #define MAX_TREATMENT_TIME_MINUTES ( 8 * MIN_PER_HOUR ) #define MAX_UF_RATE_ML_MIN ( (F32)2500 / (F32)MIN_PER_HOUR ) #define MAX_DIALYSATE_VOLUME_ML ( 180 * ML_PER_LITER ) #define TREATMENT_TIME_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) // interval (ms/task time) at which the treatment time data is published on the CAN bus // ********** private data ********** static TREATMENT_STATE_T currentTreatmentState; static U32 presTreatmentTimeSecs; static U32 presBloodFlowRate; static U32 presDialysateFlowRate; static F32 presMaxUFVolumeML; static F32 presUFRate; static U32 treatmentTimeMS; static U32 lastTreatmentTimeStamp; static U32 treatmentTimeBroadcastTimerCtr; static BUTTON_STATE_T lastOffButtonState = BUTTON_STATE_RELEASED; static F32 pendingUFVolumeChange; ///< An ultrafiltration volume change (mL) is pending user confirmation. static F32 pendingUFRateChange; ///< An ultrafiltration rate change (mL/min) is pending user confirmation. static U32 pendingTreatmentTimeChange; ///< A treatment time change (min) is pending user confirmation. // ********** private function prototypes ********** static 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; } /************************************************************************* * @brief * The transitionToTreatmentMode function prepares for transition to treatment mode. * @details * Inputs : none * Outputs : * @param none * @return none *************************************************************************/ void transitionToTreatmentMode( void ) { // initialize treatment mode each time we transition to it initTreatmentMode(); // 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 * 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 *************************************************************************/ void execTreatmentMode( void ) { U32 elapsedTreatmentTimeInSecs; #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: // TODO - s/w fault break; } // update treatment time stats and broadcast - end treatment if time elapsedTreatmentTimeInSecs = treatmentTimeMS / MS_PER_SECOND; if ( elapsedTreatmentTimeInSecs >= presTreatmentTimeSecs ) { stopDialysis(); elapsedTreatmentTimeInSecs = presTreatmentTimeSecs; currentTreatmentState = TREATMENT_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_SOLUTION_INFUSION_STATE ? TRUE : FALSE ); broadcastTreatmentTime( presTreatmentTimeSecs, elapsedTreatmentTimeInSecs, timeRemaining ); broadcastTreatmentState( currentTreatmentState, uFState, salineBolusInProgress ); treatmentTimeBroadcastTimerCtr = 0; } #endif #ifdef RM46_EVAL_BOARD_TARGET // TODO - temporary test code for eval board - move to next mode after 10 sec if ( TRUE == didTimeout( start, 10000U ) ) { // requestNewOperationMode( MODE_POST ); } #endif } /************************************************************************* * @brief * The handleTreatmentStartState function handles the Start state of \n * the Treatment Mode state machine. * @details * Inputs : none * Outputs : none * @param 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 = 300.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 * @param 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 treatmentTimeMS += msSinceLast; lastTreatmentTimeStamp = newTime; // 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(); 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 * @param 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(); result = TREATMENT_DIALYSIS_STATE; } } else { lastOffButtonState = BUTTON_STATE_RELEASED; } return result; } /************************************************************************* * @brief * The verifyTreatmentDurationSettingChange function verifies the user treatment \n * duration setting change. * @details * Inputs : none * Outputs : none * @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; U32 timeDiff = 0; F32 rateDiff = 0.0; OP_MODE 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 ) ) { F32 uFVolume; U32 dialVolume = presDialysateFlowRate * treatmentTime; // in mL // always adjust UF volume to accommodate treatment time change (not UF rate) uFVolume = (F32)treatmentTime * presUFRate; if ( ( treatmentTime <= MAX_TREATMENT_TIME_MINUTES ) && ( dialVolume <= MAX_DIALYSATE_VOLUME_ML ) ) { result = TRUE; pendingUFVolumeChange = uFVolume; // mL pendingUFRateChange = presUFRate; // no UF rate change pendingTreatmentTimeChange = treatmentTime; // min timeDiff = treatmentTime - ( (U32)( (F32)presTreatmentTimeSecs / (F32)SEC_PER_MIN ) + 1 ); } } sendChangeUFSettingsResponse( result, pendingUFVolumeChange, pendingTreatmentTimeChange, pendingUFRateChange, timeDiff, rateDiff ); return result; } /************************************************************************* * @brief * The verifyUFSettingsChange function verifies new ultrafiltration settings \n * from the user. * @details * Inputs : none * Outputs : none * @param uFVolume : . * @param adjustment : . * @return TRUE if new UF settings valid, FALSE if not. *************************************************************************/ BOOL verifyUFSettingsChange( F32 uFVolume, UF_ADJ_T adjustment ) { BOOL result = FALSE; U32 timeDiff = 0; F32 rateDiff = 0.0; OP_MODE 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 ) ) { // TODO - verify not too close to end of treatment for settings change DIALYSIS_STATE_T currDialysisState = getDialysisState(); UF_STATE_T currUFState = getUltrafiltrationState(); F32 uFRate; U32 trtTime; // UF should already be paused but let's make sure. if ( ( TREATMENT_DIALYSIS_STATE == currentTreatmentState ) && ( DIALYSIS_UF_STATE == currDialysisState ) && ( UF_RUNNING_STATE == currUFState ) ) { pauseUF(); } // reset pending UF/time settings change pendingUFVolumeChange = 0.0; pendingUFRateChange = 0.0; pendingTreatmentTimeChange = 0; // which setting does user want to adjust to accommodate the UF volume change? (treatment time or UF rate) if ( UF_ADJ_TREATMENT_TIME == adjustment ) { F32 uFRate = presUFRate; // no change in UF rate U32 trtTime = (S32)( uFVolume / uFRate ) + 1; // in minutes U32 dialVolume = presDialysateFlowRate * trtTime; // in mL // verify new treatment duration is valid if ( ( trtTime <= MAX_TREATMENT_TIME_MINUTES ) && ( dialVolume <= MAX_DIALYSATE_VOLUME_ML ) ) { result = TRUE; pendingUFVolumeChange = uFVolume; pendingUFRateChange = uFRate; pendingTreatmentTimeChange = trtTime; timeDiff = trtTime - ( (U32)( (F32)presTreatmentTimeSecs / (F32)SEC_PER_MIN ) + 1 ); } } else { // UF Rate is adjusted then trtTime = presTreatmentTimeSecs / SEC_PER_MIN; // in minutes uFRate = uFVolume / (F32)trtTime; if ( uFRate <= (F32)MAX_UF_RATE_ML_MIN ) { result = TRUE; pendingUFVolumeChange = uFVolume; pendingUFRateChange = uFRate; pendingTreatmentTimeChange = trtTime; rateDiff = ( uFRate - presUFRate ); } } } // respond to UF settings change request sendChangeUFSettingsResponse( result, pendingUFVolumeChange, pendingTreatmentTimeChange, pendingUFRateChange, timeDiff, rateDiff ); return result; } /************************************************************************* * @brief * The verifyUFSettingsConfirmation function verifies the user confirmed \n * ultrafiltration settings change(s) and, if valid, implements the new settings. * @details * Inputs : none * Outputs : none * @param confirmed : . * @param uFVolume : . * @param treatmentTime : . * @param uFRate : . * @return next treatment mode state *************************************************************************/ BOOL verifyUFSettingsConfirmation( BOOL confirmed, F32 uFVolume, U32 treatmentTime, F32 uFRate ) { BOOL result = FALSE; OP_MODE currMode = getCurrentOperationMode(); // user confirmed UF settings change(s)? if ( ( MODE_TREA == currMode ) && ( TRUE == confirmed ) ) { // user confirmed changes are same as verified pending changes? if ( ( FABS( uFVolume - pendingUFVolumeChange ) < NEARLY_ZERO ) && ( treatmentTime == pendingTreatmentTimeChange ) && ( FABS( uFRate - pendingUFRateChange ) < NEARLY_ZERO ) ) { DIALYSIS_STATE_T currDialysisState = getDialysisState(); UF_STATE_T currUFState = getUltrafiltrationState(); // set pending settings changes presMaxUFVolumeML = pendingUFVolumeChange; presUFRate = pendingUFRateChange; setDialysisParams( presBloodFlowRate, presDialysateFlowRate, presMaxUFVolumeML, presUFRate ); presTreatmentTimeSecs = pendingTreatmentTimeChange * SEC_PER_MIN; // if UF paused, resume with new settings if ( ( TREATMENT_DIALYSIS_STATE == currentTreatmentState ) && ( DIALYSIS_UF_STATE == currDialysisState ) && ( UF_PAUSED_STATE == currUFState ) ) { resumeUF(); } result = TRUE; } } return result; }