Align AXIS control flow and fix pause and program rendering
Document source UI gates, explicit native live-plot clearing, and remaining parity boundaries. Fix pause/resume transport continuity, repeated stepping, coordinate previews, and spindle override state. Optimize large program snapshots and rendering, and cover the control flows with native Tcl, WASM, and browser checks. Validation: 22 native tests and source/browser checks pass. Worker/App regression: 113 of 123 pass; 10 pre-existing failures remain documented with no new failures.
This commit is contained in:
70
scripts/test_axis_source_ui_oracle.mjs
Normal file
70
scripts/test_axis_source_ui_oracle.mjs
Normal file
@@ -0,0 +1,70 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import { spawnSync } from "node:child_process";
|
||||
import { axisSourceUi } from "../web/worker/axis-source-ui.mjs";
|
||||
|
||||
const source = await readFile(new URL("../linuxcnc/share/axis/tcl/axis.tcl", import.meta.url), "utf8");
|
||||
const start = source.indexOf("proc update_state {args} {");
|
||||
const end = source.indexOf("\nproc set_mode_from_tab", start);
|
||||
assert.ok(start >= 0 && end > start);
|
||||
const formulas = source.slice(start, end);
|
||||
const scenarios = [];
|
||||
for (const task_state of ["STATE_ESTOP", "STATE_ESTOP_RESET", "STATE_ON"])
|
||||
for (const interp_state of ["INTERP_IDLE", "INTERP_READING", "INTERP_PAUSED", "INTERP_WAITING"])
|
||||
for (const taskfile of [false, true])
|
||||
for (const queued_mdi_commands of [0, 10])
|
||||
for (const motion_mode of ["FREE", "TELEOP"])
|
||||
for (const kinematics_type of ["BOTH", "IDENTITY"])
|
||||
for (const spindle_direction of [0, -1, 1]) scenarios.push({ task_state, interp_state, taskfile, queued_mdi_commands, motion_mode, kinematics_type, spindle_direction, tool: taskfile ? 1 : 0, paused: interp_state === "INTERP_PAUSED" });
|
||||
|
||||
const widgetMap = { power: ".toolbar.machine_power", load: ".toolbar.file_open", reload: ".toolbar.reload", run: ".toolbar.program_run", step: ".toolbar.program_step", pauseResume: ".toolbar.program_pause", abort: ".toolbar.program_stop", manual: "manualgroup", mdi: "mdigroup", spindleAdjust: ".manual.spindlef.spindleminus", touchOff: ".manual.jogf.zerohome.zero", toolTouchOff: ".manual.jogf.zerohome.tooltouch", overrideLimits: ".manual.jogf.override" };
|
||||
const prelude = `
|
||||
proc _ {x} { return $x }
|
||||
proc relief {condition args} {}
|
||||
proc state {condition args} { foreach widget $args { set ::result($widget) [uplevel #0 [list expr $condition]] } }
|
||||
proc enable_group {group} { set ::result($group) 1 }
|
||||
proc disable_group {group} { set ::result($group) 0 }
|
||||
proc set_mode_from_tab {} {}
|
||||
proc unknown {name args} { if {[lindex $args 0] eq "configure"} { set ::result($name) [expr {[lindex $args 2] eq "normal"}] }; return "" }
|
||||
foreach name {STATE_ESTOP STATE_ESTOP_RESET STATE_ON INTERP_IDLE INTERP_READING INTERP_PAUSED INTERP_WAITING TRAJ_MODE_FREE TRAJ_MODE_TELEOP KINEMATICS_IDENTITY KINEMATICS_BOTH TASK_MODE_AUTO} { set ::$name $name }
|
||||
set ::_tabs_manual .manual
|
||||
set ::manualgroup manualgroup
|
||||
set ::mdigroup mdigroup
|
||||
set ::has_editor 1
|
||||
set ::has_ladder 0
|
||||
set ::highlight_line -1
|
||||
set ::task_mode MANUAL
|
||||
set ::coord_type 0
|
||||
set ::display_type 0
|
||||
set ::block_delete 0
|
||||
set ::optional_stop 0
|
||||
set ::last_interp_state INTERP_IDLE
|
||||
set ::last_task_state STATE_ON
|
||||
set ::on_any_limit 0
|
||||
set ::max_queued_mdi_commands 10
|
||||
${formulas}
|
||||
`;
|
||||
const script = prelude + scenarios.map((s) => `
|
||||
set ::task_state ${s.task_state}
|
||||
set ::interp_state ${s.interp_state}
|
||||
set ::taskfile ${s.taskfile ? "program.ngc" : '""'}
|
||||
set ::queued_mdi_commands ${s.queued_mdi_commands}
|
||||
set ::motion_mode TRAJ_MODE_${s.motion_mode}
|
||||
set ::kinematics_type KINEMATICS_${s.kinematics_type}
|
||||
set ::spindledir ${s.spindle_direction}
|
||||
set ::tool ${s.tool || '"No tool"'}
|
||||
set ::interp_pause ${Number(s.paused)}
|
||||
update_state
|
||||
puts [list ${Object.values(widgetMap).map((key) => `$::result(${key})`).join(" ")}]
|
||||
`).join("\n");
|
||||
const result = spawnSync("tclsh", [], { input: script, encoding: "utf8", maxBuffer: 4 * 1024 * 1024 });
|
||||
assert.equal(result.status, 0, result.stderr);
|
||||
assert.equal(result.stderr, "");
|
||||
const rows = result.stdout.trim().split("\n");
|
||||
assert.equal(rows.length, scenarios.length);
|
||||
for (const [index, row] of rows.entries()) {
|
||||
const expected = row.split(" ").map(Number);
|
||||
const actual = axisSourceUi(scenarios[index]).gates;
|
||||
Object.keys(widgetMap).forEach((name, column) => assert.equal(Number(actual[name].enabled), expected[column], `${name}: ${JSON.stringify(scenarios[index])}`));
|
||||
}
|
||||
console.log(JSON.stringify({ schema: "axis-source-ui-oracle.v1", status: "verified", source: "axis.tcl::update_state", scenarios: scenarios.length, predicates: Object.keys(widgetMap).length, assertions: scenarios.length * Object.keys(widgetMap).length }));
|
||||
Reference in New Issue
Block a user