当前位置: 首页 > article >正文

Secs/Gem第一讲(基于secs4net项目的ChatGpt介绍)


后续内容为基于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-IRS-232 串口串口连接老设备常用,慢
HSMSTCP/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 LengthBody 的长度(不含 Header)
4 字节System Bytes主机和设备之间标识配对的请求-回应
1 字节Stream流号:表示消息类别
1 字节Function功能号:表示具体的操作

(部分实现会把 Stream 和 Function 组成一个完整的 Command,如 S6F11)

🔹 Stream(流)常见编号含义:
编号含义
S1Equipment Status Request(状态)
S2Host Command(控制命令)
S5Alarm Related(报警)
S6Data Collection(事件/报告)
S7Recipe Related(程式)
S9Error Message(异常)
S10Terminal Display(日志)
S14File Transfer(文件传输)
🔹 Function(功能)常见示例:
编号含义备注
F1Request发起方请求
F2Response对 F1 的回应
F3Set主机设置设备数据
F5Get主机获取设备数据
F6Get Response对 F5 的回应
F11Event Report事件通知(设备主动发)
F12Acknowledge对事件的回应

🧩 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-ISECS over RS232早期串口通信协议串口
HSMSHigh Speed SECS Message Services新一代高速通信协议TCP
SECS-IISEMI Equipment Communications Standard (Part II)报文格式 & 内容规范协议内容层

简而言之:

  • SECS-I:串口版本的 SECS,速度慢
  • HSMS:TCP/IP 网络版的 SECS,更快更稳定
  • SECS-II:通用的数据结构和报文语法

🔄 HSMS 的通信过程(官方定义)

  1. TCP连接阶段
    • Active(主机)发起连接到 Passive(设备)
    • TCP 成功建立后,双方进入 HSMS 层
  2. HSMS 握手阶段
    • Active 发起 Select.req
    • Passive 回复 Select.rsp 表示同意连接
  3. 通信阶段
    • 双方可以任意方向发送 SECS 报文(SxFx)
    • 报文封装在 HSMS Message Frame 中传输
  4. 断开阶段
    • 任一方可以发送 Separate.req 断开连接
    • 或者网络异常导致连接中断

⚙️ HSMS 消息类型(Message Type)

HSMS 并非只传业务数据,还定义了一些控制消息:

Msg Type含义说明
0x01Select.req请求连接
0x02Select.rsp响应连接
0x03Deselect.req请求断开连接
0x04Deselect.rsp响应断开
0x05Linktest.req心跳检测
0x06Linktest.rsp心跳回应
0x07Reject.req拒绝连接
0x09Separate.req主动断开连接
0x10Data 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 的所有通信,其实就是一条条“报文”在飞。每条报文都是由两个关键部分组成的:

  1. 报文头(Header):包含 Stream、Function、设备ID、是否需要回应等信息;
  2. 报文体(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 类型(像学新语言)

类型说明示例
LList(列表)<L [2] <A "X"> <B>>
AASCII 字符串<A "Ready">
BByte<B 01 02 03>
U1Unsigned 1 Byte Integer<U1 255>
U2Unsigned 2 Byte Integer<U2 65535>
I4Signed 4 Byte Integer<I4 -1>
F4Float<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、是否上线成功、是否可以发命令。

通常流程是这样的(简化版):


✅ 设备上线流程图(主机视角):

  1. TCP 连接成功
  2. 建立 SECS Session(S1F13)
  3. 请求设备状态(S1F1)
  4. 强制设备上线(S1F17)
  5. 确认上线状态(S1F15)
  6. 注册事件(S2F33)
  7. 订阅事件报告(S2F35)
  8. 设备开始主动汇报事件(S6F11)
  9. 可以发命令(S2F41)

这就叫:GEM 上线的标准动作(Host Startup Sequence)


🔍 报文细节解析

步骤报文说明
1S1F13 Host Send建立 Linktest / Are you there
2S1F14 Equip Ack确认设备在线(给个 OK)
3S1F1 Host Req请求设备状态(Are you OK)
4S1F2 Equip Rep返回设备状态码(如 1 = online)
5S1F17 Host Send要求设备上线(Transition to online)
6S1F18 Equip Ack确认上线成功
7S1F15 Host Req查询当前设备是否 online
8S1F16 Equip Rep返回状态 1(online)
9S2F33/S2F35 Host Send注册、订阅事件
10S6F11 Equip Send设备开始报告事件
11S2F41 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 协议的本质是为了实现三件事:

  1. 事件驱动的数据收集(Event/Report)
  2. 设备状态感知(Status Variables)
  3. 远程命令控制(Remote Command)

所有的报文、定义、流程,最终都要服务于这三件事。


✅ 一、事件报告机制(CEID、RPTID、VID)

1. 概念说明:

  • CEID(Collection Event ID):事件编号,如“设备Ready”、“加工完成”
  • RPTID(Report ID):报告编号,用来定义这次事件触发时应该上报哪些内容
  • VID(Variable ID):变量编号,真正的“数据字段”,比如 LotID、RecipeName、Time

2. 报文流程:

步骤报文含义
1S2F33告诉设备:哪些事件会触发报告(CEID -> RPTID)
2S2F35告诉设备:每个报告包含哪些字段(RPTID -> VIDs)
3S6F11设备发事件,携带 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 / S1F2Inquire Device Status
状态读取S1F3 / S1F4主动读取状态变量(SVID)
远程命令S2F41 / S2F42主机对设备下达动作命令
事件注册S2F33 / S2F35告诉设备哪些事件触发哪些报告
事件上报S6F11设备主动上报事件
报文构成CEID / RPTID / VID事件编号 / 报告编号 / 变量编号
编程框架SECS4NET.NET 实现的通信框架,支持异步、事件注册
调试工具MessageLog查看报文内容、调试字段、断点调试
面试话术“三板斧”事件报告、状态变量、远程命令

【通关口诀】

给你编一个口诀,帮助记住 GEM 的三大核心流程:

连线问状态,注册等通告,发令才开跑。
建链:S1F13;
问状态:S1F3;
注册事件:S2F33 + S2F35;
接收通告:S6F11;
发命令:S2F41。


以上就是《第一讲 · 总结精华版》,可以直接背诵,也可以用来准备面试时的条理化答题。

如果你准备好了,我可以进入《第二讲:SECS4NET 项目结构详解,主机程序怎么搭出来的》。
说一声 “继续” 就开讲。


http://www.kler.cn/a/585786.html

相关文章:

  • 完善机器人:让 DeepSeek 使用Vue Element UI快速搭建 AI 交互页面
  • 【Linux系统编程】管道
  • 什么是mysql索引回表?
  • 杨辉三角形(信息学奥赛一本通-2043)
  • 智慧应急消防解决方案(35页PPT)(文末有下载方式)
  • doris:SQL 方言兼容
  • 【0x80070666】-已安装另一个版本...(Tableau 安装失败)
  • 裸机开发-GPIO外设
  • Android的第一次面试(Java篇)
  • 为什么 JPA 可以通过 findByNameContaining 自动生成 SQL 语句?
  • 如何在PHP中实现数据加密与解密:保护敏感信息
  • 小语言模型(SLM)技术解析:如何在有限资源下实现高效AI推理
  • 《CircleCI:CircleCI:解锁软件开发持续集成(CI)和持续部署(CD)高效密码》:此文为AI自动生成
  • Windows 上安装配置 Maven
  • WVP前后端部署
  • Java 大视界 -- Java 大数据分布式计算中的资源调度与优化策略(131)
  • 基于Python的物联网智慧农业数据采集与管理系统设计方案
  • 登录认证-登录校验-Filter
  • c++常用的算术生成算法
  • Kotlin apply 方法的用法和使用场景