提交 xyzbc-trt 界面与验证更新

This commit is contained in:
mes123456
2026-07-02 20:25:37 -04:00
parent 68ecd05353
commit 370c344b96
868 changed files with 275426 additions and 39640 deletions

View File

@@ -121,46 +121,45 @@
throw new Error(`OPFS tool table save did not contain T2/Z10: ${savedToolDb.path}`);
}
const expectedAxisSections = {
".preview-panel": "preview-toolpath-and-machine-model",
".dro-panel": "coordinates-dro",
".gcode-panel": "program-and-mdi",
".status-sidebar": "status-mode-and-switchkins",
".info-tabs": "execution-tool-and-runtime-state",
".override-panel": "feed-rapid-override",
".spindle-coolant-panel": "spindle-coolant",
".bottom-controls": "run-jog-home-controls",
};
for (const [selector, section] of Object.entries(expectedAxisSections)) {
const element = doc.querySelector(selector);
if (!element) {
throw new Error(`missing AXIS UI section ${selector}`);
}
if (
element.dataset.axisUiSection !== section ||
element.dataset.axisUiProfile !== "xyzbc-trt" ||
element.dataset.axisUiCoordinates !== "XYZBC"
) {
throw new Error(`AXIS section dataset mismatch ${selector}: ${JSON.stringify(element.dataset)}`);
const expectedAxisRegions = ["menubar", "toolbar", "manual", "main-tabs", "pyvcp", "program", "statusbar"];
const regions = api.getRegions();
for (const region of expectedAxisRegions) {
if (regions[region] !== true || !doc.querySelector(`[data-region="${region}"]`)) {
throw new Error(`missing AXIS region ${region}: ${JSON.stringify(regions)}`);
}
}
if (!doc.querySelector('[data-shell="axis-xyzbc-trt"]')) {
throw new Error("AXIS xyzbc-trt shell missing");
}
if (!doc.querySelector('[data-action="mdi-input"]') || !doc.querySelector('[data-action="kins-tcp"]')) {
throw new Error("MDI input or switchkins TCP control missing from AXIS shell");
}
const firstScreen = await waitFor(
() => doc.querySelector('[data-axis-main-equivalence="xyzbc_trt_axis_first_screen_program_coordinates_status_mdi_switchkins_override_tool_preview_execution"]'),
"AXIS first-screen summary",
);
const capabilities = Array.from(firstScreen.querySelectorAll("[data-axis-ui-capability]"));
const capabilityIds = capabilities.map((element) => element.dataset.axisUiCapability);
for (const id of ["program", "coordinates", "status", "mdi", "switchkins", "override", "tool", "preview", "execution", "axis-buttons"]) {
if (!capabilityIds.includes(id)) {
throw new Error(`missing first-screen capability ${id}: ${capabilityIds.join(",")}`);
const buttonParity = api.getButtonParity();
if (!Array.isArray(buttonParity) || buttonParity.length < 50) {
throw new Error(`AXIS button parity list too small: ${buttonParity?.length}`);
}
for (const action of ["estop", "power", "run-ready", "run", "pause", "resume", "step", "stop", "home-all", "jog-plus", "jog-minus", "kins-identity", "kins-tcp", "kins-userk", "clear-preview"]) {
if (!buttonParity.some((item) => item.action === action && item.sourceSymbol && item.sourceLines && item.expected)) {
throw new Error(`missing source-referenced button parity for ${action}`);
}
}
if (firstScreen.dataset.axisFirstScreenReady !== "true") {
throw new Error(`first-screen summary not ready: ${firstScreen.dataset.axisFirstScreenMissing}`);
}
if (!doc.querySelector('[data-action="mdi-command"]') || !doc.querySelector('[data-action="kins-tcp"]')) {
throw new Error("MDI input or switchkins TCP control missing from first screen");
for (const selector of [
'[data-action="estop"]',
'[data-action="power"]',
'[data-action="run"]',
'[data-action="step"]',
'[data-action="stop"]',
'[data-action="jog-plus"]',
'[data-action="toggle-flood"]',
'[data-action="kins-tcp"]',
'[data-action="clear-preview"]',
'[data-menu-command="run-ready"]',
]) {
const control = doc.querySelector(selector);
if (!control?.dataset.axisSourceRef || !control?.dataset.axisExpectedEffect) {
throw new Error(`missing AXIS source metadata on ${selector}: ${JSON.stringify(control?.dataset || {})}`);
}
}
const canvas = await waitFor(() => doc.querySelector("[data-five-axis-canvas]"), "preview canvas");
@@ -198,6 +197,8 @@
}
assertCanvasNonblank(canvas);
await verifyAxisButtonLogic(doc, api);
if (runtimeErrors.length > 0) {
throw new Error(`runtime errors: ${runtimeErrors.join(" | ")}`);
}
@@ -245,6 +246,79 @@
}
}
async function verifyAxisButtonLogic(doc, api) {
const click = async (selector, label = selector) => {
const element = doc.querySelector(selector);
if (!element) throw new Error(`missing button for ${label}`);
element.click();
await wait(50);
return api.getState();
};
const waitState = (predicate, label) => waitFor(() => {
const next = api.getState();
return predicate(next) ? next : null;
}, label);
await click('[data-action="estop"]', "AXIS estop");
await waitState((state) => state.machine.estopActive === true && state.runState === "estopped", "estop active");
await click('[data-action="estop"]', "AXIS estop reset");
await waitState((state) => state.machine.estopActive === false && state.machine.taskState === "estop-reset", "estop reset");
await click('[data-action="power"]', "AXIS power");
await waitState((state) => state.machine.powerOn === true && state.machine.taskState === "on", "machine power on");
await click('[data-action="home-all"]', "AXIS home all");
await waitState((state) => state.machine.allHomed === true && state.machine.mode === "manual", "home all");
await click('[data-action="tab-mdi"]', "AXIS MDI tab");
await waitState((state) => state.machine.mode === "mdi", "MDI mode");
await click('[data-action="kins-tcp"]', "PyVCP TCP");
await waitState((state) => state.kinsType === "tcp-xyzbc" && state.rtcpState === "on" && state.mdiHistory[0] === "M428", "TCP kins");
await click('[data-action="kins-identity"]', "PyVCP identity");
await waitState((state) => state.kinsType === "identity" && state.rtcpState === "off" && state.mdiHistory[0] === "M429", "identity kins");
await click('[data-action="kins-userk"]', "PyVCP userk");
await waitState((state) => state.kinsType === "userk" && state.mdiHistory[0] === "M430", "userk kins");
await click('[data-action="tab-manual"]', "AXIS manual tab");
await waitState((state) => state.machine.mode === "manual", "manual mode");
const joint4 = doc.querySelector('[data-action="select-joint"][value="4"]');
joint4.checked = true;
joint4.dispatchEvent(new Event("change", { bubbles: true }));
await waitState((state) => state.machine.selectedJoint === 4 && state.machine.jogAxis === "c", "select C joint");
const beforeJogC = api.getState().axisPose.c;
await click('[data-action="jog-plus"]', "AXIS jog plus");
await waitState((state) => state.axisPose.c > beforeJogC, "jog C plus");
const beforeFeed = api.getState().feed.feedOverride;
await click('[data-action="feed-override-up"]', "feed override up");
await waitState((state) => state.feed.feedOverride === Math.min(beforeFeed + 10, 200), "feed override");
await click('[data-action="spindle-forward"]', "spindle forward");
await waitState((state) => state.spindle.direction === "forward" && state.spindle.enabled === true, "spindle forward");
await click('[data-action="spindle-stop"]', "spindle stop");
await waitState((state) => state.spindle.direction === "stop" && state.spindle.enabled === false, "spindle stop");
await click('[data-action="toggle-flood"]', "flood coolant");
await waitState((state) => state.coolant.flood === true, "flood coolant");
await click('[data-action="block-delete"]', "block delete");
await waitState((state) => state.gmoccapyGui.optionalBlocks === true, "block delete");
await click('[data-action="optional-stop"]', "optional stop");
await waitState((state) => state.gmoccapyGui.optionalStop === true, "optional stop");
await click('[data-action="ignore-limits"]', "ignore limits");
await waitState((state) => state.gmoccapyGui.ignoreLimits === true, "ignore limits");
await click('[data-action="view-x"]', "view X");
await waitState((state) => state.preview.selectedView === "x", "view X");
await click('[data-action="view-y"]', "view Y");
await waitState((state) => state.preview.selectedView === "y", "view Y");
await click('[data-action="view-z"]', "view Z");
await waitState((state) => state.preview.selectedView === "z", "view Z");
await click('[data-action="view-p"]', "view P");
await waitState((state) => state.preview.selectedView === "iso", "view P");
await click('[data-action="clear-preview"]', "clear preview");
await waitState((state) => state.preview.pathPoints === 0, "clear preview");
doc.querySelector('[data-menu-command="run-ready"]').click();
await waitState((state) => state.machine.powerOn === true && state.machine.allHomed === true && state.machine.mode === "auto", "run-ready menu");
}
async function readOpfsText(storage, path) {
if (!storage?.getDirectory) {
throw new Error("browser OPFS getDirectory is unavailable");