GST Modbus 通訊整合指南
本文件說明 GST Framework 的 Modbus 協定架構、Controller 介面與整合模式。 適用於 ProcessVision、Jope-PLC-Monitor、GST.Simulator 等 Modbus 通訊專案。
1. 架構總覽
┌────────────────────────────────────────────────────────┐
│ 應用層(Application) │
│ PlcPollingService / DataCollection / UI │
├────────────────────────────────────────────────────────┤
│ Controller 層(Device Abstraction) │
│ ITemperatureController / IPressureController │
│ TaieFcController(TAIE FC Series 實作) │
├────────────────────────────────────────────────────────┤
│ Command Queue 層(Priority Scheduling) │
│ IModbusCommandQueue — 優先級排程、批次原子操作 │
├────────────────────────────────────────────────────────┤
│ Transport 層(Protocol) │
│ IModbusClient → ModbusTcpClient / ModbusRtuClient │
│ ModbusHelper(CRC、PDU 建構) │
└────────────────────────────────────────────────────────┘
2. 協定差異與選擇
| Modbus RTU | Modbus ASCII | Modbus TCP | |
|---|---|---|---|
| 傳輸媒介 | RS-485/232 | RS-485/232 | Ethernet |
| 封裝 | Binary + CRC16 | ASCII Hex + LRC | MBAP Header |
| 效率 | 最高 | 較低(2x 資料量) | 高 |
| 錯誤檢測 | CRC-16 | LRC | TCP 本身 |
| 適用場景 | 單一設備直連 | 舊型設備相容 | 多設備/遠端 |
| GST 實作 | ModbusRtuClient | — | ModbusTcpClient |
選擇依據:
- 設備有 Ethernet → Modbus TCP
- 設備只有 Serial Port → Modbus RTU
- PLC 多設備監控 → Modbus TCP(每台 PLC 獨立 IP)
3. 核心介面
3.1 IModbusClient — Transport 層
public interface IModbusClient
{
string ConnectionId { get; } // "COM3" 或 "192.168.1.100:502"
bool IsConnected { get; }
// 連線管理
Task ConnectAsync(CancellationToken ct);
Task DisconnectAsync(CancellationToken ct);
// 讀取操作
Task<ushort[]> ReadHoldingRegistersAsync(byte unitId, ushort startAddress, ushort quantity, CancellationToken ct);
Task<ushort[]> ReadInputRegistersAsync(byte unitId, ushort startAddress, ushort quantity, CancellationToken ct);
Task<bool[]> ReadCoilsAsync(byte unitId, ushort startAddress, ushort quantity, CancellationToken ct);
Task<bool[]> ReadDiscreteInputsAsync(byte unitId, ushort startAddress, ushort quantity, CancellationToken ct);
// 寫入操作
Task WriteSingleRegisterAsync(byte unitId, ushort address, ushort value, CancellationToken ct);
Task WriteSingleCoilAsync(byte unitId, ushort address, bool value, CancellationToken ct);
Task WriteMultipleRegistersAsync(byte unitId, ushort startAddress, ushort[] values, CancellationToken ct);
Task WriteMultipleCoilsAsync(byte unitId, ushort startAddress, bool[] values, CancellationToken ct);
}
3.2 IModbusController — Controller 層
public interface IModbusController
{
string Name { get; }
byte UnitId { get; }
bool IsConnected { get; }
string BusId { get; } // 同一 ConnectionId 表示共用匯流排
Task<IReadOnlyDictionary<string, object>> ReadAllAsync(CancellationToken ct);
Task ConnectAsync(CancellationToken ct);
Task DisconnectAsync(CancellationToken ct);
}
3.3 ITemperatureController / IPressureController
// 溫度控制器專用介面
Task<double> GetProcessValueAsync(CancellationToken ct); // 當前 PV
Task<double> GetSetValueAsync(CancellationToken ct); // 目標 SV
Task SetSetValueAsync(double value, CancellationToken ct); // 寫入 SV
Task<double> GetOutputPercentageAsync(CancellationToken ct); // 輸出 0-100%
Task<ControllerStatus> GetStatusAsync(CancellationToken ct); // 狀態位元
// 程式段控制(1-8 段)
Task SetProgramSegmentsAsync(ProgramSegment[] segments, CancellationToken ct);
Task StartProgramAsync(CancellationToken ct);
Task StopProgramAsync(CancellationToken ct);
Task<int> GetCurrentSegmentAsync(CancellationToken ct);
Task<int> GetRemainingTimeAsync(CancellationToken ct);
// PID 參數
Task<PidParameters> GetPidParametersAsync(CancellationToken ct);
Task SetPidParametersAsync(PidParameters pid, CancellationToken ct);
3.4 資料型別
record ProgramSegment(
double SetValue, // 目標值
int TimeSeconds, // 持續時間(秒)
double OutputLimit = 100.0 // 輸出限制 %
);
record PidParameters
{
double ProportionalBand; // P(線路上 x10)
int IntegralTime; // I(秒)
int DerivativeTime; // D(秒)
}
record ControllerStatus
{
bool SensorAbnormal;
bool AutoTuning;
bool Alarm1Active;
bool Alarm2Active;
bool IsRunning;
bool OutputOn;
ushort RawBits;
}
4. CommandQueue 優先級排程
4.1 問題場景
單一 Modbus 匯流排(Serial 或 TCP)同一時間只能有一個請求/回應。當多個操作同時需要存取同一設備時,需要排程機制。
4.2 優先級定義
enum ModbusCommandPriority
{
Low = 0, // Polling 讀取(可取消)
Normal = 50, // 一般操作(可取消)
High = 100, // 重要寫入(不可取消)
Critical = 200 // 批次操作(阻塞所有其他操作)
}
4.3 使用介面
public interface IModbusCommandQueue
{
Task<T> EnqueueReadAsync<T>(Func<CancellationToken, Task<T>> readFunc,
ModbusCommandPriority priority, CancellationToken ct);
Task EnqueueWriteAsync(Func<CancellationToken, Task> writeFunc,
ModbusCommandPriority priority, CancellationToken ct);
Task ExecuteBatchAsync(Func<CancellationToken, Task> batchFunc,
ModbusCommandPriority priority, CancellationToken ct);
Task<int> CancelPendingAsync(ModbusCommandPriority belowPriority);
}
4.4 運作機制
背景佇列處理器(單一 Task)
↓ 取出最高優先級命令
SemaphoreSlim(執行鎖)
↓ 確保同時只有一個操作存取匯流排
執行 Modbus 操作
↓
回傳結果給呼叫者
特殊行為:
- High/Critical 命令進入佇列時,自動取消 Low/Normal 命令
- Batch 操作(Critical)取得執行鎖後,阻塞所有其他操作
- 防止 Polling 中斷使用者操作
5. TAIE FC Controller
5.1 Register Map
地址 用途 讀/寫 備註
0x0000 SetValue (SV) R/W 受 DecimalPoint 影響
0x008A ProcessValue (PV) R/O 受 DecimalPoint 影響
0x0087 OutputPercentage R/O 0-1000 = 0-100.0%
0x0088 OutputBits R/O 狀態位元
0x004B DecimalPoint R/O 0=xxxx, 1=xxx.x, 2=xx.xx, 3=x.xxx
0x0003 ProportionalBand R/W 實際值 x10
0x0004 IntegralTime R/W 秒
0x0005 DerivativeTime R/W 秒
0x000D Alarm1 SetValue R/W
0x000E Alarm2 SetValue R/W
0x0006 ProgramPattern R/W 0-2 選擇程式組
0x0007 CurrentSegment R/O 1-8
0x0008 RemainingTime R/O 秒
程式段(8 段 x 3 暫存器):
0x0009 Segment 1 SV R/W
0x000A Segment 1 Time R/W
0x000B Segment 1 Output R/W
0x000C Segment 2 SV R/W
...(每段 +3)
5.2 DecimalPoint 值轉換
// Register 值 ↔ 實際值(依 DecimalPoint 設定)
DecimalPoint 0: register = value // 25 → 25
DecimalPoint 1: register = value * 10 // 25.3 → 253
DecimalPoint 2: register = value * 100 // 25.30 → 2530
DecimalPoint 3: register = value * 1000 // 2.530 → 2530
連線時自動讀取 DecimalPoint 暫存器(0x004B)以正確轉換。
5.3 已知問題
| Issue | 說明 | 狀態 |
|---|---|---|
| GST-94 | TAIE FC DecimalPoint 暫存器地址修正 | Done |
| GST-114 | AL1/AL2 + PID 地址互換修正 | Done |
教訓:Register Map 地址必須與實際硬體手冊逐一核對,不可依賴猜測或文件。
6. DI 註冊
6.1 基本註冊
services.AddModbus()
.AddModbusControllers();
6.2 TCP Client
services.AddModbusTcpClient("controller1", opts =>
{
opts.Host = "192.168.1.100";
opts.Port = 502;
opts.ConnectionTimeoutMs = 5000;
opts.ReadWriteTimeoutMs = 3000;
opts.RetryCount = 3;
opts.RetryDelayMs = 100;
opts.KeepAlive = true;
});
6.3 RTU Client
services.AddModbusRtuClient("device", opts =>
{
opts.PortName = "COM3";
opts.BaudRate = 9600;
});
6.4 TAIE FC Controller
// 溫度控制器
services.AddTaieFcTemperatureController(
name: "Chamber1",
modbusClientName: "controller1",
opts =>
{
opts.UnitId = 1;
opts.DecimalPoint = 1; // xxx.x
opts.TimeoutMs = 3000;
});
// 壓力控制器
services.AddTaieFcPressureController(
name: "Pressure1",
modbusClientName: "controller1",
opts =>
{
opts.UnitId = 2;
opts.DecimalPoint = 2; // xx.xx
});
7. 整合模式
7.1 模式 A:直接 IModbusClient
最簡單的方式,適合單次讀寫操作。
var client = factory.CreateTcpClient(options);
await client.ConnectAsync(ct);
var registers = await client.ReadHoldingRegistersAsync(
unitId: 1, startAddress: 0x0000, quantity: 10, ct);
await client.WriteSingleRegisterAsync(1, 0x0000, 5000, ct);
await client.DisconnectAsync(ct);
7.2 模式 B:TAIE FC Controller
設備級抽象,適合溫控/壓控設備。
await controller.ConnectAsync(ct);
var pv = await controller.GetProcessValueAsync(ct); // 25.3°C
await controller.SetSetValueAsync(30.5, ct);
// 載入程式段
var segments = new ProgramSegment[]
{
new(SetValue: 30.0, TimeSeconds: 60),
new(SetValue: 40.0, TimeSeconds: 120),
new(SetValue: 30.0, TimeSeconds: 60)
};
await controller.SetProgramSegmentsAsync(segments, ct);
await controller.StartProgramAsync(ct);
7.3 模式 C:Polling Service(PLC Monitor)
背景服務持續輪詢,適合即時監控。
// DI 註冊
services.AddSingleton<IPollingDataCache, PlcPollingService>();
services.AddHostedService<PlcPollingService>();
// 內部流程:
// 1. 從 DB 載入 Device + Tag 配置
// 2. 建立各 Device 的 IModbusClient 連線
// 3. PeriodicTimer 定期輪詢(預設 1 秒)
// 4. 平行讀取所有 Device
// 5. Raw 值 → 工程單位(Scaling)
// 6. 批次寫入 TimescaleDB
// 7. 更新 In-Memory Cache
// 8. 觸發 DataUpdated 事件 → UI 更新
7.4 模式 D:CommandQueue 排程
共用匯流排多 Controller,適合 ProcessVision。
var queue = new ModbusCommandQueue(logger);
controller1.CommandQueue = queue;
controller2.CommandQueue = queue;
// Polling(Low)→ 可被使用者操作取消
var pv = await controller1.GetProcessValueAsync(ct);
// 使用者寫入(High)→ 取消排隊中的 Low 命令
await controller2.SetSetValueAsync(50.0, ct);
// 原子批次(Critical)→ 阻塞所有操作
await queue.ExecuteBatchAsync(async ct =>
{
await controller1.SetSetValueAsync(25.0, ct);
await controller2.SetSetValueAsync(30.0, ct);
}, ModbusCommandPriority.Critical, ct);
8. Simulator 對接
8.1 GST.Simulator 支援
Simulator 提供 Modbus 設備模擬,支援:
- Holding / Input Registers、Coils / Discrete Inputs
- TCP Server 模式(模擬遠端設備)
- 故障注入(無回應、CRC 錯誤、延遲回應、Modbus Exception)
8.2 測試流程
1. 啟動 GST.Simulator(TCP Server 模式)
2. 應用程式連線到 Simulator 的 IP:Port
3. Simulator 回應預設 Register 值
4. 可注入故障驗證錯誤處理
9. 常見問題
9.1 Register 地址錯誤
症狀:讀到的值不正確或無意義。 解法:逐一核對硬體手冊的 Register Map,注意地址偏移(有些設備 0-based,有些 1-based)。
9.2 DecimalPoint 值異常
症狀:溫度顯示 253 而非 25.3。 解法:確認 Controller 的 DecimalPoint 設定是否正確,或讓 Controller 自動偵測(連線時讀取 0x004B)。
9.3 多設備共用匯流排衝突
症狀:偶爾讀寫失敗、回應混淆。 解法:使用 CommandQueue 序列化所有操作,確保同一時間只有一個請求。
9.4 TCP 斷線重連
症狀:長時間運行後通訊中斷。
解法:啟用 KeepAlive + RetryCount,TCP Client 內建自動重連機制。
文件版本:v1.0 建立日期:2026-03-16 最後更新:2026-03-16 對應 Linear Issue:GST-165