完善 xyzbc-trt 刀具偏置闭环
This commit is contained in:
@@ -8,8 +8,16 @@ import {
|
||||
selectMachineFileProgram,
|
||||
stageProfileMachineFiles,
|
||||
} from "../app/src/runtime/linuxcnc-machine-file-staging.js";
|
||||
import {
|
||||
applyToolCommandSequence,
|
||||
createToolDbSimulation,
|
||||
createToolRuntimeState,
|
||||
extractToolCommandSequenceFromProgram,
|
||||
parseLinuxCncToolTable,
|
||||
} from "../app/src/runtime/tool-db-simulation.js";
|
||||
import { createSimulationStore } from "../app/src/state/store.js";
|
||||
import { getFiveAxisProfile } from "../app/src/profiles/index.js";
|
||||
import { buildVismachModelState } from "../app/src/runtime/vismach-model-state.js";
|
||||
|
||||
const SAMPLE_PERIOD_MS = 20;
|
||||
const repoRoot = resolve(dirname(fileURLToPath(import.meta.url)), "../..");
|
||||
@@ -42,7 +50,13 @@ const paths = await collectPathEvidence({
|
||||
selectedPlan,
|
||||
wasmArtifacts,
|
||||
});
|
||||
const semanticFields = buildSemanticFields({ profile, ini, staged, state });
|
||||
const toolRuntime = buildToolRuntimeEvidence({
|
||||
profile,
|
||||
staged,
|
||||
selectedPlan,
|
||||
fallbackToolLength: state.toolPreview?.length,
|
||||
});
|
||||
const semanticFields = buildSemanticFields({ profile, ini, staged, state, toolRuntime });
|
||||
|
||||
const evidence = {
|
||||
apiName: "xyzbc-trt-web-opfs-wasm-evidence",
|
||||
@@ -110,6 +124,7 @@ const evidence = {
|
||||
pathSampling: createPathSampling(),
|
||||
previewPath: paths.previewPath,
|
||||
executionPath: paths.executionPath,
|
||||
toolRuntime,
|
||||
...semanticFields,
|
||||
wasm: wasmArtifacts,
|
||||
coverage: {
|
||||
@@ -137,6 +152,11 @@ const evidence = {
|
||||
&& Boolean(semanticFields.axisJointLimits.axisLimits.C),
|
||||
previewPathAvailable: paths.previewPath.sampleCount > 0,
|
||||
executionPathAvailable: paths.executionPath.sampleCount > 0,
|
||||
toolTableToToolOffsetClosed: toolRuntime.ready
|
||||
&& toolRuntime.toolTable.toolCount > 0
|
||||
&& toolRuntime.activeOffsetApplied
|
||||
&& toolRuntime.kinematics.toolOffsetZ === toolRuntime.pathTool.length
|
||||
&& toolRuntime.vismach.toolOffset === toolRuntime.pathTool.length,
|
||||
wasmArtifactsReady: wasmArtifacts.ready,
|
||||
},
|
||||
blockers: [
|
||||
@@ -222,11 +242,16 @@ async function collectPathEvidence({ profile, staged, selectedPlan, wasmArtifact
|
||||
files: staged.save.files,
|
||||
executionMode: "fiveAxisRemap",
|
||||
});
|
||||
const previewPath = pathFromWebMotion(execution, profile);
|
||||
const toolRuntime = buildToolRuntimeEvidence({
|
||||
profile,
|
||||
staged,
|
||||
selectedPlan,
|
||||
});
|
||||
const previewPath = pathFromWebMotion(execution, profile, toolRuntime.pathTool);
|
||||
return {
|
||||
previewPath,
|
||||
executionPath: wasmArtifacts.ready
|
||||
? await pathFromTaskHalExecution({ profile, staged, selectedPlan, execution })
|
||||
? await pathFromTaskHalExecution({ profile, staged, selectedPlan, execution, toolRuntime })
|
||||
: emptyPath("web-task-hal", "missing task/HAL WASM runtime artifacts"),
|
||||
};
|
||||
} catch (error) {
|
||||
@@ -237,7 +262,7 @@ async function collectPathEvidence({ profile, staged, selectedPlan, wasmArtifact
|
||||
}
|
||||
}
|
||||
|
||||
function pathFromWebMotion(execution, profile) {
|
||||
function pathFromWebMotion(execution, profile, pathTool = null) {
|
||||
const plannerSamples = execution.plannerTiming?.samples || [];
|
||||
const motion = execution.motion || [];
|
||||
const motionByIndex = new Map(motion.map((event, index) => [index, event]));
|
||||
@@ -252,7 +277,7 @@ function pathFromWebMotion(execution, profile) {
|
||||
motionType: motionTypeFromCanonical(sample.type || event.type),
|
||||
activeKinematics: activeKinematics(event),
|
||||
axes,
|
||||
tool: firstTool(profile),
|
||||
tool: pathTool || firstTool(profile),
|
||||
feed: sample.currentVelocityMmPerMin ?? event.feedRate ?? 0,
|
||||
spindle: 0,
|
||||
});
|
||||
@@ -311,7 +336,7 @@ function interpolatePlannerSample(left, right, timeMs) {
|
||||
};
|
||||
}
|
||||
|
||||
async function pathFromTaskHalExecution({ profile, staged, selectedPlan, execution }) {
|
||||
async function pathFromTaskHalExecution({ profile, staged, selectedPlan, execution, toolRuntime = null }) {
|
||||
try {
|
||||
const { createLinuxCncTaskHalSdk } = await import("../../wasm-port/runtime/sdk/src/linuxcnc-task-hal.js");
|
||||
const {
|
||||
@@ -377,7 +402,7 @@ async function pathFromTaskHalExecution({ profile, staged, selectedPlan, executi
|
||||
switchkinsType: status.ui?.switchkinsType ?? motion.switchkinsType,
|
||||
}),
|
||||
axes: xyzbcAxesFromTaskHalStatus(status),
|
||||
tool: firstTool(profile),
|
||||
tool: toolRuntime?.pathTool || firstTool(profile),
|
||||
feed: status.ui?.currentVelocity ?? Number(motion.currentVel || motion.currentVelocity || 0) * 60,
|
||||
spindle: spindleFromTaskHalStatus(status),
|
||||
});
|
||||
@@ -409,7 +434,7 @@ async function pathFromTaskHalExecution({ profile, staged, selectedPlan, executi
|
||||
}
|
||||
}
|
||||
|
||||
function buildSemanticFields({ profile, ini, staged, state }) {
|
||||
function buildSemanticFields({ profile, ini, staged, state, toolRuntime }) {
|
||||
const postguiNets = [
|
||||
{ signal: "kinstype.is-0", source: "kinstype.is-0", target: "pyvcp.multilabel.0.legend0", boundary: "postgui-hal" },
|
||||
{ signal: "kinstype.is-1", source: "kinstype.is-1", target: "pyvcp.multilabel.0.legend1", boundary: "postgui-hal" },
|
||||
@@ -425,6 +450,10 @@ function buildSemanticFields({ profile, ini, staged, state }) {
|
||||
...(profile.hal?.halcmd?.offsetNets || []),
|
||||
].filter(Boolean).map((net) => ({ ...net, boundary: "ini-halcmd" }));
|
||||
const gcodeFiles = staged.save.gcodeFiles || [];
|
||||
const vismachModelState = buildVismachModelState({
|
||||
...state,
|
||||
toolRuntimeState: toolRuntime,
|
||||
});
|
||||
|
||||
return {
|
||||
startupSequence: [
|
||||
@@ -460,6 +489,8 @@ function buildSemanticFields({ profile, ini, staged, state }) {
|
||||
zRotPoint: profile.offsets?.zRotPoint,
|
||||
conventionalDirections: profile.offsets?.conventionalDirections,
|
||||
toolOffsetSource: "motion.tooloffset.z",
|
||||
toolOffsetValue: toolRuntime.kinematics.toolOffsetZ,
|
||||
toolOffsetToolNumber: toolRuntime.activeToolNumber,
|
||||
pins: profile.halPins,
|
||||
},
|
||||
axisJointLimits: {
|
||||
@@ -498,17 +529,26 @@ function buildSemanticFields({ profile, ini, staged, state }) {
|
||||
vismachEquivalent: {
|
||||
sourceGui: "src/hal/user_comps/vismach/xyzbc-trt-gui.py",
|
||||
webModel: "app/src/visualization/five-axis-scene.js",
|
||||
pins: [
|
||||
"table-x",
|
||||
"saddle-y",
|
||||
"spindle-z",
|
||||
"tilt-b",
|
||||
"rotate-c",
|
||||
"tool-offset",
|
||||
"x-offset",
|
||||
"z-offset",
|
||||
],
|
||||
pins: Object.keys(vismachModelState.pins),
|
||||
pinValues: vismachModelState.pins,
|
||||
transforms: vismachModelState.transforms,
|
||||
halNets: vismachModelState.halNets,
|
||||
clearTraceSignal: "pyvcp.vismach-clear => vismach.plotclear",
|
||||
semanticBoundary: vismachModelState.semanticBoundary,
|
||||
},
|
||||
toolOffsetClosure: {
|
||||
sourceToolTable: toolRuntime.toolTable.sourceRel,
|
||||
selectedProgramSourceRel: toolRuntime.selectedProgramSourceRel,
|
||||
programToolCommands: toolRuntime.programToolCommands,
|
||||
activeToolOffset: toolRuntime.activeToolOffset,
|
||||
kinematicsToolOffsetZ: toolRuntime.kinematics.toolOffsetZ,
|
||||
pathTool: toolRuntime.pathTool,
|
||||
vismachToolOffset: toolRuntime.vismach.toolOffset,
|
||||
closed: toolRuntime.ready
|
||||
&& toolRuntime.activeOffsetApplied
|
||||
&& toolRuntime.kinematics.toolOffsetZ === toolRuntime.pathTool.length
|
||||
&& toolRuntime.vismach.toolOffset === toolRuntime.pathTool.length,
|
||||
semanticBoundary: "tool_table_current_t_p_z_d_drives_kinematics_path_and_vismach",
|
||||
},
|
||||
ngcguiSubroutines: ["xyzbc_switchkins_sub.ngc", "centering.ngc", "helix_bc.ngc"].map((filename) => ({
|
||||
filename,
|
||||
@@ -524,6 +564,58 @@ function buildSemanticFields({ profile, ini, staged, state }) {
|
||||
};
|
||||
}
|
||||
|
||||
function buildToolRuntimeEvidence({ profile, staged, selectedPlan, fallbackToolLength = 0 }) {
|
||||
const toolTableFile = staged.save.files.find((file) => file.sourceRel === profile.toolTablePath)
|
||||
|| staged.save.files.find((file) => file.kind === "toolTable");
|
||||
const programFile = staged.save.files.find((file) => (
|
||||
file.sourceRel === selectedPlan.selectedProgramSourceRel
|
||||
|| (file.wasmPath || file.path) === selectedPlan.wasmProgramPath
|
||||
));
|
||||
const toolTable = parseLinuxCncToolTable(toolTableFile?.text || "", {
|
||||
sourceRel: toolTableFile?.sourceRel || profile.toolTablePath,
|
||||
path: toolTableFile?.wasmPath || toolTableFile?.path || null,
|
||||
});
|
||||
const programToolCommands = extractToolCommandSequenceFromProgram(programFile?.text || "");
|
||||
const commands = programToolCommands.length > 0
|
||||
? programToolCommands
|
||||
: defaultToolActivationCommands(toolTable);
|
||||
const toolDb = applyToolCommandSequence(createToolDbSimulation({
|
||||
toolTable,
|
||||
profile,
|
||||
storageMode: staged.save.storageMode,
|
||||
}), commands);
|
||||
const runtimeState = createToolRuntimeState(toolDb, { fallbackToolLength });
|
||||
return {
|
||||
...runtimeState,
|
||||
selectedProgramSourceRel: selectedPlan.selectedProgramSourceRel,
|
||||
toolTable: {
|
||||
sourceRel: toolTable.sourceRel,
|
||||
toolCount: toolTable.toolCount,
|
||||
entries: toolTable.entries.map((entry) => ({
|
||||
idx: entry.idx,
|
||||
toolNumber: entry.toolNumber,
|
||||
pocket: entry.pocket,
|
||||
z: entry.offset.z,
|
||||
diameter: entry.diameter,
|
||||
})),
|
||||
},
|
||||
programToolCommands,
|
||||
appliedToolCommands: commands,
|
||||
defaultActivationUsed: programToolCommands.length === 0,
|
||||
};
|
||||
}
|
||||
|
||||
function defaultToolActivationCommands(toolTable) {
|
||||
const preferred = toolTable.entries.find((entry) => entry.offset.z !== 0)
|
||||
|| toolTable.entries.find((entry) => entry.toolNumber > 0);
|
||||
if (!preferred) return [];
|
||||
return [
|
||||
{ code: "T", toolNumber: preferred.toolNumber, source: "default-tool-table-activation" },
|
||||
{ code: "M6", source: "default-tool-table-activation" },
|
||||
{ code: "G43", h: preferred.toolNumber, toolNumber: preferred.toolNumber, source: "default-tool-table-activation" },
|
||||
];
|
||||
}
|
||||
|
||||
function createPathSampling() {
|
||||
return {
|
||||
samplePeriodMs: SAMPLE_PERIOD_MS,
|
||||
|
||||
@@ -37,6 +37,17 @@ const checks = [
|
||||
check("tool-table", "web tool table staged", webEvidence.coverage?.toolTableStaged === true, {
|
||||
toolTable: webEvidence.ini?.toolTable,
|
||||
}),
|
||||
check("tool-table", "web tool table active offset drives kinematics, path, and Vismach", webEvidence.coverage?.toolTableToToolOffsetClosed === true, {
|
||||
toolRuntime: {
|
||||
activeToolNumber: webEvidence.toolRuntime?.activeToolNumber,
|
||||
activePocket: webEvidence.toolRuntime?.activePocket,
|
||||
kinematicsToolOffsetZ: webEvidence.toolRuntime?.kinematics?.toolOffsetZ,
|
||||
pathTool: webEvidence.toolRuntime?.pathTool,
|
||||
vismachToolOffset: webEvidence.toolRuntime?.vismach?.toolOffset,
|
||||
defaultActivationUsed: webEvidence.toolRuntime?.defaultActivationUsed,
|
||||
},
|
||||
toolOffsetClosure: webEvidence.toolOffsetClosure,
|
||||
}),
|
||||
check("pyvcp", "web PyVCP XML staged", webEvidence.coverage?.pyvcpXmlStaged === true, {
|
||||
pyvcpXmlPath: webEvidence.profile?.pyvcpXmlPath,
|
||||
}),
|
||||
|
||||
Reference in New Issue
Block a user