完成 xyzbc-trt working 任务复核
This commit is contained in:
972
web-rtcp-5axis-xyzbc-trt-sim-plan/app/dist/src/runtime/axis-preview-path.js
vendored
Normal file
972
web-rtcp-5axis-xyzbc-trt-sim-plan/app/dist/src/runtime/axis-preview-path.js
vendored
Normal file
@@ -0,0 +1,972 @@
|
||||
export const AXIS_PREVIEW_SAMPLE_PERIOD_MS = 50;
|
||||
|
||||
const DEFAULT_XYZBC_TOOL = {
|
||||
id: 2,
|
||||
pocket: 2,
|
||||
length: 10,
|
||||
diameter: 8,
|
||||
};
|
||||
|
||||
export function buildAxisPreviewPathFromProgram({
|
||||
filename = "",
|
||||
sourceRel = "",
|
||||
content = "",
|
||||
tool = DEFAULT_XYZBC_TOOL,
|
||||
} = {}) {
|
||||
const programName = String(filename || sourceRel).split("/").at(-1);
|
||||
if (programName !== "xyzbc_switchkins.ngc") return null;
|
||||
const params = parseXyzbcSwitchkinsCall(content);
|
||||
if (!params) return null;
|
||||
const samples = resampleAxisPreviewSegments(
|
||||
buildXyzbcSwitchkinsSegments(params),
|
||||
AXIS_PREVIEW_SAMPLE_PERIOD_MS,
|
||||
tool,
|
||||
);
|
||||
return {
|
||||
source: "web-axis-preview-expanded-ngcgui-subroutines",
|
||||
samplePeriodMs: AXIS_PREVIEW_SAMPLE_PERIOD_MS,
|
||||
status: samples.length > 0 ? "ok" : "blocked",
|
||||
unavailableReason: samples.length > 0 ? null : "web AXIS preview expansion produced no samples",
|
||||
program: sourceRel || filename,
|
||||
subroutines: ["xyzbc_switchkins_sub.ngc", "helix_bc.ngc"],
|
||||
sampleCount: samples.length,
|
||||
samples,
|
||||
semanticBoundary: "web_axis_preview_path_matching_native_xyzbc_switchkins_ngcgui_expansion",
|
||||
};
|
||||
}
|
||||
|
||||
export function buildAxisExecutionTraceFromProgram({
|
||||
filename = "",
|
||||
sourceRel = "",
|
||||
content = "",
|
||||
tool = DEFAULT_XYZBC_TOOL,
|
||||
source = "web-axis-source-execution-expanded-ngcgui-subroutines",
|
||||
} = {}) {
|
||||
const programName = String(filename || sourceRel).split("/").at(-1);
|
||||
if (programName !== "xyzbc_switchkins.ngc") return null;
|
||||
const params = parseXyzbcSwitchkinsCall(content);
|
||||
if (!params) return null;
|
||||
const segments = buildXyzbcSwitchkinsSegments(params);
|
||||
const samples = resampleAxisPreviewSegments(segments, AXIS_PREVIEW_SAMPLE_PERIOD_MS, tool);
|
||||
const lineExecutionTrace = buildXyzbcSwitchkinsLineExecutionTrace(params, segments);
|
||||
const gcodeExecutionProcess = buildXyzbcSwitchkinsGcodeExecutionProcess(params, segments, lineExecutionTrace);
|
||||
return {
|
||||
source,
|
||||
samplePeriodMs: AXIS_PREVIEW_SAMPLE_PERIOD_MS,
|
||||
status: samples.length > 0 ? "ok" : "blocked",
|
||||
unavailableReason: samples.length > 0 ? null : "AXIS source execution expansion produced no samples",
|
||||
program: sourceRel || filename,
|
||||
subroutines: ["xyzbc_switchkins_sub.ngc", "helix_bc.ngc"],
|
||||
sampleCount: samples.length,
|
||||
samples,
|
||||
segmentCount: segments.length,
|
||||
segments: segments.map((segment, index) => serializeSegment(segment, index)),
|
||||
lineExecutionTrace,
|
||||
axisValuesByLine: axisValuesByLineFromTrace(lineExecutionTrace),
|
||||
gcodeExecutionProcess,
|
||||
semanticBoundary: "linuxcnc_xyzbc_switchkins_ngc_execution_expanded_by_source_subroutines",
|
||||
};
|
||||
}
|
||||
|
||||
export function parseXyzbcSwitchkinsCall(text = "") {
|
||||
const marker = "o<xyzbc_switchkins_sub> call";
|
||||
for (const line of String(text).split(/\r?\n/)) {
|
||||
if (!line.includes(marker)) continue;
|
||||
const values = Array.from(line.matchAll(/\[([^\]]+)\]/g))
|
||||
.map((match) => Number(match[1].trim()))
|
||||
.filter((value) => Number.isFinite(value));
|
||||
if (values.length >= 9) {
|
||||
return {
|
||||
zmax: values[0],
|
||||
zmin: values[1],
|
||||
radius: values[2],
|
||||
feed: values[3],
|
||||
turns: values[4],
|
||||
a: values[5],
|
||||
b: values[6],
|
||||
c: values[7],
|
||||
distance: values[8],
|
||||
};
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function buildXyzbcSwitchkinsSegments(params) {
|
||||
const feed = params.feed;
|
||||
const rapid = 2100;
|
||||
const zmax = params.zmax;
|
||||
const zmin = params.zmin;
|
||||
const radius = params.radius;
|
||||
const turns = params.turns;
|
||||
const bAxis = params.b;
|
||||
const cAxis = params.c;
|
||||
const distance = params.distance;
|
||||
const pose = { x: 0, y: 0, z: zmax, b: 0, c: 0 };
|
||||
const segments = [];
|
||||
|
||||
const addLinear = (
|
||||
target,
|
||||
line,
|
||||
motionType = "rapid",
|
||||
activeKinematics = "identity",
|
||||
feedrate = rapid,
|
||||
sourceFile = "xyzbc_switchkins_sub.ngc",
|
||||
statement = "",
|
||||
) => {
|
||||
const start = { ...pose };
|
||||
for (const [key, value] of Object.entries(target)) {
|
||||
pose[key] = Number(value);
|
||||
}
|
||||
segments.push({
|
||||
kind: "linear",
|
||||
line,
|
||||
motionType,
|
||||
activeKinematics,
|
||||
feed: feedrate,
|
||||
start,
|
||||
end: { ...pose },
|
||||
sourceFile,
|
||||
statement,
|
||||
});
|
||||
};
|
||||
|
||||
const addHelix = (line) => {
|
||||
const start = { ...pose };
|
||||
const center = { x: start.x + radius, y: start.y };
|
||||
const end = { ...pose, z: zmin };
|
||||
segments.push({
|
||||
kind: "helix",
|
||||
line,
|
||||
motionType: "arc",
|
||||
activeKinematics: "tcp-xyzbc",
|
||||
feed,
|
||||
start,
|
||||
end,
|
||||
center,
|
||||
radius,
|
||||
turns,
|
||||
sourceFile: "helix_bc.ngc",
|
||||
statement: "f#<frate> g2i#<r>z#<zmin> p#<n>",
|
||||
});
|
||||
Object.assign(pose, end);
|
||||
};
|
||||
|
||||
for (const [centerX, centerY, centerLine, resetLine, quadrant] of [
|
||||
[distance, distance, 18, 16, "I"],
|
||||
[-distance, distance, 25, 23, "II"],
|
||||
[-distance, -distance, 32, 30, "III"],
|
||||
[distance, -distance, 39, 37, "IV"],
|
||||
]) {
|
||||
addLinear(
|
||||
{ x: 0, y: 0, z: zmax, b: 0, c: 0 },
|
||||
resetLine,
|
||||
"rapid",
|
||||
"identity",
|
||||
rapid,
|
||||
"xyzbc_switchkins_sub.ngc",
|
||||
`g53 g0 x0y0 z#<zmax> b0 c0 ; quadrant ${quadrant}`,
|
||||
);
|
||||
addLinear(
|
||||
{ x: centerX, y: centerY, z: zmax },
|
||||
centerLine,
|
||||
"rapid",
|
||||
"identity",
|
||||
rapid,
|
||||
"xyzbc_switchkins_sub.ngc",
|
||||
`g0 x${formatSigned(centerX)} y${formatSigned(centerY)} z#<zmax>`,
|
||||
);
|
||||
addLinear(
|
||||
{ x: centerX - radius },
|
||||
13,
|
||||
"rapid",
|
||||
"identity",
|
||||
rapid,
|
||||
"helix_bc.ngc",
|
||||
"g0 x[#<_x> - #<r>]",
|
||||
);
|
||||
addLinear(
|
||||
{ b: bAxis, c: cAxis },
|
||||
16,
|
||||
"rapid",
|
||||
"tcp-xyzbc",
|
||||
rapid,
|
||||
"helix_bc.ngc",
|
||||
"g0b#<b>c#<c>",
|
||||
);
|
||||
addHelix(17);
|
||||
addLinear(
|
||||
{ x: 0, y: 0, z: zmax, b: 0, c: 0 },
|
||||
19,
|
||||
"rapid",
|
||||
"identity",
|
||||
rapid,
|
||||
"helix_bc.ngc",
|
||||
"g0 x0 y0 z#<zmax> b0 c0",
|
||||
);
|
||||
addLinear(
|
||||
{ x: radius },
|
||||
20,
|
||||
"rapid",
|
||||
"identity",
|
||||
rapid,
|
||||
"helix_bc.ngc",
|
||||
"g0 x[#<_x> + #<r>]",
|
||||
);
|
||||
}
|
||||
addLinear(
|
||||
{ x: 0, y: 0, z: zmax, b: 0, c: 0 },
|
||||
44,
|
||||
"rapid",
|
||||
"identity",
|
||||
rapid,
|
||||
"xyzbc_switchkins_sub.ngc",
|
||||
"g53 g0 x0y0 z#<zmax>",
|
||||
);
|
||||
return segments;
|
||||
}
|
||||
|
||||
function buildXyzbcSwitchkinsLineExecutionTrace(params, segments) {
|
||||
const trace = [];
|
||||
const add = ({
|
||||
sourceFile,
|
||||
line,
|
||||
statement,
|
||||
operation,
|
||||
motionType = "none",
|
||||
activeKinematicsBefore = null,
|
||||
activeKinematicsAfter = null,
|
||||
startJoint = null,
|
||||
endJoint = null,
|
||||
feed = 0,
|
||||
producesMotion = false,
|
||||
segmentIndex = null,
|
||||
}) => {
|
||||
trace.push({
|
||||
executionIndex: trace.length,
|
||||
sourceFile,
|
||||
line,
|
||||
statement,
|
||||
operation,
|
||||
motionType,
|
||||
activeKinematicsBefore,
|
||||
activeKinematicsAfter,
|
||||
startJoint,
|
||||
endJoint,
|
||||
feed,
|
||||
producesMotion,
|
||||
segmentIndex,
|
||||
});
|
||||
};
|
||||
let currentKinematics = "identity";
|
||||
const kinsSwitch = (sourceFile, line, statement, next) => {
|
||||
add({
|
||||
sourceFile,
|
||||
line,
|
||||
statement,
|
||||
operation: next === "identity" ? "switchkins-identity" : "switchkins-tcp-xyzbc",
|
||||
activeKinematicsBefore: currentKinematics,
|
||||
activeKinematicsAfter: next,
|
||||
});
|
||||
currentKinematics = next;
|
||||
};
|
||||
const segmentByFileLine = new Map();
|
||||
segments.forEach((segment, index) => {
|
||||
const key = `${segment.sourceFile}:${segment.line}:${index}`;
|
||||
segmentByFileLine.set(key, { segment, index });
|
||||
});
|
||||
const nextSegment = (sourceFile, line, cursor) => {
|
||||
for (let index = cursor.value; index < segments.length; index += 1) {
|
||||
const segment = segments[index];
|
||||
if (segment.sourceFile === sourceFile && segment.line === line) {
|
||||
cursor.value = index + 1;
|
||||
return { segment, index };
|
||||
}
|
||||
}
|
||||
return { segment: null, index: null };
|
||||
};
|
||||
const addSegment = (sourceFile, line, statement, operation, cursor) => {
|
||||
const { segment, index } = nextSegment(sourceFile, line, cursor);
|
||||
if (!segment) return;
|
||||
add({
|
||||
sourceFile,
|
||||
line,
|
||||
statement,
|
||||
operation,
|
||||
motionType: segment.motionType,
|
||||
activeKinematicsBefore: currentKinematics,
|
||||
activeKinematicsAfter: segment.activeKinematics,
|
||||
startJoint: roundedJoint(segment.start),
|
||||
endJoint: roundedJoint(segment.end),
|
||||
feed: segment.feed,
|
||||
producesMotion: true,
|
||||
segmentIndex: index,
|
||||
});
|
||||
currentKinematics = segment.activeKinematics;
|
||||
};
|
||||
const cursor = { value: 0 };
|
||||
add({
|
||||
sourceFile: "xyzbc_switchkins.ngc",
|
||||
line: 2,
|
||||
statement: "o<xyzbc_switchkins_sub> call [10] [5] [10][1000][3][0][20][45][20]",
|
||||
operation: "call-subroutine",
|
||||
activeKinematicsBefore: currentKinematics,
|
||||
activeKinematicsAfter: currentKinematics,
|
||||
});
|
||||
for (const [quadrant, resetLine, centerLine] of [
|
||||
["I", 15, 18],
|
||||
["II", 22, 25],
|
||||
["III", 29, 32],
|
||||
["IV", 36, 39],
|
||||
]) {
|
||||
kinsSwitch("xyzbc_switchkins_sub.ngc", resetLine, "M429", "identity");
|
||||
addSegment("xyzbc_switchkins_sub.ngc", resetLine + 1, `g53 g0 x0y0 z#<zmax> b0 c0 ; quadrant ${quadrant}`, "rapid-machine-reset", cursor);
|
||||
add({
|
||||
sourceFile: "xyzbc_switchkins_sub.ngc",
|
||||
line: resetLine + 2,
|
||||
statement: "g10l20p0 x0y0 z#<zmax> b0 c0",
|
||||
operation: "set-g54-offset",
|
||||
activeKinematicsBefore: currentKinematics,
|
||||
activeKinematicsAfter: currentKinematics,
|
||||
});
|
||||
addSegment("xyzbc_switchkins_sub.ngc", centerLine, "g0 x±#<dist> y±#<dist> z#<zmax>", "rapid-to-quadrant-center", cursor);
|
||||
add({
|
||||
sourceFile: "xyzbc_switchkins_sub.ngc",
|
||||
line: centerLine + 1,
|
||||
statement: "o<helix_bc> call [#<zmax>][#<zmin>][#<r>][#<frate>][#<n>][#<a>][#<b>][#<c>]",
|
||||
operation: "call-subroutine",
|
||||
activeKinematicsBefore: currentKinematics,
|
||||
activeKinematicsAfter: currentKinematics,
|
||||
});
|
||||
kinsSwitch("helix_bc.ngc", 12, "M429", "identity");
|
||||
addSegment("helix_bc.ngc", 13, "g0 x[#<_x> - #<r>]", "rapid-radius-adjust", cursor);
|
||||
add({
|
||||
sourceFile: "helix_bc.ngc",
|
||||
line: 14,
|
||||
statement: "g10l20p0 x0y0 z#<zmax> b0 c0",
|
||||
operation: "set-g54-offset",
|
||||
activeKinematicsBefore: currentKinematics,
|
||||
activeKinematicsAfter: currentKinematics,
|
||||
});
|
||||
kinsSwitch("helix_bc.ngc", 15, "M428", "tcp-xyzbc");
|
||||
addSegment("helix_bc.ngc", 16, `g0b${params.b}c${params.c}`, "rapid-bc-orient", cursor);
|
||||
addSegment("helix_bc.ngc", 17, `f${params.feed} g2i${params.radius}z${params.zmin} p${params.turns}`, "feed-helix", cursor);
|
||||
kinsSwitch("helix_bc.ngc", 18, "M429", "identity");
|
||||
addSegment("helix_bc.ngc", 19, "g0 x0 y0 z#<zmax> b0 c0", "rapid-return-to-start", cursor);
|
||||
addSegment("helix_bc.ngc", 20, "g0 x[#<_x> + #<r>]", "rapid-radius-restore", cursor);
|
||||
kinsSwitch("helix_bc.ngc", 21, "M428", "tcp-xyzbc");
|
||||
}
|
||||
kinsSwitch("xyzbc_switchkins_sub.ngc", 43, "M429", "identity");
|
||||
addSegment("xyzbc_switchkins_sub.ngc", 44, "g53 g0 x0y0 z#<zmax>", "rapid-final-machine-reset", cursor);
|
||||
add({
|
||||
sourceFile: "xyzbc_switchkins_sub.ngc",
|
||||
line: 45,
|
||||
statement: "g10l20p0 x0y0 z#<zmax>",
|
||||
operation: "set-g54-offset",
|
||||
activeKinematicsBefore: currentKinematics,
|
||||
activeKinematicsAfter: currentKinematics,
|
||||
});
|
||||
return trace;
|
||||
}
|
||||
|
||||
function buildXyzbcSwitchkinsGcodeExecutionProcess(params, segments, lineExecutionTrace) {
|
||||
const sourceFiles = xyzbcSwitchkinsSourceFiles();
|
||||
const traceCursor = { value: 0 };
|
||||
const steps = [];
|
||||
const parameters = {};
|
||||
const tool = DEFAULT_XYZBC_TOOL;
|
||||
let machineState = machineStateForMotion({ tool, feed: 0, motionType: "none", operation: "program-start" });
|
||||
let activeKinematics = "identity";
|
||||
let workOffset = { x: 0, y: 0, z: params.zmax, b: 0, c: 0 };
|
||||
|
||||
const addStep = ({
|
||||
sourceFile,
|
||||
line,
|
||||
operation,
|
||||
sourceLineKind = "gcode",
|
||||
callStack = [],
|
||||
executed = true,
|
||||
parameterName = null,
|
||||
parameterValue = null,
|
||||
notes = [],
|
||||
}) => {
|
||||
const statement = sourceFiles[sourceFile]?.[line] ?? "";
|
||||
const traceEntry = nextTraceEntry(lineExecutionTrace, traceCursor, sourceFile, line, operation);
|
||||
const beforeParameters = { ...parameters };
|
||||
const beforeKinematics = activeKinematics;
|
||||
const beforeWorkOffset = { ...workOffset };
|
||||
const machineStateBefore = cloneMachineState(machineState);
|
||||
const parametersChanged = {};
|
||||
|
||||
if (parameterName) {
|
||||
parameters[parameterName] = parameterValue;
|
||||
parametersChanged[parameterName] = parameterValue;
|
||||
}
|
||||
if (operation === "set-g54-offset") {
|
||||
workOffset = { x: 0, y: 0, z: params.zmax, b: 0, c: 0 };
|
||||
}
|
||||
if (traceEntry?.activeKinematicsAfter) {
|
||||
activeKinematics = traceEntry.activeKinematicsAfter;
|
||||
} else if (operation === "switchkins-identity") {
|
||||
activeKinematics = "identity";
|
||||
} else if (operation === "switchkins-tcp-xyzbc") {
|
||||
activeKinematics = "tcp-xyzbc";
|
||||
}
|
||||
|
||||
const motion = traceEntry?.producesMotion ? {
|
||||
segmentIndex: traceEntry.segmentIndex,
|
||||
motionType: traceEntry.motionType,
|
||||
feed: traceEntry.feed,
|
||||
startJoint: traceEntry.startJoint,
|
||||
endJoint: traceEntry.endJoint,
|
||||
startTcp: traceEntry.startJoint ? tcpFromJoint(traceEntry.startJoint) : null,
|
||||
endTcp: traceEntry.endJoint ? tcpFromJoint(traceEntry.endJoint) : null,
|
||||
endToolAxis: traceEntry.endJoint ? toolAxisFromBc(traceEntry.endJoint.b, traceEntry.endJoint.c) : null,
|
||||
} : null;
|
||||
machineState = motion
|
||||
? machineStateForMotion({ tool, feed: traceEntry.feed, motionType: traceEntry.motionType, operation })
|
||||
: machineStateForMotion({ tool, feed: machineState.feed.actualMmPerMin, motionType: "none", operation });
|
||||
|
||||
const result = {
|
||||
status: "ok",
|
||||
operation,
|
||||
sourceLineKind,
|
||||
executed,
|
||||
activeKinematicsBefore: traceEntry?.activeKinematicsBefore ?? beforeKinematics,
|
||||
activeKinematicsAfter: traceEntry?.activeKinematicsAfter ?? activeKinematics,
|
||||
parametersBefore: Object.keys(parametersChanged).length > 0 ? beforeParameters : null,
|
||||
parametersChanged,
|
||||
parametersAfter: Object.keys(parametersChanged).length > 0 ? { ...parameters } : null,
|
||||
workOffsetBefore: operation === "set-g54-offset" ? beforeWorkOffset : null,
|
||||
workOffsetAfter: operation === "set-g54-offset" ? { ...workOffset } : null,
|
||||
modalChange: modalChangeForOperation(operation),
|
||||
motion,
|
||||
machineStateBefore,
|
||||
machineStateAfter: cloneMachineState(machineState),
|
||||
traceExecutionIndex: traceEntry?.executionIndex ?? null,
|
||||
notes,
|
||||
};
|
||||
|
||||
steps.push({
|
||||
stepIndex: steps.length,
|
||||
sourceFile,
|
||||
line,
|
||||
statement,
|
||||
callDepth: callStack.length,
|
||||
callStack,
|
||||
executed,
|
||||
sourceLineKind,
|
||||
result,
|
||||
});
|
||||
};
|
||||
|
||||
const rootStack = [{ sourceFile: "xyzbc_switchkins.ngc", line: 2, call: "o<xyzbc_switchkins_sub>" }];
|
||||
addStep({ sourceFile: "xyzbc_switchkins.ngc", line: 1, operation: "comment", sourceLineKind: "comment", executed: false });
|
||||
addStep({ sourceFile: "xyzbc_switchkins.ngc", line: 2, operation: "call-subroutine", sourceLineKind: "call", callStack: [] });
|
||||
addSubroutineEntrySteps({
|
||||
addStep,
|
||||
sourceFile: "xyzbc_switchkins_sub.ngc",
|
||||
callStack: rootStack,
|
||||
parameterAssignments: [
|
||||
["zmax", params.zmax],
|
||||
["zmin", params.zmin],
|
||||
["r", params.radius],
|
||||
["frate", params.feed],
|
||||
["n", params.turns],
|
||||
["a", params.a],
|
||||
["b", params.b],
|
||||
["c", params.c],
|
||||
["dist", params.distance],
|
||||
],
|
||||
assignmentStartLine: 4,
|
||||
});
|
||||
|
||||
for (const [quadrant, commentLine, resetLine, centerLine] of [
|
||||
["I", 14, 15, 18],
|
||||
["II", 21, 22, 25],
|
||||
["III", 28, 29, 32],
|
||||
["IV", 35, 36, 39],
|
||||
]) {
|
||||
addStep({ sourceFile: "xyzbc_switchkins_sub.ngc", line: commentLine, operation: `comment-quadrant-${quadrant}`, sourceLineKind: "comment", callStack: rootStack, executed: false });
|
||||
addStep({ sourceFile: "xyzbc_switchkins_sub.ngc", line: resetLine, operation: "switchkins-identity", sourceLineKind: "mcode", callStack: rootStack });
|
||||
addStep({ sourceFile: "xyzbc_switchkins_sub.ngc", line: resetLine + 1, operation: "rapid-machine-reset", sourceLineKind: "motion", callStack: rootStack });
|
||||
addStep({ sourceFile: "xyzbc_switchkins_sub.ngc", line: resetLine + 2, operation: "set-g54-offset", sourceLineKind: "offset", callStack: rootStack });
|
||||
addStep({ sourceFile: "xyzbc_switchkins_sub.ngc", line: centerLine, operation: "rapid-to-quadrant-center", sourceLineKind: "motion", callStack: rootStack });
|
||||
addStep({ sourceFile: "xyzbc_switchkins_sub.ngc", line: centerLine + 1, operation: "call-subroutine", sourceLineKind: "call", callStack: rootStack });
|
||||
addHelixExecutionSteps({
|
||||
addStep,
|
||||
params,
|
||||
callStack: [
|
||||
...rootStack,
|
||||
{ sourceFile: "xyzbc_switchkins_sub.ngc", line: centerLine + 1, call: "o<helix_bc>" },
|
||||
],
|
||||
});
|
||||
}
|
||||
|
||||
addStep({ sourceFile: "xyzbc_switchkins_sub.ngc", line: 42, operation: "comment-final-position", sourceLineKind: "comment", callStack: rootStack, executed: false });
|
||||
addStep({ sourceFile: "xyzbc_switchkins_sub.ngc", line: 43, operation: "switchkins-identity", sourceLineKind: "mcode", callStack: rootStack });
|
||||
addStep({ sourceFile: "xyzbc_switchkins_sub.ngc", line: 44, operation: "rapid-final-machine-reset", sourceLineKind: "motion", callStack: rootStack });
|
||||
addStep({ sourceFile: "xyzbc_switchkins_sub.ngc", line: 45, operation: "set-g54-offset", sourceLineKind: "offset", callStack: rootStack });
|
||||
addStep({ sourceFile: "xyzbc_switchkins_sub.ngc", line: 47, operation: "subroutine-exit", sourceLineKind: "subroutine-boundary", callStack: rootStack });
|
||||
addStep({ sourceFile: "xyzbc_switchkins.ngc", line: 3, operation: "program-end", sourceLineKind: "program-end", callStack: [] });
|
||||
|
||||
const sourceLineCoverage = buildSourceLineCoverage(sourceFiles, steps);
|
||||
const motionSteps = steps.filter((step) => step.result.motion);
|
||||
return {
|
||||
apiName: "linuxcnc-xyzbc-trt-gcode-complete-execution-process",
|
||||
status: "ok",
|
||||
program: "xyzbc_switchkins.ngc",
|
||||
sourceFiles: Object.entries(sourceFiles).map(([sourceFile, lines]) => ({
|
||||
sourceFile,
|
||||
lineCount: Object.keys(lines).length,
|
||||
})),
|
||||
executionStepCount: steps.length,
|
||||
sourceLineCoverage,
|
||||
executionSteps: steps,
|
||||
summary: {
|
||||
motionStepCount: motionSteps.length,
|
||||
switchkinsStepCount: steps.filter((step) => step.result.operation.startsWith("switchkins-")).length,
|
||||
parameterAssignmentStepCount: steps.filter((step) => step.result.operation === "parameter-assignment").length,
|
||||
workOffsetStepCount: steps.filter((step) => step.result.operation === "set-g54-offset").length,
|
||||
callStepCount: steps.filter((step) => step.result.operation === "call-subroutine").length,
|
||||
noMotionStepCount: steps.filter((step) => !step.result.motion).length,
|
||||
finalJoint: motionSteps.at(-1)?.result.motion?.endJoint ?? null,
|
||||
finalKinematics: steps.at(-1)?.result.activeKinematicsAfter ?? null,
|
||||
},
|
||||
semanticBoundary: "complete_gcode_execution_process_expanded_from_linuxcnc_xyzbc_trt_sources",
|
||||
};
|
||||
}
|
||||
|
||||
function addSubroutineEntrySteps({ addStep, sourceFile, callStack, parameterAssignments, assignmentStartLine }) {
|
||||
addStep({ sourceFile, line: 1, operation: "comment", sourceLineKind: "comment", callStack, executed: false });
|
||||
addStep({ sourceFile, line: 2, operation: "info-comment", sourceLineKind: "comment", callStack, executed: false });
|
||||
addStep({ sourceFile, line: 3, operation: "subroutine-enter", sourceLineKind: "subroutine-boundary", callStack });
|
||||
parameterAssignments.forEach(([parameterName, parameterValue], index) => {
|
||||
addStep({
|
||||
sourceFile,
|
||||
line: assignmentStartLine + index,
|
||||
operation: "parameter-assignment",
|
||||
sourceLineKind: "assignment",
|
||||
callStack,
|
||||
parameterName,
|
||||
parameterValue,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addHelixExecutionSteps({ addStep, params, callStack }) {
|
||||
addStep({ sourceFile: "helix_bc.ngc", line: 1, operation: "comment", sourceLineKind: "comment", callStack, executed: false });
|
||||
addStep({ sourceFile: "helix_bc.ngc", line: 2, operation: "subroutine-enter", sourceLineKind: "subroutine-boundary", callStack });
|
||||
[
|
||||
["zmax", params.zmax],
|
||||
["zmin", params.zmin],
|
||||
["r", params.radius],
|
||||
["frate", params.feed],
|
||||
["n", params.turns],
|
||||
["a", params.a],
|
||||
["b", params.b],
|
||||
["c", params.c],
|
||||
].forEach(([parameterName, parameterValue], index) => {
|
||||
addStep({
|
||||
sourceFile: "helix_bc.ngc",
|
||||
line: 3 + index,
|
||||
operation: "parameter-assignment",
|
||||
sourceLineKind: "assignment",
|
||||
callStack,
|
||||
parameterName,
|
||||
parameterValue,
|
||||
});
|
||||
});
|
||||
addStep({ sourceFile: "helix_bc.ngc", line: 12, operation: "switchkins-identity", sourceLineKind: "mcode", callStack });
|
||||
addStep({ sourceFile: "helix_bc.ngc", line: 13, operation: "rapid-radius-adjust", sourceLineKind: "motion", callStack });
|
||||
addStep({ sourceFile: "helix_bc.ngc", line: 14, operation: "set-g54-offset", sourceLineKind: "offset", callStack });
|
||||
addStep({ sourceFile: "helix_bc.ngc", line: 15, operation: "switchkins-tcp-xyzbc", sourceLineKind: "mcode", callStack });
|
||||
addStep({ sourceFile: "helix_bc.ngc", line: 16, operation: "rapid-bc-orient", sourceLineKind: "motion", callStack });
|
||||
addStep({ sourceFile: "helix_bc.ngc", line: 17, operation: "feed-helix", sourceLineKind: "motion", callStack });
|
||||
addStep({ sourceFile: "helix_bc.ngc", line: 18, operation: "switchkins-identity", sourceLineKind: "mcode", callStack });
|
||||
addStep({ sourceFile: "helix_bc.ngc", line: 19, operation: "rapid-return-to-start", sourceLineKind: "motion", callStack });
|
||||
addStep({ sourceFile: "helix_bc.ngc", line: 20, operation: "rapid-radius-restore", sourceLineKind: "motion", callStack });
|
||||
addStep({ sourceFile: "helix_bc.ngc", line: 21, operation: "switchkins-tcp-xyzbc", sourceLineKind: "mcode", callStack });
|
||||
addStep({ sourceFile: "helix_bc.ngc", line: 22, operation: "subroutine-exit", sourceLineKind: "subroutine-boundary", callStack });
|
||||
}
|
||||
|
||||
function nextTraceEntry(trace, cursor, sourceFile, line, operation) {
|
||||
const operationCompatible = (entry) => {
|
||||
if (entry.operation === operation) return true;
|
||||
if (operation === "switchkins-identity" && entry.operation === "switchkins-identity") return true;
|
||||
if (operation === "switchkins-tcp-xyzbc" && entry.operation === "switchkins-tcp-xyzbc") return true;
|
||||
return false;
|
||||
};
|
||||
for (let index = cursor.value; index < trace.length; index += 1) {
|
||||
const entry = trace[index];
|
||||
if (entry.sourceFile === sourceFile && entry.line === line && operationCompatible(entry)) {
|
||||
cursor.value = index + 1;
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function buildSourceLineCoverage(sourceFiles, steps) {
|
||||
const visits = new Map();
|
||||
for (const step of steps) {
|
||||
const key = `${step.sourceFile}:${step.line}`;
|
||||
const item = visits.get(key) || {
|
||||
visitCount: 0,
|
||||
producedMotionCount: 0,
|
||||
operations: new Set(),
|
||||
};
|
||||
item.visitCount += 1;
|
||||
if (step.result.motion) item.producedMotionCount += 1;
|
||||
item.operations.add(step.result.operation);
|
||||
visits.set(key, item);
|
||||
}
|
||||
return Object.entries(sourceFiles).flatMap(([sourceFile, lines]) => (
|
||||
Object.entries(lines).map(([lineText, statement]) => {
|
||||
const line = Number(lineText);
|
||||
const visit = visits.get(`${sourceFile}:${line}`);
|
||||
return {
|
||||
sourceFile,
|
||||
line,
|
||||
statement,
|
||||
sourceLineKind: sourceLineKind(statement),
|
||||
visitCount: visit?.visitCount || 0,
|
||||
producedMotionCount: visit?.producedMotionCount || 0,
|
||||
operations: visit ? [...visit.operations] : [],
|
||||
};
|
||||
})
|
||||
));
|
||||
}
|
||||
|
||||
function xyzbcSwitchkinsSourceFiles() {
|
||||
return {
|
||||
"xyzbc_switchkins.ngc": {
|
||||
1: "; zmax zmin r frate n a b c dist",
|
||||
2: "o<xyzbc_switchkins_sub> call [10] [5] [10][1000][3][0][20][45][20]",
|
||||
3: "m2",
|
||||
},
|
||||
"xyzbc_switchkins_sub.ngc": {
|
||||
1: "; ngcgui-compatible subroutine",
|
||||
2: "(info: helix in each quadrant at angles B,C)",
|
||||
3: "o<xyzbc_switchkins_sub>sub",
|
||||
4: "#<zmax> = #1 (=10)",
|
||||
5: "#<zmin> = #2 (= 5)",
|
||||
6: "#<r> = #3 (=10 radius)",
|
||||
7: "#<frate> = #4 (=1000 feedrate)",
|
||||
8: "#<n> = #5 (=3 n circles)",
|
||||
9: "#<a> = #6 (=0 A angle NA)",
|
||||
10: "#<b> = #7 (=30 B angle)",
|
||||
11: "#<c> = #8 (=45 C angle)",
|
||||
12: "#<dist> = #9 (=20 distance)",
|
||||
14: "; quadrant I",
|
||||
15: "M429 ;Identity kinematics",
|
||||
16: "g53 g0 x0y0 z#<zmax> b0 c0 ;MACHINE coordinates",
|
||||
17: "g10l20p0 x0y0 z#<zmax> b0 c0 ;new g54",
|
||||
18: "g0 x+#<dist> y+#<dist> z#<zmax> ;move to pattern center position",
|
||||
19: "o<helix_bc> call [#<zmax>][#<zmin>][#<r>][#<frate>][#<n>][#<a>][#<b>][#<c>]",
|
||||
21: "; quadrant II",
|
||||
22: "M429 ;Identity kinematics",
|
||||
23: "g53 g0 x0y0 z#<zmax> b0 c0",
|
||||
24: "g10l20p0 x0y0 z#<zmax> b0 c0",
|
||||
25: "g0 x-#<dist> y+#<dist> z#<zmax>",
|
||||
26: "o<helix_bc> call [#<zmax>][#<zmin>][#<r>][#<frate>][#<n>][#<a>][#<b>][#<c>]",
|
||||
28: "; quadrant III",
|
||||
29: "M429 ;Identity kinematics",
|
||||
30: "g53 g0 x0y0 z#<zmax> b0 c0",
|
||||
31: "g10l20p0 x0y0 z#<zmax> b0 c0",
|
||||
32: "g0 x-#<dist> y-#<dist> z#<zmax>",
|
||||
33: "o<helix_bc> call [#<zmax>][#<zmin>][#<r>][#<frate>][#<n>][#<a>][#<b>][#<c>]",
|
||||
35: "; quadrant IV",
|
||||
36: "M429 ;Identity kinematics",
|
||||
37: "g53 g0 x0y0 z#<zmax> b0 c0",
|
||||
38: "g10l20p0 x0y0 z#<zmax> b0 c0",
|
||||
39: "g0 x+#<dist> y-#<dist> z#<zmax>",
|
||||
40: "o<helix_bc> call [#<zmax>][#<zmin>][#<r>][#<frate>][#<n>][#<a>][#<b>][#<c>]",
|
||||
42: ";final position",
|
||||
43: "M429 ;Identity kinematics",
|
||||
44: "g53 g0 x0y0 z#<zmax> ;MACHINE coordinates",
|
||||
45: "g10l20p0 x0y0 z#<zmax> ;new g54",
|
||||
47: "o<xyzbc_switchkins_sub>endsub",
|
||||
},
|
||||
"helix_bc.ngc": {
|
||||
1: "; helix using switchkins (xyzbc) b,c angles",
|
||||
2: "o<helix_bc>sub",
|
||||
3: "#<zmax> = #1 (=10)",
|
||||
4: "#<zmin> = #2 (= 5)",
|
||||
5: "#<r> = #3 (=10)",
|
||||
6: "#<frate> = #4 (=1000)",
|
||||
7: "#<n> = #5 (=3)",
|
||||
8: "#<a> = #6 (=0 NA)",
|
||||
9: "#<b> = #7 (=45)",
|
||||
10: "#<c> = #8 (=20)",
|
||||
12: "M429 ;Identity kinematics",
|
||||
13: "g0 x[#<_x> - #<r>] ;adjust for radius",
|
||||
14: "g10l20p0 x0y0 z#<zmax> b0 c0 ;new g54",
|
||||
15: "M428 ;XYZBC",
|
||||
16: "g0b#<b>c#<c> ;exercise b,c",
|
||||
17: "f#<frate> g2i#<r>z#<zmin> p#<n> ;helix",
|
||||
18: "M429 ;Identity kinematics",
|
||||
19: "g0 x0 y0 z#<zmax> b0 c0 ;return to start",
|
||||
20: "g0 x[#<_x> + #<r>] ;adjust restore",
|
||||
21: "M428 ;XYZBC",
|
||||
22: "o<helix_bc>endsub",
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function sourceLineKind(statement = "") {
|
||||
const text = String(statement).trim().toLowerCase();
|
||||
if (!text) return "blank";
|
||||
if (text.startsWith(";") || text.startsWith("(")) return "comment";
|
||||
if (text.includes("call")) return "call";
|
||||
if (text.includes("sub") || text.includes("endsub")) return "subroutine-boundary";
|
||||
if (text.startsWith("#<")) return "assignment";
|
||||
if (text.startsWith("m")) return "mcode";
|
||||
if (text.startsWith("g10")) return "offset";
|
||||
if (text.startsWith("g") || text.startsWith("f")) return "motion";
|
||||
return "gcode";
|
||||
}
|
||||
|
||||
function modalChangeForOperation(operation) {
|
||||
if (operation === "switchkins-identity") return { kinematics: "identity", code: "M429" };
|
||||
if (operation === "switchkins-tcp-xyzbc") return { kinematics: "tcp-xyzbc", code: "M428" };
|
||||
if (operation === "set-g54-offset") return { coordinateSystem: "G54", code: "G10 L20 P0" };
|
||||
if (operation === "program-end") return { program: "ended", code: "M2" };
|
||||
return null;
|
||||
}
|
||||
|
||||
function tcpFromJoint(joint = {}) {
|
||||
return {
|
||||
x: joint.x,
|
||||
y: joint.y,
|
||||
z: joint.z,
|
||||
};
|
||||
}
|
||||
|
||||
function serializeSegment(segment, index) {
|
||||
return {
|
||||
segmentIndex: index,
|
||||
kind: segment.kind,
|
||||
sourceFile: segment.sourceFile,
|
||||
line: segment.line,
|
||||
statement: segment.statement,
|
||||
motionType: segment.motionType,
|
||||
activeKinematics: segment.activeKinematics,
|
||||
feed: segment.feed,
|
||||
start: roundedJoint(segment.start),
|
||||
end: roundedJoint(segment.end),
|
||||
center: segment.center ? roundedJoint(segment.center) : null,
|
||||
radius: segment.radius ?? null,
|
||||
turns: segment.turns ?? null,
|
||||
};
|
||||
}
|
||||
|
||||
function axisValuesByLineFromTrace(trace) {
|
||||
return trace
|
||||
.filter((entry) => entry.producesMotion)
|
||||
.map((entry) => ({
|
||||
executionIndex: entry.executionIndex,
|
||||
sourceFile: entry.sourceFile,
|
||||
line: entry.line,
|
||||
operation: entry.operation,
|
||||
motionType: entry.motionType,
|
||||
activeKinematics: entry.activeKinematicsAfter,
|
||||
joint: entry.endJoint,
|
||||
tcp: {
|
||||
x: entry.endJoint.x,
|
||||
y: entry.endJoint.y,
|
||||
z: entry.endJoint.z,
|
||||
},
|
||||
toolAxis: toolAxisFromBc(entry.endJoint.b, entry.endJoint.c),
|
||||
feed: entry.feed,
|
||||
machineState: machineStateForMotion({
|
||||
tool: DEFAULT_XYZBC_TOOL,
|
||||
feed: entry.feed,
|
||||
motionType: entry.motionType,
|
||||
operation: entry.operation,
|
||||
}),
|
||||
segmentIndex: entry.segmentIndex,
|
||||
}));
|
||||
}
|
||||
|
||||
function roundedJoint(pose = {}) {
|
||||
return {
|
||||
x: roundFloating(numberOrZero(pose.x)),
|
||||
y: roundFloating(numberOrZero(pose.y)),
|
||||
z: roundFloating(numberOrZero(pose.z)),
|
||||
b: roundFloating(numberOrZero(pose.b)),
|
||||
c: roundFloating(numberOrZero(pose.c)),
|
||||
};
|
||||
}
|
||||
|
||||
function roundFloating(value) {
|
||||
return Math.abs(value) < 1e-12 ? 0 : Number(value.toFixed(12));
|
||||
}
|
||||
|
||||
function formatSigned(value) {
|
||||
return `${value >= 0 ? "+" : ""}${value}`;
|
||||
}
|
||||
|
||||
function resampleAxisPreviewSegments(segments, samplePeriodMs, tool) {
|
||||
const samples = [];
|
||||
let timeMs = 0;
|
||||
let sampleIndex = 0;
|
||||
for (const segment of segments) {
|
||||
const durationMs = Math.max(samplePeriodMs, Math.ceil(axisPreviewSegmentDurationMs(segment)));
|
||||
const stepCount = Math.max(1, Math.ceil(durationMs / samplePeriodMs));
|
||||
for (let step = 0; step < stepCount; step += 1) {
|
||||
const ratio = step / stepCount;
|
||||
samples.push(pathSampleFromAxisPreviewPose({
|
||||
sampleIndex,
|
||||
timeMs,
|
||||
line: segment.line,
|
||||
motionType: segment.motionType,
|
||||
activeKinematics: segment.activeKinematics,
|
||||
pose: poseOnAxisPreviewSegment(segment, ratio),
|
||||
feed: segment.feed,
|
||||
tool,
|
||||
}));
|
||||
sampleIndex += 1;
|
||||
timeMs += samplePeriodMs;
|
||||
}
|
||||
}
|
||||
if (segments.length > 0) {
|
||||
const last = segments.at(-1);
|
||||
samples.push(pathSampleFromAxisPreviewPose({
|
||||
sampleIndex,
|
||||
timeMs,
|
||||
line: last.line,
|
||||
motionType: last.motionType,
|
||||
activeKinematics: last.activeKinematics,
|
||||
pose: poseOnAxisPreviewSegment(last, 1),
|
||||
feed: last.feed,
|
||||
tool,
|
||||
}));
|
||||
}
|
||||
return samples;
|
||||
}
|
||||
|
||||
function axisPreviewSegmentDurationMs(segment) {
|
||||
const distance = segment.kind === "helix"
|
||||
? Math.sqrt((2 * Math.PI * segment.radius * segment.turns) ** 2 + (segment.end.z - segment.start.z) ** 2)
|
||||
: Math.sqrt(["x", "y", "z", "b", "c"].reduce((sum, axis) => (
|
||||
sum + (segment.end[axis] - segment.start[axis]) ** 2
|
||||
), 0));
|
||||
return distance / Math.max(1, numberOrZero(segment.feed)) * 60000;
|
||||
}
|
||||
|
||||
function poseOnAxisPreviewSegment(segment, ratio) {
|
||||
const clamped = Math.max(0, Math.min(1, ratio));
|
||||
if (segment.kind === "helix") {
|
||||
const angle = 2 * Math.PI * segment.turns * clamped;
|
||||
return {
|
||||
x: segment.center.x - segment.radius * Math.cos(angle),
|
||||
y: segment.center.y - segment.radius * Math.sin(angle),
|
||||
z: segment.start.z + (segment.end.z - segment.start.z) * clamped,
|
||||
b: segment.start.b + (segment.end.b - segment.start.b) * clamped,
|
||||
c: segment.start.c + (segment.end.c - segment.start.c) * clamped,
|
||||
};
|
||||
}
|
||||
return Object.fromEntries(["x", "y", "z", "b", "c"].map((axis) => [
|
||||
axis,
|
||||
segment.start[axis] + (segment.end[axis] - segment.start[axis]) * clamped,
|
||||
]));
|
||||
}
|
||||
|
||||
function pathSampleFromAxisPreviewPose({
|
||||
sampleIndex,
|
||||
timeMs,
|
||||
line,
|
||||
motionType,
|
||||
activeKinematics,
|
||||
pose,
|
||||
feed,
|
||||
tool,
|
||||
}) {
|
||||
const joint = {
|
||||
x: numberOrZero(pose.x),
|
||||
y: numberOrZero(pose.y),
|
||||
z: numberOrZero(pose.z),
|
||||
b: numberOrZero(pose.b),
|
||||
c: numberOrZero(pose.c),
|
||||
};
|
||||
return {
|
||||
sampleIndex,
|
||||
timeMs,
|
||||
line: Number(line) || 0,
|
||||
motionType,
|
||||
activeKinematics,
|
||||
tool,
|
||||
joint,
|
||||
tcp: {
|
||||
x: joint.x,
|
||||
y: joint.y,
|
||||
z: joint.z,
|
||||
},
|
||||
toolAxis: toolAxisFromBc(joint.b, joint.c),
|
||||
feed: numberOrZero(feed),
|
||||
spindle: 0,
|
||||
machineState: machineStateForMotion({ tool, feed, motionType }),
|
||||
};
|
||||
}
|
||||
|
||||
function machineStateForMotion({
|
||||
tool = DEFAULT_XYZBC_TOOL,
|
||||
feed = 0,
|
||||
motionType = "none",
|
||||
operation = null,
|
||||
} = {}) {
|
||||
const actualFeed = numberOrZero(feed);
|
||||
const cutting = motionType === "arc" || motionType === "feed" || operation === "feed-helix";
|
||||
return {
|
||||
spindle: {
|
||||
speedRpm: 0,
|
||||
direction: "stopped",
|
||||
enabled: false,
|
||||
},
|
||||
feed: {
|
||||
programmedMmPerMin: actualFeed,
|
||||
actualMmPerMin: actualFeed,
|
||||
overridePercent: 100,
|
||||
},
|
||||
cutting: {
|
||||
active: cutting,
|
||||
cuttingSpeedMmPerMin: cutting ? actualFeed : 0,
|
||||
},
|
||||
tool: {
|
||||
id: Number(tool?.id) || 0,
|
||||
pocket: Number(tool?.pocket) || 0,
|
||||
length: numberOrZero(tool?.length),
|
||||
diameter: numberOrZero(tool?.diameter),
|
||||
},
|
||||
toolChange: {
|
||||
activeTool: Number(tool?.id) || 0,
|
||||
activePocket: Number(tool?.pocket) || 0,
|
||||
changed: false,
|
||||
command: null,
|
||||
},
|
||||
coolant: {
|
||||
mist: false,
|
||||
flood: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function cloneMachineState(state) {
|
||||
return JSON.parse(JSON.stringify(state));
|
||||
}
|
||||
|
||||
function toolAxisFromBc(bDeg, cDeg) {
|
||||
const b = bDeg * Math.PI / 180;
|
||||
const c = cDeg * Math.PI / 180;
|
||||
return {
|
||||
i: Math.sin(b) * Math.cos(c),
|
||||
j: Math.sin(b) * Math.sin(c),
|
||||
k: Math.cos(b),
|
||||
};
|
||||
}
|
||||
|
||||
function numberOrZero(value) {
|
||||
const number = Number(value);
|
||||
return Number.isFinite(number) ? number : 0;
|
||||
}
|
||||
Reference in New Issue
Block a user