完善虚拟HAL仿真替代能力

This commit is contained in:
2026-06-17 19:45:51 +08:00
parent 656cddf73a
commit a4d449ad48
17 changed files with 5005 additions and 302 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -15,6 +15,32 @@ export {
getSimulationTestProgram,
getSimulationTestPrograms,
};
export {
VIRTUAL_HAL_AXES,
VIRTUAL_HAL_AXISUI_PINS,
VIRTUAL_HAL_SIMULATION_REPLACEMENT_TARGETS,
VIRTUAL_HAL_SIMULATION_RUNTIME_CAPABILITIES,
applyVirtualHalAction,
applyVirtualHalPinUpdates,
cloneVirtualHalState,
createLinuxCncVirtualHalRuntime,
createVirtualHalDroState,
createVirtualHalIntegrityReport,
createVirtualHalLimitsHomeState,
createVirtualHalMachineStatusState,
createVirtualHalPinRegistry,
createVirtualHalSimulationReplacementReport,
createVirtualHalSimulationRuntimeReport,
createVirtualHalState,
createVirtualHalWasmBridgeSnapshot,
executeVirtualHalCommand,
executeVirtualHalcmd,
parseVirtualHalJogIncrement,
readVirtualHalPin,
stepVirtualHalMotion,
stepVirtualHalMotionController,
writeVirtualHalPin,
} from "../../sdk/src/index.js";
const AXES = ["x", "y", "z", "a", "b", "c", "u", "v", "w"];
const DISPLAY_AXES = ["x", "y", "z", "a", "b", "c"];
@@ -23,7 +49,7 @@ const PLANE_AXIS_MAP = {
180: ["x", "z", "y"],
190: ["y", "z", "x"],
};
const UNAVAILABLE_VALUE = "n/a";
const UNAVAILABLE_VALUE = "-";
const DEFAULT_PREVIEW_LAYER_STATE = Object.freeze({
traverse: true,
feed: true,
@@ -178,35 +204,58 @@ export function createModalState(resultText) {
available: Number.isFinite(fields[field]),
source: Number.isFinite(fields[field]) ? "linuxcnc-update-tag" : "unavailable",
}));
const axisCodes = [
fields.motion === 0 ? "G80" : `G${fields.motion ?? 80}`,
({ 170: "G17", 180: "G18", 190: "G19" })[fields.plane] ?? "G17",
"G40",
"G20",
"G90",
"G94",
"G54",
"G49",
"G99",
"G64",
"G97",
"G91.1",
"G8",
"M5",
"M9",
"M48",
"M53",
"M0",
`F${Number.isFinite(fields.feed) ? fields.feed : 0}`,
`S${Number.isFinite(fields.speed) ? fields.speed : 0}`,
];
return {
apiName: "real-browser-simulation-modal-state",
modalVersion: 1,
source: activeTag ? "linuxcnc-update-tag" : "unavailable",
raw: activeTag,
rows: modalRows,
axisCodes,
};
}
export function createDroState(frame) {
const axes = frame?.axes ?? Object.fromEntries(AXES.map((axis) => [axis, 0]));
const actual = Object.fromEntries(DISPLAY_AXES.map((axis) => [axis, formatAxis(axes[axis])]));
const unavailableAxes = Object.fromEntries(DISPLAY_AXES.map((axis) => [axis, UNAVAILABLE_VALUE]));
const zeroAxes = Object.fromEntries(DISPLAY_AXES.map((axis) => [axis, formatAxis(0)]));
return {
apiName: "real-browser-simulation-dro-state",
droVersion: 1,
source: "linuxcnc-canonical-motion",
actual,
distanceToGo: { ...unavailableAxes },
workOffsetG54: { ...unavailableAxes },
g92Offset: { ...unavailableAxes },
toolLengthOffset: { ...unavailableAxes },
velocity: UNAVAILABLE_VALUE,
unavailable: {
distanceToGo: "unavailable from current canonical output",
workOffsetG54: "unavailable from current canonical output",
g92Offset: "unavailable from current canonical output",
toolLengthOffset: "unavailable from current canonical output",
velocity: "unavailable from current canonical output",
distanceToGo: { ...zeroAxes },
workOffsetG54: { ...zeroAxes },
g92Offset: { ...zeroAxes },
toolLengthOffset: { ...zeroAxes },
velocity: formatAxis(0),
defaults: {
distanceToGo: "zeroed display default until trajectory DTG is available",
workOffsetG54: "zeroed display default until coordinate offsets are available",
g92Offset: "zeroed display default until coordinate offsets are available",
toolLengthOffset: "zeroed display default until tool offsets are available",
velocity: "zeroed display default until live velocity is available",
},
};
}
@@ -217,25 +266,25 @@ export function createMachineStatusState(resultText) {
statusVersion: 1,
source: "linuxcnc-canonical-events",
spindle: {
direction: UNAVAILABLE_VALUE,
speed: UNAVAILABLE_VALUE,
state: UNAVAILABLE_VALUE,
direction: "stopped",
speed: "0",
state: "off",
},
coolant: {
mist: UNAVAILABLE_VALUE,
flood: UNAVAILABLE_VALUE,
mist: "off",
flood: "off",
},
tool: {
selected: UNAVAILABLE_VALUE,
current: UNAVAILABLE_VALUE,
pocket: UNAVAILABLE_VALUE,
lengthOffset: UNAVAILABLE_VALUE,
selected: "0",
current: "0",
pocket: "0",
lengthOffset: "z=0.000",
},
overrides: {
feed: UNAVAILABLE_VALUE,
speed: UNAVAILABLE_VALUE,
adaptiveFeed: UNAVAILABLE_VALUE,
feedHold: UNAVAILABLE_VALUE,
feed: "enabled",
speed: "enabled",
adaptiveFeed: "disabled",
feedHold: "enabled",
},
};
for (const line of String(resultText).split("\n")) {
@@ -250,6 +299,7 @@ export function createMachineStatusState(resultText) {
status.spindle.state = "on";
} else if (line.startsWith("canon_event=STOP_SPINDLE_TURNING")) {
status.spindle.state = "off";
status.spindle.direction = "stopped";
} else if (line.startsWith("canon_event=MIST_ON")) {
status.coolant.mist = "on";
} else if (line.startsWith("canon_event=MIST_OFF")) {
@@ -449,8 +499,14 @@ function renderDroState(documentRef, dro) {
setText(documentRef, `[data-dro-g54="${axis}"]`, dro.workOffsetG54[axis]);
setText(documentRef, `[data-dro-g92="${axis}"]`, dro.g92Offset[axis]);
setText(documentRef, `[data-dro-tlo="${axis}"]`, dro.toolLengthOffset[axis]);
setText(documentRef, `[data-preview-hud="${axis}"]`, Number(dro.actual[axis]).toFixed(4));
setText(documentRef, `[data-preview-hud="dtg-${axis}"]`, Number(dro.distanceToGo[axis]).toFixed(4));
setText(documentRef, `[data-preview-hud="g54-${axis}"]`, Number(dro.workOffsetG54[axis]).toFixed(4));
setText(documentRef, `[data-preview-hud="g92-${axis}"]`, Number(dro.g92Offset[axis]).toFixed(4));
setText(documentRef, `[data-preview-hud="tlo-${axis}"]`, Number(dro.toolLengthOffset[axis]).toFixed(4));
}
setText(documentRef, "[data-dro-velocity]", dro.velocity);
setText(documentRef, "[data-preview-hud=\"velocity\"]", Number(dro.velocity).toFixed(4));
}
function renderModalState(documentRef, modal) {
@@ -459,13 +515,20 @@ function renderModalState(documentRef, modal) {
return;
}
container.textContent = "";
for (const row of modal.rows) {
for (const row of modal.rows ?? []) {
const item = documentRef.createElement("span");
item.dataset.modalField = row.id;
item.dataset.available = row.available ? "true" : "false";
item.textContent = `${row.label}: ${row.value}`;
container.append(item);
}
for (const code of modal.axisCodes ?? []) {
const item = documentRef.createElement("span");
item.dataset.modalCode = code;
item.dataset.available = "true";
item.textContent = code;
container.append(item);
}
}
function renderMachineStatusState(documentRef, machineStatus) {