Files
linuxcncWebWASM/web/workspace/trt-geometry.mjs

77 lines
4.6 KiB
JavaScript

import { parseIni } from "./ini-parser.mjs";
import { parseHalLine } from "./hal-configuration.mjs";
import { WorkspaceRuntime } from "./runtime.mjs";
import { machineDefinition } from "./machine-definitions.mjs";
import { fnv1a64 } from "../worker/replay.mjs";
export const TRT_GEOMETRY_KEYS = Object.freeze(["x_rot_point", "y_rot_point", "z_rot_point", "x_offset", "y_offset", "z_offset"]);
const numeric = /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i;
function fail(code, detail) { throw Object.assign(new Error(`${code}: ${detail}`), { code }); }
export function readTrtGeometry({ entrypoint, files }) {
const machine = machineDefinition(entrypoint);
if (!machine) fail("trt_geometry_machine", entrypoint);
const parsed = parseIni({ workspace: new WorkspaceRuntime({ files }), entrypoint });
if (parsed.sections.TRAJ?.COORDINATES?.replace(/\s/g, "").toUpperCase() !== machine.coordinates || parsed.sections.KINS?.KINEMATICS?.split(/\s+/)[0] !== `${machine.id}-kins`) fail("trt_geometry_machine_mismatch", entrypoint);
const commands = parsed.entries.filter((e) => e.section.toUpperCase() === "HAL" && e.key.toUpperCase() === "HALCMD")
.map((entry) => ({ entry, record: parseHalLine(entry.value, entry.line, parsed.sections) }));
const unsupported = machine.coordinates === "XYZAC" ? "x_offset" : "y_offset";
const offsets = { tool_offset: 0 };
const sources = {};
for (const key of TRT_GEOMETRY_KEYS) {
const pin = `${machine.id}-kins.${key.replaceAll("_", "-")}`;
const nets = commands.filter(({ record: r }) => r?.command === "net" && r.tokens.includes(pin));
const direct = commands.filter(({ record: r }) => r?.command === "setp" && r.tokens[0] === pin);
if (nets.length > 1 || nets.length && direct.length) fail("trt_geometry_ambiguous", pin);
const assignments = nets.length ? commands.filter(({ record: r }) => r?.command === "sets" && r.tokens[0] === nets[0].record.tokens[0]) : direct;
if (assignments.length > 1) fail("trt_geometry_duplicate", pin);
if (!assignments.length) {
if (key !== unsupported) fail("trt_geometry_missing", pin);
offsets[key] = 0;
continue;
}
const { entry, record } = assignments[0];
const value = record.tokens[1];
if (record.tokens.length !== 2 || !numeric.test(value) || !Number.isFinite(Number(value))) fail("trt_geometry_number", `${pin} @ ${entry.source}:${entry.line}`);
if (key === unsupported && Number(value) !== 0) fail("trt_geometry_unsupported", pin);
offsets[key] = Number(value);
// Follow INI references so editing updates the original scalar definition.
const rawValue = entry.value.trim().split(/\s+/).at(-1);
const reference = /^\[([^\]]+)\](.+)$/.exec(rawValue);
let source = entry;
if (reference) {
const definitions = parsed.entries.filter((e) => e.section === reference[1] && e.key === reference[2]);
if (definitions.length !== 1) fail("trt_geometry_reference", rawValue);
source = definitions[0];
}
sources[key] = { path: source.source, line: source.line, value: reference ? source.value : rawValue };
if (Object.entries(sources).some(([other, location]) => other !== key && location.path === source.source && location.line === source.line)) fail("trt_geometry_shared_definition", key);
}
return { coordinates: machine.coordinates, offsets, sources, unsupported, state_hash: fnv1a64({ entrypoint, offsets, sources }) };
}
export function writeTrtGeometry(configuration, values) {
const geometry = readTrtGeometry(configuration);
const files = { ...configuration.files };
for (const key of TRT_GEOMETRY_KEYS) {
const raw = String(values[key] ?? geometry.offsets[key]).trim();
if (!numeric.test(raw) || !Number.isFinite(Number(raw))) fail("trt_geometry_number", key);
if (key === geometry.unsupported && Number(raw) !== 0) fail("trt_geometry_unsupported", key);
const source = geometry.sources[key];
if (!source) continue;
const lines = files[source.path].split("\n");
const line = lines[source.line - 1];
const comment = line.search(/[;#]/);
const end = comment < 0 ? line.length : comment;
const body = line.slice(0, end);
const index = body.lastIndexOf(source.value);
if (index < 0) fail("trt_geometry_source_changed", key);
lines[source.line - 1] = body.slice(0, index) + String(Number(raw)) + body.slice(index + source.value.length) + line.slice(end);
files[source.path] = lines.join("\n");
}
const result = { ...configuration, files };
if ("state_hash" in result) result.state_hash = fnv1a64({ entrypoint: configuration.entrypoint, files });
readTrtGeometry(result);
return result;
}