干半导体设备这行的人想必都绕不开SECS/GEM。你辛辛苦苦把设备机械结构调好了电气接线也跑通了结果客户现场说设备必须接入MES系统需要支持SECS/GEM协议这个时候再从头啃协议栈往往是一个头两个大。我之前用C# .NET做过几个项目的设备上位机从最开始对着协议文档发懵到最后用secs4net库顺利跑通和MES的联调中间踩了不少坑也沉淀了一些经验。这篇博文就把我的实操配置过程、核心代码逻辑以及避坑细节完整梳理一遍希望能给正准备上手SECS/GEM开发的朋友一些参考。这篇文章适用于以下人群设备端上位机开发工程师、准备做EAPEquipment Automation Program对接的软件工程师以及对半导体设备通信协议感兴趣、想快速跑通一个Demo的技术爱好者。文章会聚焦HSMS通信方式这也是目前半导体设备接入MES最主流的连接方式。我会从环境准备讲起逐步拆解secs4net的核心配置、报文收发机制、GEM标准行为实现最后把联调过程中遇到的典型问题列成一张排查表。1. 开发背景与整体认知1.1 SECS/GEM到底是什么SECSSEMI Equipment Communications Standard是半导体设备通信标准的统称它实际上分了两层SECS-I定义了基于串口RS-232的物理传输规则而SECS-II负责定义消息内容和数据结构。现在工厂里绝大多数设备都走HSMSHigh-Speed Message Service方式本质上是把SECS-II的消息封装后通过TCP/IP传输因此开发时可以直接把HSMS理解为“基于TCP的SECS通信通道”。而GEMGeneric Equipment Model则是在SECS通信之上定义了一套设备行为标准它规定了设备必须具备哪些状态、如何报告事件、如何响应远程命令。打个比方SECS像是“语言”GEM则像“行为契约”——你要能和MES对话就得遵守这套语言规则而且该有的状态上报、报警处理这些动作一样不能少。明白了这个层级关系后面写代码时你就知道哪些东西是传输层的哪些是消息层的哪些又是设备行为层的越早梳理清楚后边Debug越省力。1.2 为什么选secs4net这个库开发SECS/GEM驱动可以完全从零手写TCP报文解析但这样做的工作量非常大而且容易在一些边界细节上出错。比如Header的Session ID解析、Message Length的计算、SECS-II消息的Item嵌套规则任何一个字节不对都会导致对端解析失败。而成熟的第三方库可以帮助我们屏蔽掉大量底层细节让我们专注业务逻辑。我选择secs4net库的原因主要有三点它足够轻量核心功能就是HSMS通信加SECS-II消息编解码没有一堆用不上的冗余配置。它支持.NET Standard 2.0意味着在.NET Framework 4.6.1以上的老项目和.NET Core/.NET 5新项目里都能用兼容性非常友好。它的API设计相当直观封装好了Connection、Message、Item模型上手写代码的曲线比较平缓。当然这里也要说一句公道话直接基于裸Socket自己实现HSMS协议也有它的好处比如对协议内部的控制逻辑会有更深的掌控感遇到非常规需求时也更好改。但从项目交付的角度看secs4net能够让我们把主要精力放在设备业务逻辑和GEM行为实现上性价比很高。1.3 开发前的环境准备清单在开始写代码之前先把开发环境整理好可以避免后续联调时出现各种“低级但致命”的问题开发环境Visual Studio 2022建议安装“.NET桌面开发”工作负载同时确认安装了.NET 6.0 SDK或更高版本如果项目是老设备工控机也可以选择.NET Framework 4.8secs4net同样支持。依赖包通过NuGet安装secs4net。在NuGet包管理器里搜索“secs4net”即可注意要认准“Chenhsuan”作者发布的那个版本避免选错第三方仿制的同名包。联调工具准备一个SECS/GEM模拟器。可以用免费的SECS Simulator工具也可以用secs4net库自带示例来模拟Host端后面我会讲讲如何搭建一个最简联调环境。协议文档手边备一份SEMI E37HSMS、SEMI E5SECS-II、SEMI E30GEM标准文档。虽然secs4net帮我们处理了解析细节但你会经常需要查询某个SINFStream/Function的消息格式和状态模型定义。提示secs4net库本身不包含GEM状态模型的高层封装它更侧重于通信层和消息编解码。所以GEM标准中要求的设备状态、事件上报这些逻辑需要我们基于secs4net的消息收发能力自行组织实现。这个设计其实更灵活也更符合不同设备厂的实际情况。2. HSMS通信核心配置2.1 连接参数的完整解析secs4net库与HSMS通信最相关的就是HSMS连接配置类需要理解每一个参数的含义而不是直接照抄默认值。典型的配置代码如下var config new HsmsConnectionConfig { // 设备ID即Device ID也就是常说的Session ID DeviceId 0, // 目标主机IP也就是EAP/Host端的IP地址 IpAddress 192.168.1.100, // 目标端口标准的HSMS端口是5000 Port 5000, // 连接模式 IsActive true, // 链路测试间隔秒 LinkTestInterval 60, // 连接超时时间秒 ConnectTimeout 30, // 接收超时时间秒 ReceiveTimeout 30 };这里面有几个非常容易踩坑的地方DeviceId设备ID这是设备侧的会话ID。你的设备在MES系统中会有一个对应的编号联调时必须和EAP侧配置一致。如果EAP配置的DeviceId与你不一致消息虽然可以发送但EAP可能会直接丢弃或视为无效连接。这一项几乎是联调时第一个排查点。IsActive主动连接/被动监听设备作为客户端主动连接EAP还是EAP主动连接设备半导体工厂里大多数情况是设备主动向EAP发起连接也就是IsActivetrue。如果是EAP主动连接设备你需要将设备端配置为被动模式并开放相应端口监听。这个方向如果搞反连接永远建立不起来。联调之前先跟对方工程师确认清楚能省很多时间。LinkTestInterval链路测试间隔HSMS规范要求在空闲状态下定时发送链路测试消息T5测试默认间隔通常设置为60秒。如果间隔太短会增加无谓的网络开销如果太长可能导致防火墙将空闲连接切断。有些现场的IT策略会把长连接空闲连接回收切断此时需要针对IP和端口做白名单。2.2 最小可运行代码从建立连接到上线配置写好了接下来就是创建一个连接实例连接到EAP并保持通信。这里给出一个最小可运行的示例using Secs4Net; using Secs4Net.Hsms; // 先建立连接管理器 var config new HsmsConnectionConfig { DeviceId 0, IpAddress 192.168.1.100, Port 5000, IsActive true, LinkTestInterval 60 }; // 创建连接 using var connection new HsmsConnection(config); // 订阅消息接收事件 connection.MessageArrived OnMessageArrived; // 启动连接 await connection.OpenAsync(CancellationToken.None); Console.WriteLine(HSMS连接已建立);这里的OpenAsync会执行主动连接动作。注意secs4net内部有自动重连机制如果连接意外断开它会在后台不断尝试重新建立连接。这个机制在做无人值守的设备端软件时非常重要不用我们自己写断线重连逻辑。打开连接只是第一步SECS连接建立之后需要执行握手流程才能上线通信。这个握手就是S1F13Establish Communication Request设备收到S1F14消息后联机流程才算完成。在secs4net中你收到S1F14回复之后才能认为整个通信通道“真正可用”。2.3 日志体系搭建经验SECS/GEM联调过程中日志的重要性不亚于通信本身。你无法在EAP侧随时打日志很多时候只能靠设备侧日志来定位问题。我在项目里通常会搭建两套日志一套是通信层日志记录原始的收发报文。secs4net库内部有日志输出机制接入到ILogger之后就可以在日志文件中看到类似“Sent: S1F1”这样的记录。这样能够非常直观地看到报文是否正常收发、消息内容是什么。另一套是业务层日志记录每个消息的处理结果和业务动作。比如收到S2F17启动上传之后设备是否成功创建了数据收集请求采集是否启动等。这套日志需要覆盖到具体的业务流程。经验联调回到现场前先把这两套日志全部打开并确保日志文件带时间戳和消息方向标识。这样做有一个非常大的好处一旦出现莫名其妙的问题第一件事不是推测而是翻日志看是哪一层断了。3. SECS-II消息编解码与实操3.1 用SML语法理解消息结构SECS-II消息是按Stream和Function组织的例如S1F1表示Stream 1 Function 1通常被称为“Are You There”表示询问设备是否存在。S1F2是对S1F1的应答。secs4net通过PrimaryMessage和ReplyMessage来区分这组消息类型通过内置的Message类和Item类来构建SECS-II数据结构。与直接用字节数组逐段构造不同secs4net让我可以使用更接近SMLSECS Message Language语法的方式来构建消息。比如构建一个简单的S1F1消息var areYouThere new PrimaryMessage(1, 1, Item.Empty());这里Item.Empty()表示空参数列表。S1F1本身不需要携带额外参数发出去就是一个标准的轮询消息。看似简单但很多SINF消息的数据结构嵌套非常复杂比如S6F11Event Report Data Message里面会嵌套多个DataItem数据项每个数据项又是由多层的List组成的。只要嵌套层级错了一层EAP端解析出来的结果就会错位甚至会直接报消息格式错误。3.2 Primary与Reply的消息处理机制在secs4net中发送一个Primary消息后库内部会等待Reply消息的返回并自动匹配对应的Transaction。这种机制是SECS协议的核心特征之一消息的每个请求必须有对应的应答消息才能算完成一次事务。// 发送S1F1并等待S1F2回复 var reply await connection.SendAsync(s1f1Message, CancellationToken.None); if (reply.Function 2) { Console.WriteLine(设备在线回复 reply.SecsItem); }这里有一点很关键SendAsync必须等收到Reply或者超时后才返回。如果EAP端在规定时间内没有应答就会抛出超时异常。在实际项目中你需要为不同的消息设置合适的应答超时时间特别是涉及设备执行动作的指令比如S2F17数据采集启动由于设备可能需要几秒甚至更长时间来响应超时时间设置得过短会导致误报。不过在当前版本的secs4net里超时时间是在连接层面的全局配置没有精细到单独每条消息的超时控制需要你在业务逻辑层处理长耗时消息和超时之间的关系。另一个容易忽略的点是回报消息的处理。无论设备是否成功处理了一个Primary消息都应该以规范要求的方式回发Reply。比如收到S2F17就应该回S2F18并通过参数中的EACEquipment Acknowledge Code字段向EAP传递“成功”“拒绝”等信息。如果不知道该怎么回就回一个最简单的0Accept总比不回复强。3.3 常见消息处理S1F1、S1F13、S2F17、S6F11等实际开发中有一些SINF消息是几乎每个设备都会用到的以下是我在项目中处理得最频繁的几个S1F1/S1F2设备在线检测。这条消息在建立连接后就会被EAP周期性发送用来确认设备是否还“活着”。收到S1F1后设备回复S1F2即可回复内容通常需要包含设备制造商、设备型号、软件版本等信息。格式大致是var reply new ReplyMessage(1, 2, Item.L( Item.A(MyEquipment), // Manufacturer Item.A(Model-X), // Model Item.A(1.0.0), // Software Version Item.A(SN00123) // Serial Number ));S1F13/S1F14建立通信请求。这是设备上电建立连接后的关键握手。设备收到S1F13后如果确认支持SECS/GEM就在S1F14中返回COMMACK0同时附带设备支持的通信能力信息例如版本和支持的功能集合。S2F17/S2F18数据采集启动与应答。这条消息用于启动数据收集它带有Data ID等参数。收到之后需要回一个S2F18给EAP并在S2F18中把Data ID原样返回再加上等长的EAC码表示启动是否成功。需要注意的是在secs4net中返回数据时不要忘了将原始Data ID一并带回去这对EAP做数据关联很重要。S6F11事件报告发送。设备主动上报异常、产品完成等信息时使用。它的结构较为复杂大概格式是var s6f11 new PrimaryMessage(6, 11, Item.L( Item.U4(reportId), Item.L( // DataItem列表 Item.L(Item.U4(1), Item.U4(2), Item.U4(3)) ) ));S6F11发出去之后EAP会回复S6F12确认。如果是非请求上报设备只需要发S6F11不必等待EAP的周期性询问。4. GEM状态模型与事件上报实现4.1 状态模型的核心理解GEM标准要求设备具备一套标准的状态模型从初始化到联机再到执行生产每一步都有明确的状态。常见的关键状态有EQUIPMENT_OFF设备断电或未初始化。EQUIPMENT_INIT设备正在初始化此时不能执行生产动作。EQUIPMENT_IDLE设备空闲生产还没有启动。EQUIPMENT_READY设备已就绪可以开始生产。EQUIPMENT_PROCESSING设备正在执行生产流程。EQUIPMENT_END生产流程结束设备回到Idle或者Ready。这套状态模型的意义在于让EAP系统随时知道设备当前在干什么、能不能接受新的生产任务。毕竟MES控制系统不可能和每台设备人工确认状态它需要靠设备主动上报状态切换事件来更新整个工厂的生产调度。secs4net没有内置这个状态机的实现所以你需要自己设计。别担心这个状态机并不复杂核心做法就是用枚举定义状态在设备业务流程的关键节点触发状态迁移并发送对应的事件消息给EAP。比如在设备完成初始化准备进入Idle状态时通过S6F11发送一个状态变更事件private async Task ReportStateAsync(string equipmentState) { var stateItem Item.L( Item.A(EquipmentState), Item.A(equipmentState) ); var primary new PrimaryMessage(6, 11, Item.L( Item.U4(101), // 事件ID Item.L(stateItem) // 数据项 )); await connection.SendAsync(primary, CancellationToken.None); }4.2 设备常量、数据变量与事件上报GEM标准定义了三类核心数据对象理解了它们你对GEM的把握就完成了一大半Equipment Constant设备常量比如温度设定值、速度参数、配方参数等。这些参数可以通过S2F13/S2F14发送设备常量远程读写。Data Variable数据变量设备采集到的实时数据比如当前温度、压力、转速等。EAP通过S2F17/S2F18启动采集然后设备周期性地通过S6F11上报。Collection Event事件数据当某个事件发生时把与该事件相关的Data Variable组合起来一起上报。比如“产品完成”事件上报时就伴随产线速度、压力数据、成品数量等信息。这三个对象之间的关系可以这么理解设备常量是“旋钮”EAP可以远程调整数据变量是“仪表盘”EAP可以主动查看或让设备周期性地读事件上报则是“警报器”设备主动向EAP喊话“我这里发生事了”。用secs4net实现这些概念时代码层面不需要什么特殊设计主要是定义一个数据模型来管理这些变量和事件然后在消息处理中把它们串起来即可。4.3 报警与远程命令的实现报警处理GEM的报警机制非常重要。当设备发生异常如温度超限、气压不足、门盖打开等需要立即通过S5F1Alarm Report Send向EAP上报。这有两个关键点第一每条报警要有唯一的Alarm Code报警编号。 第二报警分为两类一类是触发型报警发生和恢复都要上报另一类是通知型报警只报一次。实现方法很简单在设备检测到异常时构造S5F1消息发送当异常消除时再构造一条新的S5F1并将ALCDAlarm Code恢复为0表示报警已清除。远程命令GEM允许EAP在远端向设备下发控制指令。常见的远程指令有START开始生产、STOP停止生产、RESET复位等。EAP通过S2F41Host Command Send下发一个远程命令设备收到后在S2F42中返回响应以及执行结果。private async Task HandleHostCommand(PrimaryMessage msg) { var item msg.SecsItem; // 远端命令名 var commandName ((Item)item[0]).ToString(); switch (commandName) { case START: // 执行启动流程 this.StartProcess(); break; case STOP: this.StopProcess(); break; } // 回S2F42 var reply new ReplyMessage(2, 42, Item.L( Item.A(commandName), Item.B(0) // BINACK0表示接受并执行 )); await connection.SendReplyAsync(msg, reply, CancellationToken.None); }注意在secs4net中为Primary消息发送Reply的方式是SendReplyAsync而不是重新构建一个Primary消息。操作对象上的差异如果搞混了会导致EAP侧等不到回复进而认为消息超时。5. 联调技巧与常见错误排查5.1 搭建一个免费的模拟器测试环境编写设备端SECS/GEM驱动时不可能每次都去客户现场连EAP联调。用模拟器在自己电脑上搭一个Host端环境会大大提升开发效率。我常用的测试方案是使用secs4net库自带的示例项目来模拟Host。你可以在GitHub上找到secs4net仓库其中包含一个Simulator示例运行起来之后它会作为Host端监听端口这样你的设备端程序就可以像连接真实EAP一样连接它。需要注意secs4net模拟器用的是同一套库因此它能够完整支持SECS-II消息的收发解析。唯一要提醒的是模拟器只适合功能验证不能完全替代EAP系统中复杂的流量控制逻辑真正验收还是要在MES测试环境里做。如果不想自己编译模拟器也可以找一些独立的SECS/GEM调试工具来用。这类工具能够以Host身份连接设备并且允许用户直观地组织SML消息并发送。对设备端调试来说它能比代码模拟器更快地制造测试场景。5.2 高频踩坑点与排查方法把我在实际联调中遇到并解决的典型问题整理成了一张表建议收藏这份问题排查清单问题现象根本原因解决方法HSMS连接一直被拒绝IpAddress/Port配置错误或Host端未启动监听用telnet测试IP和端口确认Host监听正常连接成功但没有任何消息收发DeviceId不一致EAP丢弃设备消息核对设备ID修改为EAP侧配置的值消息发出后一直超时对端没有回复可能消息格式错误或消息未注册查看对端日志确认收到消息用模拟器验证消息格式S6F11发不出去提示Encoding错误SECS-II消息构建时Item类型错误或嵌套错误对照SML语法检查Item的层级结构设备端与Host端数据字节顺序不一致不同工具/系统对无符号整数的字节序解析不同使用标准SECS-II类型U1/U2/U4等避免跨平台歧义设备上线后EAP不推送配方连接却正常S1F13的MDLN和SoftRev字段与EAP期望值不一致确认设备型号和版本信息与EAP台账一致偶尔出现连接断开日志里无异常空闲时间过长防火墙切断了长时间空闲的TCP连接向IT申请防火墙白名单或调整LinkTestInterval5.3 关键日志阅读技巧很多SECS/GEM联调问题都是日志杀手但大多数情况下日志读得对问题就解决了一半。一个最实用的技巧是关注消息的方向和配对。在日志里消息分为“设备发出”和“设备收到”两类。如果你发现发出的消息一直没有对应的回复而网络层面却没有断开那问题通常出在消息格式或消息内容上而不是网络问题上。第二个技巧注意日志中的超时。HSMS协议定义了不同层级的超时参数连接超时、回复超时、链路测试超时等。secs4net库在超时时会输出不同的异常信息根据异常信息能区分是连接问题还是消息应答问题。你不需要背下来所有超时参数但要学会看异常信息出现的上下文。第三个技巧在关键流程节点打上业务标记。比如在S1F13回复之后、在S6F11发送之前打上明确的操作日志。这样即使遇到EAP侧没反应的问题你也至少能判断出设备侧每一步都正确执行了从而把排查方向从“设备代码问题”引导到“EAP配置问题”。6. 从Demo到产品化的经验补全6.1 把面向模拟器的代码改为面向真实EAP在模拟器上跑通Demo很快但把Demo变成能上线生产的驱动还有很长一段路要走。首先代码封装和容错方式完全不同Demo通常只需要保证一条通路畅通而真实环境里随时可能遇到网络抖动、EAP端主备切换、异常断电重启等情况。产品化改造时有些地方值得特别关注第一个是消息处理框架的结构化。别把所有的消息处理逻辑都写在一个事件处理方法里建议按Stream/Function拆分成独立处理器private async Task OnMessageArrived(object sender, MessageEventArgs e) { switch (e.Message.Stream) { case 1: await HandleStream1(e.Message); break; case 2: await HandleStream2(e.Message); break; } }这样的结构不仅方便维护在后期排查问题的时候定位速度也快得多。第二个是设备侧通信配置的外置化。IP地址、端口、DeviceId这些参数最好放在配置文件中而不是硬编码在代码里。项目上线后客户现场的IP地址几乎一定会变到时候改配置比改代码重新编译要好得多。第三个是状态的自恢复能力。设备程序可能遇到EAP重启导致TCP连接断开、但设备侧业务正常运行的情况。驱动应该具备自动重连、断线期间消息排队、重连后状态同步的能力。secs4net的自动重连解决的是传输层问题但业务层需要你设计好重连后的状态一致性问题。6.2 底层原理TCP与HSMS消息边界用secs4net写代码时你不直接操作TCP报文但这并不代表TCP层的原理完全不需要了解。HSMS在传输层构建消息时会在每条消息的头部加上一个长度为4字节的长度字段表示消息内容的字节数。对端接收时会先读取4字节的长度再按长度值拆分完整消息。这套机制对应的是TCP协议的粘包/拆包处理。理解了这个机制你就能明白为什么HSMS要求网络必须稳定如果TCP连接上出现数据损坏或丢包消息边界就会错乱导致后续通信的解析全部异常。虽然TCP本身提供了可靠性保障应用层的数据损坏仍然可能因为传输链路上的硬件问题而产生。好在secs4net库内部已经处理好了消息边界的读取和恢复但你在排错时如果发现反复出现“解析失败”之类的异常不要只盯着业务逻辑要回顾一下网络链路是否正常、中间是否经过了一些不稳定的代理设备。6.3 性能优化与线程模型不少开发者在Demo阶段是单线程收发消息但在真实设备驱动里如果消息量比较大这种设计可能会成为瓶颈。secs4net的底层会为每个消息的接收异步通知外部你的事件处理方法如果里面做了耗时的业务操作就会阻塞其他消息的处理。经验做法是在MessageArrived事件中只做消息解析和快速回复所有耗时的设备动作放到独立的工作线程或任务队列中执行private async Task OnMessageArrived(object sender, MessageEventArgs e) { switch (e.Message.Stream) { case 2: // 收到S2F17启动采集把采集动作丢到后台线程 _ Task.Run(() this.HandleLongRunningJobAsync(e.Message)); break; } }这里特别提醒使用Task.Run把任务丢到后台线程之后必须保证后续对secs4net连接对象的访问是线程安全的。secs4net内部使用锁保护了连接状态的并发访问但你自己的设备业务对象可能需要额外加锁避免多线程同时修改设备状态导致数据不一致。我在项目里就用SemaphoreSlim保护设备状态字段的读写这个细节在并发量高的时候非常关键。7. 写在最后的调试心得开发SECS/GEM驱动本质上是和一台你看不见的“对端大脑”对话。你写的代码越是规范、越是对协议有敬畏之心联调时就越顺畅。我在实际项目中观察到真正导致项目延期的问题往往不是技术难度而是那些不起眼的配置不一致和消息格式小错误。比如EAP台账里的设备型号跟你代码里S1F2回复的设备型号对不上这种问题靠代码检查很难发现却会实实在在地阻塞联调进度。在这里分享一个我常用的做法每次联调之前先把设备端的消息日志、EAP端的连接台账截图、以及双方约定好的DeviceId/IP/端口清单放在一起核对一遍。别嫌麻烦我之前就是因为少对了这个环节浪费了两天时间排查一个“双方配置看起来都对实际上EAP端设备台账里写的是小写主机名设备端回的是大写主机名”的问题而EAP侧对主机名做了精确匹配。最后一个建议是不要怕面对原始协议栈。虽然secs4net帮我们封装了大部分逻辑但如果遇到特别诡异的问题还是要敢于把报文截出来对照SEMI标准手册一个字节一个字节地分析。驱动开发嘛本质上就是一个抠细节的活儿。把这些细节吃透了无论客户现场的EAP是谁家的你的驱动都能做到稳如磐石。
企业数字化 ERP 产品动态
相关推荐
B站抖音视频下载工具组合:BBDown与TikTokDownload实战指南 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 13:24:08
企业AI数字底座建设指南:从架构设计到落地避坑 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 13:24:08
量化回测工具选型指南:Backtrader、vn.py、VectorBT深度对比与实操 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 13:24:02
OcctCSharpBridge:.NET 下的 Open CASCADE 封装与 CAD/BIM 开发实践 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 14:00:51
ESP32开发板换板适配指南:小智源码板级适配实战 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 14:00:45
Design Compiler:使用read_file命令读取RTL设计 相关阅读
Design Compilerhttps://blog.csdn.net/weixin_45791458/category_12738116.html?spm1001.2014.3001.5482 目录 read_file命令 读取参数化设计 举例说明 等价表示 写在最后 Design Compiler可以使用read_file读取RTL设计(不建议,建议使用anal… · 2026/9/24 14:00:45
中科曙光服务器培训全解析:从硬件选型到系统部署与排障 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 14:00:45
Unity引擎底层揭秘:mono_add_internal_call如何打通C#与C++ 一次很普通的反编译。 你想看看 Transform.position 到底怎么实现的,用 dnSpy 打开 UnityEngine.CoreModule.dll: public Vector3 position
{get{get_position_Injected(out Vector3 result);return result · 2026/9/24 14:00:39
Python | PyCharm一键无脑安装 /* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/24 14:00:39
基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程 简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为… · 2026/9/24 0:00:13
1D-CNN时间序列建模实战:从Conv1d原理到工业落地 简介:面向时间序列数据建模的一维卷积神经网络完整实现,适合深度学习入门者及需要快速验证时序模型的研究者,能够从音频、文本、传感器或股价等序列中挖掘局部特征与时间依赖。压缩包体积很小,只有3KB,内含3个Python脚… · 2026/9/24 0:00:26
柔软的L:汉语语流中被忽视的舌肌张力控制 1. 这个“L”不是字母表里的L,而是舌尖上的L最近在几个方言群和语音教学社群里,反复看到有人发一句:“也说字母L:柔软的长舌”。初看以为是英语发音课笔记,点开才发现全是方言爱好者、播音系学生、语言康复师甚至戏曲演… · 2026/9/24 0:00:44