Initial commit
This commit is contained in:
557
docs/development/AppccelerateStateMachine.md
Normal file
557
docs/development/AppccelerateStateMachine.md
Normal 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()` và `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()` và `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()` và `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
|
||||
|
||||
614
docs/development/ProjectStructure.md
Normal file
614
docs/development/ProjectStructure.md
Normal file
@@ -0,0 +1,614 @@
|
||||
# Project Structure & Conventions / Cấu trúc Dự án & Quy ước
|
||||
|
||||
## 📋 Overview / Tổng quan
|
||||
|
||||
Tài liệu này mô tả cấu trúc dự án RobotNet10, các thư viện được sử dụng, và các quy ước chung khi phát triển code.
|
||||
|
||||
## 🏗️ Project Structure / Cấu trúc Dự án
|
||||
|
||||
### Solution Organization / Tổ chức Solution
|
||||
|
||||
Dự án được tổ chức theo cấu trúc solution với các thư mục chính:
|
||||
|
||||
```
|
||||
srcs/RobotNet10/
|
||||
├── Commons/ # Thư viện chung cho scripting
|
||||
│ ├── RobotNet10.Script/ # Script attributes và interfaces
|
||||
│ ├── RobotNet10.ScriptEngine/ # ScriptEngine core implementation
|
||||
│ └── RobotNet10.MapManager/ # Map management (tương lai)
|
||||
│
|
||||
├── Components/ # Blazor component libraries
|
||||
│ ├── RobotNet10.Components/ # Shared UI components
|
||||
│ ├── RobotNet10.MapEditor/ # Map editor components
|
||||
│ └── RobotNet10.ScriptEditor/ # Script editor components
|
||||
│
|
||||
├── FleetManager/ # FleetManager application
|
||||
│ ├── RobotNet10.FleetManager/ # Server-side Blazor app
|
||||
│ ├── RobotNet10.FleetManager.Client/ # Client-side Blazor (WASM)
|
||||
│ ├── RobotNet10.FleetManager.Script/ # Script APIs cho FleetManager
|
||||
│ └── RobotNet10.FleetManager.Script.Shared/ # Shared script interfaces
|
||||
│
|
||||
├── RobotApp/ # RobotApp application
|
||||
│ ├── RobotNet10.RobotApp/ # Server-side Blazor app
|
||||
│ ├── RobotNet10.RobotApp.Client/ # Client-side Blazor (WASM)
|
||||
│ ├── RobotNet10.RobotApp.Script/ # Script APIs cho RobotApp
|
||||
│ └── RobotNet10.RobotApp.Script.Shared/ # Shared script interfaces
|
||||
│
|
||||
└── Shared/ # Shared libraries
|
||||
├── RobotNet10.ScriptEngine.Shared/ # ScriptEngine shared contracts
|
||||
└── RobotNet10.Shared/ # Common utilities
|
||||
```
|
||||
|
||||
### Project Types / Các Loại Project
|
||||
|
||||
#### 1. Web Applications (Blazor Web App)
|
||||
|
||||
**RobotNet10.RobotApp** và **RobotNet10.FleetManager**:
|
||||
- **SDK**: `Microsoft.NET.Sdk.Web`
|
||||
- **Target Framework**: `net10.0`
|
||||
- **Architecture**: Blazor Web App với Server + WASM render modes
|
||||
- **Database**:
|
||||
- RobotApp: SQLite (`Microsoft.EntityFrameworkCore.Sqlite`)
|
||||
- FleetManager: SQL Server (`Microsoft.EntityFrameworkCore.SqlServer`)
|
||||
|
||||
#### 2. Client Projects (Blazor WASM)
|
||||
|
||||
**RobotNet10.RobotApp.Client** và **RobotNet10.FleetManager.Client**:
|
||||
- **SDK**: `Microsoft.NET.Sdk.BlazorWebAssembly`
|
||||
- **Purpose**: Client-side UI components và pages
|
||||
- **Dependencies**: Reference từ Web App projects
|
||||
|
||||
#### 3. Script Projects
|
||||
|
||||
**RobotNet10.*.Script** và **RobotNet10.*.Script.Shared**:
|
||||
- **SDK**: `Microsoft.NET.Sdk`
|
||||
- **Purpose**:
|
||||
- `.Script`: Implementation của script APIs
|
||||
- `.Script.Shared`: Interfaces và contracts cho script globals
|
||||
|
||||
#### 4. Component Libraries (Razor Class Library)
|
||||
|
||||
**RobotNet10.Components**, **RobotNet10.MapEditor**, **RobotNet10.ScriptEditor**:
|
||||
- **SDK**: `Microsoft.NET.Sdk.Razor`
|
||||
- **Purpose**: Reusable Blazor components
|
||||
- **Supported Platform**: `browser` (WASM)
|
||||
|
||||
#### 5. Shared Libraries
|
||||
|
||||
**RobotNet10.ScriptEngine.Shared**, **RobotNet10.Shared**:
|
||||
- **SDK**: `Microsoft.NET.Sdk`
|
||||
- **Purpose**: Shared contracts, DTOs, utilities
|
||||
|
||||
#### 6. Commons Libraries
|
||||
|
||||
**RobotNet10.Script**, **RobotNet10.ScriptEngine**:
|
||||
- **SDK**: `Microsoft.NET.Sdk`
|
||||
- **Purpose**: Core scripting infrastructure
|
||||
|
||||
## 📚 Technology Stack / Công nghệ Sử dụng
|
||||
|
||||
### Core Framework
|
||||
|
||||
| Technology | Version | Purpose |
|
||||
|------------|---------|---------|
|
||||
| **.NET** | 10.0 | Runtime và framework |
|
||||
| **C#** | Latest | Programming language |
|
||||
| **Blazor** | 10.0 | Web UI framework |
|
||||
|
||||
### Key NuGet Packages
|
||||
|
||||
#### Web & UI
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| `Microsoft.AspNetCore.Components.WebAssembly.Server` | 10.0.0 | Blazor WASM hosting |
|
||||
| `Microsoft.AspNetCore.Components.Web` | 10.0.0 | Blazor components |
|
||||
| `Microsoft.AspNetCore.Identity.EntityFrameworkCore` | 10.0.0 | Authentication & Authorization |
|
||||
| `Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore` | 10.0.0 | EF Core diagnostics |
|
||||
|
||||
#### Database
|
||||
|
||||
| Package | Version | Purpose | Used In |
|
||||
|---------|---------|----------|---------|
|
||||
| `Microsoft.EntityFrameworkCore.Sqlite` | 10.0.0 | SQLite provider | RobotApp |
|
||||
| `Microsoft.EntityFrameworkCore.SqlServer` | 10.0.0 | SQL Server provider | FleetManager |
|
||||
| `Microsoft.EntityFrameworkCore.Tools` | 10.0.0 | EF Core migrations | Both |
|
||||
|
||||
#### Scripting
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| `Microsoft.CodeAnalysis.CSharp.Scripting` | 4.14.0 | C# script compilation |
|
||||
| `Appccelerate.StateMachine` | 6.0.0 | State machine implementation |
|
||||
| `Microsoft.AspNetCore.SignalR.Core` | 1.2.0 | SignalR for real-time communication |
|
||||
| `Newtonsoft.Json` | 13.0.4 | JSON serialization |
|
||||
|
||||
#### Other
|
||||
|
||||
| Package | Version | Purpose |
|
||||
|---------|---------|---------|
|
||||
| `Microsoft.Extensions.DependencyInjection.Abstractions` | 10.0.0 | DI abstractions |
|
||||
| `Microsoft.Extensions.Configuration.Binder` | 10.0.0 | Configuration binding |
|
||||
| `Microsoft.EntityFrameworkCore.Relational` | 10.0.0 | EF Core relational features |
|
||||
|
||||
### Project Dependencies / Phụ thuộc Dự án
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Web Apps"
|
||||
RobotApp[RobotNet10.RobotApp]
|
||||
FleetManager[RobotNet10.FleetManager]
|
||||
end
|
||||
|
||||
subgraph "Clients"
|
||||
RobotAppClient[RobotNet10.RobotApp.Client]
|
||||
FleetManagerClient[RobotNet10.FleetManager.Client]
|
||||
end
|
||||
|
||||
subgraph "Script Projects"
|
||||
RobotAppScript[RobotNet10.RobotApp.Script]
|
||||
RobotAppScriptShared[RobotNet10.RobotApp.Script.Shared]
|
||||
FleetManagerScript[RobotNet10.FleetManager.Script]
|
||||
FleetManagerScriptShared[RobotNet10.FleetManager.Script.Shared]
|
||||
end
|
||||
|
||||
subgraph "Commons"
|
||||
Script[RobotNet10.Script]
|
||||
ScriptEngine[RobotNet10.ScriptEngine]
|
||||
end
|
||||
|
||||
subgraph "Shared"
|
||||
ScriptEngineShared[RobotNet10.ScriptEngine.Shared]
|
||||
Shared[RobotNet10.Shared]
|
||||
end
|
||||
|
||||
subgraph "Components"
|
||||
Components[RobotNet10.Components]
|
||||
MapEditor[RobotNet10.MapEditor]
|
||||
ScriptEditor[RobotNet10.ScriptEditor]
|
||||
end
|
||||
|
||||
RobotApp --> RobotAppClient
|
||||
RobotApp --> ScriptEngine
|
||||
FleetManager --> FleetManagerClient
|
||||
FleetManager --> ScriptEngine
|
||||
|
||||
RobotAppScript --> RobotAppScriptShared
|
||||
RobotAppScript --> ScriptEngineShared
|
||||
RobotAppScript --> Script
|
||||
|
||||
FleetManagerScript --> FleetManagerScriptShared
|
||||
FleetManagerScript --> ScriptEngineShared
|
||||
FleetManagerScript --> Script
|
||||
|
||||
ScriptEngine --> ScriptEngineShared
|
||||
ScriptEngine --> Shared
|
||||
ScriptEngine --> Script
|
||||
|
||||
Components --> ScriptEngineShared
|
||||
Components --> Shared
|
||||
|
||||
MapEditor --> ScriptEngineShared
|
||||
MapEditor --> Shared
|
||||
|
||||
ScriptEditor --> ScriptEngineShared
|
||||
ScriptEditor --> Shared
|
||||
|
||||
style RobotApp fill:#e6f3ff
|
||||
style FleetManager fill:#e6f3ff
|
||||
style ScriptEngine fill:#fff0e6
|
||||
```
|
||||
|
||||
## 📝 Naming Conventions / Quy ước Đặt tên
|
||||
|
||||
### Namespaces
|
||||
|
||||
**Pattern**: `RobotNet10.{ProjectName}[.{SubNamespace}]`
|
||||
|
||||
**Examples**:
|
||||
```csharp
|
||||
namespace RobotNet10.Script; // Commons
|
||||
namespace RobotNet10.ScriptEngine.Shared; // Shared
|
||||
namespace RobotNet10.RobotApp.Script.Shared; // Script shared
|
||||
namespace RobotNet10.Components.Clients; // Components
|
||||
```
|
||||
|
||||
**Rules**:
|
||||
- ✅ Use PascalCase
|
||||
- ✅ Match project/folder structure
|
||||
- ✅ Avoid abbreviations unless widely understood
|
||||
- ✅ Keep namespaces shallow (max 3-4 levels)
|
||||
|
||||
### Classes & Interfaces
|
||||
|
||||
**Interfaces**:
|
||||
```csharp
|
||||
// Prefix with 'I'
|
||||
public interface IScriptGlobals { }
|
||||
public interface IRobotAppScriptGlobals { }
|
||||
public interface ILogger { }
|
||||
```
|
||||
|
||||
**Classes**:
|
||||
```csharp
|
||||
// PascalCase, descriptive names
|
||||
public class ScriptEngine { }
|
||||
public class HubClient { }
|
||||
public class TaskAttribute : Attribute { }
|
||||
```
|
||||
|
||||
**Abstract Classes**:
|
||||
```csharp
|
||||
// PascalCase, can be abstract
|
||||
public abstract class HubClient { }
|
||||
```
|
||||
|
||||
**Rules**:
|
||||
- ✅ Use PascalCase
|
||||
- ✅ Use descriptive names (avoid abbreviations)
|
||||
- ✅ Interfaces start with 'I'
|
||||
- ✅ Attributes end with 'Attribute' (e.g., `TaskAttribute`)
|
||||
|
||||
### Methods & Properties
|
||||
|
||||
**Methods**:
|
||||
```csharp
|
||||
// PascalCase, verb-based names
|
||||
public async Task StartAsync() { }
|
||||
public void EnableTask(string name) { }
|
||||
public Task<Guid> CreateMission(string name, params object[] args) { }
|
||||
```
|
||||
|
||||
**Properties**:
|
||||
```csharp
|
||||
// PascalCase, noun-based names
|
||||
public IRobot Robot { get; }
|
||||
public bool IsConnected => Connection.State == HubConnectionState.Connected;
|
||||
public int Interval { get; }
|
||||
```
|
||||
|
||||
**Async Methods**:
|
||||
- ✅ Always end with `Async` suffix
|
||||
- ✅ Return `Task` or `Task<T>`
|
||||
- ✅ Use `async/await` pattern
|
||||
|
||||
**Rules**:
|
||||
- ✅ Use PascalCase
|
||||
- ✅ Methods: verb-based (e.g., `StartAsync`, `EnableTask`)
|
||||
- ✅ Properties: noun-based (e.g., `Robot`, `IsConnected`)
|
||||
- ✅ Async methods: suffix `Async`
|
||||
|
||||
### Fields & Variables
|
||||
|
||||
**Private Fields**:
|
||||
```csharp
|
||||
// camelCase with underscore prefix (if needed for clarity)
|
||||
private readonly HubConnection Connection;
|
||||
private readonly ManualResetEvent connectedWaitHandler;
|
||||
```
|
||||
|
||||
**Local Variables**:
|
||||
```csharp
|
||||
// camelCase
|
||||
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
|
||||
var app = builder.Build();
|
||||
```
|
||||
|
||||
**Constants**:
|
||||
```csharp
|
||||
// PascalCase
|
||||
public const int DefaultInterval = 60;
|
||||
public const string DefaultConnectionString = "DefaultConnection";
|
||||
```
|
||||
|
||||
**Rules**:
|
||||
- ✅ Private fields: camelCase (or underscore prefix if needed)
|
||||
- ✅ Local variables: camelCase
|
||||
- ✅ Constants: PascalCase
|
||||
|
||||
### Attributes
|
||||
|
||||
**Custom Attributes**:
|
||||
```csharp
|
||||
// End with 'Attribute', PascalCase
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
|
||||
public class TaskAttribute(int interval, bool autoStart = true) : Attribute
|
||||
{
|
||||
public int Interval { get; } = interval;
|
||||
public bool AutoStart { get; } = autoStart;
|
||||
}
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
```csharp
|
||||
[Task(interval: 60, autoStart: true)]
|
||||
public void MonitorTask() { }
|
||||
```
|
||||
|
||||
### Project & File Names
|
||||
|
||||
**Projects**:
|
||||
- ✅ Format: `RobotNet10.{Component}.{SubComponent}`
|
||||
- ✅ Examples: `RobotNet10.RobotApp`, `RobotNet10.FleetManager.Client`
|
||||
|
||||
**Files**:
|
||||
- ✅ Match class/interface name (one class per file)
|
||||
- ✅ Use PascalCase: `HubClient.cs`, `TaskAttribute.cs`
|
||||
|
||||
**Folders**:
|
||||
- ✅ Use PascalCase: `Components/`, `Clients/`, `Data/`
|
||||
|
||||
## 🎯 Code Organization Principles / Nguyên tắc Tổ chức Code
|
||||
|
||||
### 1. Separation of Concerns / Phân tách Trách nhiệm
|
||||
|
||||
**Layers**:
|
||||
- **Presentation**: Blazor components và pages
|
||||
- **Application**: Business logic và services
|
||||
- **Data**: Database access (EF Core)
|
||||
- **Infrastructure**: External integrations (MQTT, hardware)
|
||||
|
||||
### 2. Dependency Injection / Tiêm Phụ thuộc
|
||||
|
||||
**Pattern**: Constructor injection
|
||||
|
||||
```csharp
|
||||
public class OrderManager
|
||||
{
|
||||
private readonly INavigationService _navigation;
|
||||
private readonly ILogger<OrderManager> _logger;
|
||||
|
||||
public OrderManager(INavigationService navigation, ILogger<OrderManager> logger)
|
||||
{
|
||||
_navigation = navigation;
|
||||
_logger = logger;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**Registration**:
|
||||
```csharp
|
||||
// In Program.cs
|
||||
builder.Services.AddScoped<IOrderManager, OrderManager>();
|
||||
builder.Services.AddSingleton<IRobotController, RobotController>();
|
||||
```
|
||||
|
||||
### 3. Async/Await Pattern / Mẫu Async/Await
|
||||
|
||||
**Always use async for I/O operations**:
|
||||
|
||||
```csharp
|
||||
public async Task<bool> HandleOrderAsync(Order order)
|
||||
{
|
||||
// Validate
|
||||
if (!ValidateOrder(order))
|
||||
return false;
|
||||
|
||||
// Process (async)
|
||||
await _navigation.MoveToNodeAsync(order.Nodes[0].NodeId);
|
||||
|
||||
return true;
|
||||
}
|
||||
```
|
||||
|
||||
**Rules**:
|
||||
- ✅ All I/O operations: async
|
||||
- ✅ Database queries: async
|
||||
- ✅ MQTT operations: async
|
||||
- ✅ HTTP requests: async
|
||||
- ✅ File operations: async
|
||||
|
||||
### 4. Error Handling / Xử lý Lỗi
|
||||
|
||||
**Pattern**: Structured exception handling
|
||||
|
||||
```csharp
|
||||
try
|
||||
{
|
||||
await ProcessOrderAsync(order);
|
||||
}
|
||||
catch (ValidationException ex)
|
||||
{
|
||||
_logger.LogWarning(ex, "Order validation failed: OrderId={OrderId}", order.OrderId);
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Unexpected error processing order: OrderId={OrderId}", order.OrderId);
|
||||
throw;
|
||||
}
|
||||
```
|
||||
|
||||
### 5. Logging / Ghi Log
|
||||
|
||||
**Pattern**: Structured logging với parameters
|
||||
|
||||
```csharp
|
||||
_logger.LogInformation("Order received: OrderId={OrderId}, UpdateId={UpdateId}",
|
||||
order.OrderId, order.OrderUpdateId);
|
||||
|
||||
_logger.LogWarning("Robot not available: SerialNumber={SerialNumber}", serialNumber);
|
||||
|
||||
_logger.LogError(ex, "Failed to process order: OrderId={OrderId}", order.OrderId);
|
||||
```
|
||||
|
||||
**Rules**:
|
||||
- ✅ Use structured logging (parameters, not string interpolation)
|
||||
- ✅ Appropriate log levels (Debug, Information, Warning, Error)
|
||||
- ✅ Include context (OrderId, SerialNumber, etc.)
|
||||
|
||||
## 📦 Project Configuration / Cấu hình Dự án
|
||||
|
||||
### Common Properties / Thuộc tính Chung
|
||||
|
||||
**Target Framework**:
|
||||
```xml
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
```
|
||||
|
||||
**Nullable Reference Types**:
|
||||
```xml
|
||||
<Nullable>enable</Nullable>
|
||||
```
|
||||
|
||||
**Implicit Usings**:
|
||||
```xml
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
```
|
||||
|
||||
**Documentation**:
|
||||
```xml
|
||||
<GenerateDocumentationFile>True</GenerateDocumentationFile>
|
||||
```
|
||||
|
||||
### Blazor-Specific Properties / Thuộc tính Blazor
|
||||
|
||||
**Disable Navigation Exception**:
|
||||
```xml
|
||||
<BlazorDisableThrowNavigationException>true</BlazorDisableThrowNavigationException>
|
||||
```
|
||||
|
||||
**User Secrets** (for development):
|
||||
```xml
|
||||
<UserSecretsId>aspnet-RobotNet10_RobotApp-{guid}</UserSecretsId>
|
||||
```
|
||||
|
||||
## 🔗 Project References / Tham chiếu Dự án
|
||||
|
||||
### Reference Patterns / Mẫu Tham chiếu
|
||||
|
||||
**Web App → Client**:
|
||||
```xml
|
||||
<ProjectReference Include="..\RobotNet10.RobotApp.Client\RobotNet10.RobotApp.Client.csproj" />
|
||||
```
|
||||
|
||||
**Script → Script Shared**:
|
||||
```xml
|
||||
<ProjectReference Include="..\RobotNet10.RobotApp.Script.Shared\RobotNet10.RobotApp.Script.Shared.csproj" />
|
||||
```
|
||||
|
||||
**ScriptEngine → Commons**:
|
||||
```xml
|
||||
<ProjectReference Include="..\RobotNet10.Script\RobotNet10.Script.csproj" />
|
||||
```
|
||||
|
||||
**Components → Shared**:
|
||||
```xml
|
||||
<ProjectReference Include="..\..\Shared\RobotNet10.ScriptEngine.Shared\RobotNet10.ScriptEngine.Shared.csproj" />
|
||||
<ProjectReference Include="..\..\Shared\RobotNet10.Shared\RobotNet10.Shared.csproj" />
|
||||
```
|
||||
|
||||
### Dependency Rules / Quy tắc Phụ thuộc
|
||||
|
||||
**Allowed**:
|
||||
- ✅ Web App → Client
|
||||
- ✅ Web App → ScriptEngine
|
||||
- ✅ Script → Script.Shared
|
||||
- ✅ Script → ScriptEngine.Shared
|
||||
- ✅ ScriptEngine → Script (Commons)
|
||||
- ✅ Components → Shared libraries
|
||||
|
||||
**Not Allowed**:
|
||||
- ❌ Client → Server (WASM cannot reference server code)
|
||||
- ❌ Shared → Application-specific projects
|
||||
- ❌ Circular dependencies
|
||||
|
||||
## 📂 Folder Structure / Cấu trúc Thư mục
|
||||
|
||||
### Standard Folders / Thư mục Chuẩn
|
||||
|
||||
**Web Applications**:
|
||||
```
|
||||
RobotNet10.RobotApp/
|
||||
├── Components/ # Blazor components
|
||||
├── Data/ # EF Core DbContext
|
||||
├── Pages/ # Blazor pages (if needed)
|
||||
├── Properties/ # Assembly info, launch settings
|
||||
├── wwwroot/ # Static files
|
||||
├── Program.cs # Application entry point
|
||||
└── appsettings.json # Configuration
|
||||
```
|
||||
|
||||
**Component Libraries**:
|
||||
```
|
||||
RobotNet10.Components/
|
||||
├── Clients/ # SignalR clients
|
||||
├── _Imports.razor # Global imports
|
||||
├── wwwroot/ # Static assets
|
||||
└── *.razor # Component files
|
||||
```
|
||||
|
||||
**Script Projects**:
|
||||
```
|
||||
RobotNet10.RobotApp.Script/
|
||||
├── IRobot.cs # Script APIs interface
|
||||
└── *.cs # Implementation files
|
||||
```
|
||||
|
||||
## 🎨 Code Style Guidelines / Hướng dẫn Phong cách Code
|
||||
|
||||
### C# Language Features / Tính năng C#
|
||||
|
||||
**Preferred**:
|
||||
- ✅ Primary constructors (C# 12)
|
||||
- ✅ Collection expressions
|
||||
- ✅ Pattern matching
|
||||
- ✅ Nullable reference types
|
||||
- ✅ File-scoped namespaces
|
||||
|
||||
**Example**:
|
||||
```csharp
|
||||
namespace RobotNet10.Script;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
|
||||
public class TaskAttribute(int interval, bool autoStart = true) : Attribute
|
||||
{
|
||||
public int Interval { get; } = interval;
|
||||
public bool AutoStart { get; } = autoStart;
|
||||
}
|
||||
```
|
||||
|
||||
### XML Documentation / Tài liệu XML
|
||||
|
||||
**Required for public APIs**:
|
||||
```csharp
|
||||
/// <summary>
|
||||
/// Thuộc tính để đánh dấu một phương thức là một tác vụ định kỳ trong hệ thống RobotNet.
|
||||
/// </summary>
|
||||
/// <param name="interval">Thời gian định kỳ để thực hiện tác vụ, tính bằng giây.</param>
|
||||
/// <param name="autoStart">Cho phép tác vụ này tự động bắt đầu khi hệ thống khởi động hay không.</param>
|
||||
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)]
|
||||
public class TaskAttribute(int interval, bool autoStart = true) : Attribute
|
||||
{
|
||||
/// <summary>
|
||||
/// Thời gian định kỳ để thực hiện tác vụ, tính bằng giây.
|
||||
/// </summary>
|
||||
public int Interval { get; } = interval;
|
||||
}
|
||||
```
|
||||
|
||||
## 🔄 Versioning / Phiên bản
|
||||
|
||||
### .NET Version
|
||||
|
||||
- **Current**: .NET 10.0
|
||||
- **Target**: Latest LTS when available
|
||||
|
||||
### Package Versions
|
||||
|
||||
- **Strategy**: Use latest stable versions compatible with .NET 10
|
||||
- **Update Policy**: Regular updates, test before upgrading
|
||||
|
||||
## 📚 Related Documents / Tài liệu Liên quan
|
||||
|
||||
- [Architecture Overview](../architecture/README.md) - System architecture
|
||||
- [AI Collaboration Guide](../ai-guide/README.md) - AI agent guidelines
|
||||
- [ScriptEngine Documentation](../ScriptEngine/README.md) - Scripting system
|
||||
- [Development Guide](README.md) - Development setup (if exists)
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-11-13
|
||||
**Status**: Project Structure & Conventions Document
|
||||
**Version**: 1.0
|
||||
|
||||
310
docs/development/RealtimeIntegration.md
Normal file
310
docs/development/RealtimeIntegration.md
Normal file
@@ -0,0 +1,310 @@
|
||||
# Realtime Integration Guide / Hướng dẫn Tích hợp Realtime
|
||||
|
||||
## 📋 Overview / Tổng quan
|
||||
|
||||
Tài liệu này mô tả cách tích hợp tính năng Linux realtime (preempt_rt) vào ScriptTask và cách enable/disable tính năng này ở compile time.
|
||||
|
||||
## 🔧 Enabling Realtime Support / Bật Hỗ trợ Realtime
|
||||
|
||||
### Step 1: Uncomment DefineConstants / Bỏ comment DefineConstants
|
||||
|
||||
Trong file `RobotNet10.ScriptEngine.csproj`, uncomment dòng sau:
|
||||
|
||||
```xml
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<!-- Define REALTIME symbol to enable Linux realtime features -->
|
||||
<DefineConstants>$(DefineConstants);REALTIME</DefineConstants>
|
||||
</PropertyGroup>
|
||||
```
|
||||
|
||||
### Step 2: Build Project / Build Dự án
|
||||
|
||||
```bash
|
||||
dotnet build srcs/RobotNet10/Commons/RobotNet10.ScriptEngine/RobotNet10.ScriptEngine.csproj
|
||||
```
|
||||
|
||||
Khi `REALTIME` symbol được define:
|
||||
- Code realtime sẽ được compile vào binary
|
||||
- ScriptTask sẽ sử dụng `RealtimeTimer` thay vì `System.Threading.Timer`
|
||||
- Có thể configure realtime scheduling policy và CPU affinity
|
||||
|
||||
Khi `REALTIME` symbol **KHÔNG** được define:
|
||||
- Code realtime sẽ bị loại bỏ hoàn toàn (không compile)
|
||||
- ScriptTask sử dụng `System.Threading.Timer` (standard .NET timer)
|
||||
- Không có dependency vào Linux-specific APIs
|
||||
|
||||
## 📝 Usage Examples / Ví dụ Sử dụng
|
||||
|
||||
### Example 1: Basic Realtime Task / Task Realtime Cơ bản
|
||||
|
||||
```csharp
|
||||
using RobotNet10.ScriptEngine.Models;
|
||||
using Microsoft.CodeAnalysis.Scripting;
|
||||
|
||||
// Create ScriptTaskModel with compiled script runner
|
||||
var taskModel = new ScriptTaskModel(
|
||||
name: "HighPriorityTask",
|
||||
interval: 1, // 1 second
|
||||
autoStart: true,
|
||||
code: scriptCode,
|
||||
runner: compiledScriptRunner
|
||||
);
|
||||
|
||||
// Create ScriptGlobals with dictionaries
|
||||
var globals = new ScriptGlobals(
|
||||
scriptRobotNet: robotNetGlobals,
|
||||
scriptApp: appGlobals,
|
||||
scriptVariables: variables,
|
||||
scriptParameters: parameters
|
||||
);
|
||||
|
||||
// Create task with realtime options
|
||||
var realtimeOptions = new RealtimeTaskOptions
|
||||
{
|
||||
Enabled = true,
|
||||
SetSchedulingPolicy = true,
|
||||
SchedulingPolicy = RealtimeSchedulingPolicy.Fifo,
|
||||
Priority = 50,
|
||||
ClockType = RealtimeClockType.Monotonic
|
||||
};
|
||||
|
||||
var task = new ScriptTask(
|
||||
model: taskModel,
|
||||
globals: globals,
|
||||
realtimeOptions: realtimeOptions
|
||||
);
|
||||
```
|
||||
|
||||
### Example 2: Task with CPU Affinity / Task với CPU Affinity
|
||||
|
||||
```csharp
|
||||
var taskModel = new ScriptTaskModel(
|
||||
name: "PinnedTask",
|
||||
interval: 2,
|
||||
autoStart: true,
|
||||
code: scriptCode,
|
||||
runner: compiledScriptRunner
|
||||
);
|
||||
|
||||
var globals = new ScriptGlobals(
|
||||
scriptRobotNet: robotNetGlobals,
|
||||
scriptApp: appGlobals,
|
||||
scriptVariables: variables,
|
||||
scriptParameters: parameters
|
||||
);
|
||||
|
||||
var realtimeOptions = new RealtimeTaskOptions
|
||||
{
|
||||
Enabled = true,
|
||||
SetSchedulingPolicy = true,
|
||||
SchedulingPolicy = RealtimeSchedulingPolicy.Fifo,
|
||||
Priority = 75,
|
||||
CpuAffinity = new[] { 0, 1 }, // Pin to CPU 0 and 1
|
||||
ClockType = RealtimeClockType.Monotonic
|
||||
};
|
||||
|
||||
var task = new ScriptTask(
|
||||
model: taskModel,
|
||||
globals: globals,
|
||||
realtimeOptions: realtimeOptions
|
||||
);
|
||||
```
|
||||
|
||||
### Example 3: Disable Realtime for Specific Task / Tắt Realtime cho Task Cụ thể
|
||||
|
||||
```csharp
|
||||
var taskModel = new ScriptTaskModel(
|
||||
name: "StandardTask",
|
||||
interval: 5,
|
||||
autoStart: true,
|
||||
code: scriptCode,
|
||||
runner: compiledScriptRunner
|
||||
);
|
||||
|
||||
var globals = new ScriptGlobals(
|
||||
scriptRobotNet: robotNetGlobals,
|
||||
scriptApp: appGlobals,
|
||||
scriptVariables: variables,
|
||||
scriptParameters: parameters
|
||||
);
|
||||
|
||||
var realtimeOptions = new RealtimeTaskOptions
|
||||
{
|
||||
Enabled = false // Use standard timer even if REALTIME is defined
|
||||
};
|
||||
|
||||
var task = new ScriptTask(
|
||||
model: taskModel,
|
||||
globals: globals,
|
||||
realtimeOptions: realtimeOptions
|
||||
);
|
||||
```
|
||||
|
||||
### Example 4: Without Realtime Options / Không có Realtime Options
|
||||
|
||||
```csharp
|
||||
var taskModel = new ScriptTaskModel(
|
||||
name: "StandardTask",
|
||||
interval: 10,
|
||||
autoStart: true,
|
||||
code: scriptCode,
|
||||
runner: compiledScriptRunner
|
||||
);
|
||||
|
||||
var globals = new ScriptGlobals(
|
||||
scriptRobotNet: robotNetGlobals,
|
||||
scriptApp: appGlobals,
|
||||
scriptVariables: variables,
|
||||
scriptParameters: parameters
|
||||
);
|
||||
|
||||
// If realtimeOptions is null or not provided, uses standard timer
|
||||
var task = new ScriptTask(
|
||||
model: taskModel,
|
||||
globals: globals
|
||||
// realtimeOptions: null (default)
|
||||
);
|
||||
```
|
||||
|
||||
## ⚙️ RealtimeTaskOptions Configuration / Cấu hình RealtimeTaskOptions
|
||||
|
||||
| Property | Type | Default | Description |
|
||||
|----------|------|---------|-------------|
|
||||
| `Enabled` | `bool` | `true` | Enable/disable realtime features for this task |
|
||||
| `SetSchedulingPolicy` | `bool` | `true` | Whether to set realtime scheduling policy |
|
||||
| `SchedulingPolicy` | `RealtimeSchedulingPolicy` | `Fifo` | SCHED_FIFO or SCHED_RR |
|
||||
| `Priority` | `int` | `50` | Priority (1-99, higher = higher priority) |
|
||||
| `CpuAffinity` | `int[]?` | `null` | CPU cores to pin thread to (null = no affinity) |
|
||||
| `ClockType` | `RealtimeClockType` | `Monotonic` | Clock type for timer (Monotonic recommended) |
|
||||
| `NonBlocking` | `bool` | `false` | **Note**: Always set to `true` internally for async monitoring |
|
||||
|
||||
## 🎯 How It Works / Cách Hoạt động
|
||||
|
||||
### Without REALTIME Symbol / Không có REALTIME Symbol
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[ScriptTask.Start] --> B[System.Threading.Timer]
|
||||
B --> C[ExecuteTask callback]
|
||||
C --> D[Task execution]
|
||||
```
|
||||
|
||||
### With REALTIME Symbol / Có REALTIME Symbol
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[ScriptTask.Start] --> B{RealtimeOptions<br/>Enabled?}
|
||||
B -->|Yes| C[RealtimeTimer<br/>timerfd]
|
||||
B -->|No| D[System.Threading.Timer]
|
||||
C --> E[Create Dedicated Thread<br/>Highest Priority]
|
||||
E --> F[Set Scheduling Policy<br/>SCHED_FIFO/RR in thread]
|
||||
E --> G[Set CPU Affinity<br/>in thread if configured]
|
||||
E --> H[MonitorRealtimeTimer<br/>loop in thread]
|
||||
H --> I[ReadExpirations<br/>non-blocking]
|
||||
I --> J{Is Paused?}
|
||||
J -->|No| K[ExecuteTask]
|
||||
J -->|Yes| L[Skip Execution]
|
||||
D --> K
|
||||
```
|
||||
|
||||
## 🔑 Key Features / Tính năng Chính
|
||||
|
||||
### 1. High-Resolution Timer / Timer Độ phân giải Cao
|
||||
|
||||
- **RealtimeTimer**: Sử dụng Linux `timerfd` API
|
||||
- **Accuracy**: Nanosecond precision
|
||||
- **Better than**: `System.Threading.Timer` (millisecond precision)
|
||||
|
||||
### 2. Dedicated Thread with Highest Priority / Thread Riêng với Độ Ưu tiên Cao nhất
|
||||
|
||||
- **Dedicated Thread**: Tạo thread riêng cho realtime monitoring với `IsBackground = false`
|
||||
- **Thread Priority**: Set SCHED_FIFO/SCHED_RR với priority cao nhất trong thread
|
||||
- **Isolation**: Realtime monitoring chạy độc lập, không bị ảnh hưởng bởi các thread khác
|
||||
- **CPU Affinity**: Pin thread to specific CPU cores (nếu configured)
|
||||
- **Reduces cache misses**: Improves determinism
|
||||
|
||||
### 3. Real-time Scheduling / Lập lịch Real-time
|
||||
|
||||
- **SCHED_FIFO**: First-In-First-Out, highest priority threads run first
|
||||
- **SCHED_RR**: Round-Robin, time-sliced real-time scheduling
|
||||
- **Priority**: 1-99 (higher = higher priority)
|
||||
- **Applied in thread**: Scheduling policy được set trong dedicated thread, không ảnh hưởng main thread
|
||||
|
||||
### 4. Pause/Resume Behavior / Hành vi Pause/Resume
|
||||
|
||||
- **Timer continues**: Khi paused, timer/realtime loop vẫn tiếp tục chạy
|
||||
- **Skip execution**: Chỉ skip execution khi timer expire (check `_isPaused` flag)
|
||||
- **Instant resume**: Resume ngay lập tức không cần restart timer
|
||||
- **No interruption**: Timer không bị gián đoạn khi pause/resume
|
||||
|
||||
### 5. ScriptTask Constructor / Constructor của ScriptTask
|
||||
|
||||
- **ScriptTaskModel**: Nhận model chứa metadata và `ScriptRunner<object>`
|
||||
- **ScriptGlobals**: Nhận globals dictionary với ScriptRobotNet, ScriptApp, ScriptVariables, ScriptParameters
|
||||
- **Logger**: Tự động lấy từ `globals.ScriptRobotNet.TryGetValue("get_Logger", ...)`
|
||||
- **Execution**: Gọi `model.Runner(globals)` trực tiếp, không merge dictionaries
|
||||
|
||||
### 6. Fallback Mechanism / Cơ chế Dự phòng
|
||||
|
||||
- Nếu realtime initialization fails, falls back to standard timer
|
||||
- Logs error but continues operation
|
||||
- Ensures system reliability
|
||||
|
||||
## ⚠️ Important Notes / Lưu Ý Quan trọng
|
||||
|
||||
### 1. Root Privileges / Quyền Root
|
||||
|
||||
Realtime scheduling requires root privileges or capabilities:
|
||||
|
||||
```bash
|
||||
# Option 1: Run with sudo
|
||||
sudo dotnet run
|
||||
|
||||
# Option 2: Set capabilities (recommended for production)
|
||||
sudo setcap cap_sys_nice+ep /path/to/your/app
|
||||
```
|
||||
|
||||
### 2. Platform Specific / Phụ thuộc Nền tảng
|
||||
|
||||
- **Only works on Linux**: Realtime features require Linux kernel
|
||||
- **preempt_rt recommended**: For best real-time performance
|
||||
- **Windows/macOS**: Code compiles but realtime features are disabled
|
||||
|
||||
### 3. Performance Considerations / Xem xét Hiệu năng
|
||||
|
||||
- **RealtimeTimer**: More accurate but requires Linux
|
||||
- **Standard Timer**: Cross-platform but less accurate
|
||||
- **Choose based on**: Target platform and accuracy requirements
|
||||
|
||||
### 4. Error Handling / Xử lý Lỗi
|
||||
|
||||
- Realtime initialization errors are caught and logged
|
||||
- System automatically falls back to standard timer
|
||||
- Task continues to function normally
|
||||
|
||||
## 📊 Comparison / So sánh
|
||||
|
||||
| Feature | Standard Timer | Realtime Timer |
|
||||
|---------|---------------|----------------|
|
||||
| **Platform** | Cross-platform | Linux only |
|
||||
| **Accuracy** | ~1ms | Nanosecond |
|
||||
| **Scheduling** | OS default | SCHED_FIFO/RR |
|
||||
| **CPU Affinity** | No | Yes |
|
||||
| **Root Required** | No | Yes (for scheduling) |
|
||||
| **Compile-time** | Always available | Requires REALTIME symbol |
|
||||
|
||||
## 🔗 Related Documents / Tài liệu Liên quan
|
||||
|
||||
- [RobotNet10.Realtime README](../../srcs/RobotNet10/Commons/RobotNet10.Realtime/README.md) - Realtime library documentation
|
||||
- [Appccelerate.StateMachine Guide](AppccelerateStateMachine.md) - State machine usage
|
||||
- [ScriptTask Implementation](../../srcs/RobotNet10/Commons/RobotNet10.ScriptEngine/Models/ScriptTask.cs) - Reference implementation
|
||||
|
||||
---
|
||||
|
||||
**Last Updated**: 2025-11-13
|
||||
**Status**: Integration Guide
|
||||
**Version**: 1.0
|
||||
|
||||
Reference in New Issue
Block a user