msecs:基于 .NET 生态的 SECS/GEM 协议库项目

可用于 .NET 工业设备或采集系统快速实现 SECS/GEM 通信,完成机台对接、数据采集与指令下发。提供 SECS-II 编解码、SML 渲染、HSMS 主动/被动连接、心跳重连及常用消息构造,兼容旧版与新版 .NET,基于内存管道提升性能。【此简介由AI生成】

分支1Tags2

Msecs

.NET 的轻量 SECS/GEM 协议库: SECS-II 编解码、SML、HSMS-SS 双端,以及常用 GEM / GEM300 消息构造器。 只做协议,不做行为。

NuGet NuGet Downloads License .NET GitCode

msecs 面向半导体设备、EAP/MES 采集器和虚拟设备。它把 SECS-II/HSMS-SS 的“链路上的事”封装好: 你在应用层只写 S1F13 该回什么、S6F11 该推什么,不用自己拼 10 字节头、维护 System Bytes 或处理 Select 握手。

  • SECS-II: Item 树编解码,线上格式与 secsgem 对齐,核心报文用 secsgem 黄金字节锁定
  • HSMS-SS 双端: 机台侧 HsmsPassivePeer,Host 侧 HsmsActivePeer,Select/Linktest/Separate 自动处理
  • 完整生命周期: SendAsync 请求-应答(T3)、T5 自动重连、T6 心跳断链、T7 NOT SELECTED 超时、T8 半帧超时
  • GEM / GEM300 构造器: 常用 S1/S2/S5/S6/S7/S9/S10,以及 E87/E39/E40/E94 的 S3/S14/S16
  • 低分配: 解码零拷贝,编码走 IBufferWriter<byte>,写路径直接对接 PipeWriter

安装

dotnet add package Msecs
目标框架 适用
netstandard2.0 .NET Framework 4.6.2+、.NET Core 2.x+
net8.0 .NET 8+

依赖只有 Microsoft 官方包: net8.0 为 System.IO.Pipelines;netstandard2.0 另补 System.Memory、System.Threading.Channels、System.Threading.Tasks.Extensions。

快速开始

设备侧(机台,Passive)

链路上的 Select/Linktest/Separate 全部自动,你只写业务分发:

using Msecs.Hsms;
using Msecs.Messages;
using Msecs.Secs2;

using var peer = new HsmsPassivePeer("0.0.0.0", 5000);

// 报文流日志: 一行 SML 看清收发内容
peer.MessageReceived += m =>
    Console.WriteLine($"<- {m.Header.SmlTag()} {Sml.RenderCompact(m.BodyItem)}");

peer.DataMessageReceived += (h, body) =>
{
    switch ((h.Stream, h.Function))
    {
        case (1, 13):   // Establish Communications
            peer.Send(SecsMessages.S1F14(h, "MY-TOOL", "1.0.0"));
            break;

        case (2, 41):   // Remote Command
            peer.Send(SecsMessages.S2F42(h, 0));   // HCACK = 0
            break;

        case (1, 3):    // Selected Equipment Status
            var req = (ListItem)Item.Decode(body);
            var values = req.Items
                .Select(_ => (Item)Item.F4(233.5f))
                .ToList();
            peer.Send(SecsMessages.S1F4(h, values));
            break;
    }
};

peer.StateChanged += s =>
{
    if (s == HsmsState.Selected)
    {
        // 主动推事件; System Bytes 由连接自动分配
        peer.Send(SecsMessages.S6F11(1, 2501,
            new[] { (3501, new Item[] { Item.F4(233.5f) }) }));
    }
};

peer.Start();
Console.WriteLine("waiting for host...");
Console.ReadLine();

采集侧(Host,Active)

using Msecs.Hsms;
using Msecs.Messages;
using Msecs.Secs2;

using var host = new HsmsActivePeer("192.168.1.10", 5000)
{
    LinktestInterval = TimeSpan.FromSeconds(30),   // 心跳,Zero = 不发
    AutoReconnect = true,                          // 意外断线按 T5 重连
    T3ReplyTimeout = TimeSpan.FromSeconds(45),     // SendAsync 等应答上限
};

host.StateChanged += s => Console.WriteLine($"[state] {s}");
host.DataMessageReceived += (h, body) =>           // 机台主动推的 S6F11/S5F1
    Console.WriteLine($"<- {h.SmlTag()} {Sml.RenderCompact(body.Length > 0 ? Item.Decode(body) : null)}");

await host.ConnectAsync();                         // 返回即 SELECTED

var s1f14 = await host.SendAsync(SecsMessages.S1F13());   // 按 System 自动配对
Console.WriteLine(Sml.RenderCompact(s1f14.BodyItem));

host.Send(SecsMessages.S2F33(                    // 报告定义
    new[] { (3501u, new uint[] { 1501, 1502 }) }));
host.Send(SecsMessages.S2F35(                    // 报告链到 CEID
    new[] { (2501u, new uint[] { 3501 }) }));
host.Send(SecsMessages.S2F41("START"));          // 远程命令

await host.Stop();                               // 发 Separate.req 干净断链

SendAsync 的异常三态可区分:

  • TimeoutException: T3 超时
  • IOException: 连接断开,含断线杀掉在途请求
  • OperationCanceledException: 调用方取消

Select 被拒抛 HsmsException(带 StatusCode)。断线自动重连成功后,断线时失败的请求由应用层决定是否重发。

只用 SECS-II 编解码

不需要连接,也不需要 HSMS:

var body = Item.L(
    Item.Binary(new byte[] { 0 }),
    Item.L(Item.A("PECVD-X1"), Item.A("1.0.0"))).ToBytes();

// 01 02 21 01 00 01 02 41 08 ...  — 与 secsgem 编码一致
var root = (ListItem)Item.Decode(body);
var mdln = ((ListItem)root.Items[1]).Items[0];
Console.WriteLine(((ValueItem)mdln).GetAscii());   // PECVD-X1

核心能力

能力 说明
SECS-II E5 数据类型编解码、Item 树、SML 紧凑渲染
HSMS-SS Active / Passive 双端,共用同一套连接状态机
请求-应答 SendAsync 按 System Bytes 自动配对,支持 T3 与取消
连接生命周期 Select / Linktest / Deselect / Separate / Reject 自动处理
超时 T3 应答、T5 重连、T6 控制事务、T7 NOT SELECTED、T8 字节间隔
DeviceId 可配 SessionID;ValidateDeviceId 不匹配自动回 S9F1
多连接保护 ProtectSelectedConnection 防误连/端口扫描踢掉在用 Host
消息构造器 核心 GEM + GEM300,回复类消息自动回显 System/SessionID
诊断 MessageReceived / MessageSent + SML,直接看报文流

性能

本机简易基准(bench/Msecs.Bench,net8.0 Release,200k 次,量级参考):

场景 吞吐 分配
S6F11(3 报告)构造+编码 ~76 万 msg/s ~1.5 KB/msg
S6F11 解码 ~116 万 msg/s ~1.3 KB/msg
Active↔Passive 本地回环(S1F13↔S1F14) ~68 万请求/s —

与 secs4net 3.1.0 的 codec 对照(同机 .NET 8、同一条 80 B S6F11、编码字节完全一致,预热后取最好值):

场景 msecs secs4net
构造+编码(writer 路径) ~0.33 µs / 1384 B ~0.34–0.36 µs / 1304 B
只编码(预构造) ~0.11 µs / 0 B ~0.13–0.16 µs / 0 B
小报文解码 ~0.34 µs / 1288 B ~1.1 µs / 1304 B
64 KB Binary 解码 ~0.04 µs / 136 B ~1.4 µs / 152 B

只比较 codec,不含 TCP 回环、重连和 GEM 行为。解码的零拷贝优势有代价:解码结果持有输入缓冲区,不要把池化数组提前归还。

消息覆盖

常用构造器,不是 E5/E30 全量:

Stream 覆盖
S1 F1/F2、F3/F4、F11/F12、F13/F14、F15–F18、F21–F24
S2 F13/F14、F15/F16、F17/F18、F21/F22、F25/F26、F29/F30、F31–F38、F39–F48
S3 F15–F20、F23–F32(E87 载具管理)
S5 F1/F2、F3–F8(告警)
S6 F5/F6、F11/F12、F15/F16、F19/F20、F23/F24
S7 F1–F6、F17–F20、F25/F26(配方)
S9 F1/F3/F5/F7/F9/F11/F13
S10 F1–F6(终端)
S14 F1–F12、F19–F24(E39 对象服务)
S16 F1/F2、F5–F12、F15–F30(E40 工艺任务 + E94 控制任务)

S2F30 是标准 Equipment Constant Namelist: L[n] <L[6] <ECID> <ECNAME> <ECMIN> <ECMAX> <ECDEF> <UNITS>>,与 secsgem SecsS02F30 一致。

架构与边界

┌────────────────────────────────────────────┐
│ Messages  SecsMessages / GEM / GEM300      │
├────────────────────────────────────────────┤
│ HSMS-SS   HsmsActivePeer / HsmsPassivePeer │
│           HsmsConnection(共享状态机/事务)   │
├────────────────────────────────────────────┤
│ SECS-II   Item / Sml / 编解码               │
└────────────────────────────────────────────┘

msecs 只做协议,不做行为。 以下都归应用层:

  • SVID/CEID/ALID 管理、事件调度、GEM 通讯状态机
  • GEM300 对象模型与状态迁移(E39 对象生命周期、E87 载具状态、E90 基片追踪)
  • 配方管理、报警业务规则、场景编排

库提供 S3/S14/S16 的报文构造器,但不提供 DTO、对象模型或状态机。

行为与限制

  • System Bytes 自动管理: 应用层不用也不该自己维护计数器;需要请求-应答配对时用 SendAsync。
  • DeviceId: 默认 0xFFFF(HSMS-SS)。ValidateDeviceId = true 时,非 0xFFFF 且不匹配的数据消息回 S9F1 并丢弃;0xFFFF 一律放行,S9 流报文不回 S9F1(防互刷死循环)。
  • 协议错: 非 SELECTED 收数据回 Reject.req reason 4,未知 SType reason 1,PType≠0 reason 2,无主控制应答 reason 3。
  • Select.rsp / Deselect.rsp: 状态码在头部字节 3,SessionID 回显请求;ProtectSelectedConnection 拒绝另一条连接时回 Select Status 1(Already Active)。
  • T7/T8: T7 覆盖被动端建链后、Active Deselect 后的 NOT SELECTED 阶段;T8 约束半帧内相邻字节间隔。T8 不是 TCP 能保证的东西。
  • Send 返回 bool: 未连接/已断开/尚未 SELECTED/出站队列满 = false;出站队列有界(65536)。
  • 文本编码 Latin1: Item.A 对 char > 0xFF 抛 ArgumentException,不会静默替换成 ?;中文等文本请自行编码后用 Item.Binary。
  • Item.L(existingArray) 直接持有传入数组: 构造后不要修改它;L(a, b, c) 是编译器新建数组,不受影响。
  • 零拷贝解码: 解码结果引用输入缓冲区;如果输入来自池化数组,解码结果生命周期内不要归还。

升级到 0.9.0

  • S2F30 改为标准 Equipment Constant Namelist,签名发生变化;旧的 L[n]{ECID, ECACK} 结构是错的。
  • 补齐 S1F3、S2F29、S5F2。
  • Send(SecsMessage) 在未 SELECTED 时返回 false;SendAsync 在未 SELECTED 时抛 InvalidOperationException,断线后仍抛 IOException。
  • Item.A 拒绝非 Latin1;U1/U2/I1 等工厂对越界值抛 ArgumentOutOfRangeException。
  • Item.L(existingArray) 不再复制数组,构造后不要修改。
  • ProtectSelectedConnection 的“已有连接占用”状态码从 3 改为 1(Already Active)。

完整变更见 CHANGELOG。

测试

dotnet test
  • 255 个测试: 黄金字节、编解码边界、SML、Active↔Passive 真 TCP 回环、FakeEquipment、T3/T5/T6/T7/T8、Reject/S9、GEM300 结构
  • 测试运行在 net8.0;netstandard2.0 作为打包目标参与编译
  • 基准项目: bench/Msecs.Bench
  • 最小示例: demo/Msecs.Demo

路线图

  • 协议层: 继续补边界用例、性能热点路径和 CI
  • GEM300 对象模型与状态机: 按设计留给应用层,库只做报文
  • 1.0 之前仍可能有 API 调整;破坏性变更会写进 CHANGELOG

License

MIT


关键词: SECS、SECS-II、SECS2、HSMS、HSMS-SS、GEM、GEM300、SEMI E5/E30/E37/E87/E39/E40/E94、半导体设备、EAP、CIM、MES、设备自动化、.NET、C#

项目介绍

可用于 .NET 工业设备或采集系统快速实现 SECS/GEM 通信,完成机台对接、数据采集与指令下发。提供 SECS-II 编解码、SML 渲染、HSMS 主动/被动连接、心跳重连及常用消息构造,兼容旧版与新版 .NET,基于内存管道提升性能。【此简介由AI生成】

定制我的领域