按规划持续工作

This commit is contained in:
2026-06-09 06:13:28 +08:00
parent 902c6ecaea
commit 2658ee3b72
255 changed files with 12487 additions and 150 deletions

View File

@@ -67,6 +67,22 @@ const eventCountNode = document.getElementById("event-count");
const wasmBadge = document.getElementById("wasm-badge");
const interpBadge = document.getElementById("interp-badge");
const opfsBadge = document.getElementById("opfs-badge");
const runProgressNode = document.getElementById("run-progress");
const runProgressLabelNode = document.getElementById("run-progress-label");
const runMotionNode = document.getElementById("run-motion");
const runLineNode = document.getElementById("run-line");
const runStatementNode = document.getElementById("run-statement");
const axisNodes = {
x: document.getElementById("axis-x"),
y: document.getElementById("axis-y"),
z: document.getElementById("axis-z"),
a: document.getElementById("axis-a"),
b: document.getElementById("axis-b"),
c: document.getElementById("axis-c"),
u: document.getElementById("axis-u"),
v: document.getElementById("axis-v"),
w: document.getElementById("axis-w"),
};
const fields = {
machine: document.getElementById("field-machine"),
@@ -88,6 +104,7 @@ let iniSdk = null;
let interpSdk = null;
let loadedSession = null;
let canonicalEventText = "";
let runPlaybackTimer = null;
function setBadge(node, text, className = "badge") {
node.className = className;
@@ -119,6 +136,109 @@ function setField(name, value) {
fields[name].textContent = value ?? "-";
}
function nextFrame() {
return new Promise((resolve) => requestAnimationFrame(resolve));
}
function formatAxisValue(value) {
return Number.isFinite(value) ? value.toFixed(3) : "0.000";
}
function clearRunPlayback() {
if (runPlaybackTimer) {
clearInterval(runPlaybackTimer);
runPlaybackTimer = null;
}
}
function setRunMonitor(progress, label, axes = {}, motionText = "idle", lineText = "line -", statement = "-") {
runProgressNode.value = Math.max(0, Math.min(100, progress));
runProgressLabelNode.textContent = label;
runMotionNode.textContent = motionText;
runLineNode.textContent = lineText;
runStatementNode.textContent = statement;
for (const [axis, node] of Object.entries(axisNodes)) {
node.textContent = formatAxisValue(axes[axis] ?? 0);
}
}
function readCanonicalNumber(line, name) {
const match = line.match(new RegExp(`(?:^| )${name}=([-+0-9.eE]+)`));
return match ? Number(match[1]) : null;
}
function programLineMap(programText) {
const lines = new Map();
programText.split(/\r?\n/).forEach((line, index) => {
lines.set(index + 1, line.trim() || "(blank)");
});
return lines;
}
function parseRunMotion(resultText, programText) {
const axes = { x: 0, y: 0, z: 0, a: 0, b: 0, c: 0, u: 0, v: 0, w: 0 };
const snapshots = [];
const sourceLines = programLineMap(programText);
for (const line of resultText.split("\n")) {
const motion = line.match(/^canon_event=(STRAIGHT_TRAVERSE|STRAIGHT_FEED|ARC_FEED)\b/);
if (!motion) {
continue;
}
for (const axis of Object.keys(axes)) {
const value = readCanonicalNumber(line, axis);
if (value !== null && Number.isFinite(value)) {
axes[axis] = value;
}
}
const sourceLine = readCanonicalNumber(line, "line");
snapshots.push({
type: motion[1],
line: sourceLine,
statement: Number.isFinite(sourceLine) ? (sourceLines.get(sourceLine) ?? "-") : "-",
axes: { ...axes },
});
}
return snapshots;
}
function showRunSnapshot(snapshot, index, total) {
const progress = total > 0 ? Math.round(((index + 1) / total) * 100) : 0;
const lineText = Number.isFinite(snapshot.line) ? `line ${snapshot.line}` : "line -";
setRunMonitor(
progress,
`${progress}%`,
snapshot.axes,
`${snapshot.type} ${lineText} (${index + 1}/${total})`,
lineText,
snapshot.statement,
);
}
function playRunMotion(resultText, programText) {
clearRunPlayback();
const snapshots = parseRunMotion(resultText, programText);
if (snapshots.length === 0) {
setRunMonitor(100, "100%", {}, "complete; no axis motion");
return;
}
let index = 0;
showRunSnapshot(snapshots[index], index, snapshots.length);
runPlaybackTimer = setInterval(() => {
index += 1;
if (index >= snapshots.length) {
clearRunPlayback();
showRunSnapshot(snapshots[snapshots.length - 1], snapshots.length - 1, snapshots.length);
return;
}
showRunSnapshot(snapshots[index], index, snapshots.length);
}, 220);
}
function requireIniSdk() {
if (!iniSdk) {
throw new Error("INI WASM module is not ready yet.");
@@ -287,10 +407,15 @@ document.getElementById("run-gcode").addEventListener("click", async () => {
const interp = requireInterpSdk();
const programText = await loadGcodeProgram(GCODE_FILENAME);
interp.writeTextFile(GCODE_WASM_FILE, programText);
clearRunPlayback();
setField("runStatus", "running");
setRunMonitor(5, "running", {}, "running");
await nextFrame();
const result = interp.runFileWithIni(GCODE_WASM_FILE, loadedSession.ini.wasmPath);
setField("sessionGcode", GCODE_WASM_FILE);
setField("runStatus", "ok");
setCanonicalEvents(result.trim());
playRunMotion(result.trim(), programText);
setLog(
[
"Ran G-code through LinuxCNC interpreter WASM.",
@@ -302,6 +427,8 @@ document.getElementById("run-gcode").addEventListener("click", async () => {
} catch (error) {
setField("runStatus", "failed");
setCanonicalEvents("");
clearRunPlayback();
setRunMonitor(0, "failed", {}, "failed");
setLog(`G-code run failed: ${error.message}`, true);
}
});