前端进度条性能优化、仿真结束后后处理优化;后端C代码生成流程优化:先识别来源,再按照已知未知量需求排序,最后局部求解
This commit is contained in:
1 parent
0dcb465d84
commit
91bd9fb252
77 files changed
+11223
-551
No files matched your search
@@ -0,0 +1,122 @@
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { existsSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
import { fileURLToPath } from "node:url";
|
||||
import {
|
||||
normalizePortComputation, portConnectionIssue, referenceSupplyIssues,
|
||||
} from "../../src/portComputation";
|
||||
import { expandSimulationConsole, prepareApp } from "./fixtures";
|
||||
|
||||
const root = fileURLToPath(new URL("../../..", import.meta.url));
|
||||
const localPython = resolve(root, ".venv-win/Scripts/python.exe");
|
||||
const python = process.env.TEST_PYTHON ?? (existsSync(localPython) ? localPython : "python");
|
||||
// Use real backend metadata: frontend/backend drift must fail this test.
|
||||
const catalog = JSON.parse(execFileSync(python, ["-c",
|
||||
"import json; from app.simulation.registry import build_component_catalog; print(json.dumps(build_component_catalog()))",
|
||||
], { cwd: root, encoding: "utf8" }));
|
||||
const definitions = new Map<string, any>(catalog.libraries.flatMap((library: any) =>
|
||||
library.components.map((component: any) => [component.modelType, component])));
|
||||
const port = (model: string, name: string) => definitions.get(model).ports.find((p: any) => p.name === name);
|
||||
|
||||
function node(id: string, model: string, x: number, y = 0) {
|
||||
const definition = definitions.get(model);
|
||||
return { id, type: "simulationComponent", position: { x, y }, data: {
|
||||
label: id, componentType: model, modelType: model, modelVersion: definition.modelVersion,
|
||||
symbol: definition.symbol, ports: definition.ports.map((p: any) => ({ ...p })),
|
||||
parameters: Object.fromEntries(definition.parameters.map((p: any) => [p.name, p.name === "gi" ? 0 : p.default])),
|
||||
parameterUnits: Object.fromEntries(definition.parameters.map((p: any) => [p.name, p.unit])),
|
||||
rotation: 0, mirrored: false,
|
||||
} };
|
||||
}
|
||||
|
||||
function project(wrong = false) {
|
||||
const nodes = [node("junction", "amesim_p4node2", 330),
|
||||
node("valve", "amesim_pnvo001_fixed", 0), node("storage", "amesim_pnl0001", 660)];
|
||||
if (wrong) {
|
||||
// A forged/old snapshot must not replace the current catalog contract.
|
||||
nodes[0].data.ports.forEach((p: any) => { delete p.computation; });
|
||||
}
|
||||
return { projectSchemaVersion: 1, name: "port-supply-e2e", nodes,
|
||||
edges: wrong ? [
|
||||
{ id: "wrong-reference", source: "junction", sourceHandle: "port_2",
|
||||
target: "valve", targetHandle: "port_2", data: { isContactEdge: false } },
|
||||
{ id: "wrong-branch", source: "junction", sourceHandle: "port_1",
|
||||
target: "storage", targetHandle: "port_2", data: { isContactEdge: false } },
|
||||
] : [],
|
||||
simulation: { t_start: 0, t_stop: 0.01, step: 0.001, max_step: 0.001, method: "RK45" },
|
||||
};
|
||||
}
|
||||
|
||||
async function load(page: Page, value: object) {
|
||||
await prepareApp(page);
|
||||
await page.route("**/api/components/catalog", (route) => route.fulfill({
|
||||
contentType: "application/json", body: JSON.stringify(catalog),
|
||||
}));
|
||||
await page.goto("/");
|
||||
await expect(page.locator(".catalog-source.ready")).toBeVisible();
|
||||
await page.locator('input[type="file"]').setInputFiles({
|
||||
name: "port-supply.json", mimeType: "application/json", buffer: Buffer.from(JSON.stringify(value)),
|
||||
});
|
||||
await expect(page.locator(".flow-canvas .react-flow__node")).toHaveCount(3);
|
||||
}
|
||||
|
||||
test("真实目录的参考口/支路口供需在两种绘线方向下一致", () => {
|
||||
const reference = port("amesim_p4node2", "port_2");
|
||||
const branch = port("amesim_p4node2", "port_1");
|
||||
const state = port("amesim_pnl0001", "port_2");
|
||||
const flow = port("amesim_pnvo001_fixed", "port_2");
|
||||
for (const [a, b] of [[reference, state], [branch, flow]]) {
|
||||
expect(portConnectionIssue(a, b)).toBeNull();
|
||||
expect(portConnectionIssue(b, a)).toBeNull();
|
||||
}
|
||||
for (const [a, b] of [[reference, flow], [flow, reference]]) {
|
||||
expect(portConnectionIssue(a, b)).toContain("温度");
|
||||
expect(portConnectionIssue(a, b)).toContain("压力");
|
||||
}
|
||||
expect(portConnectionIssue(branch, state)).toContain("能量流率");
|
||||
expect(portConnectionIssue(flow, port("amesim_pnl00r", "port_1"))).toBeNull();
|
||||
expect(() => normalizePortComputation({ mode: "fixed", inputs: ["T"], outputs: ["T"] })).toThrow();
|
||||
});
|
||||
|
||||
test("参考链能追溯储气状态,拒绝无来源循环", () => {
|
||||
const nodes = [node("a", "amesim_p4node2", 0), node("b", "amesim_p4node2", 300),
|
||||
node("state", "amesim_pnl0001", 600)];
|
||||
const edges = [{ source: "a", sourceHandle: "port_2", target: "b", targetHandle: "port_1" },
|
||||
{ source: "b", sourceHandle: "port_2", target: "state", targetHandle: "port_2" }];
|
||||
expect(referenceSupplyIssues(nodes, edges)).toEqual([]);
|
||||
edges[1] = { source: "b", sourceHandle: "port_2", target: "a", targetHandle: "port_1" };
|
||||
expect(referenceSupplyIssues(nodes, edges)).toHaveLength(4);
|
||||
expect(referenceSupplyIssues(nodes, edges)[0].message).toContain("没有实际提供者");
|
||||
});
|
||||
|
||||
test("画布按变量供需过滤接线,并说明不兼容的具体原因", async ({ page }) => {
|
||||
await load(page, project());
|
||||
const handle = (id: string, name: string) => page.locator(
|
||||
`.react-flow__node[data-id="${id}"] .port-handle[data-port-name="${name}"]`);
|
||||
const reference = handle("junction", "port_2");
|
||||
await expect(reference).toHaveAttribute("title", /温度、压力参考口/);
|
||||
await reference.click();
|
||||
await expect(handle("valve", "port_2")).toHaveAttribute("data-connection-state", "incompatible");
|
||||
await expect(handle("valve", "port_2")).toHaveAttribute("title", /需要对端提供压力、温度/);
|
||||
await expect(handle("storage", "port_2")).toHaveAttribute("data-connection-state", "compatible");
|
||||
await handle("storage", "port_2").click();
|
||||
await expect(page.locator(".flow-canvas .react-flow__edge")).toHaveCount(1);
|
||||
});
|
||||
|
||||
test("导入时保留供需错误连线,模型检查阻止仿真且不信任旧端口快照", async ({ page }) => {
|
||||
let simulations = 0;
|
||||
await page.route("**/api/system-xml/simulate-stream", (route) => {
|
||||
simulations += 1;
|
||||
return route.fulfill({ status: 500, body: "unexpected simulation" });
|
||||
});
|
||||
await load(page, project(true));
|
||||
await expect(page.locator(".flow-canvas .react-flow__edge")).toHaveCount(2);
|
||||
await page.getByRole("button", { name: "运行仿真", exact: true }).click();
|
||||
await expandSimulationConsole(page);
|
||||
const panel = page.getByRole("complementary", { name: "仿真控制台", exact: true });
|
||||
await expect(panel).toContainText("需要对端提供压力、温度");
|
||||
await expect(panel).toContainText("质量流率、能量流率");
|
||||
expect(simulations).toBe(0);
|
||||
await expect(page.locator(".flow-canvas .react-flow__edge")).toHaveCount(2);
|
||||
});
|
||||
Reference in new issue
Block a user