接续上一轮:推进浏览器验证与M428配置收拢

结论:已完成浏览器侧真实加载验证,M428/M429/M430 的配置入口进一步从 LinuxCNC INI/HALFILE/REMAP 来源生成,native 与 source-link 验证通过。
This commit is contained in:
cnc
2026-05-30 10:03:21 +08:00
parent b40914747d
commit de273bf830
141 changed files with 11255 additions and 1259 deletions

View File

@@ -64,8 +64,8 @@
<label class="backend-select">
<span>BACKEND</span>
<select id="backendSelect">
<option value="smoke">SMOKE</option>
<option value="linuxcnc-rs274">LINUXCNC</option>
<option value="smoke">SMOKE</option>
</select>
</label>
<span id="lineCount">0 LINES</span>

19
web/public/cnc_sim.js Normal file

File diff suppressed because one or more lines are too long

View File

@@ -48,12 +48,55 @@ export type CncEvent = {
reserved: number;
};
export type CncRtcpOptions = {
enabled: boolean;
toolLength?: number;
tool_length?: number;
toolLengths?: Record<string, number>;
tool_lengths?: Record<string, number>;
};
export type CncXyzbcTrtOptions = {
xRotPoint?: number;
yRotPoint?: number;
zRotPoint?: number;
xOffset?: number;
yOffset?: number;
zOffset?: number;
conventionalDirections?: boolean;
x_rot_point?: number;
y_rot_point?: number;
z_rot_point?: number;
x_offset?: number;
y_offset?: number;
z_offset?: number;
conventional_directions?: boolean;
};
export type CncParseOptions = {
backend?: string;
rtcp?: CncRtcpOptions;
blockDelete?: boolean;
block_delete?: boolean;
switchkins?: string;
remap?: string;
kinematics?: string;
trt?: "xyzbc" | "xyzac" | string;
pivotLength?: number;
pivot_length?: number;
kinematicsType?: number;
kinematics_type?: number;
fiveaxisKinematicsType?: number;
fiveaxis_kinematics_type?: number;
xyzbcTrt?: CncXyzbcTrtOptions;
};
export type SimulatorCore = {
parse(
program: string,
dialect: CncDialect,
options?: { backend?: string; rtcp?: { enabled: boolean; toolLength: number } },
): Promise<CncEvent[]>;
options?: CncParseOptions,
): CncEvent[];
};
export async function createSimulatorCore(): Promise<SimulatorCore> {

View File

@@ -1,8 +1,14 @@
import type { CncDialect, CncEvent } from "./index";
import type { CncDialect, CncEvent, CncParseOptions } from "./index";
export type WasmSimulator = {
parse(program: string, dialect?: CncDialect, options?: { backend?: string }): CncEvent[];
parse(program: string, dialect?: CncDialect, options?: CncParseOptions): CncEvent[];
dispose(): void;
};
export function createWasmSimulator(): Promise<WasmSimulator>;
export type WasmModuleOptions = {
locateFile?: (file: string) => string;
print?: (text: string) => void;
printErr?: (text: string) => void;
};
export function createWasmSimulator(moduleOptions?: WasmModuleOptions): Promise<WasmSimulator>;

View File

@@ -74,17 +74,20 @@ function readEvent(module, ptr) {
}
async function loadModuleFactory() {
if (globalThis.createCncSimModule) {
return globalThis.createCncSimModule;
}
const module = await import("/cnc_sim.js");
return module.default ?? module.createCncSimModule ?? globalThis.createCncSimModule;
}
export async function createWasmSimulator() {
export async function createWasmSimulator(moduleOptions = {}) {
const createModule = await loadModuleFactory();
if (!createModule) {
throw new Error("cnc_sim.js did not export createCncSimModule");
}
const module = await createModule();
const module = await createModule(moduleOptions);
const create = module.cwrap("cnc_sim_create", "number", []);
const destroy = module.cwrap("cnc_sim_destroy", null, ["number"]);
const reset = module.cwrap("cnc_sim_reset", null, ["number"]);
@@ -104,8 +107,25 @@ export async function createWasmSimulator() {
setDialect(handle, DIALECT[dialect] ?? DIALECT.linuxcnc);
const config = JSON.stringify({
backend: options.backend ?? "smoke",
backend: options.backend ?? "linuxcnc-rs274",
...(options.rtcp ? { rtcp: options.rtcp } : {}),
...(options.blockDelete !== undefined ? { blockDelete: options.blockDelete } : {}),
...(options.block_delete !== undefined ? { block_delete: options.block_delete } : {}),
...(options.switchkins ? { switchkins: options.switchkins } : {}),
...(options.remap ? { remap: options.remap } : {}),
...(options.kinematics ? { kinematics: options.kinematics } : {}),
...(options.trt ? { trt: options.trt } : {}),
...(options.pivotLength !== undefined ? { pivotLength: options.pivotLength } : {}),
...(options.pivot_length !== undefined ? { pivot_length: options.pivot_length } : {}),
...(options.kinematicsType !== undefined ? { kinematicsType: options.kinematicsType } : {}),
...(options.kinematics_type !== undefined ? { kinematics_type: options.kinematics_type } : {}),
...(options.fiveaxisKinematicsType !== undefined
? { fiveaxisKinematicsType: options.fiveaxisKinematicsType }
: {}),
...(options.fiveaxis_kinematics_type !== undefined
? { fiveaxis_kinematics_type: options.fiveaxis_kinematics_type }
: {}),
...(options.xyzbcTrt ? { xyzbcTrt: options.xyzbcTrt } : {}),
});
const configBytes = module.lengthBytesUTF8(config) + 1;
const configPtr = module._malloc(configBytes);

View File

@@ -0,0 +1,56 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>CNC WASM Browser Smoke</title>
</head>
<body>
<pre id="result">pending</pre>
<script src="/public/cnc_sim.js"></script>
<script type="module">
import { createWasmSimulator } from "/src/wasm-core.js";
const result = document.querySelector("#result");
const near = (actual, expected) => Math.abs(actual - expected) < 1e-6;
try {
const simulator = await createWasmSimulator({
locateFile: (file) => `/public/${file}`,
print: () => {},
printErr: () => {},
});
try {
const events = simulator.parse("G21 G90\nG0 X0\nG1 X5 F100\nM30\n", "linuxcnc", {
backend: "linuxcnc-rs274",
});
const sawFeed = events.some((event) => event.type === "linear-feed" && near(event.end.x, 5));
const sawEnd = events.some((event) => event.type === "program-end");
if (!sawFeed || !sawEnd) {
throw new Error("missing expected LinuxCNC WASM browser events");
}
const switchEvents = simulator.parse("M428\nM429\nM430\n", "linuxcnc", {
backend: "linuxcnc-rs274",
switchkins: "scara",
});
for (const [line, kinstype] of [
[1, 0],
[2, 1],
[3, 2],
]) {
const sawSwitch = switchEvents.some(
(event) => event.type === "kinematics-switch" && event.line === line && event.reserved === kinstype,
);
if (!sawSwitch) {
throw new Error(`missing LinuxCNC WASM browser M${427 + line} switchkins type ${kinstype}`);
}
}
result.textContent = "browser wasm smoke passed";
} finally {
simulator.dispose();
}
} catch (error) {
result.textContent = `browser wasm smoke failed: ${error && error.stack ? error.stack : error}`;
}
</script>
</body>
</html>

14
web/tsconfig.json Normal file
View File

@@ -0,0 +1,14 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "Bundler",
"strict": true,
"noEmit": true,
"allowJs": false,
"checkJs": false,
"lib": ["ES2022", "DOM"],
"skipLibCheck": true
},
"include": ["src/index.ts", "src/**/*.d.ts"]
}