using Appccelerate.StateMachine; using Appccelerate.StateMachine.AsyncMachine; using Appccelerate.StateMachine.AsyncMachine.Events; namespace RobotNet10.RobotApp.Services.State; public class RobotStateMachine(Logger Logger, RobotStateMachineExecute StateExecute) { private AsyncPassiveStateMachine? _stateMachine; private RobotStateType _currentState = RobotStateType.System; public bool IsInitialized { get; private set; } = false; public RobotStateType CurrentState => _currentState; public event EventHandler? StateChanged; // Dictionary để track hierarchy relationships cho helper methods private readonly Dictionary _stateHierarchies = []; public async Task InitializeAsync() { if (IsInitialized) return; var builder = new StateMachineDefinitionBuilder(); // Build hierarchy map (chỉ cần track parent-child relationship) BuildHierarchyMap(); // =========================== // ROOT LEVEL - Hierarchical States // =========================== // System State Hierarchy builder.In(RobotStateType.System) .On(RobotEventType.EnterAuto).Goto(RobotStateType.Auto) .On(RobotEventType.EnterManual).Goto(RobotStateType.Manual) .On(RobotEventType.EnterService).Goto(RobotStateType.Service) .On(RobotEventType.RemoteOverride).Goto(RobotStateType.Remote_Override) .On(RobotEventType.EnterStop).Goto(RobotStateType.Stop) .On(RobotEventType.EnterFault).Goto(RobotStateType.Fault); builder.DefineHierarchyOn(RobotStateType.System) .WithHistoryType(HistoryType.Deep) .WithInitialSubState(RobotStateType.Initializing) .WithSubState(RobotStateType.Standby) .WithSubState(RobotStateType.Shutting_Down); // Auto State Hierarchy builder.In(RobotStateType.Auto) .On(RobotEventType.EnterManual).Goto(RobotStateType.Manual) .On(RobotEventType.EnterService).Goto(RobotStateType.Service) .On(RobotEventType.RemoteOverride).Goto(RobotStateType.Remote_Override) .On(RobotEventType.EnterStop).Goto(RobotStateType.Stop) .On(RobotEventType.EnterFault).Goto(RobotStateType.Fault); builder.DefineHierarchyOn(RobotStateType.Auto) .WithHistoryType(HistoryType.Deep) .WithInitialSubState(RobotStateType.Idle) .WithSubState(RobotStateType.Executing) .WithSubState(RobotStateType.Paused) .WithSubState(RobotStateType.Canceling) .WithSubState(RobotStateType.Recovering); // Manual State builder.In(RobotStateType.Manual) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Manual); StateExecute.EntryManual(); }) .ExecuteOnExit(StateExecute.ExitManual) .On(RobotEventType.EnterAuto).Goto(RobotStateType.Auto) .On(RobotEventType.EnterService).Goto(RobotStateType.Service) .On(RobotEventType.RemoteOverride).Goto(RobotStateType.Remote_Override) .On(RobotEventType.EnterStop).Goto(RobotStateType.Stop) .On(RobotEventType.EnterFault).Goto(RobotStateType.Fault); // Service State builder.In(RobotStateType.Service) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Service); StateExecute.EntryService(); }) .ExecuteOnExit(StateExecute.ExitService) .On(RobotEventType.EnterAuto).Goto(RobotStateType.Auto) .On(RobotEventType.EnterManual).Goto(RobotStateType.Manual) .On(RobotEventType.RemoteOverride).Goto(RobotStateType.Remote_Override) .On(RobotEventType.EnterStop).Goto(RobotStateType.Stop) .On(RobotEventType.EnterFault).Goto(RobotStateType.Fault); // Stop State builder.In(RobotStateType.Stop) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Stop); StateExecute.EntryStop(); }) .ExecuteOnExit(StateExecute.ExitStop) .On(RobotEventType.ReleaseStop).Goto(RobotStateType.System) .On(RobotEventType.EnterFault).Goto(RobotStateType.Fault); // Fault State builder.In(RobotStateType.Fault) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Fault); StateExecute.EntryFault(); }) .ExecuteOnExit(StateExecute.ExitFault) .On(RobotEventType.ExitFault).Goto(RobotStateType.System); // Remote_Override State (top-level, peer of Service) builder.In(RobotStateType.Remote_Override) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Remote_Override); StateExecute.EntryRemoteOverride(); }) .ExecuteOnExit(StateExecute.ExitRemoteOverride) .On(RobotEventType.EnterAuto).Goto(RobotStateType.Auto) .On(RobotEventType.EnterManual).Goto(RobotStateType.Manual) .On(RobotEventType.EnterService).Goto(RobotStateType.Service) .On(RobotEventType.EnterStop).Goto(RobotStateType.Stop) .On(RobotEventType.EnterFault).Goto(RobotStateType.Fault); // =========================== // SYSTEM SUB-STATES // =========================== builder.In(RobotStateType.Initializing) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Initializing); StateExecute.EntryInitializing(); }) .On(RobotEventType.InitializeCompleted).Goto(RobotStateType.Standby) .On(RobotEventType.EnterFault).Goto(RobotStateType.Fault); builder.In(RobotStateType.Standby) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Standby); StateExecute.EntryStandby(); }) .On(RobotEventType.Shutdown).Goto(RobotStateType.Shutting_Down); builder.In(RobotStateType.Shutting_Down) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Shutting_Down); StateExecute.EntryShuttingDown(); }) .ExecuteOnExit(StateExecute.ExitShuttingDown) .On(RobotEventType.ShutdownCompleted).Goto(RobotStateType.Standby); // =========================== // AUTO SUB-STATES // =========================== builder.In(RobotStateType.Idle) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Idle); StateExecute.EntryIdle(); }) .On(RobotEventType.StartExecution).Goto(RobotStateType.Executing); // Executing State Hierarchy builder.In(RobotStateType.Executing) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Executing); StateExecute.EntryExecuting(); }) .ExecuteOnExit(StateExecute.ExitExecuting) .On(RobotEventType.PauseExecution).Goto(RobotStateType.Paused) .On(RobotEventType.CancelExecution).Goto(RobotStateType.Canceling) .On(RobotEventType.CompleteExecution).Goto(RobotStateType.Idle); builder.DefineHierarchyOn(RobotStateType.Executing) .WithHistoryType(HistoryType.Deep) .WithInitialSubState(RobotStateType.Moving) .WithSubState(RobotStateType.ACT); builder.In(RobotStateType.Paused) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Paused); StateExecute.EntryPaused(); }) .ExecuteOnExit(StateExecute.ExitPaused) .On(RobotEventType.ResumeExecution).Goto(RobotStateType.Executing) .On(RobotEventType.CancelExecution).Goto(RobotStateType.Canceling); builder.In(RobotStateType.Canceling) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Canceling); StateExecute.EntryCanceling(); }) .ExecuteOnExit(StateExecute.ExitCanceling) .On(RobotEventType.CompleteExecution).Goto(RobotStateType.Idle); builder.In(RobotStateType.Recovering) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Recovering); StateExecute.EntryRecovering(); }) .On(RobotEventType.CompleteRecovery).Goto(RobotStateType.Idle); // =========================== // EXECUTING SUB-STATES // =========================== // Moving State Hierarchy builder.In(RobotStateType.Moving) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Moving); StateExecute.EntryMoving(); }) .ExecuteOnExit(StateExecute.ExitMoving) .On(RobotEventType.StartACT).Goto(RobotStateType.ACT) .On(RobotEventType.CompleteMoving).Goto(RobotStateType.Idle); // ACT State Hierarchy builder.In(RobotStateType.ACT) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.ACT); StateExecute.EntryACT(); }) .ExecuteOnExit(StateExecute.ExitACT) .On(RobotEventType.StartMoving).Goto(RobotStateType.Moving) .On(RobotEventType.CompleteACT).Goto(RobotStateType.Idle); builder.DefineHierarchyOn(RobotStateType.ACT) .WithHistoryType(HistoryType.Deep) .WithInitialSubState(RobotStateType.Docking) .WithSubState(RobotStateType.Docked) .WithSubState(RobotStateType.Charging) .WithSubState(RobotStateType.Undocking) .WithSubState(RobotStateType.Loading) .WithSubState(RobotStateType.Unloading) .WithSubState(RobotStateType.TechAction); // =========================== // ACT SUB-STATES // =========================== builder.In(RobotStateType.Docking) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Docking); StateExecute.EntryDocking(); }) .On(RobotEventType.CompleteDocking).Goto(RobotStateType.Docked); builder.In(RobotStateType.Docked) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Docked); StateExecute.EntryDocked(); }) .On(RobotEventType.StartCharging).Goto(RobotStateType.Charging) .On(RobotEventType.StartUndocking).Goto(RobotStateType.Undocking) .On(RobotEventType.StartLoading).Goto(RobotStateType.Loading) .On(RobotEventType.StartUnloading).Goto(RobotStateType.Unloading); builder.In(RobotStateType.Charging) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Charging); StateExecute.EntryCharging(); }) .ExecuteOnExit(StateExecute.ExitCharging) .On(RobotEventType.CompleteCharging).Goto(RobotStateType.Docked); builder.In(RobotStateType.Undocking) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Undocking); StateExecute.EntryUndocking(); }) .On(RobotEventType.CompleteUndocking).Goto(RobotStateType.Docking); builder.In(RobotStateType.Loading) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Loading); StateExecute.EntryLoading(); }) .On(RobotEventType.CompleteLoading).Goto(RobotStateType.Docked); builder.In(RobotStateType.Unloading) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.Unloading); StateExecute.EntryUnloading(); }) .On(RobotEventType.CompleteUnloading).Goto(RobotStateType.Docked); builder.In(RobotStateType.TechAction) .ExecuteOnEntry(() => { OnEnterState(RobotStateType.TechAction); StateExecute.EntryTechAction(); }) .On(RobotEventType.CompleteTechAction).Goto(RobotStateType.Docked); // =========================== // CREATE STATE MACHINE // =========================== _stateMachine = builder .WithInitialState(RobotStateType.System) .Build() .CreatePassiveStateMachine(); // Subscribe to state change events _stateMachine.TransitionCompleted += OnTransitionCompleted; // Set IsInitialized = true TRƯỚC khi Start() để tránh deadlock // Vì EntryInitializing() có thể gọi ModuleInitializeAsync() chờ IsInitialized IsInitialized = true; // Start state machine await _stateMachine.Start(); Logger.Info($"State Machine initialized successfully with current state: {CurrentState}"); } /// /// Build hierarchy map - chỉ để track parent-child relationships cho helper methods /// Không cần build transitions vì Appccelerate tự động xử lý /// private void BuildHierarchyMap() { // System sub-states _stateHierarchies[RobotStateType.Initializing] = RobotStateType.System; _stateHierarchies[RobotStateType.Standby] = RobotStateType.System; _stateHierarchies[RobotStateType.Shutting_Down] = RobotStateType.System; // Auto sub-states _stateHierarchies[RobotStateType.Idle] = RobotStateType.Auto; _stateHierarchies[RobotStateType.Executing] = RobotStateType.Auto; _stateHierarchies[RobotStateType.Paused] = RobotStateType.Auto; _stateHierarchies[RobotStateType.Canceling] = RobotStateType.Auto; _stateHierarchies[RobotStateType.Recovering] = RobotStateType.Auto; // Executing sub-states _stateHierarchies[RobotStateType.Moving] = RobotStateType.Executing; _stateHierarchies[RobotStateType.ACT] = RobotStateType.Executing; // ACT sub-states _stateHierarchies[RobotStateType.Docking] = RobotStateType.ACT; _stateHierarchies[RobotStateType.Docked] = RobotStateType.ACT; _stateHierarchies[RobotStateType.Charging] = RobotStateType.ACT; _stateHierarchies[RobotStateType.Undocking] = RobotStateType.ACT; _stateHierarchies[RobotStateType.Loading] = RobotStateType.ACT; _stateHierarchies[RobotStateType.Unloading] = RobotStateType.ACT; _stateHierarchies[RobotStateType.TechAction] = RobotStateType.ACT; } public void Initialize() => InitializeAsync().GetAwaiter().GetResult(); public async Task FireAsync(RobotEventType eventType) { if (!IsInitialized) { Logger.Warning("State Machine not initialized!"); return; } try { if (_stateMachine != null) { await _stateMachine.Fire(eventType); } } catch (Exception ex) { Logger.Error($"Fire event {eventType} error: {ex.Message}"); } } public void Fire(RobotEventType eventType) => FireAsync(eventType).GetAwaiter().GetResult(); public bool IsInState(RobotStateType state) { if (!IsInitialized) return false; if (CurrentState == state) return true; if(_stateHierarchies.TryGetValue(CurrentState, out RobotStateType parentState)) { if(parentState == state) return true; while (_stateHierarchies.TryGetValue(parentState, out parentState)) { if (parentState == state) return true; } } return false; } private void OnEnterState(RobotStateType state) { _currentState = state; } private void OnTransitionCompleted(object? sender, TransitionCompletedEventArgs e) { Logger.Info($"State Transition: {e.StateId} -> Event: {e.EventId}"); StateChanged?.Invoke(this, new StateChangedEventArgs(e.StateId, e.EventId)); } } public class StateChangedEventArgs(RobotStateType newState, RobotEventType eventType) : EventArgs { public RobotStateType NewState { get; } = newState; public RobotEventType EventType { get; } = eventType; }