/************************************************************************** * * Copyright (c) 2025-2025 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 * @date (last) 25-Mar-2025 * * @author (original) Sean * @date (original) 25-Mar-2025 * ***************************************************************************/ #include "AirTrap.h" //#include "BloodFlow.h" #include "Bubbles.h" #include "Buttons.h" #include "DDInterface.h" #include "ModeService.h" #include "ModeTreatment.h" #include "ModeTxParams.h" //#include "NVDataMgmt.h" #include "OperationModes.h" #include "StateTxDialysis.h" #include "StateTxPaused.h" //#include "Switches.h" #include "TaskGeneral.h" #include "Timers.h" #include "Valves.h" /** * @addtogroup TDTreatmentMode * @{ */ // ********** private definitions ********** #define MAX_TREATMENT_TIME_MINUTES ( 8 * MIN_PER_HOUR ) ///< Maximum treatment time (in minutes). #define MIN_TREATMENT_TIME_MINUTES ( 1 * MIN_PER_HOUR ) ///< Minimum treatment time (in minutes). /// 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 ) /// Ultrafiltration data broadcast interval (ms/task time) count. #define ULTRAFILTRATION_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) /// Saline bolus data broadcast interval (ms/task time) count. // TODO - move to StateTxSalineBolus.c when implemented #define SALINE_BOLUS_DATA_PUB_INTERVAL ( MS_PER_SECOND / TASK_GENERAL_INTERVAL ) /// Macro to calculate the elapsed treatment time in seconds. #define CALC_ELAPSED_TREAT_TIME_IN_SECS() ( treatmentTimeMS / MS_PER_SECOND ) /// 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 seconds). static F32 presUFVolumeL; ///< Prescribed ultrafiltration volume (in L). static F32 presUFRateLHr; ///< Prescribed ultrafiltration rate (in L/hr). 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. static U32 ultrafiltrationBroadcastTimerCtr; ///< Ultrafiltration data broadcast timer counter used to schedule when to transmit data. static U32 salineBolusBroadcastTimerCtr; ///< Saline bolus data broadcast timer counter used to schedule when to transmit data. /// Interval (in task intervals) at which to publish alarm status to CAN bus. static OVERRIDE_U32_T treatmentTimePublishInterval; /// Interval (in task intervals) at which to publish alarm status to CAN bus. static OVERRIDE_U32_T treatmentStatePublishInterval; /// Interval (in task intervals) at which to publish alarm status to CAN bus. static OVERRIDE_U32_T treatmentParamRangesPublishInterval; 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 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 resumeBlockedByAlarm; ///< Flag indicates an alarm has blocked resume of this treatment. static U32 treatmentStartTimeStamp; ///< Treatment start timestamp for logging purpose. static U32 treatmentEndTimeStamp; ///< Treatment end timestamp for logging purpose. // ********** private function prototypes ********** static void broadcastTreatmentSettingsRanges( void ); static void broadcastTreatmentPeriodicData(); static void publishSalineBolusData( void ); // TODO - move to StateTxSalineBolus.c when implemented static void publishUltrafiltrationData( void ); static U32 getMinTreatmentTimeInMinutes( void ); static TREATMENT_STATE_T handleTreatmentStartState( void ); static TREATMENT_STATE_T handleTreatmentBloodPrimeState( void ); static TREATMENT_STATE_T handleTreatmentDialysisState( void ); static TREATMENT_STATE_T handleTreatmentIsoUFState( void ); static TREATMENT_STATE_T handleTreatmentDialysatePausedState( void ); static TREATMENT_STATE_T handleTreatmentSalineBolusState( void ); static TREATMENT_STATE_T handleTreatmentPausedState( void ); static TREATMENT_STATE_T handleTreatmentRinsebackState( void ); static TREATMENT_STATE_T handleTreatmentRecircState( void ); static TREATMENT_STATE_T handleTreatmentEndState( void ); static void resetSignalFlags( void ); static void resetAlarmSignalFlags( void ); /*********************************************************************//** * @brief * The initTreatmentMode function initializes the Treatment Mode unit. * @details \b Inputs: none * @details \b Outputs: Treatment Mode unit initialized. * @return none *************************************************************************/ void initTreatmentMode( void ) { currentTreatmentState = TREATMENT_START_STATE; bloodIsPrimed = FALSE; treatmentCompleted = FALSE; rinsebackDone = FALSE; resumeBlockedByAlarm = FALSE; treatmentTimePublishInterval.data = TREATMENT_TIME_DATA_PUB_INTERVAL; treatmentTimePublishInterval.ovData = TREATMENT_TIME_DATA_PUB_INTERVAL; treatmentTimePublishInterval.ovInitData = TREATMENT_TIME_DATA_PUB_INTERVAL; treatmentTimePublishInterval.override = OVERRIDE_RESET; treatmentStatePublishInterval.data = TREATMENT_STATE_DATA_PUB_INTERVAL; treatmentStatePublishInterval.ovData = TREATMENT_STATE_DATA_PUB_INTERVAL; treatmentStatePublishInterval.ovInitData = TREATMENT_STATE_DATA_PUB_INTERVAL; treatmentStatePublishInterval.override = OVERRIDE_RESET; treatmentParamRangesPublishInterval.data = TREATMENT_SETTINGS_RANGES_PUB_INTERVAL; treatmentParamRangesPublishInterval.ovData = TREATMENT_SETTINGS_RANGES_PUB_INTERVAL; treatmentParamRangesPublishInterval.ovInitData = TREATMENT_SETTINGS_RANGES_PUB_INTERVAL; treatmentParamRangesPublishInterval.override = OVERRIDE_RESET; 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 ultrafiltrationBroadcastTimerCtr = ULTRAFILTRATION_DATA_PUB_INTERVAL; // So we send ultrafiltration immediately when we begin treatment mode salineBolusBroadcastTimerCtr = SALINE_BOLUS_DATA_PUB_INTERVAL; // So we send saline bolus data immediately when we begin treatment mode presTreatmentTimeSecs = 0; presUFVolumeL = 0.0F; presUFRateLHr = 0.0F; resetSignalFlags(); resetAlarmSignalFlags(); treatmentStartTimeStamp = 0; treatmentEndTimeStamp = 0; } /*********************************************************************//** * @brief * The transitionToTreatmentMode function prepares for transition to treatment * mode. * @details \b Inputs: none * @details \b Outputs: none * @return initial state *************************************************************************/ U32 transitionToTreatmentMode( void ) { // PRESSURE_LIMIT_CHANGE_RESPONSE_T respRecord; // don't re-initialize treatment if tester is jumping to Tx mode // if ( getPreviousOperationMode() != MODE_PRET ) { // Initialize treatment mode each time we transition to it initTreatmentMode(); // Initialize treatment sub-modes each time we transition to treatment mode // initBloodPrime(); initDialysis(); initTreatmentPaused(); // initRinseback(); // initTreatmentRecirc(); // initTreatmentEnd(); // Started the treatment - set the start time in epoch // setTxLastStartTimeEpoch( getRTCTimestamp() ); } setCurrentSubState( NO_SUB_STATE ); #ifndef TEST_UI_ONLY // Enable venous bubble detection in treatment mode setVenousBubbleDetectionEnabled( H18_BBLD, TRUE ); #endif // Set treatment parameters presTreatmentTimeSecs = getTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_DURATION ) * SEC_PER_MIN; presUFVolumeL = getTreatmentParameterF32( TREATMENT_PARAM_UF_VOLUME ); presUFRateLHr = presUFVolumeL / ( (F32)getTreatmentParameterU32( TREATMENT_PARAM_TREATMENT_DURATION ) / (F32)MIN_PER_HOUR ); setDialysisDDParams( getTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW ), presUFVolumeL, presUFRateLHr ); // Direct DD to generate dialysate and bypass while priming blood cmdStartGenerateDialysate( (F32)getTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW ), presUFRateLHr, getTreatmentParameterF32( TREATMENT_PARAM_DIALYSATE_TEMPERATURE ), TRUE, (ACID_CONCENTRATE_TYPE_T)getTreatmentParameterU32( TREATMENT_PARAM_ACID_CONCENTRATE ), (BICARB_CONCENTRATE_TYPE_T)getTreatmentParameterU32( TREATMENT_PARAM_BICARB_CONCENTRATE ) ); // Read back limits for transmit to UI. // respRecord.artPresLimitWindowmmHg = getTreatmentParameterS32( TREATMENT_PARAM_ART_PRES_LIMIT_WINDOW ); // respRecord.venPresLimitWindowmmHg = getTreatmentParameterS32( TREATMENT_PARAM_VEN_PRES_LIMIT_WINDOW ); // respRecord.venPresLimitAsymmetricmmHg = getTreatmentParameterS32( TREATMENT_PARAM_VEN_PRES_LIMIT_ASYMMETRIC ); // Send response // sendPressureLimitsChangeResponse( &respRecord ); return currentTreatmentState; } /*********************************************************************//** * @brief * The getTreatmentState function gets the current treatment mode state. * @details \b Inputs: currentTreatmentState * @details \b 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 \b Inputs: treatmentCompleted * @details \b Outputs: none * @return treatmentCompleted *************************************************************************/ BOOL isTreatmentCompleted( void ) { return treatmentCompleted; } /*********************************************************************//** * @brief * The isTreatmentResumeBlocked function determines whether the treatment has * seen an alarm that prevents resumption. * @details \b Inputs: resumeBlockedByAlarm * @details \b Outputs: none * @return resumeBlockedByAlarm *************************************************************************/ BOOL isTreatmentResumeBlocked( void ) { return resumeBlockedByAlarm; } /*********************************************************************//** * @brief * The getTreatmentTimeRemainingSecs function determines the number of seconds * remaining in the treatment. * @details \b Inputs: presTreatmentTimeSecs, treatmentTimeMS * @details \b 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 \b Inputs: treatmentTimeMS * @details \b Outputs: none * @return The actual treatment duration in seconds. *************************************************************************/ U32 getActualTreatmentTimeSecs( void ) { return ( treatmentTimeMS / MS_PER_SECOND ); } /*********************************************************************//** * @brief * The getTreatmentStartTimeStamp function returns the treatment start * time stamp. * @details \b Inputs: treatmentStartTimeStamp * @details \b Outputs: none * @return the treatment start time stamp *************************************************************************/ U32 getTreatmentStartTimeStamp( void ) { return treatmentStartTimeStamp; } /*********************************************************************//** * @brief * The getTreatmentEndTimeStamp function returns the treatment end * time stamp. * @details \b Inputs: treatmentEndTimeStamp * @details \b 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 \b Alarm: ALARM_ID_TD_SOFTWARE_FAULT if invalid alarm action given. * @details \b Inputs: none * @details \b 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_TD_SOFTWARE_FAULT, SW_FAULT_ID_MODE_TREATMENT_INVALID_ALARM_ACTION, (U32)action ) break; } } /*********************************************************************//** * @brief * The signalEndTreatment function signals that user wants to end the * treatment. * @details \b Inputs: none * @details \b Outputs: endTreatmentRequest * @return none *************************************************************************/ void signalEndTreatment( void ) { endTreatmentRequest = TRUE; } /*********************************************************************//** * @brief * The execTreatmentMode function executes the Treatment Mode state machine. * @details \b Alarm: ALARM_ID_TD_TREATMENT_STOPPED_BY_USER if user presses * the stop button * @details \b Inputs: currentTreatmentState * @details \b Outputs: currentTreatmentState, resumeBlockedByAlarm * @return current state (sub-mode) *************************************************************************/ U32 execTreatmentMode( void ) { BOOL stop = isStopButtonPressed(); // Trigger user stop alarm at any time in treatment mode when user presses stop button if ( TRUE == stop ) { activateAlarmNoData( ALARM_ID_TD_TREATMENT_STOPPED_BY_USER ); } // Check if resume treatment blocked by alarm if ( TRUE == doesAlarmIndicateNoResume() ) { resumeBlockedByAlarm = TRUE; } // Check dialysate temperature during treatment mode (except end state where treatment is over, dialyzer is bypassed and temp is no longer an issue) // if ( currentTreatmentState != TREATMENT_END_STATE ) // { // checkDialysateTemperature(); // } // 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_ISO_UF_STATE: // currentTreatmentState = handleTreatmentIsoUFState(); // break; // // case TREATMENT_SALINE_BOLUS_STATE: // currentTreatmentState = handleTreatmentSalineBolusState(); // break; // // case TREATMENT_DIALYSATE_PAUSED_STATE: // currentTreatmentState = handleTreatmentDialysatePausedState(); // break; case TREATMENT_PAUSED_STATE: currentTreatmentState = handleTreatmentPausedState(); 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_TD_SOFTWARE_FAULT, SW_FAULT_ID_MODE_TREATMENT_INVALID_STATE, (U32)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(); // Publish ultrafiltration data at set interval (whether we are performing UF or not) publishUltrafiltrationData(); // Publish saline bolus data at set interval (whether we are delivering one or not) publishSalineBolusData(); // 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 \b Inputs: none * @details \b Outputs: treatmentTimeMS, lastTreatmentTimeStamp * @return next treatment mode state *************************************************************************/ static TREATMENT_STATE_T handleTreatmentStartState( void ) { TREATMENT_STATE_T result = TREATMENT_BLOOD_PRIME_STATE; lastTreatmentTimeStamp = getMSTimerCount(); //transitionToBloodPrime(); // TODO setDialysisBloodPumpFlowRate( getTreatmentParameterU32( TREATMENT_PARAM_BLOOD_FLOW ) ); // TODO - remove later return result; } /*********************************************************************//** * @brief * The handleTreatmentBloodPrimeState function handles the blood prime state of * the Treatment Mode state machine. * @details \b Inputs: presUFRate * @details \b 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 setDialysisBloodPumpFlowRate( getTreatmentParameterU32( TREATMENT_PARAM_BLOOD_FLOW ) ); // setDialysisDialInFlowAndUFRate( getTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW ), presMaxUFVolumeML, presUFRate ); // // if ( presUFRate > 0.0 ) // { // sendTreatmentLogEventData( UF_START_RESUME_EVENT, 0.0, presUFRate ); // } transitionToDialysis(); // // To update partial blood pump occlusion baseline - start of treatment // signalBloodPumpPressureOcclBaseline(); result = TREATMENT_DIALYSIS_STATE; // } // } return result; } /*********************************************************************//** * @brief * The handleTreatmentDialysisState function handles the Dialysis state of * the Treatment Mode state machine. * @details \b Inputs: none * @details \b 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 ); DIALYSIS_STATE_T dialysisState = getDialysisState(); // Update treatment time 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(); pauseDialysis(); requestNewOperationMode( MODE_STAN ); // TODO transitionToTreatmentEnd(); //SET_ALARM_WITH_1_U32_DATA( ALARM_ID_TD_END_OF_TREATMENT_WARNING, presTreatmentTimeSecs ); result = TREATMENT_END_STATE; } // Otherwise, execute state machine for treatment dialysis sub-mode else { execDialysis(); // Handle alarm stoppage if ( TRUE == doesAlarmStatusIndicateStop() ) { transitionToTreatmentPaused(); result = TREATMENT_PAUSED_STATE; } } return result; } /*********************************************************************//** * @brief * The handleTreatmentPausedState function executes the Paused state of the * Treatment Mode state machine. * @details \b Inputs: none * @details \b Outputs: treatment paused sub-mode executed. * @return next treatment mode state *************************************************************************/ static TREATMENT_STATE_T handleTreatmentPausedState( void ) { TREATMENT_STATE_T result = TREATMENT_PAUSED_STATE; BOOL leavingTreatmentPausedState = TRUE; // 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; // } // signalInitiatePressureStabilization( USE_NORMAL_STABILIZATION_PERIOD ); } // If user requests rinseback, go to rinseback // else if ( TRUE == initiateRinsebackAlarmResponseRequest ) // { // 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 paused sub-mode else { execTreatmentPaused(); leavingTreatmentPausedState = FALSE; } // If leaving treatment paused state, zero alarm countdown timer for UI // if ( TRUE == leavingTreatmentPausedState ) // { // TREATMENT_STOP_PAYLOAD_T data; // // data.timeout = 0; // data.countdown = 0; // broadcastData( MSG_ID_TD_TREATMENT_PAUSED_TIMER_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)&data, sizeof( TREATMENT_PAUSED_PAYLOAD_T ) ); // } return result; } /*********************************************************************//** * @brief * The handleTreatmentRinsebackState function executes the rinseback state of the * Treatment Mode state machine. * @details \b Inputs: endTreatmentAlarmResponseRequest, resumeTreatmentAlarmResponseRequest, * rinsebackToRecircRequest, rinsebackToStoppedRequest, endTreatmentRequest * @details \b Outputs: treatment rinseback sub-mode executed, sendLastTreatmentPeriodicData * @return next treatment mode state *************************************************************************/ static TREATMENT_STATE_T handleTreatmentRinsebackState( void ) { TREATMENT_STATE_T result = TREATMENT_RINSEBACK_STATE; // // If user requests treatment end, end treatment // if ( TRUE == endTreatmentAlarmResponseRequest ) // { // sendLastTreatmentPeriodicData = TRUE; // requestNewOperationMode( MODE_POST ); // } // // Otherwise execute state machine for treatment rinseback sub-mode // else // { // // If user requests resumption of treatment, resume rinseback // if ( TRUE == resumeTreatmentAlarmResponseRequest ) // { // resumeTreatmentAlarmResponseRequest = FALSE; // signalRinsebackAlarmResumeUserAction(); // } // // 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 \b Inputs: endTreatmentAlarmResponseRequest, rinsebackToStoppedRequest, * endTreatmentRequest * @details \b Outputs: sendLastTreatmentPeriodicData * @return next treatment mode state *************************************************************************/ static TREATMENT_STATE_T handleTreatmentRecircState( void ) { TREATMENT_STATE_T result = TREATMENT_RECIRC_STATE; // // If user requests treatment end, end treatment // if ( TRUE == endTreatmentAlarmResponseRequest ) // { // sendLastTreatmentPeriodicData = TRUE; // requestNewOperationMode( MODE_POST ); // } // else // { // // Execute treatment re-circ sub-mode // execTreatmentRecirc(); // } // // // Handle signals from treatment re-circ sub-mode // if ( TRUE == rinsebackToStoppedRequest ) // { // stopDialysis(); // 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 \b Inputs: none * @details \b 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(); // } // // End treatment state does not allow resume // // // 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 broadcastTreatmentTimeAndState function broadcasts treatment time and * state data during treatment. * @details \b Inputs: treatmentTimeBroadcastTimerCtr, elapsedTreatmentTimeInSecs, * presTreatmentTimeSecs, treatmentStateBroadcastTimerCtr * @details \b Outputs: treatmentTimeBroadcastTimerCtr, treatmentStateBroadcastTimerCtr * @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 and set point data at interval if ( ++treatmentTimeBroadcastTimerCtr >= getU32OverrideValue( &treatmentTimePublishInterval ) ) { TREATMENT_TIME_DATA_T payload = { 0, 0, 0 }; TREATMENT_SET_POINTS_T spPayload = { 0, 0, 0.0F }; treatmentTimeBroadcastTimerCtr = 0; // Build and send treatment time data if treatment not yet completed. if ( isTreatmentCompleted() != TRUE ) { payload.treatmentTimeElapsedinSec = elapsedTreatmentTimeInSecs; // if alarm preventing treatment resumption, treatment is essentially over and we want to indicate that with published treatment time data if ( isTreatmentResumeBlocked() != TRUE ) { payload.treatmentTimePrescribedinSec = presTreatmentTimeSecs; payload.treatmentTimeRemaininginSec = presTreatmentTimeSecs - elapsedTreatmentTimeInSecs; } else { payload.treatmentTimePrescribedinSec = 0; payload.treatmentTimeRemaininginSec = 0; } } broadcastData( MSG_ID_TD_TREATMENT_TIME_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)(&payload), sizeof( TREATMENT_TIME_DATA_T ) ); // Build and send treatment set points message. spPayload.bloodFlow = getTreatmentParameterU32( TREATMENT_PARAM_BLOOD_FLOW ); spPayload.dialFlow = getTreatmentParameterU32( TREATMENT_PARAM_DIALYSATE_FLOW ); spPayload.dialTemp = getTreatmentParameterF32( TREATMENT_PARAM_DIALYSATE_TEMPERATURE ); broadcastData( MSG_ID_TD_TREATMENT_SET_POINTS, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)(&spPayload), sizeof( TREATMENT_SET_POINTS_T ) ); } // Broadcast treatment state data at interval if ( ++treatmentStateBroadcastTimerCtr >= getU32OverrideValue( &treatmentStatePublishInterval ) ) { TREATMENT_STATE_DATA_T payload; treatmentStateBroadcastTimerCtr = 0; payload.treatmentSubMode = (U32)currentTreatmentState; payload.bldPrimeState = 0; // getCurrentBloodPrimeState(); payload.dialysisState = getDialysisState(); payload.isoUFState = 0; payload.txStopState = getCurrentTreatmentPausedState(); payload.rinsebackState = 0; // getCurrentRinsebackState(); payload.txRecircState = 0; // getCurrentTreatmentRecircState(); payload.txEndState = 0; // getCurrentTreatmentEndState(); payload.txSalBolusState = 0; // payload.txHepState = 0; // broadcastData( MSG_ID_TD_TREATMENT_STATE_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)(&payload), sizeof( TREATMENT_STATE_DATA_T ) ); } } /*********************************************************************//** * @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 \b Inputs: current operating mode, treatment states and parameters * @details \b Outputs: valid ranges message sent on interval * @return none *************************************************************************/ static void broadcastTreatmentSettingsRanges( void ) { if ( ++treatmentParamsRangesBroadcastTimerCtr >= getU32OverrideValue( &treatmentParamRangesPublishInterval ) ) { TREATMENT_PARAM_RANGE_BROADCAST_PAYLOAD_T payload; // Compute minimum treatment duration U32 presTime = ( presTreatmentTimeSecs / SEC_PER_MIN ); U32 elapseTime = CALC_ELAPSED_TREAT_TIME_IN_MIN(); U32 minTime = MAX( (elapseTime + 2), getMinTreatmentTimeInMinutes() ); // 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.0F ? (U32)presUFRate : 1 ); U32 maxTime1 = minTime; // TODO + maxTimeRem; U32 maxTime2 = MAX_TREATMENT_TIME_MINUTES; U32 maxTime = MAX( maxTime1, maxTime2 ); // Compute minimum UF volume F32 minUFVol = 0.0F; // TODO getUltrafiltrationReferenceVolume() + presUFRate; // current UF volume + 1 min at current rate // 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 = ( presUFRateLHr > 0.0F ? minUFVol + ( (F32)( MAX_TREATMENT_TIME_MINUTES - elapseTime - 1 ) * presUFRateLHr ) : minUFVol ); F32 maxUFVol = MAX( maxUFVol1, maxUFVol2 ); // Set minimum dialysate flow rate U32 minDialRate = MIN_DIALYSATE_FLOW_RATE; // Set maximum dialysate flow rate U32 maxDialRate = MAX_DIALYSATE_FLOW_RATE; treatmentParamsRangesBroadcastTimerCtr = 0; // Now ensure maximums do not exceed the literal maximums maxTime = MIN( maxTime, MAX_TREATMENT_TIME_MINUTES ); maxUFVol = MIN( maxUFVol, (F32)MAX_UF_VOLUME_ML ); // Send updated treatment parameter ranges to UI payload.minTreatmentTime = minTime; payload.maxTreatmentTime = maxTime; payload.minUFVolume = minUFVol; payload.maxUFVolume = maxUFVol; payload.minDialRate = minDialRate; payload.maxDialRate = maxDialRate; sendMessage( MSG_ID_TD_TREATMENT_PARAM_RANGES, COMM_BUFFER_OUT_CAN_TD_2_UI, (U08*)(&payload), sizeof( TREATMENT_PARAM_RANGE_BROADCAST_PAYLOAD_T ) ); } } /*********************************************************************//** * @brief * The broadcastTreatmentPeriodicData function computes and broadcasts * periodic treatment data during treatment. * @details \b Inputs: treatmentPeriodDataBroadcastTimerCtr * @details \b 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(); // // if ( ( TREATMENT_DIALYSIS_STATE == getTreatmentState() ) && ( getDialysisState() != DIALYSIS_SALINE_BOLUS_STATE ) ) // { // treatmentBloodFlowRateTotal_mL_min += getMeasuredBloodFlowRate(); // treatmentDialysateFlowRateTotal_mL_min += getMeasuredDialInFlowRate(); // treatmentDialysateTempTotal_degree_C += getDialysateTemperature(); // treatmentArterialPressureTotal_mmHg += arterialPres; // treatmentVenousPressureTotal_mmHg += venousPres; // } // // lastTreatmentPeriodicDataCollectTimeStamp = treatmentTimeMS; // bloodFlowRateSum_mL_min += getMeasuredBloodFlowRate(); // dialysateFlowRateSum_mL_min += getMeasuredDialInFlowRate(); // arterialPressureSum_mmHg += arterialPres; // venousPressureSum_mmHg += venousPres; // } // // if ( ( timeElapsedSinceLastPublish_ms >= TREATMENT_PERIODIC_DATA_PUB_INTERVAL ) || ( TRUE == sendLastTreatmentPeriodicData ) ) // { // TREATMENT_LOG_DATA_PERIODIC_T periodTreatmentData; // F32 const uFVolumeCollected = getUltrafiltrationVolumeCollected(); // U32 const numberOfDataPoint = timeElapsedSinceLastPublish_ms / TREATMENT_PERIODIC_DATA_LOG_INTERVAL; // // if ( numberOfDataPoint > 0 ) // { // don't send data if no data since last publish // periodTreatmentData.avgUFRate = ( uFVolumeCollected - 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; // sendTreatmentPeriodicDataToUI( &periodTreatmentData ); // } // // reset for next round to publish // sendLastTreatmentPeriodicData = FALSE; // lastTreatmentPeriodicDataPublishTimeStamp = treatmentTimeMS; // lastUltraFiltrationVolume_mL = uFVolumeCollected; // bloodFlowRateSum_mL_min = 0.0; // dialysateFlowRateSum_mL_min = 0.0; // arterialPressureSum_mmHg = 0.0; // venousPressureSum_mmHg = 0.0; // } } /*********************************************************************//** * @brief * The getMinTreatmentTimeInMinutes function returns the minimum treatment * time (in minutes) that the user could set treatment duration to. * @details \b Inputs: MIN_TREATMENT_TIME_MINUTES * @details \b Outputs: none * @return minimum treatment time in minutes *************************************************************************/ static U32 getMinTreatmentTimeInMinutes( void ) { U32 treatmentTime = MIN_TREATMENT_TIME_MINUTES; // if ( TRUE == getTestConfigStatus( TEST_CONFIG_ENABLE_ONE_MINUTE_TREATMENT ) ) { treatmentTime = 1; } return treatmentTime; } /*********************************************************************//** * @brief * The signalAlarmActionToServiceMode function executes the given alarm action * as appropriate while in Service Mode. * @details \b Inputs: none * @details \b Outputs: given alarm action executed * @param action ID of alarm action to execute * @return none *************************************************************************/ //void signalAlarmActionToServiceMode( ALARM_ACTION_T action ) //{ // // TODO - implement //} /*********************************************************************//** * @brief * The resetSignalFlags function resets all non-alarm signal flags. * @details \b Inputs: none * @details \b 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 \b Inputs: none * @details \b Outputs: alarm signal flags set to FALSE * @return none *************************************************************************/ static void resetAlarmSignalFlags( void ) { resumeTreatmentAlarmResponseRequest = FALSE; initiateRinsebackAlarmResponseRequest = FALSE; endTreatmentAlarmResponseRequest = FALSE; } /*********************************************************************//** * @brief * The publishSalineBolusData function publishes the saline bolus data * at the set time interval. * @details \b Inputs: none * @details \b Outputs: none * @param dialysisState next dialysis state * @return next saline bolus state *************************************************************************/ static void publishSalineBolusData( void ) { if ( ++salineBolusBroadcastTimerCtr >= SALINE_BOLUS_DATA_PUB_INTERVAL ) { SALINE_BOLUS_DATA_PAYLOAD_T data; data.tgtSalineVolumeMl = getTreatmentParameterU32( TREATMENT_PARAM_SALINE_BOLUS_VOLUME ); data.cumSalineVolumeMl = 0.0F; // TODO data.bolSalineVolumeMl = 0.0F; // TODO sendMessage( MSG_ID_TD_SALINE_BOLUS_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)(&data), sizeof( SALINE_BOLUS_DATA_PAYLOAD_T ) ); salineBolusBroadcastTimerCtr = 0; } } /*********************************************************************//** * @brief * The publishUltrafiltrationData function publishes the ultrafiltration data * at the set time interval. * @details \b Inputs: presUFVolumeL, presUFRate * @details \b Outputs: none * @return none *************************************************************************/ static void publishUltrafiltrationData( void ) { if ( ++ultrafiltrationBroadcastTimerCtr >= ULTRAFILTRATION_DATA_PUB_INTERVAL ) { UF_DATA_PAYLOAD_T data; data.setUFVolumeL = presUFVolumeL; data.tgtUFRateLHr = presUFRateLHr; data.ufVolumeDeliveredL = getUltrafiltrationVolumeDrawn(); data.ufState = (U32)getDialysisState(); sendMessage( MSG_ID_TD_ULTRAFILTRATION_DATA, COMM_BUFFER_OUT_CAN_TD_BROADCAST, (U08*)(&data), sizeof( UF_DATA_PAYLOAD_T ) ); ultrafiltrationBroadcastTimerCtr = 0; } } /************************************************************************* * TEST SUPPORT FUNCTIONS *************************************************************************/ /**@}*/