完善 xyzbc-trt OPFS 与 AXIS 首屏等效
This commit is contained in:
@@ -93,7 +93,7 @@ async function ensureMachineFilesForProfile(store) {
|
||||
if (state.machineFileStaging?.status === "staged" && state.machineFileStaging?.profileId === state.machineProfile) {
|
||||
return state.machineFileStaging;
|
||||
}
|
||||
return await store.stageMachineFiles();
|
||||
return await store.stageMachineFiles({ requireOpfs: true });
|
||||
} catch (error) {
|
||||
return {
|
||||
apiName: "web-rtcp-5axis-machine-file-staging-seed",
|
||||
|
||||
@@ -184,18 +184,22 @@ export function detectBrowserStorageCapability(globalScope = globalThis) {
|
||||
|
||||
function resolveSessionStorage(options = {}) {
|
||||
if (options.storage) {
|
||||
const mode = options.storageMode || storageModeFor(options.storage);
|
||||
if (options.requireOpfs === true && mode !== "opfs") {
|
||||
throw new Error(`OPFS storage required for session snapshots, got ${mode}`);
|
||||
}
|
||||
return {
|
||||
storage: options.storage,
|
||||
mode: options.storageMode || storageModeFor(options.storage),
|
||||
mode,
|
||||
capability: {
|
||||
apiName: "web-rtcp-5axis-storage-capability",
|
||||
opfsAvailable: options.storageMode === "opfs",
|
||||
opfsUnavailable: options.storageMode !== "opfs",
|
||||
opfsAvailable: mode === "opfs",
|
||||
opfsUnavailable: mode !== "opfs",
|
||||
secureContext: true,
|
||||
forcedUnavailable: false,
|
||||
hasNavigatorStorage: false,
|
||||
hasGetDirectory: typeof options.storage.getDirectory === "function",
|
||||
fallbackMode: options.storageMode === "opfs" ? null : options.storageMode || "custom",
|
||||
fallbackMode: mode === "opfs" ? null : mode || "custom",
|
||||
reason: "explicit_storage",
|
||||
},
|
||||
};
|
||||
@@ -209,6 +213,9 @@ function resolveSessionStorage(options = {}) {
|
||||
capability,
|
||||
};
|
||||
}
|
||||
if (options.requireOpfs === true) {
|
||||
throw new Error(`OPFS storage required for session snapshots: ${capability.reason}`);
|
||||
}
|
||||
browserMemorySessionStorage ??= createMemorySessionStorage();
|
||||
return {
|
||||
storage: browserMemorySessionStorage,
|
||||
|
||||
@@ -241,15 +241,19 @@ export function detectMachineFileStorageCapability(globalScope = globalThis) {
|
||||
|
||||
function resolveMachineFileStorage(options = {}) {
|
||||
if (options.storage) {
|
||||
const mode = options.storageMode || storageModeFor(options.storage);
|
||||
if (options.requireOpfs === true && mode !== "opfs") {
|
||||
throw new Error(`OPFS storage required for machine files, got ${mode}`);
|
||||
}
|
||||
return {
|
||||
storage: options.storage,
|
||||
mode: options.storageMode || storageModeFor(options.storage),
|
||||
mode,
|
||||
capability: {
|
||||
apiName: "web-rtcp-5axis-machine-file-storage-capability",
|
||||
opfsAvailable: options.storageMode === "opfs",
|
||||
opfsUnavailable: options.storageMode !== "opfs",
|
||||
opfsAvailable: mode === "opfs",
|
||||
opfsUnavailable: mode !== "opfs",
|
||||
secureContext: true,
|
||||
fallbackMode: options.storageMode === "opfs" ? null : options.storageMode || "custom",
|
||||
fallbackMode: mode === "opfs" ? null : mode || "custom",
|
||||
reason: "explicit_storage",
|
||||
},
|
||||
};
|
||||
@@ -262,6 +266,9 @@ function resolveMachineFileStorage(options = {}) {
|
||||
capability,
|
||||
};
|
||||
}
|
||||
if (options.requireOpfs === true) {
|
||||
throw new Error(`OPFS storage required for machine files: ${capability.reason}`);
|
||||
}
|
||||
browserMemoryMachineFileStorage ??= createMemoryStorage();
|
||||
return {
|
||||
storage: browserMemoryMachineFileStorage,
|
||||
|
||||
@@ -998,14 +998,18 @@ function basename(path = "") {
|
||||
|
||||
function resolveToolDbStorage(options = {}) {
|
||||
if (options.storage) {
|
||||
const mode = options.storageMode || storageModeFor(options.storage);
|
||||
if (options.requireOpfs === true && mode !== "opfs") {
|
||||
throw new Error(`OPFS storage required for tool DB, got ${mode}`);
|
||||
}
|
||||
return {
|
||||
storage: options.storage,
|
||||
mode: options.storageMode || storageModeFor(options.storage),
|
||||
mode,
|
||||
capability: {
|
||||
apiName: "web-rtcp-5axis-tool-db-storage-capability",
|
||||
opfsAvailable: options.storageMode === "opfs",
|
||||
opfsUnavailable: options.storageMode !== "opfs",
|
||||
fallbackMode: options.storageMode === "opfs" ? null : options.storageMode || "custom",
|
||||
opfsAvailable: mode === "opfs",
|
||||
opfsUnavailable: mode !== "opfs",
|
||||
fallbackMode: mode === "opfs" ? null : mode || "custom",
|
||||
reason: "explicit_storage",
|
||||
},
|
||||
};
|
||||
@@ -1018,6 +1022,9 @@ function resolveToolDbStorage(options = {}) {
|
||||
capability,
|
||||
};
|
||||
}
|
||||
if (options.requireOpfs === true) {
|
||||
throw new Error(`OPFS storage required for tool DB: ${capability.reason}`);
|
||||
}
|
||||
browserMemoryToolDbStorage ??= createMemoryToolDbStorage();
|
||||
return {
|
||||
storage: browserMemoryToolDbStorage,
|
||||
|
||||
@@ -99,6 +99,14 @@ function createTaskHalStatusLoopState({
|
||||
};
|
||||
}
|
||||
|
||||
function withDefaultWebOpfsRequirement(options = {}) {
|
||||
if (options.storage || options.requireOpfs !== undefined) {
|
||||
return options;
|
||||
}
|
||||
const isBrowser = typeof globalThis.window === "object" || typeof globalThis.document === "object";
|
||||
return isBrowser ? { ...options, requireOpfs: true } : options;
|
||||
}
|
||||
|
||||
const initialState = {
|
||||
machineProfile: "xyzbc-trt",
|
||||
availableProfiles: fiveAxisProfiles.map(({ id, title, traj, kinematicsModuleId, kinematics }) => ({
|
||||
@@ -2061,13 +2069,15 @@ export function createSimulationStore(seed = {}) {
|
||||
const saveSession = async (options = {}) => {
|
||||
dispatch({ type: "SESSION_SAVE_STARTED" });
|
||||
try {
|
||||
const storageOptions = withDefaultWebOpfsRequirement(options);
|
||||
const sessionId = options.sessionId || state.sessionPersistence.sessionId;
|
||||
const filename = options.filename || state.sessionPersistence.filename;
|
||||
const payload = createFiveAxisSessionPayload(state);
|
||||
const { snapshot, path, storageMode, storageCapability } = await saveFiveAxisSessionSnapshot(sessionId, payload, {
|
||||
filename,
|
||||
storage: options.storage,
|
||||
storageMode: options.storageMode,
|
||||
storage: storageOptions.storage,
|
||||
storageMode: storageOptions.storageMode,
|
||||
requireOpfs: storageOptions.requireOpfs,
|
||||
metadata: options.metadata,
|
||||
});
|
||||
dispatch({
|
||||
@@ -2087,12 +2097,14 @@ export function createSimulationStore(seed = {}) {
|
||||
const restoreSession = async (options = {}) => {
|
||||
dispatch({ type: "SESSION_RESTORE_STARTED" });
|
||||
try {
|
||||
const storageOptions = withDefaultWebOpfsRequirement(options);
|
||||
const sessionId = options.sessionId || state.sessionPersistence.sessionId;
|
||||
const filename = options.filename || state.sessionPersistence.filename;
|
||||
const { snapshot, path, storageMode, storageCapability } = await loadFiveAxisSessionSnapshot(sessionId, {
|
||||
filename,
|
||||
storage: options.storage,
|
||||
storageMode: options.storageMode,
|
||||
storage: storageOptions.storage,
|
||||
storageMode: storageOptions.storageMode,
|
||||
requireOpfs: storageOptions.requireOpfs,
|
||||
});
|
||||
dispatch({
|
||||
type: "SESSION_RESTORE_COMPLETE",
|
||||
@@ -2113,8 +2125,10 @@ export function createSimulationStore(seed = {}) {
|
||||
const stageMachineFiles = async (options = {}) => {
|
||||
dispatch({ type: "MACHINE_FILE_STAGING_STARTED" });
|
||||
try {
|
||||
const storageOptions = withDefaultWebOpfsRequirement(options);
|
||||
const { plan, save } = await stageProfileMachineFiles(state.profile, {
|
||||
...options,
|
||||
...storageOptions,
|
||||
iniText: options.iniText || state.linuxCncIniConfig?.sourceText,
|
||||
});
|
||||
dispatch({ type: "MACHINE_FILE_STAGING_COMPLETE", plan, save });
|
||||
@@ -2153,7 +2167,7 @@ export function createSimulationStore(seed = {}) {
|
||||
if (!state.toolDbSimulation) {
|
||||
throw new Error("tool DB simulation is not ready");
|
||||
}
|
||||
const save = await saveToolDbSimulation(state.toolDbSimulation, options);
|
||||
const save = await saveToolDbSimulation(state.toolDbSimulation, withDefaultWebOpfsRequirement(options));
|
||||
setState({
|
||||
toolDbSimulation: save.toolDb,
|
||||
toolDbReadiness: createToolDbReadiness(save.toolDb),
|
||||
|
||||
@@ -754,6 +754,38 @@ button[data-icon-name] span {
|
||||
background: #eeeae3;
|
||||
}
|
||||
|
||||
.axis-first-screen-strip {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(5, minmax(96px, 1fr));
|
||||
gap: 3px;
|
||||
padding: 5px 6px;
|
||||
border-bottom: 1px solid #aaa59b;
|
||||
background: #151515;
|
||||
color: #e9f8e9;
|
||||
font: 11px/1.2 "Courier New", monospace;
|
||||
}
|
||||
|
||||
.axis-first-screen-strip span {
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
padding: 3px 5px;
|
||||
border: 1px solid #3e5f43;
|
||||
background: #071108;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.axis-first-screen-strip b {
|
||||
color: var(--green);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.axis-first-screen-strip [data-axis-ui-capability-ready="false"] {
|
||||
border-color: #7a6840;
|
||||
background: #1b1508;
|
||||
color: #f0d58c;
|
||||
}
|
||||
|
||||
.tabs {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr 1fr;
|
||||
@@ -1074,6 +1106,10 @@ button[data-icon-name] span {
|
||||
.spindle h2 {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.axis-first-screen-strip {
|
||||
grid-template-columns: repeat(2, minmax(90px, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-height: 720px) {
|
||||
|
||||
@@ -50,6 +50,7 @@ export function mountGmoccapyShell(root, store) {
|
||||
}
|
||||
|
||||
function render(regions, state, dispatch) {
|
||||
renderShellEquivalenceDataset(regions, state);
|
||||
renderTitlebar(regions.titlebar, state);
|
||||
renderPreview(regions.preview, state, dispatch);
|
||||
renderDro(regions.dro, state);
|
||||
@@ -61,6 +62,26 @@ function render(regions, state, dispatch) {
|
||||
renderBottomControls(regions["bottom-controls"], state, dispatch);
|
||||
}
|
||||
|
||||
function renderShellEquivalenceDataset(regions, state) {
|
||||
const sectionMap = {
|
||||
preview: "preview-toolpath-and-machine-model",
|
||||
dro: "coordinates-dro",
|
||||
gcode: "program-and-mdi",
|
||||
"status-sidebar": "status-mode-and-switchkins",
|
||||
"info-tabs": "execution-tool-and-runtime-state",
|
||||
override: "feed-rapid-override",
|
||||
"spindle-coolant": "spindle-coolant",
|
||||
"bottom-controls": "run-jog-home-controls",
|
||||
};
|
||||
for (const [name, section] of Object.entries(sectionMap)) {
|
||||
const element = regions[name];
|
||||
if (!element) continue;
|
||||
element.dataset.axisUiSection = section;
|
||||
element.dataset.axisUiProfile = state.machineProfile;
|
||||
element.dataset.axisUiCoordinates = state.profile?.traj?.coordinates || "";
|
||||
}
|
||||
}
|
||||
|
||||
function renderTitlebar(element, state) {
|
||||
const currentLine = currentGcodeExecutionLine(state);
|
||||
|
||||
@@ -599,7 +620,20 @@ function renderInfoTabs(element, state) {
|
||||
const parityMatrix = state.linuxCncParityMatrix;
|
||||
const commSummary = createGmoccapyCommunicationSummary();
|
||||
const halSummary = createGmoccapyHalSummary();
|
||||
const firstScreen = createAxisFirstScreenSummary(state);
|
||||
element.innerHTML = `
|
||||
<section
|
||||
class="axis-first-screen-strip"
|
||||
data-axis-main-equivalence="${escapeHtml(firstScreen.semanticBoundary)}"
|
||||
data-axis-first-screen-ready="${firstScreen.ready ? "true" : "false"}"
|
||||
data-axis-first-screen-missing="${escapeHtml(firstScreen.missing.join(","))}"
|
||||
>
|
||||
${firstScreen.items.map((item) => `
|
||||
<span data-axis-ui-capability="${escapeHtml(item.id)}" data-axis-ui-capability-ready="${item.ready ? "true" : "false"}">
|
||||
<b>${escapeHtml(item.label)}</b>${escapeHtml(item.value)}
|
||||
</span>
|
||||
`).join("")}
|
||||
</section>
|
||||
<nav class="tabs">
|
||||
<button type="button" class="active">Tool info and G-codes</button>
|
||||
<button type="button">G-code properties</button>
|
||||
@@ -690,6 +724,84 @@ function renderInfoTabs(element, state) {
|
||||
`;
|
||||
}
|
||||
|
||||
function createAxisFirstScreenSummary(state) {
|
||||
const currentLine = currentGcodeExecutionLine(state);
|
||||
const taskPolicy = state.linuxCncTaskPolicy || {};
|
||||
const taskHalReady = state.taskHalRuntimeReadiness?.loaded === true
|
||||
&& state.taskHalRuntimeReadiness?.taskRuntimeReady === true
|
||||
&& state.taskHalRuntimeReadiness?.motionRuntimeReady === true
|
||||
&& state.taskHalRuntimeReadiness?.halRuntimeReady === true;
|
||||
const items = [
|
||||
{
|
||||
id: "program",
|
||||
label: "Program ",
|
||||
ready: Boolean(state.activeProgram),
|
||||
value: `${state.activeProgram || "-"} L${currentLine}`,
|
||||
},
|
||||
{
|
||||
id: "coordinates",
|
||||
label: "XYZBC ",
|
||||
ready: state.profile?.traj?.coordinates === "XYZBC",
|
||||
value: `${formatNumber(state.dro.x)} ${formatNumber(state.dro.y)} ${formatNumber(state.dro.z)} B${formatNumber(state.dro.b)} C${formatNumber(state.dro.c)}`,
|
||||
},
|
||||
{
|
||||
id: "status",
|
||||
label: "State ",
|
||||
ready: Boolean(state.machine?.taskState),
|
||||
value: `${state.machine.taskState}/${state.machine.mode}/${state.runState}`,
|
||||
},
|
||||
{
|
||||
id: "mdi",
|
||||
label: "MDI ",
|
||||
ready: Boolean(state.machine?.mdiCommand),
|
||||
value: state.machine.mdiCommand || "-",
|
||||
},
|
||||
{
|
||||
id: "switchkins",
|
||||
label: "KINS ",
|
||||
ready: state.profile?.kinematicsParameters?.switchkinsTypes?.length > 0,
|
||||
value: `${state.kinsType} ${formatSwitchkinsSummary(state.programExecution)}`,
|
||||
},
|
||||
{
|
||||
id: "override",
|
||||
label: "Override ",
|
||||
ready: Number.isFinite(Number(state.feed?.feedOverride)) && Number.isFinite(Number(state.feed?.rapidOverride)),
|
||||
value: `F${state.feed.feedOverride}% R${state.feed.rapidOverride}%`,
|
||||
},
|
||||
{
|
||||
id: "tool",
|
||||
label: "Tool ",
|
||||
ready: Boolean(state.toolPreview?.toolNumber),
|
||||
value: `T${state.toolPreview.toolNumber} D${formatNumber(state.toolPreview.diameter, 2)} L${formatNumber(state.toolPreview.length, 3)}`,
|
||||
},
|
||||
{
|
||||
id: "preview",
|
||||
label: "Preview ",
|
||||
ready: state.preview?.pathPoints > 0,
|
||||
value: `${state.preview?.pathPoints || 0} pts ${state.programExecutionSourceMode}`,
|
||||
},
|
||||
{
|
||||
id: "execution",
|
||||
label: "Execution ",
|
||||
ready: taskHalReady || Boolean(state.taskHalStatus) || Boolean(state.programRuntimeFeedback),
|
||||
value: taskHalReady ? "task/HAL ready" : (state.programRuntimeFeedback ? "runtime feedback" : "pending"),
|
||||
},
|
||||
{
|
||||
id: "axis-buttons",
|
||||
label: "Panel ",
|
||||
ready: taskPolicy.canExecuteMdi !== undefined && taskPolicy.canRunAuto !== undefined,
|
||||
value: `${taskPolicy.canRunAuto ? "AUTO" : "AUTO gated"} ${taskPolicy.canExecuteMdi ? "MDI" : "MDI gated"} ${taskPolicy.canJog ? "JOG" : "JOG gated"}`,
|
||||
},
|
||||
];
|
||||
const missing = items.filter((item) => !item.ready).map((item) => item.id);
|
||||
return {
|
||||
semanticBoundary: "xyzbc_trt_axis_first_screen_program_coordinates_status_mdi_switchkins_override_tool_preview_execution",
|
||||
ready: missing.length === 0,
|
||||
missing,
|
||||
items,
|
||||
};
|
||||
}
|
||||
|
||||
function formatLinuxCncTaskGates(taskPolicy) {
|
||||
if (!taskPolicy) return "pending";
|
||||
return [
|
||||
|
||||
Reference in New Issue
Block a user