跳至主要内容

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 RTUModbus ASCIIModbus TCP
傳輸媒介RS-485/232RS-485/232Ethernet
封裝Binary + CRC16ASCII Hex + LRCMBAP Header
效率最高較低(2x 資料量)
錯誤檢測CRC-16LRCTCP 本身
適用場景單一設備直連舊型設備相容多設備/遠端
GST 實作ModbusRtuClientModbusTcpClient

選擇依據

  • 設備有 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-94TAIE FC DecimalPoint 暫存器地址修正Done
GST-114AL1/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