Complete XYZAC LinuxCNC WASM parity and smooth 3D preview
This commit is contained in:
@@ -38,7 +38,7 @@ export async function runOpfsProbe({ storage, projectId = "s6-05-opfs", mode = "
|
||||
const probe = opfsCapability({ navigator: { storage } });
|
||||
if (probe.status !== "verified") return { schema: OPFS_RUNTIME_SCHEMA, ...probe, project_id: projectId };
|
||||
if (!validProjectId(projectId)) return { schema: OPFS_RUNTIME_SCHEMA, status: "blocked", code: "opfs_project_id_invalid" };
|
||||
if (!["round_trip", "write", "read"].includes(mode)) return { schema: OPFS_RUNTIME_SCHEMA, status: "blocked", code: "opfs_mode_invalid", project_id: projectId };
|
||||
if (!["round_trip", "write", "read", "corrupt"].includes(mode)) return { schema: OPFS_RUNTIME_SCHEMA, status: "blocked", code: "opfs_mode_invalid", project_id: projectId };
|
||||
let access = null;
|
||||
try {
|
||||
const rootResult = await rootHandle(storage);
|
||||
@@ -64,6 +64,18 @@ export async function runOpfsProbe({ storage, projectId = "s6-05-opfs", mode = "
|
||||
access.close();
|
||||
access = null;
|
||||
if (mode === "write") return { schema: OPFS_RUNTIME_SCHEMA, status: "verified", mode, project_id: projectId, bytes: encoded.byteLength, commit_seq: record.commit_seq, content_hash: record.content_hash, cleanup: false };
|
||||
if (mode === "corrupt") {
|
||||
const corrupt = await file.createSyncAccessHandle();
|
||||
corrupt.write(TEXT_ENCODER.encode("{corrupt"), { at: 0 });
|
||||
corrupt.flush();
|
||||
corrupt.close();
|
||||
let damaged = null;
|
||||
try { damaged = JSON.parse(await (await file.getFile()).text()); } catch { damaged = null; }
|
||||
if (cleanup) { await project.removeEntry(FILE_NAME); await directory.removeEntry(projectId); }
|
||||
return !validateCommittedRecord(damaged, projectId)
|
||||
? { schema: OPFS_RUNTIME_SCHEMA, status: "verified", mode, project_id: projectId, corruption_detected: true, cleanup }
|
||||
: { schema: OPFS_RUNTIME_SCHEMA, status: "blocked", code: "opfs_corruption_undetected", project_id: projectId };
|
||||
}
|
||||
const persisted = JSON.parse(await (await file.getFile()).text());
|
||||
const valid = written === encoded.byteLength && size === encoded.byteLength && JSON.stringify(persisted) === JSON.stringify(record);
|
||||
if (cleanup) { await project.removeEntry(FILE_NAME); await directory.removeEntry(projectId); }
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -5,17 +5,18 @@ import { isOptionalSourceKey } from "./linuxcnc-source-semantics.mjs";
|
||||
|
||||
export const SIMULATION_CASE_SCHEMA = "simulation-case-bundle.v1";
|
||||
const RESOURCE_KEYS = new Set([
|
||||
"HAL", "HALFILE", "TOOLTABLE", "TOOL_TABLE", "VARFILE", "PARAMETER_FILE", "OPEN_FILE",
|
||||
"HAL", "HALFILE", "POSTGUI_HALFILE", "TOOLTABLE", "TOOL_TABLE", "VARFILE", "PARAMETER_FILE", "OPEN_FILE",
|
||||
"PROGRAM", "PROGRAMS", "PYTHON", "PYTHONFILE", "VCP", "VCPFILE", "CLPFILE",
|
||||
"CLASSICLADDER", "INTRO_GRAPHIC",
|
||||
"PYVCP", "NGCGUI_SUBFILE", "INTRO_GRAPHIC", "SUBROUTINE_PATH", "REMAP",
|
||||
]);
|
||||
const MANIFEST = new Set(SIM_FILE_INDEX);
|
||||
const PRELOADED_HAL_COMPONENTS = Object.freeze(["corexykins", "lineardeltakins", "motmod", "pid", "rotarydeltakins", "stepgen", "trivkins", "tpmod", "homemod"]);
|
||||
const PRELOADED_HAL_COMPONENTS = Object.freeze(["corexykins", "lineardeltakins", "motmod", "pid", "rotarydeltakins", "stepgen", "trivkins", "xyzac-trt-kins", "xyzbc-trt-kins", "tpmod", "homemod"]);
|
||||
const CASE_KINEMATICS = Object.freeze({
|
||||
"linuxcnc/configs/sim/axis/corexy/corexy_by_kins.ini": Object.freeze({ type: "corexy", path_mode: "kins" }),
|
||||
"linuxcnc/configs/sim/axis/corexy/corexy_by_hal.ini": Object.freeze({ type: "corexy", path_mode: "hal" }),
|
||||
"linuxcnc/configs/sim/axis/ldelta.ini": Object.freeze({ type: "lineardelta", path_mode: "kins" }),
|
||||
"linuxcnc/configs/sim/axis/rdelta.ini": Object.freeze({ type: "rotarydelta", path_mode: "kins" }),
|
||||
"linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini": Object.freeze({ type: "xyzac-trt", path_mode: "wasm", coordinates: "XYZAC", joint_count: 5, max_linear_velocity: 35, switchkins: { 0: "identity", 1: "xyzac", 2: "userk" }, offsets: { x_rot_point: 0, y_offset: 20, z_offset: 10, tool_offset: 0 } }),
|
||||
});
|
||||
const RUNTIME_FIXTURES = Object.freeze({
|
||||
"linuxcnc/configs/sim/axis/minimal_xyz.ini": Object.freeze({
|
||||
@@ -42,6 +43,12 @@ const RUNTIME_FIXTURES = Object.freeze({
|
||||
with: "HALFILE = xz_both_auto_cmds.hal",
|
||||
reason: "LinuxCNC basic_sim.tcl generated XZ BOTH AUTO HAL command fixture",
|
||||
}),
|
||||
"linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini": Object.freeze({
|
||||
generated_hal: "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt-headless_cmds.hal",
|
||||
replace: "HALFILE = LIB:basic_sim.tcl",
|
||||
with: "HALFILE = xyzac-trt-headless_cmds.hal",
|
||||
reason: "LinuxCNC basic_sim.tcl generated XYZAC HAL command fixture",
|
||||
}),
|
||||
});
|
||||
|
||||
function clone(value) { return structuredClone(value); }
|
||||
@@ -82,6 +89,10 @@ function candidates(source, raw, key = null, section = null) {
|
||||
const libraryPath = normalize(`linuxcnc/lib/hallib/${name}`);
|
||||
return libraryPath ? [libraryPath] : [];
|
||||
}
|
||||
if (["NGCGUI_SUBFILE", "REMAP"].includes(String(key).toUpperCase())) {
|
||||
const subroutine = normalize(`${dirname(source)}/remap_subs/${first}`);
|
||||
if (subroutine) return [subroutine];
|
||||
}
|
||||
if (["HAL", "HALFILE"].includes(String(key).toUpperCase()) && !first.includes("/") && !first.startsWith("linuxcnc/")) {
|
||||
const halCandidates = [normalize(`${dirname(source)}/${first}`), `linuxcnc/lib/hallib/${first}`].filter(Boolean);
|
||||
const indexed = halCandidates.filter(inManifest);
|
||||
@@ -128,6 +139,16 @@ function resourceReferences(source, content) {
|
||||
const line = rawLine.split("#", 1)[0].trim();
|
||||
const sectionMatch = line.match(/^\[([^\]]+)\]$/);
|
||||
if (sectionMatch) { section = sectionMatch[1].trim(); continue; }
|
||||
if (section === "RS274NGC" && /^SUBROUTINE_PATH\s*=\s*/i.test(line)) {
|
||||
const directory = normalize(`${dirname(source)}/${line.split("=", 2)[1].trim()}`);
|
||||
for (const candidate of SIM_FILE_INDEX.filter((path) => directory && path.startsWith(`${directory}/`) && path.endsWith(".ngc"))) references.push({ path: candidate, key: "SUBROUTINE_PATH", section, source });
|
||||
continue;
|
||||
}
|
||||
if (section === "RS274NGC" && /^REMAP\s*=\s*/i.test(line)) {
|
||||
const name = /ngc=([^\s]+)/i.exec(line)?.[1];
|
||||
if (name) { const candidate = normalize(`${dirname(source)}/remap_subs/${name}.ngc`); if (candidate) references.push({ path: candidate, key: "REMAP", section, source }); }
|
||||
continue;
|
||||
}
|
||||
const assignment = line.match(/^([^=:#]+?)\s*(?:=|:)\s*(.*)$/);
|
||||
if (assignment && RESOURCE_KEYS.has(assignment[1].trim().toUpperCase())) {
|
||||
const key = assignment[1].trim().toUpperCase();
|
||||
@@ -236,6 +257,7 @@ export function toWorkerConfiguration(bundle) {
|
||||
optional_missing: clone(bundle.optional_missing ?? []),
|
||||
runtime_fixtures: clone(bundle.runtime_fixtures ?? []),
|
||||
...(bundle.entrypoint === "linuxcnc/configs/sim/axis/vismach/3axis-tutorial/3axis-tutorial.ini" ? { home_limits: { z: [-240, 100] } } : {}),
|
||||
...(bundle.entrypoint === "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini" ? { home_limits: { joint_count: 5, kinematics_type: 2, coordinates: "XYZAC", x: [-200, 200], y: [-100, 100], z: [-120, 120], a: [-100, 50], c: [-36000, 36000] } } : {}),
|
||||
...(CASE_KINEMATICS[bundle.entrypoint] ? { kinematics: clone(CASE_KINEMATICS[bundle.entrypoint]) } : {}),
|
||||
};
|
||||
}
|
||||
|
||||
165
web/workspace/xyzac-trt-case-contract.mjs
Normal file
165
web/workspace/xyzac-trt-case-contract.mjs
Normal file
@@ -0,0 +1,165 @@
|
||||
export const XYZAC_TRT_CASE_CONTRACT = Object.freeze({
|
||||
"authority": "linuxcnc-wasm-worker",
|
||||
"case": {
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||
"sha256": "f985970db293b70c055e4c41fc316d0c16037b5dc7ce07490e18f554e851f811",
|
||||
"status": "indexed"
|
||||
},
|
||||
"commands": [
|
||||
"MDI_EXECUTE",
|
||||
"PROGRAM_LOAD",
|
||||
"PROGRAM_RUN",
|
||||
"PROGRAM_PAUSE",
|
||||
"PROGRAM_RESUME",
|
||||
"PROGRAM_ABORT",
|
||||
"JOG_START",
|
||||
"JOG_STOP",
|
||||
"HOME",
|
||||
"KINEMATICS_SELECT",
|
||||
"SNAPSHOT_REQUEST"
|
||||
],
|
||||
"context_id": "linuxcnc-2.10.0-pre0-wasm-m1-py-c1",
|
||||
"js_authority": 0,
|
||||
"report_sha256": "817531c109c341ccda78b085d2613f35379a716a98f54c9561a118f995b6cd5e",
|
||||
"resources": [
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini",
|
||||
"role": "case-entrypoint",
|
||||
"sha256": "f985970db293b70c055e4c41fc316d0c16037b5dc7ce07490e18f554e851f811",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.xml",
|
||||
"role": "PYVCP",
|
||||
"sha256": "84efa01584693ab8f4a9c0730e341a9dbe87d9341472081ee80d59a0bfb90941",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/switchkins_postgui.hal",
|
||||
"role": "POSTGUI_HALFILE",
|
||||
"sha256": "55e969c22ca9054a818dbedc8ec50bed768d9ab3d8c4bfdfcfc78e912e9271c4",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.tbl",
|
||||
"role": "TOOL_TABLE",
|
||||
"sha256": "41cf3c92574be99ecd6ed45e1869158b184620419d5d95a41649d25f15e141ff",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac.var",
|
||||
"role": "PARAMETER_FILE",
|
||||
"sha256": "163bbf80485fe2fe12700c1acb4b05eff774df68176d17e4eef5de8a7024c1ae",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzac_switchkins.ngc",
|
||||
"role": "OPEN_FILE",
|
||||
"sha256": "5afc03d5c1b692edd4154a6d37e5f94d74936a7bd5e82dc9c022f7bc21c7dc17",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/428remap.ngc",
|
||||
"role": "REMAP:M428",
|
||||
"sha256": "a0aee1d6b481a979849a17c0a23aa7524f52cbd875c92cbde3b73bf36f91d17b",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/429remap.ngc",
|
||||
"role": "REMAP:M429",
|
||||
"sha256": "160479860b84e55ace49ffceb9594285ee90c521ba5eef916ef47ac8a6b3349d",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/430remap.ngc",
|
||||
"role": "REMAP:M430",
|
||||
"sha256": "41090461a936a63432252d42f277c925f9aa5a0faf0497933910c3b051fcfe4f",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/xyzac_switchkins_sub.ngc",
|
||||
"role": "NGCGUI_SUBFILE",
|
||||
"sha256": "833bf21b8ed0ce600dcf5819234320d85348d906bbba390b62f3704836ea254d",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/centering.ngc",
|
||||
"role": "NGCGUI_SUBFILE",
|
||||
"sha256": "58cf585aa10480a80fe8ac8f2eab5e877adca51b4b3518b337eab476de01b11c",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/helix_ac.ngc",
|
||||
"role": "RTCP-SUBROUTINE",
|
||||
"sha256": "06cb755e33dc35d9a179b3c48e4203c8469632ab248d94379b10f2538dd0b54a",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/lib/hallib/basic_sim.tcl",
|
||||
"role": "HALFILE:LIB:basic_sim.tcl",
|
||||
"sha256": "d9851b0a8861e6d66de745a8ae230d4b38b65c34f8b05bdb6705792d2d4050be",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/linuxcnc.gif",
|
||||
"role": "INTRO_GRAPHIC",
|
||||
"sha256": "6c6da4afc3ecdc1b812dd6878318da0120996bd6889b4dd2ddb480b905d9f4b1",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/boat-xyzac.ngc",
|
||||
"role": "DEMO_PROGRAM",
|
||||
"sha256": "4d4769cbbb47ad7bf82c12bb1545c3854addebebe4642a3f50c8ccf5c560bcda",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/boat-xyzbc.ngc",
|
||||
"role": "DEMO_PROGRAM",
|
||||
"sha256": "f46a1f485ca85e4e9900b7a9b4f59fed7fc9d7cf27a403e15841b933ab98f0bf",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/impeller-7bl-xyzac.ngc",
|
||||
"role": "DEMO_PROGRAM",
|
||||
"sha256": "e90f0b4b6c43809da94a8170ee1029b5afc3e1bbe9bf2ae66298a8baefad013c",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzac_switchkins_test_1.ngc",
|
||||
"role": "DEMO_PROGRAM",
|
||||
"sha256": "3cdffc0481c1d6b0a0a67151a06448c686d4109249ed73438d5ff4e72b9e7267",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzac_switchkins_test_2.ngc",
|
||||
"role": "DEMO_PROGRAM",
|
||||
"sha256": "a0b05e672e7f8b5d9c8f42ac3fd16320f68a67926ae1413d361d60cc508d953f",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzac_switchkins_test_3.ngc",
|
||||
"role": "DEMO_PROGRAM",
|
||||
"sha256": "0e7e3c2fe554f888d3f5ef055390a7153ffa7f9eeedaac116f78832ff7a31cc1",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/demos/xyzbc_switchkins.ngc",
|
||||
"role": "DEMO_PROGRAM",
|
||||
"sha256": "75db56e24242b68e32a9a7f1943fe240c5f518cc1866ee823ea158f9f87ce9fe",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/helix_bc.ngc",
|
||||
"role": "REMAP_SUBROUTINE",
|
||||
"sha256": "2b8b36bf78d9227bd061083f883c72c7b0b205a8fe6d0a9407203b5375fe89fa",
|
||||
"status": "resolved"
|
||||
},
|
||||
{
|
||||
"path": "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/remap_subs/xyzbc_switchkins_sub.ngc",
|
||||
"role": "REMAP_SUBROUTINE",
|
||||
"sha256": "7265e848d0e123f439208caa88afccdf2803f34770042c778a8797a25d44537a",
|
||||
"status": "resolved"
|
||||
}
|
||||
],
|
||||
"schema": "xyzac-trt-case-contract.v1"
|
||||
});
|
||||
127
web/workspace/xyzac-trt-case-loader.mjs
Normal file
127
web/workspace/xyzac-trt-case-loader.mjs
Normal file
@@ -0,0 +1,127 @@
|
||||
import { fnv1a64 } from "../worker/replay.mjs";
|
||||
import { WorkspaceRuntime } from "./runtime.mjs";
|
||||
import { parseIni } from "./ini-parser.mjs";
|
||||
import { ConfigurationStartupStateMachine } from "./configuration-startup.mjs";
|
||||
import { loadSimulationCase, toWorkerConfiguration } from "./sim-case-loader.mjs";
|
||||
|
||||
export const XYZAC_TRT_CASE_LOADER_SCHEMA = "xyzac-trt-case-loader.v1";
|
||||
export const XYZAC_TRT_CASE = "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting/xyzac-trt.ini";
|
||||
export const XYZAC_TRT_STARTUP_STATES = Object.freeze(["TASK_NEW", "CONFIG_RESOLVED", "HAL_READY", "RUNTIME_READY", "TASK_READY"]);
|
||||
|
||||
const CASE_DIR = "linuxcnc/configs/sim/axis/vismach/5axis/table-rotary-tilting";
|
||||
const REQUIRED_RESOURCES = Object.freeze([
|
||||
[XYZAC_TRT_CASE, "case_entrypoint"],
|
||||
[`${CASE_DIR}/xyzac-trt.xml`, "pyvcp"],
|
||||
[`${CASE_DIR}/switchkins_postgui.hal`, "postgui_hal"],
|
||||
[`${CASE_DIR}/xyzac-trt.tbl`, "tool_table"],
|
||||
[`${CASE_DIR}/xyzac.var`, "parameter_file"],
|
||||
[`${CASE_DIR}/demos/xyzac_switchkins.ngc`, "open_file"],
|
||||
[`${CASE_DIR}/remap_subs/428remap.ngc`, "remap_m428"],
|
||||
[`${CASE_DIR}/remap_subs/429remap.ngc`, "remap_m429"],
|
||||
[`${CASE_DIR}/remap_subs/430remap.ngc`, "remap_m430"],
|
||||
[`${CASE_DIR}/remap_subs/xyzac_switchkins_sub.ngc`, "ngcgui_subfile"],
|
||||
[`${CASE_DIR}/remap_subs/centering.ngc`, "ngcgui_subfile"],
|
||||
[`${CASE_DIR}/remap_subs/helix_ac.ngc`, "rtcp_subroutine"],
|
||||
["linuxcnc/src/hal/user_comps/vismach/xyzac-trt-gui.py", "vismach_source"],
|
||||
["linuxcnc/lib/hallib/basic_sim.tcl", "hal_library"],
|
||||
["linuxcnc/linuxcnc.gif", "intro_graphic"],
|
||||
]);
|
||||
const REQUIRED_SECTIONS = Object.freeze(["DISPLAY", "TASK", "HAL", "RS274NGC", "KINS", "TRAJ", "EMCIO"]);
|
||||
|
||||
function clone(value) { return structuredClone(value); }
|
||||
|
||||
async function readExact(fetchImpl, baseUrl, path) {
|
||||
try {
|
||||
const response = await fetchImpl(`${String(baseUrl ?? "").replace(/\/$/, "")}/${path}`.replace(/^\//, "/"));
|
||||
if (!response?.ok) return null;
|
||||
return await response.text();
|
||||
} catch { return null; }
|
||||
}
|
||||
|
||||
function resourceRows(files) {
|
||||
return REQUIRED_RESOURCES.map(([path, role]) => {
|
||||
const content = files[path];
|
||||
return {
|
||||
path,
|
||||
role,
|
||||
status: typeof content === "string" ? "resolved" : "BLOCKED_RESOURCE",
|
||||
bytes: typeof content === "string" ? new TextEncoder().encode(content).byteLength : null,
|
||||
data_hash: typeof content === "string" ? fnv1a64(content) : null,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
function xyzacSections(parsed) {
|
||||
const sections = Object.keys(parsed.sections ?? {}).map((name) => name.toUpperCase());
|
||||
return Object.fromEntries(REQUIRED_SECTIONS.map((name) => [name, sections.includes(name)]));
|
||||
}
|
||||
|
||||
function startupProjection(startup, exactResources, sections) {
|
||||
const generic = startup;
|
||||
const resolved = exactResources.every((row) => row.status === "resolved");
|
||||
const requiredSections = Object.values(sections).every(Boolean);
|
||||
const state = generic.state === "TASK_READY" && resolved && requiredSections ? "TASK_READY" : generic.state === "BLOCKED" ? "BLOCKED" : "RUNTIME_READY";
|
||||
const transitions = [{ from: null, to: "TASK_NEW" }];
|
||||
if (generic.state !== "BLOCKED") {
|
||||
transitions.push({ from: "TASK_NEW", to: "CONFIG_RESOLVED" });
|
||||
transitions.push({ from: "CONFIG_RESOLVED", to: "HAL_READY" });
|
||||
transitions.push({ from: "HAL_READY", to: "RUNTIME_READY" });
|
||||
if (state === "TASK_READY") transitions.push({ from: "RUNTIME_READY", to: "TASK_READY" });
|
||||
} else transitions.push({ from: "TASK_NEW", to: "BLOCKED" });
|
||||
return {
|
||||
schema: "xyzac-trt-startup.v1",
|
||||
state,
|
||||
status: state === "TASK_READY" ? "ready" : "blocked",
|
||||
transitions,
|
||||
generic_state: generic.state,
|
||||
generic_status: generic.status,
|
||||
diagnostics: generic.diagnostics,
|
||||
hal: generic.hal,
|
||||
exact_resources: exactResources,
|
||||
required_sections: sections,
|
||||
state_hash: fnv1a64({ state, transitions, diagnostics: generic.diagnostics, exact_resources: exactResources, required_sections: sections }),
|
||||
};
|
||||
}
|
||||
|
||||
export async function loadXyzacTrtCase({ fetchImpl = globalThis.fetch, baseUrl = "", core = null, task = null } = {}) {
|
||||
if (typeof fetchImpl !== "function") throw new Error("xyzac_case_fetch_required");
|
||||
const bundle = await loadSimulationCase({ path: XYZAC_TRT_CASE, fetchImpl, baseUrl });
|
||||
const files = { ...bundle.files };
|
||||
for (const [path] of REQUIRED_RESOURCES) if (!(path in files)) {
|
||||
const content = await readExact(fetchImpl, baseUrl, path);
|
||||
if (content !== null) files[path] = content;
|
||||
}
|
||||
const exact = resourceRows(files);
|
||||
const caseText = files[XYZAC_TRT_CASE];
|
||||
const workspace = new WorkspaceRuntime({ files });
|
||||
let parsed = null;
|
||||
let startup = null;
|
||||
let parsedError = null;
|
||||
try {
|
||||
parsed = parseIni({ workspace, entrypoint: XYZAC_TRT_CASE });
|
||||
const machine = new ConfigurationStartupStateMachine({ workspace, entrypoint: XYZAC_TRT_CASE, core, task, halManifest: bundle.halManifest ?? [] });
|
||||
startup = startupProjection(machine.run(), exact, xyzacSections(parsed));
|
||||
} catch (error) {
|
||||
parsedError = { code: error?.code ?? "xyzac_startup_exception", message: String(error?.message ?? error) };
|
||||
}
|
||||
const sections = parsed ? xyzacSections(parsed) : Object.fromEntries(REQUIRED_SECTIONS.map((name) => [name, false]));
|
||||
const missing = exact.filter((row) => row.status !== "resolved").map((row) => row.path);
|
||||
const unsigned = {
|
||||
schema: XYZAC_TRT_CASE_LOADER_SCHEMA,
|
||||
case: XYZAC_TRT_CASE,
|
||||
case_hash: typeof caseText === "string" ? fnv1a64(caseText) : null,
|
||||
bundle_status: bundle.status,
|
||||
bundle_hash: bundle.state_hash,
|
||||
fetched: [...new Set(bundle.fetched ?? [])].sort(),
|
||||
missing: [...new Set([...(bundle.missing ?? []), ...missing])].sort(),
|
||||
exact_resources: exact,
|
||||
required_sections: sections,
|
||||
ini: parsed ? { state_hash: parsed.state_hash, graph: parsed.graph, includes: parsed.includes } : null,
|
||||
startup,
|
||||
parsed_error: parsedError,
|
||||
machine: { coordinates: "XYZAC", joint_count: 5, rotary_units: "degrees", kinematics: "xyzac-trt", switchkins: { 0: "identity", 1: "xyzac", 2: "userk" } },
|
||||
};
|
||||
return { ...clone(unsigned), status: startup?.status === "ready" && missing.length === 0 ? "ready" : "blocked", state_hash: fnv1a64(unsigned), worker_configuration: toWorkerConfiguration({ ...bundle, files, missing: unsigned.missing, status: unsigned.missing.length ? "blocked" : "ready" }) };
|
||||
}
|
||||
|
||||
export { REQUIRED_RESOURCES, REQUIRED_SECTIONS };
|
||||
1996
web/workspace/xyzac-trt-source-slices.mjs
Normal file
1996
web/workspace/xyzac-trt-source-slices.mjs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user