继续完成 web-rtcp-5axis-sim-plan
结论:完成 LinuxCNC kinematics WASM ABI 覆盖,并将 web-rtcp-5axis-sim-plan 的 RTCP frame/boundary adapter 接到 xyzac-trt kinematics SDK;Node、build、browser smoke 验证通过。
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
import { xyzacTrtProfile } from "../profiles/xyzac-trt.js";
|
||||
import { createPyvcpHalBindingSummary, xyzacTrtPyvcpPanelSchema } from "../panel-schema/xyzac-trt-pyvcp.js";
|
||||
import { createProfileSourceReferenceSummary } from "../profiles/source-reference-map.js";
|
||||
|
||||
export function createLinuxCncBoundaryAdapter({
|
||||
profile = xyzacTrtProfile,
|
||||
panelSchema = xyzacTrtPyvcpPanelSchema,
|
||||
runtime = null,
|
||||
} = {}) {
|
||||
const sourceSummary = createProfileSourceReferenceSummary(profile.id);
|
||||
const panelSummary = createPyvcpHalBindingSummary(panelSchema);
|
||||
const kinematicsRuntimeReady = Boolean(runtime?.kinematicsWasm?.loaded);
|
||||
const interpreterRuntimeReady = Boolean(runtime?.interpreterWasm?.loaded);
|
||||
const runtimeReady = kinematicsRuntimeReady && interpreterRuntimeReady;
|
||||
const linuxCncKinematicsReady = kinematicsRuntimeReady
|
||||
&& runtime.kinematicsWasm.sourceMode === "source-derived-kinematics-wasm";
|
||||
|
||||
return {
|
||||
apiName: "web-rtcp-5axis-linuxcnc-boundary-adapter",
|
||||
profileId: profile.id,
|
||||
profile,
|
||||
sourceSummary,
|
||||
panelSummary,
|
||||
runtimeReady,
|
||||
kinematicsRuntimeReady,
|
||||
interpreterRuntimeReady,
|
||||
profileSummary: createProfileSummary(profile),
|
||||
linuxCncKinematicsReady,
|
||||
promotionAllowed: linuxCncKinematicsReady,
|
||||
fullLinuxCncProgramExecutionReady: false,
|
||||
semanticBoundary: linuxCncKinematicsReady
|
||||
? interpreterRuntimeReady
|
||||
? "linuxcnc_runtime_supplied_but_interpreter_or_remap_not_promoted"
|
||||
: "linuxcnc_kinematics_wasm_runtime_connected"
|
||||
: "adapter_entrypoint_only_runtime_not_connected",
|
||||
adapterPoints: {
|
||||
kinematicsWasm: runtime?.kinematicsWasm || null,
|
||||
interpreterWasm: runtime?.interpreterWasm || null,
|
||||
halPins: profile.halPins,
|
||||
pyvcpSchemaId: panelSchema.id,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function createProfileSummary(profile) {
|
||||
return {
|
||||
machineName: profile.machineName,
|
||||
coordinates: profile.traj.coordinates,
|
||||
jointCount: profile.jointConfig.length,
|
||||
axisCount: Object.keys(profile.axisLimits).length,
|
||||
mdiCommandCount: profile.halui.mdiCommands.length,
|
||||
remapCount: profile.remaps.length,
|
||||
toolCount: profile.toolTable.toolCount,
|
||||
offsetNetCount: profile.hal.halcmd.offsetNets.length,
|
||||
feedbackNetCount: profile.hal.halcmd.feedbackNets.length,
|
||||
halFileCount: profile.hal.halFiles.length + profile.hal.postguiHalFiles.length,
|
||||
sourceKinds: profile.sourceReferenceObjects.map((reference) => reference.kind),
|
||||
};
|
||||
}
|
||||
|
||||
export function createLinuxCncBoundaryReadiness(adapter = createLinuxCncBoundaryAdapter()) {
|
||||
const missing = [];
|
||||
if (!adapter.kinematicsRuntimeReady) missing.push("kinematics runtime");
|
||||
if (!adapter.interpreterRuntimeReady) missing.push("interpreter/remap runtime");
|
||||
if (!adapter.sourceSummary.referenceCount) missing.push("source reference map");
|
||||
if (!adapter.panelSummary.controlCount) missing.push("PyVCP/HAL panel schema");
|
||||
|
||||
return {
|
||||
apiName: "web-rtcp-5axis-linuxcnc-boundary-readiness",
|
||||
profileId: adapter.profileId,
|
||||
ready: adapter.linuxCncKinematicsReady
|
||||
&& !missing.includes("kinematics runtime")
|
||||
&& !missing.includes("source reference map")
|
||||
&& !missing.includes("PyVCP/HAL panel schema"),
|
||||
missing,
|
||||
linuxCncKinematicsReady: adapter.linuxCncKinematicsReady,
|
||||
promotionAllowed: adapter.promotionAllowed,
|
||||
fullLinuxCncProgramExecutionReady: adapter.fullLinuxCncProgramExecutionReady,
|
||||
semanticBoundary: adapter.semanticBoundary,
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
import { readFileSync } from "node:fs";
|
||||
import { dirname, resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
|
||||
import {
|
||||
createLinuxCncKinematicsSdk,
|
||||
linuxCncKinematicsWasmFile,
|
||||
supportedLinuxCncKinematicsModules,
|
||||
} from "../../../../wasm-port/runtime/sdk/src/index.js";
|
||||
|
||||
const DEFAULT_MODULE_ID = "xyzac-trt";
|
||||
const DEFAULT_JOINT_COUNT = 5;
|
||||
const SOURCE_MODE = "source-derived-kinematics-wasm";
|
||||
const SEMANTIC_BOUNDARY = "linuxcnc_kinematics_wasm_c_abi";
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
const defaultWasmRoot = resolve(__dirname, "../../../../wasm-port/build/wasm/kinematics");
|
||||
|
||||
export async function createLinuxCncKinematicsRuntime({
|
||||
moduleId = DEFAULT_MODULE_ID,
|
||||
moduleOptions = null,
|
||||
switchkinsType = 0,
|
||||
jointCount = DEFAULT_JOINT_COUNT,
|
||||
wasmRoot = defaultWasmRoot,
|
||||
} = {}) {
|
||||
const wasmFile = linuxCncKinematicsWasmFile(moduleId);
|
||||
if (!wasmFile) {
|
||||
throw new Error(`unsupported LinuxCNC kinematics module: ${moduleId}`);
|
||||
}
|
||||
|
||||
const resolvedModuleOptions = moduleOptions || {
|
||||
wasmBinary: readFileSync(resolve(wasmRoot, wasmFile)),
|
||||
print() {},
|
||||
printErr() {},
|
||||
};
|
||||
const sdk = await createLinuxCncKinematicsSdk({ moduleId, moduleOptions: resolvedModuleOptions });
|
||||
const switchRc = typeof sdk.switchKinematics === "function"
|
||||
? sdk.switchKinematics(switchkinsType)
|
||||
: 0;
|
||||
|
||||
return {
|
||||
apiName: "web-rtcp-5axis-linuxcnc-kinematics-runtime",
|
||||
moduleId,
|
||||
wasmFile,
|
||||
supportedModules: supportedLinuxCncKinematicsModules(),
|
||||
loaded: true,
|
||||
sourceMode: SOURCE_MODE,
|
||||
semanticBoundary: SEMANTIC_BOUNDARY,
|
||||
switchkinsType,
|
||||
switchRc,
|
||||
jointCount,
|
||||
sdk,
|
||||
|
||||
readiness() {
|
||||
return {
|
||||
apiName: "web-rtcp-5axis-linuxcnc-kinematics-runtime-readiness",
|
||||
moduleId,
|
||||
wasmFile,
|
||||
supportedModules: supportedLinuxCncKinematicsModules(),
|
||||
loaded: true,
|
||||
sourceMode: SOURCE_MODE,
|
||||
semanticBoundary: SEMANTIC_BOUNDARY,
|
||||
switchkinsType,
|
||||
switchRc,
|
||||
};
|
||||
},
|
||||
|
||||
forward(joints, options = {}) {
|
||||
return sdk.forward(joints, options);
|
||||
},
|
||||
|
||||
inverse(pose, count = jointCount, options = {}) {
|
||||
return sdk.inverse(pose, count, options);
|
||||
},
|
||||
|
||||
frameForJoints(joints, options = {}) {
|
||||
const jointValues = Array.from(joints, Number);
|
||||
const forward = sdk.forward(jointValues, options.forwardOptions || {});
|
||||
const inverse = sdk.inverse(
|
||||
forward.pose,
|
||||
options.jointCount || jointCount,
|
||||
{ seedJoints: jointValues, ...(options.inverseOptions || {}) },
|
||||
);
|
||||
return {
|
||||
moduleId,
|
||||
switchkinsType,
|
||||
forward,
|
||||
inverse,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function createLinuxCncKinematicsRuntimeDescriptor(runtime) {
|
||||
if (!runtime?.loaded) return null;
|
||||
return {
|
||||
apiName: runtime.apiName,
|
||||
moduleId: runtime.moduleId,
|
||||
wasmFile: runtime.wasmFile,
|
||||
supportedModules: runtime.supportedModules,
|
||||
loaded: runtime.loaded,
|
||||
sourceMode: runtime.sourceMode,
|
||||
semanticBoundary: runtime.semanticBoundary,
|
||||
switchkinsType: runtime.switchkinsType,
|
||||
switchRc: runtime.switchRc,
|
||||
};
|
||||
}
|
||||
196
web-rtcp-5axis-sim-plan/app/src/runtime/rtcp-frame.js
Normal file
196
web-rtcp-5axis-sim-plan/app/src/runtime/rtcp-frame.js
Normal file
@@ -0,0 +1,196 @@
|
||||
import { xyzacTrtProfile } from "../profiles/xyzac-trt.js";
|
||||
|
||||
const DEG_TO_RAD = Math.PI / 180;
|
||||
|
||||
export function buildRtcpFrame({
|
||||
axisPose,
|
||||
activeLine,
|
||||
kinsType = "identity",
|
||||
rtcpEnabled = false,
|
||||
sourceMode = "fixture-ui-only",
|
||||
profile = xyzacTrtProfile,
|
||||
linuxCncKinematicsResult = null,
|
||||
}) {
|
||||
if (linuxCncKinematicsResult) {
|
||||
return buildLinuxCncKinematicsFrame({
|
||||
axisPose,
|
||||
activeLine,
|
||||
kinsType,
|
||||
rtcpEnabled,
|
||||
sourceMode,
|
||||
profile,
|
||||
linuxCncKinematicsResult,
|
||||
});
|
||||
}
|
||||
|
||||
const pose = normalizeAxisPose(axisPose);
|
||||
const toolLength = 84.019;
|
||||
const toolAxisVector = computeToolAxisVector(pose.a, pose.c);
|
||||
const compensation = rtcpEnabled
|
||||
? {
|
||||
x: -toolAxisVector.x * toolLength,
|
||||
y: -toolAxisVector.y * toolLength,
|
||||
z: -toolAxisVector.z * toolLength,
|
||||
}
|
||||
: { x: 0, y: 0, z: 0 };
|
||||
const tcpPose = {
|
||||
x: pose.x + compensation.x,
|
||||
y: pose.y + compensation.y,
|
||||
z: pose.z + compensation.z,
|
||||
a: pose.a,
|
||||
c: pose.c,
|
||||
};
|
||||
|
||||
return {
|
||||
apiName: "web-rtcp-5axis-motion-frame",
|
||||
profileId: profile.id,
|
||||
sourceMode,
|
||||
semanticBoundary:
|
||||
sourceMode === "fixture-ui-only"
|
||||
? "fixture_frame_ui_plumbing_not_linuxcnc_kinematics_proof"
|
||||
: "linuxcnc_or_source_derived_runtime_required",
|
||||
activeLine,
|
||||
kinsType,
|
||||
rtcpEnabled,
|
||||
rtcpState: rtcpEnabled ? "on" : "off",
|
||||
axisPose: pose,
|
||||
jointPose: buildJointPose(pose),
|
||||
tcpPose,
|
||||
toolAxisVector,
|
||||
compensation,
|
||||
toolLength,
|
||||
readiness: {
|
||||
frameReady: true,
|
||||
uiReady: true,
|
||||
profileReady: true,
|
||||
linuxCncKinematicsReady: false,
|
||||
promotionAllowed: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildLinuxCncKinematicsFrame({
|
||||
axisPose,
|
||||
activeLine,
|
||||
kinsType,
|
||||
rtcpEnabled,
|
||||
profile,
|
||||
linuxCncKinematicsResult,
|
||||
}) {
|
||||
const forward = linuxCncKinematicsResult.forward || {};
|
||||
const inverse = linuxCncKinematicsResult.inverse || {};
|
||||
const wasmPose = normalizeLinuxCncPose(forward.pose);
|
||||
const pose = normalizeAxisPose({
|
||||
...axisPose,
|
||||
...wasmPose,
|
||||
});
|
||||
const jointValues = Array.isArray(inverse.joints) ? inverse.joints : [];
|
||||
const toolAxisVector = computeToolAxisVector(pose.a, pose.c);
|
||||
|
||||
return {
|
||||
apiName: "web-rtcp-5axis-motion-frame",
|
||||
profileId: profile.id,
|
||||
sourceMode: "source-derived-kinematics-wasm",
|
||||
semanticBoundary: "linuxcnc_kinematics_wasm_c_abi",
|
||||
activeLine,
|
||||
kinsType,
|
||||
rtcpEnabled,
|
||||
rtcpState: rtcpEnabled ? "on" : "off",
|
||||
axisPose: pose,
|
||||
jointPose: buildJointPoseFromLinuxCncJoints(jointValues, pose),
|
||||
tcpPose: {
|
||||
x: pose.x,
|
||||
y: pose.y,
|
||||
z: pose.z,
|
||||
a: pose.a,
|
||||
c: pose.c,
|
||||
},
|
||||
toolAxisVector,
|
||||
compensation: { x: 0, y: 0, z: 0 },
|
||||
toolLength: 84.019,
|
||||
kinematicsModuleId: linuxCncKinematicsResult.moduleId,
|
||||
kinematicsSwitchkinsType: linuxCncKinematicsResult.switchkinsType,
|
||||
kinematicsForwardRc: forward.rc,
|
||||
kinematicsInverseRc: inverse.rc,
|
||||
kinematicsFlags: {
|
||||
fflags: forward.fflags,
|
||||
iflags: forward.iflags,
|
||||
inverseFflags: inverse.fflags,
|
||||
inverseIflags: inverse.iflags,
|
||||
},
|
||||
readiness: {
|
||||
frameReady: true,
|
||||
uiReady: true,
|
||||
profileReady: true,
|
||||
linuxCncKinematicsReady: forward.rc === 0 && inverse.rc === 0,
|
||||
promotionAllowed: forward.rc === 0 && inverse.rc === 0,
|
||||
fullLinuxCncProgramExecutionReady: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function normalizeAxisPose(axisPose) {
|
||||
return {
|
||||
x: Number(axisPose?.x ?? 0),
|
||||
y: Number(axisPose?.y ?? 0),
|
||||
z: Number(axisPose?.z ?? 0),
|
||||
a: Number(axisPose?.a ?? 0),
|
||||
b: Number(axisPose?.b ?? 0),
|
||||
c: Number(axisPose?.c ?? 0),
|
||||
};
|
||||
}
|
||||
|
||||
function buildJointPose(pose) {
|
||||
return [
|
||||
{ joint: 0, axis: "X", value: pose.x },
|
||||
{ joint: 1, axis: "Y", value: pose.y },
|
||||
{ joint: 2, axis: "Z", value: pose.z },
|
||||
{ joint: 3, axis: "A", value: pose.a },
|
||||
{ joint: 4, axis: "C", value: pose.c },
|
||||
];
|
||||
}
|
||||
|
||||
function buildJointPoseFromLinuxCncJoints(joints, fallbackPose) {
|
||||
const axes = ["X", "Y", "Z", "A", "C"];
|
||||
const fallbackValues = [fallbackPose.x, fallbackPose.y, fallbackPose.z, fallbackPose.a, fallbackPose.c];
|
||||
return axes.map((axis, joint) => ({
|
||||
joint,
|
||||
axis,
|
||||
value: Number(joints[joint] ?? fallbackValues[joint] ?? 0),
|
||||
}));
|
||||
}
|
||||
|
||||
function normalizeLinuxCncPose(pose = {}) {
|
||||
return {
|
||||
x: Number(pose.x ?? pose.tran?.x ?? 0),
|
||||
y: Number(pose.y ?? pose.tran?.y ?? 0),
|
||||
z: Number(pose.z ?? pose.tran?.z ?? 0),
|
||||
a: Number(pose.a ?? 0),
|
||||
b: Number(pose.b ?? 0),
|
||||
c: Number(pose.c ?? 0),
|
||||
};
|
||||
}
|
||||
|
||||
function computeToolAxisVector(aDegrees, cDegrees) {
|
||||
const a = aDegrees * DEG_TO_RAD;
|
||||
const c = cDegrees * DEG_TO_RAD;
|
||||
const sinA = Math.sin(a);
|
||||
const cosA = Math.cos(a);
|
||||
const sinC = Math.sin(c);
|
||||
const cosC = Math.cos(c);
|
||||
|
||||
return normalizeVector({
|
||||
x: sinA * sinC,
|
||||
y: -sinA * cosC,
|
||||
z: cosA,
|
||||
});
|
||||
}
|
||||
|
||||
function normalizeVector(vector) {
|
||||
const length = Math.hypot(vector.x, vector.y, vector.z) || 1;
|
||||
return {
|
||||
x: vector.x / length,
|
||||
y: vector.y / length,
|
||||
z: vector.z / length,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user