Initial commit

This commit is contained in:
2026-07-13 09:25:40 +07:00
parent c08ff54676
commit bccfb156d7
1938 changed files with 641646 additions and 0 deletions

View File

@@ -0,0 +1,557 @@
# Appccelerate.StateMachine Usage Guide / Hướng dẫn Sử dụng Appccelerate.StateMachine
## 📋 Overview / Tổng quan
Tài liệu này mô tả cách sử dụng thư viện **Appccelerate.StateMachine** trong dự án RobotNet10. Thư viện này được sử dụng để quản lý state machine cho ScriptEngine (Task, Mission, và Engine Manager).
## 📦 Installation / Cài đặt
### NuGet Package
```xml
<PackageReference Include="Appccelerate.StateMachine" Version="6.0.0" />
```
### Namespaces
```csharp
using Appccelerate.StateMachine;
using Appccelerate.StateMachine.Machine;
```
## 🎯 Core Concepts / Khái niệm Cơ bản
### 1. States / Trạng thái
States là các trạng thái mà state machine có thể ở trong. Thường được định nghĩa bằng enum:
```csharp
public enum TaskState
{
Idle = 0,
Running,
Pausing,
Paused,
Resuming,
Stopping,
Stopped,
Error,
}
```
### 2. Triggers / Sự kiện
Triggers là các sự kiện có thể kích hoạt chuyển đổi trạng thái. Cũng thường là enum:
```csharp
public enum TaskTrigger
{
Start,
Pause,
Resume,
Stop,
Dispose,
PausingCompleted,
ResumingCompleted,
StoppingCompleted,
ErrorOccurred,
}
```
### 3. State Machine Types / Các Loại State Machine
Appccelerate.StateMachine hỗ trợ hai loại state machine:
- **PassiveStateMachine**: State machine được điều khiển thủ công, cần gọi `Fire()` để trigger transitions
- **ActiveStateMachine**: State machine tự động xử lý events từ queue
**Trong RobotNet10, chúng ta sử dụng `PassiveStateMachine`** để có kiểm soát tốt hơn.
## 🏗️ Building State Machine / Xây dựng State Machine
### Step 1: Create Builder / Tạo Builder
```csharp
var builder = new StateMachineDefinitionBuilder<TState, TTrigger>();
```
### Step 2: Configure States / Cấu hình States
Sử dụng fluent API để cấu hình các states và transitions:
```csharp
builder.In(TaskState.Idle)
.On(TaskTrigger.Start)
.Goto(TaskState.Running)
.Execute(() => OnEnterRunning());
```
### Step 3: Configure Entry/Exit Actions / Cấu hình Entry/Exit Actions
**Important**: `ExecuteOnEntry()``ExecuteOnExit()` phải được gọi **trước** các `On()` calls:
```csharp
builder.In(TaskState.Running)
.ExecuteOnEntry(() => { _currentState = TaskState.Running; OnEnterRunning(); })
.ExecuteOnExit(() => OnExitRunning())
.On(TaskTrigger.Pause)
.Goto(TaskState.Pausing)
.Execute(() => { _currentState = TaskState.Pausing; OnEnterPausing(); });
```
**⚠️ Common Mistake**: Đặt `ExecuteOnEntry()` sau `On()` sẽ gây lỗi compile.
### Step 4: Build and Create / Build và Tạo
```csharp
var stateMachine = builder
.WithInitialState(TaskState.Idle)
.Build()
.CreatePassiveStateMachine();
stateMachine.Start();
```
## 📝 Complete Example / Ví dụ Hoàn chỉnh
Dựa trên implementation của `ScriptTask`:
```csharp
using Appccelerate.StateMachine;
using Appccelerate.StateMachine.Machine;
public class ScriptTask
{
private readonly PassiveStateMachine<ScriptTaskState, TaskTrigger> _stateMachine;
private ScriptTaskState _currentState; // Track state manually
public enum TaskTrigger
{
Start,
Pause,
Resume,
Stop,
Dispose,
PausingCompleted,
ResumingCompleted,
StoppingCompleted,
ErrorOccurred,
}
public ScriptTask()
{
var builder = new StateMachineDefinitionBuilder<ScriptTaskState, TaskTrigger>();
ConfigureStateMachine(builder);
_stateMachine = builder
.WithInitialState(ScriptTaskState.Idle)
.Build()
.CreatePassiveStateMachine();
_currentState = ScriptTaskState.Idle;
_stateMachine.Start();
}
private void ConfigureStateMachine(StateMachineDefinitionBuilder<ScriptTaskState, TaskTrigger> builder)
{
// Idle state
builder.In(ScriptTaskState.Idle)
.On(TaskTrigger.Start)
.Goto(ScriptTaskState.Running)
.Execute(() => { _currentState = ScriptTaskState.Running; OnEnterRunning(); });
// Running state
builder.In(ScriptTaskState.Running)
.ExecuteOnEntry(() => { _currentState = ScriptTaskState.Running; OnEnterRunning(); })
.ExecuteOnExit(() => OnExitRunning())
.On(TaskTrigger.Pause)
.Goto(ScriptTaskState.Pausing)
.Execute(() => { _currentState = ScriptTaskState.Pausing; OnEnterPausing(); })
.On(TaskTrigger.Stop)
.Goto(ScriptTaskState.Stopping)
.Execute(() => { _currentState = ScriptTaskState.Stopping; OnEnterStopping(); })
.On(TaskTrigger.ErrorOccurred)
.Goto(ScriptTaskState.Error)
.Execute(() => { _currentState = ScriptTaskState.Error; OnEnterError(); });
// Pausing state
builder.In(ScriptTaskState.Pausing)
.ExecuteOnEntry(() => { _currentState = ScriptTaskState.Pausing; OnEnterPausing(); })
.ExecuteOnExit(() => OnExitPausing())
.On(TaskTrigger.PausingCompleted)
.Goto(ScriptTaskState.Paused)
.Execute(() => { _currentState = ScriptTaskState.Paused; OnEnterPaused(); })
.On(TaskTrigger.ErrorOccurred)
.Goto(ScriptTaskState.Error)
.Execute(() => { _currentState = ScriptTaskState.Error; OnEnterError(); });
// Paused state
builder.In(ScriptTaskState.Paused)
.ExecuteOnEntry(() => { _currentState = ScriptTaskState.Paused; OnEnterPaused(); })
.ExecuteOnExit(() => OnExitPaused())
.On(TaskTrigger.Resume)
.Goto(ScriptTaskState.Resuming)
.Execute(() => { _currentState = ScriptTaskState.Resuming; OnEnterResuming(); })
.On(TaskTrigger.Stop)
.Goto(ScriptTaskState.Stopping)
.Execute(() => { _currentState = ScriptTaskState.Stopping; OnEnterStopping(); });
// Resuming state
builder.In(ScriptTaskState.Resuming)
.ExecuteOnEntry(() => { _currentState = ScriptTaskState.Resuming; OnEnterResuming(); })
.ExecuteOnExit(() => OnExitResuming())
.On(TaskTrigger.ResumingCompleted)
.Goto(ScriptTaskState.Running)
.Execute(() => { _currentState = ScriptTaskState.Running; OnEnterRunning(); })
.On(TaskTrigger.ErrorOccurred)
.Goto(ScriptTaskState.Error)
.Execute(() => { _currentState = ScriptTaskState.Error; OnEnterError(); });
// Stopping state
builder.In(ScriptTaskState.Stopping)
.ExecuteOnEntry(() => { _currentState = ScriptTaskState.Stopping; OnEnterStopping(); })
.ExecuteOnExit(() => OnExitStopping())
.On(TaskTrigger.StoppingCompleted)
.Goto(ScriptTaskState.Stopped)
.Execute(() => { _currentState = ScriptTaskState.Stopped; OnEnterStopped(); })
.On(TaskTrigger.ErrorOccurred)
.Goto(ScriptTaskState.Error)
.Execute(() => { _currentState = ScriptTaskState.Error; OnEnterError(); });
// Stopped state
builder.In(ScriptTaskState.Stopped)
.ExecuteOnEntry(() => { _currentState = ScriptTaskState.Stopped; OnEnterStopped(); })
.ExecuteOnExit(() => OnExitStopped())
.On(TaskTrigger.Start)
.Goto(ScriptTaskState.Running)
.Execute(() => { _currentState = ScriptTaskState.Running; OnEnterRunning(); })
.On(TaskTrigger.Dispose)
.Execute(() => OnDispose());
// Error state
builder.In(ScriptTaskState.Error)
.ExecuteOnEntry(() => { _currentState = ScriptTaskState.Error; OnEnterError(); })
.ExecuteOnExit(() => OnExitError())
.On(TaskTrigger.Start)
.Goto(ScriptTaskState.Running)
.Execute(() => { _currentState = ScriptTaskState.Running; OnEnterRunning(); })
.On(TaskTrigger.Dispose)
.Execute(() => OnDispose());
}
// Public methods to fire triggers
public void Start()
{
_stateMachine.Fire(TaskTrigger.Start);
}
public void Pause()
{
_stateMachine.Fire(TaskTrigger.Pause);
}
// State property
public ScriptTaskState State => _currentState;
}
```
## 🔑 Key API Methods / Các Method API Chính
### StateMachineDefinitionBuilder Methods
| Method | Description | Example |
|--------|-------------|---------|
| `In(TState state)` | Bắt đầu cấu hình một state | `builder.In(TaskState.Running)` |
| `On(TTrigger trigger)` | Định nghĩa trigger cho transition | `.On(TaskTrigger.Start)` |
| `Goto(TState state)` | Chỉ định state đích | `.Goto(TaskState.Running)` |
| `Execute(Action action)` | Thực thi action khi transition | `.Execute(() => OnEnterRunning())` |
| `ExecuteOnEntry(Action action)` | Thực thi khi vào state | `.ExecuteOnEntry(() => OnEnterRunning())` |
| `ExecuteOnExit(Action action)` | Thực thi khi ra khỏi state | `.ExecuteOnExit(() => OnExitRunning())` |
| `WithInitialState(TState state)` | Đặt initial state | `.WithInitialState(TaskState.Idle)` |
| `Build()` | Build definition | `.Build()` |
| `CreatePassiveStateMachine()` | Tạo passive state machine | `.CreatePassiveStateMachine()` |
### PassiveStateMachine Methods
| Method | Description | Example |
|--------|-------------|---------|
| `Start()` | Khởi động state machine | `stateMachine.Start()` |
| `Fire(TTrigger trigger)` | Fire một trigger | `stateMachine.Fire(TaskTrigger.Start)` |
| `Stop()` | Dừng state machine | `stateMachine.Stop()` |
## ⚠️ Important Notes / Lưu Ý Quan trọng
### 1. State Tracking / Theo dõi State
**Problem**: `PassiveStateMachine` không có property `CurrentState` hoặc `CurrentStateId` để đọc state hiện tại.
**Solution**: Sử dụng một field riêng để track state và update nó trong các transition handlers:
```csharp
private ScriptTaskState _currentState;
// Update trong ExecuteOnEntry hoặc Execute
builder.In(TaskState.Running)
.ExecuteOnEntry(() => { _currentState = TaskState.Running; OnEnterRunning(); });
// Hoặc trong Execute của transition
.On(TaskTrigger.Start)
.Goto(TaskState.Running)
.Execute(() => { _currentState = TaskState.Running; OnEnterRunning(); });
```
### 2. Entry/Exit Actions Order / Thứ tự Entry/Exit Actions
**Critical**: `ExecuteOnEntry()``ExecuteOnExit()` **PHẢI** được gọi **TRƯỚC** các `On()` calls:
```csharp
// ✅ CORRECT
builder.In(TaskState.Running)
.ExecuteOnEntry(() => OnEnterRunning()) // First
.ExecuteOnExit(() => OnExitRunning()) // Second
.On(TaskTrigger.Pause) // Then transitions
.Goto(TaskState.Pausing);
// ❌ WRONG - Will cause compile error
builder.In(TaskState.Running)
.On(TaskTrigger.Pause)
.Goto(TaskState.Pausing)
.ExecuteOnEntry(() => OnEnterRunning()); // Error!
```
### 3. State Machine Lifecycle / Vòng đời State Machine
1. **Create**: Build definition và create state machine
2. **Start**: Gọi `Start()` để khởi động (state machine sẽ ở initial state)
3. **Fire Triggers**: Sử dụng `Fire()` để trigger transitions
4. **Stop**: Gọi `Stop()` khi không cần dùng nữa
```csharp
var stateMachine = builder
.WithInitialState(TaskState.Idle)
.Build()
.CreatePassiveStateMachine();
stateMachine.Start(); // State machine is now in Idle state
// Later...
stateMachine.Fire(TaskTrigger.Start); // Transition to Running
// When done...
stateMachine.Stop();
```
### 4. Error Handling / Xử lý Lỗi
State machine sẽ throw exception nếu:
- Fire trigger không hợp lệ (không có transition từ state hiện tại)
- State machine chưa được start
- State machine đã được stop
```csharp
try
{
_stateMachine.Fire(TaskTrigger.Start);
}
catch (Exception ex)
{
_logger.LogError($"Failed to fire trigger: {ex.Message}");
throw;
}
```
### 5. Thread Safety / An toàn Luồng
`PassiveStateMachine` **không thread-safe**. Nếu cần thread safety, sử dụng lock:
```csharp
private readonly object _lockObject = new();
public void Start()
{
lock (_lockObject)
{
_stateMachine.Fire(TaskTrigger.Start);
}
}
```
## 🎨 Best Practices / Thực hành Tốt nhất
### 1. Separate Configuration Method / Tách Method Cấu hình
Tách logic cấu hình state machine ra method riêng để code dễ đọc:
```csharp
private void ConfigureStateMachine(StateMachineDefinitionBuilder<TState, TTrigger> builder)
{
// All configuration here
}
```
### 2. Consistent State Tracking / Theo dõi State Nhất quán
Luôn update `_currentState` trong cả `ExecuteOnEntry()``Execute()` của transitions để đảm bảo consistency:
```csharp
builder.In(TaskState.Running)
.ExecuteOnEntry(() => { _currentState = TaskState.Running; OnEnterRunning(); })
.On(TaskTrigger.Start)
.Goto(TaskState.Running)
.Execute(() => { _currentState = TaskState.Running; OnEnterRunning(); });
```
### 3. Use Descriptive Trigger Names / Sử dụng Tên Trigger Mô tả
Đặt tên trigger rõ ràng để dễ hiểu:
```csharp
// ✅ Good
TaskTrigger.PausingCompleted
TaskTrigger.ResumingCompleted
TaskTrigger.ErrorOccurred
// ❌ Bad
TaskTrigger.Done
TaskTrigger.Ok
TaskTrigger.Err
```
### 4. Document State Transitions / Tài liệu hóa State Transitions
Sử dụng comments để giải thích logic:
```csharp
// Idle → Running: Start task
builder.In(TaskState.Idle)
.On(TaskTrigger.Start)
.Goto(TaskState.Running);
// Running → Pausing → Paused: Pause task (wait for current execution)
builder.In(TaskState.Running)
.On(TaskTrigger.Pause)
.Goto(TaskState.Pausing);
```
## 🔄 State Transition Patterns / Mẫu Chuyển đổi State
### Pattern 1: Simple Transition / Chuyển đổi Đơn giản
```csharp
builder.In(StateA)
.On(TriggerX)
.Goto(StateB)
.Execute(() => OnEnterStateB());
```
### Pattern 2: Transition with Entry/Exit / Chuyển đổi với Entry/Exit
```csharp
builder.In(StateA)
.ExecuteOnEntry(() => OnEnterStateA())
.ExecuteOnExit(() => OnExitStateA())
.On(TriggerX)
.Goto(StateB)
.Execute(() => OnEnterStateB());
```
### Pattern 3: Intermediate State / State Trung gian
Sử dụng intermediate state cho async operations:
```csharp
// Start → Intermediate → Final
builder.In(StateA)
.On(TriggerStart)
.Goto(StateIntermediate)
.Execute(() => StartAsyncOperation());
builder.In(StateIntermediate)
.ExecuteOnEntry(() => OnEnterIntermediate())
.On(TriggerCompleted)
.Goto(StateFinal)
.Execute(() => OnEnterFinal());
```
## 🐛 Common Pitfalls / Lỗi Thường gặp
### 1. Forgetting to Start / Quên Start
```csharp
// ❌ Wrong
var stateMachine = builder.Build().CreatePassiveStateMachine();
stateMachine.Fire(TaskTrigger.Start); // Exception!
// ✅ Correct
var stateMachine = builder.Build().CreatePassiveStateMachine();
stateMachine.Start();
stateMachine.Fire(TaskTrigger.Start);
```
### 2. Wrong Order of ExecuteOnEntry / Thứ tự ExecuteOnEntry Sai
```csharp
// ❌ Wrong - Compile error
builder.In(StateA)
.On(TriggerX)
.Goto(StateB)
.ExecuteOnEntry(() => OnEnterStateB());
// ✅ Correct
builder.In(StateA)
.ExecuteOnEntry(() => OnEnterStateA())
.On(TriggerX)
.Goto(StateB)
.Execute(() => OnEnterStateB());
```
### 3. Not Tracking State / Không Theo dõi State
```csharp
// ❌ Wrong - No way to read current state
public TaskState State => _stateMachine.CurrentState; // Property doesn't exist!
// ✅ Correct
private TaskState _currentState;
public TaskState State => _currentState;
// Update in transitions
.Execute(() => { _currentState = TaskState.Running; OnEnterRunning(); });
```
### 4. Fire Invalid Trigger / Fire Trigger Không hợp lệ
```csharp
// ❌ Wrong - Will throw exception if no transition defined
stateMachine.Fire(TaskTrigger.Start); // If current state doesn't allow Start
// ✅ Correct - Check state first or handle exception
if (_currentState == TaskState.Idle || _currentState == TaskState.Stopped)
{
stateMachine.Fire(TaskTrigger.Start);
}
```
## 📚 Related Documents / Tài liệu Liên quan
- [StateMachine Design](../ScriptEngine/StateMachine_Design.md) - State machine architecture cho ScriptEngine
- [ScriptTask Implementation](../../srcs/RobotNet10/Commons/RobotNet10.ScriptEngine/Models/ScriptTask.cs) - Reference implementation
- [Appccelerate.StateMachine Documentation](https://github.com/appccelerate/statemachine) - Official documentation
## 🔗 Example Usage in RobotNet10 / Ví dụ Sử dụng trong RobotNet10
State machine được sử dụng trong:
1. **ScriptTask** (`ScriptTask.cs`) - Task state management
2. **ScriptMissionInstance** (planned) - Mission instance state management
3. **ScriptEngineManager** (planned) - Engine manager state management
Xem implementation chi tiết trong các file này để hiểu cách sử dụng trong context thực tế.
---
**Last Updated**: 2025-11-13
**Status**: Usage Guide for Appccelerate.StateMachine
**Version**: 1.0
**Library Version**: 6.0.0