前端进度条性能优化、仿真结束后后处理优化;后端C代码生成流程优化:先识别来源,再按照已知未知量需求排序,最后局部求解

This commit is contained in:
ljz committed 2026-09-11 11:27:54 +08:00
1 parent 0dcb465d84
commit 91bd9fb252
77 files changed
+11223 -551

No files matched your search

+122
View File
@@ -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);
});