简介这份资源以半导体制造领域通用的SECS/GEM通信协议为核心打包了名为JngHightSpeedSecs的完整工程项目包含核心通信库和基于对话框的示例程序SecsExampleJng。面向需要开发设备端通信模块的软件工程师、系统集成人员以及研究设备自动化协议的在校学生。压缩包共31个文件体积约722KB文件构成包括8个h头文件、4个cpp源文件、5个DLL动态库、2个LIB静态库、2个EXE可执行文件以及工程解决方案和资源文件等。其中头文件与源文件可查看SECS消息解析、GEM状态机等核心逻辑生成的DLL/EXE则能直接运行而不必自行编译便于快速验证。目前已有1102人学习下载代码经过长时间运行验证稳定性好命名规范注释清晰。通过阅读源码能够系统掌握HSMS底层通信、SECS-II消息编解码、Equipment状态管理、事件上报和远程命令处理等关键技术同时示例中给出的对话框程序也演示了如何将协议库嵌入实际应用。对希望摆脱黑盒方案、自主掌控设备互联可靠性的团队而言这份代码具有很强的参考价值。1. JngHightSpeedSecs 解决的从来不是「能连通」SECS/GEM 源码方案的真正战场JngHightSpeedSecs 这类 SECS/GEM 高速源码方案名字听起来像要把报文编解码做得飞快但干过封测设备对接的人都知道真正的战场压根不在编码速度。我见过太多次这种现场EAP 系统已经上线了HSMS 握手也显示成功结果第一条 S1F1 就卡在 T3 超时设备侧日志全是 Linktest 重传现场实施工程师对着一堆看不懂的十六进制报文无从下手。这个标题里反复出现的「源代码」其实是三类人的共同诉求设备软件工程师要把它集成进机台EAP 实施工程师要拿它做模拟器联调工厂自动化团队要读得懂、改得动、扛得住高速连续上报。下面按我自己的落地路径来拆先把协议栈的职责边界讲清楚再给一套能跑通的最小实现最后把会让人返工的坑挨个点一遍。2. 先把四层协议拆开再读 SECS/GEM 源代码E4/E5/E30/E37 各管什么拿到任何一套 SECS/GEM 源代码第一件事不是打开编辑器而是先分清楚标准体系。半导体行业说的「SECS/GEM」不是单一协议而是四层标准的组合E4 定义了 SECS-I 的串行物理层E5 定义了 SECS-II 的消息格式与数据项E37 定义了基于 TCP/IP 的 HSMS 传输层E30 定义了 GEM 设备模型。JngHightSpeedSecs 这类高速实现通常只做 E37 加 E5再在上层挂一个 E30 的状态机骨架。如果你拿到源码后直接去读「处理消息」的函数很容易被绕晕因为你会看到一堆回调互相调用却不知道谁在驱动谁。我习惯的阅读路径是反过来的先看最底层 socket 收发与长度字段的解析再看 HSMS 的 Select/Linktest 状态迁移然后才进到 SECS-II 的 TLV 编解码最后才看 GEM 层的事件上报和设备模型。每一层都在解决不同的问题混在一起读必踩坑。2.1 SECS 是单向还是双向链路、会话与事件上报要分开回答网上经常有人问「secs 是单向的还是双向的」这个问题如果不分层回答永远说不清。物理链路上HSMS 是标准 TCP 全双工通道数据可以同时在两个方向流动但在消息语义上SECS-II 是典型的请求/响应模型一条 Primary 消息发出后对端必须回一条 Secondary 消息否则发送方会一直等到 T3 超时。与此同时设备又可以用 S6F11 这类事件消息主动向主机上报报警和状态变化不需要等主机先问。这个「双向链路、主从会话、独立上报」的结构直接决定了你怎么读源码。高速源代码里必然存在两条队列一条是「请求/回复」队列保存每个 System Bytes 对应的等待者另一条是「主动上报」队列设备侧产生事件时直接走这条通道。如果源码里把这两种消息混在同一个状态机里那么高频上报时很容易把回复消息挤出缓冲区出现「链路活着但业务全断」的怪象。我一般拿到代码先搜 System Bytes 的分配逻辑看它是全局自增还是每会话独立这一步能快速判断作者有没有认真处理并发。SECS-II 的 System Bytes 相当于每次会话的事务号Primary 消息发出时带上这个号对端的 Secondary 回复必须原样带回。高速方案里这个字段也承担着乱序排查的功能所以源码里事务号的分配通常是原子操作多线程实现里这一处往往就是锁竞争的热点。2.2 HSMS 的 Active/Passive 与 5000 端口源码里 connect/reconnect 在忙什么HSMS 传输层定义了两种角色Active 方主动发起 TCP 连接Passive 方监听端口等待连接。工厂里最常见的配置是设备侧做 PassiveEAP 或 MES 做 Active端口默认是 5000也有工厂规范固定要求 5001实施前先找设备厂商确认不要想当然。选型理由很朴素设备侧 IP 可能会被网络管理员改动让 EAP 主动去连改配置只动一边设备侧做 Passive 也符合安全策略不开放出方向连接。源码里 connect/reconnect 这一块往往被低估。TCP 断链后Active 侧要按退避策略重连同时要清掉上一轮会话遗留的事务号Passive 侧要处理旧连接的 TIME_WAIT避免端口被占用导致重启后监听失败。我见过不少代码把 reconnect 写成死循环 while True中间完全没有退避结果设备侧反复快速重连把 EAP 的 accept 队列打满反而连不上去。在做选型验证时我一般先用两个命令确认网络通路再去翻协议栈源码# 确认 Passive 设备侧的 5000 端口在监听 nc -vz 192.168.1.10 5000 # 连上后看 TCP 会话是否建立 ss -tn | grep 5000这两个命令能区分「网络不通」和「协议不通」避免一上来就陷入抓包分析。如果 nc 能通但 HSMS 一直 Select 失败问题一定在协议层而不是链路层。2.3 SECS-II 数据项编码为什么同一个数值会被放大 256 倍SECS-II 的消息体是一串 TLV 结构的嵌套数据项流号、功能号各占一个字节再加上 W-bit 和消息体。其中数值类型最容易翻车的地方是字节序标准规定多字节数值按大端序传输也就是高位字节在前。但在 x86 这种小端主机上写源代码如果直接拿内存里的整型去填报文不做字节序转换就会出现非常经典的「数值放大 256 倍」问题。举例U2 类型的数据项代表一个 16 位无符号整数发 256 时正确的大端字节是0x01 0x00如果发送方用小端打包出来的字节是0x00 0x01接收方按大端解出来的值是 1。反过来如果本地值是 1按小端发出去接收方读到的就是 256。这种错位在现场的表现是「温度读数翻了几百倍」或者「压力值变成负数」极其容易让人怀疑是传感器问题实际是统一设备语言没做好。import struct # SECS-II 中的 U2/I2/U4/I4 按大端序排列 raw struct.pack(H, 256) # 正确0x01 0x00 value struct.unpack(H, raw)[0] print(value) # 256 # 反例x86 小端主机直接打包收发双方就会错位 raw_native struct.pack(H, 256) # 本机输出 0x00 0x01 print(struct.unpack(H, raw_native)[0]) # 解析成 1这段代码说明了大部分 SECS/GEM 源码里pack与unpack函数存在的意义它们不是性能瓶颈而是协议正确性的守门员。读源码时留意所有struct.pack调用有没有带或!前缀凡是忘带的基本都在等着现场翻车。3. 在本地跑通最小 HSMS 会话从 Select 握手到 S1F1 上报的完整代码这一章直接进入正题如何让一套 SECS/GEM 源代码在你的开发机上把最小会话跑起来。目标不是实现完整 GEM 功能而是验证协议栈的骨架——Select 握手、心跳、一条 SECS-II 数据消息。我习惯用 Python 先做一个最小端点把协议流程走通之后再对照手里的 C/Go 源码这样无论是读代码还是改 bug都有个可参考的行为基准。3.1 源码阅读顺序先找测试和示例再碰协议栈拿到源代码包后我一般先扫一眼目录里的examples和test文件夹用这两块来反推作者设计的用法。示例代码会告诉你消息发送和接收的入口长什么样测试代码会暴露作者对边界情况的处理态度。如果测试里没有覆盖 Select 失败重连、半包、字节序错位这几种场景那这套代码的成熟度就要打个问号。接下来按依赖方向读先读消息头结构体和长度字段解析再读 HSMS 状态机然后看 SECS-II 的编解码器最后才是 GEM 的回调注册。很多人一上来就盯着 Event 上报的代码看结果连消息头都还没看懂遇到问题根本不知道是传输层丢了包还是应用层没处理。3.2 最小 Passive 端点用 Python 处理 Select.req 与 Linktest.req下面这段代码实现了一个最简 HSMS Passive 端点监听 5000 端口处理 Select.req 和 Linktest.req 两种控制消息并对收到的数据消息打印流号和功能号。连 SELECT 状态都可以不做完整校验够把流程跑通。import socket import struct def build_header(session_id: int, msg_type: int, system_bytes: int, s_fNone): 按 E37 拼一个 10 字节 HSMS 消息头 h bytearray(10) struct.pack_into(H, h, 0, session_id) # 2 字节会话 ID h[2] msg_type # 消息类型 0x00-0x0A struct.pack_into(I, h, 4, system_bytes) # 4 字节 System Bytes if s_f: h[8], h[9] s_f[0], s_f[1] # Stream / Function return bytes(h) srv socket.socket(socket.AF_INET, socket.SOCK_STREAM) srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) srv.bind((0.0.0.0, 5000)) srv.listen(1) print(Passive HSMS listening on 5000) conn, addr srv.accept() print(connection from, addr) system_id 0 while True: # 4 字节长度字段不代表消息头长度而是整帧剩余字节数 length struct.unpack(I, conn.recv(4, socket.MSG_WAITALL))[0] data b while len(data) length: data conn.recv(length - len(data)) h data[:10] msg_type h[2] system_id struct.unpack(I, h[4:8])[0] if msg_type 0x01: # Select.req rsp build_header(struct.unpack(H, h[:2])[0], 0x02, system_id) conn.sendall(len(rsp).to_bytes(4, big) rsp) elif msg_type 0x05: # Linktest.req rsp build_header(struct.unpack(H, h[:2])[0], 0x06, system_id) conn.sendall(len(rsp).to_bytes(4, big) rsp) elif msg_type 0x00: # 数据消息 st, fn h[8], h[9] print(fdata msg S{st}F{fn}, body{data[10:].hex()})这里说明几个关键点。第一长度字段是整个 HSMS 帧从消息头开始到消息体结束的字节数不包含长度字段本身的 4 个字节第二接收时要用MSG_WAITALL保证读满 4 字节否则半包会把后续数据错位第三Select 响应和 Linktest 响应都没有 Stream/Function消息头最后两字节填 0 即可。这段代码能帮你验证对端 EAP 的握手逻辑也能当调试桩来用。3.3 组装一条 S1F1W-bit、System Bytes 与报文头回填控制消息跑通后接着验证数据消息。S1F1 是 SECS/GEM 的「Are You There」相当于应用层的保活探测。设备收到 S1F1 必须回 S1F2这是所有 EAP 联调的第一条测试用例。S1F1 的请求体为空message body 就是 0 字节整帧只有消息头。def build_s1f1(session_id: int, system_id: int): # W-bit 置 1要求对端回复。W-bit 放在 Function 字节的最高位 header build_header(session_id, 0x00, system_id, (0x01, 0x01 | 0x80)) frame header # S1F1 请求体为空 return len(frame).to_bytes(4, big) frameW-bit 是 SECS-II 里最容易忽略的标志位。置 1 表示这是一条 Primary 消息发送方在等待回复置 0 则表示发送方不等待回复。如果你在写设备侧应答逻辑时把 W-bit 漏了EAP 发来 S1F1 后设备虽然回了 S1F2但 EAP 侧会因为你回的 S1F2 路线不对而判超时。另外S1F2 的 System Bytes 必须原样带回 S1F1 里的值否则对端无法把回复和请求关联起来。在实际源码里System Bytes 的分配一般在发送函数内部完成不要在业务层手动传值。高速高并发的场景下多个线程同时发消息如果 System Bytes 在业务层手写很容易重复导致回复匹配错乱。3.4 先配好超时和日志T3/T6/T7/T8 联调前要对齐HSMS 和 SECS-II 有一组定时器参数联调前必须两分钟之内对齐否则后续所有排障都是猜谜。下面这张表是我每次项目启动都会拉出来对一遍的定时器常见值作用出问题时的现象T345 秒等待 SECS-II 回复的超时第一条请求就报超时链路却好好的T65 秒等待 Select.rsp 的超时握手一直失败重复 Select.reqT710 秒TCP 连接建立的超时连不上端口但 ping 是通的T85 秒网络传输字节间的超时高频收发时连接被对端断开定时器值本身不重要重要的是两边必须一致。设备侧把 T3 设成 30 秒EAP 侧设成 60 秒一旦设备响应慢了EAP 还能等但设备侧已经判定超时并开始重传两边节奏就乱了。我一般会把这张表写进联调备忘的第一页。日志方面我要求任何 SECS/GEM 代码的收发路径都打印五个字段时间戳到毫秒、方向、消息类型、System Bytes、Stream/Function。不要在数据体上花太多精力但事务号必须打出来否则排查乱序和丢消息时没有任何后悔药。4. GEM 状态模型与 EAP 对接Offline/Online 切换如何落实成回调HSMS 和 SECS-II 解决的是「消息能不能送到」的问题GEM 解决的是「设备有没有进入正确的工作状态」的问题。EAP 判断一台设备是否可用第一眼看的不是 TCP 连接而是设备当前的控制状态OFFLINE、ONLINE_LOCAL 还是 ONLINE_REMOTE。源代码里如果只写了编解码没有写状态机那这套代码顶多算个报文工具不能叫 SECS/GEM 解决方案。4.1 GEM 不等于事件上报控制状态机才是 EAP 认人的依据GEM 设备模型包含控制状态、处理状态、报警、事件、配方管理等多块内容。其中控制状态机是 EAP 侧设备列表中显示「在线/离线」的依据。设备上电后默认处于 OFFLINE此时不响应任何远程命令进入 ONLINE_LOCAL 后允许本地面板操作进入 ONLINE_REMOTE 后完全接受 EAP 的调度。这三态之间的迁移在代码里最好实现成显式的状态迁移函数而不是在回调里到处state xxx散写。散写状态的问题在于当状态非法跳转时你没有任何拦截点等到 EAP 侧发现设备行为异常日志已经刷过去几百条根本无从追溯。4.2 设备侧三个必做入口S2F17/18、S2F41/42 与 S6F11EAP 联调时设备侧无论如何都要实现三个入口。第一个是 S2F17/18主机用它把设备切到 Online 或 Offline设备收到后执行动作并回复 S2F18第二个是 S2F41/42主机用它下发远程命令比如 Start、Stop执行结果在 S2F42 里返回第三个是 S6F11/12设备主动上报事件EAP 收到后回 S6F12 确认。这三个入口的好坏直接决定联调周期长短。写这三个入口时我一般会先用协议库的编码原语做 body 组装避免手算格式字节# 示意用协议库的编码原语拼 S6F11 事件上报 body secsii.list([ secsii.u4(event_id), # 事件 ID例如 1001 secsii.list([ secsii.ascii(SLOT1), # 数据项 1槽位名 secsii.u4(25) # 数据项 2实际数值 ]) ]) # 底层会负责格式字节、长度字段和数据项编码这里强调「示意」的原因在于不同源码库的编码接口差异很大但结构都是一致的列表嵌套、数据类型明确、长度自动计算。你只需要把业务数据组织成列表剩下的交给协议栈。如果源码要求你手写二进制 body那就要格外小心格式字节的位定义稍有偏差对端解析出来就是一个乱七八糟的列表。4.3 把状态迁移写成显式代码一张迁移表和它的测试下面这段代码展示了我习惯的状态机写法。核心是把所有合法迁移集中在一张表里任何非法迁移都会抛出异常便于在测试环境第一时间暴露问题。from enum import Enum class ControlState(Enum): OFFLINE 1 ONLINE_LOCAL 2 ONLINE_REMOTE 3 def transition(cur, new): # 大多数实现的保守做法OFFLINE 必须先经过 LOCAL不能直升 REMOTE allowed { ControlState.OFFLINE: {ControlState.ONLINE_LOCAL}, ControlState.ONLINE_LOCAL: {ControlState.OFFLINE, ControlState.ONLINE_REMOTE}, ControlState.ONLINE_REMOTE: {ControlState.ONLINE_LOCAL, ControlState.OFFLINE}, } if new in allowed[cur]: print(f{cur.name} - {new.name}) return new raise ValueError(fillegal transition: {cur.name} - {new.name}) state ControlState.OFFLINE state transition(state, ControlState.ONLINE_LOCAL)这套写法的好处有三个。第一迁移规则肉眼可读联调时直接给 EAP 工程师看这张表比解释一堆 if 判断高效得多第二非法迁移会在测试阶段就报错而不是等到现场设备行为异常才暴露第三将来要扩展新状态只需改这一处不会漏掉某个入口。设备侧收到 S2F17 时只要调用transition(state, ControlState.ONLINE_REMOTE)即可状态变化后统一触发回调通知下层驱动执行动作。4.4 高速收发的线程模型事件循环比每消息一线程更可控高速场景下的线程模型我踩过不少坑最终的结论是单线程事件循环加双队列比每来一条消息就开一个线程要稳定得多。每条消息开线程的方案在低频时没问题但 EAP 压测时一秒钟涌入几百条 S6F11线程切换和锁竞争会直接把 CPU 打满而且日志里线程 ID 各不相同排障时一片混乱。常见做法是接收线程只负责把完整消息按 Session ID 分发到对应设备对象的消息队列真正处理消息的 worker 是单线程循环。发送侧用一个带锁的发送队列所有线程要发数据就往队列里丢由发送线程统一写 socket。这样做日志清晰状态机不会并发乱跑锁竞争被限制在队列边界上。对于「JngHightSpeedSecs」这类强调高速的源码重点就查它的队列深度和背压处理不需要看业务算法。5. SECS/GEM 对接避坑5 个让现场返工的典型问题实操得越多越能体会一个道理SECS/GEM 对接只要跑不通八成不是协议本身的问题而是细节没对齐。下面这五条全是我在现场或模拟联调里真实见过的翻车现场按「现象、原因、解决」三段式写清楚帮助大家少走弯路。5.1 握手成功但第一条 S1F1 就 T3 超时先查长度字段和粘包处理现象Select 握手成功EAP 侧状态显示连接已建立但随后发出的 S1F1 一直收不到 S1F2直到 T3 超时。原因最常见的是 TCP 粘包半包处理不严谨。接收方只调了一次 recv以为能一次收完整帧实际上数据可能分两段到达或者两帧连在一起到达导致解析错位。其次是 S1F1 的 W-bit 没有置位设备侧认为这是不需要回复的通知消息。解决先抓包确认设备到底有没有回 S1F2。如果抓包里没有回包检查设备侧对 W-bit 的处理如果回包存在但 EAP 没收到检查接收逻辑确认长度字段是否读满 4 字节、消息体是否按长度字段循环读取。代码里所有 recv 调用都要用MSG_WAITALL或循环读满不能赌网络包不会拆。5.2 设备已上报 S6F11EAP 却收不到问题多半在状态机与事件使能现象设备日志明确显示 S6F11 已经发出EAP 侧毫无告警也没有回复 S6F12。原因设备侧虽然发得出消息但控制状态机不在 ONLINE_REMOTEEAP 按 GEM 规范会忽略非 Remote 状态下的事件上报。另一个原因是事件使能未配置GEM 要求事件 ID 先被使能未使能的事件不会被发送。解决联调阶段先把控制状态强制切到 ONLINE_REMOTE再验证事件上报。事件使能则检查设备配置里的事件列表确认目标事件 ID 已加入使能集合。用模拟器订阅同一事件如果模拟器能收到而 EAP 收不到问题一定在 EAP 侧的过滤规则这招能把责任边界划清楚。5.3 高频发送一压就断T8 设太短加上 TCP_NODELAY 没开现象单消息收发完全正常一旦按生产频率持续发送几十秒后连接被对端断开日志里出现 Linktest 超时。原因T8 定时器用来限制同一帧内相邻字节的最大间隔如果发送方开了 Nagle 算法小报文会被延迟合并TCP 栈迟迟不把字节推出去对端就会以为链路异常。加上发送线程本身在锁竞争里耗时T8 设到 2 秒的话很容易误判。解决socket 上显式设置TCP_NODELAY关闭 Nagle 合并让每个消息头尽快推送到对端同时把 T8 对齐到常见值 5 秒。发送侧保证同一时刻只有一个线程写 socket避免多线程交错写导致字节流错乱。这一套做下来绝大多数「一压就断」的问题都能消除。5.4 设备重启后状态不同步TCP 重连不等于 GEM 重连现象设备重启后 HSMS 自动重连成功但 EAP 界面上设备一直灰显手工操作一次才恢复。原因HSMS 重连只保证了 TCP 链路恢复GEM 的控制状态还停留在设备重启前的快照里。EAP 侧认为设备仍处于之前的 ONLINE_REMOTE但设备侧重启后回到 OFFLINE两边状态不一致。解决设备侧 HSMS 重连成功后主动发送 S1F13Establish Communications Request收到 S1F14 成功响应后再把控制状态置为 ONLINE_REMOTE。同时把当前控制状态持久化到本地存储启动时先恢复再等主机确认。源码里如果重连逻辑只处理了 Select 而没有 S1F13这是明显的半成品需要补全。5.5 Session ID 方向位被当成数据位设备消息被对端静默丢弃现象设备发给主机的消息在抓包里看着没问题但主机侧把所有消息都判为非法会话一条不落地丢弃。原因E37 规定 Session ID 的 16 位中最高位被用作方向标记实际有效会话 ID 只有 15 位。设备侧的源码如果没对方向位做处理把会话 ID 直接传入消息头主机会因为方向位不符合预期而拒收。解决读源码时搜索所有涉及 Session ID 的位操作确认有没有用到掩码0x7FFF或0x8000之类的常量。发送和接收两边对方向位的处理必须对称这一处最隐蔽——普通调试时主机可能不校验方向位但换成严格的上位机就立刻暴雷。建议在代码注释里写明方向位偏移防止后人改坏。6. 把「高速」真正压出来1 个压测脚本、3 个性能边界与日志分析习惯标题里的「HighSpeed」不是宣传词它应该能被量化单连接每秒处理多少条 SECS-II 消息、99% 延迟是多少、连续高频上报时心跳是否稳定。我一般会在交付前跑一轮这样的压测而不是等到现场 EAP 上线再暴露问题。先给一个最简单的压测脚本用 Linktest.req 测量对端的响应延迟import socket import struct import time import statistics sock socket.create_connection((127.0.0.1, 5000), timeout3) latencies [] for i in range(1000): t0 time.perf_counter() # 帧头4字节长度 10字节 HSMS Header类型为 Linktest.req frame b\x00\x00\x00\x0a b\x00\x01 bytes([0x05, 0x00]) \ i.to_bytes(4, big) b\x00\x00 sock.sendall(frame) resp sock.recv(14) # 响应帧同样是 14 字节 latencies.append((time.perf_counter() - t0) * 1000) print(avg RTT: %.3f ms, P99: %.3f ms % ( statistics.mean(latencies), sorted(latencies)[989]))压测时看三个边界吞吐量峰值在多少条每秒开始明显延迟抬升P99 延迟是否随消息量线性恶化以及长时间高频发送后 Linktest 是否还稳定返回。延迟分布出现长尾时优先查日志时间戳里相邻两条消息的间隔如果间隔忽大忽小多半是发送侧在锁竞争或 GC而不是网络问题。日志分析是我个人最看重的习惯。每一条收发的日志都要带毫秒时间戳、System Bytes、消息方向和流功能号压测结束后把日志按 System Bytes 排序可以轻松还原每一条消息的完整链路。这个习惯让我在半年前一次现场排障里只花了十分钟就定位到问题——设备侧发送线程被业务回调阻塞导致 S6F11 延迟 200 毫秒EAP 端 T3 设置又太短看起来就像随机丢消息实际上全是延迟。早期我做设备对接时只关心「能不能连通」后来被 T3 超时折磨了一下午才明白SECS/GEM 的代码能不能落地要看它对边界条件的处理——半包怎么收、超时怎么退避、断线怎么重连、状态怎么恢复。读源代码时优先看这几个地方比盯着一堆业务回调有价值得多。希望这些经验能帮你在下一次对接里少熬夜。本文还有配套的精品资源点击获取
企业数字化 ERP 产品动态
相关推荐
从零开发企业内部CRM系统:技术选型、权限设计与性能优化实战 1. 先说清楚:DeskcommCRM 到底解决什么问题我第一次接触 DeskcommCRM 这个项目的时候,团队里其实已经有一套“用 Excel 管理客户”的流程了。听起来很离谱对吧?但小团队、销售型公司、初创项目,这类场景里 Excel 管理客户反而是常… · 2026/9/25 6:51:00
Atlas 300V推理加速卡实战:从环境配置到YOLOv5模型部署与调优 1. 初见Atlas 300V:先把这张卡的定位搞清楚如果你最近在搜索框里敲过“atlas 300v 24g 是运算加速卡吗”,大概率是被昇腾生态的命名绕晕了。我第一天拿到Atlas 300V 24G时也带着同样的疑惑——它到底算不算运算加速卡?和市面上常见的训练卡有… · 2026/9/25 7:25:10
ESP32上运行WebAssembly:解释器原理、实操与性能实测 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 7:25:10
Atlas 300V加速卡部署YOLOv5全指南:从驱动、CANN到模型转换 之前手头有一块 Atlas 300V 24G,很多第一次接触昇腾计算卡的朋友第一反应都是:这玩意儿到底是不是显卡?能不能像 GPU 那样直接跑 YOLO?我最近刚好把一套 YOLOv5 检测服务完整迁移到 Atlas 平台上,从驱动固件、CANN 工具… · 2026/9/25 7:25:10
TypeScript 类型谓词(Type Predicates)完全指南:从自定义窄化到 5.5 自动推断 文档教程 【免费下载链接】typescript-book The Concise TypeScript Book: A Concise Guide to Effective Development in TypeScript. Free and Open Source. 项目地址: https://gitcode.com/gh_mirrors/typ/typescript-book 点击查看 免费下载 类型谓词ÿ… · 2026/9/25 7:25:04
OpenChamber Text 模块解析:本地文本清洗与摘要回退机制实战指南 AI Agent人工智能代码智能体交互助手 【免费下载链接】openchamber Agentic Development Environment based on OpenCode AI agent 项目地址: https://gitcode.com/gh_mirrors/op/openchamber 点击查看 免费下载 导读
OpenChamber 的 packages/web/server/lib/tex… · 2026/9/25 7:25:04
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37