完善 xyzbc-trt 刀具偏置闭环
This commit is contained in:
@@ -8,6 +8,7 @@
|
||||
"dev": "python3 -m http.server 4174",
|
||||
"smoke": "npm run smoke:node",
|
||||
"smoke:node": "node ../tests/node/verify_xyzbc_trt_web_app.mjs",
|
||||
"smoke:browser": "bash ../tests/browser/verify_xyzbc_trt_browser.sh",
|
||||
"evidence:web": "node ../tools/collect-web-xyzbc-trt-evidence.mjs",
|
||||
"evidence:compare": "node ../tools/compare-xyzbc-trt-evidence.mjs"
|
||||
},
|
||||
|
||||
@@ -553,6 +553,78 @@ export function createToolDbReadiness(toolDb) {
|
||||
};
|
||||
}
|
||||
|
||||
export function createToolRuntimeState(toolDb, options = {}) {
|
||||
if (!toolDb) {
|
||||
return {
|
||||
apiName: "web-rtcp-5axis-tool-runtime-state",
|
||||
ready: false,
|
||||
activeOffsetApplied: false,
|
||||
toolInSpindle: 0,
|
||||
toolFromPocket: 0,
|
||||
currentPocket: 0,
|
||||
activeToolNumber: 0,
|
||||
activePocket: 0,
|
||||
currentTool: null,
|
||||
activeToolOffset: null,
|
||||
kinematics: {
|
||||
toolOffsetZ: 0,
|
||||
source: "tool_db_not_ready",
|
||||
},
|
||||
pathTool: createPathTool(null, 0),
|
||||
vismach: {
|
||||
toolOffset: finiteNumber(options.fallbackToolLength, 0),
|
||||
source: "fallback_tool_preview_length",
|
||||
},
|
||||
interpreterParameters: createToolParameters(null),
|
||||
semanticBoundary: "linuxcnc_current_tool_offset_runtime_state_for_web_simulation",
|
||||
};
|
||||
}
|
||||
|
||||
const state = cloneToolDb(toolDb);
|
||||
const activeOffset = state.activeToolOffset
|
||||
? {
|
||||
...state.activeToolOffset,
|
||||
offset: { ...state.activeToolOffset.offset },
|
||||
}
|
||||
: null;
|
||||
const currentTool = state.toolTableCurrent
|
||||
? cloneToolEntry(state.toolTableCurrent)
|
||||
: cloneToolEntry(state.toolTable.pockets[0] || createEmptyToolEntry({ idx: 0, pocket: 0, toolNumber: 0 }));
|
||||
const activeToolNumber = activeOffset?.toolNumber
|
||||
?? (currentTool.toolNumber > 0 ? currentTool.toolNumber : 0);
|
||||
const activePocket = activeOffset?.pocket
|
||||
?? (currentTool.toolNumber > 0 ? currentTool.pocket : 0);
|
||||
const activeOffsetZ = activeOffset ? finiteNumber(activeOffset.offset?.z, 0) : 0;
|
||||
|
||||
return {
|
||||
apiName: "web-rtcp-5axis-tool-runtime-state",
|
||||
ready: state.processReady === true,
|
||||
activeOffsetApplied: Boolean(activeOffset),
|
||||
toolInSpindle: state.toolInSpindle,
|
||||
toolFromPocket: state.toolFromPocket,
|
||||
currentPocket: state.currentPocket,
|
||||
activeToolNumber,
|
||||
activePocket,
|
||||
currentTool,
|
||||
activeToolOffset: activeOffset,
|
||||
kinematics: {
|
||||
toolOffsetZ: activeOffsetZ,
|
||||
source: activeOffset ? "activeToolOffset.offset.z" : "no_active_g43_tool_length_offset",
|
||||
halPin: "motion.tooloffset.z",
|
||||
downstreamPins: ["xyzbc-trt-kins.tool-offset", "xyzbc-trt-gui.tool-offset"],
|
||||
},
|
||||
pathTool: createPathTool(activeOffset || currentTool, activeOffsetZ),
|
||||
vismach: {
|
||||
toolOffset: activeOffset
|
||||
? activeOffsetZ
|
||||
: finiteNumber(options.fallbackToolLength, 0),
|
||||
source: activeOffset ? "activeToolOffset.offset.z" : "fallback_tool_preview_length",
|
||||
},
|
||||
interpreterParameters: { ...state.interpreterParameters },
|
||||
semanticBoundary: "linuxcnc_current_tool_offset_runtime_state_for_web_simulation",
|
||||
};
|
||||
}
|
||||
|
||||
export function detectToolDbStorageCapability(globalScope = globalThis) {
|
||||
const forcedUnavailable = Boolean(globalScope.__WEB_RTCP_FORCE_OPFS_UNAVAILABLE__);
|
||||
const isBrowser = typeof globalScope.window === "object" || typeof globalScope.document === "object";
|
||||
@@ -855,6 +927,23 @@ function createToolParameters(activeOffset) {
|
||||
return parameters;
|
||||
}
|
||||
|
||||
function createPathTool(tool, length) {
|
||||
return {
|
||||
id: Number(tool?.toolNumber) || 0,
|
||||
pocket: Number(tool?.pocket) || 0,
|
||||
length: finiteNumber(length, 0),
|
||||
diameter: finiteNumber(tool?.diameter, 0),
|
||||
};
|
||||
}
|
||||
|
||||
function finiteNumber(...values) {
|
||||
for (const value of values) {
|
||||
const number = Number(value);
|
||||
if (Number.isFinite(number)) return number;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
function createToolDbEvent(code, payload = {}) {
|
||||
return {
|
||||
apiName: "web-rtcp-5axis-tool-db-event",
|
||||
|
||||
@@ -0,0 +1,111 @@
|
||||
const DEFAULT_LINEAR_UNITS = "mm";
|
||||
|
||||
export const XYZBC_TRT_VISMACH_PINS = [
|
||||
"table-x",
|
||||
"saddle-y",
|
||||
"spindle-z",
|
||||
"tilt-b",
|
||||
"rotate-c",
|
||||
"tool-offset",
|
||||
"x-offset",
|
||||
"z-offset",
|
||||
];
|
||||
|
||||
export function buildVismachModelState(state = {}) {
|
||||
const profile = state.profile || {};
|
||||
const axisPose = resolveAxisPose(state);
|
||||
const offsets = profile.offsets || {};
|
||||
const toolOffset = resolveToolOffset(state);
|
||||
const xOffset = finiteNumber(offsets.x, -20);
|
||||
const zOffset = finiteNumber(offsets.z, -15);
|
||||
const linearUnits = resolveLinearUnits(state);
|
||||
|
||||
return {
|
||||
apiName: "web-rtcp-5axis-vismach-model-state",
|
||||
profileId: profile.id || state.machineProfile || null,
|
||||
sourceGui: "src/hal/user_comps/vismach/xyzbc-trt-gui.py",
|
||||
webModel: "app/src/visualization/five-axis-scene.js",
|
||||
linearUnits,
|
||||
pins: {
|
||||
"table-x": finiteNumber(axisPose.x, 0),
|
||||
"saddle-y": finiteNumber(axisPose.y, 0),
|
||||
"spindle-z": finiteNumber(axisPose.z, 0),
|
||||
"tilt-b": finiteNumber(axisPose.b, 0),
|
||||
"rotate-c": finiteNumber(axisPose.c, 0),
|
||||
"tool-offset": toolOffset,
|
||||
"x-offset": xOffset,
|
||||
"z-offset": zOffset,
|
||||
},
|
||||
transforms: {
|
||||
table: {
|
||||
translate: { x: finiteNumber(axisPose.x, 0), y: 0, z: 0 },
|
||||
sourcePins: ["table-x"],
|
||||
},
|
||||
saddle: {
|
||||
translate: { x: 0, y: finiteNumber(axisPose.y, 0), z: 0 },
|
||||
sourcePins: ["saddle-y"],
|
||||
},
|
||||
spindle: {
|
||||
translate: { x: 0, y: 0, z: finiteNumber(axisPose.z, 0) },
|
||||
sourcePins: ["spindle-z"],
|
||||
},
|
||||
tilt: {
|
||||
rotateDeg: { x: 0, y: finiteNumber(axisPose.b, 0), z: 0 },
|
||||
sourcePins: ["tilt-b"],
|
||||
},
|
||||
rotary: {
|
||||
rotateDeg: { x: 0, y: 0, z: finiteNumber(axisPose.c, 0) },
|
||||
sourcePins: ["rotate-c"],
|
||||
},
|
||||
tool: {
|
||||
translate: { x: xOffset, y: 0, z: zOffset - toolOffset },
|
||||
sourcePins: ["tool-offset", "x-offset", "z-offset"],
|
||||
},
|
||||
},
|
||||
halNets: [
|
||||
{ signal: "table-x", source: "joint.0.pos-fb", target: "xyzbc-trt-gui.table-x" },
|
||||
{ signal: "saddle-y", source: "joint.1.pos-fb", target: "xyzbc-trt-gui.saddle-y" },
|
||||
{ signal: "spindle-z", source: "joint.2.pos-fb", target: "xyzbc-trt-gui.spindle-z" },
|
||||
{ signal: "tilt-b", source: "joint.3.pos-fb", target: "xyzbc-trt-gui.tilt-b" },
|
||||
{ signal: "rotate-c", source: "joint.4.pos-fb", target: "xyzbc-trt-gui.rotate-c" },
|
||||
{ signal: "tool-offset", source: "motion.tooloffset.z", target: "xyzbc-trt-kins.tool-offset" },
|
||||
{ signal: "tool-offset", source: "xyzbc-trt-kins.tool-offset", target: "xyzbc-trt-gui.tool-offset" },
|
||||
{ signal: "x-offset", source: "xyzbc-trt-kins.x-offset", target: "xyzbc-trt-gui.x-offset" },
|
||||
{ signal: "z-offset", source: "xyzbc-trt-kins.z-offset", target: "xyzbc-trt-gui.z-offset" },
|
||||
],
|
||||
clearTraceSignal: "pyvcp.vismach-clear => vismach.plotclear",
|
||||
semanticBoundary: "web_threejs_vismach_equivalent_driven_by_xyzbc_trt_hal_pins",
|
||||
};
|
||||
}
|
||||
|
||||
function resolveAxisPose(state) {
|
||||
if (state.programRuntimeFeedback?.axisPose) return state.programRuntimeFeedback.axisPose;
|
||||
if (state.taskHalStatus?.ui?.axisPose) return state.taskHalStatus.ui.axisPose;
|
||||
return state.axisPose || {};
|
||||
}
|
||||
|
||||
function resolveToolOffset(state) {
|
||||
if (Number.isFinite(Number(state.toolRuntimeState?.vismach?.toolOffset))) {
|
||||
return Number(state.toolRuntimeState.vismach.toolOffset);
|
||||
}
|
||||
return finiteNumber(
|
||||
state.toolDbSimulation?.activeToolOffset?.offset?.z,
|
||||
state.toolPreview?.length,
|
||||
0,
|
||||
);
|
||||
}
|
||||
|
||||
function resolveLinearUnits(state) {
|
||||
return state.linuxCncIniConfig?.traj?.linearUnits
|
||||
|| state.programRuntimeFeedback?.linearUnits
|
||||
|| state.programExecutionTiming?.linearUnits
|
||||
|| DEFAULT_LINEAR_UNITS;
|
||||
}
|
||||
|
||||
function finiteNumber(...values) {
|
||||
for (const value of values) {
|
||||
const number = Number(value);
|
||||
if (Number.isFinite(number)) return number;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@@ -5,6 +5,7 @@ import {
|
||||
linearValueToMeters,
|
||||
resolveStateLinearUnits,
|
||||
} from "../runtime/linear-units.js";
|
||||
import { buildVismachModelState } from "../runtime/vismach-model-state.js";
|
||||
|
||||
const scenes = new WeakMap();
|
||||
const CAMERA_PRESETS = {
|
||||
@@ -62,6 +63,7 @@ export function renderFiveAxisScene(canvas, state) {
|
||||
preview.executedPath.geometry,
|
||||
preview.currentSegmentPath.geometry,
|
||||
]),
|
||||
vismachModel: preview.currentVismachModelState,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -123,6 +125,7 @@ function createScene(canvas) {
|
||||
toolMarker,
|
||||
toolAxis,
|
||||
currentToolhead: new THREE.Vector3(),
|
||||
currentVismachModelState: null,
|
||||
lastSelectedView: null,
|
||||
lastCameraRevision: null,
|
||||
lastFitKey: null,
|
||||
@@ -177,6 +180,7 @@ function renderFallbackPreview(preview, state) {
|
||||
currentSegmentPointCount: currentSegmentPoints.length,
|
||||
pathFitBounds: computePointBounds(previewPoints.concat(executedPoints, currentSegmentPoints)) !== null,
|
||||
pathBounds: summarizeBounds(computePointBounds(previewPoints.concat(executedPoints, currentSegmentPoints))),
|
||||
vismachModel: buildVismachModelState(state),
|
||||
});
|
||||
canvas.dataset.threeFallbackReason = preview.errorMessage;
|
||||
|
||||
@@ -268,6 +272,9 @@ function exposePreviewDataset(canvas, state, preview) {
|
||||
canvas.dataset.threeArcPathPoints = String(preview.arcPointCount || 0);
|
||||
canvas.dataset.threeCurrentSegmentPoints = String(preview.currentSegmentPointCount || 0);
|
||||
canvas.dataset.threeCurrentSegmentType = currentSegmentType(state);
|
||||
canvas.dataset.threeVismachPins = JSON.stringify(preview.vismachModel?.pins || {});
|
||||
canvas.dataset.threeVismachTransforms = JSON.stringify(preview.vismachModel?.transforms || {});
|
||||
canvas.dataset.threeVismachPinDrivenModel = preview.vismachModel?.semanticBoundary || "none";
|
||||
}
|
||||
|
||||
function createLine(color, opacity) {
|
||||
@@ -291,6 +298,19 @@ function createMachineReferenceModel() {
|
||||
);
|
||||
base.position.z = -0.016;
|
||||
|
||||
const tableAxis = new THREE.Group();
|
||||
tableAxis.name = "vismach-table-x";
|
||||
const saddleAxis = new THREE.Group();
|
||||
saddleAxis.name = "vismach-saddle-y";
|
||||
const spindleAxis = new THREE.Group();
|
||||
spindleAxis.name = "vismach-spindle-z";
|
||||
const tiltAxis = new THREE.Group();
|
||||
tiltAxis.name = "vismach-tilt-b";
|
||||
const rotaryAxis = new THREE.Group();
|
||||
rotaryAxis.name = "vismach-rotate-c";
|
||||
const toolOffsetAxis = new THREE.Group();
|
||||
toolOffsetAxis.name = "vismach-tool-offset";
|
||||
|
||||
const table = new THREE.Mesh(
|
||||
new THREE.BoxGeometry(0.37, 0.235, 0.005),
|
||||
new THREE.MeshBasicMaterial({ color: 0x3a444d, transparent: true, opacity: 0.78 }),
|
||||
@@ -329,9 +349,21 @@ function createMachineReferenceModel() {
|
||||
cutter.position.z = 0.008;
|
||||
toolHolder.add(holderBody, cutter);
|
||||
|
||||
root.add(base, table, xAxis, yAxis, zAxis, rotaryA, rotaryC, toolHolder);
|
||||
toolOffsetAxis.add(toolHolder);
|
||||
spindleAxis.add(toolOffsetAxis);
|
||||
rotaryAxis.add(table, rotaryC);
|
||||
tiltAxis.add(rotaryA, rotaryAxis);
|
||||
saddleAxis.add(tiltAxis);
|
||||
tableAxis.add(saddleAxis);
|
||||
root.add(base, xAxis, yAxis, zAxis, tableAxis, spindleAxis);
|
||||
return {
|
||||
root,
|
||||
tableAxis,
|
||||
saddleAxis,
|
||||
spindleAxis,
|
||||
tiltAxis,
|
||||
rotaryAxis,
|
||||
toolOffsetAxis,
|
||||
rotaryA,
|
||||
rotaryC,
|
||||
toolHolder,
|
||||
@@ -410,15 +442,23 @@ function updateToolExecutionMarker(preview, state, toolPosition) {
|
||||
function updateMachineReferenceModel(preview, state, toolPosition) {
|
||||
const model = preview.machineModel;
|
||||
if (!model) return;
|
||||
const a = degreesToRadians(state.axisPose?.a);
|
||||
const b = degreesToRadians(state.axisPose?.b);
|
||||
const c = degreesToRadians(state.axisPose?.c);
|
||||
model.rotaryA.rotation.x = a;
|
||||
model.rotaryA.rotation.y = Math.PI / 2 + b;
|
||||
model.rotaryC.rotation.z = c;
|
||||
const vismach = buildVismachModelState(state);
|
||||
preview.currentVismachModelState = summarizeVismachModelStateForDataset(vismach);
|
||||
const toMeters = (value) => linearValueToMeters(value, vismach.linearUnits);
|
||||
model.tableAxis.position.x = toMeters(vismach.transforms.table.translate.x);
|
||||
model.saddleAxis.position.y = toMeters(vismach.transforms.saddle.translate.y);
|
||||
model.spindleAxis.position.z = toMeters(vismach.transforms.spindle.translate.z);
|
||||
model.tiltAxis.rotation.y = degreesToRadians(vismach.transforms.tilt.rotateDeg.y);
|
||||
model.rotaryAxis.rotation.z = degreesToRadians(vismach.transforms.rotary.rotateDeg.z);
|
||||
model.toolOffsetAxis.position.set(
|
||||
toMeters(vismach.transforms.tool.translate.x),
|
||||
0,
|
||||
toMeters(vismach.transforms.tool.translate.z),
|
||||
);
|
||||
model.rotaryA.rotation.y = Math.PI / 2;
|
||||
model.rotaryC.rotation.x = Math.PI / 2;
|
||||
|
||||
const tcpPosition = toolPosition || toPreviewVector(state.tcpPose || state.axisPose, state);
|
||||
model.toolHolder.position.copy(tcpPosition);
|
||||
const toolVector = toToolVector(state.toolAxisVector);
|
||||
model.toolHolder.lookAt(tcpPosition.clone().add(toolVector));
|
||||
}
|
||||
@@ -652,6 +692,26 @@ function summarizeBounds(bounds) {
|
||||
};
|
||||
}
|
||||
|
||||
function summarizeVismachModelStateForDataset(vismach) {
|
||||
const summarizeVector = (vector = {}) => ({
|
||||
x: round(vector.x),
|
||||
y: round(vector.y),
|
||||
z: round(vector.z),
|
||||
});
|
||||
return {
|
||||
...vismach,
|
||||
pins: Object.fromEntries(Object.entries(vismach.pins).map(([key, value]) => [key, round(value)])),
|
||||
transforms: {
|
||||
table: { ...vismach.transforms.table, translate: summarizeVector(vismach.transforms.table.translate) },
|
||||
saddle: { ...vismach.transforms.saddle, translate: summarizeVector(vismach.transforms.saddle.translate) },
|
||||
spindle: { ...vismach.transforms.spindle, translate: summarizeVector(vismach.transforms.spindle.translate) },
|
||||
tilt: { ...vismach.transforms.tilt },
|
||||
rotary: { ...vismach.transforms.rotary },
|
||||
tool: { ...vismach.transforms.tool, translate: summarizeVector(vismach.transforms.tool.translate) },
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function drawFallbackPolyline(ctx, points, cx, cy, scale) {
|
||||
for (let index = 0; index < points.length; index += 1) {
|
||||
const point = points[index];
|
||||
|
||||
Reference in New Issue
Block a user