磁导航1.0内部交管和信号交互

This commit is contained in:
ykkokluo
2026-08-24 14:55:35 +08:00
parent cacc5fea5c
commit 244b0af6c8
60 changed files with 6395 additions and 294 deletions
+355
View File
@@ -0,0 +1,355 @@
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<title>磁条交管逻辑与策略说明</title>
<style>
@page { size: A4; margin: 18mm 16mm 20mm 16mm; }
* { box-sizing: border-box; }
body {
font-family: "Microsoft YaHei", "PingFang SC", "SimSun", sans-serif;
font-size: 11pt;
line-height: 1.65;
color: #1a1a1a;
margin: 0;
}
h1 { font-size: 22pt; margin: 0 0 8px; letter-spacing: 0.5px; }
h2 {
font-size: 15pt;
margin: 28px 0 10px;
padding-bottom: 4px;
border-bottom: 2px solid #1f4e79;
color: #1f4e79;
page-break-after: avoid;
}
h3 {
font-size: 12.5pt;
margin: 18px 0 8px;
color: #2e75b6;
page-break-after: avoid;
}
h4 { font-size: 11.5pt; margin: 14px 0 6px; color: #333; }
p { margin: 6px 0 10px; text-align: justify; }
.cover {
text-align: center;
padding: 72px 24px 40px;
page-break-after: always;
}
.cover .sub { color: #555; font-size: 13pt; margin-top: 12px; }
.meta { margin-top: 64px; color: #444; font-size: 11pt; line-height: 1.9; }
.badge {
display: inline-block;
border: 1px solid #1f4e79;
color: #1f4e79;
padding: 2px 10px;
font-size: 10pt;
border-radius: 3px;
}
table {
width: 100%;
border-collapse: collapse;
margin: 8px 0 14px;
font-size: 10pt;
page-break-inside: avoid;
}
th, td {
border: 1px solid #b4b4b4;
padding: 6px 8px;
vertical-align: top;
}
th { background: #1f4e79; color: #fff; font-weight: 600; }
tr:nth-child(even) td { background: #f4f7fb; }
code, .mono {
font-family: Consolas, "Courier New", monospace;
font-size: 9.5pt;
background: #f3f3f3;
padding: 0 4px;
}
pre {
background: #f6f6f6;
border: 1px solid #ddd;
padding: 10px 12px;
font-size: 9pt;
line-height: 1.45;
white-space: pre-wrap;
word-break: break-all;
page-break-inside: avoid;
}
ol, ul { margin: 6px 0 12px; padding-left: 22px; }
li { margin: 3px 0; }
.note {
background: #fff8e6;
border-left: 4px solid #e6a23c;
padding: 8px 12px;
margin: 10px 0 14px;
}
.ok {
background: #eef8f0;
border-left: 4px solid #67c23a;
padding: 8px 12px;
margin: 10px 0 14px;
}
.warn {
background: #fdeeee;
border-left: 4px solid #c0392b;
padding: 8px 12px;
margin: 10px 0 14px;
}
.flow {
background: #f7fafc;
border: 1px solid #d0d7de;
padding: 10px 14px;
margin: 8px 0 14px;
page-break-inside: avoid;
}
.toc a { color: #1f4e79; text-decoration: none; }
.toc li { margin: 5px 0; }
.small { font-size: 9.5pt; color: #555; }
.footer-note { margin-top: 28px; color: #777; font-size: 9.5pt; }
</style>
</head>
<body>
<div class="cover">
<p class="badge">FASS 2.0 / scene.signal</p>
<h1>磁条交管逻辑与策略说明</h1>
<p class="sub">MagCar 管控区排队、兼容组并行与扫车放行</p>
<div class="meta">
适用对象:现场实施、调度配置、软件维护<br />
配置文件:SimpleLite 工作目录 <span class="mono">Config/Signal/mag-control-areas.json</span><br />
进程名称:磁条交管进程(MagTrafficMission<br />
文档日期:2026-08-18
</div>
</div>
<h2>1. 文档目的与适用范围</h2>
<p>本文说明 FASS 2.0 磁导航场景中,<b>磁条交管进程</b>如何根据管控区配置表对 <b>MagCar</b>(FASS 1.0 磁条车)进行停车等待、排队和放行。读者按本文即可完成十字路口、汇合点、单入口限流的配表,并理解扫车每一拍在做什么。</p>
<h3>1.1 适用</h3>
<ul>
<li>车型仅 <b>MagCar</b>。类型名必须恰好为 MagCar。</li>
<li>车在配置的<b>管控触发站点</b>停稳后,由本进程发 FASS 1.0 启动指令(<span class="mono">StartMagCar</span>)。</li>
<li>配置表由迷毂数据中心「磁条管控区」或进程内「打开管控区列表」维护,保存到上述 JSON。</li>
</ul>
<h3>1.2 不适用</h3>
<ul>
<li><b>Mag2Car</b> 不走本进程。Mag2 仍使用地图字段 <span class="mono">Fass2_ControlArea</span> / <span class="mono">Fass2_ControlCapacity</span>,以及任务报文 StartStop=12→11,本进程不改 Mag2 报文。</li>
<li>内核 <span class="mono">TrafficControl.TryLock</span> 点锁、交通管制进程 <span class="mono">traffic.json</span>、环线任务的流量字段,与本表独立。</li>
<li>不负责让车在触发点停车。车必须先由任务/站点策略停在触发点,本进程只负责「满足条件后发启动」。</li>
</ul>
<h2>2. 总体策略</h2>
<p>交管策略可以概括为四句话:</p>
<ol>
<li><b>按区绑定</b>:同一「管控区名称」的多行表示同一个物理路口(或汇合区)。</li>
<li><b>按线占用</b>:每一行有自己的内部站点列表和容量,占用只统计本行站点,不把整个路口并成一份。</li>
<li><b>冲突才互斥</b>:同区里,兼容组相同,或两行内部站点有交集,视为冲突,不能同时放;否则可并行。</li>
<li><b>冲突才排队</b>:优先级、到达时刻、等待时长只在冲突线路之间生效。不冲突的线路互不插队、互不占名额。</li>
</ol>
<div class="ok">
兼容组留空且同区多行都为空时,视为同一组,<b>整区互斥</b>,与早期「同区容量 1 只放一辆」的行为兼容。旧 JSON 不填兼容组即可沿用。
</div>
<h2>3. 配置表(交管策略字段)</h2>
<p>主键是<b>管控触发站点</b>。同一触发点多条启用记录只保留第一条。未启用(IsUse=false)的行保留在文件中但不参与判定。</p>
<table>
<tr><th style="width:18%">字段</th><th style="width:16%">JSON 键</th><th>含义与配法</th></tr>
<tr>
<td>管控触发站点</td>
<td class="mono">TriggerSit</td>
<td>MagCar 必须停稳在此站点后才进入本行排队。应对齐现场停车点,不要把路口内部行驶点当成触发点。</td>
</tr>
<tr>
<td>管控区名称</td>
<td class="mono">AreaName</td>
<td>同名多行绑成一个路口,共用等待时长(取最长)。空则该触发点自成一区,相当于单入口限流。</td>
</tr>
<tr>
<td>兼容组</td>
<td class="mono">CompatGroup</td>
<td>同区里互不干扰的线路填<b>不同组名</b>(如东西 EW、南北 NS)。会交叉、会抢同一段路的填<b>同组</b>。空字符串与其他空组视为同组。</td>
</tr>
<tr>
<td>优先级</td>
<td class="mono">Priority</td>
<td>整数,越大越先放。干道高、支路低。只在冲突线路之间比较。</td>
</tr>
<tr>
<td>等待时长</td>
<td class="mono">WaitSeconds</td>
<td>秒。同区多行不一致时取<b>最长</b>。≤0 表示不做插队。用于防止支路被干道一直堵住。</td>
</tr>
<tr>
<td>管控区站点列表</td>
<td class="mono">ControlArea</td>
<td>本线路穿过路口的<b>内部站点</b>,逗号分隔,如 10,11。不要把各方向排队触发点写进来。空则回退为触发点本身。</td>
</tr>
<tr>
<td>区内最多几辆</td>
<td class="mono">ControlCapacity</td>
<td>本行容量。小于 1 按 1 处理。统计范围:占用本行内部站点的车 + 与本行冲突、已发启动但未进区的在途车。</td>
</tr>
<tr>
<td>放行后启停字节</td>
<td class="mono">ControlReleaseStartStop</td>
<td>兼容字段。MagCar 放行发 FASS 1.0 启动指令,不使用该字节。</td>
</tr>
<tr>
<td>启用</td>
<td class="mono">IsUse</td>
<td>false 时本行不生效。</td>
</tr>
</table>
<h3>3.1 运行态(不入库)</h3>
<table>
<tr><th>名称</th><th>说明</th></tr>
<tr>
<td>到达时刻</td>
<td>进程第一次扫到该 MagCar 停在该触发点的时间。开走后再回来重新计时。不写入 JSON。</td>
</tr>
<tr>
<td>在途占额</td>
<td>已对该车发过启动,但尚未占用本行内部站点。避免「刚放行、车还在触发点」时下一拍又放一辆冲突车。</td>
</tr>
</table>
<h3>3.2 进程参数</h3>
<table>
<tr><th>参数</th><th>默认</th><th>说明</th></tr>
<tr><td>TickMs</td><td>1000</td><td>扫车周期(毫秒),下限按 200ms 夹紧。</td></tr>
<tr><td>PendingTimeoutSeconds</td><td>8</td><td>在途超时(秒)。超时后取消占额,防止启动失败把名额卡死。</td></tr>
<tr><td>AreasPath</td><td class="mono">Config\Signal\mag-control-areas.json</td><td>相对路径只解析 SimpleLite 工作目录,不读 plugins 目录。</td></tr>
</table>
<h2>4. 交管逻辑(扫车每一拍)</h2>
<p>进程启动或点「重新加载配置」后,读取启用规则。此后约每秒执行一次 <span class="mono">ScanAndRelease</span></p>
<div class="flow">
<b>单拍流程</b>
<ol>
<li>快照全场车辆:站点(siteID / GetLastSite)、占用站点(siteID + holdingLocks)、是否 MagCar、是否停稳。</li>
<li>启用规则按管控区名称分组(空名称则按「#sit:触发点」自成一区)。同区等待时长取各行之最长。</li>
<li>清理到达时刻:已离开原触发点的车重新计时。</li>
<li>清理在途:车消失、超时、或已经踩到本行内部站点 → 去掉在途标记(进区后改由占用统计)。</li>
<li>收集本区停在各触发点上的停稳 MagCar,记为等待队列;已在途的不再重复发启动。</li>
<li>按第 6 节规则排序,然后<b>从队头到队尾贪心</b>:本行有空位则发启动并登记在途;无空位则跳过,继续看后面不冲突的车。</li>
<li>放行动作:先尝试停充,再 <span class="mono">StartMagCar</span>FASS 1.0 启动)。</li>
</ol>
</div>
<h3>4.1 何谓停稳</h3>
<p>状态不是「运行中 / Running」。下列视为可排队:停止中、充电中、WaitPass,以及没有 pendingLocks、没有 occupied 标签的非运行状态。充电中的车放行前会尝试 StopCharge。</p>
<h3>4.2 占用怎么数</h3>
<p>对某一等待车辆,只统计占用<b>该行 ControlArea</b> 的他车。排队触发点上尚未放行的 MagCar 不计入占用(它们还在区外等候)。本车不计。</p>
<p>占用站点来源:车辆当前 <span class="mono">siteID</span>,以及 <span class="mono">status.holdingLocks</span>。任一命中列表即算 1 辆。</p>
<h3>4.3 可放行数(空位)</h3>
<p class="mono">空位 = 本行容量 − 占用本行站点的车 − 与本行冲突的在途车</p>
<p>空位 ≥ 1 才对该车发启动。容量 1 表示:本线路内部(含冲突在途)最多一辆。</p>
<div class="note">
在途车若已经进入本行内部站点,只算「占用」,不再重复算「在途」,避免一名额扣两次。
</div>
<h2>5. 冲突判定(能否并行)</h2>
<p>只比较<b>同一管控区名称</b>下的两行(或一行与一条在途记录)。不同区互不影响。</p>
<p>满足下面<b>任一</b>条件即冲突(互斥):</p>
<ol>
<li>兼容组相同(不区分大小写,去首尾空格)。两边都为空也算相同 → 整区互斥。</li>
<li>两行的管控区站点列表有交集。</li>
</ol>
<p>两组都不同、且站点列表不相交 → <b>不冲突,可同时放行</b></p>
<table>
<tr><th>场景</th><th>兼容组</th><th>内部站点</th><th>结果</th></tr>
<tr><td>旧表、未填兼容组</td><td>都空</td><td>任意</td><td>同区全部互斥</td></tr>
<tr><td>东西对向直行</td><td>都填 EW</td><td>可不相交</td><td>同组,互斥(对向若也要并行,应拆成不同组且站点不相交)</td></tr>
<tr><td>东西 / 南北互不交叉</td><td>EW 与 NS</td><td>10,11 与 20,21</td><td>不冲突,可并行</td></tr>
<tr><td>组名不同但抢同一段</td><td>EW 与 NS</td><td>都含 15</td><td>站点相交,仍互斥</td></tr>
<tr><td>左转与对向直行交叉</td><td>建议同组,或站点列出交叉段</td><td>有交集或同组</td><td>互斥</td></tr>
</table>
<div class="warn">
若四个方向都复制同一份内部站点(例如都写 10,11,12,13),则即使兼容组不同也会因站点相交而全部互斥。要对向并行,必须按方向拆开内部点,且不要把触发点写进列表。
</div>
<h2>6. 排队规则(仅冲突线路)</h2>
<p>同一管控区内,先按下列比较函数排序,再贪心放行。不冲突的车排序先后不影响能否同时过,只影响扫描顺序。</p>
<h3>6.1 插队(等待时长)</h3>
<p>当同区等待时长 W &gt; 0,且两车优先级不同:</p>
<p><b>高优先级到达时刻 − 低优先级到达时刻 &gt; W</b>,则低优先级先走。</p>
<p>含义:低优先级更早到达,且已经比高优先级早到超过 W 秒,避免支路饿死。差值未超过 W 时,仍让优先级高的先走。</p>
<p>使用「&gt;」而不是「≥」。例如 W=10,恰好早到 10 秒时仍按优先级。</p>
<h3>6.2 默认顺序</h3>
<ol>
<li>未触发插队时:优先级数值大的在前。</li>
<li>优先级相同:到达时刻早的在前。</li>
<li>仍相同:车号小的在前(稳定次序)。</li>
</ol>
<h3>6.3 贪心,而不是只放队头</h3>
<p>排序后从前往后看。队头因本线已满或冲突在途没有空位时,<b>跳过队头</b>,继续检查后面的车。这样南北已占用时,队头若是南北,不会挡住后面可以走的东西向车辆。</p>
<h2>7. 放行与在途生命周期</h2>
<ol>
<li>判定可放 → StopCharge(若有)→ StartMagCar。</li>
<li>登记在途:记录区名、触发点、兼容组、本行内部站点、放行时刻。</li>
<li>下一拍该车仍停在触发点:不再重复启动,但作为冲突在途占额。</li>
<li>车进入本行内部站点:清除在途,改为占用统计。</li>
<li>超过 PendingTimeoutSeconds(默认 8 秒)仍未进区:清除在途,避免卡死。车若仍停在触发点,下一拍可再次进入排队。</li>
<li>车离线或从系统消失:清除在途与到达时刻。</li>
</ol>
<p>重新加载配置会清空到达时刻和在途表,请避免在路口有车等待时频繁重载。</p>
<h2>8. 配置示例</h2>
<h3>8.1 十字路口:东西与南北并行</h3>
<p>区名均为「十字路口A」。东西兼容组 EW,南北 NS。内部站点按方向拆开,容量均为 1。东西对向若现场确实会在同一段交汇,应把对向也拆站点或改为同组。</p>
<pre>[
{ "TriggerSit": 8, "AreaName": "十字路口A", "CompatGroup": "EW", "Priority": 10, "WaitSeconds": 15, "ControlArea": "10,11", "ControlCapacity": 1, "IsUse": true },
{ "TriggerSit": 18, "AreaName": "十字路口A", "CompatGroup": "EW", "Priority": 10, "WaitSeconds": 15, "ControlArea": "12,13", "ControlCapacity": 1, "IsUse": true },
{ "TriggerSit": 9, "AreaName": "十字路口A", "CompatGroup": "NS", "Priority": 5, "WaitSeconds": 15, "ControlArea": "20,21", "ControlCapacity": 1, "IsUse": true },
{ "TriggerSit": 19, "AreaName": "十字路口A", "CompatGroup": "NS", "Priority": 5, "WaitSeconds": 15, "ControlArea": "22,23", "ControlCapacity": 1, "IsUse": true }
]</pre>
<p>效果:东西一辆与南北一辆可同时过;同一兼容组内容量 1,同向或同组第二辆要等;南北优先级较低,但若已等待超过 15 秒且比干道更早到达,可以插队。</p>
<h3>8.2 单入口限流(旧用法)</h3>
<p>只配一行:触发点 8,区名为空,兼容组空,ControlArea 写内部点,容量 1。该点自成一区,他车占用内部点时不放行。</p>
<h3>8.3 支路防饿死</h3>
<p>干道 Priority=10,支路 Priority=1,同区 WaitSeconds=20。支路先到、干道后到且时间差大于 20 秒时放支路;否则放干道。</p>
<h2>9. 现场操作</h2>
<ol>
<li>确认已加载信号插件 <span class="mono">StandardScene.Signal.dll</span>,进程栏中有「磁条交管进程」。</li>
<li>将 JSON 放到 SimpleLite 工作目录 <span class="mono">Config\Signal\mag-control-areas.json</span>。可用迷毂数据中心「磁条管控区」编辑(含兼容组列,需较新的 MiGu.Server)。</li>
<li>添加并<b>启动</b>磁条交管进程。改表后点「重新加载配置」,或进程内打开列表保存后重载。</li>
<li>状态栏可见:规则条数、触发点等待数、在途数、扫车拍数。</li>
<li>诊断通道 <span class="mono">MagTraffic</span> 会记录放行:区名、兼容组、触发点、本线占用/容量、冲突在途。</li>
</ol>
<div class="note">
交管不会替任务系统停车。请保证 MagCar 到达触发点时处于停止,否则不会进入排队,也不会发启动。
</div>
<h2>10. 与 Mag2 / 其他交管的边界</h2>
<table>
<tr><th>机制</th><th>对象</th><th>关系</th></tr>
<tr><td>本进程 mag-control-areas.json</td><td>MagCar</td><td>触发点停稳后发 StartMagCar</td></tr>
<tr><td>地图 Fass2_ControlArea 等</td><td>Mag2Car</td><td>StartStop=12 等待,放行改本车报文,本进程不介入</td></tr>
<tr><td>TrafficControl 点锁</td><td>MagCar / Mag2 路径执行</td><td>锁站点资源,与本表并行存在</td></tr>
<tr><td>交通管制 traffic.json</td><td>区域互斥钩子</td><td>需单独启动「交通管制」进程</td></tr>
</table>
<h2>11. 常见问题</h2>
<h4>十字路口空着,四个方向却一起冲出去?</h4>
<p>兼容组未区分,或内部站点写成同一串。请按方向拆兼容组并拆开 ControlArea。</p>
<h4>四个方向谁也不走?</h4>
<p>把四个停车触发点写进了 ControlArea,容量又为 1,等待车被当成区内占用。触发点不要写入内部列表。</p>
<h4>改了 JSON 不生效?</h4>
<p>必须点「重新加载配置」或重启进程。路径必须是工作目录 Config/Signal,不是插件目录。</p>
<h4>Mag2 在同一路口不停不等?</h4>
<p>本策略不管 Mag2。Mag2 需在地图配置 Fass2_StartStop=12 及管控区字段。</p>
<h4>容量填 0</h4>
<p>当前实现小于 1 按 1 处理,不会变成不限流。</p>
<h4>对向也要并行,但写成了同一兼容组?</h4>
<p>同组即互斥。对向要并行:不同组名,且内部站点不相交。</p>
<h2>12. 逻辑对照(维护用)</h2>
<table>
<tr><th>内容</th><th>位置</th></tr>
<tr><td>表字段</td><td class="mono">StandardScene.Signal / MagControlAreaModel</td></tr>
<tr><td>冲突、占用、容量</td><td class="mono">MagControlAreaGate</td></tr>
<tr><td>扫车、排队、在途、贪心放行</td><td class="mono">MagControlRuntime.ScanAndRelease</td></tr>
<tr><td>进程与重载</td><td class="mono">MagTrafficMission</td></tr>
<tr><td>迷毂列定义</td><td class="mono">MiGu.Server / SignalDataStore(表 id: mag-control</td></tr>
</table>
<p class="footer-note">本文描述 2026-08-18 代码行为。若后续调整兼容组或容量语义,以源码为准并同步修订本文。</p>
</body>
</html>