按规划持续工作
This commit is contained in:
@@ -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);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -123,6 +123,53 @@
|
||||
font: 13px/1.5 "SFMono-Regular", "Consolas", monospace;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
.run-monitor {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
background: #fbfdff;
|
||||
padding: 12px;
|
||||
display: grid;
|
||||
gap: 10px;
|
||||
}
|
||||
.progress-row {
|
||||
display: grid;
|
||||
grid-template-columns: minmax(0, 1fr) auto;
|
||||
gap: 10px;
|
||||
align-items: center;
|
||||
}
|
||||
progress {
|
||||
width: 100%;
|
||||
height: 14px;
|
||||
accent-color: var(--accent-2);
|
||||
}
|
||||
.run-readout {
|
||||
font: 13px/1.4 "SFMono-Regular", "Consolas", monospace;
|
||||
color: var(--muted);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.axis-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
.axis-cell {
|
||||
border: 1px solid #edf2f6;
|
||||
border-radius: 6px;
|
||||
padding: 8px;
|
||||
display: grid;
|
||||
gap: 4px;
|
||||
min-width: 0;
|
||||
}
|
||||
.axis-cell span:first-child {
|
||||
color: var(--muted);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.04em;
|
||||
}
|
||||
.axis-cell span:last-child {
|
||||
font: 14px/1.2 "SFMono-Regular", "Consolas", monospace;
|
||||
overflow-wrap: anywhere;
|
||||
}
|
||||
.events {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
@@ -154,6 +201,11 @@
|
||||
.grid { grid-template-columns: 1fr; }
|
||||
textarea { min-height: 320px; }
|
||||
}
|
||||
@media (max-width: 560px) {
|
||||
.axis-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
.progress-row { grid-template-columns: 1fr; }
|
||||
.run-readout { white-space: normal; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -226,6 +278,28 @@
|
||||
<dd id="field-fiveaxis-remap">-</dd>
|
||||
</dl>
|
||||
|
||||
<h2>Run Monitor</h2>
|
||||
<div class="run-monitor">
|
||||
<div class="progress-row">
|
||||
<progress id="run-progress" max="100" value="0"></progress>
|
||||
<span id="run-progress-label" class="run-readout">0%</span>
|
||||
</div>
|
||||
<div id="run-motion" class="run-readout">idle</div>
|
||||
<div id="run-line" class="run-readout">line -</div>
|
||||
<div id="run-statement" class="run-readout">-</div>
|
||||
<div class="axis-grid" id="axis-grid">
|
||||
<div class="axis-cell"><span>X</span><span id="axis-x">0.000</span></div>
|
||||
<div class="axis-cell"><span>Y</span><span id="axis-y">0.000</span></div>
|
||||
<div class="axis-cell"><span>Z</span><span id="axis-z">0.000</span></div>
|
||||
<div class="axis-cell"><span>A</span><span id="axis-a">0.000</span></div>
|
||||
<div class="axis-cell"><span>B</span><span id="axis-b">0.000</span></div>
|
||||
<div class="axis-cell"><span>C</span><span id="axis-c">0.000</span></div>
|
||||
<div class="axis-cell"><span>U</span><span id="axis-u">0.000</span></div>
|
||||
<div class="axis-cell"><span>V</span><span id="axis-v">0.000</span></div>
|
||||
<div class="axis-cell"><span>W</span><span id="axis-w">0.000</span></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="log" id="log"></div>
|
||||
|
||||
<h2>Canonical Events</h2>
|
||||
|
||||
Reference in New Issue
Block a user