fix: render M3 workbench wiring
This commit is contained in:
@@ -246,6 +246,15 @@ function runStaticSmoke() {
|
||||
]
|
||||
});
|
||||
|
||||
addCheck(checks, blockers, "m3-rendered-workbench-not-m5-fixture", m3RenderedWorkbenchNotM5Fixture(files.app), "Right workbench hardware status and wiring table render the M3 chain, not M5 fixture rows or undefined DOM-node rows.", {
|
||||
blocker: "runtime_blocker",
|
||||
evidence: [
|
||||
"renderHardwareStatus passes data objects into hardwareGroup",
|
||||
"renderWiringList uses M3_TRUSTED_ROUTE",
|
||||
"M5 activeConnections are not mapped into the workbench wiring table"
|
||||
]
|
||||
});
|
||||
|
||||
const help = inspectHelpContract(files);
|
||||
addCheck(checks, blockers, "help-md-contract", help.status, help.summary, {
|
||||
blocker: help.blocker,
|
||||
@@ -558,6 +567,21 @@ function m3FixtureCountsNotDevLive(app) {
|
||||
);
|
||||
}
|
||||
|
||||
function m3RenderedWorkbenchNotM5Fixture(app) {
|
||||
const hardwareBody = functionBody(app, "renderHardwareStatus");
|
||||
const hardwareGroupBody = functionBody(app, "hardwareGroup");
|
||||
const wiringBody = functionBody(app, "renderWiringList");
|
||||
return (
|
||||
/group\.rows\.map\(hardwareRow\)/u.test(hardwareGroupBody) &&
|
||||
!/hardwareRow\(\s*\{/u.test(hardwareBody) &&
|
||||
/M3_TRUSTED_ROUTE\.fromResourceId/u.test(wiringBody) &&
|
||||
/M3_TRUSTED_ROUTE\.patchPanelServiceId/u.test(wiringBody) &&
|
||||
/M3_TRUSTED_ROUTE\.toResourceId/u.test(wiringBody) &&
|
||||
!/activeConnections\.map/u.test(wiringBody) &&
|
||||
!/link\.fromResourceId/u.test(wiringBody)
|
||||
);
|
||||
}
|
||||
|
||||
function functionBody(source, functionName) {
|
||||
const match = source.match(new RegExp(`function\\s+${escapeRegExp(functionName)}\\s*\\([^)]*\\)\\s*\\{`, "u"));
|
||||
if (!match) return "";
|
||||
|
||||
+38
-53
@@ -311,7 +311,7 @@ function renderStaticWorkbench() {
|
||||
renderUnblockList();
|
||||
renderHardwareStatus(null);
|
||||
renderDrafts();
|
||||
renderWiringList();
|
||||
renderWiringList(null);
|
||||
renderRecords(null);
|
||||
renderDiagnostics(null);
|
||||
renderGateDiagnostics();
|
||||
@@ -459,6 +459,7 @@ function renderLiveSurface(live) {
|
||||
: "同源 API 暂不可用,当前仍可使用本地草稿和已加载资源。";
|
||||
|
||||
renderHardwareStatus(runtimeSummary);
|
||||
renderWiringList(runtimeSummary);
|
||||
renderRecords(live);
|
||||
renderDiagnostics(live);
|
||||
renderMethodList(methods.length > 0 ? methods.filter((method) => rpcReadMethods.includes(method)) : rpcReadMethods, reachable ? "live" : "degraded");
|
||||
@@ -640,7 +641,7 @@ function renderHardwareStatus(summary) {
|
||||
hardwareGroup({
|
||||
title: "Gateway/Box 在线",
|
||||
rows: [
|
||||
hardwareRow({
|
||||
{
|
||||
label: "Gateway-SIMU",
|
||||
value: counts ? `${counts.gatewaySessions ?? 0} 会话` : "离线查看",
|
||||
detail: counts
|
||||
@@ -648,8 +649,8 @@ function renderHardwareStatus(summary) {
|
||||
: `${gateSummary.gatewaySessionCount} 个 Gateway 会话来自 SOURCE fixture。`,
|
||||
sourceKind: runtimeCountsSourceKind,
|
||||
tone: runtimeCountsTone
|
||||
}),
|
||||
hardwareRow({
|
||||
},
|
||||
{
|
||||
label: "BOX-SIMU",
|
||||
value: counts ? `${counts.boxResources ?? 0} 资源` : "离线查看",
|
||||
detail: counts
|
||||
@@ -657,66 +658,66 @@ function renderHardwareStatus(summary) {
|
||||
: `${gateSummary.boxResourceCount} 个 BOX 资源来自 SOURCE fixture。`,
|
||||
sourceKind: runtimeCountsSourceKind,
|
||||
tone: runtimeCountsTone
|
||||
})
|
||||
}
|
||||
]
|
||||
}),
|
||||
hardwareGroup({
|
||||
title: "BOX-SIMU 端口",
|
||||
rows: [
|
||||
hardwareRow({
|
||||
{
|
||||
label: "DO1",
|
||||
value: m3Live ? "verified" : "not wired",
|
||||
detail: m3Live ? m3LiveDetail(liveM3Evidence) : "BLOCKED:没有 DO1 电平的 live M3 operation/trace/audit/evidence 证据。",
|
||||
sourceKind: m3Live ? "DEV-LIVE" : "BLOCKED",
|
||||
tone: m3Live ? "live" : "blocked"
|
||||
}),
|
||||
hardwareRow({
|
||||
},
|
||||
{
|
||||
label: "DI1",
|
||||
value: m3Live ? "verified" : "not wired",
|
||||
detail: m3Live ? m3LiveDetail(liveM3Evidence) : "BLOCKED:没有 DI1 回读的 live M3 operation/trace/audit/evidence 证据。",
|
||||
sourceKind: m3Live ? "DEV-LIVE" : "BLOCKED",
|
||||
tone: m3Live ? "live" : "blocked"
|
||||
}),
|
||||
hardwareRow({
|
||||
},
|
||||
{
|
||||
label: "AI/AO/FREQ",
|
||||
value: "future capability",
|
||||
detail: "当前未声明模拟量/频率闭环,按 future capability 处理。",
|
||||
sourceKind: "SOURCE",
|
||||
tone: "disabled"
|
||||
})
|
||||
}
|
||||
]
|
||||
}),
|
||||
hardwareGroup({
|
||||
title: "Patch Panel 连线",
|
||||
rows: [
|
||||
hardwareRow({
|
||||
{
|
||||
label: "hwlab-patch-panel",
|
||||
value: statusLabel(patchPanel.state),
|
||||
detail: m3Live
|
||||
? `DEV-LIVE patch-panel live report ${liveM3Evidence.patchPanelLiveReport.reportId ?? liveM3Evidence.patchPanelLiveReport.patchPanelStatusId} 已绑定 M3 route。`
|
||||
: `${patchPanel.activeConnectionCount} 条 SOURCE 接线;当前 fixture 为 ${describePatchPanelConnections(patchPanel)}。`,
|
||||
: `${patchPanel.activeConnectionCount} 条 SOURCE fixture 接线;工作台只展示 required M3 route,未升级为 DEV-LIVE。`,
|
||||
sourceKind: m3Live ? "DEV-LIVE" : "SOURCE",
|
||||
tone: patchTone
|
||||
}),
|
||||
hardwareRow({
|
||||
},
|
||||
{
|
||||
label: "DO1 -> patch-panel -> DI1",
|
||||
value: linkStatusLabel(linkTone),
|
||||
detail: m3LinkDetail({ liveM3Evidence, sourceLink, patchPanel, counts }),
|
||||
sourceKind: m3Live ? "DEV-LIVE" : "BLOCKED",
|
||||
tone: linkTone
|
||||
})
|
||||
}
|
||||
]
|
||||
}),
|
||||
hardwareGroup({
|
||||
title: "写入边界",
|
||||
rows: [
|
||||
hardwareRow({
|
||||
{
|
||||
label: "硬件控制",
|
||||
value: "已禁用",
|
||||
detail: "Web 不提供硬件写 RPC;不会新增伪硬件写操作。",
|
||||
sourceKind: "SOURCE",
|
||||
tone: "source"
|
||||
})
|
||||
}
|
||||
]
|
||||
})
|
||||
);
|
||||
@@ -757,13 +758,6 @@ function trustedM3Link(connections) {
|
||||
);
|
||||
}
|
||||
|
||||
function describePatchPanelConnections(patchPanel) {
|
||||
if (!patchPanel.activeConnections.length) return "无活动连线";
|
||||
return patchPanel.activeConnections
|
||||
.map((link) => `${link.fromResourceId}:${link.fromPort} -> ${link.toResourceId}:${link.toPort}`)
|
||||
.join(";");
|
||||
}
|
||||
|
||||
function m3LinkDetail({ liveM3Evidence, sourceLink, patchPanel, counts }) {
|
||||
if (hasTrustedM3LiveEvidence(liveM3Evidence)) {
|
||||
return `DEV-LIVE M3:${M3_TRUSTED_ROUTE.fromResourceId}:${M3_TRUSTED_ROUTE.fromPort} -> ${M3_TRUSTED_ROUTE.patchPanelServiceId} -> ${M3_TRUSTED_ROUTE.toResourceId}:${M3_TRUSTED_ROUTE.toPort};${m3LiveDetail(liveM3Evidence)}。`;
|
||||
@@ -775,7 +769,7 @@ function m3LinkDetail({ liveM3Evidence, sourceLink, patchPanel, counts }) {
|
||||
return "BLOCKED:runtime counts 已返回,但 counts 不是 patch-panel live report,不能证明 M3 DEV-LIVE。";
|
||||
}
|
||||
if (patchPanel.activeConnectionCount > 0) {
|
||||
return `SOURCE 仅看到 patch-panel 接线:${describePatchPanelConnections(patchPanel)};未冒充 live。`;
|
||||
return `SOURCE 仅看到非 M3 patch-panel fixture 接线;required M3 route ${M3_TRUSTED_ROUTE.fromResourceId}:${M3_TRUSTED_ROUTE.fromPort} -> ${M3_TRUSTED_ROUTE.patchPanelServiceId} -> ${M3_TRUSTED_ROUTE.toResourceId}:${M3_TRUSTED_ROUTE.toPort} 仍 BLOCKED,未冒充 live。`;
|
||||
}
|
||||
return "BLOCKED:当前没有 DO1 -> patch-panel -> DI1 的可信链路证据。";
|
||||
}
|
||||
@@ -885,35 +879,26 @@ function renderDrafts() {
|
||||
replaceChildren(el.controlList, ...rows.map(infoCard));
|
||||
}
|
||||
|
||||
function renderWiringList() {
|
||||
function renderWiringList(summary = null) {
|
||||
const patchPanel = gateSummary.topology.patchPanel;
|
||||
const wiringStep = gateSummary.steps.find((step) => step.kind === "wiring");
|
||||
const traceEvidence = [wiringStep?.id, patchPanel.patchPanelStatusId].filter(Boolean).join(" / ");
|
||||
const rows = patchPanel.activeConnections.map((link) => {
|
||||
const tr = document.createElement("tr");
|
||||
tr.append(cell(link.fromResourceId, "mono wrap"));
|
||||
tr.append(cell(link.fromPort, "mono"));
|
||||
tr.append(cell(patchPanel.serviceId, "mono wrap"));
|
||||
tr.append(cell(link.toResourceId, "mono wrap"));
|
||||
tr.append(cell(link.toPort, "mono"));
|
||||
const status = document.createElement("td");
|
||||
status.append(badge(statusLabel(patchPanel.state), patchPanel.state));
|
||||
tr.append(status);
|
||||
tr.append(cell(`${patchPanel.environment} / SOURCE`, "wrap"));
|
||||
tr.append(cell(traceEvidence || "无", "mono wrap"));
|
||||
return tr;
|
||||
});
|
||||
const liveM3Evidence = trustedM3LiveEvidenceFrom(summary);
|
||||
const m3Live = hasTrustedM3LiveEvidence(liveM3Evidence);
|
||||
const traceEvidence = m3Live
|
||||
? [liveM3Evidence.operationId, liveM3Evidence.traceId, liveM3Evidence.auditId, liveM3Evidence.evidenceId].join(" / ")
|
||||
: "m3-route-required / waiting-for-dev-live";
|
||||
const tr = document.createElement("tr");
|
||||
tr.append(cell(M3_TRUSTED_ROUTE.fromResourceId, "mono wrap"));
|
||||
tr.append(cell(M3_TRUSTED_ROUTE.fromPort, "mono"));
|
||||
tr.append(cell(M3_TRUSTED_ROUTE.patchPanelServiceId, "mono wrap"));
|
||||
tr.append(cell(M3_TRUSTED_ROUTE.toResourceId, "mono wrap"));
|
||||
tr.append(cell(M3_TRUSTED_ROUTE.toPort, "mono"));
|
||||
const status = document.createElement("td");
|
||||
status.append(badge(m3Live ? "开发实况 DEV-LIVE" : "阻塞 BLOCKED", m3Live ? "dev-live" : "blocked"));
|
||||
tr.append(status);
|
||||
tr.append(cell(m3Live ? "DEV-LIVE" : "SOURCE / required M3 route", "wrap"));
|
||||
tr.append(cell(traceEvidence, "mono wrap"));
|
||||
|
||||
if (rows.length === 0) {
|
||||
const tr = document.createElement("tr");
|
||||
const td = document.createElement("td");
|
||||
td.colSpan = 8;
|
||||
td.textContent = "当前没有接线记录。";
|
||||
tr.append(td);
|
||||
rows.push(tr);
|
||||
}
|
||||
|
||||
replaceChildren(el.wiringBody, ...rows);
|
||||
replaceChildren(el.wiringBody, tr);
|
||||
}
|
||||
|
||||
function renderRecords(live) {
|
||||
|
||||
@@ -89,6 +89,8 @@ assert.match(app, /function normalizePort/);
|
||||
assert.match(app, /LIVE_M3_ID_FIELDS/);
|
||||
assert.match(app, /patchPanelLiveReport/);
|
||||
assert.match(app, /runtimeCountsSourceKind = "SOURCE"/);
|
||||
assert.doesNotMatch(functionBody(app, "renderHardwareStatus"), /hardwareRow\(\s*\{/u);
|
||||
assert.match(functionBody(app, "hardwareGroup"), /group\.rows\.map\(hardwareRow\)/u);
|
||||
assert.doesNotMatch(app, /sourceKind:\s*counts\s*\?\s*"DEV-LIVE"/);
|
||||
assert.doesNotMatch(app, /counts\s*&&\s*(?:liveLink|sourceLink|m3Link)[\s\S]{0,80}\?\s*"DEV-LIVE"/);
|
||||
for (const hardwareTerm of [
|
||||
@@ -197,6 +199,10 @@ assert.match(html, /id="unblock-list"/);
|
||||
assert.match(app, /DB live readiness/);
|
||||
assert.match(app, /res_boxsimu_1:DO1 -> res_boxsimu_2:DI1/);
|
||||
assert.match(app, /patch-panel DO1 -> DI1/);
|
||||
assert.match(functionBody(app, "renderWiringList"), /M3_TRUSTED_ROUTE\.fromResourceId/);
|
||||
assert.match(functionBody(app, "renderWiringList"), /M3_TRUSTED_ROUTE\.patchPanelServiceId/);
|
||||
assert.doesNotMatch(functionBody(app, "renderWiringList"), /activeConnections\.map/u);
|
||||
assert.doesNotMatch(functionBody(app, "renderWiringList"), /link\.fromResourceId/u);
|
||||
assert.match(buildScript, /"help\.md"/);
|
||||
assert.match(buildScript, /"third_party\/marked\/marked\.esm\.js"/);
|
||||
assert.match(buildScript, /"third_party\/marked\/LICENSE"/);
|
||||
@@ -298,3 +304,18 @@ function beforeSection(source, viewId) {
|
||||
const index = source.search(new RegExp(`<section\\b[^>]*\\bdata-view=["']${escapeRegExp(viewId)}["'][^>]*>`, "u"));
|
||||
return index === -1 ? source : source.slice(0, index);
|
||||
}
|
||||
|
||||
function functionBody(source, functionName) {
|
||||
const match = source.match(new RegExp(`function\\s+${escapeRegExp(functionName)}\\s*\\([^)]*\\)\\s*\\{`, "u"));
|
||||
if (!match) return "";
|
||||
let depth = 0;
|
||||
for (let index = match.index + match[0].length - 1; index < source.length; index += 1) {
|
||||
const char = source[index];
|
||||
if (char === "{") depth += 1;
|
||||
if (char === "}") {
|
||||
depth -= 1;
|
||||
if (depth === 0) return source.slice(match.index, index + 1);
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user