查看“如何基于SimpleCore核心库进行调度系统开发”的源代码
←
如何基于SimpleCore核心库进行调度系统开发
跳到导航
跳到搜索
因为以下原因,您没有权限编辑本页:
您请求的操作仅限属于该用户组的用户执行:
管理员
您可以查看和复制此页面的源代码。
<languages/> == 概述 / Overview == MDCS 的车队调度被分为两层:'''SimpleCore'''(核心算法库,包含交管、寻路、包络、可达性、DPS 等内核算法)与 '''SimpleComposer'''(基于 SimpleCore 的 UI 壳层,包含 CAD 工具、可视化、插件宿主)。本页面对开发者:你想自己写一个调度应用(或在 SimpleComposer 之外用 SimpleCore 内核)该如何开始。 MDCS fleet management is split into two layers: '''SimpleCore''' (algorithm library — traffic control, pathfinding, envelopes, reachability, DPS) and '''SimpleComposer''' (a UI shell built on SimpleCore — CAD tool, visualisation, plugin host). This page targets developers writing their own scheduling app on top of SimpleCore, or extending SimpleComposer with a custom car / mission type. == 何时基于 SimpleCore 开发 / When to use SimpleCore directly == {| class="wikitable" ! 场景 / Scenario !! 建议 / Recommendation |- | 集成进非 SimpleComposer 的现成系统 || 直接用 SimpleCore,把 UI 留给原系统 |- | 给 SimpleComposer 加车型 / 业务 || 写插件在 SimpleComposer 上挂载,不必自己写调度 |- | 算法研究(新交管 / 新寻路) || SimpleCore,作为算法宿主 |- | 单工位单车 || 都不需要,写一个小循环够了 |} == 核心抽象 / Core abstractions == === Car 体系 / Car hierarchy === SimpleCore 把"车"按照 ''是否使用 MDCS 车载系统'' 分为两支: SimpleCore splits "cars" into two branches by whether they use the MDCS on-vehicle stack: {| class="wikitable" ! 类 / Class !! 文件 / File !! 用途 / Use |- | `AbstractCar` || `D:\src\Simple\SimpleCore\PropType\AbstractCar.cs:346` || 调度可见的车的最小契约 / minimal scheduling contract |- | `Car` || `D:\src\Simple\SimpleComposer\RCS\Car.cs:33` || SimpleComposer 中的"车"基类 / Composer-side base |- | `ClumsyCar : Car` || `D:\src\Simple\SimpleComposer\RCS\ClumsyCar.cs:34` || 跑 MDCS 车载(Medulla + Clumsy)的车 / runs full MDCS on-vehicle stack |- | 自评估 `Car`(自定义子类)/ Self-eval `Car` (custom subclass) || 各 vendor 适配代码 / vendor adapters || PLC / 磁导航 / 二维码 类无车载 PC 的车 / cars without on-board MDCS (PLC, magstrip, QR) |- | `AGVInterface` || `D:\src\Simple\SimpleCore\BasicProps\AGVInterface.cs` || 任务脚本调用的 API / API surface called by mission scripts |} === AGVInterface 与 Queue 机制 === `AGVInterface` 暴露给任务脚本(TopazScript)的核心方法是 `Queue(params Func<Task>[] actions)` —— ''超标量'' 异步流水线: The core method on `AGVInterface` exposed to mission scripts is `Queue(params Func<Task>[] actions)` — a "super-scalar" async pipeline: * 每个 Func 是一个 ''行 / row'',按提交顺序进入流水线。 * 同一行内的代码顺序执行。 * 上一行未完成前,下一行不会开始 —— 但下一行可在上一行最后一个 await 后立即 schedule,达成宏观流水。 * `WaitAsync(int pipe)` 让调用方等到指定行完成再继续。 * Each Func is a '''row''' in the pipeline. * Code within a row runs sequentially. * The next row only starts after the previous row's last await, but its prelude can overlap, giving macro-level pipelining. * `WaitAsync(int pipe)` lets the caller wait for a specific row. === TryLock / Leave — 交管 / Traffic control === `TryLock(int siteID)` / `Leave(int siteID)` 是 SimpleCore 交管在 ''每个 mission 脚本里''的钩子。任务脚本在进入一个交管区前必须 `TryLock`;离开时 `Leave`。SimpleCore 的内核保证: `TryLock(siteID)` / `Leave(siteID)` are SimpleCore's traffic-control hooks ''per mission script''. Scripts must `TryLock` before entering a controlled segment and `Leave` on exit. SimpleCore's kernel guarantees: * 无死锁(DPS 算法,见 [[Special:MyLanguage/DPS调度算法详解|DPS调度算法详解]])<br />Deadlock freedom (via DPS — see the DPS page). * 公平(按到达顺序授予锁)<br />Fairness — locks granted in arrival order. * 兼容性(同一锁可被声明 ''可兼容车型'' 一起持有)<br />Compatibility — a lock can be held simultaneously by compatible car types. == 写一个 ClumsyCar 子类 / Subclassing ClumsyCar == ClumsyCar 用于车上跑完整 MDCS(Clumsy 自动驾驶 + Medulla 硬件抽象)的情况。最小子类骨架: ClumsyCar is the right base when the vehicle runs the full MDCS stack on-board. Minimal subclass: <syntaxhighlight lang="csharp"> using SimpleComposer.RCS; namespace Vendor.Carts { [CarType(Name = "MyCart", Title = "我的车型 / MyCart")] public class MyCart : ClumsyCar { public MyCart() : base() { conf = "configs/MyCart.config"; api_retries = 3; } public override async Task actualSendScript(string script) { // 把 TopazScript 通过 HTTP 发到车载 Clumsy 适配器 / POST to on-board Clumsy adapter var url = $"http://{address}:30080/run"; var resp = await HttpClient.PostAsync(url, new StringContent(script)); resp.EnsureSuccessStatusCode(); } public override async Task keepAlive() { // 心跳 + 状态轮询 / heartbeat + status poll var status = await HttpClient.GetStringAsync($"http://{address}:30080/status"); UpdateStatusFromJson(status); } } } </syntaxhighlight> 参考样例(80 行可读完):`D:\src\cookbook\adaption-reference\法睿兰达\CartAdapters\FY2208008\Scenes\AMRScene\ProjNameCar.cs`。 Reference (80 lines, easy to read end-to-end): `D:\src\cookbook\adaption-reference\法睿兰达\CartAdapters\FY2208008\Scenes\AMRScene\ProjNameCar.cs`. == 写一个自评估 Car 子类 / Subclassing Car for self-eval vehicles == 自评估车(磁导航 / 色带 / PLC 控制 / 二维码 等)的车载没有 MDCS,调度只能发"高层意图",由 ''SimpleCore 侧'' 的 AGV 接口翻译成厂商命令,再通过 RFID / Modbus / HTTP 下发。 Self-evaluating vehicles (magnetic-strip, tape, PLC-controlled, QR-only) don't run MDCS on-board. The scheduler dispatches "high-level intents", and a ''fleet-side'' `AGV : AGVInterface` translates them into vendor commands (RFID frames, Modbus, HTTP) and reads back `(x, y, th, siteID)` via `keepAlive()`. 经典样例:`D:\src\cookbook\adaption-reference\苏州凌鸟\Scenes\AMRScene1\Cars\MNavCar.cs`(293 行,含双向导航 + RFID 命令队列 + 启发式约束容器 `MNavCarHeuristics`)。 Canonical example: `D:\src\cookbook\adaption-reference\苏州凌鸟\Scenes\AMRScene1\Cars\MNavCar.cs` (293 lines — two-way navigation, RFID command queue, `MNavCarHeuristics` for no-go turns / forced reversals). 骨架 / Skeleton: <syntaxhighlight lang="csharp"> [CarType(Name = "MyMagCart", Title = "磁导航车 / Magnetic-strip cart")] public class MyMagCart : Car { public string address; public int rfidPort = 502; public class AGV : AGVInterface { private readonly MyMagCart car; public AGV(MyMagCart c) { car = c; } public override bool TryLock(int siteID, int route_id) { /* SimpleCore 内置 / inherit */ return base.TryLock(siteID, route_id); } public override bool Leave(int siteID) { return base.Leave(siteID); } public void Go(double sx, double sy, int sid, double dx, double dy, int did, int routeId) { Queue( async () => { // 在锁拿到后才发指令 / send intent only after lock granted if (!TryLock(did, routeId)) return; car.SendRFIDGoCommand(did); }, async () => { // 等到车真的到了 / wait until reported arrival while (car.lastReportedSiteId != did) await Task.Delay(100); Leave(sid); }); } } public override async Task actualSendScript(string script) { /* 自评估车通常不发 TopazScript */ } public override async Task keepAlive() { // Modbus / TCP 读位置并写入 (x, y, th, siteId) / poll position var (x, y, th, siteId) = await PollPLCAsync(address); UpdatePose(x, y, th); lastReportedSiteId = siteId; } } </syntaxhighlight> == 任务下发 / Mission dispatch flow == 调度内核 → SegmentPlan.Compile() → TopazScript(一段脚本) ↓ CarProgram.script (放入 Car.status.programs.forecasted) ↓ Car.actualSendScript(script) ↓ (ClumsyCar) ↓ (self-eval) HTTP POST 到车载 直接在调度侧本地跑 SelfEvaluating(agv, script) ↓ ↓ 车载 SelfEvaluating(agv, script) AGV.Go / AGV.Queue 等映射到 RFID/Modbus 命令 ↓ Clumsy Movement 执行 → 物理运动 == Reachability 与包络 / Reachability & envelope == SimpleCore 提供 ''可达性状态机''(见 [[Special:MyLanguage/可达性状态编程|可达性状态编程]])与 ''包络声明''(见 [[Special:MyLanguage/使用手册 - Simple:包络如何定义|包络如何定义]])。你的 Car 子类一般只需要: * 声明默认包络(无载货状态的车体包络)。 * 在带载 / 升叉 / 顶升等状态时声明 ''备用包络'',由 `HaveExtraEnvelope` 状态触发。 * 在不可达的路径段上声明 ''可达性规则''(例如:单向通道、最低剩余电量、强制掉头)。 A Car subclass typically: (1) declares the empty envelope, (2) declares alternate envelopes triggered by carry / fork-up / jack-up states, (3) declares reachability rules on path segments (one-way, min battery, mandatory reverse-out). == 用作算法宿主:在 SimpleComposer 之外用 SimpleCore == SimpleCore 是普通 .NET 库(`D:\src\Simple\SimpleCore\`),可以独立引用。最小集成: <syntaxhighlight lang="csharp"> using SimpleCore; var world = new World(); // 加载站点 + 路径图 / load sites + path graph world.LoadFromJson(File.ReadAllText("map.json")); var car1 = new MyAbstractCarImpl(); // 实现 AbstractCar 接口 var car2 = new MyAbstractCarImpl(); world.AddCar(car1); world.AddCar(car2); var planner = new DPSPlanner(world); var mission = planner.Plan( from: world.GetSite("A"), to: world.GetSite("Z")); foreach (var step in mission.Steps) { Console.WriteLine($"car={step.Car} site={step.Site} action={step.Action}"); } </syntaxhighlight> = 算法 / Algorithms = SimpleCore 内置: SimpleCore ships with: * DPS 调度(动态规划交管)—— `[[Special:MyLanguage/DPS调度算法详解|DPS调度算法详解]]` * 寻路启发器 —— `[[Special:MyLanguage/使用手册 - 寻路启发器功能|寻路启发器功能]]` * 流场规划 —— `[[Special:MyLanguage/流场规划|流场规划]]` * 网络流业务规划 —— `[[Special:MyLanguage/基于网络流的业务规划器|基于网络流的业务规划器]]` == 调试 / Debugging == * 用 [[Special:MyLanguage/CycleGUI|CycleGUI]] 把交管锁、车辆状态、路径片段实时画出来。 * SimpleComposer 的 ''Trace 面板'' 显示每条任务的 Queue 行执行轨迹。 * 死锁怀疑时打开 ''DPS 决策可视化''(DPS 算法会输出每次决策的剪枝树)。 * Use [[Special:MyLanguage/CycleGUI|CycleGUI]] to render locks, car state, path segments live. * SimpleComposer's ''Trace panel'' shows the Queue-row execution timeline per mission. * When deadlock is suspected, enable ''DPS decision visualisation''. == 相关页面 / See also == * [[Special:MyLanguage/AGV任务运行逻辑|AGV任务运行逻辑]] * [[Special:MyLanguage/调度内核运行原理|调度内核运行原理]] * [[Special:MyLanguage/DPS调度算法详解|DPS调度算法详解]] * [[Special:MyLanguage/Simple-API|Simple-API]] * [[Special:MyLanguage/开发手册 - SimpleComposer界面开发 - CAD工具|SimpleComposer界面开发]] * [[Special:MyLanguage/使用手册 - Simple:从使用到开发|Simple:从使用到开发]] [[Category:二次开发相关说明]] [[Category:开发手册]]
返回
如何基于SimpleCore核心库进行调度系统开发
。
导航菜单
个人工具
中文(中国大陆)
创建账号
登录
命名空间
页面
讨论
大陆简体
查看
阅读
查看源代码
查看历史
更多
导航
首页
最近更改
随机页面
MediaWiki帮助
工具
链入页面
相关更改
特殊页面
页面信息