插件契约与打包约定

来自MDCS wiki2
Artheru讨论 | 贡献2026年5月16日 (六) 22:00的版本 (Initial bilingual draft (auto-published))
(差异) ←上一版本 | 最后版本 (差异) | 下一版本→ (差异)
跳到导航 跳到搜索


概述 / 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 是主战场。

相关页面 / See also