虚拟HAL完全满足Web数控仿真
结论:real simulation page 已将保存的 virtual HAL session snapshot 联动到 release diagnostics artifact,保存或恢复后的 Web CNC 仿真会话可直接输出 release-ready virtual HAL source、sim-config、halcmd fixture 与 motion matrix evidence;该能力仍限于 Web 仿真替代层,不声明 Linux kernel hard-realtime ABI、外部硬件驱动 ABI 或 native HAL module ABI。
This commit is contained in:
77
text17.txt
77
text17.txt
@@ -1113,3 +1113,80 @@ git diff --check ok
|
|||||||
让用户可在页面上输入 release artifact URL 与 browser diagnostics URL 后直接运行验证。
|
让用户可在页面上输入 release artifact URL 与 browser diagnostics URL 后直接运行验证。
|
||||||
3. 为 motion matrix 增加 manifest-backed source check,把 matrix 关联的 sim config
|
3. 为 motion matrix 增加 manifest-backed source check,把 matrix 关联的 sim config
|
||||||
source files 与 vendored LinuxCNC manifest 做显式一致性验证。
|
source files 与 vendored LinuxCNC manifest 做显式一致性验证。
|
||||||
|
|
||||||
|
### 十二、2026-06-17 Virtual HAL Session Diagnostics Artifact Linkage
|
||||||
|
|
||||||
|
继续按本文件铁律推进:虚拟 HAL 的功能完全来源于 LinuxCNC 源程序,并且虚拟 HAL
|
||||||
|
完全满足 Web 方式数控系统仿真系统。本批完成上一节下一步第 1 项,把 real
|
||||||
|
simulation page 保存的 virtual HAL session snapshot 与 release diagnostics artifact 做联动,
|
||||||
|
使保存后的 Web CNC 仿真会话可直接输出 release-ready diagnostics evidence。
|
||||||
|
|
||||||
|
新增实质能力:
|
||||||
|
|
||||||
|
- real simulation page 新增 session diagnostics artifact builder:
|
||||||
|
- `createVirtualHalSessionDiagnosticsArtifact(snapshot)`;
|
||||||
|
- 从保存的 session snapshot 读取 `payload.virtualHal`;
|
||||||
|
- 复用 snapshot 中的 virtual HAL state、simulation replacement、source compliance、
|
||||||
|
sim-config coverage、halcmd fixtures、motion controller matrix;
|
||||||
|
- 使用 `createProjectReleaseBrowserDiagnosticsArtifactValidation()` 对 snapshot
|
||||||
|
派生 diagnostics artifact 做 release 级验证;
|
||||||
|
- `exportDiagnosticsArtifact()` 新增:
|
||||||
|
- `virtualHalSessionDiagnostics`;
|
||||||
|
- 未保存 session 时返回 `phase: "not-saved"` 和 missing reason;
|
||||||
|
- 已保存或恢复 session 后返回 `phase: "release-ready"`;
|
||||||
|
- Browser simulation API 新增:
|
||||||
|
- `exportVirtualHalSessionDiagnosticsArtifact()`;
|
||||||
|
- `saveVirtualHalSessionSnapshot()` 返回值新增:
|
||||||
|
- `releaseDiagnosticsReady`;
|
||||||
|
- `sessionDiagnostics`;
|
||||||
|
- `restoreVirtualHalSessionSnapshot()` 返回值新增:
|
||||||
|
- `releaseDiagnosticsReady`;
|
||||||
|
- `sessionDiagnostics`;
|
||||||
|
- AXIS simulation docs 同步公开
|
||||||
|
`exportVirtualHalSessionDiagnosticsArtifact()`,并说明 saved virtual HAL session
|
||||||
|
snapshot 会进入 browser diagnostics artifact,形成 release-ready evidence。
|
||||||
|
|
||||||
|
新增/扩展验证:
|
||||||
|
|
||||||
|
- `real_simulation_page_smoke.html` 覆盖:
|
||||||
|
- 页面 API 暴露 `exportVirtualHalSessionDiagnosticsArtifact()`;
|
||||||
|
- 保存 virtual HAL session 后 `releaseDiagnosticsReady === true`;
|
||||||
|
- 保存返回的 `sessionDiagnostics.validation.ready === true`;
|
||||||
|
- 保存返回的 session diagnostics 包含 complete motion controller matrix;
|
||||||
|
- `exportVirtualHalSessionDiagnosticsArtifact()` 输出 `phase === "release-ready"`;
|
||||||
|
- `exportDiagnosticsArtifact()` 内嵌 `virtualHalSessionDiagnostics.ready === true`;
|
||||||
|
- restore 后 session diagnostics 仍保持 release-ready;
|
||||||
|
- `verify_axis_style_simulation_docs.mjs` 覆盖新 API 文档契约;
|
||||||
|
- `verify_sdk_surface.sh` 继续覆盖 release diagnostics validation 与 virtual HAL source
|
||||||
|
evidence。
|
||||||
|
|
||||||
|
关键验证:
|
||||||
|
|
||||||
|
```text
|
||||||
|
axis_style_simulation_docs_node_smoke=ok
|
||||||
|
browser_real_simulation_page_smoke=ok
|
||||||
|
sdk_surface_node_smoke=ok
|
||||||
|
project_release_gate_manifest_node_smoke=ok
|
||||||
|
project_release_artifact_url_workflow_node_smoke=ok
|
||||||
|
project_batch_acceptance_workflow_node_smoke=ok
|
||||||
|
project_batch_acceptance_artifact_node_smoke=ok
|
||||||
|
git diff --check ok
|
||||||
|
```
|
||||||
|
|
||||||
|
语义边界:
|
||||||
|
|
||||||
|
- 本批没有新增 G-code/interpreter/planner/kinematics/remap 语义;
|
||||||
|
- session diagnostics artifact 只复用保存时已有的 source-derived virtual HAL reports;
|
||||||
|
- release validation 只验证 Web 仿真所需 HAL、halcmd、motion controller evidence;
|
||||||
|
- virtual HAL 仍不声称提供 Linux kernel hard-realtime ABI、外部硬件驱动 ABI 或 native
|
||||||
|
HAL module ABI;
|
||||||
|
- G-code/interpreter/planner/canonical motion 语义仍来自 LinuxCNC-backed WASM。
|
||||||
|
|
||||||
|
下一步工作内容:
|
||||||
|
|
||||||
|
1. 将 ini-panel workflow overview 的 diagnostics URL workflow 接入固定页面表单/按钮,
|
||||||
|
让用户可在页面上输入 release artifact URL 与 browser diagnostics URL 后直接运行验证。
|
||||||
|
2. 为 motion matrix 增加 manifest-backed source check,把 matrix 关联的 sim config
|
||||||
|
source files 与 vendored LinuxCNC manifest 做显式一致性验证。
|
||||||
|
3. 将 virtual HAL session diagnostics artifact 纳入 release artifact URL workflow 的示例
|
||||||
|
fixtures,使 URL workflow 同时覆盖 live diagnostics 与 saved-session diagnostics。
|
||||||
|
|||||||
@@ -373,7 +373,11 @@ Current implemented behavior:
|
|||||||
state;
|
state;
|
||||||
- browser API exposes `getDiagnosticsState()`, `getStatusHistory()`,
|
- browser API exposes `getDiagnosticsState()`, `getStatusHistory()`,
|
||||||
`getToolTableSummary()`, `getLimitsHomeState()`,
|
`getToolTableSummary()`, `getLimitsHomeState()`,
|
||||||
`exportDiagnosticsArtifact()`, and `handleAxisShortcut(event)`;
|
`exportDiagnosticsArtifact()`, `exportVirtualHalSessionDiagnosticsArtifact()`,
|
||||||
|
and `handleAxisShortcut(event)`;
|
||||||
|
- saved virtual HAL session snapshots are linked back into the browser
|
||||||
|
diagnostics artifact as release-ready virtual HAL source, sim-config,
|
||||||
|
halcmd-fixture, and motion-matrix evidence;
|
||||||
- browser smoke covers run and play/pause keyboard shortcuts after waiting for
|
- browser smoke covers run and play/pause keyboard shortcuts after waiting for
|
||||||
their async UI/API side effects;
|
their async UI/API side effects;
|
||||||
- browser smoke verifies blocked readiness before OPFS files exist, then writes
|
- browser smoke verifies blocked readiness before OPFS files exist, then writes
|
||||||
@@ -458,6 +462,7 @@ Required methods:
|
|||||||
- `getToolTableSummary()`;
|
- `getToolTableSummary()`;
|
||||||
- `getLimitsHomeState()`;
|
- `getLimitsHomeState()`;
|
||||||
- `exportDiagnosticsArtifact()`;
|
- `exportDiagnosticsArtifact()`;
|
||||||
|
- `exportVirtualHalSessionDiagnosticsArtifact()`;
|
||||||
- `handleAxisShortcut(event)`;
|
- `handleAxisShortcut(event)`;
|
||||||
- `applyAxisViewFromUrl(search)`;
|
- `applyAxisViewFromUrl(search)`;
|
||||||
|
|
||||||
|
|||||||
@@ -1800,6 +1800,7 @@ M2
|
|||||||
import {
|
import {
|
||||||
createLinuxCncIniSdk,
|
createLinuxCncIniSdk,
|
||||||
createLinuxCncInterpSdk,
|
createLinuxCncInterpSdk,
|
||||||
|
createProjectReleaseBrowserDiagnosticsArtifactValidation,
|
||||||
} from "../../sdk/src/index.js";
|
} from "../../sdk/src/index.js";
|
||||||
import {
|
import {
|
||||||
DEFAULT_SIMULATION_PROGRAM_ID,
|
DEFAULT_SIMULATION_PROGRAM_ID,
|
||||||
@@ -1921,6 +1922,7 @@ M2
|
|||||||
status: "Virtual HAL session not saved",
|
status: "Virtual HAL session not saved",
|
||||||
revision: 0,
|
revision: 0,
|
||||||
restored: false,
|
restored: false,
|
||||||
|
releaseDiagnosticsReady: false,
|
||||||
};
|
};
|
||||||
let machineReadinessState = {
|
let machineReadinessState = {
|
||||||
machineId: "axis-browser-sim",
|
machineId: "axis-browser-sim",
|
||||||
@@ -2613,6 +2615,7 @@ M2
|
|||||||
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
|
virtualHalSimConfigSourceCoverage: createVirtualHalSimConfigSourceCoverageReport(),
|
||||||
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport(),
|
virtualHalCommandScriptFixtures: createVirtualHalCommandScriptFixtureReport(),
|
||||||
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport(),
|
virtualHalMotionControllerMatrix: createVirtualHalMotionControllerMatrixReport(),
|
||||||
|
virtualHalSessionDiagnostics: createVirtualHalSessionDiagnosticsArtifact(),
|
||||||
statusHistory: getStatusHistory(),
|
statusHistory: getStatusHistory(),
|
||||||
mdiHistory: getMdiHistory(),
|
mdiHistory: getMdiHistory(),
|
||||||
recentPrograms: getRecentPrograms(),
|
recentPrograms: getRecentPrograms(),
|
||||||
@@ -2627,6 +2630,44 @@ M2
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createVirtualHalSessionDiagnosticsArtifact(snapshot = virtualHalSessionState.snapshot ?? null) {
|
||||||
|
if (!snapshot?.payload?.virtualHal) {
|
||||||
|
return {
|
||||||
|
apiName: "real-browser-simulation-virtual-hal-session-diagnostics",
|
||||||
|
artifactVersion: 1,
|
||||||
|
ready: false,
|
||||||
|
phase: "not-saved",
|
||||||
|
missing: ["virtualHalSessionSnapshot"],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
const virtualHal = snapshot.payload.virtualHal;
|
||||||
|
const diagnosticsArtifact = {
|
||||||
|
apiName: "real-browser-simulation-diagnostics-artifact",
|
||||||
|
artifactVersion: 1,
|
||||||
|
virtualHal: virtualHal.state,
|
||||||
|
virtualHalSimulationReplacement: virtualHal.diagnostics,
|
||||||
|
virtualHalSourceCompliance: virtualHal.sourceCompliance,
|
||||||
|
virtualHalSimConfigSourceCoverage: virtualHal.simConfigSourceCoverage,
|
||||||
|
virtualHalCommandScriptFixtures: virtualHal.commandScriptFixtures,
|
||||||
|
virtualHalMotionControllerMatrix: virtualHal.motionControllerMatrix,
|
||||||
|
};
|
||||||
|
const validation = createProjectReleaseBrowserDiagnosticsArtifactValidation(diagnosticsArtifact);
|
||||||
|
return {
|
||||||
|
apiName: "real-browser-simulation-virtual-hal-session-diagnostics",
|
||||||
|
artifactVersion: 1,
|
||||||
|
ready: validation.ready === true,
|
||||||
|
phase: validation.ready === true ? "release-ready" : "blocked",
|
||||||
|
sessionId: snapshot.sessionId,
|
||||||
|
machineId: snapshot.payload.machineId,
|
||||||
|
createdAt: snapshot.createdAt,
|
||||||
|
metadata: { ...(snapshot.metadata ?? {}) },
|
||||||
|
files: { ...(snapshot.payload.files ?? {}) },
|
||||||
|
virtualHalRevision: virtualHal.state.revision,
|
||||||
|
validation,
|
||||||
|
diagnosticsArtifact,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
function recordError(error, context = "runtime") {
|
function recordError(error, context = "runtime") {
|
||||||
const message = `${context}: ${error?.message ?? error}`;
|
const message = `${context}: ${error?.message ?? error}`;
|
||||||
renderDiagnostics({ lastError: message });
|
renderDiagnostics({ lastError: message });
|
||||||
@@ -3549,6 +3590,7 @@ M2
|
|||||||
virtualHalRevision: virtualHal.state.revision,
|
virtualHalRevision: virtualHal.state.revision,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
const sessionDiagnostics = createVirtualHalSessionDiagnosticsArtifact(snapshot);
|
||||||
pushStatusHistory("hal-session", `saved ${session.sessionId}/${session.filename}`);
|
pushStatusHistory("hal-session", `saved ${session.sessionId}/${session.filename}`);
|
||||||
return setVirtualHalSessionState({
|
return setVirtualHalSessionState({
|
||||||
ready: true,
|
ready: true,
|
||||||
@@ -3559,6 +3601,8 @@ M2
|
|||||||
status: `Saved HAL session: linuxcnc/sessions/${session.sessionId}/${session.filename}`,
|
status: `Saved HAL session: linuxcnc/sessions/${session.sessionId}/${session.filename}`,
|
||||||
revision: virtualHal.state.revision,
|
revision: virtualHal.state.revision,
|
||||||
restored: false,
|
restored: false,
|
||||||
|
releaseDiagnosticsReady: sessionDiagnostics.ready === true,
|
||||||
|
sessionDiagnostics,
|
||||||
snapshot,
|
snapshot,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -3571,6 +3615,7 @@ M2
|
|||||||
virtualHalState = restoreVirtualHalStateFromSessionSnapshot(snapshot);
|
virtualHalState = restoreVirtualHalStateFromSessionSnapshot(snapshot);
|
||||||
renderVirtualHalState();
|
renderVirtualHalState();
|
||||||
renderAxisStatusbar();
|
renderAxisStatusbar();
|
||||||
|
const sessionDiagnostics = createVirtualHalSessionDiagnosticsArtifact(snapshot);
|
||||||
pushStatusHistory("hal-session", `restored ${session.sessionId}/${session.filename}`);
|
pushStatusHistory("hal-session", `restored ${session.sessionId}/${session.filename}`);
|
||||||
return setVirtualHalSessionState({
|
return setVirtualHalSessionState({
|
||||||
ready: true,
|
ready: true,
|
||||||
@@ -3581,6 +3626,8 @@ M2
|
|||||||
status: `Restored HAL session: linuxcnc/sessions/${session.sessionId}/${session.filename}`,
|
status: `Restored HAL session: linuxcnc/sessions/${session.sessionId}/${session.filename}`,
|
||||||
revision: virtualHalState.revision,
|
revision: virtualHalState.revision,
|
||||||
restored: true,
|
restored: true,
|
||||||
|
releaseDiagnosticsReady: sessionDiagnostics.ready === true,
|
||||||
|
sessionDiagnostics,
|
||||||
snapshot,
|
snapshot,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -3975,6 +4022,7 @@ M2
|
|||||||
getVirtualHalSessionState: () => ({ ...virtualHalSessionState }),
|
getVirtualHalSessionState: () => ({ ...virtualHalSessionState }),
|
||||||
saveVirtualHalSessionSnapshot,
|
saveVirtualHalSessionSnapshot,
|
||||||
restoreVirtualHalSessionSnapshot,
|
restoreVirtualHalSessionSnapshot,
|
||||||
|
exportVirtualHalSessionDiagnosticsArtifact: () => createVirtualHalSessionDiagnosticsArtifact(),
|
||||||
getRunMode: () => syncRunMode(),
|
getRunMode: () => syncRunMode(),
|
||||||
getRunControlState: () => renderRunControlState(),
|
getRunControlState: () => renderRunControlState(),
|
||||||
getAxisStatusbarState: () => renderAxisStatusbar(),
|
getAxisStatusbarState: () => renderAxisStatusbar(),
|
||||||
|
|||||||
@@ -252,7 +252,7 @@
|
|||||||
if (!api?.getMachineSessionLoadState || !api?.loadReadyMachineSession) {
|
if (!api?.getMachineSessionLoadState || !api?.loadReadyMachineSession) {
|
||||||
throw new Error("simulation API missing AXIS machine/session load controls");
|
throw new Error("simulation API missing AXIS machine/session load controls");
|
||||||
}
|
}
|
||||||
if (!api?.getVirtualHalSessionState || !api?.saveVirtualHalSessionSnapshot || !api?.restoreVirtualHalSessionSnapshot) {
|
if (!api?.getVirtualHalSessionState || !api?.saveVirtualHalSessionSnapshot || !api?.restoreVirtualHalSessionSnapshot || !api?.exportVirtualHalSessionDiagnosticsArtifact) {
|
||||||
throw new Error("simulation API missing virtual HAL session persistence controls");
|
throw new Error("simulation API missing virtual HAL session persistence controls");
|
||||||
}
|
}
|
||||||
if (!api?.getRunMode || !api?.getRunControlState || !api?.setUseLoadedSession || !api?.getRunSummary) {
|
if (!api?.getRunMode || !api?.getRunControlState || !api?.setUseLoadedSession || !api?.getRunSummary) {
|
||||||
@@ -747,11 +747,29 @@
|
|||||||
savedVirtualHalSession.revision < virtualHalcmdResult.state.revision ||
|
savedVirtualHalSession.revision < virtualHalcmdResult.state.revision ||
|
||||||
!savedVirtualHalSession.snapshot?.payload?.virtualHal?.sourceCompliance?.complete ||
|
!savedVirtualHalSession.snapshot?.payload?.virtualHal?.sourceCompliance?.complete ||
|
||||||
!savedVirtualHalSession.snapshot?.payload?.virtualHal?.motionControllerMatrix?.complete ||
|
!savedVirtualHalSession.snapshot?.payload?.virtualHal?.motionControllerMatrix?.complete ||
|
||||||
|
savedVirtualHalSession.releaseDiagnosticsReady !== true ||
|
||||||
|
savedVirtualHalSession.sessionDiagnostics?.ready !== true ||
|
||||||
|
savedVirtualHalSession.sessionDiagnostics?.validation?.ready !== true ||
|
||||||
|
savedVirtualHalSession.sessionDiagnostics?.validation?.motionControllerMatrixReady !== true ||
|
||||||
|
savedVirtualHalSession.sessionDiagnostics?.diagnosticsArtifact?.virtualHalMotionControllerMatrix?.complete !== true ||
|
||||||
doc.body.dataset.virtualHalSessionPhase !== "saved" ||
|
doc.body.dataset.virtualHalSessionPhase !== "saved" ||
|
||||||
!doc.querySelector("[data-virtual-hal-session-status]")?.textContent.includes("Saved HAL session")
|
!doc.querySelector("[data-virtual-hal-session-status]")?.textContent.includes("Saved HAL session")
|
||||||
) {
|
) {
|
||||||
throw new Error(`AXIS-style virtual HAL session save failed: ${JSON.stringify(savedVirtualHalSession)}`);
|
throw new Error(`AXIS-style virtual HAL session save failed: ${JSON.stringify(savedVirtualHalSession)}`);
|
||||||
}
|
}
|
||||||
|
const savedSessionDiagnosticsArtifact = api.exportVirtualHalSessionDiagnosticsArtifact();
|
||||||
|
const diagnosticsWithSavedSession = api.exportDiagnosticsArtifact();
|
||||||
|
if (
|
||||||
|
savedSessionDiagnosticsArtifact.ready !== true ||
|
||||||
|
savedSessionDiagnosticsArtifact.phase !== "release-ready" ||
|
||||||
|
savedSessionDiagnosticsArtifact.sessionId !== "axis-browser-virtual-hal-session" ||
|
||||||
|
savedSessionDiagnosticsArtifact.validation?.ready !== true ||
|
||||||
|
diagnosticsWithSavedSession.virtualHalSessionDiagnostics?.ready !== true ||
|
||||||
|
diagnosticsWithSavedSession.virtualHalSessionDiagnostics?.validation?.ready !== true ||
|
||||||
|
diagnosticsWithSavedSession.virtualHalSessionDiagnostics?.diagnosticsArtifact?.virtualHalSourceCompliance?.complete !== true
|
||||||
|
) {
|
||||||
|
throw new Error(`AXIS-style virtual HAL session diagnostics did not become release-ready: ${JSON.stringify(diagnosticsWithSavedSession.virtualHalSessionDiagnostics)}`);
|
||||||
|
}
|
||||||
api.executeVirtualHalcmd("setp axis.x.pos-cmd 9.5\nloadusr post-save-component");
|
api.executeVirtualHalcmd("setp axis.x.pos-cmd 9.5\nloadusr post-save-component");
|
||||||
if (api.readVirtualHalPin("axis.x.pos-cmd")?.value !== 9.5) {
|
if (api.readVirtualHalPin("axis.x.pos-cmd")?.value !== 9.5) {
|
||||||
throw new Error("AXIS-style virtual HAL pre-restore mutation did not apply");
|
throw new Error("AXIS-style virtual HAL pre-restore mutation did not apply");
|
||||||
@@ -766,6 +784,9 @@
|
|||||||
api.readVirtualHalPin("axis.x.pos-cmd")?.value !== 1.75 ||
|
api.readVirtualHalPin("axis.x.pos-cmd")?.value !== 1.75 ||
|
||||||
api.getVirtualHalState().hal.loadedComponents.some(({ component }) => component === "post-save-component") ||
|
api.getVirtualHalState().hal.loadedComponents.some(({ component }) => component === "post-save-component") ||
|
||||||
!api.getVirtualHalState().hal.functions.some(({ name }) => name === "motion-controller") ||
|
!api.getVirtualHalState().hal.functions.some(({ name }) => name === "motion-controller") ||
|
||||||
|
restoredVirtualHalSession.releaseDiagnosticsReady !== true ||
|
||||||
|
restoredVirtualHalSession.sessionDiagnostics?.ready !== true ||
|
||||||
|
restoredVirtualHalSession.sessionDiagnostics?.validation?.ready !== true ||
|
||||||
doc.body.dataset.virtualHalSessionPhase !== "restored" ||
|
doc.body.dataset.virtualHalSessionPhase !== "restored" ||
|
||||||
!doc.querySelector("[data-virtual-hal-session-status]")?.textContent.includes("Restored HAL session")
|
!doc.querySelector("[data-virtual-hal-session-status]")?.textContent.includes("Restored HAL session")
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ for (const required of [
|
|||||||
"getRecentPrograms()",
|
"getRecentPrograms()",
|
||||||
"Three.js `^0.183.2`",
|
"Three.js `^0.183.2`",
|
||||||
"exportDiagnosticsArtifact()",
|
"exportDiagnosticsArtifact()",
|
||||||
|
"exportVirtualHalSessionDiagnosticsArtifact()",
|
||||||
"getLimitsHomeState()",
|
"getLimitsHomeState()",
|
||||||
"getAxisStatusbarState()",
|
"getAxisStatusbarState()",
|
||||||
"applyAxisViewFromUrl(search)",
|
"applyAxisViewFromUrl(search)",
|
||||||
|
|||||||
Reference in New Issue
Block a user