Initial commit
This commit is contained in:
@@ -0,0 +1,336 @@
|
||||
using Appccelerate.StateMachine;
|
||||
using Appccelerate.StateMachine.AsyncMachine;
|
||||
using Appccelerate.StateMachine.AsyncMachine.Events;
|
||||
|
||||
namespace RobotNet10.RobotApp.Services.State;
|
||||
|
||||
public class RobotStateMachine(Logger<RobotStateMachine> Logger, RobotStateMachineExecute StateExecute)
|
||||
{
|
||||
private AsyncPassiveStateMachine<RobotStateType, RobotEventType>? _stateMachine;
|
||||
private RobotStateType _currentState = RobotStateType.System;
|
||||
public bool IsInitialized { get; private set; } = false;
|
||||
|
||||
public RobotStateType CurrentState => _currentState;
|
||||
public event EventHandler<StateChangedEventArgs>? StateChanged;
|
||||
|
||||
// Dictionary để track hierarchy relationships cho helper methods
|
||||
private readonly Dictionary<RobotStateType, RobotStateType> _stateHierarchies = [];
|
||||
|
||||
public async Task InitializeAsync()
|
||||
{
|
||||
if (IsInitialized) return;
|
||||
|
||||
var builder = new StateMachineDefinitionBuilder<RobotStateType, RobotEventType>();
|
||||
|
||||
// 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}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Build hierarchy map - chỉ để track parent-child relationships cho helper methods
|
||||
/// Không cần build transitions vì Appccelerate tự động xử lý
|
||||
/// </summary>
|
||||
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<RobotStateType, RobotEventType> 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;
|
||||
}
|
||||
Reference in New Issue
Block a user