You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
261 lines
10 KiB
Python
261 lines
10 KiB
Python
import importlib
|
|
import sys
|
|
import tempfile
|
|
import types
|
|
import unittest
|
|
from pathlib import Path
|
|
|
|
|
|
REPO_ROOT = Path(__file__).resolve().parents[2]
|
|
MODULE_DIR = REPO_ROOT / "src" / "Mod" / "FreeCADExchange"
|
|
if str(MODULE_DIR) not in sys.path:
|
|
sys.path.insert(0, str(MODULE_DIR))
|
|
|
|
|
|
def _install_fake_freecad():
|
|
class Vector:
|
|
def __init__(self, x=0.0, y=0.0, z=0.0):
|
|
self.x = float(x)
|
|
self.y = float(y)
|
|
self.z = float(z)
|
|
|
|
class Rotation:
|
|
def multVec(self, vector):
|
|
return vector
|
|
|
|
class Placement:
|
|
def __init__(self, base=None, rotation=None):
|
|
self.Base = base or Vector()
|
|
self.Rotation = rotation or Rotation()
|
|
|
|
fake_freecad = types.ModuleType("FreeCAD")
|
|
fake_freecad.Vector = Vector
|
|
fake_freecad.Rotation = Rotation
|
|
fake_freecad.Placement = Placement
|
|
fake_freecad.ActiveDocument = None
|
|
fake_freecad.Console = types.SimpleNamespace(
|
|
PrintMessage=lambda *args, **kwargs: None,
|
|
PrintWarning=lambda *args, **kwargs: None,
|
|
PrintError=lambda *args, **kwargs: None,
|
|
)
|
|
sys.modules["FreeCAD"] = fake_freecad
|
|
|
|
fake_importgui = types.ModuleType("ImportGui")
|
|
|
|
def insert(name, docName=None, merge=False, useLinkGroup=True):
|
|
doc = fake_freecad.ActiveDocument
|
|
obj = doc.addObject("Part::Feature", "ImportedBatchModel")
|
|
obj.ImportedPath = name
|
|
return obj
|
|
|
|
fake_importgui.insert = insert
|
|
sys.modules["ImportGui"] = fake_importgui
|
|
|
|
fake_freecadgui = types.ModuleType("FreeCADGui")
|
|
fake_freecadgui.addCommand = lambda *args, **kwargs: None
|
|
fake_freecadgui.Selection = types.SimpleNamespace(getSelection=lambda: [])
|
|
sys.modules["FreeCADGui"] = fake_freecadgui
|
|
|
|
|
|
class FakeViewObject:
|
|
def __init__(self):
|
|
self.Visibility = True
|
|
|
|
|
|
class FakeObject:
|
|
def __init__(self, name, type_id):
|
|
self.Name = name
|
|
self.Label = name
|
|
self.TypeId = type_id
|
|
self.PropertiesList = []
|
|
self.Group = []
|
|
self.InList = []
|
|
self.ViewObject = FakeViewObject()
|
|
self.Placement = sys.modules["FreeCAD"].Placement()
|
|
self.Shape = None
|
|
|
|
def isDerivedFrom(self, type_name):
|
|
if self.TypeId == type_name:
|
|
return True
|
|
if type_name == "App::DocumentObjectGroup":
|
|
return self.TypeId == "App::DocumentObjectGroup"
|
|
if type_name == "App::LocalCoordinateSystem":
|
|
return self.TypeId in {"Part::LocalCoordinateSystem", "PartDesign::CoordinateSystem"}
|
|
return False
|
|
|
|
def addProperty(self, _prop_type, prop_name, _group_name, _description):
|
|
if prop_name not in self.PropertiesList:
|
|
self.PropertiesList.append(prop_name)
|
|
|
|
def addObject(self, child):
|
|
if child not in self.Group:
|
|
self.Group.append(child)
|
|
if self not in child.InList:
|
|
child.InList.append(self)
|
|
|
|
|
|
class FakeDocument:
|
|
def __init__(self):
|
|
self.Objects = []
|
|
self.Name = "QETScene"
|
|
self.recompute_count = 0
|
|
|
|
def addObject(self, type_name, name):
|
|
obj = FakeObject(name, type_name)
|
|
self.Objects.append(obj)
|
|
return obj
|
|
|
|
def getObject(self, name):
|
|
for obj in self.Objects:
|
|
if obj.Name == name:
|
|
return obj
|
|
return None
|
|
|
|
def recompute(self):
|
|
self.recompute_count += 1
|
|
|
|
|
|
def _reload_modules(*extra_names):
|
|
for name in ["RoutingNetwork", "WiringObjects", "TemplateSemantics", "TerminalObjects", "BatchAssembly"] + list(extra_names):
|
|
sys.modules.pop(name, None)
|
|
terminal_objects = importlib.import_module("TerminalObjects")
|
|
batch_assembly = importlib.import_module("BatchAssembly")
|
|
return terminal_objects, batch_assembly
|
|
|
|
|
|
class BatchAssemblyTest(unittest.TestCase):
|
|
def test_create_terminal_block_places_slices_and_local_terminals_along_selected_rail(self):
|
|
_install_fake_freecad()
|
|
terminal_objects, batch_assembly = _reload_modules()
|
|
app = sys.modules["FreeCAD"]
|
|
doc = FakeDocument()
|
|
terminal_objects.ensure_root_group(doc, "project-1")
|
|
rail = doc.addObject("App::DocumentObjectGroup", "DINRail")
|
|
rail.Placement = app.Placement(app.Vector(100, 10, 5), app.Rotation())
|
|
terminal_objects.ensure_string_property(rail, "QetCarrierKind", "QET Wiring", "", "rail")
|
|
terminal_objects.ensure_string_property(rail, "QetCarrierAxis", "QET Wiring", "", "x")
|
|
|
|
report = batch_assembly.create_terminal_block(
|
|
doc,
|
|
rail,
|
|
block_name="XT1",
|
|
count=3,
|
|
pitch_mm=5.2,
|
|
start_offset_mm=10.0,
|
|
)
|
|
|
|
self.assertEqual(3, report["created_devices"])
|
|
self.assertEqual(3, report["created_terminals"])
|
|
self.assertEqual("XT1", report["group"].Label)
|
|
placements = [item.Placement.Base.x for item in report["devices"]]
|
|
self.assertEqual([110.0, 115.2, 120.4], placements)
|
|
terminal_labels = [terminal.Label for terminal in report["terminals"]]
|
|
self.assertEqual(["XT1:1", "XT1:2", "XT1:3"], terminal_labels)
|
|
self.assertTrue(all(terminal.QetTerminalUuid.startswith("local:") for terminal in report["terminals"]))
|
|
self.assertTrue(all(not terminal.ViewObject.Visibility for terminal in report["terminals"]))
|
|
|
|
def test_create_breakers_generates_numbered_devices_and_terminal_labels(self):
|
|
_install_fake_freecad()
|
|
terminal_objects, batch_assembly = _reload_modules()
|
|
app = sys.modules["FreeCAD"]
|
|
doc = FakeDocument()
|
|
terminal_objects.ensure_root_group(doc, "project-1")
|
|
rail = doc.addObject("App::DocumentObjectGroup", "DINRail")
|
|
rail.Placement = app.Placement(app.Vector(0, 0, 0), app.Rotation())
|
|
terminal_objects.ensure_string_property(rail, "QetCarrierKind", "QET Wiring", "", "rail")
|
|
terminal_objects.ensure_string_property(rail, "QetCarrierAxis", "QET Wiring", "", "x")
|
|
|
|
report = batch_assembly.create_breakers(
|
|
doc,
|
|
rail,
|
|
base_name="QF",
|
|
count=2,
|
|
pitch_mm=18.0,
|
|
start_offset_mm=0.0,
|
|
terminal_numbers=("1", "2", "3", "4", "5", "6"),
|
|
)
|
|
|
|
self.assertEqual(["QF1", "QF2"], [device.Label for device in report["devices"]])
|
|
self.assertEqual(12, report["created_terminals"])
|
|
self.assertTrue(all(device.Name.startswith("QETDevice_") for device in report["devices"]))
|
|
self.assertEqual(["QF1", "QF2"], [device.QetInstanceId for device in report["devices"]])
|
|
self.assertEqual(["QF1", "QF2"], [device.QetElementUuid for device in report["devices"]])
|
|
labels = [terminal.Label for terminal in report["terminals"]]
|
|
self.assertEqual(["QF1:1", "QF1:2", "QF1:3", "QF1:4", "QF1:5", "QF1:6"], labels[:6])
|
|
self.assertEqual(["QF2:1", "QF2:2", "QF2:3", "QF2:4", "QF2:5", "QF2:6"], labels[6:])
|
|
self.assertEqual([0.0, 18.0], [device.Placement.Base.x for device in report["devices"]])
|
|
|
|
def test_created_breaker_local_slots_can_be_promoted_to_qet_terminal_uuid(self):
|
|
_install_fake_freecad()
|
|
terminal_objects, batch_assembly = _reload_modules("AutoRouting")
|
|
auto_routing = importlib.import_module("AutoRouting")
|
|
app = sys.modules["FreeCAD"]
|
|
doc = FakeDocument()
|
|
terminal_objects.ensure_root_group(doc, "project-1")
|
|
rail = doc.addObject("App::DocumentObjectGroup", "DINRail")
|
|
rail.Placement = app.Placement(app.Vector(0, 0, 0), app.Rotation())
|
|
terminal_objects.ensure_string_property(rail, "QetCarrierKind", "QET Wiring", "", "rail")
|
|
terminal_objects.ensure_string_property(rail, "QetCarrierAxis", "QET Wiring", "", "x")
|
|
|
|
batch_assembly.create_breakers(
|
|
doc,
|
|
rail,
|
|
base_name="QF",
|
|
count=1,
|
|
terminal_numbers=("1", "2"),
|
|
)
|
|
payload = {
|
|
"project_uuid": "project-1",
|
|
"wires": [
|
|
{
|
|
"wire_uuid": "wire-1",
|
|
"start_instance_id": "QF1",
|
|
"start_terminal_uuid": "terminal-qf1-1",
|
|
"start_terminal_display": "1",
|
|
"end_instance_id": "QF1",
|
|
"end_terminal_uuid": "terminal-qf1-2",
|
|
"end_terminal_display": "2",
|
|
}
|
|
],
|
|
}
|
|
|
|
report = auto_routing.bind_wire_task_terminals_from_payload(doc, payload)
|
|
|
|
self.assertEqual(2, report["bound"])
|
|
indexed = auto_routing.index_terminals(doc)
|
|
self.assertIn("terminal-qf1-1", indexed)
|
|
self.assertIn("terminal-qf1-2", indexed)
|
|
self.assertFalse(any(key.startswith("local:QF1") for key in indexed))
|
|
|
|
def test_create_breakers_can_import_model_template_instead_of_placeholder_box(self):
|
|
_install_fake_freecad()
|
|
terminal_objects, batch_assembly = _reload_modules()
|
|
app = sys.modules["FreeCAD"]
|
|
doc = FakeDocument()
|
|
app.ActiveDocument = doc
|
|
terminal_objects.ensure_root_group(doc, "project-1")
|
|
rail = doc.addObject("App::DocumentObjectGroup", "DINRail")
|
|
rail.Placement = app.Placement(app.Vector(0, 0, 0), app.Rotation())
|
|
terminal_objects.ensure_string_property(rail, "QetCarrierKind", "QET Wiring", "", "rail")
|
|
terminal_objects.ensure_string_property(rail, "QetCarrierAxis", "QET Wiring", "", "x")
|
|
|
|
with tempfile.TemporaryDirectory() as temp_dir:
|
|
model_path = Path(temp_dir) / "breaker.step"
|
|
model_path.write_text("fake step", encoding="utf-8")
|
|
report = batch_assembly.create_breakers(
|
|
doc,
|
|
rail,
|
|
base_name="QF",
|
|
count=1,
|
|
model_path=str(model_path),
|
|
)
|
|
|
|
imported_children = [child for child in report["devices"][0].Group if getattr(child, "ImportedPath", "")]
|
|
self.assertEqual(1, len(imported_children))
|
|
self.assertEqual(str(model_path), imported_children[0].QetBatchSourceModelPath)
|
|
self.assertEqual("QF1 模型", imported_children[0].Label)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|