完善建模交互、组件图标与系统协议
This commit is contained in:
1 parent
456c29b3b6
commit
6572defaa4
66 files changed
+10067
-4163
No files matched your search
@@ -32,6 +32,7 @@ def physical_connection_project(*, reverse_edge: bool = False) -> ReactFlowProje
|
||||
"label": "source-cylinder",
|
||||
"componentType": "cylinder",
|
||||
"modelType": "cylinder",
|
||||
"modelVersion": "1.0.0",
|
||||
"ports": [physical_port("port_b", "outlet", "right")],
|
||||
"parameters": {"volume": 0.01, "p0": 35000000, "T0": 300},
|
||||
},
|
||||
@@ -44,6 +45,7 @@ def physical_connection_project(*, reverse_edge: bool = False) -> ReactFlowProje
|
||||
"label": "receiver-tank",
|
||||
"componentType": "tank",
|
||||
"modelType": "tank",
|
||||
"modelVersion": "1.0.0",
|
||||
"ports": [physical_port("port_a", "inlet", "left")],
|
||||
"parameters": {"volume": 0.1, "p0": 100000, "T0": 300},
|
||||
},
|
||||
@@ -66,6 +68,7 @@ def physical_connection_project(*, reverse_edge: bool = False) -> ReactFlowProje
|
||||
}
|
||||
)
|
||||
return ReactFlowProjectPayload(
|
||||
projectSchemaVersion=1,
|
||||
name="protocol-test",
|
||||
nodes=[source, target],
|
||||
edges=[edge],
|
||||
@@ -80,15 +83,18 @@ def physical_connection_project(*, reverse_edge: bool = False) -> ReactFlowProje
|
||||
|
||||
|
||||
class SystemXmlProtocolTests(unittest.TestCase):
|
||||
def test_v2_xml_contains_port_metadata_and_neutral_physical_endpoints(self) -> None:
|
||||
def test_v3_xml_contains_only_execution_model_fields(self) -> None:
|
||||
root = ET.fromstring(build_reactflow_system_xml(physical_connection_project()))
|
||||
|
||||
self.assertEqual(root.tag, "System")
|
||||
self.assertEqual(root.attrib["name"], "protocol-test")
|
||||
self.assertEqual(root.attrib["schemaVersion"], "2")
|
||||
self.assertEqual(root.attrib["unitSystem"], "SI")
|
||||
self.assertEqual(root.attrib["mediumReferenceVersion"], "1")
|
||||
self.assertEqual(root.attrib["amesimParameterEncodingVersion"], "1")
|
||||
self.assertEqual(
|
||||
root.attrib,
|
||||
{
|
||||
"name": "protocol-test",
|
||||
"schemaVersion": "3",
|
||||
"unitSystem": "SI",
|
||||
},
|
||||
)
|
||||
self.assertEqual(
|
||||
[child.tag for child in root],
|
||||
["Simulation", "Components", "Connections"],
|
||||
@@ -101,37 +107,34 @@ class SystemXmlProtocolTests(unittest.TestCase):
|
||||
{
|
||||
"tStart": "1.0",
|
||||
"tStop": "5.0",
|
||||
"step": "0.2",
|
||||
"sampleStep": "0.2",
|
||||
"maxStep": "0.01",
|
||||
"method": "BDF",
|
||||
},
|
||||
)
|
||||
|
||||
port = root.find("./Components/Component[@id='cylinder_1']/Port")
|
||||
assert port is not None
|
||||
component = root.find("./Components/Component[@id='cylinder_1']")
|
||||
assert component is not None
|
||||
self.assertEqual(component.attrib["rotation"], "0")
|
||||
self.assertEqual(component.attrib["mirrored"], "false")
|
||||
self.assertEqual(
|
||||
port.attrib,
|
||||
component.attrib,
|
||||
{
|
||||
"name": "port_b",
|
||||
"kind": "physical",
|
||||
"domain": "pneumatic",
|
||||
"nominalRole": "outlet",
|
||||
"side": "right",
|
||||
"positiveFlowDirection": "intoComponent",
|
||||
"id": "cylinder_1",
|
||||
"type": "cylinder",
|
||||
"modelVersion": "1.0.0",
|
||||
},
|
||||
)
|
||||
self.assertEqual(component.findall("Port"), [])
|
||||
self.assertEqual(
|
||||
{
|
||||
parameter.attrib["name"]: float(parameter.attrib["value"])
|
||||
for parameter in component.findall("Parameter")
|
||||
},
|
||||
{"volume": 0.01, "p0": 35_000_000.0, "T0": 300.0},
|
||||
)
|
||||
|
||||
connection = root.find("./Connections/Connection")
|
||||
assert connection is not None
|
||||
self.assertEqual(
|
||||
connection.attrib,
|
||||
{"id": "edge-1", "kind": "physical", "domain": "pneumatic"},
|
||||
)
|
||||
self.assertNotIn("source", connection.attrib)
|
||||
self.assertEqual(connection.attrib, {"id": "edge-1"})
|
||||
self.assertEqual(
|
||||
{
|
||||
(endpoint.attrib["component"], endpoint.attrib["port"])
|
||||
@@ -139,9 +142,7 @@ class SystemXmlProtocolTests(unittest.TestCase):
|
||||
},
|
||||
{("cylinder_1", "port_b"), ("tank_1", "port_a")},
|
||||
)
|
||||
self.assertTrue(
|
||||
all("role" not in endpoint.attrib for endpoint in connection.findall("Endpoint"))
|
||||
)
|
||||
self.assertTrue(all(len(endpoint.attrib) == 2 for endpoint in connection))
|
||||
|
||||
def test_physical_connection_meaning_does_not_depend_on_drag_direction(self) -> None:
|
||||
forward = ET.fromstring(
|
||||
@@ -159,63 +160,24 @@ class SystemXmlProtocolTests(unittest.TestCase):
|
||||
|
||||
self.assertEqual(endpoint_set(forward), endpoint_set(reverse))
|
||||
|
||||
def test_component_orientation_is_exported_as_layout_metadata(self) -> None:
|
||||
def test_editor_layout_and_display_fields_are_not_exported(self) -> None:
|
||||
project = physical_connection_project()
|
||||
project.nodes[0].position.x = 987
|
||||
project.nodes[0].position.y = 654
|
||||
project.nodes[0].data.label = "display-only-name"
|
||||
project.nodes[0].data.rotation = 90
|
||||
project.nodes[0].data.mirrored = True
|
||||
|
||||
root = ET.fromstring(build_reactflow_system_xml(project))
|
||||
component = root.find("./Components/Component[@id='cylinder_1']")
|
||||
assert component is not None
|
||||
self.assertEqual(component.attrib["rotation"], "90")
|
||||
self.assertEqual(component.attrib["mirrored"], "true")
|
||||
|
||||
def test_legacy_string_port_is_migrated_when_exporting_v2(self) -> None:
|
||||
project = ReactFlowProjectPayload(
|
||||
name="legacy-port",
|
||||
nodes=[
|
||||
{
|
||||
"id": "tank_1",
|
||||
"data": {
|
||||
"label": "tank_1",
|
||||
"componentType": "tank",
|
||||
"modelType": "tank",
|
||||
"ports": ["port_a"],
|
||||
},
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
root = ET.fromstring(build_reactflow_system_xml(project))
|
||||
port = root.find("./Components/Component/Port")
|
||||
assert port is not None
|
||||
self.assertEqual(port.attrib["kind"], "physical")
|
||||
self.assertEqual(port.attrib["domain"], "pneumatic")
|
||||
self.assertEqual(port.attrib["nominalRole"], "inlet")
|
||||
self.assertEqual(port.attrib["positiveFlowDirection"], "intoComponent")
|
||||
self.assertEqual(port.attrib["side"], "left")
|
||||
|
||||
def test_legacy_tee_port_names_do_not_restore_direction_constraints(self) -> None:
|
||||
project = ReactFlowProjectPayload(
|
||||
name="legacy-tee",
|
||||
nodes=[
|
||||
{
|
||||
"id": "tee_1",
|
||||
"data": {
|
||||
"label": "tee_1",
|
||||
"componentType": "tee",
|
||||
"modelType": "tee",
|
||||
"ports": ["port_in", "port_out1", "port_out2"],
|
||||
},
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
root = ET.fromstring(build_reactflow_system_xml(project))
|
||||
ports = root.findall("./Components/Component/Port")
|
||||
self.assertEqual(
|
||||
[port.attrib["nominalRole"] for port in ports],
|
||||
["bidirectional", "bidirectional", "bidirectional"],
|
||||
component.attrib,
|
||||
{
|
||||
"id": "cylinder_1",
|
||||
"type": "cylinder",
|
||||
"modelVersion": "1.0.0",
|
||||
},
|
||||
)
|
||||
|
||||
def test_incompatible_physical_domains_are_rejected(self) -> None:
|
||||
@@ -224,9 +186,29 @@ class SystemXmlProtocolTests(unittest.TestCase):
|
||||
assert not isinstance(tank_port, str)
|
||||
tank_port.domain = "hydraulic"
|
||||
|
||||
with self.assertRaisesRegex(ValueError, "incompatible domains"):
|
||||
with self.assertRaisesRegex(ValueError, "incompatible port type"):
|
||||
build_reactflow_system_xml(project)
|
||||
|
||||
def test_exporter_never_returns_xml_that_fails_the_v3_contract(self) -> None:
|
||||
invalid_time = physical_connection_project()
|
||||
invalid_time.simulation.t_stop = invalid_time.simulation.t_start
|
||||
with self.assertRaisesRegex(ValueError, "SIMULATION_TIME_RANGE_INVALID"):
|
||||
build_reactflow_system_xml(invalid_time)
|
||||
|
||||
duplicate_component = physical_connection_project()
|
||||
duplicate_component.nodes[1].id = duplicate_component.nodes[0].id
|
||||
duplicate_component.edges = []
|
||||
with self.assertRaisesRegex(ValueError, "COMPONENT_ID_DUPLICATE"):
|
||||
build_reactflow_system_xml(duplicate_component)
|
||||
|
||||
def test_blank_project_name_is_omitted(self) -> None:
|
||||
project = physical_connection_project()
|
||||
project.name = " "
|
||||
|
||||
root = ET.fromstring(build_reactflow_system_xml(project))
|
||||
|
||||
self.assertNotIn("name", root.attrib)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user