1037 lines
31 KiB
TypeScript
1037 lines
31 KiB
TypeScript
import { expect, type Page } from "@playwright/test";
|
||
|
||
export async function expandSimulationConsole(page: Page) {
|
||
const expandButton = page.getByRole("button", {
|
||
name: "展开仿真控制台",
|
||
exact: true,
|
||
});
|
||
if ((await expandButton.count()) > 0) {
|
||
await expandButton.click();
|
||
}
|
||
}
|
||
|
||
const physicalPort = (
|
||
name: string,
|
||
side: "left" | "right",
|
||
nominalRole: "inlet" | "outlet" | "bidirectional",
|
||
) => ({
|
||
name,
|
||
kind: "physical",
|
||
domain: "pneumatic",
|
||
nominalRole,
|
||
positiveFlowDirection: "intoComponent",
|
||
side,
|
||
order: side === "left" ? 0 : 1,
|
||
});
|
||
|
||
const ud00StageParameters = Array.from({ length: 8 }, (_, offset) => offset + 1)
|
||
.flatMap((stage) => {
|
||
const visibleWhen =
|
||
stage === 1
|
||
? {}
|
||
: {
|
||
visibleWhen: [
|
||
{
|
||
parameter: "nstages",
|
||
values: Array.from(
|
||
{ length: 9 - stage },
|
||
(_, offset) => stage + offset,
|
||
),
|
||
},
|
||
],
|
||
};
|
||
return [
|
||
{
|
||
name: `start${stage}`,
|
||
label: `第 ${stage} 段起点`,
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: stage === 1 ? 0 : 1,
|
||
minimumExclusive: false,
|
||
description: `第 ${stage} 段开始时的输出值。`,
|
||
...visibleWhen,
|
||
},
|
||
{
|
||
name: `end${stage}`,
|
||
label: `第 ${stage} 段终点`,
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 1,
|
||
minimumExclusive: false,
|
||
description: `第 ${stage} 段结束时的输出值。`,
|
||
...visibleWhen,
|
||
},
|
||
{
|
||
name: `t${stage}`,
|
||
label: `第 ${stage} 段时长`,
|
||
quantity: "time",
|
||
unit: "s",
|
||
default: stage === 1 ? 1 : 0,
|
||
minimum: 0,
|
||
minimumExclusive: false,
|
||
description: `第 ${stage} 段的持续时间。`,
|
||
...visibleWhen,
|
||
},
|
||
];
|
||
});
|
||
|
||
const ud00StageParameterNames = Array.from(
|
||
{ length: 8 },
|
||
(_, offset) => offset + 1,
|
||
).flatMap((stage) => [`start${stage}`, `end${stage}`, `t${stage}`]);
|
||
|
||
export const componentCatalog = {
|
||
schemaVersion: 1,
|
||
libraries: [
|
||
{
|
||
id: "experimental",
|
||
label: "临时测试组件库",
|
||
version: "0.1.0",
|
||
sourcePackage: "tests.e2e.experimental",
|
||
temporary: true,
|
||
order: 100,
|
||
components: [],
|
||
},
|
||
{
|
||
id: "e2e-library",
|
||
label: "自动化测试组件库",
|
||
version: "1.0.0",
|
||
sourcePackage: "tests.e2e",
|
||
temporary: true,
|
||
order: 1,
|
||
components: [
|
||
{
|
||
type: "amesim_ideal_air_medium",
|
||
label: "空气介质定义",
|
||
modelType: "amesim_ideal_air_medium",
|
||
modelVersion: "0.2.0",
|
||
symbol: "amesim_ideal_air_medium",
|
||
order: 1,
|
||
category: {
|
||
id: "media",
|
||
label: "介质物性",
|
||
order: 1,
|
||
},
|
||
role: "amesimGasMediumDefinition",
|
||
ports: [],
|
||
parameters: [
|
||
{
|
||
name: "gi",
|
||
label: "介质定义索引(gi)",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 1,
|
||
minimum: 1,
|
||
maximum: 99,
|
||
minimumExclusive: false,
|
||
description: "介质定义索引由画布自动分配;索引 0 保留给内置理想空气。",
|
||
},
|
||
{
|
||
name: "property_model",
|
||
label: "物性计算模型",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 0,
|
||
minimum: 0,
|
||
maximum: 0,
|
||
minimumExclusive: false,
|
||
editor: "amesimGasPropertyModel",
|
||
options: [{ value: 0, label: "理想气体" }],
|
||
description: "选择空气介质采用的物性计算模型。",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
type: "amesim_helium_medium",
|
||
label: "氦气介质定义",
|
||
modelType: "amesim_helium_medium",
|
||
modelVersion: "0.1.0",
|
||
symbol: "amesim_helium_medium",
|
||
order: 2,
|
||
category: {
|
||
id: "media",
|
||
label: "介质物性",
|
||
order: 1,
|
||
},
|
||
role: "amesimGasMediumDefinition",
|
||
ports: [],
|
||
parameters: [
|
||
{
|
||
name: "gi",
|
||
label: "介质定义索引(gi)",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 1,
|
||
minimum: 1,
|
||
maximum: 99,
|
||
minimumExclusive: false,
|
||
description: "介质定义索引由画布自动分配;索引 0 保留给内置理想空气。",
|
||
},
|
||
{
|
||
name: "property_model",
|
||
label: "物性计算模型",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 0,
|
||
minimum: 0,
|
||
maximum: 0,
|
||
minimumExclusive: false,
|
||
editor: "amesimGasPropertyModel",
|
||
options: [{ value: 0, label: "Peng–Robinson" }],
|
||
description: "氦气当前使用 Peng–Robinson 状态方程计算物性。",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
type: "amesim_pneumatic_test",
|
||
label: "AMESim 气动测试元件",
|
||
modelType: "amesim_pneumatic_test",
|
||
modelVersion: "1.0.0",
|
||
symbol: "symbol-not-registered",
|
||
order: 3,
|
||
category: {
|
||
id: "pneumatic",
|
||
label: "气动元件",
|
||
order: 2,
|
||
},
|
||
ports: [
|
||
physicalPort("port_a", "left", "bidirectional"),
|
||
physicalPort("port_b", "right", "bidirectional"),
|
||
],
|
||
parameters: [
|
||
{
|
||
name: "gi",
|
||
label: "介质物性模型(gi)",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 0,
|
||
minimum: 0,
|
||
maximum: 99,
|
||
minimumExclusive: false,
|
||
editor: "amesimGasReference",
|
||
description: "选择该气动元件引用的画布介质定义索引;0 表示内置理想空气。",
|
||
},
|
||
{
|
||
name: "reference_pressure",
|
||
label: "参考压力",
|
||
quantity: "pressure",
|
||
unit: "Pa",
|
||
default: 100000,
|
||
minimum: 0,
|
||
minimumExclusive: true,
|
||
description: "用于验证参数表单位换算和 XML 基础单位输出的参考压力。",
|
||
},
|
||
{
|
||
name: "reference_temperature",
|
||
label: "参考温度",
|
||
quantity: "temperature",
|
||
unit: "K",
|
||
default: 300,
|
||
minimum: 0,
|
||
minimumExclusive: false,
|
||
description: "用于验证带偏移量单位的数值换算和显示精度。",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
type: "generic_sensor",
|
||
label: "通用测试元件",
|
||
modelType: "generic_sensor",
|
||
modelVersion: "1.0.0",
|
||
symbol: "symbol-not-registered",
|
||
order: 4,
|
||
category: {
|
||
id: "generic",
|
||
label: "通用组件",
|
||
order: 1,
|
||
},
|
||
ports: [
|
||
physicalPort("port_a", "left", "bidirectional"),
|
||
physicalPort("port_b", "right", "bidirectional"),
|
||
],
|
||
parameters: [
|
||
{
|
||
name: "gain",
|
||
label: "增益",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 1,
|
||
minimumExclusive: false,
|
||
description: "传感器输入值的无量纲放大倍数。",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
type: "choice_visibility_test",
|
||
label: "离散参数测试元件",
|
||
modelType: "choice_visibility_test",
|
||
modelVersion: "1.0.0",
|
||
symbol: "symbol-not-registered",
|
||
order: 5,
|
||
category: {
|
||
id: "generic",
|
||
label: "通用组件",
|
||
order: 1,
|
||
},
|
||
ports: [],
|
||
parameters: [
|
||
{
|
||
name: "mode",
|
||
label: "工作模式",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 1,
|
||
minimum: 1,
|
||
maximum: 3,
|
||
minimumExclusive: false,
|
||
editor: "choice",
|
||
options: [
|
||
{ value: 1, label: "基础模式" },
|
||
{ value: 2, label: "高级模式" },
|
||
{ value: 3, label: "专家模式" },
|
||
],
|
||
},
|
||
{
|
||
name: "variant",
|
||
label: "高级变体",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 10,
|
||
minimum: 10,
|
||
maximum: 20,
|
||
minimumExclusive: false,
|
||
editor: "choice",
|
||
options: [
|
||
{ value: 10, label: "变体 A" },
|
||
{ value: 20, label: "变体 B" },
|
||
],
|
||
},
|
||
{
|
||
name: "basic_value",
|
||
label: "基础参数",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 1,
|
||
minimumExclusive: false,
|
||
visibleWhen: [{ parameter: "mode", values: [1] }],
|
||
},
|
||
{
|
||
name: "advanced_value",
|
||
label: "高级参数",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 1,
|
||
minimumExclusive: false,
|
||
visibleWhen: [{ parameter: "mode", values: [2, 3] }],
|
||
},
|
||
{
|
||
name: "combined_value",
|
||
label: "组合参数",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 3,
|
||
minimumExclusive: false,
|
||
visibleWhen: [
|
||
{ parameter: "mode", values: [2, 3] },
|
||
{ parameter: "variant", values: [20] },
|
||
],
|
||
},
|
||
],
|
||
},
|
||
{
|
||
type: "amesim_mecmas21",
|
||
label: "MECMAS21 编码测试元件",
|
||
modelType: "amesim_mecmas21",
|
||
modelVersion: "0.2.0",
|
||
symbol: "symbol-not-registered",
|
||
order: 6,
|
||
category: {
|
||
id: "generic",
|
||
label: "通用组件",
|
||
order: 1,
|
||
},
|
||
ports: [],
|
||
parameterGroups: [
|
||
{
|
||
id: "friction",
|
||
label: "摩擦",
|
||
order: 10,
|
||
parameters: ["frictionType", "strib", "rvisc"],
|
||
defaultExpanded: false,
|
||
},
|
||
{
|
||
id: "endstops",
|
||
label: "限位",
|
||
order: 20,
|
||
parameters: ["discContactOption", "xmin"],
|
||
defaultExpanded: false,
|
||
},
|
||
],
|
||
parameters: [
|
||
{
|
||
name: "mass",
|
||
label: "质量",
|
||
quantity: "mass",
|
||
unit: "kg",
|
||
default: 1,
|
||
minimum: 0,
|
||
minimumExclusive: true,
|
||
},
|
||
{
|
||
name: "rvisc",
|
||
label: "黏性摩擦系数",
|
||
quantity: "translational_damping",
|
||
unit: "N/(m/s)",
|
||
default: 0,
|
||
minimum: 0,
|
||
minimumExclusive: false,
|
||
description: "用于验证折叠摩擦参数仍支持详情提示和值保留。",
|
||
visibleWhen: [{ parameter: "useFriction", values: [2] }],
|
||
},
|
||
{
|
||
name: "xmin",
|
||
label: "下位移限位",
|
||
quantity: "length",
|
||
unit: "m",
|
||
default: -1,
|
||
minimumExclusive: false,
|
||
visibleWhen: [{ parameter: "stoptype", values: [1, 2, 3] }],
|
||
},
|
||
{
|
||
name: "useFriction",
|
||
label: "启用摩擦",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 1,
|
||
minimum: 1,
|
||
maximum: 2,
|
||
minimumExclusive: false,
|
||
editor: "choice",
|
||
options: [
|
||
{ value: 1, label: "禁用" },
|
||
{ value: 2, label: "启用" },
|
||
],
|
||
},
|
||
{
|
||
name: "stoptype",
|
||
label: "限位类型",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 4,
|
||
minimum: 1,
|
||
maximum: 4,
|
||
minimumExclusive: false,
|
||
editor: "choice",
|
||
options: [
|
||
{ value: 1, label: "理想限位" },
|
||
{ value: 2, label: "弹性限位" },
|
||
{ value: 3, label: "恢复碰撞" },
|
||
{ value: 4, label: "无限位" },
|
||
],
|
||
},
|
||
{
|
||
name: "discContactOption",
|
||
label: "允许负接触力",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 1,
|
||
minimum: 1,
|
||
maximum: 2,
|
||
minimumExclusive: false,
|
||
editor: "choice",
|
||
options: [
|
||
{ value: 1, label: "允许负接触力" },
|
||
{ value: 2, label: "不允许负接触力" },
|
||
],
|
||
visibleWhen: [{ parameter: "stoptype", values: [2] }],
|
||
},
|
||
{
|
||
name: "strib",
|
||
label: "Stribeck 选项",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 1,
|
||
minimum: 1,
|
||
maximum: 2,
|
||
minimumExclusive: false,
|
||
editor: "choice",
|
||
options: [
|
||
{ value: 1, label: "禁用" },
|
||
{ value: 2, label: "启用" },
|
||
],
|
||
visibleWhen: [
|
||
{ parameter: "useFriction", values: [2] },
|
||
{ parameter: "stoptype", values: [1, 2, 4] },
|
||
{ parameter: "frictionType", values: [2] },
|
||
],
|
||
},
|
||
{
|
||
name: "frictionType",
|
||
label: "摩擦类型",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 1,
|
||
minimum: 1,
|
||
maximum: 2,
|
||
minimumExclusive: false,
|
||
editor: "choice",
|
||
options: [
|
||
{ value: 1, label: "简单" },
|
||
{ value: 2, label: "高级" },
|
||
],
|
||
visibleWhen: [
|
||
{ parameter: "useFriction", values: [2] },
|
||
{ parameter: "stoptype", values: [1, 2, 4] },
|
||
],
|
||
},
|
||
{
|
||
name: "unrelatedCode",
|
||
label: "无关编码",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 7,
|
||
minimumExclusive: false,
|
||
},
|
||
],
|
||
},
|
||
{
|
||
type: "amesim_pnor001",
|
||
label: "PNOR001 常系数气动孔口",
|
||
modelType: "amesim_pnor001",
|
||
modelVersion: "0.2.0",
|
||
symbol: "symbol-not-registered",
|
||
order: 7,
|
||
category: {
|
||
id: "generic",
|
||
label: "通用组件",
|
||
order: 1,
|
||
},
|
||
ports: [],
|
||
parameterGroups: [
|
||
{
|
||
id: "flow_coefficient",
|
||
label: "流量系数",
|
||
order: 10,
|
||
parameters: ["cq", "area", "Cv", "Kv"],
|
||
defaultExpanded: false,
|
||
},
|
||
],
|
||
parameters: [
|
||
{
|
||
name: "cq",
|
||
label: "流量系数 Cq",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 0.72,
|
||
minimum: 1e-10,
|
||
maximum: 1,
|
||
minimumExclusive: false,
|
||
description: "孔口实际质量流量相对于理想可压缩流量的无量纲修正系数。",
|
||
visibleWhen: [{ parameter: "flowset", values: [1] }],
|
||
},
|
||
{
|
||
name: "area",
|
||
label: "孔口面积",
|
||
quantity: "area",
|
||
unit: "m2",
|
||
default: 5e-6,
|
||
minimum: 0,
|
||
maximum: 1,
|
||
minimumExclusive: false,
|
||
description: "选择 Cq/面积方式时用于流量计算的有效孔口面积。",
|
||
visibleWhen: [{ parameter: "flowset", values: [1] }],
|
||
},
|
||
{
|
||
name: "Cv",
|
||
label: "流量系数 Cv",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 0.5,
|
||
minimum: 0,
|
||
minimumExclusive: false,
|
||
description: "选择 Cv 方式时使用的英制流量系数。",
|
||
visibleWhen: [{ parameter: "flowset", values: [2] }],
|
||
},
|
||
{
|
||
name: "Kv",
|
||
label: "流量系数 Kv",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 0.4,
|
||
minimum: 0,
|
||
minimumExclusive: false,
|
||
description: "选择 Kv 方式时使用的公制流量系数。",
|
||
visibleWhen: [{ parameter: "flowset", values: [3] }],
|
||
},
|
||
{
|
||
name: "flowset",
|
||
label: "流量系数设置",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 1,
|
||
minimum: 1,
|
||
maximum: 3,
|
||
minimumExclusive: false,
|
||
editor: "choice",
|
||
options: [
|
||
{ value: 1, label: "Cq" },
|
||
{ value: 2, label: "Cv" },
|
||
{ value: 3, label: "Kv" },
|
||
],
|
||
description: "流量参数方式:1 使用 Cq 和面积,2 使用 Cv,3 使用 Kv。",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
type: "amesim_pnl0001",
|
||
label: "PNL0001 C-R 动态管路",
|
||
modelType: "amesim_pnl0001",
|
||
modelVersion: "0.2.0",
|
||
symbol: "symbol-not-registered",
|
||
order: 8,
|
||
category: {
|
||
id: "generic",
|
||
label: "通用组件",
|
||
order: 1,
|
||
},
|
||
ports: [],
|
||
parameterGroups: [
|
||
{
|
||
id: "thermodynamics",
|
||
label: "热力学",
|
||
order: 10,
|
||
parameters: ["k", "kth", "extemp"],
|
||
defaultExpanded: false,
|
||
},
|
||
],
|
||
parameters: [
|
||
{
|
||
name: "diam",
|
||
label: "管径",
|
||
quantity: "length",
|
||
unit: "m",
|
||
default: 0.01,
|
||
minimum: 0,
|
||
minimumExclusive: true,
|
||
description: "管路的有效内径,用于计算流通面积、储气容积和摩擦压降。",
|
||
},
|
||
{
|
||
name: "k",
|
||
label: "多方指数",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 1.35,
|
||
minimum: 0,
|
||
maximum: 2,
|
||
minimumExclusive: true,
|
||
visibleWhen: [{ parameter: "mode", values: [1] }],
|
||
},
|
||
{
|
||
name: "kth",
|
||
label: "换热系数",
|
||
quantity: "heat_transfer_coefficient",
|
||
unit: "W/(m2*K)",
|
||
default: 0,
|
||
minimum: 0,
|
||
minimumExclusive: false,
|
||
visibleWhen: [{ parameter: "mode", values: [2] }],
|
||
},
|
||
{
|
||
name: "extemp",
|
||
label: "外部温度",
|
||
quantity: "temperature",
|
||
unit: "K",
|
||
default: 293.15,
|
||
minimum: 0,
|
||
minimumExclusive: true,
|
||
visibleWhen: [{ parameter: "mode", values: [2] }],
|
||
},
|
||
{
|
||
name: "mode",
|
||
label: "热模型",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 2,
|
||
minimum: 1,
|
||
maximum: 2,
|
||
minimumExclusive: false,
|
||
editor: "choice",
|
||
options: [
|
||
{ value: 1, label: "多方过程" },
|
||
{ value: 2, label: "带换热" },
|
||
],
|
||
},
|
||
],
|
||
},
|
||
{
|
||
type: "amesim_ud00",
|
||
label: "UD00 分段线性信号",
|
||
modelType: "amesim_ud00",
|
||
modelVersion: "0.2.0",
|
||
symbol: "symbol-not-registered",
|
||
order: 9,
|
||
category: {
|
||
id: "generic",
|
||
label: "通用组件",
|
||
order: 1,
|
||
},
|
||
ports: [],
|
||
parameterGroups: [
|
||
{
|
||
id: "stages",
|
||
label: "分段参数",
|
||
order: 10,
|
||
parameters: ud00StageParameterNames,
|
||
defaultExpanded: false,
|
||
},
|
||
],
|
||
parameters: [
|
||
{
|
||
name: "tstart",
|
||
label: "启动时间",
|
||
quantity: "time",
|
||
unit: "s",
|
||
default: 0,
|
||
minimumExclusive: false,
|
||
description: "分段信号开始输出第一段之前的等待时间。",
|
||
},
|
||
...ud00StageParameters,
|
||
{
|
||
name: "nstages",
|
||
label: "段数",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 1,
|
||
minimum: 1,
|
||
maximum: 8,
|
||
minimumExclusive: false,
|
||
editor: "choice",
|
||
options: Array.from({ length: 8 }, (_, offset) => ({
|
||
value: offset + 1,
|
||
label: String(offset + 1),
|
||
})),
|
||
description: "参与输出计算的有效线性分段数量。",
|
||
},
|
||
{
|
||
name: "iscyclic",
|
||
label: "循环",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 0,
|
||
minimum: 0,
|
||
maximum: 1,
|
||
minimumExclusive: false,
|
||
editor: "choice",
|
||
options: [
|
||
{ value: 0, label: "否" },
|
||
{ value: 1, label: "是" },
|
||
],
|
||
description: "当前公共协议编码:0 表示单次输出,1 表示循环输出。",
|
||
},
|
||
],
|
||
},
|
||
{
|
||
type: "amesim_lstp00a",
|
||
label: "LSTP00A 弹性接触",
|
||
modelType: "amesim_lstp00a",
|
||
modelVersion: "0.2.0",
|
||
symbol: "symbol-not-registered",
|
||
order: 10,
|
||
category: {
|
||
id: "generic",
|
||
label: "通用组件",
|
||
order: 1,
|
||
},
|
||
ports: [],
|
||
parameterGroups: [
|
||
{
|
||
id: "stiffness",
|
||
label: "刚度",
|
||
order: 10,
|
||
parameters: ["kcont", "G", "sdiam", "wdiam", "na"],
|
||
defaultExpanded: false,
|
||
},
|
||
{
|
||
id: "contact",
|
||
label: "接触",
|
||
order: 20,
|
||
parameters: ["gap0", "rcont", "Pdis"],
|
||
defaultExpanded: false,
|
||
},
|
||
],
|
||
parameters: [
|
||
{
|
||
name: "na",
|
||
label: "有效圈数",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 10,
|
||
minimum: 0,
|
||
minimumExclusive: true,
|
||
visibleWhen: [{ parameter: "stiffmode", values: [2] }],
|
||
},
|
||
{
|
||
name: "gap0",
|
||
label: "初始间隙",
|
||
quantity: "length",
|
||
unit: "m",
|
||
default: 0,
|
||
minimumExclusive: false,
|
||
},
|
||
{
|
||
name: "kcont",
|
||
label: "接触刚度",
|
||
quantity: "translational_stiffness",
|
||
unit: "N/m",
|
||
default: 1e6,
|
||
minimum: 0,
|
||
minimumExclusive: false,
|
||
visibleWhen: [{ parameter: "stiffmode", values: [1] }],
|
||
},
|
||
{
|
||
name: "G",
|
||
label: "剪切模量",
|
||
quantity: "pressure",
|
||
unit: "Pa",
|
||
default: 8.57e10,
|
||
minimum: 0,
|
||
minimumExclusive: false,
|
||
visibleWhen: [{ parameter: "stiffmode", values: [2] }],
|
||
},
|
||
{
|
||
name: "sdiam",
|
||
label: "弹簧直径",
|
||
quantity: "length",
|
||
unit: "m",
|
||
default: 0.02,
|
||
minimum: 0,
|
||
minimumExclusive: false,
|
||
visibleWhen: [{ parameter: "stiffmode", values: [2] }],
|
||
},
|
||
{
|
||
name: "wdiam",
|
||
label: "线径",
|
||
quantity: "length",
|
||
unit: "m",
|
||
default: 0.002,
|
||
minimum: 0,
|
||
minimumExclusive: false,
|
||
visibleWhen: [{ parameter: "stiffmode", values: [2] }],
|
||
},
|
||
{
|
||
name: "rcont",
|
||
label: "接触阻尼",
|
||
quantity: "translational_damping",
|
||
unit: "N/(m/s)",
|
||
default: 0,
|
||
minimum: 0,
|
||
minimumExclusive: false,
|
||
},
|
||
{
|
||
name: "Pdis",
|
||
label: "满阻尼穿透",
|
||
quantity: "length",
|
||
unit: "m",
|
||
default: 1e-7,
|
||
minimum: 0,
|
||
minimumExclusive: false,
|
||
},
|
||
{
|
||
name: "stiffmode",
|
||
label: "刚度模式",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 1,
|
||
minimum: 1,
|
||
maximum: 2,
|
||
minimumExclusive: false,
|
||
editor: "choice",
|
||
options: [
|
||
{ value: 1, label: "数值刚度" },
|
||
{ value: 2, label: "几何参数" },
|
||
],
|
||
},
|
||
{
|
||
name: "discContactOption",
|
||
label: "允许负接触力",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
default: 1,
|
||
minimum: 1,
|
||
maximum: 2,
|
||
minimumExclusive: false,
|
||
editor: "choice",
|
||
options: [
|
||
{ value: 1, label: "允许负接触力" },
|
||
{ value: 2, label: "不允许负接触力" },
|
||
],
|
||
},
|
||
],
|
||
},
|
||
],
|
||
},
|
||
],
|
||
};
|
||
|
||
const projectPort = (
|
||
name: string,
|
||
side: "left" | "right",
|
||
nominalRole: "inlet" | "outlet" | "bidirectional",
|
||
) => ({
|
||
name,
|
||
kind: "physical",
|
||
domain: "pneumatic",
|
||
nominalRole,
|
||
positiveFlowDirection: "intoComponent",
|
||
side,
|
||
});
|
||
|
||
export const wideProject = {
|
||
projectSchemaVersion: 1,
|
||
name: "demo-system",
|
||
nodes: Array.from({ length: 4 }, (_, index) => ({
|
||
id: `generic_sensor_${index + 1}`,
|
||
type: "simulationComponent",
|
||
position: {
|
||
x: index * 620,
|
||
y: index % 2 === 0 ? 0 : 260,
|
||
},
|
||
data: {
|
||
label: `generic_sensor_${index + 1}`,
|
||
componentType: "generic_sensor",
|
||
modelType: "generic_sensor",
|
||
modelVersion: "1.0.0",
|
||
symbol: "symbol-not-registered",
|
||
ports: [
|
||
projectPort("port_a", "left", "bidirectional"),
|
||
projectPort("port_b", "right", "bidirectional"),
|
||
],
|
||
parameters: { gain: 1 },
|
||
parameterUnits: { gain: "" },
|
||
rotation: 0,
|
||
mirrored: false,
|
||
},
|
||
})),
|
||
edges: Array.from({ length: 3 }, (_, index) => ({
|
||
id: `edge-${index + 1}`,
|
||
source: `generic_sensor_${index + 1}`,
|
||
target: `generic_sensor_${index + 2}`,
|
||
sourceHandle: "port_b",
|
||
targetHandle: "port_a",
|
||
data: { isContactEdge: false },
|
||
})),
|
||
simulation: {
|
||
t_start: 0,
|
||
t_stop: 10,
|
||
step: 0.1,
|
||
max_step: 0.05,
|
||
method: "RK45",
|
||
},
|
||
};
|
||
|
||
export const resultSnapshot = {
|
||
id: "e2e-result",
|
||
createdAt: "2026-07-28T10:00:00.000Z",
|
||
project: wideProject,
|
||
result: {
|
||
success: true,
|
||
status: "completed",
|
||
partial: false,
|
||
message: "Simulation completed",
|
||
simulatedUntil: 10,
|
||
requestedStopTime: 10,
|
||
variables: [
|
||
{
|
||
key: "generic_sensor_1.value",
|
||
componentId: "generic_sensor_1",
|
||
componentType: "generic_sensor",
|
||
scope: "component",
|
||
portName: null,
|
||
name: "value",
|
||
label: "数值",
|
||
quantity: "dimensionless",
|
||
unit: "",
|
||
category: "state",
|
||
order: 1,
|
||
},
|
||
],
|
||
final: { "generic_sensor_1.value": 1 },
|
||
series: {
|
||
time: [0, 5, 10],
|
||
"generic_sensor_1.value": [0, 0.5, 1],
|
||
},
|
||
diagnostics: {
|
||
pressureFlow: {
|
||
solveCount: 3,
|
||
maxScaledResidual: 0,
|
||
maxEvaluationsPerSolve: 1,
|
||
},
|
||
stream: {
|
||
maxIterationsPerSolve: 1,
|
||
},
|
||
stateCount: 1,
|
||
sampleCount: 3,
|
||
},
|
||
},
|
||
};
|
||
|
||
export async function prepareApp(page: Page) {
|
||
await page.addInitScript(() => {
|
||
const preparedKey = "system-simulation-flow:e2e-storage-prepared";
|
||
if (window.sessionStorage.getItem(preparedKey) !== "1") {
|
||
window.localStorage.clear();
|
||
window.sessionStorage.clear();
|
||
window.sessionStorage.setItem(preparedKey, "1");
|
||
}
|
||
window.location.hash = "#/modeling";
|
||
});
|
||
await page.route("**/api/components/catalog", async (route) => {
|
||
await route.fulfill({
|
||
status: 200,
|
||
contentType: "application/json",
|
||
body: JSON.stringify(componentCatalog),
|
||
});
|
||
});
|
||
}
|
||
|
||
export async function expectAllNodesInsideCanvas(
|
||
page: Page,
|
||
canvasSelector: string,
|
||
expectedCount = wideProject.nodes.length,
|
||
) {
|
||
const canvas = page.locator(canvasSelector);
|
||
await expect(canvas).toBeVisible();
|
||
const nodes = canvas.locator(".react-flow__node");
|
||
await expect(nodes).toHaveCount(expectedCount);
|
||
|
||
await expect
|
||
.poll(
|
||
async () =>
|
||
page.evaluate(
|
||
({ selector, count }) => {
|
||
const canvasElement = document.querySelector<HTMLElement>(selector);
|
||
const nodeElements = Array.from(
|
||
canvasElement?.querySelectorAll<HTMLElement>(".react-flow__node") ?? [],
|
||
);
|
||
if (!canvasElement || nodeElements.length !== count) {
|
||
return false;
|
||
}
|
||
const canvasBounds = canvasElement.getBoundingClientRect();
|
||
const tolerance = 3;
|
||
return nodeElements.every((node) => {
|
||
const bounds = node.getBoundingClientRect();
|
||
return (
|
||
bounds.left >= canvasBounds.left - tolerance &&
|
||
bounds.top >= canvasBounds.top - tolerance &&
|
||
bounds.right <= canvasBounds.right + tolerance &&
|
||
bounds.bottom <= canvasBounds.bottom + tolerance
|
||
);
|
||
});
|
||
},
|
||
{ selector: canvasSelector, count: expectedCount },
|
||
),
|
||
{ message: `所有节点应完整显示在 ${canvasSelector} 内` },
|
||
)
|
||
.toBe(true);
|
||
}
|