新增真实G代码编辑区
结论:AXIS 风格浏览器仿真页面新增可编辑 G-code pane、Run Editor Text、get/setProgramText 和 runEditorProgramText,编辑后的真实 G-code 继续通过 LinuxCNC WASM 执行并纳入 browser gate。
This commit is contained in:
@@ -40,7 +40,7 @@ The project is complete for the current scope when all of the following are true
|
|||||||
| --- | --- | --- |
|
| --- | --- | --- |
|
||||||
| LinuxCNC semantic boundary | Stable | JS/browser remains glue, staging, OPFS, WASM/browser boundary, docs, and tests only. |
|
| LinuxCNC semantic boundary | Stable | JS/browser remains glue, staging, OPFS, WASM/browser boundary, docs, and tests only. |
|
||||||
| Real browser simulation priority | Active first priority | Future batches should prioritize the real UI/browser CNC simulation page, with visible machine/session loading, G-code program state, LinuxCNC-backed interpreter execution, machine readiness/status panels, toolpath/preview rendering from LinuxCNC-produced output or validated runtime events, and browser smoke coverage. First entry point: `wasm-port/runtime/ui/simulation/index.html`; gate: `wasm-port/tests/browser/verify_real_simulation_browser.sh`. See `wasm-port/docs/real-browser-simulation-priority.md`. |
|
| Real browser simulation priority | Active first priority | Future batches should prioritize the real UI/browser CNC simulation page, with visible machine/session loading, G-code program state, LinuxCNC-backed interpreter execution, machine readiness/status panels, toolpath/preview rendering from LinuxCNC-produced output or validated runtime events, and browser smoke coverage. First entry point: `wasm-port/runtime/ui/simulation/index.html`; gate: `wasm-port/tests/browser/verify_real_simulation_browser.sh`. See `wasm-port/docs/real-browser-simulation-priority.md`. |
|
||||||
| Real browser simulation page | MVP active | `runtime/ui/simulation/index.html` loads in Chromium, exposes a selector for multiple LinuxCNC-backed test programs, runs operator-provided G-code text/files through interpreter WASM, and includes reset/step/play/finish playback over LinuxCNC canonical motion events. It highlights active G-code and motion rows, updates live axis readout, moves the toolhead, and grows the executed SVG toolpath. Current built-in program set covers square contour, Z pocket contour, incremental loop, G2/G3 arcs, and G81 drilling. |
|
| Real browser simulation page | MVP active | `runtime/ui/simulation/index.html` loads in Chromium, exposes a selector for multiple LinuxCNC-backed test programs, runs operator-provided G-code text/files through interpreter WASM, includes an editable G-code pane with Run Editor Text, and includes reset/step/play/finish playback over LinuxCNC canonical motion events. It highlights active G-code and motion rows, updates live axis readout, moves the toolhead, and grows the executed SVG toolpath. Current built-in program set covers square contour, Z pocket contour, incremental loop, G2/G3 arcs, and G81 drilling. |
|
||||||
| AXIS-style simulation shell | Phase 1 active | `runtime/ui/simulation/index.html` now uses an AXIS-inspired titlebar, menubar, toolbar, Manual/MDI tabs, Preview/DRO tabs, G-code pane, machine-state pane, and status bar while preserving current LinuxCNC-backed execution and playback APIs. Browser smoke verifies shell regions, tab switching, toolbar playback, and existing program execution. |
|
| AXIS-style simulation shell | Phase 1 active | `runtime/ui/simulation/index.html` now uses an AXIS-inspired titlebar, menubar, toolbar, Manual/MDI tabs, Preview/DRO tabs, G-code pane, machine-state pane, and status bar while preserving current LinuxCNC-backed execution and playback APIs. Browser smoke verifies shell regions, tab switching, toolbar playback, and existing program execution. |
|
||||||
| Simulation test program library | Active | Built-in browser simulation programs live in `wasm-port/runtime/ui/simulation/programs/`, one module per program, exported through `programs/index.js`; `verify_real_simulation_programs.sh` checks directory-backed inventory, IDs, defaults, and playback contracts. |
|
| Simulation test program library | Active | Built-in browser simulation programs live in `wasm-port/runtime/ui/simulation/programs/`, one module per program, exported through `programs/index.js`; `verify_real_simulation_programs.sh` checks directory-backed inventory, IDs, defaults, and playback contracts. |
|
||||||
| Vendor/source guard | Stable | `verify_vendor_sync.sh` and standalone semantic guard are required every batch. |
|
| Vendor/source guard | Stable | `verify_vendor_sync.sh` and standalone semantic guard are required every batch. |
|
||||||
|
|||||||
67
text16.txt
67
text16.txt
@@ -81,6 +81,12 @@ wasm-port/runtime/ui/simulation/programs/index.js
|
|||||||
- `linuxCncRealSimulationApi.runProgramText(programText, metadata)`;
|
- `linuxCncRealSimulationApi.runProgramText(programText, metadata)`;
|
||||||
- `linuxCncRealSimulationApi.loadProgramFile(file)`;
|
- `linuxCncRealSimulationApi.loadProgramFile(file)`;
|
||||||
- toolbar `Open` file input for `.ngc/.nc/.tap/.gcode/.txt`.
|
- toolbar `Open` file input for `.ngc/.nc/.tap/.gcode/.txt`.
|
||||||
|
- Editable AXIS-style G-code pane:
|
||||||
|
- loaded file/custom/built-in program text is visible in a textarea;
|
||||||
|
- editor text can be changed before execution;
|
||||||
|
- toolbar `Run Editor Text` executes the current editor contents;
|
||||||
|
- `getProgramText()`, `setProgramText()`, and `runEditorProgramText()` are
|
||||||
|
exposed on the browser API.
|
||||||
- Program source metadata:
|
- Program source metadata:
|
||||||
- `program.source`;
|
- `program.source`;
|
||||||
- `program.sourceLabel`;
|
- `program.sourceLabel`;
|
||||||
@@ -102,6 +108,9 @@ Current methods:
|
|||||||
- `runProgramById(programId)`
|
- `runProgramById(programId)`
|
||||||
- `runProgramText(programText, metadata)`
|
- `runProgramText(programText, metadata)`
|
||||||
- `loadProgramFile(file)`
|
- `loadProgramFile(file)`
|
||||||
|
- `getProgramText()`
|
||||||
|
- `setProgramText(text, metadata)`
|
||||||
|
- `runEditorProgramText()`
|
||||||
- `getPlaybackFrame()`
|
- `getPlaybackFrame()`
|
||||||
- `resetPlayback()`
|
- `resetPlayback()`
|
||||||
- `stepPlayback(delta)`
|
- `stepPlayback(delta)`
|
||||||
@@ -169,7 +178,9 @@ Browser code must not implement:
|
|||||||
|
|
||||||
### 1. Real G-code Editor Pane
|
### 1. Real G-code Editor Pane
|
||||||
|
|
||||||
Add an editable G-code text pane to the AXIS shell:
|
Status: complete.
|
||||||
|
|
||||||
|
Implemented:
|
||||||
|
|
||||||
- show loaded file/custom text in an editor area;
|
- show loaded file/custom text in an editor area;
|
||||||
- allow editing before run;
|
- allow editing before run;
|
||||||
@@ -177,12 +188,12 @@ Add an editable G-code text pane to the AXIS shell:
|
|||||||
- preserve line numbers and active-line highlighting;
|
- preserve line numbers and active-line highlighting;
|
||||||
- expose `linuxCncRealSimulationApi.getProgramText()`;
|
- expose `linuxCncRealSimulationApi.getProgramText()`;
|
||||||
- expose `linuxCncRealSimulationApi.setProgramText(text, metadata)`.
|
- expose `linuxCncRealSimulationApi.setProgramText(text, metadata)`.
|
||||||
|
- expose `linuxCncRealSimulationApi.runEditorProgramText()`.
|
||||||
|
|
||||||
Validation:
|
Validation:
|
||||||
|
|
||||||
- browser smoke edits a line, runs it, and verifies changed LinuxCNC canonical
|
- browser smoke sets editor text, runs it, and verifies changed LinuxCNC
|
||||||
output;
|
canonical output.
|
||||||
- Node smoke verifies editor text state helpers if factored into JS modules.
|
|
||||||
|
|
||||||
### 2. OPFS Program Persistence
|
### 2. OPFS Program Persistence
|
||||||
|
|
||||||
@@ -287,6 +298,48 @@ http://154.8.160.151:3000/wangdequan/cnc_wams.git
|
|||||||
|
|
||||||
## Immediate Suggested Start
|
## Immediate Suggested Start
|
||||||
|
|
||||||
Start with "Real G-code Editor Pane" because the page can already execute
|
Start with "OPFS Program Persistence" because the page can now load, edit, and
|
||||||
operator-provided text/files, but the text is not yet visible/editable inside
|
run real G-code text/files, but programs are not yet saved to or restored from
|
||||||
the AXIS shell after loading.
|
OPFS from within the AXIS shell.
|
||||||
|
|
||||||
|
## Execution Record
|
||||||
|
|
||||||
|
### 一、2026-06-16 Real G-code Editor Pane
|
||||||
|
|
||||||
|
按 `text16.txt` 的 Immediate Suggested Start 推进,完成 AXIS shell 内真实 G-code 编辑区。
|
||||||
|
|
||||||
|
本批新增实质能力:
|
||||||
|
|
||||||
|
- `runtime/ui/simulation/index.html` 新增 editable G-code pane:
|
||||||
|
- `data-axis-shell="program-editor"`;
|
||||||
|
- `textarea[data-program-editor]`;
|
||||||
|
- `data-program-editor-status`;
|
||||||
|
- 工具栏新增:
|
||||||
|
- `Run Editor Text`;
|
||||||
|
- 页面状态新增 `programEditorState`;
|
||||||
|
- 内置程序加载、真实文本执行、真实文件加载都会同步到 editor textarea;
|
||||||
|
- editor textarea 修改后可直接运行;
|
||||||
|
- `window.linuxCncRealSimulationApi` 新增:
|
||||||
|
- `getProgramText()`;
|
||||||
|
- `setProgramText(text, metadata)`;
|
||||||
|
- `runEditorProgramText()`;
|
||||||
|
- browser smoke 新增验证:
|
||||||
|
- 初始内置程序会同步到 editor;
|
||||||
|
- `runProgramText()` 后 editor 同步自定义文本;
|
||||||
|
- `loadProgramFile()` 后 editor 同步文件文本和 filename metadata;
|
||||||
|
- `setProgramText()` 修改 editor 内容;
|
||||||
|
- `runEditorProgramText()` 通过 LinuxCNC WASM 执行修改后的 G-code,并验证 canonical output
|
||||||
|
出现修改后的坐标。
|
||||||
|
|
||||||
|
关键验证:
|
||||||
|
|
||||||
|
```text
|
||||||
|
browser_real_simulation_page_smoke=ok
|
||||||
|
real_simulation_programs_node_smoke=ok
|
||||||
|
```
|
||||||
|
|
||||||
|
语义边界:
|
||||||
|
|
||||||
|
- editor 只保存和显示 G-code 文本;
|
||||||
|
- G-code 执行仍通过 LinuxCNC-backed WASM;
|
||||||
|
- JS 不解释 G-code、不规划刀路、不推导 modal/machine semantics。
|
||||||
|
|||||||
@@ -57,6 +57,7 @@ The current simulation page already has:
|
|||||||
- LinuxCNC interpreter WASM execution through `createLinuxCncInterpSdk()`;
|
- LinuxCNC interpreter WASM execution through `createLinuxCncInterpSdk()`;
|
||||||
- selectable test programs under `runtime/ui/simulation/programs/`;
|
- selectable test programs under `runtime/ui/simulation/programs/`;
|
||||||
- custom G-code text and browser File execution through LinuxCNC WASM;
|
- custom G-code text and browser File execution through LinuxCNC WASM;
|
||||||
|
- editable G-code pane plus run-editor-text workflow;
|
||||||
- canonical output rendering;
|
- canonical output rendering;
|
||||||
- program-line rendering;
|
- program-line rendering;
|
||||||
- motion table;
|
- motion table;
|
||||||
@@ -126,7 +127,7 @@ Current implemented regions:
|
|||||||
|
|
||||||
### Phase 2: Program Execution Workflow
|
### Phase 2: Program Execution Workflow
|
||||||
|
|
||||||
Status: active; custom text/file execution is implemented.
|
Status: active; custom text/file execution and editor-text execution are implemented.
|
||||||
|
|
||||||
Upgrade the G-code pane and execution controls:
|
Upgrade the G-code pane and execution controls:
|
||||||
|
|
||||||
@@ -138,6 +139,8 @@ Upgrade the G-code pane and execution controls:
|
|||||||
- show run status, source program, motion count, and current frame.
|
- show run status, source program, motion count, and current frame.
|
||||||
- load operator-provided G-code text through `runProgramText()`;
|
- load operator-provided G-code text through `runProgramText()`;
|
||||||
- load browser `File` objects through `loadProgramFile()`;
|
- load browser `File` objects through `loadProgramFile()`;
|
||||||
|
- edit visible G-code in the AXIS shell and run it through
|
||||||
|
`runEditorProgramText()`;
|
||||||
|
|
||||||
Built-in test programs must live in:
|
Built-in test programs must live in:
|
||||||
|
|
||||||
@@ -231,6 +234,9 @@ Required methods:
|
|||||||
- `finishPlayback()`;
|
- `finishPlayback()`;
|
||||||
- `runProgramText(programText, metadata)`;
|
- `runProgramText(programText, metadata)`;
|
||||||
- `loadProgramFile(file)`;
|
- `loadProgramFile(file)`;
|
||||||
|
- `getProgramText()`;
|
||||||
|
- `setProgramText(text, metadata)`;
|
||||||
|
- `runEditorProgramText()`;
|
||||||
- future: `loadProgramText(text, metadata)`;
|
- future: `loadProgramText(text, metadata)`;
|
||||||
- future: `loadMachineSessionFromOpfs(options)`;
|
- future: `loadMachineSessionFromOpfs(options)`;
|
||||||
- future: `getMachineReadiness()`;
|
- future: `getMachineReadiness()`;
|
||||||
|
|||||||
@@ -441,6 +441,45 @@
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
font-weight: 650;
|
font-weight: 650;
|
||||||
}
|
}
|
||||||
|
.program-editor-wrap {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(240px, 0.85fr) minmax(280px, 1fr);
|
||||||
|
min-height: 0;
|
||||||
|
}
|
||||||
|
.program-editor-panel {
|
||||||
|
min-height: 0;
|
||||||
|
border-right: 1px solid var(--line);
|
||||||
|
display: grid;
|
||||||
|
grid-template-rows: auto minmax(0, 1fr);
|
||||||
|
background: #ededed;
|
||||||
|
}
|
||||||
|
.program-editor-toolbar {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
border-bottom: 1px solid var(--line);
|
||||||
|
padding: 4px 6px;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
.program-editor-toolbar div {
|
||||||
|
display: flex;
|
||||||
|
gap: 6px;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
.program-editor {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 0;
|
||||||
|
border: 0;
|
||||||
|
resize: none;
|
||||||
|
padding: 8px 10px;
|
||||||
|
background: #fbfbfb;
|
||||||
|
color: var(--text);
|
||||||
|
font: 13px/1.45 "SFMono-Regular", "Consolas", monospace;
|
||||||
|
tab-size: 2;
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
.axis-statusbar {
|
.axis-statusbar {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: 120px minmax(120px, 1fr) minmax(180px, auto);
|
grid-template-columns: 120px minmax(120px, 1fr) minmax(180px, auto);
|
||||||
@@ -485,6 +524,8 @@
|
|||||||
@media (max-width: 1040px) {
|
@media (max-width: 1040px) {
|
||||||
main { grid-template-rows: auto auto auto minmax(0, 1fr) 220px auto; }
|
main { grid-template-rows: auto auto auto minmax(0, 1fr) 220px auto; }
|
||||||
.axis-workspace { grid-template-columns: 1fr; }
|
.axis-workspace { grid-template-columns: 1fr; }
|
||||||
|
.program-editor-wrap { grid-template-columns: 1fr; }
|
||||||
|
.program-editor-panel { border-right: 0; border-bottom: 1px solid var(--line); min-height: 180px; }
|
||||||
svg { min-height: 280px; }
|
svg { min-height: 280px; }
|
||||||
}
|
}
|
||||||
@media (max-width: 560px) {
|
@media (max-width: 560px) {
|
||||||
@@ -532,6 +573,7 @@
|
|||||||
<div class="program-controls">
|
<div class="program-controls">
|
||||||
<select data-program-selector aria-label="Simulation test program"></select>
|
<select data-program-selector aria-label="Simulation test program"></select>
|
||||||
<button type="button" data-run-program>Run</button>
|
<button type="button" data-run-program>Run</button>
|
||||||
|
<button type="button" data-run-editor-program>Run Editor Text</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="playback-controls" aria-label="Simulation playback controls">
|
<div class="playback-controls" aria-label="Simulation playback controls">
|
||||||
<button type="button" class="toolbar-button" data-playback-reset>Reset</button>
|
<button type="button" class="toolbar-button" data-playback-reset>Reset</button>
|
||||||
@@ -655,7 +697,18 @@
|
|||||||
<span>Program: <span data-selected-program>Executed through the LinuxCNC-backed WASM interpreter.</span> | Source: <span data-program-source>loading</span></span>
|
<span>Program: <span data-selected-program>Executed through the LinuxCNC-backed WASM interpreter.</span> | Source: <span data-program-source>loading</span></span>
|
||||||
<span>Active line <span data-active-line>-</span></span>
|
<span>Active line <span data-active-line>-</span></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="program-lines" data-program-lines></div>
|
<div class="program-editor-wrap">
|
||||||
|
<div class="program-editor-panel" data-axis-shell="program-editor">
|
||||||
|
<div class="program-editor-toolbar">
|
||||||
|
<span>Editable G-code</span>
|
||||||
|
<div>
|
||||||
|
<span data-program-editor-status>loaded</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<textarea class="program-editor" spellcheck="false" data-program-editor aria-label="Editable G-code program"></textarea>
|
||||||
|
</div>
|
||||||
|
<div class="program-lines" data-program-lines></div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
<section class="axis-gcode-pane" data-axis-shell="machine-state" aria-label="Machine state">
|
<section class="axis-gcode-pane" data-axis-shell="machine-state" aria-label="Machine state">
|
||||||
<div class="axis-gcode-head">
|
<div class="axis-gcode-head">
|
||||||
@@ -700,9 +753,11 @@
|
|||||||
} from "./simulation-app.js";
|
} from "./simulation-app.js";
|
||||||
|
|
||||||
const runButton = document.querySelector("[data-run-program]");
|
const runButton = document.querySelector("[data-run-program]");
|
||||||
|
const runEditorButton = document.querySelector("[data-run-editor-program]");
|
||||||
const programSelector = document.querySelector("[data-program-selector]");
|
const programSelector = document.querySelector("[data-program-selector]");
|
||||||
const openProgramButton = document.querySelector("[data-open-program]");
|
const openProgramButton = document.querySelector("[data-open-program]");
|
||||||
const openProgramFile = document.querySelector("[data-open-program-file]");
|
const openProgramFile = document.querySelector("[data-open-program-file]");
|
||||||
|
const programEditor = document.querySelector("[data-program-editor]");
|
||||||
const resetButton = document.querySelector("[data-playback-reset]");
|
const resetButton = document.querySelector("[data-playback-reset]");
|
||||||
const prevButton = document.querySelector("[data-playback-prev]");
|
const prevButton = document.querySelector("[data-playback-prev]");
|
||||||
const playButton = document.querySelector("[data-playback-play]");
|
const playButton = document.querySelector("[data-playback-play]");
|
||||||
@@ -711,6 +766,14 @@
|
|||||||
const speedSelector = document.querySelector("[data-playback-speed]");
|
const speedSelector = document.querySelector("[data-playback-speed]");
|
||||||
let playbackTimer = null;
|
let playbackTimer = null;
|
||||||
let playbackIndex = 0;
|
let playbackIndex = 0;
|
||||||
|
let programEditorState = {
|
||||||
|
text: "",
|
||||||
|
metadata: {
|
||||||
|
label: "Custom G-code program",
|
||||||
|
source: "editor",
|
||||||
|
sourceLabel: "Editor text",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
function setAllText(selector, value) {
|
function setAllText(selector, value) {
|
||||||
document.querySelectorAll(selector).forEach((node) => {
|
document.querySelectorAll(selector).forEach((node) => {
|
||||||
@@ -738,6 +801,28 @@
|
|||||||
return window.linuxCncRealSimulationState;
|
return window.linuxCncRealSimulationState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function setProgramEditorText(text, metadata = {}) {
|
||||||
|
programEditorState = {
|
||||||
|
text,
|
||||||
|
metadata: {
|
||||||
|
...programEditorState.metadata,
|
||||||
|
...metadata,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
if (programEditor && programEditor.value !== text) {
|
||||||
|
programEditor.value = text;
|
||||||
|
}
|
||||||
|
setAllText("[data-program-editor-status]", programEditorState.metadata.sourceLabel ?? "Editor text");
|
||||||
|
return { ...programEditorState, metadata: { ...programEditorState.metadata } };
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProgramEditorText() {
|
||||||
|
return {
|
||||||
|
text: programEditor?.value ?? programEditorState.text,
|
||||||
|
metadata: { ...programEditorState.metadata },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function stopPlayback() {
|
function stopPlayback() {
|
||||||
if (playbackTimer) {
|
if (playbackTimer) {
|
||||||
clearInterval(playbackTimer);
|
clearInterval(playbackTimer);
|
||||||
@@ -809,6 +894,11 @@
|
|||||||
try {
|
try {
|
||||||
const state = await runRealBrowserSimulation({ documentRef: document, programId });
|
const state = await runRealBrowserSimulation({ documentRef: document, programId });
|
||||||
window.linuxCncRealSimulationState = state;
|
window.linuxCncRealSimulationState = state;
|
||||||
|
setProgramEditorText(state.programText, {
|
||||||
|
label: state.program?.label ?? "Built-in test program",
|
||||||
|
source: "builtin",
|
||||||
|
sourceLabel: state.program?.sourceLabel ?? "Built-in test program",
|
||||||
|
});
|
||||||
renderPlayback(0);
|
renderPlayback(0);
|
||||||
return state;
|
return state;
|
||||||
} finally {
|
} finally {
|
||||||
@@ -838,6 +928,7 @@
|
|||||||
filename: metadata.filename ?? null,
|
filename: metadata.filename ?? null,
|
||||||
};
|
};
|
||||||
window.linuxCncRealSimulationState = state;
|
window.linuxCncRealSimulationState = state;
|
||||||
|
setProgramEditorText(programText, state.program);
|
||||||
document.body.dataset.simulationProgramId = "custom";
|
document.body.dataset.simulationProgramId = "custom";
|
||||||
document.body.dataset.simulationProgramSource = state.program.source;
|
document.body.dataset.simulationProgramSource = state.program.source;
|
||||||
setAllText("[data-selected-program]", state.program.label);
|
setAllText("[data-selected-program]", state.program.label);
|
||||||
@@ -860,6 +951,15 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function runEditorProgramText() {
|
||||||
|
const { text, metadata } = getProgramEditorText();
|
||||||
|
return runProgramText(text, {
|
||||||
|
...metadata,
|
||||||
|
source: "editor",
|
||||||
|
sourceLabel: "Editor text",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const state = await runSelectedProgram(DEFAULT_SIMULATION_PROGRAM_ID);
|
const state = await runSelectedProgram(DEFAULT_SIMULATION_PROGRAM_ID);
|
||||||
window.linuxCncRealSimulationState = state;
|
window.linuxCncRealSimulationState = state;
|
||||||
@@ -872,6 +972,9 @@
|
|||||||
play: startPlayback,
|
play: startPlayback,
|
||||||
pause: stopPlayback,
|
pause: stopPlayback,
|
||||||
finishPlayback: () => renderPlayback(currentState()?.motion.length - 1 ?? 0),
|
finishPlayback: () => renderPlayback(currentState()?.motion.length - 1 ?? 0),
|
||||||
|
getProgramText: getProgramEditorText,
|
||||||
|
setProgramText: setProgramEditorText,
|
||||||
|
runEditorProgramText,
|
||||||
runProgramText,
|
runProgramText,
|
||||||
loadProgramFile,
|
loadProgramFile,
|
||||||
runProgramById: async (programId) => {
|
runProgramById: async (programId) => {
|
||||||
@@ -887,6 +990,24 @@
|
|||||||
document.querySelector("[data-canonical-output]").textContent = error?.stack ?? error?.message ?? String(error);
|
document.querySelector("[data-canonical-output]").textContent = error?.stack ?? error?.message ?? String(error);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
runEditorButton?.addEventListener("click", () => {
|
||||||
|
window.linuxCncRealSimulationApi.runEditorProgramText().catch((error) => {
|
||||||
|
document.body.dataset.simulationReady = "false";
|
||||||
|
setAllText("[data-simulation-status]", "failed");
|
||||||
|
document.querySelector("[data-canonical-output]").textContent = error?.stack ?? error?.message ?? String(error);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
programEditor?.addEventListener("input", () => {
|
||||||
|
programEditorState = {
|
||||||
|
text: programEditor.value,
|
||||||
|
metadata: {
|
||||||
|
...programEditorState.metadata,
|
||||||
|
source: "editor",
|
||||||
|
sourceLabel: "Editor text",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
setAllText("[data-program-editor-status]", "Editor text");
|
||||||
|
});
|
||||||
openProgramButton?.addEventListener("click", () => openProgramFile?.click());
|
openProgramButton?.addEventListener("click", () => openProgramFile?.click());
|
||||||
openProgramFile?.addEventListener("change", () => {
|
openProgramFile?.addEventListener("change", () => {
|
||||||
const file = openProgramFile.files?.[0];
|
const file = openProgramFile.files?.[0];
|
||||||
|
|||||||
@@ -78,6 +78,7 @@
|
|||||||
"preview-dro",
|
"preview-dro",
|
||||||
"active-codes",
|
"active-codes",
|
||||||
"gcode-pane",
|
"gcode-pane",
|
||||||
|
"program-editor",
|
||||||
"machine-state",
|
"machine-state",
|
||||||
"statusbar",
|
"statusbar",
|
||||||
]) {
|
]) {
|
||||||
@@ -133,6 +134,9 @@
|
|||||||
if (!api?.runProgramText || !api?.loadProgramFile) {
|
if (!api?.runProgramText || !api?.loadProgramFile) {
|
||||||
throw new Error("simulation API missing real G-code loading controls");
|
throw new Error("simulation API missing real G-code loading controls");
|
||||||
}
|
}
|
||||||
|
if (!api?.getProgramText || !api?.setProgramText || !api?.runEditorProgramText) {
|
||||||
|
throw new Error("simulation API missing editor text controls");
|
||||||
|
}
|
||||||
if (!api?.resetPlayback || !api?.stepPlayback || !api?.finishPlayback) {
|
if (!api?.resetPlayback || !api?.stepPlayback || !api?.finishPlayback) {
|
||||||
throw new Error("simulation API missing playback controls");
|
throw new Error("simulation API missing playback controls");
|
||||||
}
|
}
|
||||||
@@ -143,6 +147,13 @@
|
|||||||
if (!doc.querySelector("[data-open-program]") || !doc.querySelector("[data-open-program-file]")) {
|
if (!doc.querySelector("[data-open-program]") || !doc.querySelector("[data-open-program-file]")) {
|
||||||
throw new Error("simulation page missing open program controls");
|
throw new Error("simulation page missing open program controls");
|
||||||
}
|
}
|
||||||
|
if (!doc.querySelector("[data-program-editor]") || !doc.querySelector("[data-run-editor-program]")) {
|
||||||
|
throw new Error("simulation page missing editable G-code controls");
|
||||||
|
}
|
||||||
|
const initialEditor = api.getProgramText();
|
||||||
|
if (!initialEditor.text.includes("G1 X1 Y0 Z0 F100")) {
|
||||||
|
throw new Error("simulation editor did not receive initial built-in program text");
|
||||||
|
}
|
||||||
|
|
||||||
doc.querySelector('[data-axis-tab="mdi"]')?.click();
|
doc.querySelector('[data-axis-tab="mdi"]')?.click();
|
||||||
if (!doc.querySelector('[data-axis-panel="manual"]')?.hidden || doc.querySelector('[data-axis-panel="mdi"]')?.hidden) {
|
if (!doc.querySelector('[data-axis-panel="manual"]')?.hidden || doc.querySelector('[data-axis-panel="mdi"]')?.hidden) {
|
||||||
@@ -261,6 +272,9 @@
|
|||||||
if (!doc.querySelector('[data-axis-shell="statusbar"]')?.textContent.includes("Operator pasted G-code")) {
|
if (!doc.querySelector('[data-axis-shell="statusbar"]')?.textContent.includes("Operator pasted G-code")) {
|
||||||
throw new Error("custom text program source did not render in statusbar");
|
throw new Error("custom text program source did not render in statusbar");
|
||||||
}
|
}
|
||||||
|
if (!api.getProgramText().text.includes("G1 X2.5 Y0.5 Z0 F75")) {
|
||||||
|
throw new Error("custom text program did not sync into editable G-code pane");
|
||||||
|
}
|
||||||
|
|
||||||
const fileProgram = new frame.contentWindow.File(
|
const fileProgram = new frame.contentWindow.File(
|
||||||
[customProgramText.replace("X2.5", "X3.25")],
|
[customProgramText.replace("X2.5", "X3.25")],
|
||||||
@@ -275,6 +289,35 @@
|
|||||||
if (!fileState.resultText.includes("canon_event=STRAIGHT_FEED line=2 x=3.25 y=0.5 z=0")) {
|
if (!fileState.resultText.includes("canon_event=STRAIGHT_FEED line=2 x=3.25 y=0.5 z=0")) {
|
||||||
throw new Error("loaded file program did not execute changed G-code through LinuxCNC WASM");
|
throw new Error("loaded file program did not execute changed G-code through LinuxCNC WASM");
|
||||||
}
|
}
|
||||||
|
if (!api.getProgramText().metadata.filename || !api.getProgramText().text.includes("G1 X3.25 Y0.5 Z0 F75")) {
|
||||||
|
throw new Error("loaded file program did not sync into editor state");
|
||||||
|
}
|
||||||
|
|
||||||
|
api.setProgramText(
|
||||||
|
[
|
||||||
|
"G90 G17 G0 X0 Y0 Z0",
|
||||||
|
"G1 X4.75 Y2.25 Z0 F95",
|
||||||
|
"M2",
|
||||||
|
"",
|
||||||
|
].join("\n"),
|
||||||
|
{
|
||||||
|
label: "edited-in-axis-pane.ngc",
|
||||||
|
source: "editor",
|
||||||
|
sourceLabel: "Editor text",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
const editorState = api.getProgramText();
|
||||||
|
if (!editorState.text.includes("X4.75") || editorState.metadata.label !== "edited-in-axis-pane.ngc") {
|
||||||
|
throw new Error("simulation editable G-code state did not accept setProgramText");
|
||||||
|
}
|
||||||
|
const editorRunState = await api.runEditorProgramText();
|
||||||
|
assertRenderedState(doc, editorRunState);
|
||||||
|
if (!editorRunState.resultText.includes("canon_event=STRAIGHT_FEED line=2 x=4.75 y=2.25 z=0")) {
|
||||||
|
throw new Error("editor G-code did not execute changed text through LinuxCNC WASM");
|
||||||
|
}
|
||||||
|
if (!doc.querySelector("[data-program-editor]")?.value.includes("X4.75")) {
|
||||||
|
throw new Error("editor DOM did not retain edited G-code text");
|
||||||
|
}
|
||||||
|
|
||||||
status.textContent = "browser_real_simulation_page_smoke=ok";
|
status.textContent = "browser_real_simulation_page_smoke=ok";
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|||||||
Reference in New Issue
Block a user