LadderLogic框架

来自MDCS wiki2
跳到导航 跳到搜索


概述 / Overview

LadderLogic 是 Medulla 提供的 周期循环宿主。CartDefinition 插件用嵌套类 `: LadderLogic<MyCart>` + `[UseLadderLogic(IntervalMs = N)]` 声明一个定时执行的控制循环;框架按 N 毫秒间隔调用 `Operation(int iteration)`。多个循环可在同一 Cart 上共存,各自在独立线程。

LadderLogic is Medulla's cyclic-loop host. A CartDefinition plugin declares a timed loop via a nested class `: LadderLogic<MyCart>` plus `[UseLadderLogic(IntervalMs = N)]`; the framework calls `Operation(int iteration)` every N ms. Multiple loops can coexist on one cart, each on its own thread.

实现:`D:\src\M2\OfficialPlugins\CartActivator\LadderLogic.cs:7-272`,属性定义 `D:\src\M2\OfficialPlugins\CartActivator\Attrs.cs:26-36`。 Implementation: `D:\src\M2\OfficialPlugins\CartActivator\LadderLogic.cs:7-272`; attribute at `Attrs.cs:26-36`.

最小骨架 / Minimal skeleton

public class MyCart : CartDefinition
{
    [AsUpperIO] public float vCmd;
    [AsLowerIO] public float vEst;

    public override void Init()
    {
        // connect to hardware here
    }

    [UseLadderLogic(IntervalMs = 50)]
    public class Loop : LadderLogic<MyCart>
    {
        public override void Operation(int iteration)
        {
            // iteration == 0 on first tick after Init/restart
            self.WriteToHw(self.vCmd);
            self.vEst = self.ReadFromHw();
        }
    }
}

属性参数 / Attribute parameters

字段 / Field 默认 / Default 含义 / Meaning
`logic: Type` (自动) LadderLogic 子类(嵌套类自动绑定)
`scanInterval: int` (or `IntervalMs`) 50 tick 周期(ms)
`realTime: bool` false 保留,暂未使用 / reserved
`resume: bool` true 异常后自动重启循环 / auto-restart on exception
`resumeRetryDelay: int` 1000 重启前等多久(ms)
`timeoutInterval: double` 10000 保留 / reserved

生命周期 / Lifecycle

 CartDefinition.Init()                  ← 你的初始化
      │
      ▼
 CartDefinition.createLadders()         ← 反射扫 [UseLadderLogic]
      │
      ▼ 对每个属性 / for each attribute
 实例化 LadderLogic<T>, 注入 self = this
      │
      ▼
 创建 MultimediaTimer + 工作线程
      │
      ▼ 周期触发 / every scanInterval ms
 Operation(iteration: 0)   ← 第一次 / first tick
 Operation(iteration: 1)
 Operation(iteration: 2)
 ...
      │
      ▼ 异常时 / on exception
 Hedingben.ToastText(...)
 如果 resume=true 等 resumeRetryDelay 后重启 / resume after delay

Tick 精度 / Tick precision

基于 MultimediaTimer(OS 决定,Windows 约 1–15 ms 分辨率)。 Based on MultimediaTimer; OS-dependent (~1–15 ms on Windows).

超时行为 / Overrun: 若 `Operation()` 耗时 > scanInterval,下一 tick 排队执行(不丢 tick)。长期超时会累积延迟。 If `Operation()` exceeds scanInterval, the next tick queues behind (no tick skipping). Sustained overrun accrues latency.

【注意】 不要在 `Operation()` 里阻塞 / sleep / 等 IO 超过 scanInterval。
Don't block / sleep / wait IO longer than scanInterval inside `Operation()`.

多 LadderLogic 共存 / Multiple loops

一个 CartDefinition 可以有多个 `[UseLadderLogic]` 属性,每个一对独立的线程 + Timer:

A CartDefinition can declare multiple loops, each on its own thread:

[UseLadderLogic(IntervalMs = 50)]
public class CtrlLoop : LadderLogic<MyCart> { ... }   // motion control 50 Hz

[UseLadderLogic(IntervalMs = 200)]
public class BatteryLoop : LadderLogic<MyCart> { ... }  // battery / telemetry 5 Hz

[UseLadderLogic(IntervalMs = 1000)]
public class DiagLoop : LadderLogic<MyCart> { ... }     // health 1 Hz

线程之间共享 cart 字段;如果同一字段同时被多个 loop 读写,自己处理同步(lock / Interlocked)。 Threads share cart fields; if multiple loops touch the same field, synchronise (lock / Interlocked).

内置工具方法 / Built-in helpers

`LadderLogic<T>` 基类提供以下状态机助手(`LadderLogic.cs:50-272`): The `LadderLogic<T>` base class offers these state-machine helpers (`LadderLogic.cs:50-272`):

方法 / Method 用途 / Use
`TriggerOnce(bool active, int millis, Action a)` 信号持续 `millis` ms 后触发 `a()` 一次 / fire once after signal held `millis` ms
`WaitSignal(bool sig, int timeout)` 等信号上升或超时 / wait for signal rising or timeout
`TriggerIfChanged<T>(Func<T> g, Action<T> h)` 值变化触发 / fire when value changes
`FlipFlop<T>(ref T field, int millis, T[] vals)` 周期切换值 / cycle values periodically (e.g. blinking LED)
`SwitcherTrigger(bool, Action, bool)` 边沿触发一次 / edge-trigger one-shot
`ModSignal(Func<bool>, int delay, int span)` 时间窗内信号检测 / time-window detection
`IsochronousFork(Action)` "同步分叉",一次只跑一个 / one-at-a-time async fork
`PriorityActions` 高优先级动作执行器 / priority action runner
`DaemonVal(float send, float read, float r, int t, Action<bool>)` 多变量一致性看门狗 / multi-var consistency watcher

用这些替代裸 `if (Math.Abs(now - lastEvent) > T) ...` 的散乱时间状态机。

Use these helpers to replace ad-hoc `if (Math.Abs(now - lastEvent) > T) ...` time state-machines scattered through the loop.

错误传播 / Error propagation

  • `Operation()` 抛异常 → 框架捕获 → log 到 DLog + Hedingben toast `"Ladder broken: <msg>"` 在 UI 显示。
  • 若 `resume = true`:等 `resumeRetryDelay` 后重新调用 `Operation(iteration: 0)`(iteration 重置)。
  • 若 `resume = false`:循环退出,cart 进入"半工作"状态 —— 其它 loop 还在跑。

【发现】 `resume = true` 是默认值。一个 buggy 插件不会让整个进程崩,但会 无限重试 —— 务必在开发期密切看 Hedingben。
`resume = true` is the default. A buggy plugin won't crash the process but will silently retry forever — watch Hedingben during development.

iteration 参数 / iteration parameter

  • `iteration == 0`:首次 tick 或异常重启后的首次 tick;框架自动清空内部跟踪状态(如 `TriggerOnce` 的计时器)。
  • `iteration > 0`:递增;可用来做 "每 N tick 做一次"。

调试 / Debugging

  • `Hedingben` toast 是最直接的报错通道 —— 总打开。
  • DLog 有详细 stack trace 用 `$ladder` 主题。
  • 用 `[IOObjectMonitor]` 暴露 `iteration` 或自定义计数器到 Medulla dashboard 看跳速。

相关页面 / See also