<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="zh-Hans-CN">
	<id>https://wiki2.lessokaji.com/index.php?action=history&amp;feed=atom&amp;title=%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%B8%85%E5%8D%95</id>
	<title>插件开发清单 - 版本历史</title>
	<link rel="self" type="application/atom+xml" href="https://wiki2.lessokaji.com/index.php?action=history&amp;feed=atom&amp;title=%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%B8%85%E5%8D%95"/>
	<link rel="alternate" type="text/html" href="https://wiki2.lessokaji.com/index.php?title=%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%B8%85%E5%8D%95&amp;action=history"/>
	<updated>2026-05-16T16:55:40Z</updated>
	<subtitle>本wiki上该页面的版本历史</subtitle>
	<generator>MediaWiki 1.40.0</generator>
	<entry>
		<id>https://wiki2.lessokaji.com/index.php?title=%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%B8%85%E5%8D%95&amp;diff=1048&amp;oldid=prev</id>
		<title>Artheru：​Initial bilingual draft (auto-published)</title>
		<link rel="alternate" type="text/html" href="https://wiki2.lessokaji.com/index.php?title=%E6%8F%92%E4%BB%B6%E5%BC%80%E5%8F%91%E6%B8%85%E5%8D%95&amp;diff=1048&amp;oldid=prev"/>
		<updated>2026-05-16T14:00:50Z</updated>

		<summary type="html">&lt;p&gt;Initial bilingual draft (auto-published)&lt;/p&gt;
&lt;p&gt;&lt;b&gt;新页面&lt;/b&gt;&lt;/p&gt;&lt;div&gt;&amp;lt;languages/&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== 概述 / Overview ==&lt;br /&gt;
本页是 MDCS 插件开发中 ''该做与不该做''（Do's &amp;amp; Don'ts）的速查表。从生产经验中总结，每一条都对应一个真实踩过的坑。&lt;br /&gt;
&lt;br /&gt;
This page is the MDCS plugin-development Do's &amp;amp; Don'ts. Each rule reflects a real production pitfall.&lt;br /&gt;
&lt;br /&gt;
== Do's ==&lt;br /&gt;
=== 命名与契约 / Naming &amp;amp; contract ===&lt;br /&gt;
* '''Medulla 插件入口类名必须叫 `MainIOObject`''' —— 严格大小写。&lt;br /&gt;
  Medulla plugin entry class must be exactly `MainIOObject`.&lt;br /&gt;
* '''每个插件目录加 README + CHANGELOG'''，写清适配的硬件型号 + 版本 + 联系人。&lt;br /&gt;
* '''AssemblyInformationalVersion 含 ''助记词''''' 如 `1.4.0+kindling`，便于现场区分两个数字相同的二进制。&lt;br /&gt;
&lt;br /&gt;
=== 生命周期 / Lifecycle ===&lt;br /&gt;
* '''构造函数廉价无副作用''' —— 硬件连接放在 `Init()`。&lt;br /&gt;
  Constructor must be cheap and side-effect-free; hardware contact in `Init()`.&lt;br /&gt;
* '''`Init()` 内捕获异常，设 `eStop = true` + Hedingben toast''' —— ''不要''让 `Init()` 抛出，否则 LadderLogic 部分启动部分不启动，难诊断。&lt;br /&gt;
  Catch exceptions in `Init()`; set `eStop = true` rather than throwing.&lt;br /&gt;
* '''每次部署前用 `io load` 在 ''干净''控制台测试一次加载''' —— 提前发现 Costura 缺失依赖。&lt;br /&gt;
&lt;br /&gt;
=== LadderLogic ===&lt;br /&gt;
* '''不阻塞 `Operation()` 超 scanInterval''' —— 起 worker 线程或用 `TriggerOnce` / `WaitSignal` 拆分。&lt;br /&gt;
  Don't block `Operation()` for longer than `scanInterval`. Spawn a worker or split with helpers.&lt;br /&gt;
* '''共享字段加锁''' —— 多个 LadderLogic 并发访问同一字段时用 `lock` 或 `Interlocked`。&lt;br /&gt;
* '''发布期间监控 Hedingben''' —— `resume = true` 默认会静默重启 buggy 循环 ；新插件上线时人在场看 toast。&lt;br /&gt;
&lt;br /&gt;
=== IO 表 / IO fields ===&lt;br /&gt;
* '''上位字段 `*Cmd` 后缀，下位 `*Est` 后缀''' —— 项目约定，避免同名冲突。&lt;br /&gt;
* '''`[AsInitParam]` 字段加默认值''' —— 即使没人设也能初始化。&lt;br /&gt;
* '''敏感字段加 `[IOObjectWatch]`''' —— 电量低 / 温度高 自动告警。&lt;br /&gt;
&lt;br /&gt;
=== Movement / Clumsy ===&lt;br /&gt;
* '''Movement 实现可重入''' —— 不要 static 字段保存状态。&lt;br /&gt;
* '''在 `Get()` 第一帧读 pose'''，不要在构造时读 —— 构造与执行间车可能已经移动。&lt;br /&gt;
* '''Movement 长度 ≤ 50 行''' —— 超过就拆成多个 Movement 组合。&lt;br /&gt;
&lt;br /&gt;
=== Detour / 定位 / TightCoupler ===&lt;br /&gt;
* '''诚实报协方差''' —— 谎报会让 TightCoupler 让该源 ''统治''融合 → 全局发散。&lt;br /&gt;
* '''外部反馈 `counter` 单调递增''' —— 用来检测丢帧 / 乱序。&lt;br /&gt;
* '''`integrated = true` 配 `startingTick`''' —— 重置（IMU 重启）时给新 baseline。&lt;br /&gt;
&lt;br /&gt;
=== 工程 / Project ===&lt;br /&gt;
* '''每个 csproj 用 `..\tools\` HintPath''' —— 仓库一致约定。&lt;br /&gt;
* '''FodyWeavers.xml 含 Costura + LessokajiWeaver'''。&lt;br /&gt;
* '''Release 模式发布''' —— Debug 会保留更多 PDB / 符号；体积大。&lt;br /&gt;
* '''测试 .NET 4.8 ↔ .NET 6/8 兼容''' —— 部分老 driver 还在 .NET Framework。&lt;br /&gt;
&lt;br /&gt;
=== DObject ===&lt;br /&gt;
* '''名 ≤ 32 字符''' —— 超长会被静默截断。&lt;br /&gt;
* '''读者用 `Wait()` 阻塞''' —— 比轮询省 CPU。&lt;br /&gt;
* '''每次 Post 含 frame counter''' —— 读者能检测丢帧。&lt;br /&gt;
&lt;br /&gt;
=== 文档 / Documentation ===&lt;br /&gt;
* '''README 写清 ''硬件型号 + 协议版本'''''。&lt;br /&gt;
* '''CHANGELOG 含具体行为变化''' —— 不是 &amp;quot;fix bug&amp;quot;。&lt;br /&gt;
* '''startup.iocmd 示例放在 INSTALL.txt''' —— 客户照抄。&lt;br /&gt;
&lt;br /&gt;
== Don'ts ==&lt;br /&gt;
=== 命名 ===&lt;br /&gt;
* ❌ '''不要把入口类改名'''（如 `MyLidar : Lidar2DIOObject`） —— 加载器找不到。&lt;br /&gt;
* ❌ '''不要给 IOObject 加 `[Plugin]` 属性''' —— MDCS 不用 manifest，加了也无效。&lt;br /&gt;
&lt;br /&gt;
=== 加载 ===&lt;br /&gt;
* ❌ '''不要热替换 DLL''' —— Costura 缓存导致未定义行为。停 → 替换 → 启动。&lt;br /&gt;
* ❌ '''不要在 `static` 构造函数里碰硬件''' —— 加载时立刻触发。&lt;br /&gt;
* ❌ '''不要假设 `Init()` 立刻被调''' —— 它要 startup.iocmd 里的 `&amp;lt;inst&amp;gt; Init` 显式触发。&lt;br /&gt;
&lt;br /&gt;
=== IO 表 ===&lt;br /&gt;
* ❌ '''`[AsUpperIO]` 不要标在 `readonly` 字段上''' —— 编译过，运行时 `MissingMethodException`。&lt;br /&gt;
* ❌ '''不要从外部直接写 `MedullaCartUpperIO&amp;lt;tag&amp;gt;` DObject''' —— 总走 `SetUpperIO`，否则失同步。&lt;br /&gt;
* ❌ '''不要把高速字段都 `[IOObjectMonitor]` 加 `VerboseLog`''' —— 日志爆炸。&lt;br /&gt;
&lt;br /&gt;
=== LadderLogic ===&lt;br /&gt;
* ❌ '''不要在 `Operation()` 里 `Thread.Sleep`''' —— 阻塞整个 loop 线程。&lt;br /&gt;
* ❌ '''不要假设 LadderLogic 是实时''' —— Windows 上 1–15 ms 抖动；真实时需要 PLC。&lt;br /&gt;
* ❌ '''不要在多 LadderLogic 之间共享 ''可变''字段而不加锁'''。&lt;br /&gt;
&lt;br /&gt;
=== Movement ===&lt;br /&gt;
* ❌ '''不要在 Movement 里 `Thread.Sleep`''' —— 用 `yield return` 多次。&lt;br /&gt;
* ❌ '''不要在 `Get()` 抛异常但不写 `eStop = true`''' —— 车继续按上一帧 `vCmd` 走。&lt;br /&gt;
* ❌ '''不要从 ''UI 线程''调 `DriveTask.WaitDriveTask`''' —— 它阻塞。&lt;br /&gt;
&lt;br /&gt;
=== Queue ===&lt;br /&gt;
* ❌ '''不要假设 Queue 的不同行真的并行''' —— 当前实现是串行（行 i 等行 i-1）。详见 [[Special:MyLanguage/SimpleAgvInterface Queue机制|Queue 机制]] 的&amp;quot;超标量实际行为&amp;quot;段。&lt;br /&gt;
* ❌ '''不要在 action 内 ''忽略''异常''' —— 让它传出，让上层决定恢复。&lt;br /&gt;
&lt;br /&gt;
=== Detour ===&lt;br /&gt;
* ❌ '''不要在雷达插件里调 `TightCoupler.PostExternalFeed`''' —— 雷达走 DObject `output()` 路径；TightCoupler 是 ''非雷达''来源的入口。&lt;br /&gt;
* ❌ '''不要谎报协方差''' —— 永远不要 &amp;quot;我超准请相信我&amp;quot;。&lt;br /&gt;
* ❌ '''不要在 SLAM 输出帧间 ''内部''重新加载地图''' —— 用 Detour 的 reload API。&lt;br /&gt;
&lt;br /&gt;
=== SimpleComposer / Fleet ===&lt;br /&gt;
* ❌ '''不要在 `BusinessLogic.Plan` 里阻塞超过 100 ms''' —— UI 假死。&lt;br /&gt;
* ❌ '''`[CarType]` 标 ''class''，不要标实例字段''' —— 反射查找按 Type。&lt;br /&gt;
* ❌ '''不要在 SimpleComposer 进程内调阻塞 HTTP''' —— 用 async/await。&lt;br /&gt;
&lt;br /&gt;
=== Costura / 构建 ===&lt;br /&gt;
* ❌ '''不要给 ''核心 DLL'' 加 Costura.Fody''' —— `MedullaCore.dll` / `ClumsyCore.dll` 等保持 ''loose'' DLL，让插件共载。&lt;br /&gt;
* ❌ '''不要直接引用 ''impl DLL''''' —— 引用 `Ref&amp;lt;Name&amp;gt;.dll`；否则带入 Costura 内嵌的整套依赖。&lt;br /&gt;
&lt;br /&gt;
=== DObject ===&lt;br /&gt;
* ❌ '''不要在 Linux 上假设 `io_shared` MMF''' —— Linux 上是文件后备。&lt;br /&gt;
* ❌ '''不要 cache 多个 `new DObject(&amp;quot;name&amp;quot;)` 实例''' —— 一个 name 一个，跨进程共享。&lt;br /&gt;
&lt;br /&gt;
== 性能反模式 / Performance anti-patterns ==&lt;br /&gt;
* ❌ '''每帧 `new` 大数组''' —— 用 buffer 池或预分配。&lt;br /&gt;
* ❌ '''每帧 `Console.WriteLine`''' —— 日志走 DLog，主题化、可关闭。&lt;br /&gt;
* ❌ '''每帧序列化大对象到 DObject''' —— 用紧凑二进制，避免 JSON。&lt;br /&gt;
* ❌ '''Movement 内每帧 `LINQ` 链''' —— `foreach` + 索引循环。&lt;br /&gt;
&lt;br /&gt;
== 安全 / Safety ==&lt;br /&gt;
* '''e-stop 必须 ''独立''于 Movement 逻辑''' —— 硬件 e-stop 线 + 软件 watchdog 双层。&lt;br /&gt;
* '''激光雷达 &amp;lt; 300 mm 范围内任何点 → 触发硬 stop''' —— 不能等 Movement 层减速。&lt;br /&gt;
* '''功能安全设备（SIL-2 / PL-d）独立于 MDCS''' —— MDCS 是控制层，不是安全层。&lt;br /&gt;
&lt;br /&gt;
== 相关页面 / See also ==&lt;br /&gt;
* [[Special:MyLanguage/插件契约与打包约定|插件契约与打包约定]]&lt;br /&gt;
* [[Special:MyLanguage/插件测试与发布|插件测试与发布]]&lt;br /&gt;
* [[Special:MyLanguage/插件开发指南|插件开发指南]]&lt;br /&gt;
* [[Special:MyLanguage/LadderLogic框架|LadderLogic框架]]&lt;br /&gt;
* [[Special:MyLanguage/MovementDefinition详解|MovementDefinition详解]]&lt;br /&gt;
* [[Special:MyLanguage/SimpleAgvInterface Queue机制|SimpleAgvInterface Queue机制]]&lt;br /&gt;
&lt;br /&gt;
[[Category:二次开发相关说明]]&lt;br /&gt;
[[Category:开发手册]]&lt;/div&gt;</summary>
		<author><name>Artheru</name></author>
	</entry>
</feed>