后续内容为基于github上secs4net项目源码的ChatGpt介绍
以该项目为主,从零开始介绍讲解secs/gem,更多的以面试口吻讲述形式。
主要为个人学习,提升使用
🎓 第一讲:SECS/GEM 协议是个什么东西?
📌 第 1 段:SECS/GEM 是谁?它在哪些场合出现?
🎙️ 口述稿(你面试时可以这样说):
SECS/GEM 协议是半导体行业的通信标准,它解决的是“设备”和“主机系统”之间如何说话、怎么互相理解命令和数据的问题。
SECS 是 Semi Equipment Communication Standard 的缩写,而 GEM 是 Generic Equipment Model。
这两个协议加起来,定义了一个统一的规则:包括 数据格式、命令格式、通讯流程、事件结构等等,
这样不管厂里用的是哪家设备厂的设备,只要支持 SECS/GEM,就能接入 EAP 或 MES 系统,做到标准化、自动化的联机控制。
📚 技术知识点解释(你真的要懂的):
📌 1. SECS 是什么?
SECS 是 SEMI 协会(国际半导体设备材料协会) 制定的一套通信标准。
它专门为半导体行业设计,目标是让设备和工厂系统之间能像人和人之间说英语一样,有一个通用语言。
📌 2. SECS/GEM 的通信模型(设备 vs 主机):
主体 | 角色 | 举例 |
---|---|---|
主机 Host | 控制者 | EAP、MES、Fab系统 |
设备 Equipment | 被控制方 | Tester、Handler、Probe、Laser Mark |
它是一个典型的“主从结构”:主机发命令、设备执行,设备汇报状态,主机处理反馈。
📌 3. SECS 有两个主要的物理实现方式:
名称 | 协议 | 接口方式 | 描述 |
---|---|---|---|
SECS-I | RS-232 串口 | 串口连接 | 老设备常用,慢 |
HSMS | TCP/IP Socket | 网络连接 | 新设备常用,快且稳定 |
现在主流基本都采用 HSMS 协议,因为设备数量多、实时性强,串口已经远远不够用了。
🧩 4. GEM 是干嘛的?
GEM 是在 SECS 之上定义的一套“应用层行为规范”。
它规定了哪些功能是设备必须要支持的,比如:
- 联机/脱机(Online/Offline)
- 报警汇报(Alarm Report)
- 状态报告(Status Report)
- 事件定义和触发(Event Report)
- 执行命令(Remote Command)
- 设备识别(S1F1/S1F2)
- Trace(参数跟踪)
你可以理解为:SECS 是语言,GEM 是语法 + 句型结构 + 使用规范
👉 没有 GEM,大家虽然讲 SECS,但各讲各的;有了 GEM,才实现了“说得标准、听得懂”。
🏭 为什么半导体厂商都要求支持 SECS/GEM?
因为生产环节需要精确控制、追溯和自动化:
- 设备运行状态要实时汇报(比如 Probe 机什么时候开始测试?有没有测试失败?)
- 每一个晶圆、每一个 Lot 的操作都必须被记录(Track & Trace)
- 工程师要远程下达命令(Remote Start/Stop/Recipe Change)
如果设备不支持 SECS/GEM,就只能人工操作和手动记录,效率低,错误率高,良率不可控。
🔚 小结:第一段你学到了什么?
- SECS 是“设备-主机”之间的通用语言。
- 它分为 SECS-I(串口)和 HSMS(TCP/IP)。
- GEM 是在 SECS 上定义的行为规范,规定了设备必须支持的功能。
- 半导体厂选择 SECS/GEM,是为了让设备能标准化接入自动化系统,实现远程控制、状态追踪、良率管理。
如果你准备好了,我来发下一段:
第二段:SECS 报文长什么样?Stream、Function 是怎么编号的?SECS 的报文格式结构到底是啥?
等你说“继续”,我就开讲 📖
太好,马上来!这是第一讲的【第 2 段】:
🎓 第一讲 · 第2段:SECS 报文长什么样?Stream / Function 是啥?
(这是报文结构入门,面试很爱问,也很容易考住人)
🎙️ 口述稿(你可以这样说):
SECS 报文的核心概念是 Stream 和 Function,它们类似于一个命令编号体系,每个 Stream 表示一个分类,而 Function 表示这个分类下的具体命令。
举个例子,S1F1 是设备识别请求,S1F2 是对应的回应。S6F11 是事件通知,S6F12 是确认回应。这个配对的关系就是一问一答。报文的结构分为 Header 和 Body,Header 是固定长度的10个字节,用于标识通信信息;Body 是变长,用来承载你要发送的数据,比如设备ID、Alarm代码、Wafer ID等。
报文可以是 Request,也可以是 Response,还有一种是设备主动发的,比如事件通知或报警,这类叫 Unsolicited Message。
📚 技术讲解(从0讲到你能画图)
📌 1. 报文结构分成两部分:
+------------------+------------------+
| Header | Body |
| 固定10字节 | 可变长度 |
+------------------+------------------+
📦 Header(头部,固定 10 字节)结构说明:
字节数 | 名称 | 说明 |
---|---|---|
4 字节 | Message Length | Body 的长度(不含 Header) |
4 字节 | System Bytes | 主机和设备之间标识配对的请求-回应 |
1 字节 | Stream | 流号:表示消息类别 |
1 字节 | Function | 功能号:表示具体的操作 |
(部分实现会把 Stream 和 Function 组成一个完整的 Command,如 S6F11)
🔹 Stream(流)常见编号含义:
编号 | 含义 |
---|---|
S1 | Equipment Status Request(状态) |
S2 | Host Command(控制命令) |
S5 | Alarm Related(报警) |
S6 | Data Collection(事件/报告) |
S7 | Recipe Related(程式) |
S9 | Error Message(异常) |
S10 | Terminal Display(日志) |
S14 | File Transfer(文件传输) |
🔹 Function(功能)常见示例:
编号 | 含义 | 备注 |
---|---|---|
F1 | Request | 发起方请求 |
F2 | Response | 对 F1 的回应 |
F3 | Set | 主机设置设备数据 |
F5 | Get | 主机获取设备数据 |
F6 | Get Response | 对 F5 的回应 |
F11 | Event Report | 事件通知(设备主动发) |
F12 | Acknowledge | 对事件的回应 |
🧩 System Bytes 是干嘛的?
它是一种“配对码”,防止通信过程中混淆。
比如主机发了一个请求,设备回应的时候,必须带回原来的 System Bytes,主机才能知道“你这是回应我刚才的哪一条消息”。
System Bytes 是 4 字节,通常由主机生成,设备照单返回。
你可以理解为“信件编号 + 回信编号”。
📦 Body 是怎么组织的?
SECS 报文的 Body 实际上是一个树形结构,叫做 List-based encoding。
它的格式可以用一个东西来描述:SML(SEMI Message Language),后面我们会深入讲这个。
一个简单的 Body 示例如下(S6F11):
<S6F11>
<L [2]>
<U2> 1001 </U2> // CEID: 事件号
<L [1]>
<L [2]>
<U2> 2001 </U2> // Report ID
<L [2]>
<A> Lot123 </A> // 参数1:批次号
<A> PASS </A> // 参数2:测试结果
</L>
</L>
</L>
</L>
</S6F11>
这段报文的含义是:
- 我是一个“事件通知”报文(S6F11)
- 我告诉你事件号是 1001(CEID)
- 我附带一个报告(Report ID = 2001),这个报告里有两个参数(Lot编号 + 测试结果)
这个结构本质上是树状嵌套的 List 和基本数据类型(U2、A、I4、B…)
🔚 小结:第二段你学到了什么?
- SECS 报文 = Header + Body,Header 固定 10 字节。
- Header 中 Stream/Function 代表消息类型,配对组成命令(如 S6F11)。
- Body 是嵌套结构,用 SML 语法描述,类似 JSON。
- System Bytes 是“配对编号”,确保回应消息准确对应请求。
- 报文可以是 Request(主机发)、Response(设备回)、Unsolicited(设备主动上报)。
准备好就说“继续”,我将送上第三段:
SML 是什么?为什么是树?为什么不用 JSON?怎么在代码中写出 SML 报文?secs4net 是如何解析这些结构的?
好的!现在进入第一讲【第 3 段】:
🎓 第一讲 · 第3段:SML 是什么?为什么 SECS/GEM 报文是树状结构?
这一节我们要搞懂:
- **SML(SEMI Message Language)**是啥?
- 为什么 SECS/GEM 报文不是 JSON,而是树结构?
- SML 的基本语法 & 常见数据类型
🎙️ 口述稿(你可以这样说):
在 SECS/GEM 里,我们需要描述设备状态、数据、事件等信息,但 JSON 或 XML 这些格式都不是标准的,因此 SECS/GEM 定义了一种专门的表示方式,叫做 SML(SEMI Message Language)。
SML 采用嵌套 List 结构,本质上是一棵树,能够清晰地表达层级关系。
例如,一个 S6F11 事件通知报文可以表示为:
<S6F11> <L [2]> <U2> 1001 </U2> // CEID: 事件号 <L [1]> <L [2]> <U2> 2001 </U2> // Report ID <L [2]> <A> Lot123 </A> // 批次号 <A> PASS </A> // 测试结果 </L> </L> </L> </L> </S6F11>
这里
<L [2]>
代表一个包含 2 个元素的 List,类似 JSON 的数组。
<A>
是 ASCII,<U2>
是无符号 2 字节整数。
这种格式保证了 数据结构稳定,不依赖额外的 Schema 解析。
📚 为什么不用 JSON,而要用 SML?
📌 1. SML 是二进制协议,解析快
- JSON 是文本格式,占用空间大,解析慢。
- SECS/GEM 传输的是二进制格式,比 JSON 高效。
📌 2. 层级结构清晰,适合表达复杂数据
- SECS 里的数据往往是树状的,比如:
- 一个事件(S6F11)可能包含多个报告(Report)
- 每个报告里可能有多个参数
- 这些参数的类型可能不一样(字符串、整数、布尔值)
- SML 的 List 结构天然适合表达这类数据。
📌 3. SECS/GEM 早于 JSON 诞生
- SECS/GEM 诞生于 1980s,而 JSON 在 2000 年才流行。
- 当时行业已经广泛使用 SML,不可能推翻重做。
🧩 SML 的基本语法 & 常见数据类型:
类型 | 语法 | 说明 | 示例 |
---|---|---|---|
List | <L [N]> ... </L> | 表示一个包含 N 个元素的列表 | <L [2]> <U2> 1001 </U2> <A> PASS </A> </L> |
ASCII | <A> ... </A> | 代表字符串 | <A> Lot123 </A> |
Binary | <B> ... </B> | 代表二进制数据 | <B> 0A 1F 2C </B> |
Boolean | <BOOL> ... </BOOL> | 代表布尔值(TRUE/FALSE) | <BOOL> TRUE </BOOL> |
Unsigned Integer | <U1> <U2> <U4> <U8> | 无符号整数,分别占 1/2/4/8 字节 | <U2> 3001 </U2> |
Integer | <I1> <I2> <I4> <I8> | 有符号整数 | <I4> -100 </I4> |
Float | <F4> <F8> | 4 字节 / 8 字节浮点数 | <F4> 3.14 </F4> |
🛠️ SML 在代码中怎么用?
在 secs4net
这个库里,SML 是通过 SecMessage
+ Item
对象来构建的。
我们可以用 C# 代码手动创建一条 S6F11 事件报文:
var msg = new SecsMessage(6, 11, false, new List<Item>
{
Item.L(
Item.U2(1001), // CEID 事件号
Item.L(
Item.L(
Item.U2(2001), // Report ID
Item.L(
Item.A("Lot123"), // 批次号
Item.A("PASS") // 测试结果
)
)
)
)
});
这段代码等价于我们上面的 SML:
<S6F11>
<L [2]>
<U2> 1001 </U2>
<L [1]>
<L [2]>
<U2> 2001 </U2>
<L [2]>
<A> Lot123 </A>
<A> PASS </A>
</L>
</L>
</L>
</L>
</S6F11>
🔚 小结:第三段你学到了什么?
- SML 是 SECS/GEM 专用的报文格式,它采用二进制 List 结构,比 JSON 高效。
- SECS/GEM 采用 SML 而不是 JSON,原因是历史早、解析快、适合层级数据。
- SML 的数据类型包括 L(List)、A(ASCII)、U/I(整数)、F(浮点)、B(二进制)。
- 在 C# 代码里,我们可以用
Item.L()
这种方式手动构造 SML 报文。
🔥 接下来,第四段内容是:
HSMS 是怎么保证通讯稳定的?HSMS 的消息流程是啥?SECS4NET 里是如何实现 TCP 连接的?
如果你准备好了,就说“继续”!
好嘞,马上进入第一讲【第 4 段】:
🎓 第一讲 · 第4段:HSMS 是如何通信的?SECS4NET 是如何实现连接的?
这一段我们来拆解:
- HSMS 协议是干嘛的?它和 SECS 的关系?
- HSMS 的连接流程是怎样的?
- SECS4NET 底层是怎么实现连接和通信的?
🎙️ 口述稿(你可以这样说):
在半导体工厂里,SECS 协议是负责定义数据结构和命令内容的,而 HSMS 协议则是负责把这些命令怎么发出去、怎么接收回来。
可以说,SECS 是内容,HSMS 是通道。HSMS 其实就是基于 TCP 的一种通信协议,专门为 SECS 服务设计的,稳定、双向、支持异步传输。
在连接流程上,HSMS 分为两端:设备端叫 Passive(被动方),主机端叫 Active(主动方),像 Socket 编程中的 server/client。
连接建立后,会交换几个系统消息,比如 Select.req、Select.rsp,用来确认连接合法、状态正常。
而 SECS4NET 这个库,正是封装了这一整套流程,我们不用自己写 Socket,它就能在后台自动维持连接,重连机制也有,适合工业级应用。
📚 技术讲解:什么是 HSMS?
📌 SECS 是什么?HSMS 是什么?
名称 | 全称 | 作用 | 传输层 |
---|---|---|---|
SECS-I | SECS over RS232 | 早期串口通信协议 | 串口 |
HSMS | High Speed SECS Message Services | 新一代高速通信协议 | TCP |
SECS-II | SEMI Equipment Communications Standard (Part II) | 报文格式 & 内容规范 | 协议内容层 |
简而言之:
- SECS-I:串口版本的 SECS,速度慢
- HSMS:TCP/IP 网络版的 SECS,更快更稳定
- SECS-II:通用的数据结构和报文语法
🔄 HSMS 的通信过程(官方定义)
- TCP连接阶段
- Active(主机)发起连接到 Passive(设备)
- TCP 成功建立后,双方进入 HSMS 层
- HSMS 握手阶段
- Active 发起
Select.req
- Passive 回复
Select.rsp
表示同意连接
- Active 发起
- 通信阶段
- 双方可以任意方向发送 SECS 报文(SxFx)
- 报文封装在 HSMS Message Frame 中传输
- 断开阶段
- 任一方可以发送
Separate.req
断开连接 - 或者网络异常导致连接中断
- 任一方可以发送
⚙️ HSMS 消息类型(Message Type)
HSMS 并非只传业务数据,还定义了一些控制消息:
Msg Type | 含义 | 说明 |
---|---|---|
0x01 | Select.req | 请求连接 |
0x02 | Select.rsp | 响应连接 |
0x03 | Deselect.req | 请求断开连接 |
0x04 | Deselect.rsp | 响应断开 |
0x05 | Linktest.req | 心跳检测 |
0x06 | Linktest.rsp | 心跳回应 |
0x07 | Reject.req | 拒绝连接 |
0x09 | Separate.req | 主动断开连接 |
0x10 | Data Message | 正式的 SECS 报文(如 S1F1、S6F11) |
🧰 SECS4NET 是怎么封装这些内容的?
在 SECS4NET 中,有两个核心类:
SecsGem
:表示 SECS/GEM 的服务端或客户端SecsMessage
:表示一个 SxFx 报文消息
你只要这样初始化:
var gem = new SecsGem(new HSMSSettings
{
IpAddress = "192.168.1.100",
Port = 5000,
Mode = ConnectionMode.Active,
DeviceId = 1,
// 其他参数...
});
然后连接建立后,你就可以直接:
await gem.SendAsync(new SecsMessage(1, 1, false));
SECS4NET 会在内部自动:
- 建 TCP 连接
- 发 Select.req
- 保持 Linktest
- 自动重连
- 收发 SECS 报文并解析成
SecsMessage
☁️ 稳定性保障机制(面试重点)
- KeepAlive / 心跳机制: 使用
Linktest.req/resp
检测连接活性 - Reconnect 自动重连: 网络掉线后自动尝试重连
- 异步消息处理: 不阻塞主线程,使用
async/await
保证性能
🔚 小结:第四段你学到了什么?
- HSMS 是 SECS 的传输协议,基于 TCP,比串口的 SECS-I 高效可靠
- 通信过程包括连接(Select)、传输(Data)、断开(Separate)
- SECS4NET 封装了整个连接逻辑,开发者只需关注业务逻辑
- 心跳、自动重连、异步收发都是工业级通信必须考虑的问题
下一段(第五段)将讲解:
SECS4NET 的事件订阅机制是怎么做的?如何监听 S6F11 等上报?怎么回应?怎么配置多种事件?如何写出企业级 EAP 监听逻辑?
说“继续”,我们接着来!
好勒,咱们继续推进,进入第一讲【第 5 段】:
🎓 第一讲 · 第5段:SECS4NET 如何监听事件(S6F11)?如何写回应逻辑?
本讲内容:
- 如何订阅 S6F11 等事件消息?
- 如何解析事件里的数据?
- 如何写出稳定、健壮的事件响应逻辑?
- 如何“装懂”讲 SECS/GEM 的事件机制?
🎙️ 口述稿(你可以这样说):
在设备通信中,事件上报(S6F11)是最常见的报文之一,它表示设备端有某种“动作”发生,比如测试完成、状态变更、异常告警等。
为了让主机接收这些事件,设备就会发送 S6F11 报文。而主机必须有“监听逻辑”,去接住这类消息、判断是什么事件、提取参数、再作出回应。
SECS4NET 的设计非常优雅——你只需要注册一个
PrimaryMessageReceived
事件,它就能捕捉所有设备发来的消息。
然后在里面判断:是不是 S6F11?是不是我关心的 CEID?再做处理就行了。
📡 S6F11 是什么?它怎么构成?
S6F11 的语义是:设备通知主机某个事件发生,格式如下:
<S6F11>
<L [2]>
<U2> CEID </U2> // Collection Event ID
<L [N]> // Report list
<L [2]>
<U2> RPTID </U2> // Report ID
<L [N]> // Report content
<A> PARAM1 </A>
<A> PARAM2 </A>
</L>
</L>
</L>
</L>
</S6F11>
典型的参数结构:
CEID
: 表示“哪个事件”发生了RPTID
: 表示“哪个报告模板”- 报告里包含多个参数,比如“批次号”、“工站名”、“结果”等等
✅ SECS4NET 如何监听 S6F11?
在初始化 SecsGem
之后,你可以写一段监听逻辑:
gem.PrimaryMessageReceived += async (s, e) =>
{
var msg = e.PrimaryMessage;
// 判断是不是 S6F11(Stream 6, Function 11)
if (msg.S == 6 && msg.F == 11)
{
var ceid = msg.SecsItem[0].GetValue<ushort>();
var reports = msg.SecsItem[1];
Console.WriteLine($"收到事件:CEID = {ceid}");
foreach (var report in reports)
{
var rptId = report[0].GetValue<ushort>();
var data = report[1]; // 这个是 List
foreach (var item in data.Items)
{
Console.WriteLine($"参数值:{item.ToString()}");
}
}
// 回复主机(S6F12)
await e.SendReplyAsync(new SecsMessage(6, 12, false));
}
};
🧠 面试时可以怎么讲?
你可以说:
SECS4NET 的事件监听是基于异步委托实现的。收到 S6F11 报文时,可以通过
msg.SecsItem
直接解析 CEID、RPTID 和 Report 内容。
我们可以建立一套 CEID -> 动作 的字典映射,实现对不同事件的自动处理。
此外,为了保持通信流程完整,必须在处理后发送一条 S6F12 作为回应,否则设备可能视为通信异常。
⚙️ 扩展:如何实现企业级事件映射系统?
可以建立如下结构来解耦业务:
// 配置一张事件表
Dictionary<ushort, Action<Item>> eventHandlers = new Dictionary<ushort, Action<Item>>
{
[1001] = (reportData) => HandleTestComplete(reportData),
[1002] = (reportData) => HandleAlarm(reportData)
};
// 在消息监听器中调用它
if (eventHandlers.TryGetValue(ceid, out var handler))
{
handler.Invoke(reportContent);
}
然后每个 Handler 单独写,便于测试和维护。
⚠️ 注意点(也可能被问):
- 一定要回复 S6F12,不然设备会超时或报错
- 报文结构不固定,必须按 List 层级去解析
- CEID 是关键字段,不同厂商设备定义的含义不同
- 可能包含多个 RPTID 和多个参数,要遍历两层 List
🔚 小结:第五段你学到了什么?
- S6F11 是设备向主机报告事件的报文
- SECS4NET 使用异步事件注册来监听报文
- 报文是嵌套 List,需要按顺序手动解析字段
- 实际业务中可配置化映射 CEID 到具体处理逻辑
- 一定记得:事件响应要及时发送 S6F12,维持通信闭环
下一讲(第 6 段)我们将进入更有趣的内容:
设备初始化流程、主机如何向设备发命令(S1F1、S1F13、S2F41)?怎么主动触发流程?SECS/GEM 的“主从角色”是怎么回事?
说“继续”,我们接着来!
收到!我们正式进入第一讲【第 6 段】,这是整个 SECS/GEM 入门中很关键的一段,涉及“主动命令”“设备初始化”等实际落地使用的核心流程:
🎓 第一讲 · 第6段:主机如何主动发命令?从设备获取状态?(S1F1、S1F13、S2F41)
本段内容:
- 主动发消息给设备的命令有哪些?
- 什么是 S1F1、S1F13、S2F41,它们的用途?
- 怎么用 SECS4NET 主动触发这些报文?
- 面试中怎么讲“主从角色”和流程控制?
🎙️ 口述稿(你可以这样说):
在实际通信过程中,主机除了被动接收事件(S6F11),还可以主动向设备发起各种命令,比如:
- “你现在在不在线?”(S1F1)
- “你支持哪些功能?”(S1F13)
- “执行某个动作,比如开始加工、下载参数等。”(S2F41)
我们叫这类命令为“主动命令”,也就是主机主动发起,设备被动响应。而 SECS4NET 也支持这种主动发送方式。
开发时,我们用
SendAsync()
方法发送SecsMessage
,填上 Stream 和 Function 编号,就能模拟主机发出指令,测试设备的回应能力。
📚 核心指令详解(面试容易被问)
✅ S1F1:Are You There?(你在吗?)
- 用途:确认设备是否在线
- 主机发出,设备回应 S1F2
S1F1 W
=> 设备回应 S1F2
代码示例:
var s1f1 = new SecsMessage(1, 1, true); // "true" 表示需要回应
var s1f2 = await gem.SendAsync(s1f1);
✅ S1F13:Establish Communications Request(请求通信)
- 用途:让设备进入通信状态(通常用于第一次接入)
- 回复 S1F14,说明是否接受通信建立
var s1f13 = new SecsMessage(1, 13, true);
var s1f14 = await gem.SendAsync(s1f13);
**注意:**如果设备尚未 Ready,会拒绝通信,主机会接收到错误码
✅ S2F41:Host Command(主机命令)
- 用途:主机主动触发设备执行某种动作
- 常见动作:
- 启动某个流程
- 下载参数
- 启动测试、结束测试
- 设备回应 S2F42,报告执行结果
S2F41 W
<L [2]>
<U2> RCMD </U2> // Remote Command 名称
<L [N]> // 参数列表(可选)
</L>
代码示例:
var s2f41 = new SecsMessage(2, 41, true, "RCMD",
Item.L(
Item.A("START"), // Remote Command 名字
Item.L() // 参数,可省略
)
);
var s2f42 = await gem.SendAsync(s2f41);
🔄 主从角色机制(重要知识点)
主机(Host) | 设备(Equipment) |
---|---|
主动发命令(S2F41) | 被动接收命令 |
被动接收事件(S6F11) | 主动上报事件 |
主动请求状态(S1F1、S1F3) | 回应状态内容 |
SECS/GEM 不是谁控制谁的问题,而是角色分工清晰:
- 主机发控制类命令
- 设备发信息类事件
面试官可能问:设备怎么知道主机已经 Ready?
答:主机会发 S1F13,设备回应 S1F14,同意之后,才会允许发事件(S6F11)
⚠️ 小知识点补充:
- 所有 W 结尾的指令,都是“需要回应”的
SecsMessage(1, 1, true)
中的true
表示要等待设备回应(Blocking)S2F41
有些设备还会检查 Remote Command 名字是否合法,不合法会在 S2F42 里返回错误码- 在实际项目中,通常会预先定义一张 RCMD 对照表,统一主机可发哪些命令
🔚 小结:第六段你学到了什么?
- 主机可以主动发起 S1F1(心跳)、S1F13(请求通信)、S2F41(远程命令)
- SECS4NET 的主动指令通过
SendAsync(SecsMessage)
实现 - 主从机制是通信的基础:主机控制,设备响应
- 流程控制、远程命令、功能确认都是主动报文的应用场景
- “主机和设备谁是主?”——回答要从角色功能上讲,不是地位
下一讲(第 7 段)我们会进阶到更核心的工业级能力:
如何管理多个设备连接?如何实现一个设备管理平台?如何区分每个设备状态?SECS4NET 支持多设备吗?多线程如何处理?
说“继续”,我们接着来!
好耶,马上带你进入第一讲的【第 7 段】,这部分要谈的,是你以后面临多台设备管理、并发处理时最容易踩坑的关键点——
🎓 第一讲 · 第7段:如何连接多个设备?多线程并发怎么搞?SECS4NET 支不支持多设备?
本段内容:
- 一个 Host 连接多个设备,该怎么设计?
- 多个设备连接时,SECS4NET 提供什么支持?
- 多线程、异步消息监听要注意啥?
- 如何避免主机逻辑写得“一锅粥”?
🎙️ 口述稿(你可以这样说):
在实际工厂现场,往往不是一台设备,而是几十台、上百台设备同时连入 Host。
每台设备都是一个独立的 TCP 连接,有独立的 SECS Session 和报文流。所以我们需要一套“设备管理机制”,确保:
- 每台设备的连接状态、通信状态是独立的;
- 主机可以单独给某台设备发命令;
- 设备发来的事件,能知道“是哪个设备发来的”;
SECS4NET 的设计思路是:
每个设备建立一个 SecsGem 实例,每个实例跑在自己线程上,互不干扰。
✅ 多设备连接的方式?
简单来说:
- 一台设备 = 一个 SecsGem 实例
- 每个设备 = 一个 TCP Socket
- 每个设备监听自己的事件、发自己的命令
你可以把它看作“一个小网关服务+设备代理”
代码结构如下:
public class EquipmentAgent
{
public string Name { get; set; }
public SecsGem Gem { get; set; }
public void Init(string ip, int port)
{
var connection = new SocketConnection(ip, port);
Gem = new SecsGem(connection);
Gem.PrimaryMessageReceived += OnMessageReceived;
Gem.ConnectionChanged += OnConnectionChanged;
// 启动连接线程
Task.Run(() => Gem.ConnectAsync());
}
private async void OnMessageReceived(object sender, PrimaryMessageReceivedEventArgs e)
{
var deviceName = this.Name;
var msg = e.PrimaryMessage;
Console.WriteLine($"[{deviceName}] 收到消息 S{msg.S}F{msg.F}");
// 按照设备名做业务处理
}
}
🕸️ 管理多个设备实例?
你可以维护一个设备字典:
Dictionary<string, EquipmentAgent> Equipments = new();
void InitAllDevices()
{
foreach (var config in DeviceConfigs)
{
var agent = new EquipmentAgent { Name = config.Name };
agent.Init(config.IP, config.Port);
Equipments[config.Name] = agent;
}
}
这样你就可以:
- 给某台设备发命令:
Equipments["设备1"].Gem.SendAsync(...)
- 管理设备状态:
Equipments["设备2"].Gem.IsConnected
- 根据设备名路由业务逻辑
⚙️ 多线程/异步消息处理注意点
每个 SecsGem 内部是异步运行的,监听器回调 PrimaryMessageReceived
是在它自己的线程中触发的。
所以你要注意以下几点:
问题 | 建议 |
---|---|
多线程访问共享资源 | 加锁或使用线程安全集合 |
Console 输出乱序 | 用日志组件(如 Serilog)按设备分日志文件 |
异常捕获不到 | 在回调中 try-catch 处理所有逻辑异常 |
主机卡顿 | 长逻辑用 Task.Run() 分离 |
✨ 提升建议:做一个“设备连接看板”
你可以写一个 WPF 或 WinForms 小工具,把每个设备当前状态(连接中/通信中/断线)展示出来,还可以点击“重连”“发送测试命令”。
示意结构:
[设备名] | [IP] | [端口] | [连接状态] | [最近消息时间] | [操作按钮]
这样你在面试时可以说:
我们自己做了一套设备管理平台,支持几十台设备并发连接,能够实时监控连接状态,并支持批量下发命令和重连操作。后台是基于 SECS4NET 多实例架构 + 异步线程池实现。
听上去就很“工程化”,不再只是 Demo 层级了。
❓ 面试常见问题:
Q:SECS4NET 是否线程安全?
A:单个 SecsGem 实例是线程安全的;多个设备建议一个实例一个连接线程,避免锁竞争。
Q:一个程序最多能连接多少台设备?
A:看你的资源管理和线程控制能力,通常几十台不成问题,关键在于消息处理速度、错误容忍能力。
Q:多个设备怎么知道是谁发来的事件?
A:每个设备都有自己独立的回调,你可以在实例中绑定设备名、ID、序列号,用于业务路由。
🔚 小结:第七段你学到了什么?
- 多设备通信 = 多个 SecsGem 实例 + 多线程连接
- 每台设备独立处理,不共享逻辑,避免混乱
- 设备管理平台可以显示状态、支持命令、实时监控
- 异步通信要注意线程安全、日志隔离、异常捕获
- 面试时强调“多设备并发”、“平台级管理”、“线程隔离”这几个关键词会很加分
下一讲(第 8 段)我们就来讲讲终极知识点:
SECS/GEM 的报文结构如何精准理解?Item 的类型怎么对应?SML 是什么?怎么手动构造、解析复杂报文?怎么模拟设备数据?
说“继续”,我们冲刺最后的几讲!
好勒,我们进入第一讲 · 第 8 段——这部分是你最早提到的关键词:“报文结构”。既然你说了要学得深入,就别怕烧脑,咱今天直接把 SECS 报文剖开来讲,连格式、编码、类型都吃透!
🎓 第一讲 · 第8段:SECS/GEM 报文结构详解!SML 是什么?Item 是什么?
本段内容:
- SECS 报文的格式到底长什么样?
- 报文里面的内容是怎么组织的?Item 是个啥?
- 什么是 SML?和 XML 一样吗?
- 实战中怎么写、怎么读、怎么调试报文?
🎙️ 口述稿(你可以这样说):
SECS/GEM 的所有通信,其实就是一条条“报文”在飞。每条报文都是由两个关键部分组成的:
- 报文头(Header):包含 Stream、Function、设备ID、是否需要回应等信息;
- 报文体(Body):真正要交换的数据,比如状态码、参数值、事件名等等;
报文体的结构,完全是用一种叫 Item 的嵌套数据格式来表达的,类似 XML 树结构。
在调试和开发过程中,我们常用一种叫 SML (SEMI Message Language) 的语法,把报文写成文本形式,既方便调试,也方便人眼阅读。
✅ 报文结构是什么样的?
我们用一个最经典的例子:S1F3:Status Request
S1F3 W
<L [1]
<U2 1>
>
含义如下:
S1F3 W
:Stream = 1,Function = 3,需要设备回应(W = With Reply)<L [1]>
:表示一个 List,有 1 个子项<U2 1>
:无符号 2 字节整数,值是 1(请求设备状态)
这就是 SML!超简洁、可读性强,像极了 SECS 的“Hello World”。
⚙️ 各种 Item 类型(像学新语言)
类型 | 说明 | 示例 |
---|---|---|
L | List(列表) | <L [2] <A "X"> <B>> |
A | ASCII 字符串 | <A "Ready"> |
B | Byte | <B 01 02 03> |
U1 | Unsigned 1 Byte Integer | <U1 255> |
U2 | Unsigned 2 Byte Integer | <U2 65535> |
I4 | Signed 4 Byte Integer | <I4 -1> |
F4 | Float | <F4 3.14> |
BOOLEAN | 布尔 | <BOOLEAN 1> |
它和 JSON、XML 的区别在于:SML 是严格、定长、强类型的。
示例:构造一个复杂报文(S2F41)
S2F41 是主机发送的“远程命令”,它的报文体要长这样:
S2F41 W
<L [2]
<A "START"> // 命令名
<L [1]
<A "Lot123"> // 命令参数
>
>
SECS4NET 代码怎么构造这个报文?
var msg = new SecsMessage(2, 41, true,
Item.L(
Item.A("START"),
Item.L(
Item.A("Lot123")
)
)
);
await gem.SendAsync(msg);
就是一行一行把 Item 拼进去,类型和嵌套都能精确控制。
✨ 报文调试技巧:如何打印、模拟、复现?
- SECS4NET 所有收到的消息都可以转成 SML:
var sml = primaryMessage.ToSml(); Console.WriteLine(sml);
- 要模拟一个设备发报文,可以自己构造 SecsMessage,然后反向发送
- 要分析厂商设备发来的数据,就要根据 SML 判断它的 Item 类型、层级关系、值的含义
- 报文的理解能力 = 是否能读懂 SML = 能不能看懂设备和主机的“对话”
面试官可能问的“刁钻问题”:
Q:SECS 报文和 JSON 有啥不同?
A:SECS 报文是二进制格式,定长、强类型,适合工业通信;JSON 是文本型、弱类型,适合 Web 应用。
Q:SML 是协议的一部分吗?
A:不是,它只是人类读懂报文的调试工具,协议本身只定义二进制格式。
Q:Item 最深能嵌套多少层?
A:理论上无穷,但设备和主机的解析能力有限,一般不要超过 5 层。
Q:Item 和 Stream/Function 的关系是?
A:Stream/Function 表示报文的类型和作用,Item 是报文的具体数据内容。
🔚 小结:第八段你学到了什么?
- SECS 报文由 Header 和 Body 构成
- 报文体的格式叫做 Item,类型丰富、可嵌套
- SML 是 SECS 报文的“可视化语法”,利于调试和阅读
- 报文内容不仅要能读懂,还要能手写、能构造
- SECS4NET 提供了 Item 的所有构造函数,嵌套方式类比 JSON 树
- 面试时要能讲清楚:“什么是 SML?什么是 Item?报文怎么构造?”
下一讲(第 9 段),咱就来点实战干货:
设备上线/下线的全流程是怎样的?要发哪些指令?GEM 的标准流程能复述吗?主机上线要做什么准备?
说“继续”,我们稳步推进!
稳住,我们继续冲刺 第一讲 · 第9段 —— 这段是工业 GEM 系统中非常核心的场景:设备上线 / 下线流程。
这块内容不光容易被问,还常常是面试官想判断你是不是“只写了个 Demo”,还是能落地工厂现场的关键点。
🎓 第一讲 · 第9段:GEM上线流程详解(上电、建立通信、上线、状态报告)
本段内容:
- 设备是怎么从“断电”状态一步步走到“Ready”的?
- GEM 协议有哪些“标准动作”必须完成?
- 主机上线要准备啥?怎么判断设备准备好了?
- S1F13/S1F15/S1F17/S1F23 是干嘛用的?
🎙️ 口述稿(你可以这样说):
GEM 协议的核心思想是:设备是主动的,主机是管理者。
所以一台设备从上电到 Ready,主机必须完成一系列动作,确认通信是否正常、设备是否支持 GEM、是否上线成功、是否可以发命令。
通常流程是这样的(简化版):
✅ 设备上线流程图(主机视角):
- TCP 连接成功
- 建立 SECS Session(S1F13)
- 请求设备状态(S1F1)
- 强制设备上线(S1F17)
- 确认上线状态(S1F15)
- 注册事件(S2F33)
- 订阅事件报告(S2F35)
- 设备开始主动汇报事件(S6F11)
- 可以发命令(S2F41)
这就叫:GEM 上线的标准动作(Host Startup Sequence)
🔍 报文细节解析
步骤 | 报文 | 说明 |
---|---|---|
1 | S1F13 Host Send | 建立 Linktest / Are you there |
2 | S1F14 Equip Ack | 确认设备在线(给个 OK) |
3 | S1F1 Host Req | 请求设备状态(Are you OK) |
4 | S1F2 Equip Rep | 返回设备状态码(如 1 = online) |
5 | S1F17 Host Send | 要求设备上线(Transition to online) |
6 | S1F18 Equip Ack | 确认上线成功 |
7 | S1F15 Host Req | 查询当前设备是否 online |
8 | S1F16 Equip Rep | 返回状态 1(online) |
9 | S2F33/S2F35 Host Send | 注册、订阅事件 |
10 | S6F11 Equip Send | 设备开始报告事件 |
11 | S2F41 Host Send | 主机可以发送远程命令了 |
这个流程体现了“主机控制一切,但不信任任何状态”的 GEM 哲学。
✅ 面试重点关键词解释:
S1F13 / S1F14 – 建立通信
- Host 发 S1F13 给设备,设备回 S1F14 表示“我在线”。
- 没有 S1F14 表示设备没连上、程序异常、协议不支持。
S1F17 / S1F18 – 强制上线
- Host 用 S1F17 把设备从“offline”状态强制改为“online”
- 设备必须支持 GEM 上线/下线模型(否则不能管控)
S2F33 / S2F35 – 注册、订阅事件报告
- GEM 协议核心是“事件驱动”,你要告诉设备:
- 我对哪些事件感兴趣(S2F33)
- 每个事件有哪些报告项(S2F35)
- 否则设备不会发任何事件(S6F11)
SECS4NET 怎么实现这些流程?
// 1. 建立通信
await gem.SendAsync(new SecsMessage(1, 13, true));
// 2. 强制上线
await gem.SendAsync(new SecsMessage(1, 17, true,
Item.U1(1) // 1 = request online
));
// 3. 查询状态
await gem.SendAsync(new SecsMessage(1, 15, true));
// 4. 注册事件
await gem.SendAsync(new SecsMessage(2, 33, true,
Item.L(
Item.U2(1001), // CEID
Item.U2(1) // Report ID
)
));
每一步都能精确对应协议动作,哪怕没有 XML Schema,靠代码和 SML 就能还原报文。
✨ 面试要点金句(可以背):
- SECS 报文的世界里,没有“猜测”,所有状态必须通过指令确认;
- GEM 的核心是事件机制,主机必须主动注册和订阅;
- 上线流程的重点不是通信,而是状态切换和能力协商;
- 报文是数据,但流程是协议,要把报文放在场景里去理解。
❓面试可能追问:
Q:如果设备没有回应 S1F13 怎么办?
A:说明设备程序未启动、IP 端口配置错误、或者不是 GEM 设备。
Q:上线之后为什么没收到 S6F11 报文?
A:可能没有注册事件(S2F33)、没有订阅报告(S2F35)、设备未配置报告项。
Q:怎么知道设备当前状态是 Online 还是 Offline?
A:发 S1F15 请求当前状态,设备通过 S1F16 返回码(1=online,2=offline)
Q:怎么防止设备乱发事件?
A:GEM 设备通常只发已注册的事件;主机可定期重置事件定义。
🔚 小结:第九段你学到了什么?
- GEM 上线是一个有步骤、有约定的“协议流程”
- SECS 报文只是载体,重点是 Host 和 Equip 的状态协商
- 主机需要主动发起所有控制动作:建链、上线、订阅、确认状态
- SECS4NET 可以实现完整上线流程,代码可读性强,SML 帮助调试
- 面试时要能复述上线流程的关键步骤,并知道为什么要这么做
接下来的第 10 段,也是本讲最后一段:
GEM 的事件报告、状态模型、远程命令控制这些“核心业务逻辑”在真实工厂里是怎么用的?怎么设计主机程序才能既强大又稳定?
说“继续”,我们来收官!
好嘞,最后一段 第一讲 · 第10段,我们就来把整个 GEM 协议的 “核心三板斧” 给彻底讲透:事件报告机制、状态模型、远程命令。
这些内容,是设备厂真正关心的——GEM 不是让你只“能连上”,而是让你“控得住”、“收得到数据”、“远程指令能落地”。
🎓 第一讲 · 第10段:GEM 协议核心三板斧——事件、状态、命令
本段内容:
- 什么是 CEID、RPTID、VID、SVID、RCMD?
- 事件怎么注册、怎么绑定报告、怎么收到?
- 主机怎么知道设备当前状态?
- 怎么发命令让设备开始动作?设备怎么回应?
🎙️ 口述稿(你可以这样说):
GEM 协议的本质是为了实现三件事:
- 事件驱动的数据收集(Event/Report)
- 设备状态感知(Status Variables)
- 远程命令控制(Remote Command)
所有的报文、定义、流程,最终都要服务于这三件事。
✅ 一、事件报告机制(CEID、RPTID、VID)
1. 概念说明:
- CEID(Collection Event ID):事件编号,如“设备Ready”、“加工完成”
- RPTID(Report ID):报告编号,用来定义这次事件触发时应该上报哪些内容
- VID(Variable ID):变量编号,真正的“数据字段”,比如 LotID、RecipeName、Time
2. 报文流程:
步骤 | 报文 | 含义 |
---|---|---|
1 | S2F33 | 告诉设备:哪些事件会触发报告(CEID -> RPTID) |
2 | S2F35 | 告诉设备:每个报告包含哪些字段(RPTID -> VIDs) |
3 | S6F11 | 设备发事件,携带 CEID + 报告内容(按 RPTID 定义) |
举个例子:
- 当 CEID = 1001(加工完成)时,发 RPTID = 10
- RPTID = 10 包含 VID 100, 101(LotID、StartTime)
- 那么 S6F11 报文会发出
<CEID 1001>, <RPTID 10, <A "Lot001">, <U4 1680000000>>
3. SECS4NET 示例代码:
// S2F33: 绑定 CEID 到 RPTID
await gem.SendAsync(new SecsMessage(2, 33, true,
Item.L(
Item.L( // Event list
Item.L(
Item.U2(1001), // CEID
Item.L(Item.U2(10)) // RPTID
)
)
)
));
// S2F35: 绑定 RPTID 到 VID
await gem.SendAsync(new SecsMessage(2, 35, true,
Item.L(
Item.L(
Item.U2(10), // RPTID
Item.L(Item.U2(100), Item.U2(101)) // VIDs
)
)
));
✅ 二、设备状态模型(SVID)
有些数据不是靠事件推,而是靠主机拉。
比如:主机想知道当前设备温度,就用 S1F3(Status Request) 把 SVID 传过去,设备回应 S1F4。
常用的 SVID(Status Variable ID):
SVID | 含义 |
---|---|
101 | 当前状态码 |
102 | 当前批次 |
103 | 当前 Recipe 名 |
主机主动发请求:
var msg = new SecsMessage(1, 3, true,
Item.L(Item.U2(101), Item.U2(102))
);
await gem.SendAsync(msg);
设备回应 S1F4,格式是:
S1F4
<L [2]
<U1 2> // 状态码 2 = Idle
<A "Lot123"> // 当前批次
>
✅ 三、远程命令控制(RCMD)
远程命令是 GEM 协议中最关键的控制动作。
主机用 S2F41 发命令,设备用 S2F42 返回执行结果。
报文格式(SML):
S2F41 W
<L [2]
<A "START_PROCESS"> // 命令名
<L [2]
<A "Lot123"> // 参数1
<A "RecipeA"> // 参数2
>
>
响应格式:
S2F42
<B 00> // 执行成功(00=OK,01=无效命令,02=参数错误)
SECS4NET 代码:
var msg = new SecsMessage(2, 41, true,
Item.L(
Item.A("START_PROCESS"),
Item.L(
Item.A("Lot123"),
Item.A("RecipeA")
)
)
);
var reply = await gem.SendAsync(msg);
var resultCode = reply.SecsItem.GetChild(0).GetValue<byte>(); // 判断成功与否
✨ 面试要点精讲:
- CEID 是触发条件,RPTID 是包装纸,VID 是内容;
- S2F33/S2F35 是“配置”,S6F11 是“结果”;
- 状态拉取靠 S1F3/S1F4,适合周期轮询或应急判断;
- 远程命令 S2F41 是让设备“动起来”的唯一入口,要关注参数格式、返回码;
- 设备厂面试最喜欢问你:你有没有真的配过一套完整的事件机制?有没有真发过远程命令?你就要讲清这个三板斧流程!
🔚 小结:第十段你学到了什么?
- GEM 的核心是三大能力:事件、状态、命令;
- 报文看起来复杂,其实结构清晰——只要搞懂 CEID、VID、RCMD 三大类;
- 报文发出去只是第一步,更重要的是:主机逻辑能不能撑起整套流程;
- 面试时,你要像一个懂得“设备语言”的人,不只是程序员。
✅ 第一讲:全部完结!
你现在拥有了一整套口述稿 + 教学讲解,围绕以下核心内容构建了认知:
- SECS/GEM 的协议结构和通信原理
- SECS 报文的组成与调试方式(SML, Item 类型)
- 设备上线流程的标准动作和报文串
- GEM 协议的三大支柱:事件、状态、命令
- SECS4NET 如何用 C# 表达上述逻辑
- 面试官可能刁钻提问的准备
接下来只要你说,我可以继续:
- 第二讲:深入 SECS4NET 项目结构,类、接口、事件机制
- 第三讲:如何设计一个主机 Host 应用程序(比如 EAP)
- 第四讲:如果你要支持多个设备并发通信,系统要怎么设计?
- 第五讲:设备厂真实部署场景中,GEM 程序的运维注意事项
你来决定:是否继续“第二讲”?还是要我先整理“第一讲”的总结版(方便你背诵)?
好的!这就是《第一讲 · 总结精华版》——为背诵准备的口述速成稿,适合面试前复习+答题用。我们会分为两个部分:
- 第一部分:一整段口述稿,可以当成面试时开口自我介绍用;
- 第二部分:要点清单+关键词串讲,方便你临阵复盘,条理更清晰。
【第一部分】通用口述稿(面试时可复读)
“我们在对接设备时主要使用 SECS/GEM 通信协议,它是半导体行业里的一个国际标准,基于 SECS-II 报文格式和 HSMS 或 SECS-I 物理层。
我是从一个 GitHub 开源项目 SECS4NET 入手来深入学习这套协议的。它是一个基于 .NET 的实现,我主要跑了它的 Sample,研究了报文的结构、数据类型和通信流程。SECS 报文用 SML 表示结构清晰,比如 S1F13 是主机发送 Establish Communication Request,设备回 S1F14 表示应答成功。Item 是报文的数据结构,像
Item.A()
是 ASCII 类型,Item.U2()
是无符号 2 字节整数。在建立完连接后,设备通过 S1F1 报出自己的型号和软件版本,主机用 S1F3 请求状态变量,用 S2F41 发远程命令,设备通过 S6F11 上报事件。
我重点学习了 GEM 协议的三大核心:
- 事件报告机制:用 S2F33/S2F35 注册 CEID 和 RPTID,触发后设备用 S6F11 报数据;
- 状态模型:主机用 S1F3 拉取状态,设备回 S1F4;
- 远程命令控制:主机用 S2F41 发指令,设备回 S2F42。
整个学习过程中,我不仅运行了 SECS4NET 示例,还试着用它构建一个简单的 Host Demo,模拟主机与设备的连接、握手、注册事件、拉取变量、下发命令。我也会观察它的 MessageLog,确认报文格式是否正确。
这个过程让我从“知道 SECS 是啥”,转变为“可以实际操作一套主机设备通信流程”。虽然我过去是做 EAP 软件二次开发为主,但现在通过这套系统性的学习,我对设备端的通信细节,包括报文结构和协议流程,也掌握得更深入了。”
【第二部分】知识要点清单(记关键词,便于临时救急)
类别 | 知识点 | 说明 |
---|---|---|
通信基础 | SECS-I / HSMS | 物理层通信方式,串口 / TCP |
报文结构 | SML + Item 类型 | 类似 XML 的结构树,Item 有多种类型(A, U2, L) |
报文方向 | 主机 vs 设备 | 谁发谁收都很重要,尤其带 W(Wait) |
建链流程 | S1F13 / S1F14 | 建立通信 |
设备信息 | S1F1 / S1F2 | Inquire Device Status |
状态读取 | S1F3 / S1F4 | 主动读取状态变量(SVID) |
远程命令 | S2F41 / S2F42 | 主机对设备下达动作命令 |
事件注册 | S2F33 / S2F35 | 告诉设备哪些事件触发哪些报告 |
事件上报 | S6F11 | 设备主动上报事件 |
报文构成 | CEID / RPTID / VID | 事件编号 / 报告编号 / 变量编号 |
编程框架 | SECS4NET | .NET 实现的通信框架,支持异步、事件注册 |
调试工具 | MessageLog | 查看报文内容、调试字段、断点调试 |
面试话术 | “三板斧” | 事件报告、状态变量、远程命令 |
【通关口诀】
给你编一个口诀,帮助记住 GEM 的三大核心流程:
连线问状态,注册等通告,发令才开跑。
建链:S1F13;
问状态:S1F3;
注册事件:S2F33 + S2F35;
接收通告:S6F11;
发命令:S2F41。
以上就是《第一讲 · 总结精华版》,可以直接背诵,也可以用来准备面试时的条理化答题。
如果你准备好了,我可以进入《第二讲:SECS4NET 项目结构详解,主机程序怎么搭出来的》。
说一声 “继续” 就开讲。