接入虚拟HAL仿真配置promotion证据

This commit is contained in:
2026-06-18 04:47:30 +08:00
parent abf858641f
commit 0bc6896b36
24 changed files with 2879 additions and 108 deletions

View File

@@ -121,6 +121,202 @@ export const VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS = Object.freeze([
}),
]);
export const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = Object.freeze([
Object.freeze({
id: "qtdragon-multi-joint-on-abort",
label: "QtDragon multi-joint on-abort",
simConfigTarget: "qtdragon-on-abort",
sourceFiles: Object.freeze([
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
]),
iniPath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini",
gcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc",
currentNodeInventoryStatus: "PASS",
currentMatrixBrowserStatus: "delegated-to-woodpecker",
dependencyClass: "hal-ui-multi-joint",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
]),
blockedKind: "-",
nonMainFixture: false,
evidence: "Node inventory already covers the LinuxCNC QtDragon multi-joint on-abort program; virtual HAL supplies the HAL/UI/motion evidence needed for explicit browser diagnostics.",
}),
Object.freeze({
id: "qtdragon-xyz-on-abort",
label: "QtDragon XYZ on-abort",
simConfigTarget: "qtdragon-on-abort",
sourceFiles: Object.freeze([
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/qtdragon_inch.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc",
]),
iniPath: "linuxcnc/configs/sim/qtdragon/qtdragon_xyz/qtdragon_inch.ini",
gcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc",
currentNodeInventoryStatus: "PASS",
currentMatrixBrowserStatus: "delegated-to-woodpecker",
dependencyClass: "hal-ui-on-abort",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
]),
blockedKind: "-",
nonMainFixture: false,
evidence: "Node inventory already covers the LinuxCNC QtDragon XYZ on-abort program; virtual HAL can carry explicit browser diagnostics without changing CNC semantics.",
}),
Object.freeze({
id: "qtdragon-xyz45-on-abort",
label: "QtDragon XYZA on-abort",
simConfigTarget: "qtdragon-on-abort",
sourceFiles: Object.freeze([
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/qtdragon_xyza.ini",
"linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/on_abort.ngc",
]),
iniPath: "linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/qtdragon_xyza.ini",
gcodePath: "linuxcnc/configs/sim/qtdragon/qtdragon_xyz45/on_abort.ngc",
currentNodeInventoryStatus: "PASS",
currentMatrixBrowserStatus: "delegated-to-woodpecker",
dependencyClass: "hal-ui-rotary-axis",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
]),
blockedKind: "-",
nonMainFixture: false,
evidence: "The LinuxCNC QtDragon XYZA on-abort case adds rotary-axis machine context without Python remap or external process runtime.",
}),
Object.freeze({
id: "qtdragon-hd-xyz-on-abort",
label: "QtDragon HD XYZ on-abort",
simConfigTarget: "qtdragon-on-abort",
sourceFiles: Object.freeze([
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/qtdragon_hd_vertical.ini",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc",
]),
iniPath: "linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/qtdragon_hd_vertical.ini",
gcodePath: "linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_xyz/on_abort.ngc",
currentNodeInventoryStatus: "PASS",
currentMatrixBrowserStatus: "delegated-to-woodpecker",
dependencyClass: "hal-ui-on-abort",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
]),
blockedKind: "-",
nonMainFixture: false,
evidence: "The LinuxCNC QtDragon HD XYZ on-abort case is a deterministic HAL/UI workflow suitable for browser diagnostics evidence.",
}),
Object.freeze({
id: "qtdragon-hd-z-compensation-on-abort",
label: "QtDragon HD z-compensation on-abort",
simConfigTarget: "qtdragon-on-abort",
sourceFiles: Object.freeze([
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/qtdragon_hd_z_compensation.ini",
"linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/on_abort.ngc",
]),
iniPath: "linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/qtdragon_hd_z_compensation.ini",
gcodePath: "linuxcnc/configs/sim/qtdragon_hd/qtdragon_hd_z_compensation/on_abort.ngc",
currentNodeInventoryStatus: "PASS",
currentMatrixBrowserStatus: "delegated-to-woodpecker",
dependencyClass: "hal-ui-z-compensation",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
]),
blockedKind: "-",
nonMainFixture: false,
evidence: "The LinuxCNC QtDragon HD z-compensation on-abort case should only promote with explicit source evidence for the z-compensation INI.",
}),
Object.freeze({
id: "qtvcp-screens-qtdragon-on-abort",
label: "QTVCP screens QtDragon on-abort",
simConfigTarget: "qtdragon-on-abort",
sourceFiles: Object.freeze([
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/qtdragon_mpg.ini",
"linuxcnc/configs/sim/qtvcp_screens/qtdragon/on_abort.ngc",
]),
iniPath: "linuxcnc/configs/sim/qtvcp_screens/qtdragon/qtdragon_mpg.ini",
gcodePath: "linuxcnc/configs/sim/qtvcp_screens/qtdragon/on_abort.ngc",
currentNodeInventoryStatus: "PASS",
currentMatrixBrowserStatus: "delegated-to-woodpecker",
dependencyClass: "hal-ui-on-abort",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
]),
blockedKind: "-",
nonMainFixture: false,
evidence: "The LinuxCNC QTVCP QtDragon on-abort case has no hard runtime block and can be promoted as explicit browser diagnostics evidence.",
}),
Object.freeze({
id: "puma-seam-weld",
label: "PUMA seam weld",
simConfigTarget: "vismach-remap-sims",
sourceFiles: Object.freeze([
"linuxcnc/configs/sim/axis/vismach/puma/puma.ini",
"linuxcnc/configs/sim/axis/vismach/puma/puma_seam_weld.ngc",
"linuxcnc/configs/sim/axis/vismach/puma/remap_subs/428remap.ngc",
]),
iniPath: "linuxcnc/configs/sim/axis/vismach/puma/puma.ini",
gcodePath: "linuxcnc/configs/sim/axis/vismach/puma/puma_seam_weld.ngc",
currentNodeInventoryStatus: "PASS",
currentMatrixBrowserStatus: "represented-by-puma-cube",
dependencyClass: "halui-vismach-remap",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
]),
blockedKind: "-",
nonMainFixture: false,
evidence: "Node inventory covers this LinuxCNC PUMA program with the same vendored machine context as puma_cube; browser evidence can pair both cases.",
}),
Object.freeze({
id: "rose-engine-rcone-demo",
label: "Rose engine cone demo",
simConfigTarget: null,
sourceFiles: Object.freeze([
"linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
"linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
]),
iniPath: "linuxcnc/configs/sim/axis/rose_engine/rose_engine.ini",
gcodePath: "linuxcnc/configs/sim/axis/rose_engine/rcone_demo.ngc",
currentNodeInventoryStatus: "PASS",
currentMatrixBrowserStatus: "not-promoted",
dependencyClass: "hal-process-only",
targetBrowserEvidence: "explicit-browser-diagnostics",
requiredVirtualHalReports: Object.freeze([
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
]),
blockedKind: "-",
nonMainFixture: false,
evidence: "The LinuxCNC rose-engine demo is a HAL-process-only Node inventory pass and is useful as a non-Qt/non-vismach diagnostics candidate.",
}),
]);
export const VIRTUAL_HAL_COVERAGE_STATES = Object.freeze({
simulated: "simulated",
bridgeOnly: "bridge-only",
@@ -2197,6 +2393,11 @@ function virtualHalSimConfigTargetsById(targetIds = []) {
export function createVirtualHalMotionControllerMatrixReport(options = {}) {
const fixtures = options.motionFixtures ?? options.fixtures ?? VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES;
const tolerance = clampNumber(options.tolerance ?? 0.000001, 0, 1);
const manifestProvided = typeof options.manifestText === "string" || Array.isArray(options.manifestEntries);
const manifestSet = new Set([
...sourceManifestSetFromText(options.manifestText ?? ""),
...(options.manifestEntries ?? []).map(normalizeLinuxCncSourcePath),
]);
const rows = fixtures.map((fixture) => {
const initialState = createVirtualHalState({
task: { estop: false, powered: true },
@@ -2262,6 +2463,11 @@ export function createVirtualHalMotionControllerMatrixReport(options = {}) {
}));
const missingSimConfigTargets = (fixture.simConfigTargets ?? [])
.filter((targetId) => !simConfigEvidenceRows.some((target) => target.id === targetId));
const simConfigSourceFiles = [...new Set(simConfigEvidenceRows.flatMap((target) => target.sourceFiles))];
const normalizedSimConfigSourceFiles = simConfigSourceFiles.map(normalizeLinuxCncSourcePath);
const missingManifestFiles = manifestProvided
? simConfigSourceFiles.filter((file, index) => !manifestSet.has(normalizedSimConfigSourceFiles[index]))
: [];
return {
id: fixture.id,
label: fixture.label,
@@ -2269,9 +2475,11 @@ export function createVirtualHalMotionControllerMatrixReport(options = {}) {
sourceFiles: [...(fixture.sourceFiles ?? [])],
simConfigTargets: [...(fixture.simConfigTargets ?? [])],
simConfigEvidenceRows,
simConfigSourceFiles: [...new Set(simConfigEvidenceRows.flatMap((target) => target.sourceFiles))],
simConfigSourceFiles,
missingSimConfigTargets,
complete,
manifestChecked: manifestProvided,
missingManifestFiles,
complete: complete && missingManifestFiles.length === 0,
target: { ...(fixture.target ?? {}) },
initialPosition: { ...(fixture.initialPosition ?? {}) },
maxVelocity: fixture.maxVelocity,
@@ -2292,7 +2500,11 @@ export function createVirtualHalMotionControllerMatrixReport(options = {}) {
});
const missingFixtures = rows.filter((row) => !row.complete).map((row) => row.id);
const missingSimConfigTargets = rows.flatMap((row) => row.missingSimConfigTargets);
const complete = rows.length > 0 && missingFixtures.length === 0 && missingSimConfigTargets.length === 0;
const missingManifestFiles = [...new Set(rows.flatMap((row) => row.missingManifestFiles))];
const complete = rows.length > 0 &&
missingFixtures.length === 0 &&
missingSimConfigTargets.length === 0 &&
missingManifestFiles.length === 0;
return {
apiName: "linuxcnc-wasm-virtual-hal-motion-controller-matrix-report",
matrixVersion: 1,
@@ -2303,15 +2515,18 @@ export function createVirtualHalMotionControllerMatrixReport(options = {}) {
sourceFiles: [...new Set(rows.flatMap((row) => row.sourceFiles))],
simConfigSourceFiles: [...new Set(rows.flatMap((row) => row.simConfigSourceFiles))],
simConfigTargets: [...new Set(rows.flatMap((row) => row.simConfigTargets))],
manifestChecked: manifestProvided,
requiredPins: [...new Set(fixtures.flatMap((fixture) => fixture.requiredPins ?? []))],
fixtureCount: rows.length,
missingFixtures,
missingSimConfigTargets,
missingManifestFiles,
rows,
summaryRows: [
{ id: "fixtures", label: "Motion matrix fixtures", value: `${rows.length}` },
{ id: "source", label: "LinuxCNC motion source", value: complete ? "source-derived" : "incomplete" },
{ id: "sim-config-source", label: "LinuxCNC sim config source", value: missingSimConfigTargets.length === 0 ? "source-derived" : `missing ${missingSimConfigTargets.length}` },
{ id: "manifest", label: "Sim config manifest check", value: manifestProvided ? (missingManifestFiles.length === 0 ? "passed" : "failed") : "not provided" },
{ id: "servo-period", label: "Servo period stepping", value: complete ? "covered" : "blocked" },
{ id: "axis-joint-feedback", label: "Axis/joint feedback", value: complete ? "covered" : "blocked" },
],
@@ -2477,6 +2692,118 @@ export function createVirtualHalSimConfigSourceCoverageReport(options = {}) {
};
}
export function createVirtualHalSimConfigPromotionCandidateReport(options = {}) {
const candidates = options.candidates ?? VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES;
const manifestProvided = typeof options.manifestText === "string" || Array.isArray(options.manifestEntries);
const manifestSet = new Set([
...sourceManifestSetFromText(options.manifestText ?? ""),
...(options.manifestEntries ?? []).map(normalizeLinuxCncSourcePath),
]);
const blockedKinds = new Set(options.blockedKinds ?? [
"L4-PYTHON-REMAP",
"L4-TOOL-DB",
"L4-USER-M-PROCESS",
"UPSTREAM-DEMO",
]);
const allowedNodeStatuses = new Set(options.allowedNodeStatuses ?? ["PASS"]);
const requiredVirtualHalReports = options.requiredVirtualHalReports ?? [
"source-compliance",
"sim-config-source-coverage",
"command-script-fixtures",
"manifest-backed-motion-matrix",
];
const simConfigCoverage = options.simConfigSourceCoverage ?? createVirtualHalSimConfigSourceCoverageReport(options);
const sourceCompliance = options.sourceCompliance ?? createVirtualHalSourceComplianceReport(options);
const commandScriptFixtures = options.commandScriptFixtures ?? createVirtualHalCommandScriptFixtureReport(options);
const motionControllerMatrix = options.motionControllerMatrix ?? createVirtualHalMotionControllerMatrixReport(options);
const availableReports = new Set([
...(sourceCompliance.complete ? ["source-compliance"] : []),
...(simConfigCoverage.complete ? ["sim-config-source-coverage"] : []),
...(commandScriptFixtures.complete ? ["command-script-fixtures"] : []),
...(motionControllerMatrix.complete && motionControllerMatrix.manifestChecked ? ["manifest-backed-motion-matrix"] : []),
]);
const rows = candidates.map((candidate) => {
const sourceFiles = [...(candidate.sourceFiles ?? [])];
const normalizedSourceFiles = sourceFiles.map(normalizeLinuxCncSourcePath);
const missingManifestFiles = manifestProvided
? sourceFiles.filter((file, index) => !manifestSet.has(normalizedSourceFiles[index]))
: [];
const missingRequiredReports = requiredVirtualHalReports
.filter((report) => !availableReports.has(report));
const blockedKindExcluded = !blockedKinds.has(candidate.blockedKind);
const nodeStatusReady = allowedNodeStatuses.has(candidate.currentNodeInventoryStatus) ||
candidate.nonMainFixture === true;
const simConfigTargetReady = !candidate.simConfigTarget ||
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.some((target) => target.id === candidate.simConfigTarget);
const complete = sourceFiles.length > 0 &&
missingManifestFiles.length === 0 &&
missingRequiredReports.length === 0 &&
blockedKindExcluded &&
nodeStatusReady &&
simConfigTargetReady;
return {
id: candidate.id,
label: candidate.label,
source: "linuxcnc-configs-sim-source-derived-virtual-hal-promotion-candidate",
simConfigTarget: candidate.simConfigTarget ?? null,
sourceFiles,
iniPath: candidate.iniPath,
gcodePath: candidate.gcodePath,
currentNodeInventoryStatus: candidate.currentNodeInventoryStatus,
currentMatrixBrowserStatus: candidate.currentMatrixBrowserStatus,
dependencyClass: candidate.dependencyClass,
targetBrowserEvidence: candidate.targetBrowserEvidence,
requiredVirtualHalReports: [...(candidate.requiredVirtualHalReports ?? requiredVirtualHalReports)],
blockedKind: candidate.blockedKind,
blockedKindExcluded,
nonMainFixture: candidate.nonMainFixture === true,
nodeStatusReady,
simConfigTargetReady,
manifestChecked: manifestProvided,
missingManifestFiles,
missingRequiredReports,
evidence: candidate.evidence,
complete,
};
});
const missingCandidates = rows.filter((row) => !row.complete).map((row) => row.id);
const blockedCandidateIds = rows.filter((row) => !row.blockedKindExcluded).map((row) => row.id);
const missingManifestFiles = [...new Set(rows.flatMap((row) => row.missingManifestFiles))];
const missingRequiredReports = [...new Set(rows.flatMap((row) => row.missingRequiredReports))];
const complete = rows.length > 0 &&
missingCandidates.length === 0 &&
blockedCandidateIds.length === 0 &&
missingManifestFiles.length === 0 &&
missingRequiredReports.length === 0;
return {
apiName: "linuxcnc-wasm-virtual-hal-sim-config-promotion-candidate-report",
reportVersion: 1,
source: "linuxcnc-configs-sim-source-derived-virtual-hal",
phase: complete ? "ready" : "blocked",
complete,
webSimulationSatisfied: complete,
inventoryBaselineUnchanged: true,
manifestChecked: manifestProvided,
candidateCount: rows.length,
sourceFiles: [...new Set(rows.flatMap((row) => row.sourceFiles))],
requiredVirtualHalReports,
availableVirtualHalReports: [...availableReports],
blockedKinds: [...blockedKinds],
missingCandidates,
blockedCandidateIds,
missingManifestFiles,
missingRequiredReports,
rows,
summaryRows: [
{ id: "candidates", label: "Promotion candidates", value: `${rows.length}` },
{ id: "blocked-family", label: "Blocked-family exclusion", value: blockedCandidateIds.length === 0 ? "passed" : `blocked ${blockedCandidateIds.length}` },
{ id: "reports", label: "Virtual HAL reports", value: missingRequiredReports.length === 0 ? "complete" : `missing ${missingRequiredReports.length}` },
{ id: "manifest", label: "Manifest check", value: manifestProvided ? (missingManifestFiles.length === 0 ? "passed" : "failed") : "not provided" },
{ id: "inventory-baseline", label: "Inventory baseline", value: "unchanged" },
],
};
}
export function createVirtualHalSourceComplianceReport(options = {}) {
const halState = options.halState ?? createVirtualHalState();
const snapshot = options.snapshot ?? createVirtualHalWasmBridgeSnapshot(halState);
@@ -2690,6 +3017,9 @@ export function createLinuxCncVirtualHalRuntime(initialState = createVirtualHalS
getSimConfigSourceCoverageReport(options = {}) {
return createVirtualHalSimConfigSourceCoverageReport(options);
},
getSimConfigPromotionCandidateReport(options = {}) {
return createVirtualHalSimConfigPromotionCandidateReport(options);
},
getCommandScriptFixtureReport(options = {}) {
return createVirtualHalCommandScriptFixtureReport({ ...options, halState: state });
},