插件契约与打包约定
概述 / Overview
本页讲所有 MDCS 插件 共通 的约定 —— 不论是雷达 / 相机 / 底盘 / Movement / 还是 SimpleComposer 插件,这些规则都适用。具体的 基类签名见各家族页面。
This page covers the conventions shared by every MDCS plugin — lidar, camera, cart, Movement, fleet — regardless of family. The family-specific base-class contracts live on their own pages.
1. 命名约定 / Naming convention
MainIOObject 命名
Medulla 端插件 (lidar / camera / cart) 的入口类 必须叫 `MainIOObject` —— 严格区分大小写。 The Medulla-side plugin entry class must be named exactly `MainIOObject` — case-sensitive.
Medulla 加载器 (`D:\src\M2\MedullaCore\MedullaIO.cs:299`) 反射查找: The loader does reflection lookup:
var type = asm.GetTypes().FirstOrDefault(t => t.Name == "MainIOObject");
return Activator.CreateInstance(type); // parameterless ctor required
没有 `[Plugin]` 属性,没有 manifest。仅靠类名 + 无参构造函数。 No `[Plugin]` attribute, no manifest — just the class name and a parameterless ctor.
【注意】 改类名或删默认构造函数 → 加载静默失败 → 几分钟后由 `Hedingben` toast 间接报错。先用 `io load <yourplugin>.dll` 在 Medulla console 单独测试加载。
Renaming or removing the parameterless ctor → silent load failure surfacing later in `Hedingben`. Test `io load` in isolation first.
Clumsy / Simple 插件类名
Clumsy `MovementDefinition` 子类 + Simple `Car` / `ClumsyCar` / `BusinessLogic` 子类不要求特定类名,但用 属性 标识: Clumsy / Simple plugins don't need a specific name; they use attributes to identify themselves:
- `[CarType]` 标 Simple 端 Car 子类
- `[MovementTest]` 标 Clumsy Movement 测试入口
- `[CoderMethod]` 标 SimpleCore TopazScript 编码器方法
文件夹与 DLL 命名 / Folder & DLL naming
- 一方插件 / First-party: `D:\src\M2\OfficialPlugins\<VendorModel>\`
- 三方插件 / Third-party: 任意;通常 `<vendor-name>\<model>\`
- DLL 名 = `<VendorModel>.dll`,部署到 `medulla/plugins/<vendor>/` 或 `simplecomposer/plugins/`
2. 项目文件 / .csproj
目标框架 / Target framework
- .NET 6.0 or .NET 8.0 — 推荐 / preferred
- .NET Framework 4.8 — 部分旧雷达驱动用 / some legacy lidar drivers
引用 / References
所有插件 csproj 都引用 参考程序集(`Ref<Name>.dll`),不引用 impl DLL: All plugin csproj reference reference-only assemblies (`Ref<Name>.dll`), not the implementation DLLs:
<ItemGroup>
<PackageReference Include="Fody" Version="6.x" PrivateAssets="all" />
<PackageReference Include="Costura.Fody" Version="5.x" />
<Reference Include="RefFundamentalLib">
<HintPath>..\tools\RefFundamentalLib.dll</HintPath>
</Reference>
<Reference Include="RefMedullaCore">
<HintPath>..\tools\RefMedullaCore.dll</HintPath>
</Reference>
<Reference Include="RefLidarController">
<HintPath>..\tools\RefLidarController.dll</HintPath>
</Reference>
</ItemGroup>
`..\tools\` 模式 全仓库一致 —— 不要改成 NuGet。 The `..\tools\` HintPath pattern is repo-wide; don't try to NuGet-ify it.
Costura.Fody 胖 DLL
插件输出是单 `.dll`,所有依赖(包括 LessokajiWeaver 工具库)嵌入为 base64 资源。客户只需要 1 个文件部署。 Plugin output is a single `.dll` with all deps embedded as base64 resources. Customer deployment = 1 file.
`FodyWeavers.xml` 在工程根:
<Weavers>
<Costura/>
<LessokajiWeaver/>
</Weavers>
【注意】 Costura 缓存可能让你看不到 FundamentalLib 升级 —— 重新构建插件后再部署,否则胖 DLL 里还是旧版 FundamentalLib。
Costura caches dependencies in the fat DLL. After upgrading FundamentalLib, rebuild plugins before deploying.
3. 版本与助记词 / Version + mnemonic
每次修改插件,AssemblyInformationalVersion 加 单词助记词: Every plugin modification adds a single mnemonic word to AssemblyInformationalVersion:
<AssemblyInformationalVersion>1.4.0+kindling</AssemblyInformationalVersion>
<!-- 下次改动 / next change -->
<AssemblyInformationalVersion>1.4.1+lantern</AssemblyInformationalVersion>
目的:现场调试时 肉眼区分两个数字看起来一样的二进制。 Purpose: visually disambiguate two binaries with the same number in the field.
或者用 `[ReplaceWithCompileInfo]` —— LessokajiWeaver 在编译期注入时间戳 + git 哈希到 static string 字段。 Alternative: `[ReplaceWithCompileInfo]` — LessokajiWeaver injects timestamp + git hash at compile time.
4. 部署 / Deployment
Medulla 端 / Medulla-side
DLL 放到 `medulla/plugins/<vendor>/<plugin>.dll`,`startup.iocmd` 加: Drop the DLL into `medulla/plugins/<vendor>/`, add to `startup.iocmd`:
foo = io load plugins/<vendor>/<plugin>.dll
foo Init # if your plugin has an Init method
foo Start <args> # invoke whatever it needs
startup.iocmd 语法见 startup.iocmd脚本语法。
SimpleComposer 端 / SimpleComposer-side
DLL 放到 `simplecomposer/plugins/<plugin>.dll`。SimpleComposer 启动时(`D:\src\Simple\SimpleComposer\Program.cs:107-162`)自动扫所有 DLL,按 `[CarType]` / `HeuristicsContainer` / `CustomOperationsBeforeLoading.Set` / `BusinessLogic` 类型分类注册。
Drop into `simplecomposer/plugins/`. SimpleComposer auto-scans on boot.
5. 加载生命周期 / Load lifecycle
Medulla:
io load X.dll → reflection scan → instantiate MainIOObject
↓ (your ctor runs; no hardware contact)
cart Init → connect hardware → spawn LadderLogics
↓
Operation(0), Operation(1), ...
SimpleComposer:
Program.cs startup
→ load every plugins/*.dll
→ scan for [CarType] / HeuristicsContainer / BusinessLogic / CustomOps
→ register
→ MyAsmHelper.Init() invoked
【注意】 Medulla 加载器 不调 `Init()`。你需要 startup.iocmd 显式调用 `cart Init`。
The Medulla loader does NOT call `Init()`. You must invoke `cart Init` from startup.iocmd.
6. 一切重启 vs 热替换 / Restart vs hot swap
- Medulla 插件: 必须停 Medulla → 替换 DLL → 启动。热替换会导致 Costura 缓存冲突 / undefined behaviour。
- SimpleComposer 插件: 同上 —— 关 SimpleComposer → 替换 → 启动。
- Clumsy MovementDefinition: 同 Clumsy 进程重启。
Hot swap is not supported. Always stop-replace-start.
7. 跨平台 / Cross-platform
- Medulla / Detour / Clumsy / SimpleComposer 都可在 Linux 运行(.NET 6/8)。
- DObject 在 Linux 用文件后备(见 DObject 共享内存协议)。
- libVRender (CycleGUI 渲染) 已有 Linux 构建,但 Windows 是主战场。