Files
StandardSence/Doc/磁条交管逻辑与策略说明.html
T

356 lines
19 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!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>