Update XYZBC web simulation evidence

This commit is contained in:
wangdequan
2026-07-03 08:49:13 -04:00
parent 6631833c93
commit 33d4b21332
2851 changed files with 141373 additions and 22 deletions

View File

@@ -322,8 +322,11 @@ function renderMainTabs(element, state, dispatch) {
<button type="button">centering</button>
</div>
<div class="axis-preview-wrap">
<div class="axis-preview-readout" data-preview-readout></div>
<canvas class="axis-preview-canvas" data-five-axis-canvas="true" aria-label="AXIS XYZBC TRT preview"></canvas>
<div class="axis-preview-stage">
<div class="axis-preview-readout" data-preview-readout></div>
<canvas class="axis-preview-canvas" data-five-axis-canvas="true" aria-label="AXIS XYZBC TRT preview"></canvas>
</div>
<aside class="axis-process-monitor" data-linuxcnc-process-monitor></aside>
</div>
`;
element.dataset.mounted = "true";
@@ -334,6 +337,7 @@ function renderMainTabs(element, state, dispatch) {
readout.dataset.kinsType = state.kinsType;
renderFiveAxisScene(element.querySelector("[data-five-axis-canvas]"), state);
renderLinuxCncProcessMonitor(element.querySelector("[data-linuxcnc-process-monitor]"), state);
}
function renderPyvcp(element, state, dispatch) {
@@ -360,10 +364,37 @@ function renderPyvcp(element, state, dispatch) {
function renderProgram(element, state) {
const currentLine = currentGcodeExecutionLine(state);
const live = state.programUiExecution || {};
const process = state.programAxisPreviewPath?.gcodeExecutionProcess || {};
const gcodeStep = live.gcodeStepIndex === null || live.gcodeStepIndex === undefined
? null
: process.executionSteps?.[Number(live.gcodeStepIndex)] || null;
element.dataset.liveSourceFile = live.sourceFile || "";
element.dataset.liveSourceLine = String(live.line || currentLine || 0);
element.dataset.liveSampleIndex = String(live.sampleIndex || 0);
element.dataset.liveExecutedSamples = String(live.executedSampleCount || 0);
element.dataset.gcodeExecutionStatus = process.status || "";
element.dataset.gcodeExecutionStepCount = String(process.executionStepCount || 0);
element.dataset.gcodeMotionStepCount = String(process.summary?.motionStepCount || 0);
element.dataset.gcodeParameterStepCount = String(process.summary?.parameterAssignmentStepCount || 0);
element.dataset.gcodeCallDepth = String(gcodeStep?.callDepth || 0);
const lines = state.programLines?.length
? state.programLines.slice(0, 80)
: [";"];
element.innerHTML = `
<section class="axis-gcode-live" data-gcode-live>
<header>
<span>${escapeHtml(live.sourceFile || state.activeProgram || "-")}:${formatNumber(live.line || currentLine, 0)}</span>
<b>${escapeHtml(live.operation || live.motionType || state.runState || "-")}</b>
</header>
<div class="axis-gcode-live-meta">
<span>sample ${formatNumber((live.sampleIndex || 0) + 1, 0)} / ${formatNumber(live.sampleCount || 0, 0)}</span>
<span>step ${live.gcodeStepIndex === null || live.gcodeStepIndex === undefined ? "-" : formatNumber(live.gcodeStepIndex, 0)}</span>
<span>${escapeHtml(live.activeKinematics || state.kinsType || "-")}</span>
</div>
<code>${escapeHtml(live.statement || "-")}</code>
${renderGcodeExecutionProcess(live, gcodeStep, process)}
</section>
<ol class="axis-program-list">
${lines.map((line, index) => {
const lineNo = state.programStartLine + index;
@@ -376,16 +407,147 @@ function renderProgram(element, state) {
if (activeRow) activeRow.scrollIntoView({ block: "center" });
}
function renderGcodeExecutionProcess(live, gcodeStep, process) {
const summary = process?.summary || {};
const motion = gcodeStep?.result?.motion || null;
const machine = live.machineState || gcodeStep?.result?.machineStateAfter || null;
const callStack = Array.isArray(gcodeStep?.callStack) ? gcodeStep.callStack : [];
const recentSteps = recentExecutedGcodeSteps(process, live.gcodeStepIndex);
return `
<section class="axis-gcode-process" data-gcode-process>
<header>
<span>expanded ${formatNumber(process?.executionStepCount || 0, 0)} steps</span>
<span>${formatNumber(summary.motionStepCount || 0, 0)} motion / ${formatNumber(summary.parameterAssignmentStepCount || 0, 0)} params</span>
</header>
<div class="axis-gcode-flow" data-gcode-call-stack>
${callStack.length > 0
? callStack.map((entry) => `
<span>${escapeHtml(entry.sourceFile)}:${formatNumber(entry.line, 0)} ${escapeHtml(entry.call)}</span>
`).join("<b>></b>")
: "<span>xyzbc_switchkins.ngc:2 o&lt;xyzbc_switchkins_sub&gt;</span>"}
</div>
<div class="axis-gcode-result-grid">
<span>Joint</span><b>${formatJointPose(live.joint || motion?.endJoint)}</b>
<span>TCP</span><b>${formatTcpPose(live.tcp || motion?.endTcp)}</b>
<span>Tool</span><b>${formatToolAxis(live.toolAxis || motion?.endToolAxis)}</b>
<span>Feed</span><b>${formatNumber(machine?.feed?.actualMmPerMin || motion?.feed || 0, 1)} mm/min ${machine?.cutting?.active ? "cutting" : "rapid"}</b>
</div>
<ol class="axis-gcode-step-list" data-gcode-step-list>
${recentSteps.map((step) => `
<li class="${Number(step.stepIndex) === Number(live.gcodeStepIndex) ? "active" : ""}" data-gcode-step="${formatNumber(step.stepIndex, 0)}">
<span>${formatNumber(step.stepIndex, 0)}</span>
<code>${escapeHtml(step.sourceFile)}:${formatNumber(step.line, 0)} ${escapeHtml(step.statement || step.result?.operation || "")}</code>
<b>${escapeHtml(step.result?.operation || step.sourceLineKind || "")}</b>
</li>
`).join("")}
</ol>
</section>
`;
}
function recentExecutedGcodeSteps(process = {}, currentStepIndex = null) {
const steps = Array.isArray(process.executionSteps) ? process.executionSteps : [];
if (steps.length === 0) return [];
const current = Number.isFinite(Number(currentStepIndex))
? Number(currentStepIndex)
: Math.max(0, steps.findIndex((step) => step.executed !== false));
const start = Math.max(0, current - 2);
return steps.slice(start, start + 5);
}
function formatJointPose(pose = {}) {
if (!pose) return "-";
return `X${formatNumber(pose.x, 3)} Y${formatNumber(pose.y, 3)} Z${formatNumber(pose.z, 3)} B${formatNumber(pose.b, 3)} C${formatNumber(pose.c, 3)}`;
}
function formatTcpPose(pose = {}) {
if (!pose) return "-";
return `X${formatNumber(pose.x, 3)} Y${formatNumber(pose.y, 3)} Z${formatNumber(pose.z, 3)}`;
}
function formatToolAxis(axis = {}) {
if (!axis) return "-";
return `I${formatNumber(axis.i ?? axis.x, 3)} J${formatNumber(axis.j ?? axis.y, 3)} K${formatNumber(axis.k ?? axis.z, 3)}`;
}
function renderStatusbar(element, state) {
const toolLabel = formatToolStatus(state);
const monitor = state.linuxCncProcessMonitor || {};
element.innerHTML = `
<div>${state.machine.powerOn ? "ON" : state.machine.estopActive ? "ESTOP" : "OFF"}</div>
<div>${escapeHtml(toolLabel)}</div>
<div>Position: Joint</div>
<div>F ${formatNumber(monitor.feed?.currentVelocityMmPerMin || 0, 1)} S ${formatNumber(monitor.spindle?.actualRpm || 0, 0)} ${monitor.coolant?.flood ? "FLOOD" : monitor.coolant?.mist ? "MIST" : "M9"}</div>
`;
element.dataset.toolStatus = toolLabel;
}
function renderLinuxCncProcessMonitor(element, state) {
if (!element) return;
const monitor = state.linuxCncProcessMonitor || {};
const axes = monitor.axes || {};
const path = monitor.path || {};
const feed = monitor.feed || {};
const spindle = monitor.spindle || {};
const coolant = monitor.coolant || {};
const tool = monitor.toolChange || {};
const runtime = monitor.runtime || {};
element.dataset.sourceMode = path.executionSource || state.programExecutionSourceMode || "";
element.dataset.taskHalReady = String(runtime.taskHalReady === true);
element.dataset.activeLine = String(path.activeLine || state.activeLine || 0);
element.dataset.liveSourceFile = path.uiExecution?.sourceFile || "";
element.dataset.liveSourceLine = String(path.uiExecution?.line || path.activeLine || 0);
element.dataset.liveSampleIndex = String(path.uiExecution?.sampleIndex || 0);
element.innerHTML = `
<header>
<strong>LinuxCNC Task / Motion / HAL</strong>
<span>${escapeHtml(monitor.control?.runState || state.runState)}</span>
</header>
<section class="axis-monitor-group" data-monitor-group="position">
<h3>Position</h3>
${monitorAxisRows(axes.joint || state.axisPose || {})}
<div class="axis-monitor-row"><span>TCP</span><b>${formatNumber(axes.tcp?.x, 3)} ${formatNumber(axes.tcp?.y, 3)} ${formatNumber(axes.tcp?.z, 3)}</b></div>
<div class="axis-monitor-row"><span>DTG</span><b>${formatNumber(axes.distanceToGo?.x, 3)} ${formatNumber(axes.distanceToGo?.y, 3)} ${formatNumber(axes.distanceToGo?.z, 3)}</b></div>
</section>
<section class="axis-monitor-group" data-monitor-group="path">
<h3>Tool Path</h3>
<div class="axis-monitor-row"><span>Line</span><b>${formatNumber(path.activeLine, 0)} / ${formatNumber(state.lineCount || state.programLines?.length || 0, 0)}</b></div>
<div class="axis-monitor-row"><span>Source</span><b>${escapeHtml(path.uiExecution?.sourceFile || "-")}:${formatNumber(path.uiExecution?.line || 0, 0)}</b></div>
<div class="axis-monitor-row"><span>Motion</span><b>${formatNumber(path.motionIndex + 1, 0)} / ${formatNumber(path.motionCount, 0)}</b></div>
<div class="axis-monitor-row"><span>Sample</span><b>${formatNumber(path.sampleIndex + 1, 0)} / ${formatNumber(path.sampleCount, 0)}</b></div>
<div class="axis-monitor-code">${escapeHtml(path.activeGcode || "-")}</div>
</section>
<section class="axis-monitor-group" data-monitor-group="cutting">
<h3>Cutting</h3>
<div class="axis-monitor-row"><span>Feed</span><b>${formatNumber(feed.currentVelocityMmPerMin, 1)} mm/min</b></div>
<div class="axis-monitor-row"><span>Cut</span><b>${formatNumber(feed.cuttingVelocityMmPerMin, 1)} mm/min</b></div>
<div class="axis-monitor-row"><span>F cmd</span><b>${formatNumber(feed.feedRate, 1)} ${formatNumber(feed.feedOverridePercent, 0)}%</b></div>
<div class="axis-monitor-row"><span>Spindle</span><b>${spindle.enabled ? escapeHtml(spindle.direction) : "stop"} ${formatNumber(spindle.actualRpm, 0)} rpm</b></div>
<div class="axis-monitor-row"><span>Coolant</span><b>${coolant.flood ? "flood" : coolant.mist ? "mist" : "off"}</b></div>
</section>
<section class="axis-monitor-group" data-monitor-group="tool">
<h3>Tool Change</h3>
<div class="axis-monitor-row"><span>Tool</span><b>T${formatNumber(tool.activeToolNumber || tool.toolInSpindle, 0)} P${formatNumber(tool.activePocket || tool.toolFromPocket, 0)}</b></div>
<div class="axis-monitor-row"><span>Offset</span><b>Z ${formatNumber(tool.lengthOffsetZ, 3)} D ${formatNumber(tool.diameter, 3)}</b></div>
<div class="axis-monitor-row"><span>IO</span><b>${escapeHtml(tool.emcioStatus || "UNKNOWN")}</b></div>
</section>
<section class="axis-monitor-group" data-monitor-group="runtime">
<h3>Runtime</h3>
<div class="axis-monitor-row"><span>Task/HAL</span><b>${runtime.taskHalReady ? "ready" : "blocked"}</b></div>
<div class="axis-monitor-row"><span>Loop</span><b>${runtime.taskHalLoopActive ? "active" : "idle"} #${formatNumber(runtime.taskHalTickCount, 0)}</b></div>
<div class="axis-monitor-row"><span>Queue</span><b>${formatNumber(path.queueDepth, 0)} / ${formatNumber(path.activeDepth, 0)}</b></div>
</section>
`;
}
function monitorAxisRows(axisPose) {
return ["x", "y", "z", "a", "b", "c"].map((axis) => `
<div class="axis-monitor-row">
<span>${axis.toUpperCase()}</span>
<b>${formatNumber(axisPose?.[axis], axis === "x" || axis === "y" || axis === "z" ? 3 : 4)}</b>
</div>
`).join("");
}
function toolButton(buttonId, action, title, active = false) {
const icon = getGmoccapyIcon(buttonId, active ? "active" : "inactive");
const iconMarkup = icon.path