接入虚拟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

@@ -298,8 +298,9 @@ private page state.
`readPin()`, `writePin()`, `applyPinUpdates()`, `executeHalCommand()`,
`executeHalcmd()`, `stepMotion()`, `stepMotionController()`,
`getSimulationRuntimeReport()`, `getSimulationReplacementReport()`,
`getSourceComplianceReport()`, `getIntegrityReport()`, `getPinInventory()`,
`getProjectReport()`, and `applyToInterpSdk()`.
`getSourceComplianceReport()`, `getSimConfigPromotionCandidateReport()`,
`getIntegrityReport()`, `getPinInventory()`, `getProjectReport()`, and
`applyToInterpSdk()`.
- `VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES`,
`VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS`,
`VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES`,
@@ -315,7 +316,17 @@ private page state.
`addf`, thread start/stop, and servo-period motion stepping for browser
simulation and Node dashboards. The motion controller matrix rows also carry
LinuxCNC `configs/sim` evidence for external offsets, QtDragon multi-joint,
and vismach remap simulation coverage.
and vismach remap simulation coverage. When callers pass `manifestText` or
`manifestEntries`, the matrix report checks those associated sim-config
source files against the vendored LinuxCNC manifest and exposes
`manifestChecked` plus `missingManifestFiles`.
- `VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES` and
`createVirtualHalSimConfigPromotionCandidateReport()` expose the current
source-derived sim-config promotion candidate set. The first candidates keep
the inventory baseline unchanged while naming LinuxCNC INI/G-code source
files, current Node `PASS` evidence, blocked-family exclusions, target
browser diagnostics evidence, and the required virtual HAL reports for
QtDragon on-abort, PUMA seam-weld, and rose-engine promotion planning.
- `createVirtualHalSourceComplianceReport()` is the machine-readable gate for
the virtual HAL source rule. It maps replacement targets, runtime
capabilities, and HAL pin families back to LinuxCNC source files such as
@@ -419,6 +430,11 @@ workflow, report validation, and missing evidence without throwing.
`createProjectBatchAcceptanceReportUrlWorkflowSummaryViewModel()` and
`createProjectBatchAcceptanceReportUrlWorkflowActionPlan()` turn the same URL
workflow into dashboard rows and ordered input/fetch/gate actions.
`loadProjectReleaseReadinessArtifactUrlWorkflow()` can also fetch a browser
diagnostics artifact. When that artifact includes
`virtualHalSessionDiagnostics`, validation checks the saved-session diagnostics
artifact alongside the live virtual HAL evidence so URL workflows can prove
both live diagnostics and restored-session diagnostics are release-ready.
This helper does not execute shell commands and does not inspect or implement
CNC behavior. It only packages project-level release evidence and blocked
@@ -567,6 +583,13 @@ for external browser shells:
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan()`
exposes the same URL workflow next-action/next-command plan through the
browser workflow overview surface.
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState()`,
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomContract()`,
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomReadiness()`,
`renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState()`,
and `mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState()`
let the fixed workflow overview URL form render the URL workflow action plan
beside the URL workflow rows.
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowRenderState()`,
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomContract()`,
`createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness()`,

View File

@@ -7,6 +7,7 @@ export {
VIRTUAL_HAL_COVERAGE_STATES,
VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES,
VIRTUAL_HAL_PROJECT_PIN_GROUPS,
VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES,
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
VIRTUAL_HAL_SOURCE_DERIVED_CAPABILITIES,
VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS,
@@ -30,6 +31,7 @@ export {
createVirtualHalPinInventory,
createVirtualHalPinRegistry,
createVirtualHalProjectReport,
createVirtualHalSimConfigPromotionCandidateReport,
createVirtualHalSimConfigSourceCoverageReport,
createVirtualHalSimulationReplacementReport,
createVirtualHalSimulationRuntimeReport,

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 });
},

View File

@@ -99,12 +99,57 @@ function isVirtualHalSimConfigSourceCoverageReady(report) {
arrayOrEmpty(coverage.sourceFiles).includes("linuxcnc/configs/sim/qtdragon/qtdragon_xyz/on_abort.ngc");
}
function isVirtualHalMotionControllerMatrixReady(report) {
const matrix = objectOrEmpty(report);
return matrix.apiName === "linuxcnc-wasm-virtual-hal-motion-controller-matrix-report" &&
matrix.complete === true &&
matrix.webSimulationSatisfied === true &&
matrix.manifestChecked === true &&
arrayOrEmpty(matrix.missingFixtures).length === 0 &&
arrayOrEmpty(matrix.missingSimConfigTargets).length === 0 &&
arrayOrEmpty(matrix.missingManifestFiles).length === 0 &&
arrayOrEmpty(matrix.requiredPins).includes("motion.distance-to-go") &&
arrayOrEmpty(matrix.requiredPins).includes("motion.in-position") &&
arrayOrEmpty(matrix.sourceFiles).includes("linuxcnc/src/emc/motion/motion.c") &&
arrayOrEmpty(matrix.sourceFiles).includes("linuxcnc/src/emc/motion/axis.c") &&
arrayOrEmpty(matrix.simConfigTargets).includes("external-offsets") &&
arrayOrEmpty(matrix.simConfigTargets).includes("qtdragon-on-abort") &&
arrayOrEmpty(matrix.simConfigTargets).includes("vismach-remap-sims") &&
arrayOrEmpty(matrix.simConfigSourceFiles).includes("linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini") &&
arrayOrEmpty(matrix.simConfigSourceFiles).includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini") &&
arrayOrEmpty(matrix.simConfigSourceFiles).includes("linuxcnc/configs/sim/axis/vismach/puma/puma.ini");
}
function isVirtualHalSimConfigPromotionCandidateReportReady(report) {
const candidateReport = objectOrEmpty(report);
const rows = arrayOrEmpty(candidateReport.rows);
return candidateReport.apiName === "linuxcnc-wasm-virtual-hal-sim-config-promotion-candidate-report" &&
candidateReport.complete === true &&
candidateReport.webSimulationSatisfied === true &&
candidateReport.inventoryBaselineUnchanged === true &&
arrayOrEmpty(candidateReport.missingCandidates).length === 0 &&
arrayOrEmpty(candidateReport.blockedCandidateIds).length === 0 &&
arrayOrEmpty(candidateReport.missingManifestFiles).length === 0 &&
arrayOrEmpty(candidateReport.missingRequiredReports).length === 0 &&
rows.some((row) =>
row.id === "qtdragon-multi-joint-on-abort" &&
row.complete === true &&
row.currentNodeInventoryStatus === "PASS" &&
row.blockedKind === "-" &&
row.targetBrowserEvidence === "explicit-browser-diagnostics" &&
arrayOrEmpty(row.sourceFiles).includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini") &&
arrayOrEmpty(row.sourceFiles).includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/on_abort.ngc")
);
}
export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifact = {}) {
const artifactObject = objectOrEmpty(artifact);
const sourceCompliance = objectOrEmpty(artifactObject.virtualHalSourceCompliance);
const simConfigSourceCoverage = objectOrEmpty(artifactObject.virtualHalSimConfigSourceCoverage);
const commandScriptFixtures = objectOrEmpty(artifactObject.virtualHalCommandScriptFixtures);
const motionControllerMatrix = objectOrEmpty(artifactObject.virtualHalMotionControllerMatrix);
const promotionCandidates = objectOrEmpty(artifactObject.virtualHalSimConfigPromotionCandidates);
const sessionDiagnostics = objectOrEmpty(artifactObject.virtualHalSessionDiagnostics);
const replacement = objectOrEmpty(artifactObject.virtualHalSimulationReplacement);
const virtualHal = objectOrEmpty(artifactObject.virtualHal);
const sourceComplianceReady = sourceCompliance.complete === true &&
@@ -116,31 +161,35 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac
arrayOrEmpty(commandScriptFixtures.coveredActions).includes("loadusr") &&
arrayOrEmpty(commandScriptFixtures.coveredActions).includes("stop") &&
arrayOrEmpty(commandScriptFixtures.sourceFiles).includes("linuxcnc/src/hal/utils/halcmd_commands.cc");
const motionControllerMatrixReady = motionControllerMatrix.complete === true &&
motionControllerMatrix.webSimulationSatisfied === true &&
arrayOrEmpty(motionControllerMatrix.requiredPins).includes("motion.distance-to-go") &&
arrayOrEmpty(motionControllerMatrix.requiredPins).includes("motion.in-position") &&
arrayOrEmpty(motionControllerMatrix.sourceFiles).includes("linuxcnc/src/emc/motion/motion.c") &&
arrayOrEmpty(motionControllerMatrix.sourceFiles).includes("linuxcnc/src/emc/motion/axis.c") &&
arrayOrEmpty(motionControllerMatrix.simConfigTargets).includes("external-offsets") &&
arrayOrEmpty(motionControllerMatrix.simConfigTargets).includes("qtdragon-on-abort") &&
arrayOrEmpty(motionControllerMatrix.simConfigTargets).includes("vismach-remap-sims") &&
arrayOrEmpty(motionControllerMatrix.simConfigSourceFiles).includes("linuxcnc/configs/sim/axis/external_offsets/dynamic_offsets.ini") &&
arrayOrEmpty(motionControllerMatrix.simConfigSourceFiles).includes("linuxcnc/configs/sim/qtdragon/qtdragon_multi_joint/qtdragon_xyyz.ini") &&
arrayOrEmpty(motionControllerMatrix.simConfigSourceFiles).includes("linuxcnc/configs/sim/axis/vismach/puma/puma.ini");
const motionControllerMatrixReady = isVirtualHalMotionControllerMatrixReady(motionControllerMatrix);
const hasPromotionCandidates = artifactObject.virtualHalSimConfigPromotionCandidates !== undefined;
const promotionCandidatesReady = !hasPromotionCandidates ||
isVirtualHalSimConfigPromotionCandidateReportReady(promotionCandidates);
const replacementReady = replacement.ready === true &&
replacement.replacesHostRuntimeForSimulation === true &&
objectOrEmpty(replacement.sourceCompliance).webSimulationSatisfied === true &&
objectOrEmpty(replacement.motionControllerMatrix).complete === true;
const virtualHalReady = virtualHal.source === "browser-virtual-hal";
const hasSessionDiagnostics = artifactObject.virtualHalSessionDiagnostics !== undefined;
const sessionDiagnosticsReady = !hasSessionDiagnostics || (
sessionDiagnostics.apiName === "real-browser-simulation-virtual-hal-session-diagnostics" &&
sessionDiagnostics.ready === true &&
sessionDiagnostics.phase === "release-ready" &&
objectOrEmpty(sessionDiagnostics.validation).ready === true &&
objectOrEmpty(sessionDiagnostics.diagnosticsArtifact).apiName === "real-browser-simulation-diagnostics-artifact" &&
objectOrEmpty(sessionDiagnostics.diagnosticsArtifact?.virtualHalSourceCompliance).complete === true &&
objectOrEmpty(sessionDiagnostics.diagnosticsArtifact?.virtualHalMotionControllerMatrix).manifestChecked === true
);
const missing = [
...(artifactObject.apiName === "real-browser-simulation-diagnostics-artifact" ? [] : ["apiName"]),
...(virtualHalReady ? [] : ["virtualHal"]),
...(replacementReady ? [] : ["virtualHalSimulationReplacement"]),
...(sourceComplianceReady ? [] : ["virtualHalSourceCompliance"]),
...(simConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]),
...(promotionCandidatesReady ? [] : ["virtualHalSimConfigPromotionCandidates"]),
...(commandScriptFixturesReady ? [] : ["virtualHalCommandScriptFixtures"]),
...(motionControllerMatrixReady ? [] : ["virtualHalMotionControllerMatrix"]),
...(sessionDiagnosticsReady ? [] : ["virtualHalSessionDiagnostics"]),
];
return {
apiName: "project-release-browser-diagnostics-artifact-validation",
@@ -153,11 +202,16 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac
replacementReady,
sourceComplianceReady,
simConfigSourceCoverageReady,
promotionCandidatesReady,
hasPromotionCandidates,
commandScriptFixturesReady,
motionControllerMatrixReady,
sessionDiagnosticsReady,
hasSessionDiagnostics,
sourceFiles: [...new Set([
...arrayOrEmpty(sourceCompliance.sourceFiles),
...arrayOrEmpty(simConfigSourceCoverage.sourceFiles),
...arrayOrEmpty(promotionCandidates.sourceFiles),
...arrayOrEmpty(commandScriptFixtures.sourceFiles),
...arrayOrEmpty(motionControllerMatrix.sourceFiles),
...arrayOrEmpty(motionControllerMatrix.simConfigSourceFiles),
@@ -183,6 +237,11 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac
label: "Virtual HAL sim-config source coverage",
value: simConfigSourceCoverageReady ? "ready" : "missing",
},
{
id: "sim-config-promotion-candidates",
label: "Virtual HAL sim-config promotion candidates",
value: hasPromotionCandidates ? (promotionCandidatesReady ? "ready" : "missing") : "not provided",
},
{
id: "command-script-fixtures",
label: "Virtual HAL command script fixtures",
@@ -193,6 +252,11 @@ export function createProjectReleaseBrowserDiagnosticsArtifactValidation(artifac
label: "Virtual HAL motion controller matrix",
value: motionControllerMatrixReady ? "ready" : "missing",
},
{
id: "session-diagnostics",
label: "Saved-session diagnostics",
value: hasSessionDiagnostics ? (sessionDiagnosticsReady ? "ready" : "missing") : "not provided",
},
{
id: "missing",
label: "Missing diagnostics evidence",
@@ -585,6 +649,7 @@ export function createProjectBatchAcceptanceWorkflow({
gateResults = {},
observedOutputs = [],
virtualHalSimConfigSourceCoverage = null,
virtualHalMotionControllerMatrix = null,
} = {}) {
const capabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({ capabilities });
const gateManifest = createProjectReleaseGateManifest();
@@ -597,11 +662,17 @@ export function createProjectBatchAcceptanceWorkflow({
.find(({ id }) => id === "project-batch-acceptance")?.passed === true;
const virtualHalSimConfigSourceCoverageReady = virtualHalSimConfigSourceCoverage === null ||
isVirtualHalSimConfigSourceCoverageReady(virtualHalSimConfigSourceCoverage);
const ready = capabilityMatrix.ready && batchGatePassed && virtualHalSimConfigSourceCoverageReady;
const virtualHalMotionControllerMatrixReady = virtualHalMotionControllerMatrix === null ||
isVirtualHalMotionControllerMatrixReady(virtualHalMotionControllerMatrix);
const ready = capabilityMatrix.ready &&
batchGatePassed &&
virtualHalSimConfigSourceCoverageReady &&
virtualHalMotionControllerMatrixReady;
const missing = [
...capabilityMatrix.missing,
...(batchGatePassed ? [] : ["project-batch-acceptance"]),
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtual-hal-sim-config-source-coverage"]),
...(virtualHalMotionControllerMatrixReady ? [] : ["virtual-hal-motion-controller-matrix"]),
];
return {
@@ -618,6 +689,8 @@ export function createProjectBatchAcceptanceWorkflow({
rejectedCapabilities: capabilityMatrix.rejectedCapabilities,
virtualHalSimConfigSourceCoverage,
virtualHalSimConfigSourceCoverageReady,
virtualHalMotionControllerMatrix,
virtualHalMotionControllerMatrixReady,
gateResultMatrix,
missing,
rows: [
@@ -643,6 +716,13 @@ export function createProjectBatchAcceptanceWorkflow({
? "not required"
: (virtualHalSimConfigSourceCoverageReady ? "ready" : "missing"),
},
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
value: virtualHalMotionControllerMatrix === null
? "not required"
: (virtualHalMotionControllerMatrixReady ? "ready" : "missing"),
},
{
id: "missing",
label: "Missing acceptance evidence",
@@ -785,6 +865,15 @@ export function createProjectBatchAcceptanceChecklist({
? "not required"
: (workflow?.virtualHalSimConfigSourceCoverageReady === true ? "ready" : "missing"),
},
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
status: workflow?.virtualHalMotionControllerMatrixReady !== false ? "pass" : "blocked",
passed: workflow?.virtualHalMotionControllerMatrixReady !== false,
detail: workflow?.virtualHalMotionControllerMatrix === null
? "not required"
: (workflow?.virtualHalMotionControllerMatrixReady === true ? "ready" : "missing"),
},
{
id: "next-action",
label: "Next acceptance action",
@@ -817,6 +906,7 @@ export function createProjectBatchAcceptanceReport({
gateResults = {},
observedOutputs = [],
virtualHalSimConfigSourceCoverage = null,
virtualHalMotionControllerMatrix = null,
} = {}) {
const capabilityMatrix = createProjectBatchAcceptanceCapabilityMatrix({ capabilities });
const workflow = createProjectBatchAcceptanceWorkflow({
@@ -825,6 +915,7 @@ export function createProjectBatchAcceptanceReport({
gateResults,
observedOutputs,
virtualHalSimConfigSourceCoverage,
virtualHalMotionControllerMatrix,
});
const summaryViewModel = createProjectBatchAcceptanceSummaryViewModel(workflow);
const actionPlan = createProjectBatchAcceptanceActionPlan(workflow);
@@ -840,6 +931,7 @@ export function createProjectBatchAcceptanceReport({
capabilityMatrix,
workflow,
virtualHalSimConfigSourceCoverage,
virtualHalMotionControllerMatrix,
summaryViewModel,
actionPlan,
checklist,
@@ -876,11 +968,16 @@ export function createProjectBatchAcceptanceReportValidation(report = {}) {
const actionPlan = objectOrEmpty(reportObject.actionPlan);
const checklist = objectOrEmpty(reportObject.checklist);
const virtualHalSimConfigSourceCoverage = objectOrEmpty(reportObject.virtualHalSimConfigSourceCoverage);
const virtualHalMotionControllerMatrix = objectOrEmpty(reportObject.virtualHalMotionControllerMatrix);
const rows = arrayOrEmpty(reportObject.rows);
const virtualHalSimConfigSourceCoverageRequired = reportObject.virtualHalSimConfigSourceCoverage !== null &&
reportObject.virtualHalSimConfigSourceCoverage !== undefined;
const virtualHalSimConfigSourceCoverageReady = !virtualHalSimConfigSourceCoverageRequired ||
isVirtualHalSimConfigSourceCoverageReady(virtualHalSimConfigSourceCoverage);
const virtualHalMotionControllerMatrixRequired = reportObject.virtualHalMotionControllerMatrix !== null &&
reportObject.virtualHalMotionControllerMatrix !== undefined;
const virtualHalMotionControllerMatrixReady = !virtualHalMotionControllerMatrixRequired ||
isVirtualHalMotionControllerMatrixReady(virtualHalMotionControllerMatrix);
const missing = [
...(reportObject.apiName === "project-batch-acceptance-report" ? [] : ["apiName"]),
...(reportObject.reportVersion === 1 ? [] : ["reportVersion"]),
@@ -905,7 +1002,8 @@ export function createProjectBatchAcceptanceReportValidation(report = {}) {
? []
: ["checklist"]),
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]),
...(rows.find(({ id, value }) => id === "checklist" && /^([34])\/\1 passed$/.test(value))
...(virtualHalMotionControllerMatrixReady ? [] : ["virtualHalMotionControllerMatrix"]),
...(rows.find(({ id, value }) => id === "checklist" && /^([45])\/\1 passed$/.test(value))
? []
: ["rows.checklist"]),
];
@@ -919,6 +1017,8 @@ export function createProjectBatchAcceptanceReportValidation(report = {}) {
batchId: reportObject.batchId ?? null,
reportApiName: reportObject.apiName ?? null,
reportVersion: reportObject.reportVersion ?? null,
virtualHalSimConfigSourceCoverageReady,
virtualHalMotionControllerMatrixReady,
rows: [
{
id: "report",
@@ -947,6 +1047,13 @@ export function createProjectBatchAcceptanceReportValidation(report = {}) {
? (virtualHalSimConfigSourceCoverageReady ? "ready" : "missing")
: "not required",
},
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
value: virtualHalMotionControllerMatrixRequired
? (virtualHalMotionControllerMatrixReady ? "ready" : "missing")
: "not required",
},
{
id: "missing",
label: "Missing report evidence",
@@ -1404,6 +1511,7 @@ export function createProjectReleaseReadinessReport({
observedOutputs = [],
simConfigInventory = DEFAULT_SIM_CONFIG_INVENTORY_BASELINE,
virtualHalSimConfigSourceCoverage = null,
virtualHalMotionControllerMatrix = null,
blockedRuntimeFamilies = BLOCKED_RUNTIME_FAMILIES,
promotedRuntimeFamilies = [],
axisScreenshotArtifacts = [],
@@ -1439,18 +1547,23 @@ export function createProjectReleaseReadinessReport({
const virtualHalSimConfigSourceCoverageReady = isVirtualHalSimConfigSourceCoverageReady(
virtualHalSimConfigSourceCoverage,
);
const virtualHalMotionControllerMatrixReady = isVirtualHalMotionControllerMatrixReady(
virtualHalMotionControllerMatrix,
);
const blockedRuntimeReady = promotedBlockedFamilies.length === 0;
const axisScreenshotArtifactSummary = createAxisScreenshotArtifactSummary(axisScreenshotArtifacts);
const axisScreenshotArtifactsReady = axisScreenshotArtifactSummary.ready;
const ready = releaseGatePassed &&
inventoryReady &&
virtualHalSimConfigSourceCoverageReady &&
virtualHalMotionControllerMatrixReady &&
blockedRuntimeReady &&
axisScreenshotArtifactsReady;
const missing = [
...(releaseGatePassed ? [] : ["project-release-gate"]),
...(inventoryReady ? [] : ["sim-config-inventory"]),
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtual-hal-sim-config-source-coverage"]),
...(virtualHalMotionControllerMatrixReady ? [] : ["virtual-hal-motion-controller-matrix"]),
...(blockedRuntimeReady ? [] : ["blocked-runtime-families"]),
...(axisScreenshotArtifactsReady ? [] : ["axis-screenshot-artifacts"]),
];
@@ -1469,6 +1582,8 @@ export function createProjectReleaseReadinessReport({
simConfigInventory: inventory,
virtualHalSimConfigSourceCoverage,
virtualHalSimConfigSourceCoverageReady,
virtualHalMotionControllerMatrix,
virtualHalMotionControllerMatrixReady,
blockedRuntimeFamilies: blockedFamilies,
promotedRuntimeFamilies: promotedFamilies,
promotedBlockedFamilies,
@@ -1495,6 +1610,11 @@ export function createProjectReleaseReadinessReport({
label: "Virtual HAL sim-config source coverage",
value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing",
},
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
value: virtualHalMotionControllerMatrixReady ? "ready" : "missing",
},
{
id: "blocked-runtime-families",
label: "Blocked runtime families",
@@ -1518,6 +1638,7 @@ export function createProjectReleaseReadinessSummaryViewModel(
const gateResultMatrix = objectOrEmpty(report?.gateResultMatrix);
const simConfigInventory = objectOrEmpty(report?.simConfigInventory);
const virtualHalSimConfigSourceCoverageReady = report?.virtualHalSimConfigSourceCoverageReady === true;
const virtualHalMotionControllerMatrixReady = report?.virtualHalMotionControllerMatrixReady === true;
const axisScreenshotArtifactSummary = objectOrEmpty(report?.axisScreenshotArtifactSummary);
const blockedRuntimeFamilies = arrayOrEmpty(report?.blockedRuntimeFamilies);
const promotedBlockedFamilies = arrayOrEmpty(report?.promotedBlockedFamilies);
@@ -1554,6 +1675,11 @@ export function createProjectReleaseReadinessSummaryViewModel(
label: "Virtual HAL sim-config source coverage",
value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing",
},
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
value: virtualHalMotionControllerMatrixReady ? "ready" : "missing",
},
{
id: "blocked-runtime-families",
label: "Blocked runtime families",
@@ -1636,6 +1762,11 @@ export function createProjectReleaseReadinessArtifactValidationSummaryViewModel(
label: "Virtual HAL sim-config source coverage",
value: validation?.virtualHalSimConfigSourceCoverageReady === true ? "ready" : "missing",
},
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
value: validation?.virtualHalMotionControllerMatrixReady === true ? "ready" : "missing",
},
{
id: "missing",
label: "Missing artifact evidence",
@@ -1790,6 +1921,7 @@ export function createProjectReleaseReadinessArtifactJsonWorkflow({ artifactJson
export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
artifactUrl = "",
diagnosticsUrl = "",
requireSavedSessionDiagnostics = false,
fetchRef = globalThis.fetch,
} = {}) {
if (!artifactUrl || typeof fetchRef !== "function") {
@@ -1810,6 +1942,7 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
ready: false,
artifactUrl,
diagnosticsUrl,
requireSavedSessionDiagnostics,
fetched: false,
diagnosticsFetched: false,
httpStatus: null,
@@ -1855,6 +1988,7 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
ready: false,
artifactUrl,
diagnosticsUrl,
requireSavedSessionDiagnostics,
fetched: false,
diagnosticsFetched: false,
httpStatus,
@@ -1911,6 +2045,9 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
const diagnosticsArtifact = JSON.parse(await diagnosticsResponse.text());
diagnosticsValidation = createProjectReleaseBrowserDiagnosticsArtifactValidation(diagnosticsArtifact);
diagnosticsMissing.push(...diagnosticsValidation.missing.map((item) => `diagnostics.${item}`));
if (requireSavedSessionDiagnostics && diagnosticsValidation.hasSessionDiagnostics !== true) {
diagnosticsMissing.push("diagnostics.virtualHalSessionDiagnostics");
}
}
} catch (error) {
diagnosticsFetchError = error?.message ?? "diagnostics artifact URL fetch error";
@@ -1923,7 +2060,7 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
...diagnosticsMissing,
];
const ready = jsonWorkflow.ready === true &&
(!diagnosticsUrl || diagnosticsValidation?.ready === true);
(!diagnosticsUrl || (diagnosticsValidation?.ready === true && diagnosticsMissing.length === 0));
return {
apiName: "project-release-readiness-artifact-url-workflow",
workflowVersion: 1,
@@ -1931,6 +2068,7 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
ready,
artifactUrl,
diagnosticsUrl,
requireSavedSessionDiagnostics,
fetched: true,
diagnosticsFetched,
httpStatus,
@@ -1977,6 +2115,7 @@ export async function loadProjectReleaseReadinessArtifactUrlWorkflow({
ready: false,
artifactUrl,
diagnosticsUrl,
requireSavedSessionDiagnostics,
fetched: false,
diagnosticsFetched: false,
httpStatus: null,
@@ -2060,6 +2199,15 @@ export function createProjectReleaseReadinessArtifactUrlWorkflowSummaryViewModel
? (workflow?.diagnosticsValidation?.ready === true ? "ready" : "blocked")
: "not requested",
},
{
id: "saved-session-diagnostics",
label: "Saved-session diagnostics",
value: workflow?.diagnosticsUrl
? (workflow?.diagnosticsValidation?.hasSessionDiagnostics
? (workflow?.diagnosticsValidation?.sessionDiagnosticsReady === true ? "ready" : "blocked")
: "not provided")
: "not requested",
},
{
id: "next-command",
label: "Next command",
@@ -2170,6 +2318,7 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
const releaseGate = objectOrEmpty(artifactObject.releaseGate);
const simConfigInventory = objectOrEmpty(artifactObject.simConfigInventory);
const virtualHalSimConfigSourceCoverage = objectOrEmpty(artifactObject.virtualHalSimConfigSourceCoverage);
const virtualHalMotionControllerMatrix = objectOrEmpty(artifactObject.virtualHalMotionControllerMatrix);
const gateManifest = objectOrEmpty(artifactObject.gateManifest);
const gateExecutionManifest = objectOrEmpty(artifactObject.gateExecutionManifest);
const gateExecutionSummaryViewModel = objectOrEmpty(artifactObject.gateExecutionSummaryViewModel);
@@ -2218,6 +2367,9 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
const virtualHalSimConfigSourceCoverageReady = isVirtualHalSimConfigSourceCoverageReady(
virtualHalSimConfigSourceCoverage,
) && artifactObject.virtualHalSimConfigSourceCoverageReady === true;
const virtualHalMotionControllerMatrixReady = isVirtualHalMotionControllerMatrixReady(
virtualHalMotionControllerMatrix,
) && artifactObject.virtualHalMotionControllerMatrixReady === true;
const axisScreenshotArtifacts = arrayOrEmpty(axisScreenshotArtifactSummary.artifacts);
const axisScreenshotSummaryReady = !axisScreenshotArtifactSummary.apiName
|| axisScreenshotArtifacts.length === 0
@@ -2256,6 +2408,7 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
...(simConfigInventory.skipped === 131 ? [] : ["simConfigInventory.skipped"]),
...(simConfigInventory.unexpectedFail === 0 ? [] : ["simConfigInventory.unexpectedFail"]),
...(virtualHalSimConfigSourceCoverageReady ? [] : ["virtualHalSimConfigSourceCoverage"]),
...(virtualHalMotionControllerMatrixReady ? [] : ["virtualHalMotionControllerMatrix"]),
...(arrayOrEmpty(artifactObject.blockedRuntimeFamilies).join(",") === BLOCKED_RUNTIME_FAMILIES.join(",")
? []
: ["blockedRuntimeFamilies"]),
@@ -2290,6 +2443,7 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
gateResultMatrixReady: matrixReady,
gateActionPlanReady: actionPlanReady,
virtualHalSimConfigSourceCoverageReady,
virtualHalMotionControllerMatrixReady,
axisScreenshotArtifactSummaryReady: axisScreenshotSummaryReady,
axisScreenshotArtifactCount: axisScreenshotArtifacts.length,
blockedRuntimeFamilies: arrayOrEmpty(artifactObject.blockedRuntimeFamilies),
@@ -2334,6 +2488,11 @@ export function createProjectReleaseReadinessArtifactValidation(artifact = {}) {
label: "Virtual HAL sim-config source coverage",
value: virtualHalSimConfigSourceCoverageReady ? "ready" : "missing",
},
{
id: "virtual-hal-motion-controller-matrix",
label: "Virtual HAL motion controller matrix",
value: virtualHalMotionControllerMatrixReady ? "ready" : "missing",
},
{
id: "axis-screenshot-artifacts",
label: "AXIS screenshot artifacts",

View File

@@ -2560,6 +2560,15 @@ export function createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWo
? (workflow?.diagnosticsValidation?.ready === true ? "ready" : "blocked")
: "not requested",
},
{
id: "saved-session-diagnostics",
label: "Saved-session diagnostics",
value: workflow?.diagnosticsUrl
? (workflow?.diagnosticsValidation?.hasSessionDiagnostics
? (workflow?.diagnosticsValidation?.sessionDiagnosticsReady === true ? "ready" : "blocked")
: "not provided")
: "not requested",
},
{
id: "diagnostics-status",
label: "Diagnostics HTTP status",

View File

@@ -127,6 +127,14 @@
<p data-workflow-overview-release-readiness-artifact-url-workflow-status>Waiting for release artifact URL workflow.</p>
<dl data-workflow-overview-release-readiness-artifact-url-workflow-rows></dl>
</section>
<section class="overview-status" aria-label="Release artifact URL workflow action plan" data-workflow-overview-release-readiness-artifact-url-workflow-action-plan>
<p data-workflow-overview-release-readiness-artifact-url-workflow-action-plan-status>Waiting for URL workflow action plan.</p>
<dl data-workflow-overview-release-readiness-artifact-url-workflow-action-plan-rows></dl>
</section>
<section class="overview-status" aria-label="Release artifact URL gate execution summary" data-workflow-overview-release-readiness-artifact-gate-execution-summary>
<p data-workflow-overview-release-readiness-artifact-gate-execution-summary-status>Waiting for URL workflow gate evidence.</p>
<dl data-workflow-overview-release-readiness-artifact-gate-execution-summary-rows></dl>
</section>
</section>
</main>
<script type="module">
@@ -170,6 +178,11 @@
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowDomReadiness,
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowState,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomContract,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomReadiness,
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState,
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationActionPlan,
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryViewModel,
@@ -216,6 +229,25 @@
renderState,
documentRef: document,
});
const actionPlan = createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlan(workflow);
const actionPlanRenderState =
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState(actionPlan);
const actionPlanMountResult =
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState({
renderState: actionPlanRenderState,
documentRef: document,
});
const gateExecutionSummary =
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlGateExecutionSummaryViewModel(workflow);
const gateExecutionSummaryRenderState =
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryRenderState(
gateExecutionSummary,
);
const gateExecutionSummaryMountResult =
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactGateExecutionSummaryState({
renderState: gateExecutionSummaryRenderState,
documentRef: document,
});
document.body.dataset.releaseArtifactUrlWorkflowPhase = workflow.phase;
document.body.dataset.releaseArtifactUrlWorkflowReady = workflow.ready ? "true" : "false";
return {
@@ -223,6 +255,12 @@
summary,
renderState,
mountResult,
actionPlan,
actionPlanRenderState,
actionPlanMountResult,
gateExecutionSummary,
gateExecutionSummaryRenderState,
gateExecutionSummaryMountResult,
};
}
@@ -324,6 +362,28 @@
options ?? { documentRef: document },
)
),
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState: (actionPlan) => (
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanRenderState(actionPlan)
),
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomContract: (options) => (
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomContract(options)
),
getWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomReadiness: (options) => (
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanDomReadiness(
options ?? { documentRef: document },
)
),
renderWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState: (renderState, options) => (
renderIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState(
renderState,
options ?? { documentRef: document },
)
),
mountWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState: (options) => (
mountIniPanelShellWorkflowOverviewReleaseReadinessArtifactUrlWorkflowActionPlanState(
options ?? { documentRef: document },
)
),
getWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel: (validation) => (
createIniPanelShellWorkflowOverviewReleaseReadinessArtifactValidationSummaryViewModel(validation)
),

View File

@@ -1805,6 +1805,8 @@ M2
import {
DEFAULT_SIMULATION_PROGRAM_ID,
VIRTUAL_HAL_AXES as SHARED_VIRTUAL_HAL_AXES,
VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES as SHARED_VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES,
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS as SHARED_VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
applyVirtualHalAction as applySharedVirtualHalAction,
applyVirtualHalPinUpdates as applySharedVirtualHalPinUpdates,
cloneVirtualHalState as cloneSharedVirtualHalState,
@@ -1815,6 +1817,7 @@ M2
createVirtualHalMachineStatusState as createSharedVirtualHalMachineStatusState,
createVirtualHalMotionControllerMatrixReport as createSharedVirtualHalMotionControllerMatrixReport,
createVirtualHalPinRegistry as createSharedVirtualHalPinRegistry,
createVirtualHalSimConfigPromotionCandidateReport as createSharedVirtualHalSimConfigPromotionCandidateReport,
createVirtualHalSimConfigSourceCoverageReport as createSharedVirtualHalSimConfigSourceCoverageReport,
createVirtualHalSimulationReplacementReport as createSharedVirtualHalSimulationReplacementReport,
createVirtualHalSimulationRuntimeReport as createSharedVirtualHalSimulationRuntimeReport,
@@ -1983,6 +1986,8 @@ M2
};
const statusHistory = [];
const VIRTUAL_HAL_AXES = SHARED_VIRTUAL_HAL_AXES;
const VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES = SHARED_VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES;
const VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES = SHARED_VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS.flatMap((target) => target.sourceFiles ?? []);
let virtualHalState = createVirtualHalState();
function setAllText(selector, value) {
@@ -2517,12 +2522,23 @@ M2
return createSharedVirtualHalSimConfigSourceCoverageReport();
}
function createVirtualHalSimConfigPromotionCandidateReport() {
return createSharedVirtualHalSimConfigPromotionCandidateReport({
manifestEntries: [
...VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
...VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES.flatMap((candidate) => candidate.sourceFiles ?? []),
],
});
}
function createVirtualHalCommandScriptFixtureReport() {
return createSharedVirtualHalCommandScriptFixtureReport({ halState: virtualHalState });
}
function createVirtualHalMotionControllerMatrixReport() {
return createSharedVirtualHalMotionControllerMatrixReport();
return createSharedVirtualHalMotionControllerMatrixReport({
manifestEntries: VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
});
}
function renderMachineStatusDom(machineStatus) {
@@ -2613,6 +2629,7 @@ M2
virtualHalSimulationReplacement: createVirtualHalSimulationReplacementReport(),
virtualHalSourceCompliance: createVirtualHalSourceComplianceReport(),
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
virtualHalSimConfigPromotionCandidates: createVirtualHalSimConfigPromotionCandidateReport(),
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport(),
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport(),
virtualHalSessionDiagnostics: createVirtualHalSessionDiagnosticsArtifact(),
@@ -3578,7 +3595,9 @@ M2
async function saveVirtualHalSessionSnapshot(options = {}) {
const session = virtualHalSessionOptions(options);
const virtualHal = createVirtualHalSessionPayload(virtualHalState);
const virtualHal = createVirtualHalSessionPayload(virtualHalState, {
manifestEntries: VIRTUAL_HAL_MOTION_MATRIX_MANIFEST_ENTRIES,
});
const payload = createMachineSessionSnapshotPayload(session.machineId, {
...(session.gcodeFilename ? { gcodeFilename: session.gcodeFilename } : {}),
virtualHal,
@@ -4042,6 +4061,7 @@ M2
getVirtualHalSimulationReplacementReport: () => createVirtualHalSimulationReplacementReport(),
getVirtualHalSourceComplianceReport: () => createVirtualHalSourceComplianceReport(),
getVirtualHalSimConfigSourceCoverageReport: () => createVirtualHalSimConfigSourceCoverageReport(),
getVirtualHalSimConfigPromotionCandidateReport: () => createVirtualHalSimConfigPromotionCandidateReport(),
getVirtualHalCommandScriptFixtureReport: () => createVirtualHalCommandScriptFixtureReport(),
getVirtualHalMotionControllerMatrixReport: () => createVirtualHalMotionControllerMatrixReport(),
getVirtualRealtimeHalRuntimeReport: () => createVirtualHalSimulationReplacementReport(),

View File

@@ -19,6 +19,8 @@ export {
VIRTUAL_HAL_AXES,
VIRTUAL_HAL_AXISUI_PINS,
VIRTUAL_HAL_MOTION_CONTROLLER_MATRIX_FIXTURES,
VIRTUAL_HAL_SIM_CONFIG_PROMOTION_CANDIDATES,
VIRTUAL_HAL_SIM_CONFIG_SOURCE_TARGETS,
VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS,
VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES,
applyVirtualHalAction,
@@ -32,6 +34,7 @@ export {
createVirtualHalLimitsHomeState,
createVirtualHalMachineStatusState,
createVirtualHalPinRegistry,
createVirtualHalSimConfigPromotionCandidateReport,
createVirtualHalSimConfigSourceCoverageReport,
createVirtualHalSimulationReplacementReport,
createVirtualHalSimulationRuntimeReport,