diff --git a/README.md b/README.md index c732f233..f011ddce 100644 --- a/README.md +++ b/README.md @@ -32,25 +32,20 @@ In short faebryk is a python library that allows you to design ready-to-order el ```python import faebryk.library._F as F -from faebryk.core.core import Module +from faebryk.core.module import Module from faebryk.libs.brightness import TypicalLuminousIntensity from faebryk.libs.examples.buildutil import apply_design_to_pcb class App(Module): - def __init__(self) -> None: - super().__init__() + led : F.PoweredLED + battery : F.Battery - class _NODES(Module.NODES()): - led = F.PoweredLED() - battery = F.Battery() - - self.NODEs = _NODES(self) - - self.NODEs.led.IFs.power.connect(self.NODEs.battery.IFs.power) + def __preinit__(self) -> None: + self.led.power.connect(self.battery.power) # Parametrize - self.NODEs.led.NODEs.led.PARAMs.color.merge(F.LED.Color.YELLOW) - self.NODEs.led.NODEs.led.PARAMs.brightness.merge( + self.led.led.color.merge(F.LED.Color.YELLOW) + self.led.led.brightness.merge( TypicalLuminousIntensity.APPLICATION_LED_INDICATOR_INSIDE.value.value ) diff --git a/examples/iterative_design_nand.py b/examples/iterative_design_nand.py index e839664b..305c2f15 100644 --- a/examples/iterative_design_nand.py +++ b/examples/iterative_design_nand.py @@ -17,48 +17,37 @@ import typer import faebryk.library._F as F -from faebryk.core.core import Module +from faebryk.core.module import Module from faebryk.core.util import ( get_all_nodes_with_trait, specialize_interface, specialize_module, ) -from faebryk.library._F import Constant from faebryk.libs.brightness import TypicalLuminousIntensity from faebryk.libs.examples.buildutil import apply_design_to_pcb +from faebryk.libs.library import L from faebryk.libs.logging import setup_basic_logging from faebryk.libs.units import P -from faebryk.libs.util import times logger = logging.getLogger(__name__) class PowerSource(Module): - def __init__(self) -> None: - super().__init__() - - class IFS(Module.IFS()): - power = F.ElectricPower() - - self.IFs = IFS(self) + power: F.ElectricPower class XOR_with_NANDS(F.LogicGates.XOR): - def __init__( - self, - ): - super().__init__(Constant(2)) - - class NODES(Module.NODES()): - nands = times(4, lambda: F.LogicGates.NAND(Constant(2))) + nands = L.list_field(4, lambda: F.LogicGates.NAND(F.Constant(2))) - self.NODEs = NODES(self) + def __init__(self): + super().__init__(F.Constant(2)) - A = self.IFs.inputs[0] - B = self.IFs.inputs[1] + def __preinit__(self): + A = self.inputs[0] + B = self.inputs[1] - G = self.NODEs.nands - Q = self.IFs.outputs[0] + G = self.nands + Q = self.outputs[0] # ~(a&b) q0 = G[0].get_trait(F.LogicOps.can_logic_nand).nand(A, B) @@ -81,18 +70,18 @@ def App(): power_source = PowerSource() # alias - power = power_source.IFs.power + power = power_source.power # logic logic_in = F.Logic() logic_out = F.Logic() - xor = F.LogicGates.XOR(Constant(2)) + xor = F.LogicGates.XOR(F.Constant(2)) logic_out.connect(xor.get_trait(F.LogicOps.can_logic_xor).xor(logic_in, on)) # led led = F.LEDIndicator() - led.IFs.power_in.connect(power) + led.power_in.connect(power) # application switch = F.Switch(F.Logic)() @@ -104,15 +93,15 @@ def App(): e_out = specialize_interface(logic_out, F.ElectricLogic()) e_on = specialize_interface(on, F.ElectricLogic()) e_off = specialize_interface(off, F.ElectricLogic()) - e_in.IFs.reference.connect(power) - e_out.IFs.reference.connect(power) - e_on.IFs.reference.connect(power) - e_off.IFs.reference.connect(power) + e_in.reference.connect(power) + e_out.reference.connect(power) + e_on.reference.connect(power) + e_off.reference.connect(power) e_in.set_weak(on=False) e_on.set(on=True) e_off.set(on=False) - e_out.connect(led.IFs.logic_in) + e_out.connect(led.logic_in) nxor = specialize_module(xor, XOR_with_NANDS()) battery = specialize_module(power_source, F.Battery()) @@ -122,36 +111,34 @@ def App(): e_switch = specialize_module( el_switch, e_switch, - matrix=[ - (e, el.IFs.signal) - for e, el in zip(e_switch.IFs.unnamed, el_switch.IFs.unnamed) - ], + matrix=[(e, el.signal) for e, el in zip(e_switch.unnamed, el_switch.unnamed)], ) # build graph app = Module() - app.NODEs.components = [ + for c in [ led, switch, battery, e_switch, - ] + ]: + app.add(c) # parametrizing for _, t in get_all_nodes_with_trait(app.get_graph(), F.ElectricLogic.has_pulls): for pull_resistor in (r for r in t.get_pulls() if r): - pull_resistor.PARAMs.resistance.merge(100 * P.kohm) - power_source.IFs.power.PARAMs.voltage.merge(3 * P.V) - led.NODEs.led.NODEs.led.PARAMs.brightness.merge( + pull_resistor.resistance.merge(100 * P.kohm) + power_source.power.voltage.merge(3 * P.V) + led.led.led.brightness.merge( TypicalLuminousIntensity.APPLICATION_LED_INDICATOR_INSIDE.value.value ) # packages single nands as explicit IC nand_ic = F.TI_CD4011BE() - for ic_nand, xor_nand in zip(nand_ic.NODEs.gates, nxor.NODEs.nands): + for ic_nand, xor_nand in zip(nand_ic.gates, nxor.nands): specialize_module(xor_nand, ic_nand) - app.NODEs.nand_ic = nand_ic + app.add(nand_ic) return app diff --git a/examples/minimal_led.py b/examples/minimal_led.py index b542d596..a48349a7 100644 --- a/examples/minimal_led.py +++ b/examples/minimal_led.py @@ -10,31 +10,24 @@ import typer import faebryk.library._F as F -from faebryk.core.core import Module +from faebryk.core.module import Module from faebryk.libs.brightness import TypicalLuminousIntensity -from faebryk.libs.examples.buildutil import ( - apply_design_to_pcb, -) +from faebryk.libs.examples.buildutil import apply_design_to_pcb from faebryk.libs.logging import setup_basic_logging logger = logging.getLogger(__name__) class App(Module): - def __init__(self) -> None: - super().__init__() + led: F.PoweredLED + battery: F.Battery - class _NODES(Module.NODES()): - led = F.PoweredLED() - battery = F.Battery() - - self.NODEs = _NODES(self) - - self.NODEs.led.IFs.power.connect(self.NODEs.battery.IFs.power) + def __preinit__(self) -> None: + self.led.power.connect(self.battery.power) # Parametrize - self.NODEs.led.NODEs.led.PARAMs.color.merge(F.LED.Color.YELLOW) - self.NODEs.led.NODEs.led.PARAMs.brightness.merge( + self.led.led.color.merge(F.LED.Color.YELLOW) + self.led.led.brightness.merge( TypicalLuminousIntensity.APPLICATION_LED_INDICATOR_INSIDE.value.value ) diff --git a/examples/pcb_layout.py b/examples/pcb_layout.py index ce064433..d9d18816 100644 --- a/examples/pcb_layout.py +++ b/examples/pcb_layout.py @@ -10,43 +10,33 @@ import typer import faebryk.library._F as F -from faebryk.core.core import Module +from faebryk.core.module import Module from faebryk.exporters.pcb.layout.absolute import LayoutAbsolute from faebryk.exporters.pcb.layout.extrude import LayoutExtrude from faebryk.exporters.pcb.layout.typehierarchy import LayoutTypeHierarchy -from faebryk.library.has_pcb_layout_defined import has_pcb_layout_defined -from faebryk.library.has_pcb_position import has_pcb_position -from faebryk.library.has_pcb_position_defined import has_pcb_position_defined from faebryk.libs.brightness import TypicalLuminousIntensity -from faebryk.libs.examples.buildutil import ( - apply_design_to_pcb, -) +from faebryk.libs.examples.buildutil import apply_design_to_pcb from faebryk.libs.logging import setup_basic_logging logger = logging.getLogger(__name__) class App(Module): - def __init__(self) -> None: - super().__init__() + leds: F.PoweredLED + battery: F.Battery - class _NODES(Module.NODES()): - leds = F.PoweredLED() - battery = F.Battery() - - self.NODEs = _NODES(self) - - self.NODEs.leds.IFs.power.connect(self.NODEs.battery.IFs.power) + def __preinit__(self) -> None: + self.leds.power.connect(self.battery.power) # Parametrize - self.NODEs.leds.NODEs.led.PARAMs.color.merge(F.LED.Color.YELLOW) - self.NODEs.leds.NODEs.led.PARAMs.brightness.merge( + self.leds.led.color.merge(F.LED.Color.YELLOW) + self.leds.led.brightness.merge( TypicalLuminousIntensity.APPLICATION_LED_INDICATOR_INSIDE.value.value ) # Layout - Point = has_pcb_position.Point - L = has_pcb_position.layer_type + Point = F.has_pcb_position.Point + L = F.has_pcb_position.layer_type layout = LayoutTypeHierarchy( layouts=[ @@ -68,8 +58,8 @@ class _NODES(Module.NODES()): ), ] ) - self.add_trait(has_pcb_layout_defined(layout)) - self.add_trait(has_pcb_position_defined(Point((50, 50, 0, L.NONE)))) + self.add_trait(F.has_pcb_layout_defined(layout)) + self.add_trait(F.has_pcb_position_defined(Point((50, 50, 0, L.NONE)))) # Boilerplate ----------------------------------------------------------------- diff --git a/examples/route.py b/examples/route.py index cea749a5..0488ac0b 100644 --- a/examples/route.py +++ b/examples/route.py @@ -10,23 +10,12 @@ import typer import faebryk.library._F as F -from faebryk.core.core import Module +from faebryk.core.module import Module from faebryk.exporters.pcb.layout.extrude import LayoutExtrude from faebryk.exporters.pcb.layout.typehierarchy import LayoutTypeHierarchy from faebryk.exporters.pcb.routing.util import Path -from faebryk.library.Electrical import Electrical -from faebryk.library.has_pcb_layout_defined import has_pcb_layout_defined -from faebryk.library.has_pcb_position import has_pcb_position -from faebryk.library.has_pcb_position_defined import has_pcb_position_defined -from faebryk.library.has_pcb_routing_strategy_greedy_direct_line import ( - has_pcb_routing_strategy_greedy_direct_line, -) -from faebryk.library.has_pcb_routing_strategy_manual import ( - has_pcb_routing_strategy_manual, -) -from faebryk.libs.examples.buildutil import ( - apply_design_to_pcb, -) +from faebryk.libs.examples.buildutil import apply_design_to_pcb +from faebryk.libs.library import L from faebryk.libs.logging import setup_basic_logging from faebryk.libs.units import P from faebryk.libs.util import times @@ -35,139 +24,129 @@ class SubArray(Module): + unnamed = L.list_field(2, F.Electrical) + resistors = L.list_field(2, F.Resistor) + def __init__(self, extrude_y: float): super().__init__() - - class _IFs(Module.IFS()): - unnamed = times(2, Electrical) - - self.IFs = _IFs(self) - - class _NODES(Module.NODES()): - resistors = times(2, F.Resistor) - - self.NODEs = _NODES(self) - - for resistor in self.NODEs.resistors: - resistor.PARAMs.resistance.merge(F.Constant(1000 * P.ohm)) - resistor.IFs.unnamed[0].connect(self.IFs.unnamed[0]) - resistor.IFs.unnamed[1].connect(self.IFs.unnamed[1]) - - self.add_trait( - has_pcb_layout_defined( - LayoutTypeHierarchy( - layouts=[ - LayoutTypeHierarchy.Level( - mod_type=F.Resistor, - layout=LayoutExtrude((0, extrude_y)), - ), - ] - ) - ) - ) - - self.add_trait( - has_pcb_routing_strategy_greedy_direct_line( - has_pcb_routing_strategy_greedy_direct_line.Topology.DIRECT + self._extrude_y = extrude_y + + def __preinit__(self): + for resistor in self.resistors: + resistor.resistance.merge(F.Constant(1000 * P.ohm)) + resistor.unnamed[0].connect(self.unnamed[0]) + resistor.unnamed[1].connect(self.unnamed[1]) + + @L.rt_field + def pcb_layout(self): + return F.has_pcb_layout_defined( + LayoutTypeHierarchy( + layouts=[ + LayoutTypeHierarchy.Level( + mod_type=F.Resistor, + layout=LayoutExtrude((0, self._extrude_y)), + ), + ] ) ) - self.add_trait( - has_pcb_routing_strategy_manual( - [ - ( - [r.IFs.unnamed[1] for r in self.NODEs.resistors], - Path( - [ - Path.Track( - 0.1, - "F.Cu", - [ - (0, 0), - (2.5, 0), - (2.5, extrude_y), - (0, extrude_y), - ], - ), - ] - ), + pcb_routing_strategy = L.f_field(F.has_pcb_routing_strategy_greedy_direct_line)( + F.has_pcb_routing_strategy_greedy_direct_line.Topology.DIRECT + ) + + @L.rt_field + def pcb_routing_stategy_manual(self): + return F.has_pcb_routing_strategy_manual( + [ + ( + [r.unnamed[1] for r in self.resistors], + Path( + [ + Path.Track( + 0.1, + "F.Cu", + [ + (0, 0), + (2.5, 0), + (2.5, self._extrude_y), + (0, self._extrude_y), + ], + ), + ] ), - ( - [r.IFs.unnamed[0] for r in self.NODEs.resistors], - Path( - [ - Path.Track( - 0.1, - "F.Cu", - [ - (0, 0), - (-2.5, 0), - (-2.5, extrude_y), - (0, extrude_y), - ], - ), - ] - ), + ), + ( + [r.unnamed[0] for r in self.resistors], + Path( + [ + Path.Track( + 0.1, + "F.Cu", + [ + (0, 0), + (-2.5, 0), + (-2.5, self._extrude_y), + (0, self._extrude_y), + ], + ), + ] ), - ] - ) + ), + ] ) class ResistorArray(Module): - def __init__(self, count: int, extrude_y: tuple[float, float]): - super().__init__() - - class _IFs(Module.IFS()): - unnamed = times(2, Electrical) - - self.IFs = _IFs(self) + unnamed = L.list_field(2, F.Electrical) - class _NODES(Module.NODES()): - resistors = times(count, lambda: SubArray(extrude_y[1])) + @L.rt_field + def resistors(self): + return times(self._count, lambda: SubArray(self._extrude_y[1])) - self.NODEs = _NODES(self) - - for resistor in self.NODEs.resistors: - resistor.IFs.unnamed[0].connect(self.IFs.unnamed[0]) - resistor.IFs.unnamed[1].connect(self.IFs.unnamed[1]) + def __init__(self, count: int, extrude_y: tuple[float, float]): + super().__init__() - self.add_trait( - has_pcb_layout_defined( - LayoutTypeHierarchy( - layouts=[ - LayoutTypeHierarchy.Level( - mod_type=SubArray, - layout=LayoutExtrude((0, extrude_y[0])), - ), - ] - ) + self._count = count + self._extrude_y = extrude_y + + def __preinit__(self): + for resistor in self.resistors: + resistor.unnamed[0].connect(self.unnamed[0]) + resistor.unnamed[1].connect(self.unnamed[1]) + + @L.rt_field + def pcb_layout(self): + return F.has_pcb_layout_defined( + LayoutTypeHierarchy( + layouts=[ + LayoutTypeHierarchy.Level( + mod_type=SubArray, + layout=LayoutExtrude((0, self._extrude_y[0])), + ), + ] ) ) - self.add_trait( - has_pcb_routing_strategy_greedy_direct_line( - has_pcb_routing_strategy_greedy_direct_line.Topology.DIRECT - ) - ) + pcb_routing_strategy = L.f_field(F.has_pcb_routing_strategy_greedy_direct_line)( + F.has_pcb_routing_strategy_greedy_direct_line.Topology.DIRECT + ) class App(Module): + @L.rt_field + def arrays(self): + return times(2, lambda: ResistorArray(self._count, self._extrude_y)) + def __init__(self, count: int, extrude_y: tuple[float, float]) -> None: super().__init__() + self._count = count + self._extrude_y = extrude_y - class _NODES(Module.NODES()): - arrays = times(2, lambda: ResistorArray(count, extrude_y)) - - self.NODEs = _NODES(self) - - self.NODEs.arrays[0].IFs.unnamed[1].connect(self.NODEs.arrays[1].IFs.unnamed[0]) + def __preinit__(self): + self.arrays[0].unnamed[1].connect(self.arrays[1].unnamed[0]) - # Layout - Point = has_pcb_position.Point - L = has_pcb_position.layer_type - - layout = LayoutTypeHierarchy( + pcb_layout = L.f_field(F.has_pcb_layout_defined)( + LayoutTypeHierarchy( layouts=[ LayoutTypeHierarchy.Level( mod_type=ResistorArray, @@ -175,14 +154,15 @@ class _NODES(Module.NODES()): ), ] ) - self.add_trait(has_pcb_layout_defined(layout)) - self.add_trait(has_pcb_position_defined(Point((20, 20, 0, L.TOP_LAYER)))) + ) - self.add_trait( - has_pcb_routing_strategy_greedy_direct_line( - has_pcb_routing_strategy_greedy_direct_line.Topology.STAR - ) - ) + pcb_position = L.f_field(F.has_pcb_position_defined)( + F.has_pcb_position.Point((20, 20, 0, F.has_pcb_position.layer_type.TOP_LAYER)) + ) + + pcb_routing_strategy = L.f_field(F.has_pcb_routing_strategy_greedy_direct_line)( + F.has_pcb_routing_strategy_greedy_direct_line.Topology.STAR + ) # Boilerplate ----------------------------------------------------------------- diff --git a/poetry.lock b/poetry.lock index 94ab7fe3..763050b6 100644 --- a/poetry.lock +++ b/poetry.lock @@ -82,13 +82,13 @@ uvloop = ["uvloop (>=0.15.2)"] [[package]] name = "certifi" -version = "2024.7.4" +version = "2024.8.30" description = "Python package for providing Mozilla's CA Bundle." optional = false python-versions = ">=3.6" files = [ - {file = "certifi-2024.7.4-py3-none-any.whl", hash = "sha256:c198e21b1289c2ab85ee4e67bb4b4ef3ead0892059901a8d5b622f24a1101e90"}, - {file = "certifi-2024.7.4.tar.gz", hash = "sha256:5a1e7645bc0ec61a09e26c36f6106dd4cf40c6db3a1fb6352b0244e7fb057c7b"}, + {file = "certifi-2024.8.30-py3-none-any.whl", hash = "sha256:922820b53db7a7257ffbda3f597266d435245903d80737e34f8a45ff3e3230d8"}, + {file = "certifi-2024.8.30.tar.gz", hash = "sha256:bec941d2aa8195e248a60b31ff9f0558284cf01a52591ceda73ea9afffd69fd9"}, ] [[package]] @@ -228,66 +228,87 @@ files = [ [[package]] name = "contourpy" -version = "1.2.1" +version = "1.3.0" description = "Python library for calculating contours of 2D quadrilateral grids" optional = false python-versions = ">=3.9" files = [ - {file = "contourpy-1.2.1-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:bd7c23df857d488f418439686d3b10ae2fbf9bc256cd045b37a8c16575ea1040"}, - {file = "contourpy-1.2.1-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:5b9eb0ca724a241683c9685a484da9d35c872fd42756574a7cfbf58af26677fd"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4c75507d0a55378240f781599c30e7776674dbaf883a46d1c90f37e563453480"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:11959f0ce4a6f7b76ec578576a0b61a28bdc0696194b6347ba3f1c53827178b9"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb3315a8a236ee19b6df481fc5f997436e8ade24a9f03dfdc6bd490fea20c6da"}, - {file = "contourpy-1.2.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:39f3ecaf76cd98e802f094e0d4fbc6dc9c45a8d0c4d185f0f6c2234e14e5f75b"}, - {file = "contourpy-1.2.1-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:94b34f32646ca0414237168d68a9157cb3889f06b096612afdd296003fdd32fd"}, - {file = "contourpy-1.2.1-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:457499c79fa84593f22454bbd27670227874cd2ff5d6c84e60575c8b50a69619"}, - {file = "contourpy-1.2.1-cp310-cp310-win32.whl", hash = "sha256:ac58bdee53cbeba2ecad824fa8159493f0bf3b8ea4e93feb06c9a465d6c87da8"}, - {file = "contourpy-1.2.1-cp310-cp310-win_amd64.whl", hash = "sha256:9cffe0f850e89d7c0012a1fb8730f75edd4320a0a731ed0c183904fe6ecfc3a9"}, - {file = "contourpy-1.2.1-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6022cecf8f44e36af10bd9118ca71f371078b4c168b6e0fab43d4a889985dbb5"}, - {file = "contourpy-1.2.1-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:ef5adb9a3b1d0c645ff694f9bca7702ec2c70f4d734f9922ea34de02294fdf72"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6150ffa5c767bc6332df27157d95442c379b7dce3a38dff89c0f39b63275696f"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4c863140fafc615c14a4bf4efd0f4425c02230eb8ef02784c9a156461e62c965"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:00e5388f71c1a0610e6fe56b5c44ab7ba14165cdd6d695429c5cd94021e390b2"}, - {file = "contourpy-1.2.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d4492d82b3bc7fbb7e3610747b159869468079fe149ec5c4d771fa1f614a14df"}, - {file = "contourpy-1.2.1-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:49e70d111fee47284d9dd867c9bb9a7058a3c617274900780c43e38d90fe1205"}, - {file = "contourpy-1.2.1-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:b59c0ffceff8d4d3996a45f2bb6f4c207f94684a96bf3d9728dbb77428dd8cb8"}, - {file = "contourpy-1.2.1-cp311-cp311-win32.whl", hash = "sha256:7b4182299f251060996af5249c286bae9361fa8c6a9cda5efc29fe8bfd6062ec"}, - {file = "contourpy-1.2.1-cp311-cp311-win_amd64.whl", hash = "sha256:2855c8b0b55958265e8b5888d6a615ba02883b225f2227461aa9127c578a4922"}, - {file = "contourpy-1.2.1-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:62828cada4a2b850dbef89c81f5a33741898b305db244904de418cc957ff05dc"}, - {file = "contourpy-1.2.1-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:309be79c0a354afff9ff7da4aaed7c3257e77edf6c1b448a779329431ee79d7e"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2e785e0f2ef0d567099b9ff92cbfb958d71c2d5b9259981cd9bee81bd194c9a4"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:1cac0a8f71a041aa587410424ad46dfa6a11f6149ceb219ce7dd48f6b02b87a7"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:af3f4485884750dddd9c25cb7e3915d83c2db92488b38ccb77dd594eac84c4a0"}, - {file = "contourpy-1.2.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9ce6889abac9a42afd07a562c2d6d4b2b7134f83f18571d859b25624a331c90b"}, - {file = "contourpy-1.2.1-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:a1eea9aecf761c661d096d39ed9026574de8adb2ae1c5bd7b33558af884fb2ce"}, - {file = "contourpy-1.2.1-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:187fa1d4c6acc06adb0fae5544c59898ad781409e61a926ac7e84b8f276dcef4"}, - {file = "contourpy-1.2.1-cp312-cp312-win32.whl", hash = "sha256:c2528d60e398c7c4c799d56f907664673a807635b857df18f7ae64d3e6ce2d9f"}, - {file = "contourpy-1.2.1-cp312-cp312-win_amd64.whl", hash = "sha256:1a07fc092a4088ee952ddae19a2b2a85757b923217b7eed584fdf25f53a6e7ce"}, - {file = "contourpy-1.2.1-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:bb6834cbd983b19f06908b45bfc2dad6ac9479ae04abe923a275b5f48f1a186b"}, - {file = "contourpy-1.2.1-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:1d59e739ab0e3520e62a26c60707cc3ab0365d2f8fecea74bfe4de72dc56388f"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd3db01f59fdcbce5b22afad19e390260d6d0222f35a1023d9adc5690a889364"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a12a813949e5066148712a0626895c26b2578874e4cc63160bb007e6df3436fe"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:fe0ccca550bb8e5abc22f530ec0466136379c01321fd94f30a22231e8a48d985"}, - {file = "contourpy-1.2.1-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:e1d59258c3c67c865435d8fbeb35f8c59b8bef3d6f46c1f29f6123556af28445"}, - {file = "contourpy-1.2.1-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:f32c38afb74bd98ce26de7cc74a67b40afb7b05aae7b42924ea990d51e4dac02"}, - {file = "contourpy-1.2.1-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:d31a63bc6e6d87f77d71e1abbd7387ab817a66733734883d1fc0021ed9bfa083"}, - {file = "contourpy-1.2.1-cp39-cp39-win32.whl", hash = "sha256:ddcb8581510311e13421b1f544403c16e901c4e8f09083c881fab2be80ee31ba"}, - {file = "contourpy-1.2.1-cp39-cp39-win_amd64.whl", hash = "sha256:10a37ae557aabf2509c79715cd20b62e4c7c28b8cd62dd7d99e5ed3ce28c3fd9"}, - {file = "contourpy-1.2.1-pp39-pypy39_pp73-macosx_10_9_x86_64.whl", hash = "sha256:a31f94983fecbac95e58388210427d68cd30fe8a36927980fab9c20062645609"}, - {file = "contourpy-1.2.1-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ef2b055471c0eb466033760a521efb9d8a32b99ab907fc8358481a1dd29e3bd3"}, - {file = "contourpy-1.2.1-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:b33d2bc4f69caedcd0a275329eb2198f560b325605810895627be5d4b876bf7f"}, - {file = "contourpy-1.2.1.tar.gz", hash = "sha256:4d8908b3bee1c889e547867ca4cdc54e5ab6be6d3e078556814a22457f49423c"}, + {file = "contourpy-1.3.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:880ea32e5c774634f9fcd46504bf9f080a41ad855f4fef54f5380f5133d343c7"}, + {file = "contourpy-1.3.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:76c905ef940a4474a6289c71d53122a4f77766eef23c03cd57016ce19d0f7b42"}, + {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:92f8557cbb07415a4d6fa191f20fd9d2d9eb9c0b61d1b2f52a8926e43c6e9af7"}, + {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:36f965570cff02b874773c49bfe85562b47030805d7d8360748f3eca570f4cab"}, + {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cacd81e2d4b6f89c9f8a5b69b86490152ff39afc58a95af002a398273e5ce589"}, + {file = "contourpy-1.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:69375194457ad0fad3a839b9e29aa0b0ed53bb54db1bfb6c3ae43d111c31ce41"}, + {file = "contourpy-1.3.0-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:7a52040312b1a858b5e31ef28c2e865376a386c60c0e248370bbea2d3f3b760d"}, + {file = "contourpy-1.3.0-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:3faeb2998e4fcb256542e8a926d08da08977f7f5e62cf733f3c211c2a5586223"}, + {file = "contourpy-1.3.0-cp310-cp310-win32.whl", hash = "sha256:36e0cff201bcb17a0a8ecc7f454fe078437fa6bda730e695a92f2d9932bd507f"}, + {file = "contourpy-1.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:87ddffef1dbe5e669b5c2440b643d3fdd8622a348fe1983fad7a0f0ccb1cd67b"}, + {file = "contourpy-1.3.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:0fa4c02abe6c446ba70d96ece336e621efa4aecae43eaa9b030ae5fb92b309ad"}, + {file = "contourpy-1.3.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:834e0cfe17ba12f79963861e0f908556b2cedd52e1f75e6578801febcc6a9f49"}, + {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dbc4c3217eee163fa3984fd1567632b48d6dfd29216da3ded3d7b844a8014a66"}, + {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4865cd1d419e0c7a7bf6de1777b185eebdc51470800a9f42b9e9decf17762081"}, + {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:303c252947ab4b14c08afeb52375b26781ccd6a5ccd81abcdfc1fafd14cf93c1"}, + {file = "contourpy-1.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:637f674226be46f6ba372fd29d9523dd977a291f66ab2a74fbeb5530bb3f445d"}, + {file = "contourpy-1.3.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:76a896b2f195b57db25d6b44e7e03f221d32fe318d03ede41f8b4d9ba1bff53c"}, + {file = "contourpy-1.3.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:e1fd23e9d01591bab45546c089ae89d926917a66dceb3abcf01f6105d927e2cb"}, + {file = "contourpy-1.3.0-cp311-cp311-win32.whl", hash = "sha256:d402880b84df3bec6eab53cd0cf802cae6a2ef9537e70cf75e91618a3801c20c"}, + {file = "contourpy-1.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:6cb6cc968059db9c62cb35fbf70248f40994dfcd7aa10444bbf8b3faeb7c2d67"}, + {file = "contourpy-1.3.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:570ef7cf892f0afbe5b2ee410c507ce12e15a5fa91017a0009f79f7d93a1268f"}, + {file = "contourpy-1.3.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:da84c537cb8b97d153e9fb208c221c45605f73147bd4cadd23bdae915042aad6"}, + {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0be4d8425bfa755e0fd76ee1e019636ccc7c29f77a7c86b4328a9eb6a26d0639"}, + {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c0da700bf58f6e0b65312d0a5e695179a71d0163957fa381bb3c1f72972537c"}, + {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:eb8b141bb00fa977d9122636b16aa67d37fd40a3d8b52dd837e536d64b9a4d06"}, + {file = "contourpy-1.3.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3634b5385c6716c258d0419c46d05c8aa7dc8cb70326c9a4fb66b69ad2b52e09"}, + {file = "contourpy-1.3.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:0dce35502151b6bd35027ac39ba6e5a44be13a68f55735c3612c568cac3805fd"}, + {file = "contourpy-1.3.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:aea348f053c645100612b333adc5983d87be69acdc6d77d3169c090d3b01dc35"}, + {file = "contourpy-1.3.0-cp312-cp312-win32.whl", hash = "sha256:90f73a5116ad1ba7174341ef3ea5c3150ddf20b024b98fb0c3b29034752c8aeb"}, + {file = "contourpy-1.3.0-cp312-cp312-win_amd64.whl", hash = "sha256:b11b39aea6be6764f84360fce6c82211a9db32a7c7de8fa6dd5397cf1d079c3b"}, + {file = "contourpy-1.3.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:3e1c7fa44aaae40a2247e2e8e0627f4bea3dd257014764aa644f319a5f8600e3"}, + {file = "contourpy-1.3.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:364174c2a76057feef647c802652f00953b575723062560498dc7930fc9b1cb7"}, + {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:32b238b3b3b649e09ce9aaf51f0c261d38644bdfa35cbaf7b263457850957a84"}, + {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:d51fca85f9f7ad0b65b4b9fe800406d0d77017d7270d31ec3fb1cc07358fdea0"}, + {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:732896af21716b29ab3e988d4ce14bc5133733b85956316fb0c56355f398099b"}, + {file = "contourpy-1.3.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d73f659398a0904e125280836ae6f88ba9b178b2fed6884f3b1f95b989d2c8da"}, + {file = "contourpy-1.3.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:c6c7c2408b7048082932cf4e641fa3b8ca848259212f51c8c59c45aa7ac18f14"}, + {file = "contourpy-1.3.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:f317576606de89da6b7e0861cf6061f6146ead3528acabff9236458a6ba467f8"}, + {file = "contourpy-1.3.0-cp313-cp313-win32.whl", hash = "sha256:31cd3a85dbdf1fc002280c65caa7e2b5f65e4a973fcdf70dd2fdcb9868069294"}, + {file = "contourpy-1.3.0-cp313-cp313-win_amd64.whl", hash = "sha256:4553c421929ec95fb07b3aaca0fae668b2eb5a5203d1217ca7c34c063c53d087"}, + {file = "contourpy-1.3.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:345af746d7766821d05d72cb8f3845dfd08dd137101a2cb9b24de277d716def8"}, + {file = "contourpy-1.3.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:3bb3808858a9dc68f6f03d319acd5f1b8a337e6cdda197f02f4b8ff67ad2057b"}, + {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:420d39daa61aab1221567b42eecb01112908b2cab7f1b4106a52caaec8d36973"}, + {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4d63ee447261e963af02642ffcb864e5a2ee4cbfd78080657a9880b8b1868e18"}, + {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:167d6c890815e1dac9536dca00828b445d5d0df4d6a8c6adb4a7ec3166812fa8"}, + {file = "contourpy-1.3.0-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:710a26b3dc80c0e4febf04555de66f5fd17e9cf7170a7b08000601a10570bda6"}, + {file = "contourpy-1.3.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:75ee7cb1a14c617f34a51d11fa7524173e56551646828353c4af859c56b766e2"}, + {file = "contourpy-1.3.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:33c92cdae89ec5135d036e7218e69b0bb2851206077251f04a6c4e0e21f03927"}, + {file = "contourpy-1.3.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:a11077e395f67ffc2c44ec2418cfebed032cd6da3022a94fc227b6faf8e2acb8"}, + {file = "contourpy-1.3.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:e8134301d7e204c88ed7ab50028ba06c683000040ede1d617298611f9dc6240c"}, + {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e12968fdfd5bb45ffdf6192a590bd8ddd3ba9e58360b29683c6bb71a7b41edca"}, + {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fd2a0fc506eccaaa7595b7e1418951f213cf8255be2600f1ea1b61e46a60c55f"}, + {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:4cfb5c62ce023dfc410d6059c936dcf96442ba40814aefbfa575425a3a7f19dc"}, + {file = "contourpy-1.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:68a32389b06b82c2fdd68276148d7b9275b5f5cf13e5417e4252f6d1a34f72a2"}, + {file = "contourpy-1.3.0-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:94e848a6b83da10898cbf1311a815f770acc9b6a3f2d646f330d57eb4e87592e"}, + {file = "contourpy-1.3.0-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:d78ab28a03c854a873787a0a42254a0ccb3cb133c672f645c9f9c8f3ae9d0800"}, + {file = "contourpy-1.3.0-cp39-cp39-win32.whl", hash = "sha256:81cb5ed4952aae6014bc9d0421dec7c5835c9c8c31cdf51910b708f548cf58e5"}, + {file = "contourpy-1.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:14e262f67bd7e6eb6880bc564dcda30b15e351a594657e55b7eec94b6ef72843"}, + {file = "contourpy-1.3.0-pp310-pypy310_pp73-macosx_10_15_x86_64.whl", hash = "sha256:fe41b41505a5a33aeaed2a613dccaeaa74e0e3ead6dd6fd3a118fb471644fd6c"}, + {file = "contourpy-1.3.0-pp310-pypy310_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:eca7e17a65f72a5133bdbec9ecf22401c62bcf4821361ef7811faee695799779"}, + {file = "contourpy-1.3.0-pp310-pypy310_pp73-win_amd64.whl", hash = "sha256:1ec4dc6bf570f5b22ed0d7efba0dfa9c5b9e0431aeea7581aa217542d9e809a4"}, + {file = "contourpy-1.3.0-pp39-pypy39_pp73-macosx_10_15_x86_64.whl", hash = "sha256:00ccd0dbaad6d804ab259820fa7cb0b8036bda0686ef844d24125d8287178ce0"}, + {file = "contourpy-1.3.0-pp39-pypy39_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8ca947601224119117f7c19c9cdf6b3ab54c5726ef1d906aa4a69dfb6dd58102"}, + {file = "contourpy-1.3.0-pp39-pypy39_pp73-win_amd64.whl", hash = "sha256:c6ec93afeb848a0845a18989da3beca3eec2c0f852322efe21af1931147d12cb"}, + {file = "contourpy-1.3.0.tar.gz", hash = "sha256:7ffa0db17717a8ffb127efd0c95a4362d996b892c2904db72428d5b52e1938a4"}, ] [package.dependencies] -numpy = ">=1.20" +numpy = ">=1.23" [package.extras] bokeh = ["bokeh", "selenium"] docs = ["furo", "sphinx (>=7.2)", "sphinx-copybutton"] -mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.8.0)", "types-Pillow"] +mypy = ["contourpy[bokeh,docs]", "docutils-stubs", "mypy (==1.11.1)", "types-Pillow"] test = ["Pillow", "contourpy[test-no-images]", "matplotlib"] -test-no-images = ["pytest", "pytest-cov", "pytest-xdist", "wurlitzer"] +test-no-images = ["pytest", "pytest-cov", "pytest-rerunfailures", "pytest-xdist", "wurlitzer"] [[package]] name = "cycler" @@ -319,6 +340,23 @@ files = [ marshmallow = ">=3.18.0,<4.0.0" typing-inspect = ">=0.4.0,<1" +[[package]] +name = "deprecated" +version = "1.2.14" +description = "Python @deprecated decorator to deprecate old python classes, functions or methods." +optional = false +python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +files = [ + {file = "Deprecated-1.2.14-py2.py3-none-any.whl", hash = "sha256:6fac8b097794a90302bdbb17b9b815e732d3c4720583ff1b198499d78470466c"}, + {file = "Deprecated-1.2.14.tar.gz", hash = "sha256:e5323eb936458dccc2582dc6f9c322c852a775a27065ff2b0c4970b9d53d01b3"}, +] + +[package.dependencies] +wrapt = ">=1.10,<2" + +[package.extras] +dev = ["PyTest", "PyTest-Cov", "bump2version (<1)", "sphinx (<2)", "tox"] + [[package]] name = "distlib" version = "0.3.8" @@ -465,18 +503,18 @@ woff = ["brotli (>=1.0.1)", "brotlicffi (>=0.8.0)", "zopfli (>=0.1.4)"] [[package]] name = "freetype-py" -version = "2.4.0" +version = "2.5.1" description = "Freetype python bindings" optional = false python-versions = ">=3.7" files = [ - {file = "freetype-py-2.4.0.zip", hash = "sha256:8ad81195d2f8f339aba61700cebfbd77defad149c51f59b75a2a5e37833ae12e"}, - {file = "freetype_py-2.4.0-py3-none-macosx_10_9_universal2.whl", hash = "sha256:3e0f5a91bc812f42d98a92137e86bac4ed037a29e43dafdb76d716d5732189e8"}, - {file = "freetype_py-2.4.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c9a3abc277f5f6d21575c0093c0c6139c161bf05b91aa6258505ab27c5001c5e"}, - {file = "freetype_py-2.4.0-py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:ce931f581d5038c4fea1f3d314254e0264e92441a5fdaef6817fe77b7bb888d3"}, - {file = "freetype_py-2.4.0-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:c6276d92ac401c8ce02ea391fc854de413b01a8d835fb394ee5eb6f04fc947f5"}, - {file = "freetype_py-2.4.0-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:9614f68876e9c62e821dfa811dd6160e00279d9d98cf60118cb264be48da1472"}, - {file = "freetype_py-2.4.0-py3-none-win_amd64.whl", hash = "sha256:a2620788d4f0c00bd75fee2dfca61635ab0da856131598c96e2355d5257f70e5"}, + {file = "freetype-py-2.5.1.zip", hash = "sha256:cfe2686a174d0dd3d71a9d8ee9bf6a2c23f5872385cf8ce9f24af83d076e2fbd"}, + {file = "freetype_py-2.5.1-py3-none-macosx_10_9_universal2.whl", hash = "sha256:d01ded2557694f06aa0413f3400c0c0b2b5ebcaabeef7aaf3d756be44f51e90b"}, + {file = "freetype_py-2.5.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:5d2f6b3d68496797da23204b3b9c4e77e67559c80390fc0dc8b3f454ae1cd819"}, + {file = "freetype_py-2.5.1-py3-none-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:289b443547e03a4f85302e3ac91376838e0d11636050166662a4f75e3087ed0b"}, + {file = "freetype_py-2.5.1-py3-none-musllinux_1_1_aarch64.whl", hash = "sha256:cd3bfdbb7e1a84818cfbc8025fca3096f4f2afcd5d4641184bf0a3a2e6f97bbf"}, + {file = "freetype_py-2.5.1-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:3c1aefc4f0d5b7425f014daccc5fdc7c6f914fb7d6a695cc684f1c09cd8c1660"}, + {file = "freetype_py-2.5.1-py3-none-win_amd64.whl", hash = "sha256:0b7f8e0342779f65ca13ef8bc103938366fecade23e6bb37cb671c2b8ad7f124"}, ] [[package]] @@ -506,13 +544,13 @@ license = ["ukkonen"] [[package]] name = "idna" -version = "3.7" +version = "3.8" description = "Internationalized Domain Names in Applications (IDNA)" optional = false -python-versions = ">=3.5" +python-versions = ">=3.6" files = [ - {file = "idna-3.7-py3-none-any.whl", hash = "sha256:82fee1fc78add43492d3a1898bfa6d8a904cc97d8427f683ed8e798d07761aa0"}, - {file = "idna-3.7.tar.gz", hash = "sha256:028ff3aadf0609c1fd278d8ea3089299412a7a8b9bd005dd08b9f8285bcb5cfc"}, + {file = "idna-3.8-py3-none-any.whl", hash = "sha256:050b4e5baadcd44d760cedbd2b8e639f2ff89bbc7a5730fcc662954303377aac"}, + {file = "idna-3.8.tar.gz", hash = "sha256:d838c2c0ed6fced7693d5e8ab8e734d5f8fda53a039c0164afb0b82e771e3603"}, ] [[package]] @@ -705,13 +743,13 @@ testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"] [[package]] name = "marshmallow" -version = "3.21.3" +version = "3.22.0" description = "A lightweight library for converting complex datatypes to and from native Python datatypes." optional = false python-versions = ">=3.8" files = [ - {file = "marshmallow-3.21.3-py3-none-any.whl", hash = "sha256:86ce7fb914aa865001a4b2092c4c2872d13bc347f3d42673272cabfdbad386f1"}, - {file = "marshmallow-3.21.3.tar.gz", hash = "sha256:4f57c5e050a54d66361e826f94fba213eb10b67b2fdb02c3e0343ce207ba1662"}, + {file = "marshmallow-3.22.0-py3-none-any.whl", hash = "sha256:71a2dce49ef901c3f97ed296ae5051135fd3febd2bf43afe0ae9a82143a494d9"}, + {file = "marshmallow-3.22.0.tar.gz", hash = "sha256:4972f529104a220bb8637d595aa4c9762afbe7f7a77d82dc58c1615d70c5823e"}, ] [package.dependencies] @@ -719,7 +757,7 @@ packaging = ">=17.0" [package.extras] dev = ["marshmallow[tests]", "pre-commit (>=3.5,<4.0)", "tox"] -docs = ["alabaster (==0.7.16)", "autodocsumm (==0.2.12)", "sphinx (==7.3.7)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] +docs = ["alabaster (==1.0.0)", "autodocsumm (==0.2.13)", "sphinx (==8.0.2)", "sphinx-issues (==4.1.0)", "sphinx-version-warning (==1.1.2)"] tests = ["pytest", "pytz", "simplejson"] [[package]] @@ -796,6 +834,17 @@ files = [ {file = "mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba"}, ] +[[package]] +name = "more-itertools" +version = "10.4.0" +description = "More routines for operating on iterables, beyond itertools" +optional = false +python-versions = ">=3.8" +files = [ + {file = "more-itertools-10.4.0.tar.gz", hash = "sha256:fe0e63c4ab068eac62410ab05cccca2dc71ec44ba8ef29916a0090df061cf923"}, + {file = "more_itertools-10.4.0-py3-none-any.whl", hash = "sha256:0f7d9f83a0a8dcfa8a2694a770590d98a67ea943e3d9f5298309a484758c4e27"}, +] + [[package]] name = "mypy-extensions" version = "1.0.0" @@ -932,13 +981,13 @@ files = [ [[package]] name = "patool" -version = "2.3.0" +version = "2.4.0" description = "portable archive file manager" optional = false python-versions = ">=3.10" files = [ - {file = "patool-2.3.0-py2.py3-none-any.whl", hash = "sha256:e91bcd067b3967eab38ac4c6d2673b834087fcd3e036affd7f64047d0c5546c9"}, - {file = "patool-2.3.0.tar.gz", hash = "sha256:498e294fd8c7d50889d65019d431c6867bf3fb1fec5ea2d39d1d39d1215002f8"}, + {file = "patool-2.4.0-py2.py3-none-any.whl", hash = "sha256:0ab5ab376b0f7838c495a8583fab2d73b43889820c7ef2e9300a7e5c952054f2"}, + {file = "patool-2.4.0.tar.gz", hash = "sha256:2c54bcd2b904bf37253fad4f5d04cec808056b15fa974fd36b2a84fc931f3c0f"}, ] [[package]] @@ -1329,13 +1378,13 @@ types = ["typing-extensions"] [[package]] name = "pyparsing" -version = "3.1.2" +version = "3.1.4" description = "pyparsing module - Classes and methods to define and execute parsing grammars" optional = false python-versions = ">=3.6.8" files = [ - {file = "pyparsing-3.1.2-py3-none-any.whl", hash = "sha256:f9db75911801ed778fe61bb643079ff86601aca99fcae6345aa67292038fb742"}, - {file = "pyparsing-3.1.2.tar.gz", hash = "sha256:a1bac0ce561155ecc3ed78ca94d3c9378656ad4c94c1270de543f621420f94ad"}, + {file = "pyparsing-3.1.4-py3-none-any.whl", hash = "sha256:a6a7ee4235a3f944aa1fa2249307708f893fe5717dc603503c6c7969c070fb7c"}, + {file = "pyparsing-3.1.4.tar.gz", hash = "sha256:f86ec8d1a83f11977c9a6ea7598e8c27fc5cddfa5b07ea2241edbbde1d7bc032"}, ] [package.extras] @@ -1482,13 +1531,13 @@ use-chardet-on-py3 = ["chardet (>=3.0.2,<6)"] [[package]] name = "rich" -version = "13.7.1" +version = "13.8.0" description = "Render rich text, tables, progress bars, syntax highlighting, markdown and more to the terminal" optional = false python-versions = ">=3.7.0" files = [ - {file = "rich-13.7.1-py3-none-any.whl", hash = "sha256:4edbae314f59eb482f54e9e30bf00d33350aaa94f4bfcd4e9e3110e64d0d7222"}, - {file = "rich-13.7.1.tar.gz", hash = "sha256:9be308cb1fe2f1f57d67ce99e95af38a1e2bc71ad9813b0e247cf7ffbcc3a432"}, + {file = "rich-13.8.0-py3-none-any.whl", hash = "sha256:2e85306a063b9492dffc86278197a60cbece75bcb766022f3436f567cae11bdc"}, + {file = "rich-13.8.0.tar.gz", hash = "sha256:a5ac1f1cd448ade0d59cc3356f7db7a7ccda2c8cbae9c7a90c28ff463d3e91f4"}, ] [package.dependencies] @@ -1500,63 +1549,71 @@ jupyter = ["ipywidgets (>=7.5.1,<9)"] [[package]] name = "ruff" -version = "0.6.1" +version = "0.6.3" description = "An extremely fast Python linter and code formatter, written in Rust." optional = false python-versions = ">=3.7" files = [ - {file = "ruff-0.6.1-py3-none-linux_armv6l.whl", hash = "sha256:b4bb7de6a24169dc023f992718a9417380301b0c2da0fe85919f47264fb8add9"}, - {file = "ruff-0.6.1-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:45efaae53b360c81043e311cdec8a7696420b3d3e8935202c2846e7a97d4edae"}, - {file = "ruff-0.6.1-py3-none-macosx_11_0_arm64.whl", hash = "sha256:bc60c7d71b732c8fa73cf995efc0c836a2fd8b9810e115be8babb24ae87e0850"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2c7477c3b9da822e2db0b4e0b59e61b8a23e87886e727b327e7dcaf06213c5cf"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a0af7ab3f86e3dc9f157a928e08e26c4b40707d0612b01cd577cc84b8905cc9"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:392688dbb50fecf1bf7126731c90c11a9df1c3a4cdc3f481b53e851da5634fa5"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:5278d3e095ccc8c30430bcc9bc550f778790acc211865520f3041910a28d0024"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:fe6d5f65d6f276ee7a0fc50a0cecaccb362d30ef98a110f99cac1c7872df2f18"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:b2e0dd11e2ae553ee5c92a81731d88a9883af8db7408db47fc81887c1f8b672e"}, - {file = "ruff-0.6.1-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d812615525a34ecfc07fd93f906ef5b93656be01dfae9a819e31caa6cfe758a1"}, - {file = "ruff-0.6.1-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:faaa4060f4064c3b7aaaa27328080c932fa142786f8142aff095b42b6a2eb631"}, - {file = "ruff-0.6.1-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:99d7ae0df47c62729d58765c593ea54c2546d5de213f2af2a19442d50a10cec9"}, - {file = "ruff-0.6.1-py3-none-musllinux_1_2_i686.whl", hash = "sha256:9eb18dfd7b613eec000e3738b3f0e4398bf0153cb80bfa3e351b3c1c2f6d7b15"}, - {file = "ruff-0.6.1-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:c62bc04c6723a81e25e71715aa59489f15034d69bf641df88cb38bdc32fd1dbb"}, - {file = "ruff-0.6.1-py3-none-win32.whl", hash = "sha256:9fb4c4e8b83f19c9477a8745e56d2eeef07a7ff50b68a6998f7d9e2e3887bdc4"}, - {file = "ruff-0.6.1-py3-none-win_amd64.whl", hash = "sha256:c2ebfc8f51ef4aca05dad4552bbcf6fe8d1f75b2f6af546cc47cc1c1ca916b5b"}, - {file = "ruff-0.6.1-py3-none-win_arm64.whl", hash = "sha256:3bc81074971b0ffad1bd0c52284b22411f02a11a012082a76ac6da153536e014"}, - {file = "ruff-0.6.1.tar.gz", hash = "sha256:af3ffd8c6563acb8848d33cd19a69b9bfe943667f0419ca083f8ebe4224a3436"}, + {file = "ruff-0.6.3-py3-none-linux_armv6l.whl", hash = "sha256:97f58fda4e309382ad30ede7f30e2791d70dd29ea17f41970119f55bdb7a45c3"}, + {file = "ruff-0.6.3-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3b061e49b5cf3a297b4d1c27ac5587954ccb4ff601160d3d6b2f70b1622194dc"}, + {file = "ruff-0.6.3-py3-none-macosx_11_0_arm64.whl", hash = "sha256:34e2824a13bb8c668c71c1760a6ac7d795ccbd8d38ff4a0d8471fdb15de910b1"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bddfbb8d63c460f4b4128b6a506e7052bad4d6f3ff607ebbb41b0aa19c2770d1"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:ced3eeb44df75353e08ab3b6a9e113b5f3f996bea48d4f7c027bc528ba87b672"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:47021dff5445d549be954eb275156dfd7c37222acc1e8014311badcb9b4ec8c1"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_ppc64.manylinux2014_ppc64.whl", hash = "sha256:7d7bd20dc07cebd68cc8bc7b3f5ada6d637f42d947c85264f94b0d1cd9d87384"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:500f166d03fc6d0e61c8e40a3ff853fa8a43d938f5d14c183c612df1b0d6c58a"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:42844ff678f9b976366b262fa2d1d1a3fe76f6e145bd92c84e27d172e3c34500"}, + {file = "ruff-0.6.3-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:70452a10eb2d66549de8e75f89ae82462159855e983ddff91bc0bce6511d0470"}, + {file = "ruff-0.6.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:65a533235ed55f767d1fc62193a21cbf9e3329cf26d427b800fdeacfb77d296f"}, + {file = "ruff-0.6.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d2e2c23cef30dc3cbe9cc5d04f2899e7f5e478c40d2e0a633513ad081f7361b5"}, + {file = "ruff-0.6.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:d8a136aa7d228975a6aee3dd8bea9b28e2b43e9444aa678fb62aeb1956ff2351"}, + {file = "ruff-0.6.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:f92fe93bc72e262b7b3f2bba9879897e2d58a989b4714ba6a5a7273e842ad2f8"}, + {file = "ruff-0.6.3-py3-none-win32.whl", hash = "sha256:7a62d3b5b0d7f9143d94893f8ba43aa5a5c51a0ffc4a401aa97a81ed76930521"}, + {file = "ruff-0.6.3-py3-none-win_amd64.whl", hash = "sha256:746af39356fee2b89aada06c7376e1aa274a23493d7016059c3a72e3b296befb"}, + {file = "ruff-0.6.3-py3-none-win_arm64.whl", hash = "sha256:14a9528a8b70ccc7a847637c29e56fd1f9183a9db743bbc5b8e0c4ad60592a82"}, + {file = "ruff-0.6.3.tar.gz", hash = "sha256:183b99e9edd1ef63be34a3b51fee0a9f4ab95add123dbf89a71f7b1f0c991983"}, ] [[package]] name = "scipy" -version = "1.14.0" +version = "1.14.1" description = "Fundamental algorithms for scientific computing in Python" optional = false python-versions = ">=3.10" files = [ - {file = "scipy-1.14.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:7e911933d54ead4d557c02402710c2396529540b81dd554fc1ba270eb7308484"}, - {file = "scipy-1.14.0-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:687af0a35462402dd851726295c1a5ae5f987bd6e9026f52e9505994e2f84ef6"}, - {file = "scipy-1.14.0-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:07e179dc0205a50721022344fb85074f772eadbda1e1b3eecdc483f8033709b7"}, - {file = "scipy-1.14.0-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:6a9c9a9b226d9a21e0a208bdb024c3982932e43811b62d202aaf1bb59af264b1"}, - {file = "scipy-1.14.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:076c27284c768b84a45dcf2e914d4000aac537da74236a0d45d82c6fa4b7b3c0"}, - {file = "scipy-1.14.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:42470ea0195336df319741e230626b6225a740fd9dce9642ca13e98f667047c0"}, - {file = "scipy-1.14.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:176c6f0d0470a32f1b2efaf40c3d37a24876cebf447498a4cefb947a79c21e9d"}, - {file = "scipy-1.14.0-cp310-cp310-win_amd64.whl", hash = "sha256:ad36af9626d27a4326c8e884917b7ec321d8a1841cd6dacc67d2a9e90c2f0359"}, - {file = "scipy-1.14.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:6d056a8709ccda6cf36cdd2eac597d13bc03dba38360f418560a93050c76a16e"}, - {file = "scipy-1.14.0-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:f0a50da861a7ec4573b7c716b2ebdcdf142b66b756a0d392c236ae568b3a93fb"}, - {file = "scipy-1.14.0-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:94c164a9e2498e68308e6e148646e486d979f7fcdb8b4cf34b5441894bdb9caf"}, - {file = "scipy-1.14.0-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:a7d46c3e0aea5c064e734c3eac5cf9eb1f8c4ceee756262f2c7327c4c2691c86"}, - {file = "scipy-1.14.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9eee2989868e274aae26125345584254d97c56194c072ed96cb433f32f692ed8"}, - {file = "scipy-1.14.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9e3154691b9f7ed73778d746da2df67a19d046a6c8087c8b385bc4cdb2cfca74"}, - {file = "scipy-1.14.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:c40003d880f39c11c1edbae8144e3813904b10514cd3d3d00c277ae996488cdb"}, - {file = "scipy-1.14.0-cp311-cp311-win_amd64.whl", hash = "sha256:5b083c8940028bb7e0b4172acafda6df762da1927b9091f9611b0bcd8676f2bc"}, - {file = "scipy-1.14.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:bff2438ea1330e06e53c424893ec0072640dac00f29c6a43a575cbae4c99b2b9"}, - {file = "scipy-1.14.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:bbc0471b5f22c11c389075d091d3885693fd3f5e9a54ce051b46308bc787e5d4"}, - {file = "scipy-1.14.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:64b2ff514a98cf2bb734a9f90d32dc89dc6ad4a4a36a312cd0d6327170339eb0"}, - {file = "scipy-1.14.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:7d3da42fbbbb860211a811782504f38ae7aaec9de8764a9bef6b262de7a2b50f"}, - {file = "scipy-1.14.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d91db2c41dd6c20646af280355d41dfa1ec7eead235642178bd57635a3f82209"}, - {file = "scipy-1.14.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a01cc03bcdc777c9da3cfdcc74b5a75caffb48a6c39c8450a9a05f82c4250a14"}, - {file = "scipy-1.14.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:65df4da3c12a2bb9ad52b86b4dcf46813e869afb006e58be0f516bc370165159"}, - {file = "scipy-1.14.0-cp312-cp312-win_amd64.whl", hash = "sha256:4c4161597c75043f7154238ef419c29a64ac4a7c889d588ea77690ac4d0d9b20"}, - {file = "scipy-1.14.0.tar.gz", hash = "sha256:b5923f48cb840380f9854339176ef21763118a7300a88203ccd0bdd26e58527b"}, + {file = "scipy-1.14.1-cp310-cp310-macosx_10_13_x86_64.whl", hash = "sha256:b28d2ca4add7ac16ae8bb6632a3c86e4b9e4d52d3e34267f6e1b0c1f8d87e389"}, + {file = "scipy-1.14.1-cp310-cp310-macosx_12_0_arm64.whl", hash = "sha256:d0d2821003174de06b69e58cef2316a6622b60ee613121199cb2852a873f8cf3"}, + {file = "scipy-1.14.1-cp310-cp310-macosx_14_0_arm64.whl", hash = "sha256:8bddf15838ba768bb5f5083c1ea012d64c9a444e16192762bd858f1e126196d0"}, + {file = "scipy-1.14.1-cp310-cp310-macosx_14_0_x86_64.whl", hash = "sha256:97c5dddd5932bd2a1a31c927ba5e1463a53b87ca96b5c9bdf5dfd6096e27efc3"}, + {file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ff0a7e01e422c15739ecd64432743cf7aae2b03f3084288f399affcefe5222d"}, + {file = "scipy-1.14.1-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8e32dced201274bf96899e6491d9ba3e9a5f6b336708656466ad0522d8528f69"}, + {file = "scipy-1.14.1-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:8426251ad1e4ad903a4514712d2fa8fdd5382c978010d1c6f5f37ef286a713ad"}, + {file = "scipy-1.14.1-cp310-cp310-win_amd64.whl", hash = "sha256:a49f6ed96f83966f576b33a44257d869756df6cf1ef4934f59dd58b25e0327e5"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_10_13_x86_64.whl", hash = "sha256:2da0469a4ef0ecd3693761acbdc20f2fdeafb69e6819cc081308cc978153c675"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:c0ee987efa6737242745f347835da2cc5bb9f1b42996a4d97d5c7ff7928cb6f2"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:3a1b111fac6baec1c1d92f27e76511c9e7218f1695d61b59e05e0fe04dc59617"}, + {file = "scipy-1.14.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:8475230e55549ab3f207bff11ebfc91c805dc3463ef62eda3ccf593254524ce8"}, + {file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:278266012eb69f4a720827bdd2dc54b2271c97d84255b2faaa8f161a158c3b37"}, + {file = "scipy-1.14.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:fef8c87f8abfb884dac04e97824b61299880c43f4ce675dd2cbeadd3c9b466d2"}, + {file = "scipy-1.14.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b05d43735bb2f07d689f56f7b474788a13ed8adc484a85aa65c0fd931cf9ccd2"}, + {file = "scipy-1.14.1-cp311-cp311-win_amd64.whl", hash = "sha256:716e389b694c4bb564b4fc0c51bc84d381735e0d39d3f26ec1af2556ec6aad94"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:631f07b3734d34aced009aaf6fedfd0eb3498a97e581c3b1e5f14a04164a456d"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:af29a935803cc707ab2ed7791c44288a682f9c8107bc00f0eccc4f92c08d6e07"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:2843f2d527d9eebec9a43e6b406fb7266f3af25a751aa91d62ff416f54170bc5"}, + {file = "scipy-1.14.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:eb58ca0abd96911932f688528977858681a59d61a7ce908ffd355957f7025cfc"}, + {file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:30ac8812c1d2aab7131a79ba62933a2a76f582d5dbbc695192453dae67ad6310"}, + {file = "scipy-1.14.1-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8f9ea80f2e65bdaa0b7627fb00cbeb2daf163caa015e59b7516395fe3bd1e066"}, + {file = "scipy-1.14.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:edaf02b82cd7639db00dbff629995ef185c8df4c3ffa71a5562a595765a06ce1"}, + {file = "scipy-1.14.1-cp312-cp312-win_amd64.whl", hash = "sha256:2ff38e22128e6c03ff73b6bb0f85f897d2362f8c052e3b8ad00532198fbdae3f"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:1729560c906963fc8389f6aac023739ff3983e727b1a4d87696b7bf108316a79"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_12_0_arm64.whl", hash = "sha256:4079b90df244709e675cdc8b93bfd8a395d59af40b72e339c2287c91860deb8e"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_14_0_arm64.whl", hash = "sha256:e0cf28db0f24a38b2a0ca33a85a54852586e43cf6fd876365c86e0657cfe7d73"}, + {file = "scipy-1.14.1-cp313-cp313-macosx_14_0_x86_64.whl", hash = "sha256:0c2f95de3b04e26f5f3ad5bb05e74ba7f68b837133a4492414b3afd79dfe540e"}, + {file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b99722ea48b7ea25e8e015e8341ae74624f72e5f21fc2abd45f3a93266de4c5d"}, + {file = "scipy-1.14.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5149e3fd2d686e42144a093b206aef01932a0059c2a33ddfa67f5f035bdfe13e"}, + {file = "scipy-1.14.1-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:e4f5a7c49323533f9103d4dacf4e4f07078f360743dec7f7596949149efeec06"}, + {file = "scipy-1.14.1-cp313-cp313-win_amd64.whl", hash = "sha256:baff393942b550823bfce952bb62270ee17504d02a1801d7fd0719534dfb9c84"}, + {file = "scipy-1.14.1.tar.gz", hash = "sha256:5a275584e726026a5699459aa72f828a610821006228e841b94275c4a7c08417"}, ] [package.dependencies] @@ -1564,8 +1621,8 @@ numpy = ">=1.23.5,<2.3" [package.extras] dev = ["cython-lint (>=0.12.2)", "doit (>=0.36.0)", "mypy (==1.10.0)", "pycodestyle", "pydevtool", "rich-click", "ruff (>=0.0.292)", "types-psutil", "typing_extensions"] -doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.13.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0)", "sphinx-design (>=0.4.0)"] -test = ["Cython", "array-api-strict", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] +doc = ["jupyterlite-pyodide-kernel", "jupyterlite-sphinx (>=0.13.1)", "jupytext", "matplotlib (>=3.5)", "myst-nb", "numpydoc", "pooch", "pydata-sphinx-theme (>=0.15.2)", "sphinx (>=5.0.0,<=7.3.7)", "sphinx-design (>=0.4.0)"] +test = ["Cython", "array-api-strict (>=2.0)", "asv", "gmpy2", "hypothesis (>=6.30)", "meson", "mpmath", "ninja", "pooch", "pytest", "pytest-cov", "pytest-timeout", "pytest-xdist", "scikit-umfpack", "threadpoolctl"] [[package]] name = "sexpdata" @@ -1580,47 +1637,53 @@ files = [ [[package]] name = "shapely" -version = "2.0.5" +version = "2.0.6" description = "Manipulation and analysis of geometric objects" optional = false python-versions = ">=3.7" files = [ - {file = "shapely-2.0.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:89d34787c44f77a7d37d55ae821f3a784fa33592b9d217a45053a93ade899375"}, - {file = "shapely-2.0.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:798090b426142df2c5258779c1d8d5734ec6942f778dab6c6c30cfe7f3bf64ff"}, - {file = "shapely-2.0.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:45211276900c4790d6bfc6105cbf1030742da67594ea4161a9ce6812a6721e68"}, - {file = "shapely-2.0.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2e119444bc27ca33e786772b81760f2028d930ac55dafe9bc50ef538b794a8e1"}, - {file = "shapely-2.0.5-cp310-cp310-win32.whl", hash = "sha256:9a4492a2b2ccbeaebf181e7310d2dfff4fdd505aef59d6cb0f217607cb042fb3"}, - {file = "shapely-2.0.5-cp310-cp310-win_amd64.whl", hash = "sha256:1e5cb5ee72f1bc7ace737c9ecd30dc174a5295fae412972d3879bac2e82c8fae"}, - {file = "shapely-2.0.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5bbfb048a74cf273db9091ff3155d373020852805a37dfc846ab71dde4be93ec"}, - {file = "shapely-2.0.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:93be600cbe2fbaa86c8eb70656369f2f7104cd231f0d6585c7d0aa555d6878b8"}, - {file = "shapely-2.0.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0f8e71bb9a46814019f6644c4e2560a09d44b80100e46e371578f35eaaa9da1c"}, - {file = "shapely-2.0.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d5251c28a29012e92de01d2e84f11637eb1d48184ee8f22e2df6c8c578d26760"}, - {file = "shapely-2.0.5-cp311-cp311-win32.whl", hash = "sha256:35110e80070d664781ec7955c7de557456b25727a0257b354830abb759bf8311"}, - {file = "shapely-2.0.5-cp311-cp311-win_amd64.whl", hash = "sha256:6c6b78c0007a34ce7144f98b7418800e0a6a5d9a762f2244b00ea560525290c9"}, - {file = "shapely-2.0.5-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:03bd7b5fa5deb44795cc0a503999d10ae9d8a22df54ae8d4a4cd2e8a93466195"}, - {file = "shapely-2.0.5-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2ff9521991ed9e201c2e923da014e766c1aa04771bc93e6fe97c27dcf0d40ace"}, - {file = "shapely-2.0.5-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1b65365cfbf657604e50d15161ffcc68de5cdb22a601bbf7823540ab4918a98d"}, - {file = "shapely-2.0.5-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:21f64e647a025b61b19585d2247137b3a38a35314ea68c66aaf507a1c03ef6fe"}, - {file = "shapely-2.0.5-cp312-cp312-win32.whl", hash = "sha256:3ac7dc1350700c139c956b03d9c3df49a5b34aaf91d024d1510a09717ea39199"}, - {file = "shapely-2.0.5-cp312-cp312-win_amd64.whl", hash = "sha256:30e8737983c9d954cd17feb49eb169f02f1da49e24e5171122cf2c2b62d65c95"}, - {file = "shapely-2.0.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:ff7731fea5face9ec08a861ed351734a79475631b7540ceb0b66fb9732a5f529"}, - {file = "shapely-2.0.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ff9e520af0c5a578e174bca3c18713cd47a6c6a15b6cf1f50ac17dc8bb8db6a2"}, - {file = "shapely-2.0.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:49b299b91557b04acb75e9732645428470825061f871a2edc36b9417d66c1fc5"}, - {file = "shapely-2.0.5-cp37-cp37m-win32.whl", hash = "sha256:b5870633f8e684bf6d1ae4df527ddcb6f3895f7b12bced5c13266ac04f47d231"}, - {file = "shapely-2.0.5-cp37-cp37m-win_amd64.whl", hash = "sha256:401cb794c5067598f50518e5a997e270cd7642c4992645479b915c503866abed"}, - {file = "shapely-2.0.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:e91ee179af539100eb520281ba5394919067c6b51824e6ab132ad4b3b3e76dd0"}, - {file = "shapely-2.0.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8af6f7260f809c0862741ad08b1b89cb60c130ae30efab62320bbf4ee9cc71fa"}, - {file = "shapely-2.0.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f5456dd522800306ba3faef77c5ba847ec30a0bd73ab087a25e0acdd4db2514f"}, - {file = "shapely-2.0.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b714a840402cde66fd7b663bb08cacb7211fa4412ea2a209688f671e0d0631fd"}, - {file = "shapely-2.0.5-cp38-cp38-win32.whl", hash = "sha256:7e8cf5c252fac1ea51b3162be2ec3faddedc82c256a1160fc0e8ddbec81b06d2"}, - {file = "shapely-2.0.5-cp38-cp38-win_amd64.whl", hash = "sha256:4461509afdb15051e73ab178fae79974387f39c47ab635a7330d7fee02c68a3f"}, - {file = "shapely-2.0.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:7545a39c55cad1562be302d74c74586f79e07b592df8ada56b79a209731c0219"}, - {file = "shapely-2.0.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:4c83a36f12ec8dee2066946d98d4d841ab6512a6ed7eb742e026a64854019b5f"}, - {file = "shapely-2.0.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:89e640c2cd37378480caf2eeda9a51be64201f01f786d127e78eaeff091ec897"}, - {file = "shapely-2.0.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:06efe39beafde3a18a21dde169d32f315c57da962826a6d7d22630025200c5e6"}, - {file = "shapely-2.0.5-cp39-cp39-win32.whl", hash = "sha256:8203a8b2d44dcb366becbc8c3d553670320e4acf0616c39e218c9561dd738d92"}, - {file = "shapely-2.0.5-cp39-cp39-win_amd64.whl", hash = "sha256:7fed9dbfbcfec2682d9a047b9699db8dcc890dfca857ecba872c42185fc9e64e"}, - {file = "shapely-2.0.5.tar.gz", hash = "sha256:bff2366bc786bfa6cb353d6b47d0443c570c32776612e527ee47b6df63fcfe32"}, + {file = "shapely-2.0.6-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:29a34e068da2d321e926b5073539fd2a1d4429a2c656bd63f0bd4c8f5b236d0b"}, + {file = "shapely-2.0.6-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e1c84c3f53144febf6af909d6b581bc05e8785d57e27f35ebaa5c1ab9baba13b"}, + {file = "shapely-2.0.6-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2ad2fae12dca8d2b727fa12b007e46fbc522148a584f5d6546c539f3464dccde"}, + {file = "shapely-2.0.6-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3304883bd82d44be1b27a9d17f1167fda8c7f5a02a897958d86c59ec69b705e"}, + {file = "shapely-2.0.6-cp310-cp310-win32.whl", hash = "sha256:3ec3a0eab496b5e04633a39fa3d5eb5454628228201fb24903d38174ee34565e"}, + {file = "shapely-2.0.6-cp310-cp310-win_amd64.whl", hash = "sha256:28f87cdf5308a514763a5c38de295544cb27429cfa655d50ed8431a4796090c4"}, + {file = "shapely-2.0.6-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:5aeb0f51a9db176da9a30cb2f4329b6fbd1e26d359012bb0ac3d3c7781667a9e"}, + {file = "shapely-2.0.6-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9a7a78b0d51257a367ee115f4d41ca4d46edbd0dd280f697a8092dd3989867b2"}, + {file = "shapely-2.0.6-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f32c23d2f43d54029f986479f7c1f6e09c6b3a19353a3833c2ffb226fb63a855"}, + {file = "shapely-2.0.6-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b3dc9fb0eb56498912025f5eb352b5126f04801ed0e8bdbd867d21bdbfd7cbd0"}, + {file = "shapely-2.0.6-cp311-cp311-win32.whl", hash = "sha256:d93b7e0e71c9f095e09454bf18dad5ea716fb6ced5df3cb044564a00723f339d"}, + {file = "shapely-2.0.6-cp311-cp311-win_amd64.whl", hash = "sha256:c02eb6bf4cfb9fe6568502e85bb2647921ee49171bcd2d4116c7b3109724ef9b"}, + {file = "shapely-2.0.6-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:cec9193519940e9d1b86a3b4f5af9eb6910197d24af02f247afbfb47bcb3fab0"}, + {file = "shapely-2.0.6-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:83b94a44ab04a90e88be69e7ddcc6f332da7c0a0ebb1156e1c4f568bbec983c3"}, + {file = "shapely-2.0.6-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:537c4b2716d22c92036d00b34aac9d3775e3691f80c7aa517c2c290351f42cd8"}, + {file = "shapely-2.0.6-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98fea108334be345c283ce74bf064fa00cfdd718048a8af7343c59eb40f59726"}, + {file = "shapely-2.0.6-cp312-cp312-win32.whl", hash = "sha256:42fd4cd4834747e4990227e4cbafb02242c0cffe9ce7ef9971f53ac52d80d55f"}, + {file = "shapely-2.0.6-cp312-cp312-win_amd64.whl", hash = "sha256:665990c84aece05efb68a21b3523a6b2057e84a1afbef426ad287f0796ef8a48"}, + {file = "shapely-2.0.6-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:42805ef90783ce689a4dde2b6b2f261e2c52609226a0438d882e3ced40bb3013"}, + {file = "shapely-2.0.6-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:6d2cb146191a47bd0cee8ff5f90b47547b82b6345c0d02dd8b25b88b68af62d7"}, + {file = "shapely-2.0.6-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e3fdef0a1794a8fe70dc1f514440aa34426cc0ae98d9a1027fb299d45741c381"}, + {file = "shapely-2.0.6-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2c665a0301c645615a107ff7f52adafa2153beab51daf34587170d85e8ba6805"}, + {file = "shapely-2.0.6-cp313-cp313-win32.whl", hash = "sha256:0334bd51828f68cd54b87d80b3e7cee93f249d82ae55a0faf3ea21c9be7b323a"}, + {file = "shapely-2.0.6-cp313-cp313-win_amd64.whl", hash = "sha256:d37d070da9e0e0f0a530a621e17c0b8c3c9d04105655132a87cfff8bd77cc4c2"}, + {file = "shapely-2.0.6-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:fa7468e4f5b92049c0f36d63c3e309f85f2775752e076378e36c6387245c5462"}, + {file = "shapely-2.0.6-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ed5867e598a9e8ac3291da6cc9baa62ca25706eea186117034e8ec0ea4355653"}, + {file = "shapely-2.0.6-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:81d9dfe155f371f78c8d895a7b7f323bb241fb148d848a2bf2244f79213123fe"}, + {file = "shapely-2.0.6-cp37-cp37m-win32.whl", hash = "sha256:fbb7bf02a7542dba55129062570211cfb0defa05386409b3e306c39612e7fbcc"}, + {file = "shapely-2.0.6-cp37-cp37m-win_amd64.whl", hash = "sha256:837d395fac58aa01aa544495b97940995211e3e25f9aaf87bc3ba5b3a8cd1ac7"}, + {file = "shapely-2.0.6-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:c6d88ade96bf02f6bfd667ddd3626913098e243e419a0325ebef2bbd481d1eb6"}, + {file = "shapely-2.0.6-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:8b3b818c4407eaa0b4cb376fd2305e20ff6df757bf1356651589eadc14aab41b"}, + {file = "shapely-2.0.6-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:1bbc783529a21f2bd50c79cef90761f72d41c45622b3e57acf78d984c50a5d13"}, + {file = "shapely-2.0.6-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2423f6c0903ebe5df6d32e0066b3d94029aab18425ad4b07bf98c3972a6e25a1"}, + {file = "shapely-2.0.6-cp38-cp38-win32.whl", hash = "sha256:2de00c3bfa80d6750832bde1d9487e302a6dd21d90cb2f210515cefdb616e5f5"}, + {file = "shapely-2.0.6-cp38-cp38-win_amd64.whl", hash = "sha256:3a82d58a1134d5e975f19268710e53bddd9c473743356c90d97ce04b73e101ee"}, + {file = "shapely-2.0.6-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:392f66f458a0a2c706254f473290418236e52aa4c9b476a072539d63a2460595"}, + {file = "shapely-2.0.6-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:eba5bae271d523c938274c61658ebc34de6c4b33fdf43ef7e938b5776388c1be"}, + {file = "shapely-2.0.6-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7060566bc4888b0c8ed14b5d57df8a0ead5c28f9b69fb6bed4476df31c51b0af"}, + {file = "shapely-2.0.6-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b02154b3e9d076a29a8513dffcb80f047a5ea63c897c0cd3d3679f29363cf7e5"}, + {file = "shapely-2.0.6-cp39-cp39-win32.whl", hash = "sha256:44246d30124a4f1a638a7d5419149959532b99dfa25b54393512e6acc9c211ac"}, + {file = "shapely-2.0.6-cp39-cp39-win_amd64.whl", hash = "sha256:2b542d7f1dbb89192d3512c52b679c822ba916f93479fa5d4fc2fe4fa0b3c9e8"}, + {file = "shapely-2.0.6.tar.gz", hash = "sha256:997f6159b1484059ec239cacaa53467fd8b5564dabe186cd84ac2944663b0bf6"}, ] [package.dependencies] @@ -1680,13 +1743,13 @@ psycopg = ["psycopg[binary,pool] (>=3.0.12,<4.0.0)"] [[package]] name = "typer" -version = "0.12.4" +version = "0.12.5" description = "Typer, build great CLIs. Easy to code. Based on Python type hints." optional = false python-versions = ">=3.7" files = [ - {file = "typer-0.12.4-py3-none-any.whl", hash = "sha256:819aa03699f438397e876aa12b0d63766864ecba1b579092cc9fe35d886e34b6"}, - {file = "typer-0.12.4.tar.gz", hash = "sha256:c9c1613ed6a166162705b3347b8d10b661ccc5d95692654d0fb628118f2c34e6"}, + {file = "typer-0.12.5-py3-none-any.whl", hash = "sha256:62fe4e471711b147e3365034133904df3e235698399bc4de2b36c8579298d52b"}, + {file = "typer-0.12.5.tar.gz", hash = "sha256:f592f089bedcc8ec1b974125d64851029c3b1af145f04aca64d69410f0c9b722"}, ] [package.dependencies] @@ -1807,7 +1870,86 @@ objprint = ">0.1.3" [package.extras] full = ["orjson"] +[[package]] +name = "wrapt" +version = "1.16.0" +description = "Module for decorators, wrappers and monkey patching." +optional = false +python-versions = ">=3.6" +files = [ + {file = "wrapt-1.16.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:ffa565331890b90056c01db69c0fe634a776f8019c143a5ae265f9c6bc4bd6d4"}, + {file = "wrapt-1.16.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e4fdb9275308292e880dcbeb12546df7f3e0f96c6b41197e0cf37d2826359020"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bb2dee3874a500de01c93d5c71415fcaef1d858370d405824783e7a8ef5db440"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2a88e6010048489cda82b1326889ec075a8c856c2e6a256072b28eaee3ccf487"}, + {file = "wrapt-1.16.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ac83a914ebaf589b69f7d0a1277602ff494e21f4c2f743313414378f8f50a4cf"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:73aa7d98215d39b8455f103de64391cb79dfcad601701a3aa0dddacf74911d72"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:807cc8543a477ab7422f1120a217054f958a66ef7314f76dd9e77d3f02cdccd0"}, + {file = "wrapt-1.16.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bf5703fdeb350e36885f2875d853ce13172ae281c56e509f4e6eca049bdfb136"}, + {file = "wrapt-1.16.0-cp310-cp310-win32.whl", hash = "sha256:f6b2d0c6703c988d334f297aa5df18c45e97b0af3679bb75059e0e0bd8b1069d"}, + {file = "wrapt-1.16.0-cp310-cp310-win_amd64.whl", hash = "sha256:decbfa2f618fa8ed81c95ee18a387ff973143c656ef800c9f24fb7e9c16054e2"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:1a5db485fe2de4403f13fafdc231b0dbae5eca4359232d2efc79025527375b09"}, + {file = "wrapt-1.16.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:75ea7d0ee2a15733684badb16de6794894ed9c55aa5e9903260922f0482e687d"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a452f9ca3e3267cd4d0fcf2edd0d035b1934ac2bd7e0e57ac91ad6b95c0c6389"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:43aa59eadec7890d9958748db829df269f0368521ba6dc68cc172d5d03ed8060"}, + {file = "wrapt-1.16.0-cp311-cp311-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:72554a23c78a8e7aa02abbd699d129eead8b147a23c56e08d08dfc29cfdddca1"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:d2efee35b4b0a347e0d99d28e884dfd82797852d62fcd7ebdeee26f3ceb72cf3"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:6dcfcffe73710be01d90cae08c3e548d90932d37b39ef83969ae135d36ef3956"}, + {file = "wrapt-1.16.0-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:eb6e651000a19c96f452c85132811d25e9264d836951022d6e81df2fff38337d"}, + {file = "wrapt-1.16.0-cp311-cp311-win32.whl", hash = "sha256:66027d667efe95cc4fa945af59f92c5a02c6f5bb6012bff9e60542c74c75c362"}, + {file = "wrapt-1.16.0-cp311-cp311-win_amd64.whl", hash = "sha256:aefbc4cb0a54f91af643660a0a150ce2c090d3652cf4052a5397fb2de549cd89"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5eb404d89131ec9b4f748fa5cfb5346802e5ee8836f57d516576e61f304f3b7b"}, + {file = "wrapt-1.16.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9090c9e676d5236a6948330e83cb89969f433b1943a558968f659ead07cb3b36"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:94265b00870aa407bd0cbcfd536f17ecde43b94fb8d228560a1e9d3041462d73"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f2058f813d4f2b5e3a9eb2eb3faf8f1d99b81c3e51aeda4b168406443e8ba809"}, + {file = "wrapt-1.16.0-cp312-cp312-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:98b5e1f498a8ca1858a1cdbffb023bfd954da4e3fa2c0cb5853d40014557248b"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:14d7dc606219cdd7405133c713f2c218d4252f2a469003f8c46bb92d5d095d81"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_i686.whl", hash = "sha256:49aac49dc4782cb04f58986e81ea0b4768e4ff197b57324dcbd7699c5dfb40b9"}, + {file = "wrapt-1.16.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:418abb18146475c310d7a6dc71143d6f7adec5b004ac9ce08dc7a34e2babdc5c"}, + {file = "wrapt-1.16.0-cp312-cp312-win32.whl", hash = "sha256:685f568fa5e627e93f3b52fda002c7ed2fa1800b50ce51f6ed1d572d8ab3e7fc"}, + {file = "wrapt-1.16.0-cp312-cp312-win_amd64.whl", hash = "sha256:dcdba5c86e368442528f7060039eda390cc4091bfd1dca41e8046af7c910dda8"}, + {file = "wrapt-1.16.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:d462f28826f4657968ae51d2181a074dfe03c200d6131690b7d65d55b0f360f8"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a33a747400b94b6d6b8a165e4480264a64a78c8a4c734b62136062e9a248dd39"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b3646eefa23daeba62643a58aac816945cadc0afaf21800a1421eeba5f6cfb9c"}, + {file = "wrapt-1.16.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3ebf019be5c09d400cf7b024aa52b1f3aeebeff51550d007e92c3c1c4afc2a40"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:0d2691979e93d06a95a26257adb7bfd0c93818e89b1406f5a28f36e0d8c1e1fc"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:1acd723ee2a8826f3d53910255643e33673e1d11db84ce5880675954183ec47e"}, + {file = "wrapt-1.16.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:bc57efac2da352a51cc4658878a68d2b1b67dbe9d33c36cb826ca449d80a8465"}, + {file = "wrapt-1.16.0-cp36-cp36m-win32.whl", hash = "sha256:da4813f751142436b075ed7aa012a8778aa43a99f7b36afe9b742d3ed8bdc95e"}, + {file = "wrapt-1.16.0-cp36-cp36m-win_amd64.whl", hash = "sha256:6f6eac2360f2d543cc875a0e5efd413b6cbd483cb3ad7ebf888884a6e0d2e966"}, + {file = "wrapt-1.16.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a0ea261ce52b5952bf669684a251a66df239ec6d441ccb59ec7afa882265d593"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:7bd2d7ff69a2cac767fbf7a2b206add2e9a210e57947dd7ce03e25d03d2de292"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9159485323798c8dc530a224bd3ffcf76659319ccc7bbd52e01e73bd0241a0c5"}, + {file = "wrapt-1.16.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a86373cf37cd7764f2201b76496aba58a52e76dedfaa698ef9e9688bfd9e41cf"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:73870c364c11f03ed072dda68ff7aea6d2a3a5c3fe250d917a429c7432e15228"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:b935ae30c6e7400022b50f8d359c03ed233d45b725cfdd299462f41ee5ffba6f"}, + {file = "wrapt-1.16.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:db98ad84a55eb09b3c32a96c576476777e87c520a34e2519d3e59c44710c002c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win32.whl", hash = "sha256:9153ed35fc5e4fa3b2fe97bddaa7cbec0ed22412b85bcdaf54aeba92ea37428c"}, + {file = "wrapt-1.16.0-cp37-cp37m-win_amd64.whl", hash = "sha256:66dfbaa7cfa3eb707bbfcd46dab2bc6207b005cbc9caa2199bcbc81d95071a00"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:1dd50a2696ff89f57bd8847647a1c363b687d3d796dc30d4dd4a9d1689a706f0"}, + {file = "wrapt-1.16.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:44a2754372e32ab315734c6c73b24351d06e77ffff6ae27d2ecf14cf3d229202"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8e9723528b9f787dc59168369e42ae1c3b0d3fadb2f1a71de14531d321ee05b0"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:dbed418ba5c3dce92619656802cc5355cb679e58d0d89b50f116e4a9d5a9603e"}, + {file = "wrapt-1.16.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:941988b89b4fd6b41c3f0bfb20e92bd23746579736b7343283297c4c8cbae68f"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:6a42cd0cfa8ffc1915aef79cb4284f6383d8a3e9dcca70c445dcfdd639d51267"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:1ca9b6085e4f866bd584fb135a041bfc32cab916e69f714a7d1d397f8c4891ca"}, + {file = "wrapt-1.16.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:d5e49454f19ef621089e204f862388d29e6e8d8b162efce05208913dde5b9ad6"}, + {file = "wrapt-1.16.0-cp38-cp38-win32.whl", hash = "sha256:c31f72b1b6624c9d863fc095da460802f43a7c6868c5dda140f51da24fd47d7b"}, + {file = "wrapt-1.16.0-cp38-cp38-win_amd64.whl", hash = "sha256:490b0ee15c1a55be9c1bd8609b8cecd60e325f0575fc98f50058eae366e01f41"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:9b201ae332c3637a42f02d1045e1d0cccfdc41f1f2f801dafbaa7e9b4797bfc2"}, + {file = "wrapt-1.16.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2076fad65c6736184e77d7d4729b63a6d1ae0b70da4868adeec40989858eb3fb"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c5cd603b575ebceca7da5a3a251e69561bec509e0b46e4993e1cac402b7247b8"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:b47cfad9e9bbbed2339081f4e346c93ecd7ab504299403320bf85f7f85c7d46c"}, + {file = "wrapt-1.16.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f8212564d49c50eb4565e502814f694e240c55551a5f1bc841d4fcaabb0a9b8a"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:5f15814a33e42b04e3de432e573aa557f9f0f56458745c2074952f564c50e664"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:db2e408d983b0e61e238cf579c09ef7020560441906ca990fe8412153e3b291f"}, + {file = "wrapt-1.16.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:edfad1d29c73f9b863ebe7082ae9321374ccb10879eeabc84ba3b69f2579d537"}, + {file = "wrapt-1.16.0-cp39-cp39-win32.whl", hash = "sha256:ed867c42c268f876097248e05b6117a65bcd1e63b779e916fe2e33cd6fd0d3c3"}, + {file = "wrapt-1.16.0-cp39-cp39-win_amd64.whl", hash = "sha256:eb1b046be06b0fce7249f1d025cd359b4b80fc1c3e24ad9eca33e0dcdb2e4a35"}, + {file = "wrapt-1.16.0-py3-none-any.whl", hash = "sha256:6906c4100a8fcbf2fa735f6059214bb13b97f75b1a61777fcf6432121ef12ef1"}, + {file = "wrapt-1.16.0.tar.gz", hash = "sha256:5f370f952971e7d17c7d1ead40e49f32345a7f7a5373571ef44d800d06b1899d"}, +] + [metadata] lock-version = "2.0" python-versions = "^3.12,<3.13" -content-hash = "7b96e458fbfc778cabc91bcaef7a38704a50297d91f0d436c55e283510e2fc62" +content-hash = "d0dafbd8caf6f0e106af5c970066ea0d66b884103c18bacfc788f0a41c03688b" diff --git a/pyproject.toml b/pyproject.toml index 76b55190..c3adaed5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -46,6 +46,8 @@ typer = { version = ">=0.9,<0.13", extras = ["all"] } isort = "^5.6.4" ruff = ">=0.4.4,<0.7.0" pint = "^0.24.3" +deprecated = "^1.2.14" +more-itertools = "^10.4.0" [tool.poetry.group.dev.dependencies] pre-commit = ">=2.20,<4.0" diff --git a/src/faebryk/core/core.py b/src/faebryk/core/core.py index 4d0e98eb..4249d283 100644 --- a/src/faebryk/core/core.py +++ b/src/faebryk/core/core.py @@ -1,39 +1,12 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from __future__ import annotations - -import inspect import logging -from abc import ABC, abstractmethod -from itertools import pairwise -from typing import ( - Any, - Callable, - Generic, - Iterable, - Mapping, - Optional, - Sequence, - Type, - TypeVar, -) +from typing import Any, Callable -from typing_extensions import Self, deprecated +from typing_extensions import Self -from faebryk.core.graph_backends.default import GraphImpl -from faebryk.libs.util import ( - ConfigFlag, - Holder, - NotNone, - TwistArgs, - cast_assert, - groupby, - is_type_pair, - print_stack, - try_avoid_endless_recursion, - unique_ref, -) +from faebryk.libs.util import ConfigFlag logger = logging.getLogger(__name__) @@ -44,1344 +17,16 @@ ) ID_REPR = ConfigFlag("ID_REPR", False, "Add object id to repr") -# 1st order classes ----------------------------------------------------------- -T = TypeVar("T", bound="FaebrykLibObject") - - -class Trait(Generic[T]): - @classmethod - def impl(cls: Type[Trait]): - T_ = TypeVar("T_", bound="FaebrykLibObject") - - class _Impl(Generic[T_], TraitImpl[T_], cls): ... - - return _Impl[T] - - -U = TypeVar("U", bound="FaebrykLibObject") - - -class TraitImpl(Generic[U], ABC): - trait: Type[Trait[U]] - - def __init__(self) -> None: - super().__init__() - - if not hasattr(self, "_obj"): - self._obj: U | None = None - - found = False - bases = type(self).__bases__ - while not found: - for base in bases: - if not issubclass(base, TraitImpl) and issubclass(base, Trait): - self.trait = base - found = True - break - bases = [ - new_base - for base in bases - if issubclass(base, TraitImpl) - for new_base in base.__bases__ - ] - assert len(bases) > 0 - - assert type(self.trait) is type - assert issubclass(self.trait, Trait) - assert self.trait is not TraitImpl - - def set_obj(self, _obj: U): - self._obj = _obj - self.on_obj_set() - - def on_obj_set(self): ... - - def remove_obj(self): - self._obj = None - - def get_obj(self) -> U: - assert self._obj is not None, "trait is not linked to object" - return self._obj - - def cmp(self, other: TraitImpl) -> tuple[bool, TraitImpl]: - assert type(other), TraitImpl - - # If other same or more specific - if other.implements(self.trait): - return True, other - - # If we are more specific - if self.implements(other.trait): - return True, self - - return False, self - - def implements(self, trait: type): - assert issubclass(trait, Trait) - - return issubclass(self.trait, trait) - - # override this to implement a dynamic trait - def is_implemented(self): - return True - class FaebrykLibObject: - traits: list[TraitImpl] - - def __new__(cls, *args, **kwargs): - self = super().__new__(cls) - # TODO maybe dict[class => [obj] - self.traits = [] - return self - def __init__(self) -> None: ... - _TImpl = TypeVar("_TImpl", bound=TraitImpl) - - # TODO type checking InterfaceTrait -> Interface - def add_trait(self, trait: _TImpl) -> _TImpl: - assert isinstance(trait, TraitImpl), ("not a traitimpl:", trait) - assert isinstance(trait, Trait) - assert not hasattr(trait, "_obj") or trait._obj is None, "trait already in use" - trait.set_obj(self) - - # Override existing trait if more specific or same - # TODO deal with dynamic traits - for i, t in enumerate(self.traits): - hit, replace = t.cmp(trait) - if hit: - if replace == trait: - t.remove_obj() - self.traits[i] = replace - return replace - - # No hit: Add new trait - self.traits.append(trait) - return trait - - def _find(self, trait, only_implemented: bool): - return list( - filter( - lambda tup: tup[1].implements(trait) - and (tup[1].is_implemented() or not only_implemented), - enumerate(self.traits), - ) - ) - - def del_trait(self, trait): - candidates = self._find(trait, only_implemented=False) - assert len(candidates) <= 1 - if len(candidates) == 0: - return - assert len(candidates) == 1, "{} not in {}[{}]".format(trait, type(self), self) - i, impl = candidates[0] - assert self.traits[i] == impl - impl.remove_obj() - del self.traits[i] - - def has_trait(self, trait) -> bool: - return len(self._find(trait, only_implemented=True)) > 0 - - V = TypeVar("V", bound=Trait) - - def get_trait(self, trait: Type[V]) -> V: - assert not issubclass( - trait, TraitImpl - ), "You need to specify the trait, not an impl" - - candidates = self._find(trait, only_implemented=True) - assert len(candidates) <= 1 - assert len(candidates) == 1, "{} not in {}[{}]".format(trait, type(self), self) - - out = candidates[0][1] - assert isinstance(out, trait) - return out - def builder(self, op: Callable[[Self], Any]) -> Self: op(self) return self -# ----------------------------------------------------------------------------- - -# Traits ---------------------------------------------------------------------- -TI = TypeVar("TI", bound="GraphInterface") - - -class _InterfaceTrait(Generic[TI], Trait[TI]): ... - - -class InterfaceTrait(_InterfaceTrait["GraphInterface"]): ... - - -TN = TypeVar("TN", bound="Node") - - -class _NodeTrait(Generic[TN], Trait[TN]): ... - - -class NodeTrait(_NodeTrait["Node"]): ... - - -TL = TypeVar("TL", bound="Link") - - -class _LinkTrait(Generic[TL], Trait[TL]): ... - - -class LinkTrait(_LinkTrait["Link"]): ... - - -TP = TypeVar("TP", bound="Parameter") - - -class _ParameterTrait(Generic[TP], Trait[TP]): ... - - -class ParameterTrait(_ParameterTrait["Parameter"]): ... - - -class can_determine_partner_by_single_end(LinkTrait): - @abstractmethod - def get_partner(self, other: GraphInterface) -> GraphInterface: ... - - -# ----------------------------------------------------------------------------- - - -# FaebrykLibObjects ----------------------------------------------------------- -class Link(FaebrykLibObject): - def __init__(self) -> None: - super().__init__() - - if LINK_TB: - self.tb = inspect.stack() - - def get_connections(self) -> list[GraphInterface]: - raise NotImplementedError - - def __eq__(self, __value: Link) -> bool: - return set(self.get_connections()) == set(__value.get_connections()) - - def __hash__(self) -> int: - return super().__hash__() - - def __str__(self) -> str: - return ( - f"{type(self).__name__}" - f"([{', '.join(str(i) for i in self.get_connections())}])" - ) - - def __repr__(self) -> str: - return f"{type(self).__name__}()" - - -class LinkSibling(Link): - def __init__(self, interfaces: list[GraphInterface]) -> None: - super().__init__() - self.interfaces = interfaces - - def get_connections(self) -> list[GraphInterface]: - return self.interfaces - - -class LinkParent(Link): - def __init__(self, interfaces: list[GraphInterface]) -> None: - super().__init__() - - assert all([isinstance(i, GraphInterfaceHierarchical) for i in interfaces]) - # TODO rethink invariant - assert len(interfaces) == 2 - assert len([i for i in interfaces if i.is_parent]) == 1 # type: ignore - - self.interfaces: list[GraphInterfaceHierarchical] = interfaces # type: ignore - - def get_connections(self): - return self.interfaces - - def get_parent(self): - return [i for i in self.interfaces if i.is_parent][0] - - def get_child(self): - return [i for i in self.interfaces if not i.is_parent][0] - - -class LinkNamedParent(LinkParent): - def __init__(self, name: str, interfaces: list[GraphInterface]) -> None: - super().__init__(interfaces) - self.name = name - - @classmethod - def curry(cls, name: str): - def curried(interfaces: list[GraphInterface]): - return cls(name, interfaces) - - return curried - - -class LinkDirect(Link): - class _(can_determine_partner_by_single_end.impl()): - def get_partner(_self, other: GraphInterface): - obj = _self.get_obj() - assert isinstance(obj, LinkDirect) - return [i for i in obj.interfaces if i is not other][0] - - def __init__(self, interfaces: list[GraphInterface]) -> None: - super().__init__() - assert len(set(map(type, interfaces))) == 1 - self.interfaces = interfaces - - # TODO not really used, but quite heavy on the performance - # if len(interfaces) == 2: - # self.add_trait(LinkDirect._()) - - def get_connections(self) -> list[GraphInterface]: - return self.interfaces - - -class LinkFilteredException(Exception): ... - - -class _TLinkDirectShallow(LinkDirect): - def __new__(cls, *args, **kwargs): - if cls is _TLinkDirectShallow: - raise TypeError( - "Can't instantiate abstract class _TLinkDirectShallow directly" - ) - return LinkDirect.__new__(cls, *args, **kwargs) - - -def LinkDirectShallow(if_filter: Callable[[LinkDirect, GraphInterface], bool]): - class _LinkDirectShallow(_TLinkDirectShallow): - i_filter = if_filter - - def __init__(self, interfaces: list[GraphInterface]) -> None: - if not all(map(self.i_filter, interfaces)): - raise LinkFilteredException() - super().__init__(interfaces) - - return _LinkDirectShallow - - -Graph = GraphImpl["GraphInterface"] - - -class GraphInterface(FaebrykLibObject): - GT = Graph - - def __init__(self) -> None: - super().__init__() - self.G = self.GT() - - # can't put it into constructor - # else it needs a reference when defining IFs - self._node: Optional[Node] = None - self.name: str = type(self).__name__ - - @property - def node(self): - return NotNone(self._node) - - @node.setter - def node(self, value: Node): - self._node = value - - # Graph stuff - @property - def edges(self) -> Mapping[GraphInterface, Link]: - return self.G.get_edges(self) - - def get_links(self) -> list[Link]: - return list(self.edges.values()) - - def get_links_by_type[T: Link](self, link_type: type[T]) -> list[T]: - return [link for link in self.get_links() if isinstance(link, link_type)] - - @property - @deprecated("Use get_links") - def connections(self): - return self.get_links() - - def get_direct_connections(self) -> set[GraphInterface]: - return set(self.edges.keys()) - - def is_connected(self, other: GraphInterface): - return self.G.is_connected(self, other) - - # Less graph-specific stuff - - # TODO make link trait to initialize from list - def connect(self, other: Self, linkcls=None) -> Self: - assert other is not self - - if linkcls is None: - linkcls = LinkDirect - link = linkcls([other, self]) - - _, no_path = self.G.merge(other.G) - - if not no_path: - dup = self.is_connected(other) - assert ( - not dup or type(dup) is linkcls - ), f"Already connected with different link type: {dup}" - - self.G.add_edge(self, other, link=link) - - if logger.isEnabledFor(logging.DEBUG): - logger.debug(f"GIF connection: {link}") - - return self - - def get_full_name(self, types: bool = False): - typestr = f"|{type(self).__name__}|" if types else "" - return f"{self.node.get_full_name(types=types)}.{self.name}{typestr}" - - def __str__(self) -> str: - return f"{str(self.node)}.{self.name}" - - @try_avoid_endless_recursion - def __repr__(self) -> str: - id_str = f"(@{hex(id(self))})" if ID_REPR else "" - - return ( - f"{self.get_full_name(types=True)}{id_str}" - if self._node is not None - else "| " - ) - - -class GraphInterfaceHierarchical(GraphInterface): - def __init__(self, is_parent: bool) -> None: - super().__init__() - self.is_parent = is_parent - - # TODO make consistent api with get_parent - def get_children(self) -> list[tuple[str, Node]]: - assert self.is_parent - - hier_conns = self.get_links_by_type(LinkNamedParent) - if len(hier_conns) == 0: - return [] - - return [(c.name, c.get_child().node) for c in hier_conns] - - def get_parent(self) -> tuple[Node, str] | None: - assert not self.is_parent - - conns = self.get_links_by_type(LinkNamedParent) - if not conns: - return None - assert len(conns) == 1 - conn = conns[0] - parent = conn.get_parent() - - return parent.node, conn.name - - -class GraphInterfaceSelf(GraphInterface): ... - - -class GraphInterfaceModuleSibling(GraphInterfaceHierarchical): ... - - -class GraphInterfaceModuleConnection(GraphInterface): ... - - -class Node(FaebrykLibObject): - @classmethod - def GraphInterfacesCls(cls): - class InterfaceHolder(Holder(GraphInterface, cls)): - def handle_add(self, name: str, obj: GraphInterface) -> None: - assert isinstance(obj, GraphInterface) - parent: Node = self.get_parent() - obj.node = parent - obj.name = name - if not isinstance(obj, GraphInterfaceSelf): - if hasattr(self, "self"): - obj.connect(self.self, linkcls=LinkSibling) - if isinstance(obj, GraphInterfaceSelf): - assert obj is self.self - for target in self.get_all(): - if target is self.self: - continue - target.connect(obj, linkcls=LinkSibling) - return super().handle_add(name, obj) - - def __init__(self, parent: Node) -> None: - super().__init__(parent) - - # Default Component Interfaces - self.self = GraphInterfaceSelf() - self.children = GraphInterfaceHierarchical(is_parent=True) - self.parent = GraphInterfaceHierarchical(is_parent=False) - - return InterfaceHolder - - NT = TypeVar("NT", bound="Node") - - @classmethod - def NodesCls(cls, t: Type[NT]): - class NodeHolder(Holder(t, cls)): - def handle_add(self, name: str, obj: Node.NT) -> None: - assert isinstance(obj, t) - parent: Node = self.get_parent() - assert not ( - other_p := obj.get_parent() - ), f"{obj} already has parent: {other_p}" - obj.GIFs.parent.connect( - parent.GIFs.children, LinkNamedParent.curry(name) - ) - return super().handle_add(name, obj) - - def __init__(self, parent: Node) -> None: - super().__init__(parent) - - return NodeHolder - - @classmethod - def GIFS(cls): - return cls.GraphInterfacesCls() - - @classmethod - def NODES(cls): - return cls.NodesCls(Node) - - def __init__(self) -> None: - super().__init__() - - self.GIFs = Node.GIFS()(self) - self.NODEs = Node.NODES()(self) - - def get_graph(self): - return self.GIFs.self.G - - def get_parent(self): - return self.GIFs.parent.get_parent() - - def get_name(self): - p = self.get_parent() - if not p: - raise Exception("Parent required for name") - return p[1] - - def get_hierarchy(self) -> list[tuple[Node, str]]: - parent = self.get_parent() - if not parent: - return [(self, "*")] - parent_obj, name = parent - - return parent_obj.get_hierarchy() + [(self, name)] - - def get_full_name(self, types: bool = False): - hierarchy = self.get_hierarchy() - if types: - return ".".join([f"{name}|{type(obj).__name__}" for obj, name in hierarchy]) - else: - return ".".join([f"{name}" for _, name in hierarchy]) - - def inherit(self): - """ - Has to be called if inhereting from another module after setting the holders - """ - from faebryk.core.util import get_children - - children = get_children(self, direct_only=True, include_root=False) - by_name = groupby(children, lambda n: n.get_name()) - for _, cs in by_name.items(): - for c1, c2 in pairwise(cs): - # TODO check equal top-level types (module, etc) - if isinstance(c1, Parameter): - c1.merge(c2) - elif isinstance(c1, (ModuleInterface, GraphInterface)): - c1.connect(c2) - ... - - @try_avoid_endless_recursion - def __str__(self) -> str: - return f"<{self.get_full_name(types=True)}>" - - @try_avoid_endless_recursion - def __repr__(self) -> str: - id_str = f"(@{hex(id(self))})" if ID_REPR else "" - return f"<{self.get_full_name(types=True)}>{id_str}" - - -def _resolved[PV, O]( - func: Callable[[Parameter[PV], Parameter[PV]], O], -) -> Callable[ - [ - PV | set[PV] | tuple[PV, PV] | Parameter[PV], - PV | set[PV] | tuple[PV, PV] | Parameter[PV], - ], - O, -]: - def wrap(*args): - args = [Parameter.from_literal(arg).get_most_narrow() for arg in args] - return func(*args) - - return wrap - - -class Parameter[PV](Node): - type LIT = PV | set[PV] | tuple[PV, PV] - type LIT_OR_PARAM = LIT | "Parameter[PV]" - - class MergeException(Exception): ... - - class SupportsSetOps: - def __contains__(self, other: Parameter[PV].LIT_OR_PARAM) -> bool: ... - - def try_compress(self) -> Parameter[PV]: - return self - - @classmethod - def GIFS(cls): - class GIFS(Node.GIFS()): - narrowed_by = GraphInterface() - narrows = GraphInterface() - - return GIFS - - @classmethod - def PARAMS(cls): - class PARAMS(Module.NodesCls(Parameter)): - # workaround to help pylance - def get_all(self) -> list[Parameter]: - return [cast_assert(Parameter, i) for i in super().get_all()] - - def __str__(self) -> str: - return str({p.get_hierarchy()[-1][1]: p for p in self.get_all()}) - - return PARAMS - - def __init__(self) -> None: - super().__init__() - - self.GIFs = Parameter.GIFS()(self) - self.PARAMs = Parameter.PARAMS()(self) - - @classmethod - def from_literal(cls, value: LIT_OR_PARAM) -> Parameter[PV]: - from faebryk.library.Constant import Constant - from faebryk.library.Range import Range - from faebryk.library.Set import Set - - if isinstance(value, Parameter): - return value - elif isinstance(value, set): - return Set(value) - elif isinstance(value, tuple): - return Range(*value) - else: - return Constant(value) - - def _merge(self, other: Parameter[PV]) -> Parameter[PV]: - from faebryk.library.ANY import ANY - from faebryk.library.Operation import Operation - from faebryk.library.Set import Set - from faebryk.library.TBD import TBD - - def _is_pair(type1: type[T], type2: type[U]) -> Optional[tuple[T, U]]: - return is_type_pair(self, other, type1, type2) - - if self is other: - return self - - try: - if self == other: - return self - except ValueError: - ... - - if pair := _is_pair(Parameter[PV], TBD): - return pair[0] - - if pair := _is_pair(Parameter[PV], ANY): - return pair[0] - - # TODO remove as soon as possible - if pair := _is_pair(Parameter[PV], Operation): - # TODO make MergeOperation that inherits from Operation - # and return that instead, application can check if result is MergeOperation - # if it was checking mergeability - raise self.MergeException("cant merge range with operation") - - if pair := _is_pair(Parameter[PV], Parameter[PV].SupportsSetOps): - out = self.intersect(*pair) - if isinstance(out, Operation): - raise self.MergeException("not resolvable") - if out == Set([]) and not pair[0] == pair[1] == Set([]): - raise self.MergeException("conflicting sets/ranges") - return out - - raise NotImplementedError - - def _narrowed(self, other: Parameter[PV]): - if self is other: - return - - if self.GIFs.narrowed_by.is_connected(other.GIFs.narrows): - return - self.GIFs.narrowed_by.connect(other.GIFs.narrows) - - @_resolved - def is_mergeable_with(self: Parameter[PV], other: Parameter[PV]) -> bool: - try: - self._merge(other) - return True - except self.MergeException: - return False - except NotImplementedError: - return False - - @_resolved - def is_subset_of(self: Parameter[PV], other: Parameter[PV]) -> bool: - from faebryk.library.ANY import ANY - from faebryk.library.Operation import Operation - from faebryk.library.TBD import TBD - - lhs = self - rhs = other - - def is_either_instance(t: type[Parameter[PV]]): - return isinstance(lhs, t) or isinstance(rhs, t) - - # Not resolveable - if isinstance(rhs, ANY): - return True - if isinstance(lhs, ANY): - return False - if is_either_instance(TBD): - return False - if is_either_instance(Operation): - return False - - # Sets - return lhs & rhs == lhs - - @_resolved - def merge(self: Parameter[PV], other: Parameter[PV]) -> Parameter[PV]: - out = self._merge(other) - - self._narrowed(out) - other._narrowed(out) - - return out - - @_resolved - def override(self: Parameter[PV], other: Parameter[PV]) -> "Parameter[PV]": - if not other.is_subset_of(self): - raise self.MergeException("override not possible") - - self._narrowed(other) - return other - - # TODO: replace with graph-based - @staticmethod - def arithmetic_op( - op1: Parameter[PV], op2: Parameter[PV], op: Callable - ) -> "Parameter[PV]": - from faebryk.library.ANY import ANY - from faebryk.library.Constant import Constant - from faebryk.library.Operation import Operation - from faebryk.library.Range import Range - from faebryk.library.Set import Set - from faebryk.library.TBD import TBD - - def _is_pair(type1: type[T], type2: type[U]) -> Optional[tuple[T, U, Callable]]: - if isinstance(op1, type1) and isinstance(op2, type2): - return op1, op2, op - if isinstance(op1, type2) and isinstance(op2, type1): - return op2, op1, TwistArgs(op) - - return None - - if pair := _is_pair(Constant, Constant): - return Constant(op(pair[0].value, pair[1].value)) - - if pair := _is_pair(Range, Range): - try: - p0_min, p0_max = pair[0].min, pair[0].max - p1_min, p1_max = pair[1].min, pair[1].max - except Range.MinMaxError: - return Operation(pair[:2], op) - return Range( - *( - op(lhs, rhs) - for lhs, rhs in [ - (p0_min, p1_min), - (p0_max, p1_max), - (p0_min, p1_max), - (p0_max, p1_min), - ] - ) - ) - - if pair := _is_pair(Constant, Range): - sop = pair[2] - try: - return Range(*(sop(pair[0], bound) for bound in pair[1].bounds)) - except Range.MinMaxError: - return Operation(pair[:2], op) - - if pair := _is_pair(Parameter, ANY): - sop = pair[2] - return Operation(pair[:2], sop) - - if pair := _is_pair(Parameter, Operation): - sop = pair[2] - return Operation(pair[:2], sop) - - if pair := _is_pair(Parameter, TBD): - sop = pair[2] - return Operation(pair[:2], sop) - - if pair := _is_pair(Parameter, Set): - sop = pair[2] - return Set( - Parameter.arithmetic_op(nested, pair[0], sop) - for nested in pair[1].params - ) - - raise NotImplementedError - - @staticmethod - def intersect(op1: Parameter[PV], op2: Parameter[PV]) -> Parameter[PV]: - from faebryk.library.Constant import Constant - from faebryk.library.Operation import Operation - from faebryk.library.Range import Range - from faebryk.library.Set import Set - - if op1 == op2: - return op1 - - def _is_pair(type1: type[T], type2: type[U]) -> Optional[tuple[T, U, Callable]]: - if isinstance(op1, type1) and isinstance(op2, type2): - return op1, op2, op - if isinstance(op1, type2) and isinstance(op2, type1): - return op2, op1, TwistArgs(op) - - return None - - def op(a, b): - return a & b - - # same types - if pair := _is_pair(Constant, Constant): - return Set([]) - if pair := _is_pair(Set, Set): - return Set(pair[0].params.intersection(pair[1].params)) - if pair := _is_pair(Range, Range): - try: - min_ = max(pair[0].min, pair[1].min) - max_ = min(pair[0].max, pair[1].max) - if min_ > max_: - return Set([]) - if min_ == max_: - return Constant(min_) - return Range(max_, min_) - except Range.MinMaxError: - return Operation(pair[:2], op) - - # diff types - if pair := _is_pair(Constant, Range): - try: - if pair[0] in pair[1]: - return pair[0] - else: - return Set([]) - except Range.MinMaxError: - return Operation(pair[:2], op) - if pair := _is_pair(Constant, Set): - if pair[0] in pair[1]: - return pair[0] - else: - return Set([]) - if pair := _is_pair(Range, Set): - try: - return Set(i for i in pair[1].params if i in pair[0]) - except Range.MinMaxError: - return Operation(pair[:2], op) - - return Operation((op1, op2), op) - - @_resolved - def __add__(self: Parameter[PV], other: Parameter[PV]): - return self.arithmetic_op(self, other, lambda a, b: a + b) - - @_resolved - def __sub__(self: Parameter[PV], other: Parameter[PV]): - return self.arithmetic_op(self, other, lambda a, b: a - b) - - # TODO PV | float - @_resolved - def __mul__(self: Parameter[PV], other: Parameter[PV]): - return self.arithmetic_op(self, other, lambda a, b: a * b) - - # TODO PV | float - @_resolved - def __truediv__(self: Parameter[PV], other: Parameter[PV]): - return self.arithmetic_op(self, other, lambda a, b: a / b) - - @_resolved - def __and__(self: Parameter[PV], other: Parameter[PV]) -> Parameter[PV]: - return self.intersect(self, other) - - def get_most_narrow(self) -> Parameter[PV]: - out = self.get_narrowing_chain()[-1] - - com = out.try_compress() - if com is not out: - com = com.get_most_narrow() - out._narrowed(com) - out = com - - return out - - @staticmethod - def resolve_all(params: "Sequence[Parameter[PV]]") -> Parameter[PV]: - from faebryk.library.TBD import TBD - - params_set = list(params) - if not params_set: - return TBD[PV]() - it = iter(params_set) - most_specific = next(it) - for param in it: - most_specific = most_specific.merge(param) - - return most_specific - - @try_avoid_endless_recursion - def __str__(self) -> str: - narrowest = self.get_most_narrow() - if narrowest is self: - return super().__str__() - return str(narrowest) - - # @try_avoid_endless_recursion - # def __repr__(self) -> str: - # narrowest = self.get_most_narrow() - # if narrowest is self: - # return super().__repr__() - # # return f"{super().__repr__()} -> {repr(narrowest)}" - # return repr(narrowest) - - def get_narrowing_chain(self) -> list[Parameter]: - from faebryk.core.util import get_direct_connected_nodes - - out: list[Parameter] = [self] - narrowers = get_direct_connected_nodes(self.GIFs.narrowed_by, Parameter) - if narrowers: - assert len(narrowers) == 1, "Narrowing tree diverged" - out += next(iter(narrowers)).get_narrowing_chain() - assert id(self) not in map(id, out[1:]), "Narrowing tree cycle" - return out - - def get_narrowed_siblings(self) -> set[Parameter]: - from faebryk.core.util import get_direct_connected_nodes - - return get_direct_connected_nodes(self.GIFs.narrows, Parameter) - - def __copy__(self) -> Self: - return type(self)() - - def __deepcopy__(self, memo) -> Self: - return self.__copy__() - - -# ----------------------------------------------------------------------------- - -# TODO: move file-------------------------------------------------------------- -TMI = TypeVar("TMI", bound="ModuleInterface") - - -# The resolve functions are really weird -# You have to look into where they are called to make sense of what they are doing -# Chain resolve is for deciding what to do in a case like this -# if1 -> link1 -> if2 -> link2 -> if3 -# This will then decide with which link if1 and if3 are connected -def _resolve_link_transitive(links: Iterable[type[Link]]) -> type[Link]: - from faebryk.libs.util import is_type_set_subclasses - - uniq = set(links) - assert uniq - - if len(uniq) == 1: - return next(iter(uniq)) - - if is_type_set_subclasses(uniq, {_TLinkDirectShallow}): - # TODO this only works if the filter is identical - raise NotImplementedError() - - if is_type_set_subclasses(uniq, {LinkDirect, _TLinkDirectShallow}): - return [u for u in uniq if issubclass(u, _TLinkDirectShallow)][0] - - raise NotImplementedError() - - -# This one resolves the case if1 -> link1 -> if2; if1 -> link2 -> if2 -def _resolve_link_duplicate(links: Iterable[type[Link]]) -> type[Link]: - from faebryk.libs.util import is_type_set_subclasses - - uniq = set(links) - assert uniq - - if len(uniq) == 1: - return next(iter(uniq)) - - if is_type_set_subclasses(uniq, {LinkDirect, _TLinkDirectShallow}): - return [u for u in uniq if not issubclass(u, _TLinkDirectShallow)][0] - - raise NotImplementedError() - - -class _ModuleInterfaceTrait(Generic[TMI], Trait[TMI]): ... - - -class ModuleInterfaceTrait(_ModuleInterfaceTrait["ModuleInterface"]): ... - - -class _LEVEL: - """connect depth counter to debug connections in ModuleInterface""" - - def __init__(self) -> None: - self.value = 0 - - def inc(self): - self.value += 1 - return self.value - 1 - - def dec(self): - self.value -= 1 - - -_CONNECT_DEPTH = _LEVEL() - - -class ModuleInterface(Node): - @classmethod - def GIFS(cls): - class GIFS(Node.GIFS()): - specializes = GraphInterface() - specialized = GraphInterface() - connected = GraphInterfaceModuleConnection() - - return GIFS - - @classmethod - def IFS(cls): - class IFS(Module.NodesCls(ModuleInterface)): - # workaround to help pylance - def get_all(self) -> list[ModuleInterface]: - return [cast_assert(ModuleInterface, i) for i in super().get_all()] - - return IFS - - @classmethod - def PARAMS(cls): - class PARAMS(Module.NodesCls(Parameter)): - # workaround to help pylance - def get_all(self) -> list[Parameter]: - return [cast_assert(Parameter, i) for i in super().get_all()] - - def __str__(self) -> str: - return str({p.get_hierarchy()[-1][1]: p for p in self.get_all()}) - - return PARAMS - - # TODO rename - @classmethod - def LinkDirectShallow(cls): - """ - Make link that only connects up but not down - """ - - def test(node: Node): - return not any(isinstance(p[0], cls) for p in node.get_hierarchy()[:-1]) - - class _LinkDirectShallowMif( - LinkDirectShallow(lambda link, gif: test(gif.node)) - ): ... - - return _LinkDirectShallowMif - - _LinkDirectShallow: type[_TLinkDirectShallow] | None = None - - def __init__(self) -> None: - super().__init__() - self.GIFs = ModuleInterface.GIFS()(self) - self.PARAMs = ModuleInterface.PARAMS()(self) - self.IFs = ModuleInterface.IFS()(self) - if not type(self)._LinkDirectShallow: - type(self)._LinkDirectShallow = type(self).LinkDirectShallow() - - def _connect_siblings_and_connections( - self, other: ModuleInterface, linkcls: type[Link] - ) -> ModuleInterface: - from faebryk.core.util import get_connected_mifs_with_link - - if other is self: - return self - - # Already connected - if self.is_connected_to(other): - return self - - # if link is filtered, cancel here - self._connect_across_hierarchies(other, linkcls) - if not self.is_connected_to(other): - return self - - if logger.isEnabledFor(logging.DEBUG): - logger.debug(f"MIF connection: {self} to {other}") - - def cross_connect( - s_group: dict[ModuleInterface, type[Link]], - d_group: dict[ModuleInterface, type[Link]], - hint=None, - ): - if logger.isEnabledFor(logging.DEBUG) and hint is not None: - logger.debug(f"Connect {hint} {s_group} -> {d_group}") - - for s, slink in s_group.items(): - for d, dlink in d_group.items(): - # can happen while connection trees are resolving - if s is d: - continue - link = _resolve_link_transitive([slink, dlink, linkcls]) - - s._connect_across_hierarchies(d, linkcls=link) - - def _get_connected_mifs(gif: GraphInterface): - return {k: type(v) for k, v in get_connected_mifs_with_link(gif).items()} - - # Connect to all connections - s_con = _get_connected_mifs(self.GIFs.connected) | {self: linkcls} - d_con = _get_connected_mifs(other.GIFs.connected) | {other: linkcls} - cross_connect(s_con, d_con, "connections") - - # Connect to all siblings - s_sib = ( - _get_connected_mifs(self.GIFs.specialized) - | _get_connected_mifs(self.GIFs.specializes) - | {self: linkcls} - ) - d_sib = ( - _get_connected_mifs(other.GIFs.specialized) - | _get_connected_mifs(other.GIFs.specializes) - | {other: linkcls} - ) - cross_connect(s_sib, d_sib, "siblings") - - return self - - def _on_connect(self, other: ModuleInterface): - """override to handle custom connection logic""" - ... - - def _try_connect_down(self, other: ModuleInterface, linkcls: type[Link]) -> None: - from faebryk.core.util import zip_children_by_name - - if not isinstance(other, type(self)): - return - - for _, (src, dst) in zip_children_by_name(self, other, ModuleInterface).items(): - if src is None or dst is None: - continue - src.connect(dst, linkcls=linkcls) - - def _try_connect_up(self, other: ModuleInterface) -> None: - p1 = self.get_parent() - p2 = other.get_parent() - if not ( - p1 - and p2 - and p1[0] is not p2[0] - and isinstance(p1[0], type(p2[0])) - and isinstance(p1[0], ModuleInterface) - ): - return - - src_m = p1[0] - dst_m = p2[0] - assert isinstance(dst_m, ModuleInterface) - - def _is_connected(a, b): - assert isinstance(a, ModuleInterface) - assert isinstance(b, ModuleInterface) - return a.is_connected_to(b) - - src_m_is = src_m.IFs.get_all() - dst_m_is = dst_m.IFs.get_all() - connection_map = [ - (src_i, dst_i, _is_connected(src_i, dst_i)) - for src_i, dst_i in zip(src_m_is, dst_m_is) - ] - - assert connection_map - - if not all(connected for _, _, connected in connection_map): - return - - # decide which LinkType to use here - # depends on connections between src_i & dst_i - # e.g. if any Shallow, we need to choose shallow - link = _resolve_link_transitive( - [type(sublink) for _, _, sublink in connection_map if sublink] - ) - - if logger.isEnabledFor(logging.DEBUG): - logger.debug(f"Up connect {src_m} -> {dst_m}") - src_m.connect(dst_m, linkcls=link) - - def _connect_across_hierarchies(self, other: ModuleInterface, linkcls: type[Link]): - existing_link = self.is_connected_to(other) - if existing_link: - if isinstance(existing_link, linkcls): - return - resolved = _resolve_link_duplicate([type(existing_link), linkcls]) - if resolved is type(existing_link): - return - if LINK_TB: - print(print_stack(existing_link.tb)) - raise NotImplementedError( - "Overriding existing links not implemented, tried to override " - + f"{existing_link} with {resolved}" - ) - - # level 0 connect - try: - self.GIFs.connected.connect(other.GIFs.connected, linkcls=linkcls) - except LinkFilteredException: - return - - if logger.isEnabledFor(logging.DEBUG): - logger.debug(f"{' '*2*_CONNECT_DEPTH.inc()}Connect {self} to {other}") - self._on_connect(other) - - con_depth_one = _CONNECT_DEPTH.value == 1 - recursion_error = None - try: - # level +1 (down) connect - self._try_connect_down(other, linkcls=linkcls) - - # level -1 (up) connect - self._try_connect_up(other) - - except RecursionError as e: - recursion_error = e - if not con_depth_one: - raise - - if recursion_error: - raise Exception(f"Recursion error while connecting {self} to {other}") - - _CONNECT_DEPTH.dec() - - def get_direct_connections(self) -> set[ModuleInterface]: - return { - gif.node - for gif in self.GIFs.connected.get_direct_connections() - if isinstance(gif.node, ModuleInterface) and gif.node is not self - } - - def connect(self, other: Self, linkcls=None) -> Self: - # TODO consider some type of check at the end within the graph instead - # assert type(other) is type(self) - if linkcls is None: - linkcls = LinkDirect - return self._connect_siblings_and_connections(other, linkcls=linkcls) - - def connect_via( - self, bridge: Node | Sequence[Node], other: Self | None = None, linkcls=None - ): - from faebryk.library.can_bridge import can_bridge - - bridges = [bridge] if isinstance(bridge, Node) else bridge - intf = self - for sub_bridge in bridges: - t = sub_bridge.get_trait(can_bridge) - intf.connect(t.get_in(), linkcls=linkcls) - intf = t.get_out() - - if other: - intf.connect(other, linkcls=linkcls) - - def connect_shallow(self, other: Self) -> Self: - return self.connect(other, linkcls=type(self)._LinkDirectShallow) - - def is_connected_to(self, other: ModuleInterface): - return self.GIFs.connected.is_connected(other.GIFs.connected) - - -TM = TypeVar("TM", bound="Module") - - -class _ModuleTrait(Generic[TM], _NodeTrait[TM]): ... - - -class ModuleTrait(_ModuleTrait["Module"]): ... - - -class Module(Node): - @classmethod - def GIFS(cls): - class GIFS(Node.GIFS()): - # TODO - specializes = GraphInterface() - specialized = GraphInterface() - - return GIFS - - @classmethod - def IFS(cls): - class IFS(Module.NodesCls(ModuleInterface)): - # workaround to help pylance - def get_all(self) -> list[ModuleInterface]: - return [cast_assert(ModuleInterface, i) for i in super().get_all()] - - return IFS - - @classmethod - def PARAMS(cls): - class PARAMS(Module.NodesCls(Parameter)): - # workaround to help pylance - def get_all(self) -> list[Parameter]: - return [cast_assert(Parameter, i) for i in super().get_all()] - - def __str__(self) -> str: - return str({p.get_hierarchy()[-1][1]: p for p in self.get_all()}) - - return PARAMS - - def __init__(self) -> None: - super().__init__() - - self.GIFs = Module.GIFS()(self) - self.IFs = Module.IFS()(self) - self.PARAMs = Module.PARAMS()(self) - - def get_most_special(self) -> Module: - specialers = { - specialer - for specialer_gif in self.GIFs.specialized.get_direct_connections() - if (specialer := specialer_gif.node) is not self - and isinstance(specialer, Module) - } - if not specialers: - return self - - specialest_next = unique_ref( - specialer.get_most_special() for specialer in specialers - ) - - assert ( - len(specialest_next) == 1 - ), f"Ambiguous specialest {specialest_next} for {self}" - return next(iter(specialest_next)) - +class Namespace: + """Marker class for namespace objects.""" -# ----------------------------------------------------------------------------- + pass diff --git a/src/faebryk/core/graph.py b/src/faebryk/core/graph.py index a2a1ee27..1dff6c16 100644 --- a/src/faebryk/core/graph.py +++ b/src/faebryk/core/graph.py @@ -20,7 +20,7 @@ # only for typechecker if TYPE_CHECKING: - from faebryk.core.core import Link + from faebryk.core.link import Link # TODO create GraphView base class @@ -94,6 +94,10 @@ def v(self, obj: T): ... @abstractmethod def add_edge(self, from_obj: T, to_obj: T, link: "Link"): ... + # TODO implement everywhere + @abstractmethod + def remove_edge(self, from_obj: T, to_obj: T | None = None): ... + @abstractmethod def is_connected(self, from_obj: T, to_obj: T) -> "Link | None": ... diff --git a/src/faebryk/core/graph_backends/graphgt.py b/src/faebryk/core/graph_backends/graphgt.py index a1701f02..d8f25bc8 100644 --- a/src/faebryk/core/graph_backends/graphgt.py +++ b/src/faebryk/core/graph_backends/graphgt.py @@ -15,7 +15,7 @@ # only for typechecker if TYPE_CHECKING: - from faebryk.core.core import Link + from faebryk.core.link import Link class GraphGT[T](Graph[T, gt.Graph]): diff --git a/src/faebryk/core/graph_backends/graphnx.py b/src/faebryk/core/graph_backends/graphnx.py index 440cb674..ea22051f 100644 --- a/src/faebryk/core/graph_backends/graphnx.py +++ b/src/faebryk/core/graph_backends/graphnx.py @@ -13,7 +13,7 @@ # only for typechecker if TYPE_CHECKING: - from faebryk.core.core import Link + from faebryk.core.link import Link class GraphNX[T](Graph[T, nx.Graph]): diff --git a/src/faebryk/core/graph_backends/graphpy.py b/src/faebryk/core/graph_backends/graphpy.py index ab3dbbc5..40f95ef1 100644 --- a/src/faebryk/core/graph_backends/graphpy.py +++ b/src/faebryk/core/graph_backends/graphpy.py @@ -12,7 +12,7 @@ # only for typechecker if TYPE_CHECKING: - from faebryk.core.core import Link + from faebryk.core.link import Link type L = "Link" @@ -40,6 +40,13 @@ def add_edge(self, from_obj: T, to_obj: T, link: L): self._v.add(from_obj) self._v.add(to_obj) + def remove_edge(self, from_obj: T, to_obj: T | None = None): + targets = [to_obj] if to_obj else list(self.edges(from_obj).keys()) + for target in targets: + self._e.remove((from_obj, target, self._e_cache[from_obj][target])) + del self._e_cache[from_obj][target] + del self._e_cache[target][from_obj] + def update(self, other: "PyGraph[T]"): self._v.update(other._v) self._e.extend(other._e) @@ -103,6 +110,9 @@ def v(self, obj: T): def add_edge(self, from_obj: T, to_obj: T, link: L): self().add_edge(from_obj, to_obj, link=link) + def remove_edge(self, from_obj: T, to_obj: T | None = None): + return self().remove_edge(from_obj, to_obj) + def is_connected(self, from_obj: T, to_obj: T) -> "Link | None": return self.get_edges(from_obj).get(to_obj) diff --git a/src/faebryk/core/graphinterface.py b/src/faebryk/core/graphinterface.py new file mode 100644 index 00000000..5d7718e2 --- /dev/null +++ b/src/faebryk/core/graphinterface.py @@ -0,0 +1,140 @@ +# This file is part of the faebryk project +# SPDX-License-Identifier: MIT +import logging +from typing import TYPE_CHECKING, Mapping, Optional + +from typing_extensions import Self, deprecated + +from faebryk.core.core import ID_REPR, FaebrykLibObject +from faebryk.core.graph_backends.default import GraphImpl +from faebryk.core.link import Link, LinkDirect, LinkNamedParent +from faebryk.libs.util import ( + NotNone, + try_avoid_endless_recursion, +) + +if TYPE_CHECKING: + from faebryk.core.node import Node + +logger = logging.getLogger(__name__) + +Graph = GraphImpl["GraphInterface"] + + +class GraphInterface(FaebrykLibObject): + GT = Graph + + def __init__(self) -> None: + super().__init__() + self.G = self.GT() + + # can't put it into constructor + # else it needs a reference when defining IFs + self._node: Optional["Node"] = None + self.name: str = type(self).__name__ + + @property + def node(self): + return NotNone(self._node) + + @node.setter + def node(self, value: "Node"): + self._node = value + + # Graph stuff + @property + def edges(self) -> Mapping["GraphInterface", Link]: + return self.G.get_edges(self) + + def get_links(self) -> list[Link]: + return list(self.edges.values()) + + def get_links_by_type[T: Link](self, link_type: type[T]) -> list[T]: + return [link for link in self.get_links() if isinstance(link, link_type)] + + @property + @deprecated("Use get_links") + def connections(self): + return self.get_links() + + def get_direct_connections(self) -> set["GraphInterface"]: + return set(self.edges.keys()) + + def is_connected(self, other: "GraphInterface"): + return self.G.is_connected(self, other) + + # Less graph-specific stuff + + # TODO make link trait to initialize from list + def connect(self, other: Self, linkcls=None) -> Self: + assert other is not self + + if linkcls is None: + linkcls = LinkDirect + link = linkcls([other, self]) + + _, no_path = self.G.merge(other.G) + + if not no_path: + dup = self.is_connected(other) + assert ( + not dup or type(dup) is linkcls + ), f"Already connected with different link type: {dup}" + + self.G.add_edge(self, other, link=link) + + if logger.isEnabledFor(logging.DEBUG): + logger.debug(f"GIF connection: {link}") + + return self + + def get_full_name(self, types: bool = False): + typestr = f"|{type(self).__name__}|" if types else "" + return f"{self.node.get_full_name(types=types)}.{self.name}{typestr}" + + def __str__(self) -> str: + return f"{str(self.node)}.{self.name}" + + @try_avoid_endless_recursion + def __repr__(self) -> str: + id_str = f"(@{hex(id(self))})" if ID_REPR else "" + + return ( + f"{self.get_full_name(types=True)}{id_str}" + if self._node is not None + else "| " + ) + + +class GraphInterfaceHierarchical(GraphInterface): + def __init__(self, is_parent: bool) -> None: + super().__init__() + self.is_parent = is_parent + + # TODO make consistent api with get_parent + def get_children(self) -> list[tuple[str, "Node"]]: + assert self.is_parent + + hier_conns = self.get_links_by_type(LinkNamedParent) + if len(hier_conns) == 0: + return [] + + return [(c.name, c.get_child().node) for c in hier_conns] + + def get_parent(self) -> tuple["Node", str] | None: + assert not self.is_parent + + conns = self.get_links_by_type(LinkNamedParent) + if not conns: + return None + assert len(conns) == 1 + conn = conns[0] + parent = conn.get_parent() + + return parent.node, conn.name + + def disconnect_parent(self): + self.G.remove_edge(self) + + +class GraphInterfaceSelf(GraphInterface): ... diff --git a/src/faebryk/core/link.py b/src/faebryk/core/link.py new file mode 100644 index 00000000..deee44a1 --- /dev/null +++ b/src/faebryk/core/link.py @@ -0,0 +1,116 @@ +# This file is part of the faebryk project +# SPDX-License-Identifier: MIT +import inspect +import logging +from typing import TYPE_CHECKING, Callable + +from faebryk.core.core import LINK_TB, FaebrykLibObject + +logger = logging.getLogger(__name__) + +if TYPE_CHECKING: + from faebryk.core.graphinterface import GraphInterface, GraphInterfaceHierarchical + + +class Link(FaebrykLibObject): + def __init__(self) -> None: + super().__init__() + + if LINK_TB: + self.tb = inspect.stack() + + def get_connections(self) -> list["GraphInterface"]: + raise NotImplementedError + + def __eq__(self, __value: "Link") -> bool: + return set(self.get_connections()) == set(__value.get_connections()) + + def __hash__(self) -> int: + return super().__hash__() + + def __str__(self) -> str: + return ( + f"{type(self).__name__}" + f"([{', '.join(str(i) for i in self.get_connections())}])" + ) + + def __repr__(self) -> str: + return f"{type(self).__name__}()" + + +class LinkSibling(Link): + def __init__(self, interfaces: list["GraphInterface"]) -> None: + super().__init__() + self.interfaces = interfaces + + def get_connections(self) -> list["GraphInterface"]: + return self.interfaces + + +class LinkParent(Link): + def __init__(self, interfaces: list["GraphInterface"]) -> None: + super().__init__() + from faebryk.core.graphinterface import GraphInterfaceHierarchical + + assert all([isinstance(i, GraphInterfaceHierarchical) for i in interfaces]) + # TODO rethink invariant + assert len(interfaces) == 2 + assert len([i for i in interfaces if i.is_parent]) == 1 # type: ignore + + self.interfaces: list["GraphInterfaceHierarchical"] = interfaces # type: ignore + + def get_connections(self): + return self.interfaces + + def get_parent(self): + return [i for i in self.interfaces if i.is_parent][0] + + def get_child(self): + return [i for i in self.interfaces if not i.is_parent][0] + + +class LinkNamedParent(LinkParent): + def __init__(self, name: str, interfaces: list["GraphInterface"]) -> None: + super().__init__(interfaces) + self.name = name + + @classmethod + def curry(cls, name: str): + def curried(interfaces: list["GraphInterface"]): + return cls(name, interfaces) + + return curried + + +class LinkDirect(Link): + def __init__(self, interfaces: list["GraphInterface"]) -> None: + super().__init__() + assert len(set(map(type, interfaces))) == 1 + self.interfaces = interfaces + + def get_connections(self) -> list["GraphInterface"]: + return self.interfaces + + +class LinkFilteredException(Exception): ... + + +class _TLinkDirectShallow(LinkDirect): + def __new__(cls, *args, **kwargs): + if cls is _TLinkDirectShallow: + raise TypeError( + "Can't instantiate abstract class _TLinkDirectShallow directly" + ) + return super().__new__(cls) + + +def LinkDirectShallow(if_filter: Callable[[LinkDirect, "GraphInterface"], bool]): + class _LinkDirectShallow(_TLinkDirectShallow): + i_filter = if_filter + + def __init__(self, interfaces: list["GraphInterface"]) -> None: + if not all(map(self.i_filter, interfaces)): + raise LinkFilteredException() + super().__init__(interfaces) + + return _LinkDirectShallow diff --git a/src/faebryk/core/module.py b/src/faebryk/core/module.py new file mode 100644 index 00000000..f4137f98 --- /dev/null +++ b/src/faebryk/core/module.py @@ -0,0 +1,36 @@ +# This file is part of the faebryk project +# SPDX-License-Identifier: MIT +import logging + +from faebryk.core.graphinterface import GraphInterface +from faebryk.core.node import Node +from faebryk.core.trait import Trait +from faebryk.libs.util import unique_ref + +logger = logging.getLogger(__name__) + + +class Module(Node): + class TraitT(Trait): ... + + specializes: GraphInterface + specialized: GraphInterface + + def get_most_special(self) -> "Module": + specialers = { + specialer + for specialer_gif in self.specialized.get_direct_connections() + if (specialer := specialer_gif.node) is not self + and isinstance(specialer, Module) + } + if not specialers: + return self + + specialest_next = unique_ref( + specialer.get_most_special() for specialer in specialers + ) + + assert ( + len(specialest_next) == 1 + ), f"Ambiguous specialest {specialest_next} for {self}" + return next(iter(specialest_next)) diff --git a/src/faebryk/core/moduleinterface.py b/src/faebryk/core/moduleinterface.py new file mode 100644 index 00000000..ae3f0c7c --- /dev/null +++ b/src/faebryk/core/moduleinterface.py @@ -0,0 +1,340 @@ +# This file is part of the faebryk project +# SPDX-License-Identifier: MIT +import logging +from typing import ( + Iterable, + Sequence, +) + +from typing_extensions import Self + +from faebryk.core.core import LINK_TB +from faebryk.core.graphinterface import ( + GraphInterface, + GraphInterfaceHierarchical, +) +from faebryk.core.link import ( + Link, + LinkDirect, + LinkDirectShallow, + LinkFilteredException, + _TLinkDirectShallow, +) +from faebryk.core.node import Node +from faebryk.core.trait import Trait +from faebryk.libs.util import once, print_stack + +logger = logging.getLogger(__name__) + + +# The resolve functions are really weird +# You have to look into where they are called to make sense of what they are doing +# Chain resolve is for deciding what to do in a case like this +# if1 -> link1 -> if2 -> link2 -> if3 +# This will then decide with which link if1 and if3 are connected +def _resolve_link_transitive(links: Iterable[type[Link]]) -> type[Link]: + from faebryk.libs.util import is_type_set_subclasses + + uniq = set(links) + assert uniq + + if len(uniq) == 1: + return next(iter(uniq)) + + if is_type_set_subclasses(uniq, {_TLinkDirectShallow}): + # TODO this only works if the filter is identical + raise NotImplementedError() + + if is_type_set_subclasses(uniq, {LinkDirect, _TLinkDirectShallow}): + return [u for u in uniq if issubclass(u, _TLinkDirectShallow)][0] + + raise NotImplementedError() + + +# This one resolves the case if1 -> link1 -> if2; if1 -> link2 -> if2 +def _resolve_link_duplicate(links: Iterable[type[Link]]) -> type[Link]: + from faebryk.libs.util import is_type_set_subclasses + + uniq = set(links) + assert uniq + + if len(uniq) == 1: + return next(iter(uniq)) + + if is_type_set_subclasses(uniq, {LinkDirect, _TLinkDirectShallow}): + return [u for u in uniq if not issubclass(u, _TLinkDirectShallow)][0] + + raise NotImplementedError() + + +class _LEVEL: + """connect depth counter to debug connections in ModuleInterface""" + + def __init__(self) -> None: + self.value = 0 + + def inc(self): + self.value += 1 + return self.value - 1 + + def dec(self): + self.value -= 1 + + +_CONNECT_DEPTH = _LEVEL() + + +class GraphInterfaceModuleSibling(GraphInterfaceHierarchical): ... + + +class GraphInterfaceModuleConnection(GraphInterface): ... + + +# CONNECT PROCEDURE +# connect +# connect_siblings +# - check not same ref +# - check not connected +# - connect_hierarchies +# - resolve link (if exists) +# - connect gifs +# - signal on_connect +# - connect_down +# - connect direct children by name +# - connect_up +# - check for each parent if all direct children by name connected +# - connect +# - check not filtered +# - cross connect_hierarchies transitive hull +# - cross connect_hierarchies siblings + + +class ModuleInterface(Node): + class TraitT(Trait): ... + + specializes: GraphInterface + specialized: GraphInterface + connected: GraphInterfaceModuleConnection + + # TODO rename + @classmethod + @once + def LinkDirectShallow(cls): + """ + Make link that only connects up but not down + """ + + def test(node: Node): + return not any(isinstance(p[0], cls) for p in node.get_hierarchy()[:-1]) + + class _LinkDirectShallowMif( + LinkDirectShallow(lambda link, gif: test(gif.node)) + ): ... + + print("Make shallow for", cls) + + return _LinkDirectShallowMif + + def __preinit__(self) -> None: ... + + def _connect_siblings_and_connections( + self, other: "ModuleInterface", linkcls: type[Link] + ) -> Self: + from faebryk.core.util import get_connected_mifs_with_link + + if other is self: + return self + + # Already connected + if self.is_connected_to(other): + return self + + # if link is filtered, cancel here + self._connect_across_hierarchies(other, linkcls) + if not self.is_connected_to(other): + return self + + if logger.isEnabledFor(logging.DEBUG): + logger.debug(f"MIF connection: {self} to {other}") + + def cross_connect( + s_group: dict[ModuleInterface, type[Link]], + d_group: dict[ModuleInterface, type[Link]], + hint=None, + ): + if logger.isEnabledFor(logging.DEBUG) and hint is not None: + logger.debug(f"Connect {hint} {s_group} -> {d_group}") + + for s, slink in s_group.items(): + for d, dlink in d_group.items(): + # can happen while connection trees are resolving + if s is d: + continue + link = _resolve_link_transitive([slink, dlink, linkcls]) + + s._connect_across_hierarchies(d, linkcls=link) + + def _get_connected_mifs(gif: GraphInterface): + return {k: type(v) for k, v in get_connected_mifs_with_link(gif).items()} + + # Connect to all connections + s_con = _get_connected_mifs(self.connected) | {self: linkcls} + d_con = _get_connected_mifs(other.connected) | {other: linkcls} + cross_connect(s_con, d_con, "connections") + + # Connect to all siblings + s_sib = ( + _get_connected_mifs(self.specialized) + | _get_connected_mifs(self.specializes) + | {self: linkcls} + ) + d_sib = ( + _get_connected_mifs(other.specialized) + | _get_connected_mifs(other.specializes) + | {other: linkcls} + ) + cross_connect(s_sib, d_sib, "siblings") + + return self + + def _on_connect(self, other: "ModuleInterface"): + """override to handle custom connection logic""" + ... + + def _try_connect_down(self, other: "ModuleInterface", linkcls: type[Link]) -> None: + from faebryk.core.util import zip_children_by_name + + if not isinstance(other, type(self)): + return + + for _, (src, dst) in zip_children_by_name(self, other, ModuleInterface).items(): + if src is None or dst is None: + continue + src.connect(dst, linkcls=linkcls) + + def _try_connect_up(self, other: "ModuleInterface") -> None: + p1 = self.get_parent() + p2 = other.get_parent() + if not ( + p1 + and p2 + and p1[0] is not p2[0] + and isinstance(p1[0], type(p2[0])) + and isinstance(p1[0], ModuleInterface) + ): + return + + src_m = p1[0] + dst_m = p2[0] + assert isinstance(dst_m, ModuleInterface) + + def _is_connected(a, b): + assert isinstance(a, ModuleInterface) + assert isinstance(b, ModuleInterface) + return a.is_connected_to(b) + + from faebryk.core.util import zip_children_by_name + + connection_map = [ + (src_i, dst_i, _is_connected(src_i, dst_i)) + for src_i, dst_i in zip_children_by_name( + src_m, dst_m, sub_type=ModuleInterface + ).values() + ] + + assert connection_map + + if not all(connected for _, _, connected in connection_map): + return + + # decide which LinkType to use here + # depends on connections between src_i & dst_i + # e.g. if any Shallow, we need to choose shallow + link = _resolve_link_transitive( + [type(sublink) for _, _, sublink in connection_map if sublink] + ) + + if logger.isEnabledFor(logging.DEBUG): + logger.debug(f"Up connect {src_m} -> {dst_m}") + src_m.connect(dst_m, linkcls=link) + + def _connect_across_hierarchies( + self, other: "ModuleInterface", linkcls: type[Link] + ): + existing_link = self.is_connected_to(other) + if existing_link: + if isinstance(existing_link, linkcls): + return + resolved = _resolve_link_duplicate([type(existing_link), linkcls]) + if resolved is type(existing_link): + return + if LINK_TB: + print(print_stack(existing_link.tb)) + raise NotImplementedError( + "Overriding existing links not implemented, tried to override " + + f"{existing_link} with {resolved}" + ) + + # level 0 connect + try: + self.connected.connect(other.connected, linkcls=linkcls) + except LinkFilteredException: + return + + if logger.isEnabledFor(logging.DEBUG): + logger.debug(f"{' '*2*_CONNECT_DEPTH.inc()}Connect {self} to {other}") + self._on_connect(other) + + con_depth_one = _CONNECT_DEPTH.value == 1 + recursion_error = None + try: + # level +1 (down) connect + self._try_connect_down(other, linkcls=linkcls) + + # level -1 (up) connect + self._try_connect_up(other) + + except RecursionError as e: + recursion_error = e + if not con_depth_one: + raise + + if recursion_error: + raise Exception(f"Recursion error while connecting {self} to {other}") + + _CONNECT_DEPTH.dec() + + def get_direct_connections(self) -> set["ModuleInterface"]: + return { + gif.node + for gif in self.connected.get_direct_connections() + if isinstance(gif.node, ModuleInterface) and gif.node is not self + } + + def connect(self, other: Self, linkcls=None) -> Self: + # TODO consider some type of check at the end within the graph instead + # assert type(other) is type(self) + if linkcls is None: + linkcls = LinkDirect + return self._connect_siblings_and_connections(other, linkcls=linkcls) + + def connect_via( + self, bridge: Node | Sequence[Node], other: Self | None = None, linkcls=None + ): + from faebryk.library.can_bridge import can_bridge + + bridges = [bridge] if isinstance(bridge, Node) else bridge + intf = self + for sub_bridge in bridges: + t = sub_bridge.get_trait(can_bridge) + intf.connect(t.get_in(), linkcls=linkcls) + intf = t.get_out() + + if other: + intf.connect(other, linkcls=linkcls) + + def connect_shallow(self, other: Self) -> Self: + return self.connect(other, linkcls=type(self).LinkDirectShallow()) + + def is_connected_to(self, other: "ModuleInterface"): + return self.connected.is_connected(other.connected) diff --git a/src/faebryk/core/node.py b/src/faebryk/core/node.py new file mode 100644 index 00000000..7d35c825 --- /dev/null +++ b/src/faebryk/core/node.py @@ -0,0 +1,540 @@ +# This file is part of the faebryk project +# SPDX-License-Identifier: MIT +import logging +from itertools import chain +from typing import TYPE_CHECKING, Any, Callable, Iterable, Type, get_args, get_origin + +from deprecated import deprecated +from more_itertools import partition + +from faebryk.core.core import ID_REPR, FaebrykLibObject +from faebryk.core.graphinterface import ( + GraphInterface, + GraphInterfaceHierarchical, + GraphInterfaceSelf, +) +from faebryk.core.link import LinkNamedParent, LinkSibling +from faebryk.libs.exceptions import FaebrykException +from faebryk.libs.util import ( + KeyErrorNotFound, + cast_assert, + find, + times, + try_avoid_endless_recursion, +) + +if TYPE_CHECKING: + from faebryk.core.trait import Trait, TraitImpl + +logger = logging.getLogger(__name__) + + +class FieldError(Exception): + pass + + +class FieldExistsError(FieldError): + pass + + +class FieldContainerError(FieldError): + pass + + +def list_field[T: Node](n: int, if_type: Callable[[], T]) -> list[T]: + return d_field(lambda: times(n, if_type)) + + +class fab_field: + pass + + +class rt_field[T, O](property, fab_field): + def __init__(self, fget: Callable[[T], O]) -> None: + super().__init__() + self.func = fget + self.lookup: dict[T, O] = {} + + def _construct(self, obj: T): + constructed = self.func(obj) + # TODO find a better way for this + # in python 3.13 name support + self.lookup[obj] = constructed + + return constructed + + def __get__(self, instance: T, owner: type | None = None) -> Any: + return self.lookup[instance] + + +class _d_field[T](fab_field): + def __init__(self, default_factory: Callable[[], T]) -> None: + self.default_factory = default_factory + + def __repr__(self) -> str: + return f"{super().__repr__()}{self.default_factory=})" + + +def d_field[T](default_factory: Callable[[], T]) -> T: + return _d_field(default_factory) # type: ignore + + +def f_field[T, **P](con: Callable[P, T]) -> Callable[P, T]: + assert isinstance(con, type) + + def _(*args: P.args, **kwargs: P.kwargs) -> Callable[[], T]: + def __() -> T: + return con(*args, **kwargs) + + return _d_field(__) + + return _ + + +# ----------------------------------------------------------------------------- +class PostInitCaller(type): + def __call__(cls, *args, **kwargs): + obj = type.__call__(cls, *args, **kwargs) + obj.__post_init__(*args, **kwargs) + return obj + + +class NodeException(FaebrykException): + def __init__(self, node: "Node", *args: object) -> None: + super().__init__(*args) + self.node = node + + +class NodeAlreadyBound(NodeException): + def __init__(self, node: "Node", other: "Node", *args: object) -> None: + super().__init__( + node, + *args, + f"Node {other} already bound to" + f" {other.get_parent()}, can't bind to {node}", + ) + + +class NodeNoParent(NodeException): ... + + +class Node(FaebrykLibObject, metaclass=PostInitCaller): + runtime_anon: list["Node"] + runtime: dict[str, "Node"] + specialized: list["Node"] + + self_gif: GraphInterfaceSelf + children: GraphInterfaceHierarchical = d_field( + lambda: GraphInterfaceHierarchical(is_parent=True) + ) + parent: GraphInterfaceHierarchical = d_field( + lambda: GraphInterfaceHierarchical(is_parent=False) + ) + + _init: bool = False + + def __hash__(self) -> int: + # TODO proper hash + return hash(id(self)) + + def add[T: Node]( + self, + obj: T, + name: str | None = None, + container: list | dict[str, Any] | None = None, + ) -> T: + assert obj is not None + + if container is None: + container = self.runtime_anon + if name: + container = self.runtime + + try: + container_name = find(vars(self).items(), lambda x: x[1] is container)[0] + except KeyErrorNotFound: + raise FieldContainerError("Container not in fields") + + if name: + if not isinstance(container, dict): + raise FieldContainerError(f"Expected dict got {type(container)}") + if name in container: + raise FieldExistsError(name) + container[name] = obj + else: + if not isinstance(container, list): + raise FieldContainerError(f"Expected list got {type(container)}") + container.append(obj) + name = f"{container_name}[{len(container) - 1}]" + + self._handle_add_node(name, obj) + return obj + + def add_to_container[T: Node]( + self, n: int, factory: Callable[[], T], container: list[T] | None = None + ): + if container is None: + container = self.runtime_anon + + constr = [factory() for _ in range(n)] + for obj in constr: + self.add(obj, container=container) + return constr + + def __init_subclass__(cls, *, init: bool = True) -> None: + super().__init_subclass__() + cls._init = init + + def _setup_fields(self, cls): + def all_vars(cls): + return {k: v for c in reversed(cls.__mro__) for k, v in vars(c).items()} + + def all_anno(cls): + return { + k: v + for c in reversed(cls.__mro__) + if hasattr(c, "__annotations__") + for k, v in c.__annotations__.items() + } + + LL_Types = (Node, GraphInterface) + + annos = all_anno(cls) + vars_ = all_vars(cls) + + def is_node_field(obj): + def is_genalias_node(obj): + origin = get_origin(obj) + assert origin is not None + + if issubclass(origin, LL_Types): + return True + + if issubclass(origin, (list, dict)): + arg = get_args(obj)[-1] + return is_node_field(arg) + + if isinstance(obj, LL_Types): + raise FieldError("Node instances not allowed") + + if isinstance(obj, str): + return obj in [L.__name__ for L in LL_Types] + + if isinstance(obj, type): + return issubclass(obj, LL_Types) + + if isinstance(obj, _d_field): + return True + + if get_origin(obj): + return is_genalias_node(obj) + + if isinstance(obj, rt_field): + return True + + return False + + clsfields_unf = { + name: obj + for name, obj in chain( + [(name, f) for name, f in annos.items()], + [(name, f) for name, f in vars_.items() if isinstance(f, fab_field)], + ) + if not name.startswith("_") + } + + clsfields = { + name: obj for name, obj in clsfields_unf.items() if is_node_field(obj) + } + + # for name, obj in clsfields_unf.items(): + # if isinstance(obj, _d_field): + # obj = obj.type + # filtered = name not in clsfields + # filtered_str = " FILTERED" if filtered else "" + # print( + # f"{cls.__qualname__+"."+name+filtered_str:<60} = {str(obj):<70} " + # "| {type(obj)}" + # ) + + added_objects: dict[str, Node | GraphInterface] = {} + objects: dict[str, Node | GraphInterface] = {} + + def handle_add(name, obj): + del objects[name] + added_objects[name] = obj + if isinstance(obj, GraphInterface): + self._handle_add_gif(name, obj) + elif isinstance(obj, Node): + self._handle_add_node(name, obj) + else: + assert False + + def append(name, inst): + if isinstance(inst, LL_Types): + objects[name] = inst + elif isinstance(inst, list): + for i, obj in enumerate(inst): + assert obj is not None + objects[f"{name}[{i}]"] = obj + elif isinstance(inst, dict): + for k, obj in inst.items(): + objects[f"{name}[{k}]"] = obj + + return inst + + def setup_field(name, obj): + def setup_gen_alias(name, obj): + origin = get_origin(obj) + assert origin + if isinstance(origin, type): + setattr(self, name, append(name, origin())) + return + raise NotImplementedError(origin) + + if isinstance(obj, str): + raise NotImplementedError() + + if get_origin(obj): + setup_gen_alias(name, obj) + return + + if isinstance(obj, _d_field): + setattr(self, name, append(name, obj.default_factory())) + return + + if isinstance(obj, type): + setattr(self, name, append(name, obj())) + return + + if isinstance(obj, rt_field): + append(name, obj._construct(self)) + return + + raise NotImplementedError() + + nonrt, rt = partition(lambda x: isinstance(x[1], rt_field), clsfields.items()) + for name, obj in nonrt: + setup_field(name, obj) + + for name, obj in list(objects.items()): + handle_add(name, obj) + + # rt fields depend on full self + for name, obj in rt: + setup_field(name, obj) + + for name, obj in list(objects.items()): + handle_add(name, obj) + + return added_objects, clsfields + + def __new__(cls, *args, **kwargs): + out = super().__new__(cls) + return out + + def _setup(self) -> None: + cls = type(self) + # print(f"Called Node init {cls.__qualname__:<20} {'-' * 80}") + + # check if accidentally added a node instance instead of field + node_instances = [ + (name, f) + for name, f in vars(cls).items() + if isinstance(f, Node) and not name.startswith("_") + ] + if node_instances: + raise FieldError(f"Node instances not allowed: {node_instances}") + + # Construct Fields + _, _ = self._setup_fields(cls) + + # Call 2-stage constructors + if self._init: + for base in reversed(type(self).mro()): + if hasattr(base, "__preinit__"): + base.__preinit__(self) + for base in reversed(type(self).mro()): + if hasattr(base, "__postinit__"): + base.__postinit__(self) + + def __init__(self): + assert not hasattr(self, "_is_setup") + self._is_setup = True + + def __preinit__(self): ... + + def __postinit__(self): ... + + def __post_init__(self, *args, **kwargs): + self._setup() + + def _handle_add_gif(self, name: str, gif: GraphInterface): + gif.node = self + gif.name = name + if not isinstance(gif, GraphInterfaceSelf): + gif.connect(self.self_gif, linkcls=LinkSibling) + + def _handle_add_node(self, name: str, node: "Node"): + if node.get_parent(): + raise NodeAlreadyBound(self, node) + + from faebryk.core.trait import TraitImpl + + if isinstance(node, TraitImpl): + if self.has_trait(node._trait): + if not node.handle_duplicate( + cast_assert(TraitImpl, self.get_trait(node._trait)), self + ): + return + + node.parent.connect(self.children, LinkNamedParent.curry(name)) + node._handle_added_to_parent() + + def _remove_child(self, node: "Node"): + node.parent.disconnect_parent() + + def _handle_added_to_parent(self): ... + + def get_graph(self): + return self.self_gif.G + + def get_parent(self): + return self.parent.get_parent() + + def get_name(self): + p = self.get_parent() + if not p: + raise NodeNoParent(self, "Parent required for name") + return p[1] + + def get_hierarchy(self) -> list[tuple["Node", str]]: + parent = self.get_parent() + if not parent: + return [(self, "*")] + parent_obj, name = parent + + return parent_obj.get_hierarchy() + [(self, name)] + + def get_full_name(self, types: bool = False): + hierarchy = self.get_hierarchy() + if types: + return ".".join([f"{name}|{type(obj).__name__}" for obj, name in hierarchy]) + else: + return ".".join([f"{name}" for _, name in hierarchy]) + + @try_avoid_endless_recursion + def __str__(self) -> str: + return f"<{self.get_full_name(types=True)}>" + + @try_avoid_endless_recursion + def __repr__(self) -> str: + id_str = f"(@{hex(id(self))})" if ID_REPR else "" + return f"<{self.get_full_name(types=True)}>{id_str}" + + # Trait stuff ---------------------------------------------------------------------- + + # TODO type checking InterfaceTrait -> Interface + @deprecated("Just use add") + def add_trait[_TImpl: "TraitImpl"](self, trait: _TImpl) -> _TImpl: + from faebryk.core.trait import Trait, TraitImpl + + assert isinstance(trait, TraitImpl), ("not a traitimpl:", trait) + assert isinstance(trait, Trait) + + self.add(trait) + + return trait + + def _find[V: "Trait"](self, trait: type[V], only_implemented: bool) -> V | None: + from faebryk.core.trait import TraitImpl + + out = self.get_children( + direct_only=True, + types=TraitImpl, + f_filter=lambda impl: impl.implements(trait) + and (impl.is_implemented() or not only_implemented), + ) + assert len(out) <= 1 + return cast_assert(trait, next(iter(out))) if out else None + + def del_trait(self, trait: type["Trait"]): + impl = self._find(trait, only_implemented=False) + if not impl: + return + self._remove_child(impl) + + def try_get_trait[V: "Trait"](self, trait: Type[V]) -> V | None: + return self._find(trait, only_implemented=True) + + def has_trait(self, trait: type["Trait"]) -> bool: + return self.try_get_trait(trait) is not None + + def get_trait[V: "Trait"](self, trait: Type[V]) -> V: + from faebryk.core.trait import TraitImpl, TraitNotFound + + assert not issubclass( + trait, TraitImpl + ), "You need to specify the trait, not an impl" + + impl = self._find(trait, only_implemented=True) + if not impl: + raise TraitNotFound(self, trait) + + return cast_assert(trait, impl) + + # Graph stuff ---------------------------------------------------------------------- + + def get_node_direct_children_(self): + return { + gif.node + for gif, link in self.get_graph().get_edges(self.children).items() + if isinstance(link, LinkNamedParent) + } + + def get_children[T: Node]( + self, + direct_only: bool, + types: type[T] | tuple[type[T], ...], + include_root: bool = False, + f_filter: Callable[[T], bool] | None = None, + sort: bool = True, + ): + if direct_only: + out = self.get_node_direct_children_() + if include_root: + out.add(self) + else: + out = self.get_node_children_all(include_root=include_root) + + out = {n for n in out if isinstance(n, types) and (not f_filter or f_filter(n))} + + if sort: + out = set(sorted(out, key=lambda n: n.get_name())) + + return out + + def get_node_children_all(self, include_root=True) -> list["Node"]: + # TODO looks like get_node_tree is 2x faster + + out = self.bfs_node( + lambda x: isinstance(x, (GraphInterfaceSelf, GraphInterfaceHierarchical)) + and x is not self.parent, + ) + + if not include_root: + out.remove(self) + + return list(out) + + def bfs_node(self, filter: Callable[[GraphInterface], bool]): + return Node.get_nodes_from_gifs( + self.get_graph().bfs_visit(filter, [self.self_gif]) + ) + + @staticmethod + def get_nodes_from_gifs(gifs: Iterable[GraphInterface]): + # TODO move this to gif? + return {gif.node for gif in gifs} + # TODO what is faster + # return {n.node for n in gifs if isinstance(n, GraphInterfaceSelf)} diff --git a/src/faebryk/core/parameter.py b/src/faebryk/core/parameter.py new file mode 100644 index 00000000..587d11e4 --- /dev/null +++ b/src/faebryk/core/parameter.py @@ -0,0 +1,386 @@ +# This file is part of the faebryk project +# SPDX-License-Identifier: MIT +import logging +from typing import ( + Callable, + Optional, + Sequence, +) + +from typing_extensions import Self + +from faebryk.core.graphinterface import GraphInterface +from faebryk.core.node import Node +from faebryk.core.trait import Trait +from faebryk.libs.util import TwistArgs, is_type_pair, try_avoid_endless_recursion + +logger = logging.getLogger(__name__) + + +def _resolved[PV, O]( + func: Callable[["Parameter[PV]", "Parameter[PV]"], O], +) -> Callable[ + [ + "PV | set[PV] | tuple[PV, PV] | Parameter[PV]", + "PV | set[PV] | tuple[PV, PV] | Parameter[PV]", + ], + O, +]: + def wrap(*args): + args = [Parameter.from_literal(arg).get_most_narrow() for arg in args] + return func(*args) + + return wrap + + +class Parameter[PV](Node): + type LIT = PV | set[PV] | tuple[PV, PV] + type LIT_OR_PARAM = LIT | "Parameter[PV]" + + class TraitT(Trait): ... + + narrowed_by: GraphInterface + narrows: GraphInterface + + class MergeException(Exception): ... + + class SupportsSetOps: + def __contains__(self, other: "Parameter[PV].LIT_OR_PARAM") -> bool: ... + + def try_compress(self) -> "Parameter[PV]": + return self + + @classmethod + def from_literal(cls, value: LIT_OR_PARAM) -> '"Parameter[PV]"': + from faebryk.library.Constant import Constant + from faebryk.library.Range import Range + from faebryk.library.Set import Set + + if isinstance(value, Parameter): + return value + elif isinstance(value, set): + return Set(value) + elif isinstance(value, tuple): + return Range(*value) + else: + return Constant(value) + + def _merge(self, other: "Parameter[PV]") -> "Parameter[PV]": + from faebryk.library.ANY import ANY + from faebryk.library.Operation import Operation + from faebryk.library.Set import Set + from faebryk.library.TBD import TBD + + def _is_pair[T, U](type1: type[T], type2: type[U]) -> Optional[tuple[T, U]]: + return is_type_pair(self, other, type1, type2) + + if self is other: + return self + + try: + if self == other: + return self + except ValueError: + ... + + if pair := _is_pair(Parameter[PV], TBD): + return pair[0] + + if pair := _is_pair(Parameter[PV], ANY): + return pair[0] + + # TODO remove as soon as possible + if pair := _is_pair(Parameter[PV], Operation): + # TODO make MergeOperation that inherits from Operation + # and return that instead, application can check if result is MergeOperation + # if it was checking mergeability + raise self.MergeException("cant merge range with operation") + + if pair := _is_pair(Parameter[PV], Parameter[PV].SupportsSetOps): + out = self.intersect(*pair) + if isinstance(out, Operation): + raise self.MergeException("not resolvable") + if out == Set([]) and not pair[0] == pair[1] == Set([]): + raise self.MergeException( + f"conflicting sets/ranges: {self!r} {other!r}" + ) + return out + + raise NotImplementedError + + def _narrowed(self, other: "Parameter[PV]"): + if self is other: + return + + if self.narrowed_by.is_connected(other.narrows): + return + self.narrowed_by.connect(other.narrows) + + @_resolved + def is_mergeable_with(self: "Parameter[PV]", other: "Parameter[PV]") -> bool: + try: + self._merge(other) + return True + except self.MergeException: + return False + except NotImplementedError: + return False + + @_resolved + def is_subset_of(self: "Parameter[PV]", other: "Parameter[PV]") -> bool: + from faebryk.library.ANY import ANY + from faebryk.library.Operation import Operation + from faebryk.library.TBD import TBD + + lhs = self + rhs = other + + def is_either_instance(t: type["Parameter[PV]"]): + return isinstance(lhs, t) or isinstance(rhs, t) + + # Not resolveable + if isinstance(rhs, ANY): + return True + if isinstance(lhs, ANY): + return False + if is_either_instance(TBD): + return False + if is_either_instance(Operation): + return False + + # Sets + return lhs & rhs == lhs + + @_resolved + def merge(self: "Parameter[PV]", other: "Parameter[PV]") -> "Parameter[PV]": + out = self._merge(other) + + self._narrowed(out) + other._narrowed(out) + + return out + + @_resolved + def override(self: "Parameter[PV]", other: "Parameter[PV]") -> "Parameter[PV]": + if not other.is_subset_of(self): + raise self.MergeException("override not possible") + + self._narrowed(other) + return other + + # TODO: replace with graph-based + @staticmethod + def arithmetic_op( + op1: "Parameter[PV]", op2: "Parameter[PV]", op: Callable + ) -> "Parameter[PV]": + from faebryk.library.ANY import ANY + from faebryk.library.Constant import Constant + from faebryk.library.Operation import Operation + from faebryk.library.Range import Range + from faebryk.library.Set import Set + from faebryk.library.TBD import TBD + + def _is_pair[T, U]( + type1: type[T], type2: type[U] + ) -> Optional[tuple[T, U, Callable]]: + if isinstance(op1, type1) and isinstance(op2, type2): + return op1, op2, op + if isinstance(op1, type2) and isinstance(op2, type1): + return op2, op1, TwistArgs(op) + + return None + + if pair := _is_pair(Constant, Constant): + return Constant(op(pair[0].value, pair[1].value)) + + if pair := _is_pair(Range, Range): + try: + p0_min, p0_max = pair[0].min, pair[0].max + p1_min, p1_max = pair[1].min, pair[1].max + except Range.MinMaxError: + return Operation(pair[:2], op) + return Range( + *( + op(lhs, rhs) + for lhs, rhs in [ + (p0_min, p1_min), + (p0_max, p1_max), + (p0_min, p1_max), + (p0_max, p1_min), + ] + ) + ) + + if pair := _is_pair(Constant, Range): + sop = pair[2] + try: + return Range(*(sop(pair[0], bound) for bound in pair[1].bounds)) + except Range.MinMaxError: + return Operation(pair[:2], op) + + if pair := _is_pair(Parameter, ANY): + sop = pair[2] + return Operation(pair[:2], sop) + + if pair := _is_pair(Parameter, Operation): + sop = pair[2] + return Operation(pair[:2], sop) + + if pair := _is_pair(Parameter, TBD): + sop = pair[2] + return Operation(pair[:2], sop) + + if pair := _is_pair(Parameter, Set): + sop = pair[2] + return Set( + Parameter.arithmetic_op(nested, pair[0], sop) + for nested in pair[1].params + ) + + raise NotImplementedError + + @staticmethod + def intersect(op1: "Parameter[PV]", op2: "Parameter[PV]") -> "Parameter[PV]": + from faebryk.library.Constant import Constant + from faebryk.library.Operation import Operation + from faebryk.library.Range import Range + from faebryk.library.Set import Set + + if op1 == op2: + return op1 + + def _is_pair[T, U]( + type1: type[T], type2: type[U] + ) -> Optional[tuple[T, U, Callable]]: + if isinstance(op1, type1) and isinstance(op2, type2): + return op1, op2, op + if isinstance(op1, type2) and isinstance(op2, type1): + return op2, op1, TwistArgs(op) + + return None + + def op(a, b): + return a & b + + # same types + if pair := _is_pair(Constant, Constant): + return Set([]) + if pair := _is_pair(Set, Set): + return Set(pair[0].params.intersection(pair[1].params)) + if pair := _is_pair(Range, Range): + try: + min_ = max(pair[0].min, pair[1].min) + max_ = min(pair[0].max, pair[1].max) + if min_ > max_: + return Set([]) + if min_ == max_: + return Constant(min_) + return Range(max_, min_) + except Range.MinMaxError: + return Operation(pair[:2], op) + + # diff types + if pair := _is_pair(Constant, Range): + try: + if pair[0] in pair[1]: + return pair[0] + else: + return Set([]) + except Range.MinMaxError: + return Operation(pair[:2], op) + if pair := _is_pair(Constant, Set): + if pair[0] in pair[1]: + return pair[0] + else: + return Set([]) + if pair := _is_pair(Range, Set): + try: + return Set(i for i in pair[1].params if i in pair[0]) + except Range.MinMaxError: + return Operation(pair[:2], op) + + return Operation((op1, op2), op) + + @_resolved + def __add__(self: "Parameter[PV]", other: "Parameter[PV]"): + return self.arithmetic_op(self, other, lambda a, b: a + b) + + @_resolved + def __sub__(self: "Parameter[PV]", other: "Parameter[PV]"): + return self.arithmetic_op(self, other, lambda a, b: a - b) + + # TODO PV | float + @_resolved + def __mul__(self: "Parameter[PV]", other: "Parameter[PV]"): + return self.arithmetic_op(self, other, lambda a, b: a * b) + + # TODO PV | float + @_resolved + def __truediv__(self: "Parameter[PV]", other: "Parameter[PV]"): + return self.arithmetic_op(self, other, lambda a, b: a / b) + + @_resolved + def __and__(self: "Parameter[PV]", other: "Parameter[PV]") -> "Parameter[PV]": + return self.intersect(self, other) + + def get_most_narrow(self) -> "Parameter[PV]": + out = self.get_narrowing_chain()[-1] + + com = out.try_compress() + if com is not out: + com = com.get_most_narrow() + out._narrowed(com) + out = com + + return out + + @staticmethod + def resolve_all(params: "Sequence[Parameter[PV]]") -> "Parameter[PV]": + from faebryk.library.TBD import TBD + + params_set = list(params) + if not params_set: + return TBD[PV]() + it = iter(params_set) + most_specific = next(it) + for param in it: + most_specific = most_specific.merge(param) + + return most_specific + + @try_avoid_endless_recursion + def __str__(self) -> str: + narrowest = self.get_most_narrow() + if narrowest is self: + return super().__str__() + return str(narrowest) + + # @try_avoid_endless_recursion + # def __repr__(self) -> str: + # narrowest = self.get_most_narrow() + # if narrowest is self: + # return super().__repr__() + # # return f"{super().__repr__()} -> {repr(narrowest)}" + # return repr(narrowest) + + def get_narrowing_chain(self) -> list["Parameter"]: + from faebryk.core.util import get_direct_connected_nodes + + out: list[Parameter] = [self] + narrowers = get_direct_connected_nodes(self.narrowed_by, Parameter) + if narrowers: + assert len(narrowers) == 1, "Narrowing tree diverged" + out += next(iter(narrowers)).get_narrowing_chain() + assert id(self) not in map(id, out[1:]), "Narrowing tree cycle" + return out + + def get_narrowed_siblings(self) -> set["Parameter"]: + from faebryk.core.util import get_direct_connected_nodes + + return get_direct_connected_nodes(self.narrows, Parameter) + + def __copy__(self) -> Self: + return type(self)() + + def __deepcopy__(self, memo) -> Self: + return self.__copy__() diff --git a/src/faebryk/core/trait.py b/src/faebryk/core/trait.py new file mode 100644 index 00000000..0a727212 --- /dev/null +++ b/src/faebryk/core/trait.py @@ -0,0 +1,116 @@ +# This file is part of the faebryk project +# SPDX-License-Identifier: MIT +import logging + +from faebryk.core.node import Node, NodeException +from faebryk.libs.util import cast_assert + +logger = logging.getLogger(__name__) + + +class TraitNotFound(NodeException): + def __init__(self, node: Node, trait: type["Trait"], *args: object) -> None: + super().__init__( + node, *args, f"Trait {trait} not found in {type(node)}[{node}]" + ) + self.trait = trait + + +class TraitAlreadyExists(NodeException): + def __init__(self, node: Node, trait: "TraitImpl", *args: object) -> None: + trait_type = trait._trait + super().__init__( + node, + *args, + f"Trait {trait_type} already exists in {node}: {node.get_trait(trait_type)}" + f", trying to add {trait}", + ) + self.trait = trait + + +class TraitUnbound(NodeException): + def __init__(self, node: Node, *args: object) -> None: + super().__init__(node, *args, f"Trait {node} is not bound to a node") + + +class Trait(Node): + @classmethod + def impl[T: "Trait"](cls: type[T]): + class _Impl(TraitImpl, cls): ... + + return _Impl + + +class TraitImpl(Node): + _trait: type[Trait] + + def __preinit__(self) -> None: + found = False + bases = type(self).__bases__ + while not found: + for base in bases: + if not issubclass(base, TraitImpl) and issubclass(base, Trait): + self._trait = base + found = True + break + bases = [ + new_base + for base in bases + if issubclass(base, TraitImpl) + for new_base in base.__bases__ + ] + assert len(bases) > 0 + + assert isinstance(self._trait, type) + assert issubclass(self._trait, Trait) + assert self._trait is not TraitImpl + + @property + def obj(self) -> Node: + p = self.get_parent() + if not p: + raise TraitUnbound(self) + return p[0] + + def get_obj[T: Node](self, type: type[T]) -> T: + return cast_assert(type, self.obj) + + def cmp(self, other: "TraitImpl") -> tuple[bool, "TraitImpl"]: + assert type(other), TraitImpl + + # If other same or more specific + if other.implements(self._trait): + return True, other + + # If we are more specific + if self.implements(other._trait): + return True, self + + return False, self + + def implements(self, trait: type): + assert issubclass(trait, Trait) + + return issubclass(self._trait, trait) + + # Overwriteable -------------------------------------------------------------------- + + def _handle_added_to_parent(self): + self.on_obj_set() + + def on_obj_set(self): ... + + def handle_duplicate(self, other: "TraitImpl", node: Node) -> bool: + assert other is not self + _, candidate = other.cmp(self) + if candidate is not self: + return False + + node.del_trait(other._trait) + return True + + # raise TraitAlreadyExists(node, self) + + # override this to implement a dynamic trait + def is_implemented(self): + return True diff --git a/src/faebryk/core/util.py b/src/faebryk/core/util.py index 75f3dc3f..1b33ce72 100644 --- a/src/faebryk/core/util.py +++ b/src/faebryk/core/util.py @@ -12,28 +12,18 @@ from typing_extensions import deprecated -from faebryk.core.core import ( +import faebryk.library._F as F +from faebryk.core.graphinterface import ( Graph, GraphInterface, - GraphInterfaceHierarchical, GraphInterfaceSelf, - Link, - LinkDirect, - LinkNamedParent, - Module, - ModuleInterface, - Node, - Parameter, - Trait, ) -from faebryk.library.ANY import ANY -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.Constant import Constant -from faebryk.library.Electrical import Electrical -from faebryk.library.has_overriden_name_defined import has_overriden_name_defined -from faebryk.library.Range import Range -from faebryk.library.Set import Set -from faebryk.library.TBD import TBD +from faebryk.core.link import Link, LinkDirect +from faebryk.core.module import Module +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.core.node import Node +from faebryk.core.parameter import Parameter +from faebryk.core.trait import Trait from faebryk.libs.units import Quantity, UnitsContainer, to_si_str from faebryk.libs.util import NotNone, cast_assert, zip_dicts_by_key @@ -43,18 +33,18 @@ def enum_parameter_representation(param: Parameter, required: bool = False) -> str: - if isinstance(param, Constant): + if isinstance(param, F.Constant): return param.value.name if isinstance(param.value, Enum) else str(param.value) - elif isinstance(param, Range): + elif isinstance(param, F.Range): return ( f"{enum_parameter_representation(param.min)} - " f"{enum_parameter_representation(param.max)}" ) - elif isinstance(param, Set): + elif isinstance(param, F.Set): return f"Set({', '.join(map(enum_parameter_representation, param.params))})" - elif isinstance(param, TBD): + elif isinstance(param, F.TBD): return "TBD" if required else "" - elif isinstance(param, ANY): + elif isinstance(param, F.ANY): return "ANY" if required else "" else: return type(param).__name__ @@ -68,23 +58,23 @@ def as_unit( ) -> str: if base != 1000: raise NotImplementedError("Only base 1000 supported") - if isinstance(param, Constant): + if isinstance(param, F.Constant): return to_si_str(param.value, unit) - elif isinstance(param, Range): + elif isinstance(param, F.Range): return ( as_unit(param.min, unit, base=base) + " - " + as_unit(param.max, unit, base=base, required=True) ) - elif isinstance(param, Set): + elif isinstance(param, F.Set): return ( "Set(" + ", ".join(map(lambda x: as_unit(x, unit, required=True), param.params)) + ")" ) - elif isinstance(param, TBD): + elif isinstance(param, F.TBD): return "TBD" if required else "" - elif isinstance(param, ANY): + elif isinstance(param, F.ANY): return "ANY" if required else "" raise ValueError(f"Unsupported {param=}") @@ -93,15 +83,15 @@ def as_unit( def as_unit_with_tolerance( param: Parameter, unit: str, base: int = 1000, required: bool = False ) -> str: - if isinstance(param, Constant): + if isinstance(param, F.Constant): return as_unit(param, unit, base=base) - elif isinstance(param, Range): + elif isinstance(param, F.Range): center, delta = param.as_center_tuple(relative=True) delta_percent_str = f"±{to_si_str(delta.value, "%", 0)}" return ( f"{as_unit(center, unit, base=base, required=required)} {delta_percent_str}" ) - elif isinstance(param, Set): + elif isinstance(param, F.Set): return ( "Set(" + ", ".join( @@ -109,25 +99,25 @@ def as_unit_with_tolerance( ) + ")" ) - elif isinstance(param, TBD): + elif isinstance(param, F.TBD): return "TBD" if required else "" - elif isinstance(param, ANY): + elif isinstance(param, F.ANY): return "ANY" if required else "" raise ValueError(f"Unsupported {param=}") def get_parameter_max(param: Parameter): - if isinstance(param, Constant): + if isinstance(param, F.Constant): return param.value - if isinstance(param, Range): + if isinstance(param, F.Range): return param.max - if isinstance(param, Set): + if isinstance(param, F.Set): return max(map(get_parameter_max, param.params)) raise ValueError(f"Can't get max for {param}") def with_same_unit(to_convert: float | int, param: Parameter | Quantity | float | int): - if isinstance(param, Constant) and isinstance(param.value, Quantity): + if isinstance(param, F.Constant) and isinstance(param.value, Quantity): return Quantity(to_convert, param.value.units) if isinstance(param, Quantity): return Quantity(to_convert, param.units) @@ -141,16 +131,6 @@ def with_same_unit(to_convert: float | int, param: Parameter | Quantity | float # Graph Querying ----------------------------------------------------------------------- -def bfs_node(node: Node, filter: Callable[[GraphInterface], bool]): - return get_nodes_from_gifs(node.get_graph().bfs_visit(filter, [node.GIFs.self])) - - -def get_nodes_from_gifs(gifs: Iterable[GraphInterface]): - return {gif.node for gif in gifs} - # TODO what is faster - # return {n.node for n in gifs if isinstance(n, GraphInterfaceSelf)} - - # Make all kinds of graph filtering functions so we can optimize them in the future # Avoid letting user query all graph nodes always because quickly very slow @@ -159,27 +139,12 @@ def node_projected_graph(g: Graph) -> set[Node]: """ Don't call this directly, use get_all_nodes_by/of/with instead """ - return get_nodes_from_gifs(g.subgraph_type(GraphInterfaceSelf)) + return Node.get_nodes_from_gifs(g.subgraph_type(GraphInterfaceSelf)) @deprecated("Use get_node_children_all") def get_all_nodes(node: Node, include_root=False) -> list[Node]: - return get_node_children_all(node, include_root=include_root) - - -def get_node_children_all(node: Node, include_root=True) -> list[Node]: - # TODO looks like get_node_tree is 2x faster - - out = bfs_node( - node, - lambda x: isinstance(x, (GraphInterfaceSelf, GraphInterfaceHierarchical)) - and x is not node.GIFs.parent, - ) - - if not include_root: - out.remove(node) - - return list(out) + return node.get_node_children_all(include_root=include_root) def get_all_modules(node: Node) -> set[Module]: @@ -251,19 +216,19 @@ def get_connected_mifs_with_link(gif: GraphInterface): def get_direct_connected_nodes[T: Node]( gif: GraphInterface, ty: type[T] = Node ) -> set[T]: - out = get_nodes_from_gifs( + out = Node.get_nodes_from_gifs( g for g, t in get_all_connected(gif) if isinstance(t, LinkDirect) ) assert all(isinstance(n, ty) for n in out) return cast(set[T], out) -def get_net(mif: Electrical): +def get_net(mif: F.Electrical): from faebryk.library.Net import Net nets = { net - for mif in get_connected_mifs(mif.GIFs.connected) + for mif in get_connected_mifs(mif.connected) if (net := get_parent_of_type(mif, Net)) is not None } @@ -274,24 +239,6 @@ def get_net(mif: Electrical): return next(iter(nets)) -def get_children[T: Node]( - node: Node, - direct_only: bool, - types: type[T] | tuple[type[T], ...] = Node, - include_root: bool = False, -): - if direct_only: - children = get_node_direct_children_(node) - if include_root: - children.add(node) - else: - children = get_node_children_all(node, include_root=include_root) - - filtered = {n for n in children if isinstance(n, types)} - - return filtered - - @deprecated("Use get_node_direct_mods_or_mifs") def get_node_direct_children(node: Node, include_mifs: bool = True): return get_node_direct_mods_or_mifs(node, include_mifs=include_mifs) @@ -299,15 +246,7 @@ def get_node_direct_children(node: Node, include_mifs: bool = True): def get_node_direct_mods_or_mifs(node: Node, include_mifs: bool = True): types = (Module, ModuleInterface) if include_mifs else Module - return get_children(node, direct_only=True, types=types) - - -def get_node_direct_children_(node: Node): - return { - gif.node - for gif, link in node.get_graph().get_edges(node.GIFs.children).items() - if isinstance(link, LinkNamedParent) - } + return node.get_children(direct_only=True, types=types) def get_node_tree( @@ -347,7 +286,7 @@ def zip_exhaust(*args): def get_mif_tree( obj: ModuleInterface | Module, ) -> dict[ModuleInterface, dict[ModuleInterface, dict]]: - mifs = get_children(obj, direct_only=True, types=ModuleInterface) + mifs = obj.get_children(direct_only=True, types=ModuleInterface) return {mif: get_mif_tree(mif) for mif in mifs} @@ -382,22 +321,24 @@ def get_param_tree(param: Parameter) -> list[tuple[Parameter, list]]: def connect_interfaces_via_chain( - start: ModuleInterface, bridges: Iterable[Node], end: ModuleInterface + start: ModuleInterface, bridges: Iterable[Node], end: ModuleInterface, linkcls=None ): from faebryk.library.can_bridge import can_bridge last = start for bridge in bridges: - last.connect(bridge.get_trait(can_bridge).get_in()) + last.connect(bridge.get_trait(can_bridge).get_in(), linkcls=linkcls) last = bridge.get_trait(can_bridge).get_out() - last.connect(end) + last.connect(end, linkcls=linkcls) -def connect_all_interfaces[MIF: ModuleInterface](interfaces: Iterable[MIF]): +def connect_all_interfaces[MIF: ModuleInterface]( + interfaces: Iterable[MIF], linkcls=None +): interfaces = list(interfaces) if not interfaces: return - return connect_to_all_interfaces(interfaces[0], interfaces[1:]) + return connect_to_all_interfaces(interfaces[0], interfaces[1:], linkcls=linkcls) # not needed with current connection implementation # for i in interfaces: # for j in interfaces: @@ -405,10 +346,10 @@ def connect_all_interfaces[MIF: ModuleInterface](interfaces: Iterable[MIF]): def connect_to_all_interfaces[MIF: ModuleInterface]( - source: MIF, targets: Iterable[MIF] + source: MIF, targets: Iterable[MIF], linkcls=None ): for i in targets: - source.connect(i) + source.connect(i, linkcls=linkcls) return source @@ -428,7 +369,7 @@ def connect_module_mifs_by_name( ).items(): if src_m is None or dst_m is None: if not allow_partial: - raise Exception(f"Node with name {k} mot present in both") + raise Exception(f"Node with name {k} not present in both") continue src_m.connect(dst_m) @@ -444,7 +385,7 @@ def __init__(self) -> None: if_in = bridge_trait.get_in() if_out = bridge_trait.get_out() - self.add_trait(can_bridge_defined(if_out, if_in)) + self.add_trait(F.can_bridge_defined(if_out, if_in)) return _reversed_bridge() @@ -454,9 +395,7 @@ def zip_children_by_name[N: Node]( ) -> dict[str, tuple[N, N]]: nodes = (node1, node2) children = tuple( - with_names( - get_children(n, direct_only=True, include_root=False, types=sub_type) - ) + with_names(n.get_children(direct_only=True, include_root=False, types=sub_type)) for n in nodes ) return zip_dicts_by_key(*children) @@ -477,7 +416,7 @@ def specialize_interface[T: ModuleInterface]( general.connect(special) # Establish sibling relationship - general.GIFs.specialized.connect(special.GIFs.specializes) + general.specialized.connect(special.specializes) return special @@ -521,18 +460,18 @@ def get_node_prop_matrix[N: Node](sub_type: type[N]): # continue # special.add_trait(t) - general.GIFs.specialized.connect(special.GIFs.specializes) + general.specialized.connect(special.specializes) # Attach to new parent has_parent = special.get_parent() is not None assert not has_parent or attach_to is None if not has_parent: if attach_to: - attach_to.NODEs.extend_list("specialized", special) + attach_to.add(special, container=attach_to.specialized) else: gen_parent = general.get_parent() if gen_parent: - setattr(gen_parent[0].NODEs, f"{gen_parent[1]}_specialized", special) + gen_parent[0].add(special, name=f"{gen_parent[1]}_specialized") return special @@ -562,7 +501,7 @@ def get_parent_with_trait[TR: Trait](node: Node, trait: type[TR]): def get_children_of_type[U: Node](node: Node, child_type: type[U]) -> list[U]: - return list(get_children(node, direct_only=False, types=child_type)) + return list(node.get_children(direct_only=False, types=child_type)) def get_first_child_of_type[U: Node](node: Node, child_type: type[U]) -> U: @@ -579,9 +518,9 @@ def get_first_child_of_type[U: Node](node: Node, child_type: type[U]) -> U: def pretty_params(node: Module | ModuleInterface) -> str: - # TODO dont use get_all params = { - NotNone(p.get_parent())[1]: p.get_most_narrow() for p in node.PARAMs.get_all() + NotNone(p.get_parent())[1]: p.get_most_narrow() + for p in node.get_children(direct_only=True, types=Parameter) } params_str = "\n".join(f"{k}: {v}" for k, v in params.items()) @@ -630,7 +569,7 @@ def use_interface_names_as_net_names(node: Node, name: str | None = None): name_prefix = node.get_full_name() - el_ifs = {n for n in get_all_nodes(node) if isinstance(n, Electrical)} + el_ifs = {n for n in get_all_nodes(node) if isinstance(n, F.Electrical)} # for el_if in el_ifs: # print(el_if) @@ -640,7 +579,7 @@ def use_interface_names_as_net_names(node: Node, name: str | None = None): resolved: set[ModuleInterface] = set() # get representative interfaces that determine the name of the Net - to_use: set[Electrical] = set() + to_use: set[F.Electrical] = set() for el_if in el_ifs: # performance if el_if in resolved: @@ -654,7 +593,7 @@ def use_interface_names_as_net_names(node: Node, name: str | None = None): for c in connections if (p := c.get_parent()) and isinstance(n := p[0], Net) - and n.IFs.part_of in connections + and n.part_of in connections }: # logger.warning(f"Skipped, attached to Net: {el_if}: {matched_nets!r}") resolved.update(connections) @@ -673,7 +612,7 @@ def use_interface_names_as_net_names(node: Node, name: str | None = None): # performance resolved.update(group) - nets: dict[str, tuple[Net, Electrical]] = {} + nets: dict[str, tuple[Net, F.Electrical]] = {} for el_if in to_use: net_name = f"{name}{el_if.get_full_name().removeprefix(name_prefix)}" @@ -688,12 +627,12 @@ def use_interface_names_as_net_names(node: Node, name: str | None = None): + "\n\t".join(map(str, el_if.get_direct_connections())) + f"\n{'-'*80}" + "\nNet Connections\n\t" - + "\n\t".join(map(str, net.IFs.part_of.get_direct_connections())) + + "\n\t".join(map(str, net.part_of.get_direct_connections())) ) net = Net() - net.add_trait(has_overriden_name_defined(net_name)) - net.IFs.part_of.connect(el_if) + net.add_trait(F.has_overriden_name_defined(net_name)) + net.part_of.connect(el_if) logger.debug(f"Created {net_name} for {el_if}") nets[net_name] = net, el_if diff --git a/src/faebryk/exporters/bom/jlcpcb.py b/src/faebryk/exporters/bom/jlcpcb.py index ab5b9d88..9e6b8b8d 100644 --- a/src/faebryk/exporters/bom/jlcpcb.py +++ b/src/faebryk/exporters/bom/jlcpcb.py @@ -1,3 +1,6 @@ +# This file is part of the faebryk project +# SPDX-License-Identifier: MIT + import csv import logging import os @@ -5,14 +8,8 @@ from dataclasses import dataclass from pathlib import Path -from faebryk.core.core import Module -from faebryk.library.has_descriptive_properties import has_descriptive_properties -from faebryk.library.has_designator import has_designator -from faebryk.library.has_footprint import has_footprint -from faebryk.library.has_kicad_footprint import has_kicad_footprint -from faebryk.library.has_simple_value_representation import ( - has_simple_value_representation, -) +import faebryk.library._F as F +from faebryk.core.module import Module from faebryk.libs.picker.picker import DescriptiveProperties logger = logging.getLogger(__name__) @@ -104,30 +101,30 @@ def _compact_bomlines(bomlines: list[BOMLine]) -> list[BOMLine]: def _get_bomline(cmp: Module) -> BOMLine | None: - if not cmp.has_trait(has_footprint): + if not cmp.has_trait(F.has_footprint): return if not all( cmp.has_trait(t) for t in ( - has_descriptive_properties, - has_designator, + F.has_descriptive_properties, + F.has_designator, ) ): logger.warning(f"Missing fields on component {cmp}") return - properties = cmp.get_trait(has_descriptive_properties).get_properties() - footprint = cmp.get_trait(has_footprint).get_footprint() + properties = cmp.get_trait(F.has_descriptive_properties).get_properties() + footprint = cmp.get_trait(F.has_footprint).get_footprint() value = ( - cmp.get_trait(has_simple_value_representation).get_value() - if cmp.has_trait(has_simple_value_representation) + cmp.get_trait(F.has_simple_value_representation).get_value() + if cmp.has_trait(F.has_simple_value_representation) else "" ) - designator = cmp.get_trait(has_designator).get_designator() + designator = cmp.get_trait(F.has_designator).get_designator() - if not footprint.has_trait(has_kicad_footprint): + if not footprint.has_trait(F.has_kicad_footprint): logger.warning(f"Missing kicad footprint on component {cmp}") return @@ -145,7 +142,9 @@ def _get_bomline(cmp: Module) -> BOMLine | None: else "" ) - footprint_name = footprint.get_trait(has_kicad_footprint).get_kicad_footprint_name() + footprint_name = footprint.get_trait( + F.has_kicad_footprint + ).get_kicad_footprint_name() return BOMLine( Designator=designator, diff --git a/src/faebryk/exporters/esphome/esphome.py b/src/faebryk/exporters/esphome/esphome.py index 86fb2e2a..4496f9f4 100644 --- a/src/faebryk/exporters/esphome/esphome.py +++ b/src/faebryk/exporters/esphome/esphome.py @@ -6,10 +6,9 @@ import yaml -from faebryk.core.core import Graph, Parameter -from faebryk.core.util import get_all_nodes_with_trait -from faebryk.library.Constant import Constant -from faebryk.library.has_esphome_config import has_esphome_config +import faebryk.library._F as F +from faebryk.core.graphinterface import Graph +from faebryk.core.parameter import Parameter logger = logging.getLogger(__name__) @@ -54,7 +53,9 @@ def merge_dicts(*dicts: dict) -> dict: def make_esphome_config(G: Graph) -> dict: - esphome_components = get_all_nodes_with_trait(G, has_esphome_config) + from faebryk.core.util import get_all_nodes_with_trait + + esphome_components = get_all_nodes_with_trait(G, F.has_esphome_config) esphome_config = merge_dicts(*[t.get_config() for _, t in esphome_components]) @@ -62,9 +63,9 @@ def instantiate_param(param: Parameter | Any): if not isinstance(param, Parameter): return param - if not isinstance(param, Constant): + if not isinstance(param, F.Constant): raise Exception( - f"Parameter {param} is not a Constant, but {type(param)}" + f"Parameter {param} is not a F.Constant, but {type(param)}" f"Config: {esphome_config}" ) return param.value diff --git a/src/faebryk/exporters/netlist/graph.py b/src/faebryk/exporters/netlist/graph.py index c4026ba2..a18f01f8 100644 --- a/src/faebryk/exporters/netlist/graph.py +++ b/src/faebryk/exporters/netlist/graph.py @@ -6,32 +6,15 @@ import networkx as nx -from faebryk.core.core import Graph, Module -from faebryk.core.util import ( - get_all_nodes_with_trait, - get_connected_mifs, -) +import faebryk.library._F as F +from faebryk.core.graphinterface import Graph +from faebryk.core.module import Module from faebryk.exporters.netlist.netlist import T2Netlist -from faebryk.library.Electrical import Electrical -from faebryk.library.FootprintTrait import FootprintTrait -from faebryk.library.has_defined_descriptive_properties import ( - has_defined_descriptive_properties, -) -from faebryk.library.has_descriptive_properties import has_descriptive_properties -from faebryk.library.has_footprint import has_footprint -from faebryk.library.has_kicad_footprint import has_kicad_footprint -from faebryk.library.has_overriden_name import has_overriden_name -from faebryk.library.has_overriden_name_defined import has_overriden_name_defined -from faebryk.library.has_simple_value_representation import ( - has_simple_value_representation, -) -from faebryk.library.Net import Net -from faebryk.library.Pad import Pad logger = logging.getLogger(__name__) -class can_represent_kicad_footprint(FootprintTrait): +class can_represent_kicad_footprint(F.Footprint.TraitT): kicad_footprint = T2Netlist.Component @abstractmethod @@ -41,19 +24,19 @@ def get_name_and_value(self) -> tuple[str, str]: ... def get_kicad_obj(self) -> kicad_footprint: ... @abstractmethod - def get_pin_name(self, pin: Pad) -> str: ... + def get_pin_name(self, pin: F.Pad) -> str: ... def get_or_set_name_and_value_of_node(c: Module): value = ( - c.get_trait(has_simple_value_representation).get_value() - if c.has_trait(has_simple_value_representation) + c.get_trait(F.has_simple_value_representation).get_value() + if c.has_trait(F.has_simple_value_representation) else type(c).__name__ ) - if not c.has_trait(has_overriden_name): + if not c.has_trait(F.has_overriden_name): c.add_trait( - has_overriden_name_defined( + F.has_overriden_name_defined( "{}[{}:{}]".format( c.get_full_name(), type(c).__name__, @@ -62,11 +45,11 @@ def get_or_set_name_and_value_of_node(c: Module): ) ) - has_defined_descriptive_properties.add_properties_to( + F.has_descriptive_properties_defined.add_properties_to( c, {"faebryk_name": c.get_full_name()} ) - return c.get_trait(has_overriden_name).get_name(), value + return c.get_trait(F.has_overriden_name).get_name(), value class can_represent_kicad_footprint_via_attached_component( @@ -84,20 +67,20 @@ def __init__(self, component: Module, graph: nx.Graph) -> None: def get_name_and_value(self): return get_or_set_name_and_value_of_node(self.component) - def get_pin_name(self, pin: Pad): - return self.get_obj().get_trait(has_kicad_footprint).get_pin_names()[pin] + def get_pin_name(self, pin: F.Pad): + return self.obj.get_trait(F.has_kicad_footprint).get_pin_names()[pin] def get_kicad_obj(self): - fp = self.get_obj() + fp = self.get_obj(F.Footprint) properties = { - "footprint": fp.get_trait(has_kicad_footprint).get_kicad_footprint() + "footprint": fp.get_trait(F.has_kicad_footprint).get_kicad_footprint() } for c in [fp, self.component]: - if c.has_trait(has_descriptive_properties): + if c.has_trait(F.has_descriptive_properties): properties.update( - c.get_trait(has_descriptive_properties).get_properties() + c.get_trait(F.has_descriptive_properties).get_properties() ) name, value = self.get_name_and_value() @@ -109,16 +92,18 @@ def get_kicad_obj(self): ) -def add_or_get_net(interface: Electrical): - mifs = get_connected_mifs(interface.GIFs.connected) +def add_or_get_net(interface: F.Electrical): + from faebryk.core.util import get_connected_mifs + + mifs = get_connected_mifs(interface.connected) nets = { p[0] for mif in mifs - if (p := mif.get_parent()) is not None and isinstance(p[0], Net) + if (p := mif.get_parent()) is not None and isinstance(p[0], F.Net) } if not nets: - net = Net() - net.IFs.part_of.connect(interface) + net = F.Net() + net.part_of.connect(interface) return net if len(nets) > 1: raise Exception(f"Multiple nets interconnected: {nets}") @@ -126,6 +111,7 @@ def add_or_get_net(interface: Electrical): def attach_nets_and_kicad_info(g: Graph): + from faebryk.core.util import get_all_nodes_with_trait # g has to be closed Gclosed = g @@ -135,7 +121,7 @@ def attach_nets_and_kicad_info(g: Graph): n: t.get_footprint() # TODO maybe nicer to just look for footprints # and get their respective components instead - for n, t in get_all_nodes_with_trait(Gclosed, has_footprint) + for n, t in get_all_nodes_with_trait(Gclosed, F.has_footprint) if isinstance(n, Module) } @@ -151,7 +137,5 @@ def attach_nets_and_kicad_info(g: Graph): for fp in node_fps.values(): # TODO use graph - for mif in fp.IFs.get_all(): - if not isinstance(mif, Pad): - continue - add_or_get_net(mif.IFs.net) + for mif in fp.get_children(direct_only=True, types=F.Pad): + add_or_get_net(mif.net) diff --git a/src/faebryk/exporters/netlist/netlist.py b/src/faebryk/exporters/netlist/netlist.py index eadde0f5..0e7d77cd 100644 --- a/src/faebryk/exporters/netlist/netlist.py +++ b/src/faebryk/exporters/netlist/netlist.py @@ -4,10 +4,8 @@ import logging from dataclasses import dataclass -from faebryk.core.core import Graph -from faebryk.core.util import get_all_nodes_of_type, get_all_nodes_with_trait -from faebryk.library.has_footprint import has_footprint -from faebryk.library.has_overriden_name import has_overriden_name +import faebryk.library._F as F +from faebryk.core.graphinterface import Graph logger = logging.getLogger(__name__) @@ -38,6 +36,7 @@ class Vertex: def make_t2_netlist_from_graph(G: Graph) -> T2Netlist: + from faebryk.core.util import get_all_nodes_of_type, get_all_nodes_with_trait from faebryk.exporters.netlist.graph import can_represent_kicad_footprint from faebryk.library.Net import Net as FNet @@ -45,7 +44,7 @@ def make_t2_netlist_from_graph(G: Graph) -> T2Netlist: t2_nets = [ T2Netlist.Net( - properties={"name": net.get_trait(has_overriden_name).get_name()}, + properties={"name": net.get_trait(F.has_overriden_name).get_name()}, vertices=sorted( [ T2Netlist.Net.Vertex( @@ -63,7 +62,7 @@ def make_t2_netlist_from_graph(G: Graph) -> T2Netlist: comps = { t.get_footprint().get_trait(can_represent_kicad_footprint).get_kicad_obj() - for _, t in get_all_nodes_with_trait(G, has_footprint) + for _, t in get_all_nodes_with_trait(G, F.has_footprint) } not_found = [ diff --git a/src/faebryk/exporters/parameters/parameters_to_file.py b/src/faebryk/exporters/parameters/parameters_to_file.py index 95f23ba6..4e49f7ae 100644 --- a/src/faebryk/exporters/parameters/parameters_to_file.py +++ b/src/faebryk/exporters/parameters/parameters_to_file.py @@ -4,14 +4,15 @@ import logging from pathlib import Path -from faebryk.core.core import Module, Parameter -from faebryk.core.util import get_all_modules +from faebryk.core.module import Module +from faebryk.core.parameter import Parameter logger = logging.getLogger(__name__) def export_parameters_to_file(module: Module, path: Path): """Write all parameters of the given module to a file.""" + from faebryk.core.util import get_all_modules # {module_name: [{param_name: param_value}, {param_name: param_value},...]} parameters = dict[str, list[dict[str, Parameter]]]() @@ -21,7 +22,7 @@ def export_parameters_to_file(module: Module, path: Path): }: parameters[m.get_full_name(types=True).split(".", maxsplit=1)[-1]] = [ {param.get_full_name().split(".")[-1]: param} - for param in m.PARAMs.get_all() + for param in m.get_children(direct_only=True, types=Parameter) ] logger.info(f"Writing parameters to {path}") diff --git a/src/faebryk/exporters/pcb/kicad/transformer.py b/src/faebryk/exporters/pcb/kicad/transformer.py index 91017583..6ac0e658 100644 --- a/src/faebryk/exporters/pcb/kicad/transformer.py +++ b/src/faebryk/exporters/pcb/kicad/transformer.py @@ -14,24 +14,11 @@ from shapely import Polygon from typing_extensions import deprecated -from faebryk.core.core import ( - Graph, - Module, - ModuleInterfaceTrait, - ModuleTrait, - Node, -) -from faebryk.core.util import get_all_nodes_with_trait, get_all_nodes_with_traits -from faebryk.library.Electrical import Electrical -from faebryk.library.Footprint import ( - Footprint as FFootprint, -) -from faebryk.library.has_footprint import has_footprint -from faebryk.library.has_kicad_footprint import has_kicad_footprint -from faebryk.library.has_overriden_name import has_overriden_name -from faebryk.library.has_pcb_position import has_pcb_position -from faebryk.library.Net import Net as FNet -from faebryk.library.Pad import Pad as FPad +import faebryk.library._F as F +from faebryk.core.graphinterface import Graph +from faebryk.core.module import Module +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.core.node import Node from faebryk.libs.geometry.basic import Geometry from faebryk.libs.kicad.fileformats import ( UUID, @@ -55,6 +42,9 @@ logger = logging.getLogger(__name__) +FPad = F.Pad +FNet = F.Net +FFootprint = F.Footprint PCB = C_kicad_pcb_file.C_kicad_pcb Footprint = PCB.C_pcb_footprint @@ -141,10 +131,7 @@ def abs_pos2d(origin: T, vector: T2) -> Point2D: ) -R = TypeVar("R") - - -def per_point( +def per_point[R]( line: tuple[Point2D, Point2D], func: Callable[[Point2D], R] ) -> tuple[R, R]: return func(line[0]), func(line[1]) @@ -166,7 +153,7 @@ def get_all_geos(obj: PCB | Footprint) -> list[Geom]: class PCB_Transformer: - class has_linked_kicad_footprint(ModuleTrait): + class has_linked_kicad_footprint(Module.TraitT): """ Module has footprint (which has kicad footprint) and that footprint is found in the current PCB file. @@ -190,7 +177,7 @@ def get_fp(self): def get_transformer(self): return self.transformer - class has_linked_kicad_pad(ModuleInterfaceTrait): + class has_linked_kicad_pad(ModuleInterface.TraitT): @abstractmethod def get_pad(self) -> tuple[Footprint, list[Pad]]: ... @@ -235,16 +222,17 @@ def attach(self): footprints = { (f.propertys["Reference"].value, f.name): f for f in self.pcb.footprints } + from faebryk.core.util import get_all_nodes_with_trait - for node, fpt in get_all_nodes_with_trait(self.graph, has_footprint): - if not node.has_trait(has_overriden_name): + for node, fpt in get_all_nodes_with_trait(self.graph, F.has_footprint): + if not node.has_trait(F.has_overriden_name): continue g_fp = fpt.get_footprint() - if not g_fp.has_trait(has_kicad_footprint): + if not g_fp.has_trait(F.has_kicad_footprint): continue - fp_ref = node.get_trait(has_overriden_name).get_name() - fp_name = g_fp.get_trait(has_kicad_footprint).get_kicad_footprint() + fp_ref = node.get_trait(F.has_overriden_name).get_name() + fp_name = g_fp.get_trait(F.has_kicad_footprint).get_kicad_footprint() assert ( fp_ref, @@ -258,8 +246,8 @@ def attach(self): g_fp.add_trait(self.has_linked_kicad_footprint_defined(fp, self)) node.add_trait(self.has_linked_kicad_footprint_defined(fp, self)) - pin_names = g_fp.get_trait(has_kicad_footprint).get_pin_names() - for fpad in g_fp.IFs.get_all(): + pin_names = g_fp.get_trait(F.has_kicad_footprint).get_pin_names() + for fpad in g_fp.get_children(direct_only=True, types=ModuleInterface): pads = [ pad for pad in fp.pads @@ -293,10 +281,8 @@ def cleanup(self): elif isinstance(holder, dict): del holder[get_key(obj, holder)] - T = TypeVar("T") - @staticmethod - def flipped(input_list: list[tuple[T, int]]) -> list[tuple[T, int]]: + def flipped[T](input_list: list[tuple[T, int]]) -> list[tuple[T, int]]: return [(x, (y + 180) % 360) for x, y in reversed(input_list)] @staticmethod @@ -316,7 +302,7 @@ def get_fp(cmp) -> Footprint: def get_net(self, net: FNet) -> Net: nets = {pcb_net.name: pcb_net for pcb_net in self.pcb.nets} - return nets[net.get_trait(has_overriden_name).get_name()] + return nets[net.get_trait(F.has_overriden_name).get_name()] def get_edge(self) -> list[Point2D]: def geo_to_lines( @@ -407,12 +393,11 @@ def geo_to_lines( return list(poly.exterior.coords) @staticmethod - def _get_pad(ffp: FFootprint, intf: Electrical): - pin_map = ffp.get_trait(has_kicad_footprint).get_pin_names() + def _get_pad(ffp: FFootprint, intf: F.Electrical): + pin_map = ffp.get_trait(F.has_kicad_footprint).get_pin_names() pin_name = find( pin_map.items(), - lambda pad_and_name: intf.is_connected_to(pad_and_name[0].IFs.net) - is not None, + lambda pad_and_name: intf.is_connected_to(pad_and_name[0].net) is not None, )[1] fp = PCB_Transformer.get_fp(ffp) @@ -421,14 +406,14 @@ def _get_pad(ffp: FFootprint, intf: Electrical): return fp, pad @staticmethod - def get_pad(intf: Electrical) -> tuple[Footprint, Pad, Node]: + def get_pad(intf: F.Electrical) -> tuple[Footprint, Pad, Node]: obj, ffp = FFootprint.get_footprint_of_parent(intf) fp, pad = PCB_Transformer._get_pad(ffp, intf) return fp, pad, obj @staticmethod - def get_pad_pos_any(intf: Electrical) -> list[tuple[FPad, Point]]: + def get_pad_pos_any(intf: F.Electrical) -> list[tuple[FPad, Point]]: try: fpads = FPad.find_pad_for_intf_with_parent_that_has_footprint(intf) except ValueError: @@ -438,7 +423,7 @@ def get_pad_pos_any(intf: Electrical) -> list[tuple[FPad, Point]]: return [PCB_Transformer._get_pad_pos(fpad) for fpad in fpads] @staticmethod - def get_pad_pos(intf: Electrical) -> tuple[FPad, Point] | None: + def get_pad_pos(intf: F.Electrical) -> tuple[FPad, Point] | None: try: fpad = FPad.find_pad_for_intf_with_parent_that_has_footprint_unique(intf) except ValueError: @@ -516,7 +501,7 @@ def get_layer_name(self, layer_id: int) -> str: # Insert --------------------------------------------------------------------------- @staticmethod - def mark(node: R) -> R: + def mark[R](node: R) -> R: if hasattr(node, "uuid"): node.uuid = PCB_Transformer.gen_uuid(mark=True) # type: ignore @@ -526,7 +511,7 @@ def mark(node: R) -> R: def insert(self, obj: Any): self._insert(obj) - def _get_pcb_node_list(self, node: R, prefix: str = "") -> list[R]: + def _get_pcb_list_field[R](self, node: R, prefix: str = "") -> list[R]: root = self.pcb key = prefix + type(node).__name__.removeprefix("C_") + "s" @@ -539,10 +524,10 @@ def _get_pcb_node_list(self, node: R, prefix: str = "") -> list[R]: def _insert(self, obj: Any, prefix: str = ""): obj = PCB_Transformer.mark(obj) - self._get_pcb_node_list(obj, prefix=prefix).append(obj) + self._get_pcb_list_field(obj, prefix=prefix).append(obj) def _delete(self, obj: Any, prefix: str = ""): - self._get_pcb_node_list(obj, prefix=prefix).remove(obj) + self._get_pcb_list_field(obj, prefix=prefix).remove(obj) def insert_via( self, coord: tuple[float, float], net: str, size_drill: tuple[float, float] @@ -707,22 +692,24 @@ def insert_zone(self, net: Net, layers: str | list[str], polygon: list[Point2D]) # Positioning ---------------------------------------------------------------------- def move_footprints(self): + from faebryk.core.util import get_all_nodes_with_traits + # position modules with defined positions pos_mods = get_all_nodes_with_traits( - self.graph, (has_pcb_position, self.has_linked_kicad_footprint) + self.graph, (F.has_pcb_position, self.has_linked_kicad_footprint) ) logger.info(f"Positioning {len(pos_mods)} footprints") for module, _ in pos_mods: fp = module.get_trait(self.has_linked_kicad_footprint).get_fp() - coord = module.get_trait(has_pcb_position).get_position() + coord = module.get_trait(F.has_pcb_position).get_position() layer_name = { - has_pcb_position.layer_type.TOP_LAYER: "F.Cu", - has_pcb_position.layer_type.BOTTOM_LAYER: "B.Cu", + F.has_pcb_position.layer_type.TOP_LAYER: "F.Cu", + F.has_pcb_position.layer_type.BOTTOM_LAYER: "B.Cu", } - if coord[3] == has_pcb_position.layer_type.NONE: + if coord[3] == F.has_pcb_position.layer_type.NONE: raise Exception(f"Component {module}({fp.name}) has no layer defined") logger.debug(f"Placing {fp.name} at {coord} layer {layer_name[coord[3]]}") diff --git a/src/faebryk/exporters/pcb/layout/absolute.py b/src/faebryk/exporters/pcb/layout/absolute.py index bf98801a..8d0020fa 100644 --- a/src/faebryk/exporters/pcb/layout/absolute.py +++ b/src/faebryk/exporters/pcb/layout/absolute.py @@ -4,28 +4,23 @@ import logging from dataclasses import dataclass -from faebryk.core.core import ( - Node, -) +import faebryk.library._F as F +from faebryk.core.node import Node from faebryk.exporters.pcb.layout.layout import Layout -from faebryk.library.has_pcb_position import has_pcb_position -from faebryk.library.has_pcb_position_defined_relative_to_parent import ( - has_pcb_position_defined_relative_to_parent, -) logger = logging.getLogger(__name__) @dataclass(frozen=True, eq=True) class LayoutAbsolute(Layout): - pos: has_pcb_position.Point + pos: F.has_pcb_position.Point def apply(self, *node: Node): """ Tip: Make sure at least one parent of node has an absolute position defined """ # Remove nodes that have a position defined - node = tuple(n for n in node if not n.has_trait(has_pcb_position)) + node = tuple(n for n in node if not n.has_trait(F.has_pcb_position)) for n in node: - n.add_trait(has_pcb_position_defined_relative_to_parent(self.pos)) + n.add_trait(F.has_pcb_position_defined_relative_to_parent(self.pos)) diff --git a/src/faebryk/exporters/pcb/layout/extrude.py b/src/faebryk/exporters/pcb/layout/extrude.py index 5e815c38..f3ce1265 100644 --- a/src/faebryk/exporters/pcb/layout/extrude.py +++ b/src/faebryk/exporters/pcb/layout/extrude.py @@ -4,14 +4,9 @@ import logging from dataclasses import dataclass -from faebryk.core.core import ( - Node, -) +import faebryk.library._F as F +from faebryk.core.node import Node from faebryk.exporters.pcb.layout.layout import Layout -from faebryk.library.has_pcb_position import has_pcb_position -from faebryk.library.has_pcb_position_defined_relative_to_parent import ( - has_pcb_position_defined_relative_to_parent, -) from faebryk.libs.geometry.basic import Geometry logger = logging.getLogger(__name__) @@ -30,8 +25,8 @@ class LayoutExtrude(Layout): """ vector: tuple[float, float] | tuple[float, float, float] - base: has_pcb_position.Point = has_pcb_position.Point( - (0, 0, 0, has_pcb_position.layer_type.NONE) + base: F.has_pcb_position.Point = F.has_pcb_position.Point( + (0, 0, 0, F.has_pcb_position.layer_type.NONE) ) dynamic_rotation: bool = False @@ -41,7 +36,7 @@ def apply(self, *node: Node): """ # Remove nodes that have a position defined - node = tuple(n for n in node if not n.has_trait(has_pcb_position)) + node = tuple(n for n in node if not n.has_trait(F.has_pcb_position)) vector = self.vector if len(self.vector) == 3 else (*self.vector, 0) @@ -50,8 +45,8 @@ def apply(self, *node: Node): vector[0] * i, vector[1] * i, (vector[2] * (i if self.dynamic_rotation else 1)) % 360, - has_pcb_position.layer_type.NONE, + F.has_pcb_position.layer_type.NONE, ) pos = Geometry.abs_pos(self.base, vec_i) - n.add_trait(has_pcb_position_defined_relative_to_parent(pos)) + n.add_trait(F.has_pcb_position_defined_relative_to_parent(pos)) diff --git a/src/faebryk/exporters/pcb/layout/font.py b/src/faebryk/exporters/pcb/layout/font.py index 4617e578..0a8e9aa6 100644 --- a/src/faebryk/exporters/pcb/layout/font.py +++ b/src/faebryk/exporters/pcb/layout/font.py @@ -5,12 +5,9 @@ from rich.progress import track -from faebryk.core.core import Node +import faebryk.library._F as F +from faebryk.core.node import Node from faebryk.exporters.pcb.layout.layout import Layout -from faebryk.library.has_pcb_position import has_pcb_position -from faebryk.library.has_pcb_position_defined_relative_to_parent import ( - has_pcb_position_defined_relative_to_parent, -) from faebryk.libs.font import Font from faebryk.libs.geometry.basic import get_distributed_points_in_polygon @@ -74,11 +71,11 @@ def apply(self, *nodes_to_distribute: Node) -> None: for coord, node in zip(self.coords, nodes_to_distribute): node.add_trait( - has_pcb_position_defined_relative_to_parent( + F.has_pcb_position_defined_relative_to_parent( ( *coord, 0, - has_pcb_position.layer_type.NONE, + F.has_pcb_position.layer_type.NONE, ) ) ) diff --git a/src/faebryk/exporters/pcb/layout/layout.py b/src/faebryk/exporters/pcb/layout/layout.py index 7da7705a..62551936 100644 --- a/src/faebryk/exporters/pcb/layout/layout.py +++ b/src/faebryk/exporters/pcb/layout/layout.py @@ -1,6 +1,6 @@ from abc import abstractmethod -from faebryk.core.core import Node +from faebryk.core.node import Node class Layout: diff --git a/src/faebryk/exporters/pcb/layout/matrix.py b/src/faebryk/exporters/pcb/layout/matrix.py index a0d164ba..5215cb72 100644 --- a/src/faebryk/exporters/pcb/layout/matrix.py +++ b/src/faebryk/exporters/pcb/layout/matrix.py @@ -4,14 +4,9 @@ import logging from dataclasses import dataclass -from faebryk.core.core import ( - Node, -) +import faebryk.library._F as F +from faebryk.core.node import Node from faebryk.exporters.pcb.layout.layout import Layout -from faebryk.library.has_pcb_position import has_pcb_position -from faebryk.library.has_pcb_position_defined_relative_to_parent import ( - has_pcb_position_defined_relative_to_parent, -) from faebryk.libs.geometry.basic import Geometry logger = logging.getLogger(__name__) @@ -30,8 +25,8 @@ class LayoutMatrix(Layout): vector: tuple[float, float] | tuple[float, float, float] distribution: tuple[int, int] - base: has_pcb_position.Point = has_pcb_position.Point( - (0, 0, 0, has_pcb_position.layer_type.NONE) + base: F.has_pcb_position.Point = F.has_pcb_position.Point( + (0, 0, 0, F.has_pcb_position.layer_type.NONE) ) def apply(self, *node: Node): @@ -40,7 +35,7 @@ def apply(self, *node: Node): """ # Remove nodes that have a position defined - node = tuple(n for n in node if not n.has_trait(has_pcb_position)) + node = tuple(n for n in node if not n.has_trait(F.has_pcb_position)) vector = self.vector if len(self.vector) == 3 else (*self.vector, 0) @@ -63,11 +58,11 @@ def apply(self, *node: Node): vector[0] * x, vector[1] * y, vector[2], - has_pcb_position.layer_type.NONE, + F.has_pcb_position.layer_type.NONE, ) pos = Geometry.abs_pos(self.base, vec_i) node[node_index].add_trait( - has_pcb_position_defined_relative_to_parent(pos) + F.has_pcb_position_defined_relative_to_parent(pos) ) node_index += 1 diff --git a/src/faebryk/exporters/pcb/layout/typehierarchy.py b/src/faebryk/exporters/pcb/layout/typehierarchy.py index eb6ebd79..1389de43 100644 --- a/src/faebryk/exporters/pcb/layout/typehierarchy.py +++ b/src/faebryk/exporters/pcb/layout/typehierarchy.py @@ -4,13 +4,11 @@ import logging from dataclasses import dataclass -from faebryk.core.core import ( - Module, - Node, -) -from faebryk.core.util import get_node_direct_children +from faebryk.core.module import Module +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.core.node import Node from faebryk.exporters.pcb.layout.layout import Layout -from faebryk.libs.util import NotNone, find_or, flatten, groupby +from faebryk.libs.util import NotNone, find_or, groupby logger = logging.getLogger(__name__) @@ -30,7 +28,7 @@ def apply(self, *node: Node): Tip: Make sure at least one parent of node has an absolute position defined """ - # Find the layout for each node and group by matched level + # Find the layout for each node (isinstance mod_type) and group by matched level levels = groupby( { n: find_or( @@ -48,12 +46,19 @@ def apply(self, *node: Node): for level, nodes_tuple in levels.items(): nodes = [n for n, _ in nodes_tuple] - direct_children = flatten(get_node_direct_children(n) for n in nodes) + direct_children = { + c + for n in nodes + for c in n.get_children( + direct_only=True, types=(Module, ModuleInterface) + ) + } logger.debug( f"Level: {level.mod_type if level else None}," f" Children: {direct_children}" ) + # No type match, search for children instead if level is None: self.apply(*direct_children) continue diff --git a/src/faebryk/exporters/pcb/routing/grid.py b/src/faebryk/exporters/pcb/routing/grid.py index e7b10bd7..93cfe3f3 100644 --- a/src/faebryk/exporters/pcb/routing/grid.py +++ b/src/faebryk/exporters/pcb/routing/grid.py @@ -6,7 +6,7 @@ import math import re from abc import ABC, abstractmethod -from typing import Generic, Self, TypeVar +from typing import Self import graph_tool.all as gt import networkx as nx @@ -19,8 +19,6 @@ DIAGONALS = True WEIGHTS = (10, 15, 10000) -T = TypeVar("T", int, float) - class GridException(Exception): ... @@ -35,7 +33,7 @@ def get_coord(self, grid: "Grid"): return grid._project_out(self.vertex_index) -class Coord(Generic[T], np.ndarray): +class Coord[T: int, float](np.ndarray): EPS = 0.05 def __new__(cls, x: T, y: T, z: T): @@ -130,10 +128,7 @@ def eq(c1: intCoord, c2: intCoord): return all([_c1 == _c2 for _c1, _c2 in zip(c1, c2)]) -T = TypeVar("T") - - -class Graph(Generic[T], ABC): +class Graph[T](ABC): def __init__(self, G: T, steps): self.G = G self.steps = steps diff --git a/src/faebryk/exporters/pcb/routing/routing.py b/src/faebryk/exporters/pcb/routing/routing.py index 57b76576..92cbb30c 100644 --- a/src/faebryk/exporters/pcb/routing/routing.py +++ b/src/faebryk/exporters/pcb/routing/routing.py @@ -10,10 +10,7 @@ import networkx as nx -from faebryk.core.util import ( - get_all_nodes_of_type, - get_net, -) +import faebryk.library._F as F from faebryk.exporters.pcb.kicad.transformer import PCB_Transformer from faebryk.exporters.pcb.routing.grid import ( Coord, @@ -22,8 +19,6 @@ GridInvalidVertexException, OutCoord, ) -from faebryk.library.has_overriden_name import has_overriden_name -from faebryk.library.Net import Net from faebryk.libs.geometry.basic import Geometry from faebryk.libs.kicad.pcb import Footprint, GR_Circle, GR_Line, GR_Rect, Pad @@ -129,13 +124,13 @@ def draw_circle(self, coord: OutCoord, size=0.5, layer="User.9"): ) def route_all(self): - from faebryk.library.Net import Net as FNet + from faebryk.core.util import get_all_nodes_of_type - nets = get_all_nodes_of_type(self.transformer.graph, FNet) + nets = get_all_nodes_of_type(self.transformer.graph, F.Net) # TODO add net picking heuristic for net in nets: - netname = net.get_trait(has_overriden_name).get_name() + netname = net.get_trait(F.has_overriden_name).get_name() try: self.route_net(net) except GridInvalidVertexException as e: @@ -161,12 +156,12 @@ def layer(p): return [layer(p) for p in pad.pos] - def route_net(self, net: Net): + def route_net(self, net: F.Net): transformer = self.transformer assert net is not None pcb_net = transformer.get_net(net) - net_name = net.get_trait(has_overriden_name).get_name() + net_name = net.get_trait(F.has_overriden_name).get_name() mifs = net.get_connected_interfaces() # get pads @@ -239,6 +234,8 @@ def route_net(self, net: Net): ) def route_if_net(self, mif): + from faebryk.core.util import get_net + net = get_net(mif) assert net is not None self.route_net(net) diff --git a/src/faebryk/exporters/pcb/routing/util.py b/src/faebryk/exporters/pcb/routing/util.py index b9b1ffe2..5bef9acb 100644 --- a/src/faebryk/exporters/pcb/routing/util.py +++ b/src/faebryk/exporters/pcb/routing/util.py @@ -3,25 +3,17 @@ import logging from dataclasses import dataclass -from typing import Iterable, Sequence - -from faebryk.core.core import ( - Module, - ModuleInterface, - Node, -) -from faebryk.core.util import ( - get_all_nodes, - get_connected_mifs, - get_net, - get_parent_of_type, -) -from faebryk.exporters.pcb.kicad.transformer import PCB_Transformer -from faebryk.library.Electrical import Electrical -from faebryk.library.Net import Net -from faebryk.library.Pad import Pad +from typing import TYPE_CHECKING, Iterable, Sequence + +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.core.node import Node from faebryk.libs.geometry.basic import Geometry +if TYPE_CHECKING: + from faebryk.exporters.pcb.kicad.transformer import PCB_Transformer + # logging settings logger = logging.getLogger(__name__) @@ -95,36 +87,36 @@ def __add__(self, other: "Path"): class Route(Module): + net_: F.Electrical + pcb: ModuleInterface + def __init__( self, - pads: Iterable[Pad], + pads: Iterable[F.Pad], path: Path | None = None, ): super().__init__() - self.path = path or Path() + self._pads = pads - class _IFs(super().IFS()): - net = Electrical() - pcb = ModuleInterface() - - self.IFs = _IFs(self) - - for pad in pads: - self.IFs.pcb.connect(pad.IFs.pcb) - self.IFs.net.connect(pad.IFs.net) + def __preinit__(self): + for pad in self._pads: + self.pcb.connect(pad.pcb) + self.net_.connect(pad.net) def add(self, obj: Path.Obj): self.path.add(obj) @property def net(self): - net = get_net(self.IFs.net) + from faebryk.core.util import get_net + + net = get_net(self.net_) assert net return net -def apply_route_in_pcb(route: Route, transformer: PCB_Transformer): +def apply_route_in_pcb(route: Route, transformer: "PCB_Transformer"): pcb_net = transformer.get_net(route.net) logger.debug(f"Insert tracks for net {pcb_net.name}, {pcb_net.number}, {route}") @@ -178,25 +170,32 @@ def apply_route_in_pcb(route: Route, transformer: PCB_Transformer): def get_internal_nets_of_node( node: Node, -) -> dict[Net | None, Iterable[ModuleInterface]]: +) -> dict[F.Net | None, Iterable[ModuleInterface]]: """ Returns all Nets occuring (at least partially) within Node and returns for each of those the corresponding mifs For Nets returns all connected mifs """ + from faebryk.core.util import ( + get_all_nodes, + get_connected_mifs, + get_net, + ) from faebryk.libs.util import groupby - if isinstance(node, Net): - return {node: get_connected_mifs(node.IFs.part_of.GIFs.connected)} + if isinstance(node, F.Net): + return {node: get_connected_mifs(node.part_of.connected)} - mifs = {n for n in get_all_nodes(node) + [node] if isinstance(n, Electrical)} + mifs = {n for n in get_all_nodes(node) + [node] if isinstance(n, F.Electrical)} nets = groupby(mifs, lambda mif: get_net(mif)) return nets -def get_pads_pos_of_mifs(mifs: Sequence[Electrical]): +def get_pads_pos_of_mifs(mifs: Sequence[F.Electrical]): + from faebryk.exporters.pcb.kicad.transformer import PCB_Transformer + return { pad_pos[0]: pad_pos[1] for mif in mifs @@ -205,13 +204,13 @@ def get_pads_pos_of_mifs(mifs: Sequence[Electrical]): def group_pads_that_are_connected_already( - pads: Iterable[Pad], -) -> list[set[Pad]]: - out: list[set[Pad]] = [] + pads: Iterable[F.Pad], +) -> list[set[F.Pad]]: + out: list[set[F.Pad]] = [] for pad in pads: for group in out: # Only need to check first, because transitively connected - if pad.IFs.pcb.is_connected_to(next(iter(group)).IFs.pcb): + if pad.pcb.is_connected_to(next(iter(group)).pcb): group.add(pad) break else: @@ -219,9 +218,11 @@ def group_pads_that_are_connected_already( return out -def get_routes_of_pad(pad: Pad): +def get_routes_of_pad(pad: F.Pad): + from faebryk.core.util import get_parent_of_type + return { route - for mif in pad.IFs.pcb.get_direct_connections() + for mif in pad.pcb.get_direct_connections() if (route := get_parent_of_type(mif, Route)) } diff --git a/src/faebryk/exporters/project/faebryk/project_faebryk.py b/src/faebryk/exporters/project/faebryk/project_faebryk.py index abdcbdd9..b190a07e 100644 --- a/src/faebryk/exporters/project/faebryk/project_faebryk.py +++ b/src/faebryk/exporters/project/faebryk/project_faebryk.py @@ -91,7 +91,7 @@ class _IFs(Component.InterfacesCls()): unnamed = {unnamed_ifs} {named_if_expr} - self.IFs = _IFs(self) + self = _IFs(self) {trait_expr} @@ -145,7 +145,7 @@ def get_comp_name(component): name = get_comp_name(component) pinmap = { - pin: (f"{{name}}.IFs.P{pin}", f"P{pin}") + pin: (f"{{name}}.P{pin}", f"P{pin}") for pin in component["neighbors"].keys() } diff --git a/src/faebryk/exporters/visualize/graph.py b/src/faebryk/exporters/visualize/graph.py deleted file mode 100644 index cb38ac1b..00000000 --- a/src/faebryk/exporters/visualize/graph.py +++ /dev/null @@ -1,537 +0,0 @@ -# This file is part of the faebryk project -# SPDX-License-Identifier: MIT - -import logging -from copy import copy - -import networkx as nx - -from faebryk.core.core import ( - GraphInterface, - GraphInterfaceHierarchical, - GraphInterfaceModuleConnection, - GraphInterfaceModuleSibling, - GraphInterfaceSelf, - Link, - LinkDirect, - LinkNamedParent, - LinkSibling, - Node, -) -from faebryk.library.Electrical import Electrical -from faebryk.libs.util import cast_assert - -logger = logging.getLogger(__name__) - - -def _direction_to_str(direction: tuple[GraphInterface, GraphInterface] | None): - if direction is None: - return str(direction) - return [str(x) for x in direction] - - -def merge(G: nx.Graph, root: GraphInterface, group: set[GraphInterface]) -> nx.Graph: - if len(group) == 0: - return G - - nodes = set(G.nodes) - to_merge = group - {root} - assert root in group - - Gout = nx.Graph(G.subgraph(nodes - to_merge)) - assert group.issubset(nodes) - - # find connections to the outside (of group) to move to root node - edges: dict[GraphInterface, dict] = {} - for n in to_merge: - for d in set(G[n]) - group: - # TODO also merge links - data = dict(G.get_edge_data(n, d)) - - # connection to representative - if d not in edges: - edges[d] = {"merged": []} - e = edges[d] - - # Direction - direction = data["direction"] - direction_new = None - if direction is not None: - direction_new = tuple( - intf if intf is not n else root for intf in direction - ) - - e["merged"].extend(data["merged"]) - - if "direction" not in e: - e["direction"] = direction_new - if direction_new is not None and e["direction"] != direction_new: - e["direction"] = None - - Gout.add_edges_from([(root, d, data) for d, data in edges.items()]) - # print("Merge:", len(G.nodes), root, len(group), "->", len(Gout.nodes)) - return Gout - - -# TODO untested -def make_abstract(G: nx.Graph, root: Node) -> nx.Graph: - merged_ifs = _get_all_sub_GIFs(G, root) - assert all(map(lambda n: n in G.nodes, merged_ifs)) - - # Remove to be merged ifs from graph - Gout = G.copy() - Gout.remove_nodes_from(merged_ifs) - - node = Node() - edges: list[tuple[GraphInterface, GraphInterface, dict]] = [] - - for child in [root] + _get_children(G, root): - gifs = _get_neighbor_gifs(G, child.GIFs.self) - for gif in gifs: - if gif.name not in [x.name for x in node.GIFs.get_all()]: - # TODO not sure if thats ok or needed - copied_gif = copy(gif) - copied_gif.connections = [] - setattr(node.GIFs, gif.name, copied_gif) - node_gif = getattr(node.GIFs, gif.name) - assert isinstance(node_gif, GraphInterface) - - for e in G[gif]: - assert isinstance(e, GraphInterface) - if e in merged_ifs: - continue - data = dict(G.get_edge_data(gif, e)) - - direction = data["direction"] - if direction is not None: - direction_new = tuple( - intf if intf is not gif else node_gif for intf in direction - ) - print( - "Replace", - _direction_to_str(direction), - _direction_to_str(direction_new), - ) - data["direction"] = direction_new - - # connection to representative - edges.append((e, node_gif, data)) - - Gout.add_nodes_from(node.GIFs.get_all()) - Gout.add_edges_from([(e, node_gif, d) for e, node_gif, d in edges]) - - return Gout - - -def merge_sub(G: nx.Graph, node: Node): - return merge(G, node.GIFs.self, set(_get_all_sub_GIFs(G, node))) - - -def _get_neighbor_gifs(G: nx.Graph, gif: GraphInterface) -> list[GraphInterface]: - if gif not in G: - return [] - return [cast_assert(GraphInterface, x) for x in G[gif]] - - -def _get_neighbor_nodes(G: nx.Graph, gif: GraphInterface) -> list[Node]: - if gif not in G: - return [] - return [ - n - for neighbor_gif in G[gif] - if (n := cast_assert(GraphInterface, neighbor_gif).node) is not gif.node - ] - - -def _get_all_sub_GIFs(G: nx.Graph, node: Node) -> list[GraphInterface]: - node_if = node.GIFs.self - - if node_if not in G: - return [] - - out: list[GraphInterface] = [node_if] - # all siblings - out.extend(_get_neighbor_gifs(G, node_if)) - # all gifs of children - for c in _get_children(G, node, recursive=False): - out.extend(_get_all_sub_GIFs(G, c)) - return out - - -def _get_parents(G: nx.Graph, node: Node, recursive=True) -> list[Node]: - node_if = node.GIFs.self - - parent_if = [ - x - for x in _get_neighbor_gifs(G, node_if) - if isinstance(x, GraphInterfaceHierarchical) and not x.is_parent - ] - assert len(parent_if) == 1 - - parent_if = parent_if[0] - - parents = _get_neighbor_nodes(G, parent_if) - if len(parents) == 0: - return parents - if not recursive: - return parents - - return parents + [ - p - for direct_p in parents - for p in _get_parents(G, direct_p, recursive=recursive) - ] - - -def _get_children(G: nx.Graph, node: Node, recursive=True) -> list[Node]: - node_if = node.GIFs.self - - children_if = [ - x - for x in _get_neighbor_gifs(G, node_if) - if isinstance(x, GraphInterfaceHierarchical) and x.is_parent - ] - assert len(children_if) == 1 - children_if = children_if[0] - - children = _get_neighbor_nodes(G, children_if) - if len(children) == 0: - return children - if not recursive: - return children - - return children + [ - c - for direct_c in children - for c in _get_children(G, direct_c, recursive=recursive) - ] - - -def _get_top_level_nodes(G: nx.Graph, level: int): - # print(level, "-" * 40) - top_nodes: set[Node] = set() - - for i in G.nodes: - assert isinstance(i, GraphInterface) - if not isinstance(i, GraphInterfaceSelf): - continue - - n = i.node - # find top-level nodes - if len(_get_parents(G, n, recursive=False)) > 0: - continue - - # get children deep in hierarchy - targets: list[Node] = [n] - for i in range(level): - targets = [ - i for _n in targets for i in _get_children(G, _n, recursive=False) - ] - for t in targets: - top_nodes.add(t) - - # print("Top", level, top_nodes) - return top_nodes - - -def _add_node(G: nx.Graph, node: Node): - G_ = node.get_graph().G - tag_with_info(G_) - G.add_nodes_from(G_.nodes) - G.add_edges_from(G_.edges(data=True)) - - -def sub_graph(G: nx.Graph, nodes: list[Node]): - """ - Merge all GIFs that are not part of the specified nodes into the highest level - representantive that is not a parent of nodes. - """ - - # no duplicate - assert len(set(nodes)) == len(nodes) - - gifs = {gif for node in nodes for gif in _get_all_sub_GIFs(G, node)} - other_gifs: set[GraphInterface] = set(G.nodes) - gifs - - G_ = G.copy() - - # Detach from parents - for n in nodes: - ps = _get_parents(G, n, recursive=False) - for p in ps: - G_.remove_edge(n.GIFs.parent, p.GIFs.children) - - # Make representing node for rest of graph - repr_node = Node() - repr_gif = GraphInterface() - repr_node.GIFs.sub_conns = repr_gif - _add_node(G_, repr_node) - root = repr_gif - other_gifs.add(root) - - Gout = merge(G_, root, other_gifs) - - return Gout - - -def sub_tree(G: nx.Graph, nodes: list[Node]): - return G.subgraph([gif for node in nodes for gif in _get_all_sub_GIFs(G, node)]) - - -def node_graph(G: nx.Graph, level: int) -> nx.Graph: - Gout = G - for n in _get_top_level_nodes(G, level): - Gout = merge_sub(Gout, n) - return Gout - - -def tag_with_info(G: nx.Graph): - for t0, t1, d in G.edges(data=True): - link = d["link"] - assert isinstance(link, Link) - - # Direction - direction = None - if isinstance(link, LinkNamedParent): - assert link.get_parent() in [t0, t1] - assert link.get_child() in [t0, t1] - direction = (link.get_parent(), link.get_child()) - elif isinstance(link, LinkSibling): - assert isinstance(t0, GraphInterfaceSelf) or isinstance( - t1, GraphInterfaceSelf - ) - direction = (t0, t1) if isinstance(t0, GraphInterfaceSelf) else (t1, t0) - - d["direction"] = direction - - # Merged info - d["merged"] = [d] - - -def render_graph(G: nx.Graph, ax=None): - import matplotlib.pyplot as plt - - DIRECTIONAL = False - MULTI = True - assert not DIRECTIONAL or not MULTI - - if DIRECTIONAL: - G_ = nx.DiGraph() - G_.add_nodes_from(G) - for t0, t1, d in G.edges(data=True): - assert isinstance(t0, GraphInterface) - assert isinstance(t1, GraphInterface) - assert isinstance(d, dict) - - direction = d.get("direction") - if direction is None: - G_.add_edge(t0, t1, **d) - G_.add_edge(t1, t0, **d) - else: - assert t0 in direction and t1 in direction - G_.add_edge(*direction, **d) - - G = G_ - - if MULTI: - G_ = nx.MultiDiGraph() - G_.add_nodes_from(G) - for t0, t1, d in G.edges(data=True): - assert isinstance(t0, GraphInterface) - assert isinstance(t1, GraphInterface) - assert isinstance(d, dict) - - for d_ in d["merged"]: - G_.add_edge(t0, t1, **d_, root=d) - - G = G_ - - for t0, t1, d in G.edges(data=True): - assert isinstance(d, dict) - - merged: list[dict] = d["merged"] - - def params_for_link(link): - color = "#000000" - weight = 1 - - if isinstance(link, LinkSibling): - color = "#000000" # black - weight = 100 - elif isinstance(link, LinkNamedParent): - color = "#FF0000" # red - weight = 40 - elif isinstance(link, LinkDirect) and all( - isinstance(c.node, Electrical) for c in link.get_connections() - ): - color = "#00FF00" # green - weight = 1 - elif isinstance(link, LinkDirect) and all( - isinstance(c, GraphInterfaceModuleSibling) - for c in link.get_connections() - ): - color = "#AD139D" # purple-like - weight = 40 - elif isinstance(link, LinkDirect) and all( - isinstance(c, GraphInterfaceModuleConnection) - for c in link.get_connections() - ): - color = "#C1BE0F" # yellow-like - weight = 1 - else: - color = "#1BD0D3" # turqoise-like - weight = 10 - - return color, weight - - params = [params_for_link(m["link"]) for m in merged] - param = max(params, key=lambda x: x[1]) - - d["color"], d["weight"] = param - - # Draw - layout = nx.spring_layout(G.to_undirected(as_view=True)) - nx.draw_networkx_nodes(G, ax=ax, pos=layout, node_size=150) - # nx.draw_networkx_edges( - # G, - # ax=ax, - # pos=layout, - # # edgelist=G.edges, - # edge_color=[c for _, __, c in G.edges.data("color")] # type: ignore - # # edge_color=color_edges_by_type(G.edges(data=True)), - # ) - - def dir_to_arrow(t0, t1, data): - direction = d["root"].get("direction") - if direction is None: - return "-" - assert t0 in direction and t1 in direction - return "<-" if direction == (t0, t1) else "->" - - pos = layout - for t0, t1, k, d in G.edges(data=True, keys=True): - ax.annotate( - "", - xy=pos[t0], - xycoords="data", - xytext=pos[t1], - textcoords="data", - arrowprops=dict( - arrowstyle=dir_to_arrow(t0, t1, d), - color=d["color"], - shrinkA=5, - shrinkB=5, - patchA=None, - patchB=None, - connectionstyle=f"arc3,rad={0.1 * k}", - ), - ) - - # nx.draw_networkx_edges( - # G, pos=layout, edgelist=intra_comp_edges, edge_color="#0000FF" - # ) - - nodes: list[GraphInterface] = list(G.nodes) - vertex_names = { - vertex: f"{type(vertex.node).__name__}.{vertex.name}" - + ( - f"|{vertex.node.get_full_name()}" - if isinstance(vertex, GraphInterfaceSelf) and vertex.node is not None - else "" - ) - for vertex in nodes - } - nx.draw_networkx_labels(G, ax=ax, pos=layout, labels=vertex_names, font_size=10) - - # nx.draw_networkx_edge_labels( - # G, - # pos=layout, - # edge_labels=intra_edge_dict, - # font_size=10, - # rotate=False, - # bbox=dict(fc="blue"), - # font_color="white", - # ) - - return plt - - -def render_sidebyside(G: nx.Graph, nodes: list[Node] | None = None, depth=2): - tag_with_info(G) - - import matplotlib.pyplot as plt - - if nodes is not None: - G = sub_tree(G, nodes) - - # fig = plt.figure() - fig, axs = plt.subplots(1, depth + 1) - fig.subplots_adjust(0, 0, 1, 1) - # plt.subplot(111) - for i in range(depth + 1): - nG = node_graph(G, i) - render_graph(nG, ax=axs[i] if depth > 0 else axs) - - # render_graph(G, ax=axs[-1]) - return plt - - -def render_matrix( - G: nx.Graph, - nodes_rows: list[list[Node]], - depth=2, - min_depth=0, - show_full=True, - show_non_sum=True, -): - tag_with_info(G) - - import matplotlib.pyplot as plt - - _nodes_rows: list[list[Node] | None] = list(nodes_rows) - if show_full: - _nodes_rows.append(None) - - depths: list[int | None] = list(range(min_depth, depth + 1)) - if show_non_sum: - depths.append(None) - - row_cnt = len(_nodes_rows) - col_cnt = len(depths) - fig, _axs = plt.subplots(row_cnt, col_cnt) - - def axs(row, col): - if row_cnt > 1 and col_cnt > 1: - return _axs[row, col] - if col_cnt > 1: - return _axs[col] - if row_cnt > 1: - return _axs[row] - return _axs - - for j, nodes in enumerate(_nodes_rows): - Gn = G - if nodes is not None: - # Gn = sub_tree(G, nodes) - Gn = sub_graph(Gn, nodes) - - for i, level in enumerate(depths): - ax = axs(j, i) - - if level is not None: - nG = node_graph(Gn, level) - # print(Gn, "->", nG) - else: - nG = Gn - - render_graph(nG, ax=ax) - - # ax.set_title( - # f"row={j if nodes is not None else 'full'}" - # f"depth={i if level is not None else 'full'}", - # fontsize=8, - # ) - - plt.tight_layout() - fig.subplots_adjust(0, 0, 1, 1) - return plt diff --git a/src/faebryk/library/ANY.py b/src/faebryk/library/ANY.py index a563bf89..c8b7421d 100644 --- a/src/faebryk/library/ANY.py +++ b/src/faebryk/library/ANY.py @@ -1,23 +1,16 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from typing import Generic, TypeVar +from faebryk.core.parameter import Parameter -from faebryk.core.core import Parameter -PV = TypeVar("PV") - - -class ANY(Generic[PV], Parameter[PV]): +class ANY[PV](Parameter[PV]): """ Allow parameter to take any value. Operations with this parameter automatically resolve to ANY too. - Don't mistake with TBD. + Don't mistake with F.TBD. """ - def __init__(self) -> None: - super().__init__() - def __eq__(self, __value: object) -> bool: if isinstance(__value, ANY): return True diff --git a/src/faebryk/library/B4B_ZR_SM4_TF.py b/src/faebryk/library/B4B_ZR_SM4_TF.py index 58924a32..b44f6f52 100644 --- a/src/faebryk/library/B4B_ZR_SM4_TF.py +++ b/src/faebryk/library/B4B_ZR_SM4_TF.py @@ -1,46 +1,29 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.Electrical import Electrical -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_designator_prefix_defined import ( - has_designator_prefix_defined, -) -from faebryk.libs.util import times +import faebryk.library._F as F +import faebryk.libs.library.L as L +from faebryk.core.module import Module class B4B_ZR_SM4_TF(Module): - def __init__(self) -> None: - super().__init__() - - class _IFs(Module.IFS()): - pin = times(4, Electrical) - mount = times(2, Electrical) - - self.IFs = _IFs(self) - - x = self.IFs - self.add_trait( - can_attach_to_footprint_via_pinmap( - { - "1": x.pin[0], - "2": x.pin[1], - "3": x.pin[2], - "4": x.pin[3], - "5": x.mount[0], - "6": x.mount[1], - } - ) + pin = L.list_field(4, F.Electrical) + mount = L.list_field(2, F.Electrical) + + datasheet = L.f_field(F.has_datasheet_defined)( + "https://wmsc.lcsc.com/wmsc/upload/file/pdf/v2/lcsc/2304140030_BOOMELE-Boom-Precision-Elec-1-5-4P_C145997.pdf" + ) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("J") + + @L.rt_field + def can_attach_to_footprint(self): + return F.can_attach_to_footprint_via_pinmap( + { + "1": self.pin[0], + "2": self.pin[1], + "3": self.pin[2], + "4": self.pin[3], + "5": self.mount[0], + "6": self.mount[1], + } ) - - self.add_trait( - has_datasheet_defined( - "https://wmsc.lcsc.com/wmsc/upload/file/pdf/v2/lcsc/2304140030_BOOMELE-Boom-Precision-Elec-1-5-4P_C145997.pdf" - ) - ) - - self.add_trait(has_designator_prefix_defined("J")) diff --git a/src/faebryk/library/BH1750FVI_TR.py b/src/faebryk/library/BH1750FVI_TR.py index 91b5a14d..5f723613 100644 --- a/src/faebryk/library/BH1750FVI_TR.py +++ b/src/faebryk/library/BH1750FVI_TR.py @@ -2,52 +2,27 @@ # SPDX-License-Identifier: MIT import logging -from dataclasses import dataclass, field - -from faebryk.core.core import Module, Parameter -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.Capacitor import Capacitor -from faebryk.library.Constant import Constant -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_designator_prefix_defined import ( - has_designator_prefix_defined, -) -from faebryk.library.has_esphome_config import has_esphome_config -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.library.I2C import I2C -from faebryk.library.is_esphome_bus import is_esphome_bus -from faebryk.library.Range import Range -from faebryk.library.Resistor import Resistor -from faebryk.library.TBD import TBD -from faebryk.libs.units import P + +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L +from faebryk.libs.units import P, Quantity logger = logging.getLogger(__name__) class BH1750FVI_TR(Module): - @dataclass - class _bh1750_esphome_config(has_esphome_config.impl()): - update_interval_s: Parameter = field(default_factory=TBD) - - def __post_init__(self) -> None: - super().__init__() + class _bh1750_esphome_config(F.has_esphome_config.impl()): + update_interval: F.TBD[Quantity] def get_config(self) -> dict: - assert isinstance( - self.update_interval_s, Constant - ), "No update interval set!" + val = self.update_interval.get_most_narrow() + assert isinstance(val, F.Constant), "No update interval set!" - obj = self.get_obj() + obj = self.obj assert isinstance(obj, BH1750FVI_TR) - i2c = is_esphome_bus.find_connected_bus(obj.IFs.i2c) + i2c = F.is_esphome_bus.find_connected_bus(obj.i2c) return { "sensor": [ @@ -55,88 +30,74 @@ def get_config(self) -> dict: "platform": "bh1750", "name": "BH1750 Illuminance", "address": "0x23", - "i2c_id": i2c.get_trait(is_esphome_bus).get_bus_id(), - "update_interval": f"{self.update_interval_s.value}s", + "i2c_id": i2c.get_trait(F.is_esphome_bus).get_bus_id(), + "update_interval": f"{val.value.to('s')}", } ] } + dvi_capacitor: F.Capacitor + dvi_resistor: F.Resistor + + power: F.ElectricPower + addr: F.ElectricLogic + dvi: F.ElectricLogic + ep: F.ElectricLogic + i2c: F.I2C + def set_address(self, addr: int): raise NotImplementedError() + # TODO: Implement set_address # ADDR = ‘H’ ( ADDR ≧ 0.7VCC ) “1011100“ # ADDR = 'L' ( ADDR ≦ 0.3VCC ) “0100011“ ... - # assert addr < (1 << len(self.IFs.e)) + # assert addr < (1 << len(self.e)) - # for i, e in enumerate(self.IFs.e): + # for i, e in enumerate(self.e): # e.set(addr & (1 << i) != 0) - def __init__(self) -> None: - super().__init__() - - class _NODEs(Module.NODES()): - dvi_capacitor = Capacitor() - dvi_resistor = Resistor() - - self.NODEs = _NODEs(self) - - class _IFs(Module.IFS()): - power = ElectricPower() - addr = ElectricLogic() - dvi = ElectricLogic() - ep = ElectricLogic() - i2c = I2C() - - self.IFs = _IFs(self) - - class _PARAMs(Module.PARAMS()): ... + esphome_config: _bh1750_esphome_config - self.PARAMs = _PARAMs(self) + def __preinit__(self): + self.dvi_capacitor.capacitance.merge(1 * P.uF) + self.dvi_resistor.resistance.merge(1 * P.kohm) - self.NODEs.dvi_capacitor.PARAMs.capacitance.merge(1 * P.uF) - self.NODEs.dvi_resistor.PARAMs.resistance.merge(1 * P.kohm) + self.i2c.terminate() - self.IFs.i2c.terminate() - - self.IFs.i2c.PARAMs.frequency.merge( - I2C.define_max_frequency_capability(I2C.SpeedMode.fast_speed) - ) - - self.add_trait(has_designator_prefix_defined("U")) - - self.add_trait( - can_attach_to_footprint_via_pinmap( - { - "1": self.IFs.power.IFs.hv, - "2": self.IFs.addr.IFs.signal, - "3": self.IFs.power.IFs.lv, - "4": self.IFs.i2c.IFs.sda.IFs.signal, - "5": self.IFs.dvi.IFs.signal, - "6": self.IFs.i2c.IFs.scl.IFs.signal, - "7": self.IFs.ep.IFs.signal, - } - ) - ) - - self.add_trait( - has_datasheet_defined( - "https://datasheet.lcsc.com/lcsc/1811081611_ROHM-Semicon-BH1750FVI-TR_C78960.pdf" - ) + self.i2c.frequency.merge( + F.I2C.define_max_frequency_capability(F.I2C.SpeedMode.fast_speed) ) # set constraints - self.IFs.power.PARAMs.voltage.merge(Range(2.4 * P.V, 3.6 * P.V)) + self.power.voltage.merge(F.Range(2.4 * P.V, 3.6 * P.V)) + + self.power.decoupled.decouple().capacitance.merge(100 * P.nF) + # TODO: self.dvi.low_pass(self.dvi_capacitor, self.dvi_resistor) + self.dvi.signal.connect_via(self.dvi_capacitor, self.power.lv) + self.dvi.signal.connect_via(self.dvi_resistor, self.power.hv) - # internal connections - ref = ElectricLogic.connect_all_module_references(self) - self.add_trait(has_single_electric_reference_defined(ref)) - ref.connect(self.IFs.power) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) + ) - self.IFs.power.get_trait(can_be_decoupled).decouple().PARAMs.capacitance.merge( - 0.1 * P.uF + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") + + @L.rt_field + def attach_to_footprint(self): + return F.can_attach_to_footprint_via_pinmap( + { + "1": self.power.hv, + "2": self.addr.signal, + "3": self.power.lv, + "4": self.i2c.sda.signal, + "5": self.dvi.signal, + "6": self.i2c.scl.signal, + "7": self.ep.signal, + } ) - # TODO: self.IFs.dvi.low_pass(self.IF.dvi_capacitor, self.IF.dvi_resistor) - # self.IFs.i2c.add_trait(is_esphome_bus.impl()()) - self.esphome = self._bh1750_esphome_config() - self.add_trait(self.esphome) + datasheet = L.f_field(F.has_datasheet_defined)( + "https://datasheet.lcsc.com/lcsc/1811081611_ROHM-Semicon-BH1750FVI-TR_C78960.pdf" + ) diff --git a/src/faebryk/library/BJT.py b/src/faebryk/library/BJT.py index 4883cb16..cf1c603f 100644 --- a/src/faebryk/library/BJT.py +++ b/src/faebryk/library/BJT.py @@ -3,16 +3,11 @@ from enum import Enum, auto -from faebryk.core.core import Module, Parameter -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.Electrical import Electrical -from faebryk.library.has_designator_prefix_defined import ( - has_designator_prefix_defined, -) -from faebryk.library.has_pin_association_heuristic_lookup_table import ( - has_pin_association_heuristic_lookup_table, -) -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.core.node import rt_field +from faebryk.core.parameter import Parameter +from faebryk.libs.library import L class BJT(Module): @@ -27,38 +22,27 @@ class OperationRegion(Enum): SATURATION = auto() CUT_OFF = auto() - def __init__( - self, - doping_type: Parameter[DopingType], - operation_region: Parameter[OperationRegion], - ): - super().__init__() - - class _PARAMs(super().PARAMS()): - doping_type = TBD[self.DopingType]() - operation_region = TBD[self.OperationRegion]() - - self.PARAMs = _PARAMs(self) - self.PARAMs.doping_type.merge(doping_type) - self.PARAMs.operation_region.merge(operation_region) - - class _IFs(super().IFS()): - emitter = Electrical() - base = Electrical() - collector = Electrical() - - self.IFs = _IFs(self) - - self.add_trait(has_designator_prefix_defined("Q")) - self.add_trait(can_bridge_defined(self.IFs.collector, self.IFs.emitter)) - self.add_trait( - has_pin_association_heuristic_lookup_table( - mapping={ - self.IFs.emitter: ["E", "Emitter"], - self.IFs.base: ["B", "Base"], - self.IFs.collector: ["C", "Collector"], - }, - accept_prefix=False, - case_sensitive=False, - ) + doping_type: Parameter[DopingType] + operation_region: Parameter[OperationRegion] + + emitter: F.Electrical + base: F.Electrical + collector: F.Electrical + + designator_prefix = L.f_field(F.has_designator_prefix_defined)("Q") + + @rt_field + def can_bridge(self): + return F.can_bridge_defined(self.collector, self.emitter) + + @rt_field + def pin_association_heuristic(self): + return F.has_pin_association_heuristic_lookup_table( + mapping={ + self.emitter: ["E", "Emitter"], + self.base: ["B", "Base"], + self.collector: ["C", "Collector"], + }, + accept_prefix=False, + case_sensitive=False, ) diff --git a/src/faebryk/library/Battery.py b/src/faebryk/library/Battery.py index 39bdfc2b..21c1f2d0 100644 --- a/src/faebryk/library/Battery.py +++ b/src/faebryk/library/Battery.py @@ -2,29 +2,16 @@ # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module from faebryk.libs.units import Quantity class Battery(Module): - @classmethod - def PARAMS(cls): - class _PARAMs(super().PARAMS()): - voltage = TBD[Quantity]() - capacity = TBD[Quantity]() + voltage: F.TBD[Quantity] + capacity: F.TBD[Quantity] - return _PARAMs + power: F.ElectricPower - def __init__(self) -> None: - super().__init__() - - class _IFs(Module.IFS()): - power = ElectricPower() - - self.IFs = _IFs(self) - - self.PARAMs = self.PARAMS()(self) - - self.IFs.power.PARAMs.voltage.merge(self.PARAMs.voltage) + def __preinit__(self) -> None: + self.power.voltage.merge(self.voltage) diff --git a/src/faebryk/library/Button.py b/src/faebryk/library/Button.py index ddc17098..122451e9 100644 --- a/src/faebryk/library/Button.py +++ b/src/faebryk/library/Button.py @@ -3,32 +3,20 @@ import logging -from faebryk.core.core import Module -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.Electrical import Electrical -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.libs.util import times +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L +from faebryk.libs.units import Quantity logger = logging.getLogger(__name__) class Button(Module): - def __init__(self) -> None: - super().__init__() + unnamed = L.list_field(2, F.Electrical) + height: F.TBD[Quantity] - class _NODEs(Module.NODES()): ... + designator_prefix = L.f_field(F.has_designator_prefix_defined)("SW") - self.NODEs = _NODEs(self) - - class _IFs(Module.IFS()): - unnamed = times(2, Electrical) - - self.IFs = _IFs(self) - - class _PARAMs(Module.PARAMS()): ... - - self.PARAMs = _PARAMs(self) - - self.add_trait(has_designator_prefix_defined("S")) - - self.add_trait(can_bridge_defined(self.IFs.unnamed[0], self.IFs.unnamed[1])) + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(self.unnamed[0], self.unnamed[1]) diff --git a/src/faebryk/library/ButtonCell.py b/src/faebryk/library/ButtonCell.py index 3db9ac9a..4da5e251 100644 --- a/src/faebryk/library/ButtonCell.py +++ b/src/faebryk/library/ButtonCell.py @@ -4,15 +4,13 @@ from enum import IntEnum, StrEnum -from faebryk.core.core import Parameter -from faebryk.library.Battery import Battery -from faebryk.library.Constant import Constant -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.parameter import Parameter +from faebryk.libs.library import L from faebryk.libs.units import P -class ButtonCell(Battery): +class ButtonCell(F.Battery): class Material(StrEnum): Alkaline = "L" SilverOxide = "S" @@ -25,13 +23,13 @@ class Material(StrEnum): @property def voltage(self) -> Parameter: return { - self.Alkaline: Constant(1.5 * P.V), - self.SilverOxide: Constant(1.55 * P.V), - self.ZincAir: Constant(1.65 * P.V), - self.Lithium: Constant(3.0 * P.V), - self.Mercury: Constant(1.35 * P.V), - self.NickelCadmium: Constant(1.2 * P.V), - self.NickelMetalHydride: Constant(1.2 * P.V), + self.Alkaline: F.Constant(1.5 * P.V), + self.SilverOxide: F.Constant(1.55 * P.V), + self.ZincAir: F.Constant(1.65 * P.V), + self.Lithium: F.Constant(3.0 * P.V), + self.Mercury: F.Constant(1.35 * P.V), + self.NickelCadmium: F.Constant(1.2 * P.V), + self.NickelMetalHydride: F.Constant(1.2 * P.V), }[self] class Shape(StrEnum): @@ -55,18 +53,10 @@ class Size(IntEnum): N_2430 = 2430 N_2450 = 2450 - def __init__(self) -> None: - super().__init__() + material: F.TBD[Material] + shape: F.TBD[Shape] + size: F.TBD[Size] - class _PARAMs(Battery.PARAMS()): - material = TBD[self.Material]() - shape = TBD[self.Shape]() - size = TBD[self.Size]() + designator_prefix = L.f_field(F.has_designator_prefix_defined)("B") - self.PARAMs = _PARAMs(self) - - self.add_trait(has_designator_prefix_defined("B")) - - self.inherit() - - # TODO merge voltage with material voltage + # TODO merge voltage with material voltage diff --git a/src/faebryk/library/CBM9002A_56ILG.py b/src/faebryk/library/CBM9002A_56ILG.py index df9ab85c..df840126 100644 --- a/src/faebryk/library/CBM9002A_56ILG.py +++ b/src/faebryk/library/CBM9002A_56ILG.py @@ -1,19 +1,10 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.library.I2C import I2C -from faebryk.library.USB2_0 import USB2_0 -from faebryk.libs.util import times +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L class CBM9002A_56ILG(Module): @@ -23,63 +14,45 @@ class CBM9002A_56ILG(Module): Cypress Semicon CY7C68013A-56L Clone """ - def __init__(self): - super().__init__() - - # ---------------------------------------- - # modules, interfaces, parameters - # ---------------------------------------- - class _PARAMs(Module.PARAMS()): ... - - self.PARAMs = _PARAMs(self) - - class _IFS(Module.IFS()): - PA = times(8, ElectricLogic) - PB = times(8, ElectricLogic) - PD = times(8, ElectricLogic) - usb = USB2_0() - i2c = I2C() - - avcc = ElectricPower() - vcc = ElectricPower() - - rdy = times(2, ElectricLogic) - ctl = times(3, ElectricLogic) - reset = ElectricLogic() - wakeup = ElectricLogic() - - ifclk = ElectricLogic() - clkout = ElectricLogic() - xtalin = Electrical() - xtalout = Electrical() - - self.IFs = _IFS(self) - - # ---------------------------------------- - # traits - # ---------------------------------------- - self.add_trait(has_designator_prefix_defined("U")) - self.add_trait( - has_datasheet_defined( - "https://corebai.com/Data/corebai/upload/file/20240201/CBM9002A.pdf" - ) - ) - self.add_trait( - has_single_electric_reference_defined( - ElectricLogic.connect_all_module_references(self) + # ---------------------------------------- + # modules, interfaces, parameters + # ---------------------------------------- + PA = L.list_field(8, F.ElectricLogic) + PB = L.list_field(8, F.ElectricLogic) + PD = L.list_field(8, F.ElectricLogic) + usb: F.USB2_0 + i2c: F.I2C + + avcc: F.ElectricPower + vcc: F.ElectricPower + + rdy = L.list_field(2, F.ElectricLogic) + ctl = L.list_field(3, F.ElectricLogic) + reset: F.ElectricLogic + wakeup: F.ElectricLogic + + ifclk: F.ElectricLogic + clkout: F.ElectricLogic + xtalin: F.Electrical + xtalout: F.Electrical + + # ---------------------------------------- + # traits + # ---------------------------------------- + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") + datasheet = L.f_field(F.has_datasheet_defined)( + "https://corebai.com/Data/corebai/upload/file/20240201/CBM9002A.pdf" + ) + + # ---------------------------------------- + # connections + # ---------------------------------------- + def __preinit__(self): + self.avcc.decoupled.decouple() # TODO: decouple all pins + self.vcc.decoupled.decouple() # TODO: decouple all pins + + F.ElectricLogic.connect_all_node_references( + self.get_children(direct_only=True, types=ModuleInterface).difference( + {self.avcc} ) ) - - # ---------------------------------------- - # aliases - # ---------------------------------------- - - # ---------------------------------------- - # connections - # ---------------------------------------- - self.IFs.avcc.get_trait(can_be_decoupled).decouple() # TODO: decouple all pins - self.IFs.vcc.get_trait(can_be_decoupled).decouple() # TODO: decouple all pins - - # ---------------------------------------- - # Parameters - # ---------------------------------------- diff --git a/src/faebryk/library/CBM9002A_56ILG_Reference_Design.py b/src/faebryk/library/CBM9002A_56ILG_Reference_Design.py index 3a9deb15..eeabab69 100644 --- a/src/faebryk/library/CBM9002A_56ILG_Reference_Design.py +++ b/src/faebryk/library/CBM9002A_56ILG_Reference_Design.py @@ -1,19 +1,10 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.Capacitor import Capacitor -from faebryk.library.CBM9002A_56ILG import CBM9002A_56ILG -from faebryk.library.Constant import Constant -from faebryk.library.Crystal_Oscillator import Crystal_Oscillator -from faebryk.library.Diode import Diode -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.I2C import I2C -from faebryk.library.USB2_0 import USB2_0 +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import P -from faebryk.libs.util import times class CBM9002A_56ILG_Reference_Design(Module): @@ -21,101 +12,69 @@ class CBM9002A_56ILG_Reference_Design(Module): Minimal working example for the CBM9002A_56ILG """ - def __init__(self): - super().__init__() - - # ---------------------------------------- - # modules, interfaces, parameters - # ---------------------------------------- - class _NODEs(Module.NODES()): - mcu = CBM9002A_56ILG() - reset_diode = Diode() - reset_lowpass_cap = Capacitor() - oscillator = Crystal_Oscillator() - - self.NODEs = _NODEs(self) - - class _PARAMs(Module.PARAMS()): ... - - self.PARAMs = _PARAMs(self) - - class _IFS(Module.IFS()): - PA = times(8, ElectricLogic) - PB = times(8, ElectricLogic) - PD = times(8, ElectricLogic) - usb = USB2_0() - i2c = I2C() - - avcc = ElectricPower() - vcc = ElectricPower() - - rdy = times(2, ElectricLogic) - ctl = times(3, ElectricLogic) - reset = ElectricLogic() - wakeup = ElectricLogic() - - ifclk = ElectricLogic() - clkout = ElectricLogic() - xtalin = Electrical() - xtalout = Electrical() - - self.IFs = _IFS(self) - - # ---------------------------------------- - # traits - # ---------------------------------------- - - # ---------------------------------------- - # aliases - # ---------------------------------------- - gnd = self.IFs.vcc.IFs.lv - - # ---------------------------------------- - # connections - # ---------------------------------------- - # connect all mcu IFs to this module IFs - for i, interface in enumerate(self.IFs.PA): - interface.connect(self.NODEs.mcu.IFs.PA[i]) - for i, interface in enumerate(self.IFs.PB): - interface.connect(self.NODEs.mcu.IFs.PB[i]) - for i, interface in enumerate(self.IFs.PD): - interface.connect(self.NODEs.mcu.IFs.PD[i]) - self.IFs.usb.connect(self.NODEs.mcu.IFs.usb) - self.IFs.i2c.connect(self.NODEs.mcu.IFs.i2c) - self.IFs.avcc.connect(self.NODEs.mcu.IFs.avcc) - self.IFs.vcc.connect(self.NODEs.mcu.IFs.vcc) - for i, interface in enumerate(self.IFs.rdy): - interface.connect(self.NODEs.mcu.IFs.rdy[i]) - for i, interface in enumerate(self.IFs.ctl): - interface.connect(self.NODEs.mcu.IFs.ctl[i]) - self.IFs.reset.connect(self.NODEs.mcu.IFs.reset) - self.IFs.wakeup.connect(self.NODEs.mcu.IFs.wakeup) - self.IFs.ifclk.connect(self.NODEs.mcu.IFs.ifclk) - self.IFs.clkout.connect(self.NODEs.mcu.IFs.clkout) - self.IFs.xtalin.connect(self.NODEs.mcu.IFs.xtalin) - self.IFs.xtalout.connect(self.NODEs.mcu.IFs.xtalout) - - self.IFs.reset.IFs.signal.connect_via( - self.NODEs.reset_lowpass_cap, gnd + # ---------------------------------------- + # modules, interfaces, parameters + # ---------------------------------------- + mcu: F.CBM9002A_56ILG + reset_diode: F.Diode + reset_lowpass_cap: F.Capacitor + oscillator: F.Crystal_Oscillator + + PA = L.list_field(8, F.ElectricLogic) + PB = L.list_field(8, F.ElectricLogic) + PD = L.list_field(8, F.ElectricLogic) + usb: F.USB2_0 + i2c: F.I2C + + avcc: F.ElectricPower + vcc: F.ElectricPower + + rdy = L.list_field(2, F.ElectricLogic) + ctl = L.list_field(3, F.ElectricLogic) + reset: F.ElectricLogic + wakeup: F.ElectricLogic + + ifclk: F.ElectricLogic + clkout: F.ElectricLogic + xtalin: F.Electrical + xtalout: F.Electrical + + # ---------------------------------------- + # traits + # ---------------------------------------- + + # ---------------------------------------- + # connections + # ---------------------------------------- + def __preinit__(self): + gnd = self.vcc.lv + from faebryk.core.util import connect_module_mifs_by_name + + connect_module_mifs_by_name(self, self.mcu, allow_partial=True) + + self.reset.signal.connect_via( + self.reset_lowpass_cap, gnd ) # TODO: should come from a low pass for electric logic - self.IFs.reset.get_trait(ElectricLogic.can_be_pulled).pull(up=True) - self.IFs.reset.IFs.signal.connect_via( - self.NODEs.reset_diode, self.IFs.vcc.IFs.hv - ) + self.reset.pulled.pull(up=True) + self.reset.signal.connect_via(self.reset_diode, self.vcc.hv) # crystal oscillator - self.NODEs.oscillator.IFs.power.connect(self.IFs.vcc) - self.NODEs.oscillator.IFs.n.connect(self.IFs.xtalin) - self.NODEs.oscillator.IFs.p.connect(self.IFs.xtalout) + self.oscillator.power.connect(self.vcc) + self.oscillator.n.connect(self.xtalin) + self.oscillator.p.connect(self.xtalout) # ---------------------------------------- # Parameters # ---------------------------------------- - self.NODEs.reset_lowpass_cap.PARAMs.capacitance.merge(Constant(1 * P.uF)) + self.reset_lowpass_cap.capacitance.merge(F.Constant(1 * P.uF)) - self.NODEs.oscillator.NODEs.crystal.PARAMs.frequency.merge( - Constant(24 * P.Mhertz) - ) - self.NODEs.oscillator.NODEs.crystal.PARAMs.load_impedance.merge( - Constant(12 * P.pohm) + self.oscillator.crystal.frequency.merge(F.Constant(24 * P.Mhertz)) + self.oscillator.crystal.frequency_tolerance.merge( + F.Range.upper_bound(20 * P.ppm) ) + + # TODO: just set to a 1N4148 + self.reset_diode.forward_voltage.merge(715 * P.mV) + self.reset_diode.reverse_leakage_current.merge(1 * P.uA) + self.reset_diode.current.merge(300 * P.mA) + self.reset_diode.max_current.merge(1 * P.A) diff --git a/src/faebryk/library/CD4011.py b/src/faebryk/library/CD4011.py index 7d74c868..cc2b3353 100644 --- a/src/faebryk/library/CD4011.py +++ b/src/faebryk/library/CD4011.py @@ -1,18 +1,19 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.Constant import Constant -from faebryk.library.ElectricLogicGates import ElectricLogicGates -from faebryk.library.has_simple_value_representation_defined import ( - has_simple_value_representation_defined, -) -from faebryk.library.Logic74xx import Logic74xx +import faebryk.library._F as F +from faebryk.libs.library import L -class CD4011(Logic74xx): +class CD4011(F.Logic74xx): def __init__(self): super().__init__( - [lambda: ElectricLogicGates.NAND(input_cnt=Constant(2)) for _ in range(4)] + [ + lambda: F.ElectricLogicGates.NAND(input_cnt=F.Constant(2)) + for _ in range(4) + ] ) - self.add_trait(has_simple_value_representation_defined("cd4011")) + simple_value_representation = L.f_field(F.has_simple_value_representation_defined)( + "cd4011" + ) diff --git a/src/faebryk/library/CH340x.py b/src/faebryk/library/CH340x.py index 85b6c663..d159ae4a 100644 --- a/src/faebryk/library/CH340x.py +++ b/src/faebryk/library/CH340x.py @@ -3,53 +3,32 @@ import logging -from faebryk.core.core import Module -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.Range import Range -from faebryk.library.UART import UART -from faebryk.library.USB2_0 import USB2_0 +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import P logger = logging.getLogger(__name__) class CH340x(Module): - def __init__(self) -> None: - super().__init__() + usb: F.USB2_0 + uart: F.UART + tnow: F.Electrical + gpio_power: F.ElectricPower - class _NODEs(Module.NODES()): ... + designator = L.f_field(F.has_designator_prefix_defined)("U") + datasheet = L.f_field(F.has_datasheet_defined)( + "https://wch-ic.com/downloads/file/79.html" + ) - self.NODEs = _NODEs(self) + def __preinit__(self): + self.gpio_power.lv.connect(self.usb.usb_if.buspower.lv) - class _IFs(Module.IFS()): - usb = USB2_0() - uart = UART() - tnow = Electrical() - gpio_power = ElectricPower() + self.gpio_power.voltage.merge(F.Range(0 * P.V, 5.3 * P.V)) + self.gpio_power.decoupled.decouple() + self.usb.usb_if.buspower.voltage.merge(F.Range(4 * P.V, 5.3 * P.V)) - self.IFs = _IFs(self) + self.usb.usb_if.buspower.decoupled.decouple() - class _PARAMs(Module.PARAMS()): ... - - self.PARAMs = _PARAMs(self) - - self.IFs.gpio_power.IFs.lv.connect(self.IFs.usb.IFs.usb_if.IFs.buspower.IFs.lv) - - self.IFs.gpio_power.PARAMs.voltage.merge(Range(0 * P.V, 5.3 * P.V)) - self.IFs.gpio_power.get_trait(can_be_decoupled).decouple() - self.IFs.usb.IFs.usb_if.IFs.buspower.PARAMs.voltage.merge( - Range(4 * P.V, 5.3 * P.V) - ) - - self.add_trait(has_designator_prefix_defined("U")) - self.add_trait( - has_datasheet_defined("https://wch-ic.com/downloads/file/79.html") - ) - - self.IFs.usb.IFs.usb_if.IFs.buspower.get_trait(can_be_decoupled).decouple() - - self.IFs.gpio_power.IFs.lv.connect(self.IFs.usb.IFs.usb_if.IFs.buspower.IFs.lv) + self.gpio_power.lv.connect(self.usb.usb_if.buspower.lv) diff --git a/src/faebryk/library/Capacitor.py b/src/faebryk/library/Capacitor.py index 38c3e50a..3e4e6244 100644 --- a/src/faebryk/library/Capacitor.py +++ b/src/faebryk/library/Capacitor.py @@ -4,24 +4,10 @@ import logging from enum import IntEnum, auto -from faebryk.core.core import Module -from faebryk.core.util import ( - as_unit, - as_unit_with_tolerance, - enum_parameter_representation, -) -from faebryk.library.can_attach_to_footprint_symmetrically import ( - can_attach_to_footprint_symmetrically, -) -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.Electrical import Electrical -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.has_simple_value_representation_based_on_params import ( - has_simple_value_representation_based_on_params, -) -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import Quantity -from faebryk.libs.util import times logger = logging.getLogger(__name__) @@ -37,41 +23,41 @@ class TemperatureCoefficient(IntEnum): X8R = auto() C0G = auto() - def __init__(self): - super().__init__() + unnamed = L.list_field(2, F.Electrical) - class _IFs(Module.IFS()): - unnamed = times(2, Electrical) + capacitance: F.TBD[Quantity] + rated_voltage: F.TBD[Quantity] + temperature_coefficient: F.TBD[TemperatureCoefficient] - self.IFs = _IFs(self) + attach_to_footprint: F.can_attach_to_footprint_symmetrically + designator_prefix = L.f_field(F.has_designator_prefix_defined)("C") - class _PARAMs(Module.PARAMS()): - capacitance = TBD[Quantity]() - rated_voltage = TBD[Quantity]() - temperature_coefficient = TBD[Capacitor.TemperatureCoefficient]() + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(*self.unnamed) - self.PARAMs = _PARAMs(self) - - self.add_trait(can_bridge_defined(*self.IFs.unnamed)) + @L.rt_field + def simple_value_representation(self): + from faebryk.core.util import ( + as_unit, + as_unit_with_tolerance, + enum_parameter_representation, + ) - self.add_trait( - has_simple_value_representation_based_on_params( - ( - self.PARAMs.capacitance, - self.PARAMs.rated_voltage, - self.PARAMs.temperature_coefficient, - ), - lambda ps: " ".join( - filter( - None, - [ - as_unit_with_tolerance(ps[0], "F"), - as_unit(ps[1], "V"), - enum_parameter_representation(ps[2].get_most_narrow()), - ], - ) - ), - ) + return F.has_simple_value_representation_based_on_params( + ( + self.capacitance, + self.rated_voltage, + self.temperature_coefficient, + ), + lambda ps: " ".join( + filter( + None, + [ + as_unit_with_tolerance(ps[0], "F"), + as_unit(ps[1], "V"), + enum_parameter_representation(ps[2].get_most_narrow()), + ], + ) + ), ) - self.add_trait(can_attach_to_footprint_symmetrically()) - self.add_trait(has_designator_prefix_defined("C")) diff --git a/src/faebryk/library/Common_Mode_Filter.py b/src/faebryk/library/Common_Mode_Filter.py index 363102ea..5f60bbda 100644 --- a/src/faebryk/library/Common_Mode_Filter.py +++ b/src/faebryk/library/Common_Mode_Filter.py @@ -3,30 +3,15 @@ import logging -from faebryk.core.core import Module -from faebryk.library.Electrical import Electrical -from faebryk.library.has_designator_defined import has_designator_defined -from faebryk.libs.util import times +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L logger = logging.getLogger(__name__) class Common_Mode_Filter(Module): - def __init__(self) -> None: - super().__init__() + c_a = L.list_field(2, F.Electrical) + c_b = L.list_field(2, F.Electrical) - class _NODEs(Module.NODES()): ... - - self.NODEs = _NODEs(self) - - class _IFs(Module.IFS()): - c_a = times(2, Electrical) - c_b = times(2, Electrical) - - self.IFs = _IFs(self) - - class _PARAMs(Module.PARAMS()): ... - - self.PARAMs = _PARAMs(self) - - self.add_trait(has_designator_defined("FL")) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("FL") diff --git a/src/faebryk/library/Comparator.py b/src/faebryk/library/Comparator.py index 2dac01b1..e7c2905a 100644 --- a/src/faebryk/library/Comparator.py +++ b/src/faebryk/library/Comparator.py @@ -3,15 +3,9 @@ from enum import Enum, auto -from faebryk.core.core import Module -from faebryk.core.util import as_unit -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.has_simple_value_representation_based_on_params import ( - has_simple_value_representation_based_on_params, -) -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import Quantity @@ -21,40 +15,34 @@ class OutputType(Enum): PushPull = auto() OpenDrain = auto() - def __init__(self): - super().__init__() - - class _PARAMs(self.PARAMS()): - common_mode_rejection_ratio = TBD[Quantity]() - input_bias_current = TBD[Quantity]() - input_hysteresis_voltage = TBD[Quantity]() - input_offset_voltage = TBD[Quantity]() - propagation_delay = TBD[Quantity]() - output_type = TBD[Comparator.OutputType]() - - self.PARAMs = _PARAMs(self) - - class _IFs(super().IFS()): - power = ElectricPower() - inverting_input = Electrical() - non_inverting_input = Electrical() - output = Electrical() - - self.IFs = _IFs(self) - - self.add_trait( - has_simple_value_representation_based_on_params( - [ - self.PARAMs.common_mode_rejection_ratio, - self.PARAMs.input_bias_current, - self.PARAMs.input_hysteresis_voltage, - self.PARAMs.input_offset_voltage, - self.PARAMs.propagation_delay, - ], - lambda p: ( - f"{p[0]} CMRR, {as_unit(p[1], 'A')} Ib, {as_unit(p[2], 'V')} Vhys, " - f"{as_unit(p[3], 'V')} Vos, {as_unit(p[4], 's')} tpd" - ), - ) + common_mode_rejection_ratio: F.TBD[Quantity] + input_bias_current: F.TBD[Quantity] + input_hysteresis_voltage: F.TBD[Quantity] + input_offset_voltage: F.TBD[Quantity] + propagation_delay: F.TBD[Quantity] + output_type: F.TBD[OutputType] + + power: F.ElectricPower + inverting_input: F.Electrical + non_inverting_input: F.Electrical + output: F.Electrical + + @L.rt_field + def simple_value_representation(self): + from faebryk.core.util import as_unit + + return F.has_simple_value_representation_based_on_params( + [ + self.common_mode_rejection_ratio, + self.input_bias_current, + self.input_hysteresis_voltage, + self.input_offset_voltage, + self.propagation_delay, + ], + lambda p: ( + f"{p[0]} CMRR, {as_unit(p[1], 'A')} Ib, {as_unit(p[2], 'V')} Vhys, " + f"{as_unit(p[3], 'V')} Vos, {as_unit(p[4], 's')} tpd" + ), ) - self.add_trait(has_designator_prefix_defined("U")) + + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") diff --git a/src/faebryk/library/Constant.py b/src/faebryk/library/Constant.py index c6d18c45..ea6cbc27 100644 --- a/src/faebryk/library/Constant.py +++ b/src/faebryk/library/Constant.py @@ -3,10 +3,7 @@ from typing import Self, SupportsAbs -from faebryk.core.core import Parameter, _resolved -from faebryk.library.is_representable_by_single_value_defined import ( - is_representable_by_single_value_defined, -) +from faebryk.core.parameter import Parameter, _resolved from faebryk.libs.units import Quantity @@ -16,7 +13,6 @@ class Constant[PV](Parameter[PV], Parameter[PV].SupportsSetOps): def __init__(self, value: LIT_OR_PARAM) -> None: super().__init__() self.value = value - self.add_trait(is_representable_by_single_value_defined(self.value)) def _pretty_val(self): val = repr(self.value) diff --git a/src/faebryk/library/Crystal.py b/src/faebryk/library/Crystal.py index e4250f6b..07b66107 100644 --- a/src/faebryk/library/Crystal.py +++ b/src/faebryk/library/Crystal.py @@ -1,52 +1,35 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.Electrical import Electrical -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.Range import Range -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import Quantity -from faebryk.libs.util import times class Crystal(Module): - def __init__(self): - super().__init__() - - # ---------------------------------------- - # modules, interfaces, parameters - # ---------------------------------------- - class _PARAMs(Module.PARAMS()): - frequency = TBD[Quantity]() - frequency_tolerance = TBD[Range]() - frequency_temperature_tolerance = TBD[Range]() - frequency_ageing = TBD[Range]() - equivalent_series_resistance = TBD[Quantity]() - shunt_capacitance = TBD[Quantity]() - load_impedance = TBD[Quantity]() - - self.PARAMs = _PARAMs(self) - - class _IFs(Module.IFS()): - gnd = Electrical() - unnamed = times(2, Electrical) - - self.IFs = _IFs(self) - - # ---------------------------------------- - # parameters - # ---------------------------------------- - - # ---------------------------------------- - # traits - # ---------------------------------------- - self.add_trait(has_designator_prefix_defined("XTAL")) - - # ---------------------------------------- - # aliases - # ---------------------------------------- - - # ---------------------------------------- - # connections - # ---------------------------------------- + # ---------------------------------------- + # modules, interfaces, parameters + # ---------------------------------------- + gnd: F.Electrical + unnamed = L.list_field(2, F.Electrical) + + # ---------------------------------------- + # parameters + # ---------------------------------------- + frequency: F.TBD[Quantity] + frequency_tolerance: F.TBD[F.Range] + frequency_temperature_tolerance: F.TBD[F.Range] + frequency_ageing: F.TBD[F.Range] + equivalent_series_resistance: F.TBD[Quantity] + shunt_capacitance: F.TBD[Quantity] + load_capacitance: F.TBD[Quantity] + + # ---------------------------------------- + # traits + # ---------------------------------------- + designator = L.f_field(F.has_designator_prefix_defined)("XTAL") + + # ---------------------------------------- + # connections + # ---------------------------------------- diff --git a/src/faebryk/library/Crystal_Oscillator.py b/src/faebryk/library/Crystal_Oscillator.py index 4ea746a4..8571f20c 100644 --- a/src/faebryk/library/Crystal_Oscillator.py +++ b/src/faebryk/library/Crystal_Oscillator.py @@ -2,53 +2,39 @@ # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.Capacitor import Capacitor -from faebryk.library.Constant import Constant -from faebryk.library.Crystal import Crystal -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.Range import Range +from copy import copy + +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import P -from faebryk.libs.util import times class Crystal_Oscillator(Module): - def __init__(self): - super().__init__() - - # ---------------------------------------- - # modules, interfaces, parameters - # ---------------------------------------- - class _NODEs(Module.NODES()): - crystal = Crystal() - capacitors = times(2, Capacitor) - - self.NODEs = _NODEs(self) + # ---------------------------------------- + # modules, interfaces, parameters + # ---------------------------------------- + crystal: F.Crystal + capacitors = L.list_field(2, F.Capacitor) - class _PARAMs(Module.PARAMS()): ... + power: F.ElectricPower + p: F.Electrical + n: F.Electrical - self.PARAMs = _PARAMs(self) + # ---------------------------------------- + # parameters + # ---------------------------------------- + # https://blog.adafruit.com/2012/01/24/choosing-the-right-crystal-and-caps-for-your-design/ + # http://www.st.com/internet/com/TECHNICAL_RESOURCES/TECHNICAL_LITERATURE/APPLICATION_NOTE/CD00221665.pdf + _STRAY_CAPACITANCE = F.Range(1 * P.pF, 5 * P.pF) - class _IFs(Module.IFS()): - power = ElectricPower() - p = Electrical() - n = Electrical() + @L.rt_field + def capacitance(self): + return (self.crystal.load_capacitance - copy(self._STRAY_CAPACITANCE)) * 2 - self.IFs = _IFs(self) - - # ---------------------------------------- - # parameters - # ---------------------------------------- - # https://blog.adafruit.com/2012/01/24/choosing-the-right-crystal-and-caps-for-your-design/ - STRAY_CAPACITANCE = Range(1 * P.nF, 5 * P.nF) - load_capacitance = self.NODEs.crystal.PARAMs.load_impedance - capacitance = Constant(2 * P.dimesionless) * ( - load_capacitance - STRAY_CAPACITANCE - ) - - for cap in self.NODEs.capacitors: - cap.PARAMs.capacitance.merge(capacitance) + def __preinit__(self): + for cap in self.capacitors: + cap.capacitance.merge(self.capacitance) # ---------------------------------------- # traits @@ -57,14 +43,18 @@ class _IFs(Module.IFS()): # ---------------------------------------- # aliases # ---------------------------------------- - gnd = self.IFs.power.IFs.lv + gnd = self.power.lv # ---------------------------------------- # connections # ---------------------------------------- - self.NODEs.crystal.IFs.gnd.connect(gnd) - self.NODEs.crystal.IFs.unnamed[0].connect_via(self.NODEs.capacitors[0], gnd) - self.NODEs.crystal.IFs.unnamed[1].connect_via(self.NODEs.capacitors[1], gnd) + self.crystal.gnd.connect(gnd) + self.crystal.unnamed[0].connect_via(self.capacitors[0], gnd) + self.crystal.unnamed[1].connect_via(self.capacitors[1], gnd) + + self.crystal.unnamed[0].connect(self.n) + self.crystal.unnamed[1].connect(self.p) - self.NODEs.crystal.IFs.unnamed[0].connect(self.IFs.n) - self.NODEs.crystal.IFs.unnamed[1].connect(self.IFs.p) + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(self.p, self.n) diff --git a/src/faebryk/library/DIP.py b/src/faebryk/library/DIP.py index 88995041..e628d359 100644 --- a/src/faebryk/library/DIP.py +++ b/src/faebryk/library/DIP.py @@ -1,35 +1,37 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.can_attach_via_pinmap_equal import can_attach_via_pinmap_equal -from faebryk.library.Footprint import Footprint -from faebryk.library.has_equal_pins_in_ifs import has_equal_pins_in_ifs -from faebryk.library.Pad import Pad + +import faebryk.library._F as F +from faebryk.libs.library import L from faebryk.libs.units import P, Quantity from faebryk.libs.util import times -class DIP(Footprint): +class DIP(F.Footprint): def __init__(self, pin_cnt: int, spacing: Quantity, long_pads: bool) -> None: super().__init__() - class _IFs(Footprint.IFS()): - pins = times(pin_cnt, Pad) + self.spacing = spacing + self.long_pads = long_pads + self.pin_cnt = pin_cnt - self.IFs = _IFs(self) - from faebryk.library.has_kicad_footprint_equal_ifs import ( - has_kicad_footprint_equal_ifs, - ) + @L.rt_field + def pins(self): + return times(self.pin_cnt, F.Pad) - class _has_kicad_footprint(has_kicad_footprint_equal_ifs): + @L.rt_field + def kicad_footprint(self): + class _has_kicad_footprint(F.has_kicad_footprint_equal_ifs): @staticmethod def get_kicad_footprint() -> str: return "Package_DIP:DIP-{leads}_W{spacing:.2f}mm{longpads}".format( - leads=pin_cnt, - spacing=spacing.to(P.mm).m, - longpads="_LongPads" if long_pads else "", + leads=len(self.pins), + spacing=self.spacing.to(P.mm).m, + longpads="_LongPads" if self.long_pads else "", ) - self.add_trait(_has_kicad_footprint()) - self.add_trait(has_equal_pins_in_ifs()) - self.add_trait(can_attach_via_pinmap_equal()) + return _has_kicad_footprint() + + equal_pins_in_ifs: F.has_equal_pins_in_ifs + attach_via_pinmap: F.can_attach_via_pinmap_equal diff --git a/src/faebryk/library/DifferentialPair.py b/src/faebryk/library/DifferentialPair.py index 6796fc8e..82ccdb03 100644 --- a/src/faebryk/library/DifferentialPair.py +++ b/src/faebryk/library/DifferentialPair.py @@ -1,16 +1,10 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface -from faebryk.library.Electrical import Electrical +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface class DifferentialPair(ModuleInterface): - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - - class NODES(ModuleInterface.NODES()): - p = Electrical() - n = Electrical() - - self.IFs = NODES(self) + p: F.Electrical + n: F.Electrical diff --git a/src/faebryk/library/Diode.py b/src/faebryk/library/Diode.py index 0734a8d3..caa64319 100644 --- a/src/faebryk/library/Diode.py +++ b/src/faebryk/library/Diode.py @@ -1,64 +1,50 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module, Parameter -from faebryk.core.util import as_unit -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.Electrical import Electrical -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.has_pin_association_heuristic_lookup_table import ( - has_pin_association_heuristic_lookup_table, -) -from faebryk.library.has_simple_value_representation_based_on_param import ( - has_simple_value_representation_based_on_param, -) -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.core.parameter import Parameter +from faebryk.libs.library import L from faebryk.libs.units import Quantity class Diode(Module): - @classmethod - def PARAMS(cls): - class _PARAMs(super().PARAMS()): - forward_voltage = TBD[Quantity]() - max_current = TBD[Quantity]() - current = TBD[Quantity]() - reverse_working_voltage = TBD[Quantity]() - reverse_leakage_current = TBD[Quantity]() - - return _PARAMs - - def __init__(self): - super().__init__() - - self.PARAMs = self.PARAMS()(self) - - class _IFs(super().IFS()): - anode = Electrical() - cathode = Electrical() - - self.IFs = _IFs(self) - - self.add_trait(can_bridge_defined(self.IFs.anode, self.IFs.cathode)) - self.add_trait( - has_simple_value_representation_based_on_param( - self.PARAMs.forward_voltage, - lambda p: as_unit(p, "V"), - ) + forward_voltage: F.TBD[Quantity] + max_current: F.TBD[Quantity] + current: F.TBD[Quantity] + reverse_working_voltage: F.TBD[Quantity] + reverse_leakage_current: F.TBD[Quantity] + + anode: F.Electrical + cathode: F.Electrical + + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(self.anode, self.cathode) + + @L.rt_field + def simple_value_representation(self): + from faebryk.core.util import as_unit + + return F.has_simple_value_representation_based_on_param( + self.forward_voltage, + lambda p: as_unit(p, "V"), ) - self.add_trait(has_designator_prefix_defined("D")) - self.add_trait( - has_pin_association_heuristic_lookup_table( - mapping={ - self.IFs.anode: ["A", "Anode", "+"], - self.IFs.cathode: ["K", "C", "Cathode", "-"], - }, - accept_prefix=False, - case_sensitive=False, - ) + + designator_prefix = L.f_field(F.has_designator_prefix_defined)("D") + + @L.rt_field + def pin_association_heuristic(self): + return F.has_pin_association_heuristic_lookup_table( + mapping={ + self.anode: ["A", "Anode", "+"], + self.cathode: ["K", "C", "Cathode", "-"], + }, + accept_prefix=False, + case_sensitive=False, ) def get_needed_series_resistance_for_current_limit( self, input_voltage_V: Parameter[Quantity] ) -> Parameter[Quantity]: - return (input_voltage_V - self.PARAMs.forward_voltage) / self.PARAMs.current + return (input_voltage_V - self.forward_voltage) / self.current diff --git a/src/faebryk/library/EEPROM.py b/src/faebryk/library/EEPROM.py index 23fdedf8..a14f0e26 100644 --- a/src/faebryk/library/EEPROM.py +++ b/src/faebryk/library/EEPROM.py @@ -1,65 +1,52 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.library.I2C import I2C -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import Quantity -from faebryk.libs.util import times class EEPROM(Module): """ - Generic EEPROM module with I2C interface. + Generic EEPROM module with F.I2C interface. """ def set_address(self, addr: int): """ Configure the address of the EEPROM by setting the address pins. """ - assert addr < (1 << len(self.IFs.address)) + assert addr < (1 << len(self.address)) - for i, e in enumerate(self.IFs.address): + for i, e in enumerate(self.address): e.set(addr & (1 << i) != 0) - def __init__(self): - super().__init__() + # ---------------------------------------- + # modules, interfaces, parameters + # ---------------------------------------- - # ---------------------------------------- - # modules, interfaces, parameters - # ---------------------------------------- - class _PARAMs(Module.PARAMS()): - memory_size = TBD[Quantity]() + memory_size: F.TBD[Quantity] - self.PARAMs = _PARAMs(self) + power: F.ElectricPower + i2c: F.I2C + write_protect: F.ElectricLogic + address = L.list_field(3, F.ElectricLogic) - class _IFs(Module.IFS()): - power = ElectricPower() - i2c = I2C() - write_protect = ElectricLogic() - address = times(3, ElectricLogic) + # ---------------------------------------- + # traits + # ---------------------------------------- - self.IFs = _IFs(self) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") - # ---------------------------------------- - # traits - # ---------------------------------------- - self.add_trait(has_designator_prefix_defined("U")) - self.add_trait( - has_single_electric_reference_defined( - ElectricLogic.connect_all_module_references(self) - ) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) ) + def __preinit__(self): # ---------------------------------------- # connections # ---------------------------------------- - self.IFs.power.get_trait(can_be_decoupled).decouple() - self.IFs.i2c.terminate() + self.power.decoupled.decouple() + self.i2c.terminate() diff --git a/src/faebryk/library/ESP32.py b/src/faebryk/library/ESP32.py deleted file mode 100644 index d088ea8d..00000000 --- a/src/faebryk/library/ESP32.py +++ /dev/null @@ -1,442 +0,0 @@ -# This file is part of the faebryk project -# SPDX-License-Identifier: MIT - -import logging -import typing -from dataclasses import dataclass - -from faebryk.core.core import Module, ModuleInterface -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_defined_footprint import has_defined_footprint -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.has_simple_value_representation_defined import ( - has_simple_value_representation_defined, -) -from faebryk.library.I2C import I2C -from faebryk.library.JTAG import JTAG -from faebryk.library.QFN import QFN -from faebryk.library.UART_Base import UART_Base -from faebryk.libs.units import P -from faebryk.libs.util import times - -logger = logging.getLogger(__name__) - - -# TODO -class _ESP_ADC(ModuleInterface): - def __init__(self, channel_count: int) -> None: - super().__init__() - - class IFS(ModuleInterface.IFS()): - CHANNELS = times(channel_count, Electrical) - - self.IFs = IFS(self) - - -class _ESP_SDIO(ModuleInterface): - def __init__(self): - super().__init__() - - class IFS(ModuleInterface.IFS()): - DATA = times(4, Electrical) - CLK = Electrical() - CMD = Electrical() - GND = Electrical() - - self.IFs = IFS(self) - - -class _ESP32_EMAC(ModuleInterface): - def __init__(self): - super().__init__() - - class IFS(ModuleInterface.IFS()): - TXD = times(4, Electrical) - RXD = times(4, Electrical) - TX_CLK = Electrical() - RX_CLK = Electrical() - TX_EN = Electrical() - RX_ER = Electrical() - RX_DV = Electrical() - CLK_OUT = Electrical() - CLK_OUT_180 = Electrical() - TX_ER = Electrical() - MDC_out = Electrical() - MDI_in = Electrical() - MDO_out = Electrical() - CRS_out = Electrical() - COL_out = Electrical() - - self.IFs = IFS(self) - - -class _ESP32_SPI(ModuleInterface): - def __init__(self): - super().__init__() - - class IFS(ModuleInterface.IFS()): - D = Electrical() - Q = Electrical() - WP = Electrical() - HD = Electrical() - - CS = Electrical() - - CLK = Electrical() - GND = Electrical() - - self.IFs = IFS(self) - - -class ESP32(Module): - def __init__(self): - super().__init__() - - self.add_trait(has_simple_value_representation_defined("ESP32")) - self.add_trait(has_designator_prefix_defined("U")) - - class IFS(Module.IFS()): - # Analog - VDDA0 = Electrical() - LNA_IN = Electrical() - VDD3P30 = Electrical() - VDD3P31 = Electrical() - SENSOR_VP = Electrical() - # VDD3P3_RTC - SENSOR_CAPP = Electrical() - SENSOR_CAPN = Electrical() - SENSOR_VN = Electrical() - CHIP_PU = Electrical() - VDET_1 = Electrical() - VDET_2 = Electrical() - _32K_XP = Electrical() - _32K_XN = Electrical() - GPIO25 = Electrical() - GPIO26 = Electrical() - GPIO27 = Electrical() - MTMS = Electrical() - MTDI = Electrical() - VDD3P3_RTC = Electrical() - MTCK = Electrical() - MTDO = Electrical() - GPIO2 = Electrical() - GPIO0 = Electrical() - GPIO4 = Electrical() - # VDD_SDIO - GPIO16 = Electrical() - VDD_SDIO = Electrical() - GPIO17 = Electrical() - SD_DATA_2 = Electrical() - SD_DATA_3 = Electrical() - SD_CMD = Electrical() - SD_CLK = Electrical() - SD_DATA_0 = Electrical() - SD_DATA_1 = Electrical() - # VDD3P3_CPU - GPIO5 = Electrical() - GPIO18 = Electrical() - GPIO23 = Electrical() - VDD3P3_CPU = Electrical() - GPIO19 = Electrical() - GPIO22 = Electrical() - U0RXD = Electrical() - U0TXD = Electrical() - GPIO21 = Electrical() - # Analog - VDDA1 = Electrical() - XTAL_N = Electrical() - XTAL_P = Electrical() - VDDA2 = Electrical() - CAP2 = Electrical() - CAP1 = Electrical() - GND = Electrical() - - # High Level Functions - I2C = times(2, I2C) - SDIO_SLAVE = _ESP_SDIO() - SDIO_HOST = times(2, _ESP_SDIO) - UART = UART_Base() - JTAG = JTAG() - TOUCH = times(10, Electrical) - GPIO = times(40 - 6, Electrical) - RTC_GPIO = times(18, Electrical) - ADC = [ - None, - _ESP_ADC(channel_count=8), - _ESP_ADC(channel_count=10), - ] - SPI = times(4, _ESP32_SPI) - EMAC = _ESP32_EMAC() - - # Power - POWER_RTC = ElectricPower() - POWER_CPU = ElectricPower() - POWER_SDIO = ElectricPower() - POWER_ANALOG = ElectricPower() - - self.IFs = IFS(self) - - x = self.IFs - self.pinmap = { - # Analog - "1": x.VDDA0, - "2": x.LNA_IN, - "3": x.VDD3P30, - "4": x.SENSOR_VP, - # VDD"3"P3_RTC - "5": x.SENSOR_CAPP, - "6": x.SENSOR_CAPN, - "7": x.SENSOR_VN, - "8": x.CHIP_PU, - "9": x.VDET_1, - "10": x.VDET_2, - "11": x._32K_XP, - "12": x._32K_XN, - "13": x.GPIO25, - "14": x.GPIO26, - "15": x.GPIO27, - "16": x.MTMS, - "17": x.MTDI, - "18": x.VDD3P3_RTC, - "19": x.MTCK, - "20": x.MTDO, - "21": x.GPIO2, - "22": x.GPIO0, - "23": x.GPIO4, - # VDD_SDIO - "24": x.GPIO16, - "25": x.VDD_SDIO, - "26": x.GPIO17, - "27": x.SD_DATA_2, - "28": x.SD_DATA_3, - "29": x.SD_CMD, - "30": x.SD_CLK, - "31": x.SD_DATA_0, - "32": x.SD_DATA_1, - # VDD"3"P3_CPU - "33": x.GPIO5, - "34": x.GPIO18, - "35": x.GPIO23, - "36": x.VDD3P3_CPU, - "37": x.GPIO19, - "38": x.GPIO22, - "39": x.U0RXD, - "40": x.U0TXD, - "41": x.GPIO21, - # Analog - "42": x.VDDA1, - "43": x.XTAL_N, - "44": x.XTAL_P, - "45": x.VDDA2, - "46": x.CAP2, - "47": x.CAP1, - "48": x.GND, - } - - self.add_trait(can_attach_to_footprint_via_pinmap(self.pinmap)) - - # SPI0 is connected to SPI1 (Arbiter) - x.SPI[0].IFs.Q.connect(x.SPI[1].IFs.Q) - x.SPI[0].IFs.D.connect(x.SPI[1].IFs.D) - x.SPI[0].IFs.HD.connect(x.SPI[1].IFs.HD) - x.SPI[0].IFs.WP.connect(x.SPI[1].IFs.WP) - x.SPI[0].IFs.CLK.connect(x.SPI[1].IFs.CLK) - x.SPI[0].IFs.CS.connect(x.SPI[1].NODES.CS) - - x.POWER_RTC.IFs.hv.connect(x.VDD3P3_RTC) - x.POWER_RTC.IFs.lv.connect(x.GND) - x.POWER_CPU.IFs.hv.connect(x.VDD3P3_CPU) - x.POWER_CPU.IFs.lv.connect(x.GND) - x.POWER_SDIO.IFs.hv.connect(x.VDD_SDIO) - x.POWER_SDIO.IFs.lv.connect(x.GND) - x.POWER_ANALOG.IFs.hv.connect(x.VDDA0) - x.POWER_ANALOG.IFs.hv.connect(x.VDDA1) - x.POWER_ANALOG.IFs.hv.connect(x.VDDA2) - x.POWER_ANALOG.IFs.lv.connect(x.GND) - - self.pinmux = _ESP32_Pinmux(self) - - def get_gpio(self, idx: int): - filtered = [20, 24, 28, 29, 30, 31] - # count of elements in filtered that are smaller than idx: - offset = len([x for x in filtered if x < idx]) - return self.IFs.GPIO[idx - offset] - - -class _ESP32_D0WD(ESP32): - def __init__(self): - super().__init__() - - self.add_trait( - has_defined_footprint( - QFN( - pin_cnt=48, - size_xy=(5 * P.mm, 5 * P.mm), - pitch=0.350 * P.mm, - exposed_thermal_pad_cnt=1, - exposed_thermal_pad_dimensions=(3.700 * P.mm, 3.700 * P.mm), - has_thermal_vias=True, - ) - ) - ) - - -class _ESP32_D0WD_V3(_ESP32_D0WD): - # Dual core - No embedded flash/PSRAM - ... - - -class _ESP32_D0WDR2_V3(_ESP32_D0WD): - # Dual core - 2 MB PSRAM - ... - - -@dataclass(frozen=True) -class _Function: - interface: Electrical - name: str - type: "typing.Any" - - -@dataclass(frozen=False) -class _Pad: - no: int - name: str - interface: ModuleInterface - power_domain: "typing.Any" - # - at_reset: "typing.Any" - after_reset: "typing.Any" - drive_strenght: "typing.Any" - # - functions: dict[int, _Function] - # - current_function: _Function | None = None - - -class _Mux(Module): - def __init__(self, input: Electrical, *outputs: Electrical) -> None: - super().__init__() - - class _IFS(Module.IFS()): - IN = Electrical() - OUT = times(len(outputs), Electrical) - - self.IFs = _IFS(self) - - input.connect(self.IFs.IN) - self.map = dict(zip(outputs, self.IFs.OUT)) - for o1, o2 in self.map.items(): - o1.connect(o2) - - def select(self, output: Electrical): - self.IFs.IN.connect(self.map[output]) - - -def _matrix(esp32: ESP32): - x = esp32.IFs - - # fmt: off - return [ - # Power - _Pad(1, "VDDA", x.VDDA0, "VDDA supply in", None, None, None, {}), # noqa: E501 - _Pad(43, "VDDA", x.VDDA1, "VDDA supply in", None, None, None, {}), # noqa: E501 - _Pad(46, "VDDA", x.VDDA2, "VDDA supply in", None, None, None, {}), # noqa: E501 - _Pad(2, "LNA_IN", x.LNA_IN, "VDD3P3", None, None, None, {}), # noqa: E501 - _Pad(3, "VDD3P3", x.VDD3P30, "VDD3P3 supply in", None, None, None, {}), # noqa: E501 - _Pad(4, "VDD3P3", x.VDD3P31, "VDD3P3 supply in", None, None, None, {}), # noqa: E501 - _Pad(19, "VDD3P3_RTC", x.VDD3P3_RTC, "VDD3P3_RTC supply in", None, None, None, {}), # noqa: E501 - _Pad(26, "VDD_SDIO", x.VDD_SDIO, "VDD_SDIO supply out/in", None, None, None, {}), # noqa: E501 - _Pad(37, "VDD3P3_CPU", x.VDD3P3_CPU, "VDD3P3_CPU supply in", None, None, None, {}), # noqa: E501 - _Pad(5, "SENSOR_VP", x.SENSOR_VP, "VDD3P3_RTC", "oe=0,ie=0", "oe=0,ie=0", None, { # noqa: E501 - 1 : _Function(x.ADC[1].CHANNELS[0], "ADC1_CH0", None), # noqa: E501 - 3 : _Function(x.RTC_GPIO[0], "RTC_GPIO0", None), # noqa: E501 - 5 : _Function(esp32.get_gpio(36), "GPIO36", "I"), # noqa: E501 - }), # noqa: E501 - _Pad(6, "SENSOR_CAPP", x.SENSOR_CAPP, "VDD3P3_RTC", "oe=0,ie=0", "oe=0,ie=0", None, { # noqa: E501 - 1 : _Function(x.ADC[1].CHANNELS[1], "ADC1_CH1", None), # noqa: E501 - 3 : _Function(x.RTC_GPIO[1], "RTC_GPIO1", None), # noqa: E501 - 5 : _Function(esp32.get_gpio(37), "GPIO37", "I"), # noqa: E501 - }), # noqa: E501 - _Pad(7, "SENSOR_CAPN", x.SENSOR_CAPN, "VDD3P3_RTC", "oe=0,ie=0", "oe=0,ie=0", None, { # noqa: E501 - 1 : _Function(x.ADC[1].CHANNELS[2], "ADC1_CH2", None), # noqa: E501 - 3 : _Function(x.RTC_GPIO[2], "RTC_GPIO2", None), # noqa: E501 - 5 : _Function(esp32.get_gpio(38), "GPIO38", "I"), # noqa: E501 - }), # noqa: E501 - _Pad(18, "MTDI", x.MTDI, "VDD3P3_RTC", "oe=0,ie=1,wpd", "oe=0,ie=1,wpd", "2'd2", { # noqa: E501 - 1 : _Function(x.ADC[2].CHANNELS[5], "ADC2_CH5", None), # noqa: E501 - 2 : _Function(x.TOUCH[5], "TOUCH5", None), # noqa: E501 - 3 : _Function(x.RTC_GPIO[15], "RTC_GPIO15", None), # noqa: E501 - 5 : _Function(x.JTAG.IFs.tdi.IFs.signal, "MTDI", "I1"), # noqa: E501 - 6 : _Function(x.SPI[2].IFs.Q, "HSPIQ", "I/O/T"), # noqa: E501 - 7 : _Function(esp32.get_gpio(12), "GPIO12", "I/O/T"), # noqa: E501 - 8 : _Function(x.SDIO_HOST[1].IFs.DATA[2], "HS2_DATA2", "I1/O/T"), # noqa: E501 - 9 : _Function(x.SDIO_SLAVE.IFs.DATA[2], "SD_DATA2", "I1/O/T"), # noqa: E501 - 10: _Function(x.EMAC.IFs.TXD[3], "EMAC_TXD3", "O") # noqa: E501 - }), # noqa: E501 - ] - # fmt: on - - -class _ESP32_Pinmux(Module): - def __init__(self, esp32: ESP32) -> None: - default_function = 5 - self.matrix = _matrix(esp32) - - class _NODES(ModuleInterface.NODES()): - MUXES = [ - _Mux( - esp32.pinmap[str(pad.interface)], - *[f.interface for f in pad.functions.values()], - ) - for pad in self.matrix - ] - - self.NODEs = _NODES(self) - - for pad in self.matrix: - if len(pad.functions.items()) == 0: - continue - self._mux(pad.functions[default_function], pad) - - def _mux(self, function: _Function, pad: _Pad): - if pad.current_function == function: - return - - # Check if already set - # TODO remove, and make sure that reconnection is legal or spit warning or so - # assert (pad.current_function == None), "Already set" - - pad.current_function = function - self.NODEs.MUXES[self.matrix.index(pad)].select(function.interface) - - def mux(self, internal: ModuleInterface, pad: ModuleInterface): - # Check if combination legal - row = [pin for pin in self.matrix if pin.interface == pad][0] - col = [ - function - for function in row.functions.values() - if function.interface == internal - ][0] - - self._mux(col, row) - - def mux_if(self, internal: ModuleInterface): - for pad in self.matrix: - for function in pad.functions.values(): - if function.interface == internal: - self._mux(function, pad) - return - assert False, "Not a pinmux interface" - - def mux_peripheral(self, peripheral: ModuleInterface): - ... - # ifs = peripheral.get_trait(can_list_interfaces).get_interfaces() - # for interface in ifs: - # self.mux_if(interface) - # TODO - # is this not ambiguous? diff --git a/src/faebryk/library/ESP32_C3.py b/src/faebryk/library/ESP32_C3.py index a5da80a4..6f50f65c 100644 --- a/src/faebryk/library/ESP32_C3.py +++ b/src/faebryk/library/ESP32_C3.py @@ -3,20 +3,10 @@ import logging -from faebryk.core.core import Module -from faebryk.core.util import connect_to_all_interfaces -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.I2C import I2C -from faebryk.library.Range import Range -from faebryk.library.UART_Base import UART_Base -from faebryk.library.USB2_0 import USB2_0 +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import P -from faebryk.libs.util import times logger = logging.getLogger(__name__) @@ -24,221 +14,208 @@ class ESP32_C3(Module): """ESP32-C3""" - def __init__(self) -> None: - super().__init__() - - class _NODEs(Module.NODES()): ... - - self.NODEs = _NODEs(self) - - class _IFs(Module.IFS()): - vdd3p3_cpu = ElectricPower() - vdd3p3_rtc = ElectricPower() - vdd_spi = ElectricPower() - vdd3p3 = ElectricPower() - vdda = ElectricPower() - lna_in = Electrical() - enable = ElectricLogic() - xtal_p = Electrical() - xtal_n = Electrical() - gpio = times(22, ElectricLogic) - # TODO: map peripherals to GPIOs with pinmux - usb = USB2_0() - i2c = I2C() - uart = times(2, UART_Base) - # ... etc - - self.IFs = _IFs(self) - - x = self.IFs + vdd3p3_cpu: F.ElectricPower + vdd3p3_rtc: F.ElectricPower + vdd_spi: F.ElectricPower + vdd3p3: F.ElectricPower + vdda: F.ElectricPower + lna_in: F.Electrical + enable: F.ElectricLogic + xtal_p: F.Electrical + xtal_n: F.Electrical + gpio = L.list_field(22, F.ElectricLogic) + # TODO: map peripherals to GPIOs with pinmux + usb: F.USB2_0 + i2c: F.I2C + uart = L.list_field(2, F.UART_Base) + # ... etc + + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") + datasheet = L.f_field(F.has_datasheet_defined)( + "https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf" + ) + + def __preinit__(self): + from faebryk.core.util import connect_to_all_interfaces + + x = self # https://www.espressif.com/sites/default/files/documentation/esp32-c3_technical_reference_manual_en.pdf#uart for ser in x.uart: - ser.PARAMs.baud.merge(Range(0, 5000000)) + ser.baud.merge(F.Range(0 * P.baud, 5000000 * P.baud)) # connect all logic references # TODO: set correctly for each power domain - # ref = ElectricLogic.connect_all_module_references(self) - # self.add_trait(has_single_electric_reference_defined(ref)) + # ref = F.ElectricLogic.connect_all_module_references(self) + # self.add_trait(F.has_single_electric_reference_defined(ref)) # set power domain constraints to recommended operating conditions - for power_domain in [self.IFs.vdd3p3_rtc, self.IFs.vdd3p3, self.IFs.vdda]: - power_domain.PARAMs.voltage.merge(Range.from_center(3.3 * P.V, 0.3 * P.V)) - self.IFs.vdd3p3_cpu.PARAMs.voltage.merge( - Range(3.0 * P.V, 3.6 * P.V) + for power_domain in [self.vdd3p3_rtc, self.vdd3p3, self.vdda]: + power_domain.voltage.merge(F.Range.from_center(3.3 * P.V, 0.3 * P.V)) + self.vdd3p3_cpu.voltage.merge( + F.Range(3.0 * P.V, 3.6 * P.V) ) # TODO: max 3.3V when writing eFuses - self.IFs.vdd_spi.PARAMs.voltage.merge( - Range.from_center(3.3 * P.V, 0.3 * P.V) + self.vdd_spi.voltage.merge( + F.Range.from_center(3.3 * P.V, 0.3 * P.V) ) # TODO: when configured as input # connect all grounds to eachother and power connect_to_all_interfaces( - self.IFs.vdd3p3.IFs.lv, + self.vdd3p3.lv, [ - self.IFs.vdd3p3_cpu.IFs.lv, - self.IFs.vdd3p3_rtc.IFs.lv, - self.IFs.vdda.IFs.lv, - self.IFs.vdd_spi.IFs.lv, + self.vdd3p3_cpu.lv, + self.vdd3p3_rtc.lv, + self.vdda.lv, + self.vdd_spi.lv, ], ) # connect decoupling caps to power domains - self.IFs.vdd3p3.get_trait(can_be_decoupled).decouple() - self.IFs.vdd3p3_cpu.get_trait(can_be_decoupled).decouple() - self.IFs.vdd3p3_rtc.get_trait(can_be_decoupled).decouple() - self.IFs.vdda.get_trait(can_be_decoupled).decouple() - self.IFs.vdd_spi.get_trait(can_be_decoupled).decouple() + self.vdd3p3.decoupled.decouple() + self.vdd3p3_cpu.decoupled.decouple() + self.vdd3p3_rtc.decoupled.decouple() + self.vdda.decoupled.decouple() + self.vdd_spi.decoupled.decouple() # rc delay circuit on enable pin for startup delay # https://www.espressif.com/sites/default/files/documentation/esp32-c3-mini-1_datasheet_en.pdf page 24 # noqa E501 # TODO: add lowpass filter - # self.IFs.enable.IFs.signal.connect_via( - # self.NODEs.en_rc_capacitor, self.IFs.pwr3v3.IFs.lv + # self.enable.signal.connect_via( + # self.en_rc_capacitor, self.pwr3v3.lv # ) - self.IFs.enable.get_trait(ElectricLogic.can_be_pulled).pull( - up=True - ) # TODO: combine with lowpass filter - - # set default boot mode to "SPI Boot mode" (gpio = N.C. or HIGH) - # https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf page 25 # noqa E501 + self.enable.pulled.pull(up=True) # TODO: combine with lowpass filter + + # TODO: Fix this + # # set mux states + # # UART 1 + # self.set_mux(x.gpio[20], self.serial[1].rx) + # self.set_mux(x.gpio[21], self.serial[1].tx) + # # UART 0 + # self.set_mux(x.gpio[0], self.serial[0].rx) + # self.set_mux(x.gpio[1], self.serial[0].tx) + + # # F.I2C + # self.set_mux(x.gpio[4], self.i2c.scl) + # self.set_mux(x.gpio[5], self.i2c.sda) + + # class _uart_esphome_config(has_esphome_config.impl()): + # def get_config(self_) -> dict: + # assert isinstance(self, ESP32_C3) + # obj = self_.obj + # assert isinstance(obj, F.UART_Base) + # config = { + # "uart": [ + # { + # "id": obj.get_trait(is_esphome_bus).get_bus_id(), + # "baud_rate": get_parameter_max(obj.baud), + # } + # ] + # } + + # try: + # config["uart"][0]["rx_pin"] = self.get_mux_pin(obj.rx)[1] + # except IndexError: + # ... + + # try: + # config["uart"][0]["tx_pin"] = self.get_mux_pin(obj.tx)[1] + # except IndexError: + # ... + + # # if no rx/tx pin is set, then not in use + # if set(config["uart"][0].keys()).isdisjoint({"rx_pin", "tx_pin"}): + # return {} + + # return config + + # class _i2c_esphome_config(has_esphome_config.impl()): + # def get_config(self_) -> dict: + # assert isinstance(self, ESP32_C3) + # obj = self_.obj + # assert isinstance(obj, F.I2C) + + # try: + # sda = self.get_mux_pin(obj.sda)[1] + # scl = self.get_mux_pin(obj.scl)[1] + # except IndexError: + # # Not in use if pinmux is not set + # return {} + + # config = { + # "i2c": [ + # { + # "id": obj.get_trait(is_esphome_bus).get_bus_id(), + # "frequency": int(get_parameter_max(obj.frequency)), # noqa: E501 + # "sda": sda, + # "scl": scl, + # } + # ] + # } + + # return config + + # for serial in self.serial: + # serial.add_trait( + # is_esphome_bus_defined(f"uart_{self.serial.index(serial)}") + # ) + # serial.add_trait(_uart_esphome_config()) + + # for i, gpio in enumerate(self.gpio): + # gpio.add_trait(is_esphome_bus_defined(f"GPIO{i}")) + + # self.i2c.add_trait(is_esphome_bus_defined("i2c_0")) + # self.i2c.add_trait(_i2c_esphome_config()) + # self.i2c.frequency.merge( + # Set( + # [ + # F.I2C.define_max_frequency_capability(speed) + # for speed in [ + # F.I2C.SpeedMode.low_speed, + # F.I2C.SpeedMode.standard_speed, + # ] + # ] + # + [ + # F.Range(10 * P.khertz, 800 * P.khertz) + # ], # TODO: should be range 200k-800k, but breaks parameter merge + # ) + # ) + + # self.add_trait( + # has_esphome_config_defined( + # { + # "esp32": { + # "board": "Espressif ESP32-C3-DevKitM-1", + # "variant": "esp32c3", + # "framework": { + # "type": "esp-idf", + # "version": "recommended", + # }, + # }, + # } + # ) + # ) + + # very simple mux that uses pinmap + # def set_mux(self, gpio: F.ElectricLogic, target: F.ElectricLogic): + # """Careful not checked""" + # pin, _ = self.get_mux_pin(gpio) + # self.pinmap[pin] = target.signal + + # def get_mux_pin(self, target: F.ElectricLogic) -> tuple[str, int]: + # """Returns pin & gpio number""" + # pin = [k for k, v in self.pinmap.items() if v == target.signal][0] + # gpio = self.pinmap_default[pin] + # gpio_index = [ + # i for i, g in enumerate(self.gpio) if g.signal == gpio + # ][0] + # return pin, gpio_index + + def set_default_boot_mode(self, default_boot_to_spi_flash: bool = True): + # set default boot mode to "SPI Boot mode" + # https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf page 26 # noqa E501 # TODO: make configurable - self.IFs.gpio[8].get_trait(ElectricLogic.can_be_pulled).pull( - up=True - ) # boot_resistors[0] - self.IFs.gpio[2].get_trait(ElectricLogic.can_be_pulled).pull( - up=True - ) # boot_resistors[1] - # TODO: gpio[9] has an internal pull-up at boot = SPI-Boot - - self.add_trait(has_designator_prefix_defined("U")) - - self.add_trait( - has_datasheet_defined( - "https://www.espressif.com/sites/default/files/documentation/esp32-c3_datasheet_en.pdf" - ) - ) - - # TODO: Fix this - # # set mux states - # # UART 1 - # self.set_mux(x.gpio[20], self.IFs.serial[1].IFs.rx) - # self.set_mux(x.gpio[21], self.IFs.serial[1].IFs.tx) - # # UART 0 - # self.set_mux(x.gpio[0], self.IFs.serial[0].IFs.rx) - # self.set_mux(x.gpio[1], self.IFs.serial[0].IFs.tx) - - # # I2C - # self.set_mux(x.gpio[4], self.IFs.i2c.IFs.scl) - # self.set_mux(x.gpio[5], self.IFs.i2c.IFs.sda) - - # class _uart_esphome_config(has_esphome_config.impl()): - # def get_config(self_) -> dict: - # assert isinstance(self, ESP32_C3) - # obj = self_.get_obj() - # assert isinstance(obj, UART_Base) - # config = { - # "uart": [ - # { - # "id": obj.get_trait(is_esphome_bus).get_bus_id(), - # "baud_rate": get_parameter_max(obj.PARAMs.baud), - # } - # ] - # } - - # try: - # config["uart"][0]["rx_pin"] = self.get_mux_pin(obj.IFs.rx)[1] - # except IndexError: - # ... - - # try: - # config["uart"][0]["tx_pin"] = self.get_mux_pin(obj.IFs.tx)[1] - # except IndexError: - # ... - - # # if no rx/tx pin is set, then not in use - # if set(config["uart"][0].keys()).isdisjoint({"rx_pin", "tx_pin"}): - # return {} - - # return config - - # class _i2c_esphome_config(has_esphome_config.impl()): - # def get_config(self_) -> dict: - # assert isinstance(self, ESP32_C3) - # obj = self_.get_obj() - # assert isinstance(obj, I2C) - - # try: - # sda = self.get_mux_pin(obj.IFs.sda)[1] - # scl = self.get_mux_pin(obj.IFs.scl)[1] - # except IndexError: - # # Not in use if pinmux is not set - # return {} - - # config = { - # "i2c": [ - # { - # "id": obj.get_trait(is_esphome_bus).get_bus_id(), - # "frequency": int(get_parameter_max(obj.PARAMs.frequency)), # noqa: E501 - # "sda": sda, - # "scl": scl, - # } - # ] - # } - - # return config - - # for serial in self.IFs.serial: - # serial.add_trait( - # is_esphome_bus_defined(f"uart_{self.IFs.serial.index(serial)}") - # ) - # serial.add_trait(_uart_esphome_config()) - - # for i, gpio in enumerate(self.IFs.gpio): - # gpio.add_trait(is_esphome_bus_defined(f"GPIO{i}")) - - # self.IFs.i2c.add_trait(is_esphome_bus_defined("i2c_0")) - # self.IFs.i2c.add_trait(_i2c_esphome_config()) - # self.IFs.i2c.PARAMs.frequency.merge( - # Set( - # [ - # I2C.define_max_frequency_capability(speed) - # for speed in [ - # I2C.SpeedMode.low_speed, - # I2C.SpeedMode.standard_speed, - # ] - # ] - # + [ - # Range(10 * P.khertz, 800 * P.khertz) - # ], # TODO: should be range 200k-800k, but breaks parameter merge - # ) - # ) - - # self.add_trait( - # has_esphome_config_defined( - # { - # "esp32": { - # "board": "Espressif ESP32-C3-DevKitM-1", - # "variant": "esp32c3", - # "framework": { - # "type": "esp-idf", - # "version": "recommended", - # }, - # }, - # } - # ) - # ) - - # very simple mux that uses pinmap - # def set_mux(self, gpio: ElectricLogic, target: ElectricLogic): - # """Careful not checked""" - # pin, _ = self.get_mux_pin(gpio) - # self.pinmap[pin] = target.IFs.signal - - # def get_mux_pin(self, target: ElectricLogic) -> tuple[str, int]: - # """Returns pin & gpio number""" - # pin = [k for k, v in self.pinmap.items() if v == target.IFs.signal][0] - # gpio = self.pinmap_default[pin] - # gpio_index = [ - # i for i, g in enumerate(self.IFs.gpio) if g.IFs.signal == gpio - # ][0] - # return pin, gpio_index + self.gpio[8].pulled.pull(up=True).resistance.merge(10 * P.kohm) + self.gpio[2].pulled.pull(up=True).resistance.merge(10 * P.kohm) + # gpio[9] has an internal pull-up at boot = SPI-Boot + if not default_boot_to_spi_flash: + self.gpio[9].pulled.pull(up=False).resistance.merge(10 * P.kohm) diff --git a/src/faebryk/library/ESP32_C3_MINI_1.py b/src/faebryk/library/ESP32_C3_MINI_1.py index 09087520..07ab53aa 100644 --- a/src/faebryk/library/ESP32_C3_MINI_1.py +++ b/src/faebryk/library/ESP32_C3_MINI_1.py @@ -3,22 +3,9 @@ import logging -from faebryk.core.core import Module -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.ESP32_C3 import ESP32_C3 -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.library.UART_Base import UART_Base -from faebryk.libs.util import times +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L logger = logging.getLogger(__name__) @@ -26,72 +13,70 @@ class ESP32_C3_MINI_1(Module): """ESP32-C3-MINI-1 module""" - def __init__(self) -> None: - super().__init__() + esp32_c3: F.ESP32_C3 + # TODO: add components as described in the datasheet - class _NODEs(Module.NODES()): - esp32_c3 = ESP32_C3() - # TODO: add components as described in the datasheet + rf_output: F.Electrical + chip_enable: F.ElectricLogic + gpio = L.list_field( + 22, F.ElectricLogic + ) # TODO: Only GPIO 0 to 10 and 18, 19 are exposed + uart: F.UART_Base + vdd3v3: F.ElectricPower - self.NODEs = _NODEs(self) + # TODO: connect all components (nodes) - class _IFs(Module.IFS()): - rf_output = Electrical() - chip_enable = ElectricLogic() - gpio = times( - 22, ElectricLogic - ) # TODO: Only GPIO 0 to 10 and 18, 19 are exposed - uart = UART_Base() - vdd3v3 = ElectricPower() - - self.IFs = _IFs(self) - - # TODO: connect all components (nodes) - - # connect all logic references - ref = ElectricLogic.connect_all_module_references(self) - self.add_trait(has_single_electric_reference_defined(ref)) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) + ) + def __preinit__(self): # connect power decoupling caps - self.IFs.vdd3v3.get_trait(can_be_decoupled).decouple() + self.vdd3v3.decoupled.decouple() - for i, gpio in enumerate(self.IFs.gpio): - gpio.connect(self.NODEs.esp32_c3.IFs.gpio[i]) + for lhs, rhs in zip(self.gpio, self.esp32_c3.gpio): + lhs.connect(rhs) - gnd = self.IFs.vdd3v3.IFs.lv + # TODO: set the following in the pinmux + # UART0 gpio 20/21 + @L.rt_field + def attach_to_footprint(self): + gnd = self.vdd3v3.lv self.pinmap_default = { "1": gnd, "2": gnd, - "3": self.IFs.vdd3v3.IFs.hv, + "3": self.vdd3v3.hv, # 4 is not connected - "5": self.IFs.gpio[2].IFs.signal, - "6": self.IFs.gpio[3].IFs.signal, + "5": self.gpio[2].signal, + "6": self.gpio[3].signal, # 7 is not connected - "8": self.IFs.chip_enable.IFs.signal, + "8": self.chip_enable.signal, # 9 is not connected # 10 is not connected "11": gnd, - "12": self.IFs.gpio[0].IFs.signal, - "13": self.IFs.gpio[1].IFs.signal, + "12": self.gpio[0].signal, + "13": self.gpio[1].signal, "14": gnd, # 15 is not connected - "16": self.IFs.gpio[10].IFs.signal, + "16": self.gpio[10].signal, # 17 is not connected - "18": self.IFs.gpio[4].IFs.signal, - "19": self.IFs.gpio[5].IFs.signal, - "20": self.IFs.gpio[6].IFs.signal, - "21": self.IFs.gpio[7].IFs.signal, - "22": self.IFs.gpio[8].IFs.signal, - "23": self.IFs.gpio[9].IFs.signal, + "18": self.gpio[4].signal, + "19": self.gpio[5].signal, + "20": self.gpio[6].signal, + "21": self.gpio[7].signal, + "22": self.gpio[8].signal, + "23": self.gpio[9].signal, # 24 is not connected # 25 is not connected - "26": self.IFs.gpio[18].IFs.signal, - "27": self.IFs.gpio[19].IFs.signal, + "26": self.gpio[18].signal, + "27": self.gpio[19].signal, # 28 is not connected # 29 is not connected - "30": self.IFs.uart.IFs.rx.IFs.signal, - "31": self.IFs.uart.IFs.tx.IFs.signal, + "30": self.uart.rx.signal, + "31": self.uart.tx.signal, # 32 is not connected # 33 is not connected # 34 is not connected @@ -125,15 +110,9 @@ class _IFs(Module.IFS()): } self.pinmap = dict(self.pinmap_default) - self.add_trait(can_attach_to_footprint_via_pinmap(self.pinmap)) + return F.can_attach_to_footprint_via_pinmap(self.pinmap) - # TODO: set the following in the pinmux - # UART0 gpio 20/21 - - self.add_trait(has_designator_prefix_defined("U")) - - self.add_trait( - has_datasheet_defined( - "https://www.espressif.com/sites/default/files/russianDocumentation/esp32-c3-mini-1_datasheet_en.pdf" - ) - ) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") + datasheet = L.f_field(F.has_datasheet_defined)( + "https://www.espressif.com/sites/default/files/russianDocumentation/esp32-c3-mini-1_datasheet_en.pdf" + ) diff --git a/src/faebryk/library/ESP32_C3_MINI_1_Reference_Design.py b/src/faebryk/library/ESP32_C3_MINI_1_Reference_Design.py index 7de845c0..ea312996 100644 --- a/src/faebryk/library/ESP32_C3_MINI_1_Reference_Design.py +++ b/src/faebryk/library/ESP32_C3_MINI_1_Reference_Design.py @@ -3,14 +3,9 @@ import logging -from faebryk.core.core import Module -from faebryk.library.Button import Button -from faebryk.library.Crystal_Oscillator import Crystal_Oscillator -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.ESP32_C3_MINI_1 import ESP32_C3_MINI_1 -from faebryk.library.JTAG import JTAG -from faebryk.library.UART_Base import UART_Base -from faebryk.library.USB2_0 import USB2_0 +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.units import P logger = logging.getLogger(__name__) @@ -18,55 +13,53 @@ class ESP32_C3_MINI_1_Reference_Design(Module): """ESP32_C3_MINI_1 Module reference design""" - def __init__(self) -> None: - super().__init__() + esp32_c3_mini_1: F.ESP32_C3_MINI_1 + # TODO make switch debounced + boot_switch: F.Button # TODO: this cannot be picked Switch(F.Electrical) + reset_switch: F.Button # TODO: this cannot be picked Switch(F.Electrical) + low_speed_crystal_clock: F.Crystal_Oscillator - class _NODEs(Module.NODES()): - esp32_c3_mini_1 = ESP32_C3_MINI_1() - # TODO make switch debounced - boot_switch = Button() # TODO: this cannot be picked Switch(Electrical) - reset_switch = Button() # TODO: this cannot be picked Switch(Electrical) - low_speed_crystal_clock = Crystal_Oscillator() + vdd3v3: F.ElectricPower + uart: F.UART_Base + jtag: F.JTAG + usb: F.USB2_0 - self.NODEs = _NODEs(self) - - class _IFs(Module.IFS()): - vdd3v3 = ElectricPower() - uart = UART_Base() - jtag = JTAG() - usb = USB2_0() - - self.IFs = _IFs(self) - - gnd = self.IFs.vdd3v3.IFs.lv + def __preinit__(self): + gnd = self.vdd3v3.lv # connect power - self.IFs.vdd3v3.connect(self.NODEs.esp32_c3_mini_1.IFs.vdd3v3) + self.vdd3v3.connect(self.esp32_c3_mini_1.vdd3v3) # TODO: set default boot mode (GPIO[8] pull up with 10k resistor) + (GPIO[2] pull up with 10k resistor) # noqa: E501 + self.esp32_c3_mini_1.esp32_c3 # boot and enable switches # TODO: Fix bridging of (boot and reset) switches - # self.NODEs.esp32_c3_mini_1.IFs.chip_enable.connect_via( - # self.NODEs.boot_switch, gnd - # ) + self.esp32_c3_mini_1.chip_enable.signal.connect_via(self.boot_switch, gnd) # TODO: lowpass chip_enable - # self.IFs.gpio[9].connect_via(self.NODEs.reset_switch, gnd) + self.esp32_c3_mini_1.gpio[9].signal.connect_via(self.reset_switch, gnd) # connect low speed crystal oscillator - self.NODEs.low_speed_crystal_clock.IFs.n.connect( - self.NODEs.esp32_c3_mini_1.IFs.gpio[0].IFs.signal - ) - self.NODEs.low_speed_crystal_clock.IFs.p.connect( - self.NODEs.esp32_c3_mini_1.IFs.gpio[1].IFs.signal - ) - self.NODEs.low_speed_crystal_clock.IFs.power.IFs.lv.connect(gnd) + self.low_speed_crystal_clock.n.connect(self.esp32_c3_mini_1.gpio[0].signal) + self.low_speed_crystal_clock.p.connect(self.esp32_c3_mini_1.gpio[1].signal) + self.low_speed_crystal_clock.power.connect(self.vdd3v3) # TODO: set the following in the pinmux # jtag gpio 4,5,6,7 # USB gpio 18,19 # connect USB - self.IFs.usb.connect(self.NODEs.esp32_c3_mini_1.NODEs.esp32_c3.IFs.usb) + self.usb.connect(self.esp32_c3_mini_1.esp32_c3.usb) # connect UART[0] - self.IFs.uart.connect(self.NODEs.esp32_c3_mini_1.NODEs.esp32_c3.IFs.uart[0]) + self.uart.connect(self.esp32_c3_mini_1.esp32_c3.uart[0]) + + # default to SPI flash boot mode + self.esp32_c3_mini_1.esp32_c3.set_default_boot_mode() + + # ------------------------------------ + # parametrization + # ------------------------------------ + self.low_speed_crystal_clock.crystal.frequency.merge(32.768 * P.kHz) + self.low_speed_crystal_clock.crystal.frequency_tolerance.merge( + F.Range.lower_bound(20 * P.ppm) + ) diff --git a/src/faebryk/library/ElectricLogic.py b/src/faebryk/library/ElectricLogic.py index 10d716df..759e7a82 100644 --- a/src/faebryk/library/ElectricLogic.py +++ b/src/faebryk/library/ElectricLogic.py @@ -5,53 +5,41 @@ from enum import Enum, auto from typing import Iterable, Self -from faebryk.core.core import ( - Module, - ModuleInterface, - Node, - NodeTrait, -) -from faebryk.core.util import connect_all_interfaces -from faebryk.library.can_be_surge_protected_defined import ( - can_be_surge_protected_defined, -) -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_single_electric_reference import has_single_electric_reference -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.library.Logic import Logic -from faebryk.library.Resistor import Resistor -from faebryk.library.TBD import TBD - - -class ElectricLogic(Logic): - class has_pulls(NodeTrait): +import faebryk.library._F as F +from faebryk.core.graphinterface import GraphInterface +from faebryk.core.link import LinkFilteredException, _TLinkDirectShallow +from faebryk.core.module import Module +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.core.node import Node +from faebryk.libs.library import L + + +class ElectricLogic(F.Logic): + class has_pulls(F.Logic.TraitT): @abstractmethod - def get_pulls(self) -> tuple[Resistor | None, Resistor | None]: ... + def get_pulls(self) -> tuple[F.Resistor | None, F.Resistor | None]: ... class has_pulls_defined(has_pulls.impl()): - def __init__(self, up: Resistor | None, down: Resistor | None) -> None: + def __init__(self, up: F.Resistor | None, down: F.Resistor | None) -> None: super().__init__() self.up = up self.down = down - def get_pulls(self) -> tuple[Resistor | None, Resistor | None]: + def get_pulls(self) -> tuple[F.Resistor | None, F.Resistor | None]: return self.up, self.down - class can_be_pulled(NodeTrait): + class can_be_pulled(F.Logic.TraitT): @abstractmethod - def pull(self, up: bool) -> Resistor: ... + def pull(self, up: bool) -> F.Resistor: ... class can_be_pulled_defined(can_be_pulled.impl()): - def __init__(self, signal: Electrical, ref: ElectricPower) -> None: + def __init__(self, signal: F.Electrical, ref: F.ElectricPower) -> None: super().__init__() self.ref = ref self.signal = signal def pull(self, up: bool): - obj = self.get_obj() + obj = self.obj up_r, down_r = None, None if obj.has_trait(ElectricLogic.has_pulls): @@ -62,22 +50,26 @@ def pull(self, up: bool): if not up and down_r: return down_r - resistor = Resistor() + resistor = F.Resistor() if up: - obj.NODEs.pull_up = resistor + obj.add(resistor, "pull_up") up_r = resistor else: - obj.NODEs.pull_down = resistor + obj.add(resistor, "pull_down") down_r = resistor - self.signal.connect_via( - resistor, self.ref.IFs.hv if up else self.ref.IFs.lv - ) + self.signal.connect_via(resistor, self.ref.hv if up else self.ref.lv) obj.add_trait(ElectricLogic.has_pulls_defined(up_r, down_r)) return resistor - # class can_be_buffered(NodeTrait): + class LinkIsolatedReference(_TLinkDirectShallow): + def __init__(self, interfaces: list[GraphInterface]) -> None: + if any(isinstance(gif.node, F.ElectricPower) for gif in interfaces): + raise LinkFilteredException("All nodes are ElectricPower") + super().__init__(interfaces) + + # class can_be_buffered(Trait): # @abstractmethod # def buffer(self): # ... @@ -89,106 +81,100 @@ def pull(self, up: bool): # self.signal = signal # # def buffer(self): - # obj = self.get_obj() + # obj = self.obj # - # if hasattr(obj.NODEs, "buffer"): - # return cast_assert(SignalBuffer, getattr(obj.NODEs, "buffer")) + # if hasattr(obj, "buffer"): + # return cast_assert(SignalBuffer, getattr(obj, "buffer")) # # buffer = SignalBuffer() - # obj.NODEs.buffer = buffer - # self.signal.connect(buffer.NODEs.logic_in) + # obj.buffer = buffer + # self.signal.connect(buffer.logic_in) # - # return buffer.NODEs.logic_out + # return buffer.logic_out class PushPull(Enum): PUSH_PULL = auto() OPEN_DRAIN = auto() OPEN_SOURCE = auto() - def __init__(self) -> None: - super().__init__() - - class PARAMS(Logic.PARAMS()): - push_pull = TBD[ElectricLogic.PushPull]() - - self.PARAMs = PARAMS(self) + push_pull: F.TBD[PushPull] + reference: F.ElectricPower + signal: F.Electrical - class IFS(Logic.NODES()): - reference = ElectricPower() - signal = Electrical() + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined(self.reference) - self.IFs = IFS(self) - - class _can_be_surge_protected_defined(can_be_surge_protected_defined): + @L.rt_field + def surge_protected(self): + class _can_be_surge_protected_defined(F.can_be_surge_protected_defined): def protect(_self): return [ tvs.builder( - lambda t: t.PARAMs.reverse_working_voltage.merge( - self.IFs.reference.PARAMs.voltage + lambda t: t.reverse_working_voltage.merge( + self.reference.voltage ) ) for tvs in super().protect() ] - self.add_trait(has_single_electric_reference_defined(self.IFs.reference)) - self.add_trait( - _can_be_surge_protected_defined(self.IFs.reference.IFs.lv, self.IFs.signal) - ) - self.add_trait( - ElectricLogic.can_be_pulled_defined( - self.IFs.signal, - self.IFs.reference, - ) - ) + return _can_be_surge_protected_defined(self.reference.lv, self.signal) - def connect_to_electric(self, signal: Electrical, reference: ElectricPower): - self.IFs.reference.connect(reference) - self.IFs.signal.connect(signal) + @L.rt_field + def pulled(self): + return ElectricLogic.can_be_pulled_defined(self.signal, self.reference) + + def connect_to_electric(self, signal: F.Electrical, reference: F.ElectricPower): + self.reference.connect(reference) + self.signal.connect(signal) return self - def connect_reference(self, reference: ElectricPower, invert: bool = False): + def connect_reference(self, reference: F.ElectricPower, invert: bool = False): if invert: # TODO raise NotImplementedError() - # inverted = ElectricPower() - # inverted.NODEs.lv.connect(reference.NODEs.hv) - # inverted.NODEs.hv.connect(reference.NODEs.lv) + # inverted : F.ElectricPower + # inverted.lv.connect(reference.hv) + # inverted.hv.connect(reference.lv) # reference = inverted - self.IFs.reference.connect(reference) + self.reference.connect(reference) def connect_references(self, other: "ElectricLogic", invert: bool = False): - self.connect_reference(other.IFs.reference, invert=invert) + self.connect_reference(other.reference, invert=invert) def set(self, on: bool): super().set(on) - r = self.IFs.reference.IFs - self.IFs.signal.connect(r.hv if on else r.lv) + r = self.reference + self.signal.connect(r.hv if on else r.lv) def set_weak(self, on: bool): return self.get_trait(self.can_be_pulled).pull(up=on) @staticmethod - def connect_all_references(ifs: Iterable["ElectricLogic"]) -> ElectricPower: - out = connect_all_interfaces([x.IFs.reference for x in ifs]) + def connect_all_references(ifs: Iterable["ElectricLogic"]) -> F.ElectricPower: + from faebryk.core.util import connect_all_interfaces + + out = connect_all_interfaces([x.reference for x in ifs]) assert out return out @staticmethod def connect_all_node_references( nodes: Iterable[Node], gnd_only=False - ) -> ElectricPower: + ) -> F.ElectricPower: + from faebryk.core.util import connect_all_interfaces # TODO check if any child contains ElectricLogic which is not connected # e.g find them in graph and check if any has parent without "single reference" refs = { - x.get_trait(has_single_electric_reference).get_reference() + x.get_trait(F.has_single_electric_reference).get_reference() for x in nodes - if x.has_trait(has_single_electric_reference) - } | {x for x in nodes if isinstance(x, ElectricPower)} + if x.has_trait(F.has_single_electric_reference) + } | {x for x in nodes if isinstance(x, F.ElectricPower)} assert refs if gnd_only: - connect_all_interfaces({r.IFs.lv for r in refs}) + connect_all_interfaces({r.lv for r in refs}) return next(iter(refs)) connect_all_interfaces(refs) @@ -197,9 +183,9 @@ def connect_all_node_references( @classmethod def connect_all_module_references( cls, node: Module | ModuleInterface, gnd_only=False - ) -> ElectricPower: + ) -> F.ElectricPower: return cls.connect_all_node_references( - node.IFs.get_all() + node.NODEs.get_all(), + node.get_children(direct_only=True, types=(Module, ModuleInterface)), gnd_only=gnd_only, ) @@ -212,10 +198,10 @@ def connect_all_module_references( def connect_via_bridge( self, bridge: Module, up: bool, bridge_ref_to_signal: bool = False ): - target = self.IFs.reference.IFs.hv if up else self.IFs.reference.IFs.lv + target = self.reference.hv if up else self.reference.lv if bridge_ref_to_signal: - return target.connect_via(bridge, self.IFs.signal) - return self.IFs.signal.connect_via(bridge, target) + return target.connect_via(bridge, self.signal) + return self.signal.connect_via(bridge, target) def connect_shallow( self, @@ -229,10 +215,10 @@ def connect_shallow( # TODO make custom LinkDirectShallow that also allows the specified params if signal: - self.IFs.signal.connect(other.IFs.signal) + self.signal.connect(other.signal) if reference: - self.IFs.reference.connect(other.IFs.reference) + self.reference.connect(other.reference) if lv: - self.IFs.reference.IFs.lv.connect(other.IFs.reference.IFs.lv) + self.reference.lv.connect(other.reference.lv) return super().connect_shallow(other) diff --git a/src/faebryk/library/ElectricLogicGate.py b/src/faebryk/library/ElectricLogicGate.py index cb66ff46..8a46be26 100644 --- a/src/faebryk/library/ElectricLogicGate.py +++ b/src/faebryk/library/ElectricLogicGate.py @@ -1,43 +1,43 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from typing import TypeVar - -from faebryk.core.core import Module, TraitImpl -from faebryk.core.util import specialize_interface -from faebryk.library.Constant import Constant -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.library.Logic import Logic -from faebryk.library.LogicGate import LogicGate +import faebryk.library._F as F +from faebryk.core.trait import TraitImpl +from faebryk.libs.library import L from faebryk.libs.util import times -T = TypeVar("T", bound=Logic) - -class ElectricLogicGate(LogicGate): +class ElectricLogicGate(F.LogicGate): def __init__( - self, input_cnt: Constant[int], output_cnt: Constant[int], *functions: TraitImpl + self, + input_cnt: F.Constant[int], + output_cnt: F.Constant[int], + *functions: TraitImpl, ) -> None: + self.input_cnt = input_cnt + self.output_cnt = output_cnt super().__init__(input_cnt, output_cnt, *functions) - self.IFs_logic = self.IFs - - class IFS(Module.IFS()): - inputs = times(input_cnt, ElectricLogic) - outputs = times(output_cnt, ElectricLogic) + def __preinit__(self): + from faebryk.core.util import specialize_interface - self.IFs = IFS(self) + self_logic = self - for in_if_l, in_if_el in zip(self.IFs_logic.inputs, self.IFs.inputs): + for in_if_l, in_if_el in zip(self_logic.inputs, self.inputs): specialize_interface(in_if_l, in_if_el) - for out_if_l, out_if_el in zip(self.IFs_logic.outputs, self.IFs.outputs): + for out_if_l, out_if_el in zip(self_logic.outputs, self.outputs): specialize_interface(out_if_l, out_if_el) - self.add_trait( - has_single_electric_reference_defined( - ElectricLogic.connect_all_module_references(self) - ) + @L.rt_field + def inputs(self): + return times(self.input_cnt, F.ElectricLogic) + + @L.rt_field + def outputs(self): + return times(self.output_cnt, F.ElectricLogic) + + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) ) diff --git a/src/faebryk/library/ElectricLogicGates.py b/src/faebryk/library/ElectricLogicGates.py index 9e860fbd..dc3d78fe 100644 --- a/src/faebryk/library/ElectricLogicGates.py +++ b/src/faebryk/library/ElectricLogicGates.py @@ -1,29 +1,25 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from typing import TypeVar +import faebryk.library._F as F +from faebryk.core.core import Namespace -from faebryk.library.Constant import Constant -from faebryk.library.ElectricLogicGate import ElectricLogicGate -from faebryk.library.Logic import Logic -from faebryk.library.LogicGate import LogicGate -T = TypeVar("T", bound=Logic) +class ElectricLogicGates(Namespace): + class OR(F.ElectricLogicGate): + def __init__(self, input_cnt: F.Constant[int]): + super().__init__(input_cnt, F.Constant(1), F.LogicGate.can_logic_or_gate()) + class NOR(F.ElectricLogicGate): + def __init__(self, input_cnt: F.Constant[int]): + super().__init__(input_cnt, F.Constant(1), F.LogicGate.can_logic_nor_gate()) -class ElectricLogicGates: - class OR(ElectricLogicGate): - def __init__(self, input_cnt: Constant[int]): - super().__init__(input_cnt, Constant(1), LogicGate.can_logic_or_gate()) + class NAND(F.ElectricLogicGate): + def __init__(self, input_cnt: F.Constant[int]): + super().__init__( + input_cnt, F.Constant(1), F.LogicGate.can_logic_nand_gate() + ) - class NOR(ElectricLogicGate): - def __init__(self, input_cnt: Constant[int]): - super().__init__(input_cnt, Constant(1), LogicGate.can_logic_nor_gate()) - - class NAND(ElectricLogicGate): - def __init__(self, input_cnt: Constant[int]): - super().__init__(input_cnt, Constant(1), LogicGate.can_logic_nand_gate()) - - class XOR(ElectricLogicGate): - def __init__(self, input_cnt: Constant[int]): - super().__init__(input_cnt, Constant(1), LogicGate.can_logic_xor_gate()) + class XOR(F.ElectricLogicGate): + def __init__(self, input_cnt: F.Constant[int]): + super().__init__(input_cnt, F.Constant(1), F.LogicGate.can_logic_xor_gate()) diff --git a/src/faebryk/library/ElectricPower.py b/src/faebryk/library/ElectricPower.py index f2d91ffb..8cd5f6e9 100644 --- a/src/faebryk/library/ElectricPower.py +++ b/src/faebryk/library/ElectricPower.py @@ -2,83 +2,70 @@ # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface -from faebryk.library.can_be_decoupled_defined import can_be_decoupled_defined -from faebryk.library.can_be_surge_protected_defined import ( - can_be_surge_protected_defined, -) -from faebryk.library.Electrical import Electrical -from faebryk.library.Power import Power -from faebryk.library.Range import Range -from faebryk.library.TBD import TBD +import math + +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L from faebryk.libs.units import P, Quantity -class ElectricPower(Power): - class can_be_decoupled_power(can_be_decoupled_defined): +class ElectricPower(F.Power): + class can_be_decoupled_power(F.can_be_decoupled_defined): def __init__(self) -> None: ... def on_obj_set(self): - super().__init__(hv=self.get_obj().IFs.hv, lv=self.get_obj().IFs.lv) + obj = self.get_obj(ElectricPower) + super().__init__(hv=obj.hv, lv=obj.lv) def decouple(self): + obj = self.get_obj(ElectricPower) return ( super() .decouple() .builder( - lambda c: c.PARAMs.rated_voltage.merge( - Range(0 * P.V, self.get_obj().PARAMs.voltage * 2.0) + lambda c: c.rated_voltage.merge( + F.Range(obj.voltage * 2.0, math.inf * P.V) ) ) ) - class can_be_surge_protected_power(can_be_surge_protected_defined): + class can_be_surge_protected_power(F.can_be_surge_protected_defined): def __init__(self) -> None: ... def on_obj_set(self): - super().__init__(self.get_obj().IFs.lv, self.get_obj().IFs.hv) + obj = self.get_obj(ElectricPower) + super().__init__(obj.lv, obj.hv) def protect(self): + obj = self.get_obj(ElectricPower) return [ - tvs.builder( - lambda t: t.PARAMs.reverse_working_voltage.merge( - self.get_obj().PARAMs.voltage - ) - ) + tvs.builder(lambda t: t.reverse_working_voltage.merge(obj.voltage)) for tvs in super().protect() ] - def __init__(self) -> None: - super().__init__() - - class IFS(Power.IFS()): - hv = Electrical() - lv = Electrical() + hv: F.Electrical + lv: F.Electrical - self.IFs = IFS(self) + voltage: F.TBD[Quantity] - class PARAMS(Power.PARAMS()): - voltage = TBD[Quantity]() + surge_protected: can_be_surge_protected_power + decoupled: can_be_decoupled_power - self.PARAMs = PARAMS(self) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined(self) - # self.PARAMs.voltage.merge( - # self.NODEs.hv.PARAMs.potential - self.NODEs.lv.PARAMs.potential + def __preinit__(self) -> None: + ... + # self.voltage.merge( + # self.hv.potential - self.lv.potential # ) - self.add_trait(ElectricPower.can_be_surge_protected_power()) - self.add_trait(ElectricPower.can_be_decoupled_power()) - - from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, - ) - - self.add_trait(has_single_electric_reference_defined(self)) - def _on_connect(self, other: ModuleInterface) -> None: super()._on_connect(other) if not isinstance(other, ElectricPower): return - self.PARAMs.voltage.merge(other.PARAMs.voltage) + self.voltage.merge(other.voltage) diff --git a/src/faebryk/library/Electrical.py b/src/faebryk/library/Electrical.py index 7cc8d992..fb2046f0 100644 --- a/src/faebryk/library/Electrical.py +++ b/src/faebryk/library/Electrical.py @@ -1,16 +1,10 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface from faebryk.libs.units import Quantity class Electrical(ModuleInterface): - def __init__(self) -> None: - super().__init__() - - class PARAMS(ModuleInterface.PARAMS()): - potential = TBD[Quantity]() - - self.PARAMs = PARAMS(self) + potential: F.TBD[Quantity] diff --git a/src/faebryk/library/Ethernet.py b/src/faebryk/library/Ethernet.py index 0bd0ec17..165d9a6e 100644 --- a/src/faebryk/library/Ethernet.py +++ b/src/faebryk/library/Ethernet.py @@ -1,16 +1,10 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module, ModuleInterface -from faebryk.library.DifferentialPair import DifferentialPair +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface class Ethernet(ModuleInterface): - def __init__(self) -> None: - super().__init__() - - class IFS(Module.IFS()): - tx = DifferentialPair() - rx = DifferentialPair() - - self.IFs = IFS(self) + tx: F.DifferentialPair + rx: F.DifferentialPair diff --git a/src/faebryk/library/Fan.py b/src/faebryk/library/Fan.py index 5b24f7e5..9fdf6c01 100644 --- a/src/faebryk/library/Fan.py +++ b/src/faebryk/library/Fan.py @@ -1,19 +1,9 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.ElectricPower import ElectricPower +import faebryk.library._F as F +from faebryk.core.module import Module class Fan(Module): - def __init__(self) -> None: - super().__init__() - - class _IFs(Module.IFS()): - power = ElectricPower() - - self.IFs = _IFs(self) - - class _NODEs(Module.NODES()): ... - - self.NODEs = _NODEs(self) + power: F.ElectricPower diff --git a/src/faebryk/library/Footprint.py b/src/faebryk/library/Footprint.py index 45e14012..f53ba3f5 100644 --- a/src/faebryk/library/Footprint.py +++ b/src/faebryk/library/Footprint.py @@ -2,19 +2,21 @@ # SPDX-License-Identifier: MIT -from faebryk.core.core import Module, ModuleInterface, Node +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.core.node import Node +from faebryk.core.trait import Trait class Footprint(Module): - def __init__(self) -> None: - super().__init__() + class TraitT(Trait): ... @staticmethod def get_footprint_of_parent( intf: ModuleInterface, ) -> "tuple[Node, Footprint]": from faebryk.core.util import get_parent_with_trait - from faebryk.library.has_footprint import has_footprint - parent, trait = get_parent_with_trait(intf, has_footprint) + parent, trait = get_parent_with_trait(intf, F.has_footprint) return parent, trait.get_footprint() diff --git a/src/faebryk/library/FootprintTrait.py b/src/faebryk/library/FootprintTrait.py deleted file mode 100644 index 24016c51..00000000 --- a/src/faebryk/library/FootprintTrait.py +++ /dev/null @@ -1,17 +0,0 @@ -# This file is part of the faebryk project -# SPDX-License-Identifier: MIT - -from typing import Generic, TypeVar - -from faebryk.core.core import ( - _ModuleTrait, -) -from faebryk.library.Footprint import Footprint - -TF = TypeVar("TF", bound="Footprint") - - -class _FootprintTrait(Generic[TF], _ModuleTrait[TF]): ... - - -class FootprintTrait(_FootprintTrait["Footprint"]): ... diff --git a/src/faebryk/library/Fuse.py b/src/faebryk/library/Fuse.py index 04b5b789..27cbd851 100644 --- a/src/faebryk/library/Fuse.py +++ b/src/faebryk/library/Fuse.py @@ -4,18 +4,10 @@ import logging from enum import Enum, auto -from faebryk.core.core import Module -from faebryk.library.can_attach_to_footprint_symmetrically import ( - can_attach_to_footprint_symmetrically, -) -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.Electrical import Electrical -from faebryk.library.has_designator_prefix_defined import ( - has_designator_prefix_defined, -) -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import Quantity -from faebryk.libs.util import times logger = logging.getLogger(__name__) @@ -29,21 +21,15 @@ class ResponseType(Enum): SLOW = auto() FAST = auto() - def __init__(self): - super().__init__() + unnamed = L.list_field(2, F.Electrical) + fuse_type: F.TBD[FuseType] + response_type: F.TBD[ResponseType] + trip_current: F.TBD[Quantity] - class _IFs(Module.IFS()): - unnamed = times(2, Electrical) + attach_to_footprint: F.can_attach_to_footprint_symmetrically - self.IFs = _IFs(self) + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(self.unnamed[0], self.unnamed[1]) - class _PARAMs(Module.PARAMS()): - fuse_type = TBD[Fuse.FuseType]() - response_type = TBD[Fuse.ResponseType]() - trip_current = TBD[Quantity]() - - self.PARAMs = _PARAMs(self) - - self.add_trait(can_attach_to_footprint_symmetrically()) - self.add_trait(can_bridge_defined(self.IFs.unnamed[0], self.IFs.unnamed[1])) - self.add_trait(has_designator_prefix_defined("F")) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("F") diff --git a/src/faebryk/library/GDT.py b/src/faebryk/library/GDT.py index cfb7ba6e..249be751 100644 --- a/src/faebryk/library/GDT.py +++ b/src/faebryk/library/GDT.py @@ -3,37 +3,24 @@ import logging -from faebryk.core.core import Module -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.Electrical import Electrical -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import Quantity logger = logging.getLogger(__name__) class GDT(Module): - def __init__(self) -> None: - super().__init__() + common: F.Electrical + tube_1: F.Electrical + tube_2: F.Electrical - class _NODEs(Module.NODES()): ... + dc_breakdown_voltage: F.TBD[Quantity] + impulse_discharge_current: F.TBD[Quantity] - self.NODEs = _NODEs(self) + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(self.tube_1, self.tube_2) - class _IFs(Module.IFS()): - common = Electrical() - tube_1 = Electrical() - tube_2 = Electrical() - - self.IFs = _IFs(self) - - class _PARAMs(Module.PARAMS()): - dc_breakdown_voltage = TBD[Quantity]() - impulse_discharge_current = TBD[Quantity]() - - self.PARAMs = _PARAMs(self) - - self.add_trait(can_bridge_defined(self.IFs.tube_1, self.IFs.tube_2)) - - self.add_trait(has_designator_prefix_defined("GDT")) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("GDT") diff --git a/src/faebryk/library/GenericBusProtection.py b/src/faebryk/library/GenericBusProtection.py index 613a5f97..ba7865bb 100644 --- a/src/faebryk/library/GenericBusProtection.py +++ b/src/faebryk/library/GenericBusProtection.py @@ -1,84 +1,75 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from typing import Callable, Generic, TypeVar +from typing import Callable -from faebryk.core.core import ( - Module, - ModuleInterface, -) -from faebryk.library.can_be_surge_protected import can_be_surge_protected -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.Fuse import Fuse -from faebryk.libs.util import times +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L -T = TypeVar("T", bound=ModuleInterface) +class GenericBusProtection[T: ModuleInterface](Module): + @L.rt_field + def bus_unprotected(self): + return self.bus_factory() + + @L.rt_field + def bus_protected(self): + return self.bus_factory() -class GenericBusProtection(Generic[T], Module): def __init__(self, bus_factory: Callable[[], T]) -> None: super().__init__() + self.bus_factory = bus_factory - class _IFs(Module.IFS()): - bus_unprotected = bus_factory() - bus_protected = bus_factory() - - self.IFs = _IFs(self) - - U = TypeVar("U", bound=ModuleInterface) - - def get_mifs(bus: T, mif_type: type[U]) -> list[U]: - return [i for i in bus.IFs.get_all() if isinstance(i, mif_type)] + def __preinit__(self): + def get_mifs[U: ModuleInterface](bus: T, mif_type: type[U]) -> set[U]: + return bus.get_children(direct_only=True, types=mif_type) raw = list( zip( - get_mifs(self.IFs.bus_unprotected, Electrical), - get_mifs(self.IFs.bus_protected, Electrical), + get_mifs(self.bus_unprotected, F.Electrical), + get_mifs(self.bus_protected, F.Electrical), ) ) signals = list( zip( - get_mifs(self.IFs.bus_unprotected, ElectricLogic), - get_mifs(self.IFs.bus_protected, ElectricLogic), + get_mifs(self.bus_unprotected, F.ElectricLogic), + get_mifs(self.bus_protected, F.ElectricLogic), ) ) power = list( zip( - get_mifs(self.IFs.bus_unprotected, ElectricPower), - get_mifs(self.IFs.bus_protected, ElectricPower), + get_mifs(self.bus_unprotected, F.ElectricPower), + get_mifs(self.bus_protected, F.ElectricPower), ) ) - class _NODEs(Module.NODES()): - fuse = times(len(power), Fuse) - - self.NODEs = _NODEs(self) + fuse = L.list_field(len(power), F.Fuse) # Pass through except hv for power_unprotected, power_protected in power: - power_unprotected.IFs.lv.connect(power_protected.IFs.lv) + power_unprotected.lv.connect(power_protected.lv) for logic_unprotected, logic_protected in signals: logic_unprotected.connect_shallow(logic_protected, signal=True, lv=True) for raw_unprotected, raw_protected in raw: raw_unprotected.connect(raw_protected) # Fuse - for (power_unprotected, power_protected), fuse in zip(power, self.NODEs.fuse): - power_unprotected.IFs.hv.connect_via(fuse, power_protected.IFs.hv) + for (power_unprotected, power_protected), fuse in zip(power, fuse): + power_unprotected.hv.connect_via(fuse, power_protected.hv) # TODO maybe shallow connect? - power_protected.PARAMs.voltage.merge(power_unprotected.PARAMs.voltage) + power_protected.voltage.merge(power_unprotected.voltage) # TVS - if self.IFs.bus_protected.has_trait(can_be_surge_protected): - self.IFs.bus_protected.get_trait(can_be_surge_protected).protect() + if self.bus_protected.has_trait(F.can_be_surge_protected): + self.bus_protected.get_trait(F.can_be_surge_protected).protect() else: for line_unprotected, line_protected in signals + power + raw: - line_protected.get_trait(can_be_surge_protected).protect() + line_protected.get_trait(F.can_be_surge_protected).protect() # TODO add shallow connect - self.add_trait( - can_bridge_defined(self.IFs.bus_unprotected, self.IFs.bus_protected) - ) + + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(self.bus_unprotected, self.bus_protected) diff --git a/src/faebryk/library/HLK_LD2410B_P.py b/src/faebryk/library/HLK_LD2410B_P.py index 51ef13bc..7fb8c522 100644 --- a/src/faebryk/library/HLK_LD2410B_P.py +++ b/src/faebryk/library/HLK_LD2410B_P.py @@ -1,59 +1,40 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from dataclasses import dataclass, field - -from faebryk.core.core import Module, Parameter -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.Constant import Constant -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_designator_prefix_defined import ( - has_designator_prefix_defined, -) -from faebryk.library.has_esphome_config import has_esphome_config -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.library.is_esphome_bus import is_esphome_bus -from faebryk.library.TBD import TBD -from faebryk.library.UART_Base import UART_Base -from faebryk.libs.units import P +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L +from faebryk.libs.units import P, Quantity class HLK_LD2410B_P(Module): - @dataclass - class _ld2410b_esphome_config(has_esphome_config.impl()): - throttle_ms: Parameter = field(default_factory=TBD) - - def __post_init__(self) -> None: - super().__init__() + class _ld2410b_esphome_config(F.has_esphome_config.impl()): + throttle: F.TBD[Quantity] def get_config(self) -> dict: - assert isinstance(self.throttle_ms, Constant), "No update interval set!" + val = self.throttle.get_most_narrow() + assert isinstance(val, F.Constant), "No update interval set!" - obj = self.get_obj() + obj = self.obj assert isinstance(obj, HLK_LD2410B_P), "This is not an HLK_LD2410B_P!" uart_candidates = { mif - for mif in obj.IFs.uart.get_direct_connections() - if mif.has_trait(is_esphome_bus) and mif.has_trait(has_esphome_config) + for mif in obj.uart.get_direct_connections() + if mif.has_trait(F.is_esphome_bus) + and mif.has_trait(F.has_esphome_config) } assert len(uart_candidates) == 1, f"Expected 1 UART, got {uart_candidates}" uart = uart_candidates.pop() - uart_cfg = uart.get_trait(has_esphome_config).get_config()["uart"][0] + uart_cfg = uart.get_trait(F.has_esphome_config).get_config()["uart"][0] assert ( uart_cfg["baud_rate"] == 256000 ), f"Baudrate not 256000 but {uart_cfg['baud_rate']}" return { "ld2410": { - "throttle": f"{self.throttle_ms.value}ms", + "throttle": f"{val.value.to('ms')}", "uart_id": uart_cfg["id"], }, "binary_sensor": [ @@ -75,43 +56,38 @@ def get_config(self) -> dict: ], } - def __init__(self) -> None: - super().__init__() - - # interfaces - class _IFs(Module.IFS()): - power = ElectricPower() - uart = UART_Base() - out = ElectricLogic() - - self.IFs = _IFs(self) - - x = self.IFs - self.add_trait( - can_attach_to_footprint_via_pinmap( - { - "5": x.power.IFs.hv, - "4": x.power.IFs.lv, - "3": x.uart.IFs.rx.IFs.signal, - "2": x.uart.IFs.tx.IFs.signal, - "1": x.out.IFs.signal, - } - ) + # interfaces + power: F.ElectricPower + uart: F.UART_Base + out: F.ElectricLogic + + esphome_config: _ld2410b_esphome_config + + @L.rt_field + def attach_to_footprint(self): + x = self + return F.can_attach_to_footprint_via_pinmap( + { + "5": x.power.hv, + "4": x.power.lv, + "3": x.uart.rx.signal, + "2": x.uart.tx.signal, + "1": x.out.signal, + } ) - # connect all logic references - ref = ElectricLogic.connect_all_module_references(self, gnd_only=True) - self.add_trait(has_single_electric_reference_defined(ref)) - - self.add_trait(has_designator_prefix_defined("U")) + def __preinit__(self): + self.uart.baud.merge(F.Constant(256 * P.kbaud)) - self.esphome = self._ld2410b_esphome_config() - self.add_trait(self.esphome) - - self.add_trait( - has_datasheet_defined( - "https://datasheet.lcsc.com/lcsc/2209271801_HI-LINK-HLK-LD2410B-P_C5183132.pdf" - ) + # connect all logic references + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self, gnd_only=True) ) - self.IFs.uart.PARAMs.baud.merge(Constant(256 * P.kbaud)) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") + + datasheet = L.f_field(F.has_datasheet_defined)( + "https://datasheet.lcsc.com/lcsc/2209271801_HI-LINK-HLK-LD2410B-P_C5183132.pdf" + ) diff --git a/src/faebryk/library/Header.py b/src/faebryk/library/Header.py index f5029053..12e4f4ce 100644 --- a/src/faebryk/library/Header.py +++ b/src/faebryk/library/Header.py @@ -3,11 +3,9 @@ from enum import Enum, auto -from faebryk.core.core import Module -from faebryk.library.Constant import Constant -from faebryk.library.Electrical import Electrical -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import Quantity from faebryk.libs.util import times @@ -32,24 +30,23 @@ def __init__( vertical_pin_count: int, ) -> None: super().__init__() + self.horizontal_pin_count = horizonal_pin_count + self.vertical_pin_count = vertical_pin_count - class _NODEs(Module.NODES()): ... + def __preinit__(self): + self.pin_count_horizonal.merge(self.horizontal_pin_count) + self.pin_count_vertical.merge(self.vertical_pin_count) - self.NODEs = _NODEs(self) + pin_pitch: F.TBD[Quantity] + pin_type: F.TBD[PinType] + pad_type: F.TBD[PadType] + angle: F.TBD[Angle] - class _IFs(Module.IFS()): - unnamed = times(horizonal_pin_count * vertical_pin_count, Electrical) + pin_count_horizonal: F.TBD[int] + pin_count_vertical: F.TBD[int] - self.IFs = _IFs(self) + @L.rt_field + def unnamed(self): + return times(self.horizonal_pin_count * self.vertical_pin_count, F.Electrical) - class _PARAMs(Module.PARAMS()): - pin_pitch = TBD[Quantity]() - pin_type = TBD[self.PinType]() - pad_type = TBD[self.PadType]() - angle = TBD[self.Angle]() - pin_count_horizonal = Constant(horizonal_pin_count) - pin_count_vertical = Constant(vertical_pin_count) - - self.PARAMs = _PARAMs(self) - - self.add_trait(has_designator_prefix_defined("J")) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("J") diff --git a/src/faebryk/library/I2C.py b/src/faebryk/library/I2C.py index a4bfa54a..7db966d9 100644 --- a/src/faebryk/library/I2C.py +++ b/src/faebryk/library/I2C.py @@ -3,46 +3,36 @@ import logging from enum import Enum -from faebryk.core.core import ModuleInterface -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.library.Range import Range -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L from faebryk.libs.units import P, Quantity logger = logging.getLogger(__name__) class I2C(ModuleInterface): - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) + scl: F.ElectricLogic + sda: F.ElectricLogic - class IFS(ModuleInterface.IFS()): - scl = ElectricLogic() - sda = ElectricLogic() + frequency: F.TBD[Quantity] - self.IFs = IFS(self) - - class PARAMS(ModuleInterface.PARAMS()): - frequency = TBD[Quantity]() - - self.PARAMs = PARAMS(self) - - ref = ElectricLogic.connect_all_module_references(self) - self.add_trait(has_single_electric_reference_defined(ref)) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) + ) def terminate(self): # TODO: https://www.ti.com/lit/an/slva689/slva689.pdf - self.IFs.sda.get_trait(ElectricLogic.can_be_pulled).pull(up=True) - self.IFs.scl.get_trait(ElectricLogic.can_be_pulled).pull(up=True) + self.sda.pulled.pull(up=True) + self.scl.pulled.pull(up=True) def _on_connect(self, other: "I2C"): super()._on_connect(other) - self.PARAMs.frequency.merge(other.PARAMs.frequency) + self.frequency.merge(other.frequency) class SpeedMode(Enum): low_speed = 10 * P.khertz @@ -52,4 +42,4 @@ class SpeedMode(Enum): @staticmethod def define_max_frequency_capability(mode: SpeedMode): - return Range(I2C.SpeedMode.low_speed, mode) + return F.Range(I2C.SpeedMode.low_speed, mode) diff --git a/src/faebryk/library/Inductor.py b/src/faebryk/library/Inductor.py index df13a4ab..727b4ac4 100644 --- a/src/faebryk/library/Inductor.py +++ b/src/faebryk/library/Inductor.py @@ -2,65 +2,51 @@ # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.core.util import ( - as_unit, - as_unit_with_tolerance, -) -from faebryk.library.can_attach_to_footprint_symmetrically import ( - can_attach_to_footprint_symmetrically, -) -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.Electrical import Electrical -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.has_simple_value_representation_based_on_params import ( - has_simple_value_representation_based_on_params, -) -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import Quantity -from faebryk.libs.util import times class Inductor(Module): - def __init__( - self, - ): - super().__init__() + unnamed = L.list_field(2, F.Electrical) - class _IFs(super().IFS()): - unnamed = times(2, Electrical) + inductance: F.TBD[Quantity] + self_resonant_frequency: F.TBD[Quantity] + rated_current: F.TBD[Quantity] + dc_resistance: F.TBD[Quantity] - self.IFs = _IFs(self) - self.add_trait(can_bridge_defined(*self.IFs.unnamed)) + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(*self.unnamed) - class _PARAMs(super().PARAMS()): - inductance = TBD[Quantity]() - self_resonant_frequency = TBD[Quantity]() - rated_current = TBD[Quantity]() - dc_resistance = TBD[Quantity]() + attach_to_footprint: F.can_attach_to_footprint_symmetrically - self.PARAMs = _PARAMs(self) + @L.rt_field + def simple_value_representation(self): + from faebryk.core.util import ( + as_unit, + as_unit_with_tolerance, + ) - self.add_trait(can_attach_to_footprint_symmetrically()) - self.add_trait( - has_simple_value_representation_based_on_params( - ( - self.PARAMs.inductance, - self.PARAMs.self_resonant_frequency, - self.PARAMs.rated_current, - self.PARAMs.dc_resistance, - ), - lambda ps: " ".join( - filter( - None, - [ - as_unit_with_tolerance(ps[0], "H"), - as_unit(ps[1], "Hz"), - as_unit(ps[2], "A"), - as_unit(ps[3], "Ω"), - ], - ) - ), - ) + return F.has_simple_value_representation_based_on_params( + ( + self.inductance, + self.self_resonant_frequency, + self.rated_current, + self.dc_resistance, + ), + lambda ps: " ".join( + filter( + None, + [ + as_unit_with_tolerance(ps[0], "H"), + as_unit(ps[1], "Hz"), + as_unit(ps[2], "A"), + as_unit(ps[3], "Ω"), + ], + ) + ), ) - self.add_trait(has_designator_prefix_defined("L")) + + designator_prefix = L.f_field(F.has_designator_prefix_defined)("L") diff --git a/src/faebryk/library/JTAG.py b/src/faebryk/library/JTAG.py index 041e8f57..566f74c4 100644 --- a/src/faebryk/library/JTAG.py +++ b/src/faebryk/library/JTAG.py @@ -1,29 +1,23 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L class JTAG(ModuleInterface): - def __init__(self) -> None: - super().__init__() + dbgrq: F.ElectricLogic + tdo: F.ElectricLogic + tdi: F.ElectricLogic + tms: F.ElectricLogic + tck: F.ElectricLogic + n_trst: F.ElectricLogic + n_reset: F.ElectricLogic + vtref: F.Electrical - class IFS(ModuleInterface.IFS()): - dbgrq = ElectricLogic() - tdo = ElectricLogic() - tdi = ElectricLogic() - tms = ElectricLogic() - tck = ElectricLogic() - n_trst = ElectricLogic() - n_reset = ElectricLogic() - vtref = Electrical() - - self.IFs = IFS(self) - - ref = ElectricLogic.connect_all_module_references(self) - self.add_trait(has_single_electric_reference_defined(ref)) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) + ) diff --git a/src/faebryk/library/KicadFootprint.py b/src/faebryk/library/KicadFootprint.py index 22e76a9b..9026c68f 100644 --- a/src/faebryk/library/KicadFootprint.py +++ b/src/faebryk/library/KicadFootprint.py @@ -1,37 +1,36 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.can_attach_via_pinmap_pinlist import can_attach_via_pinmap_pinlist -from faebryk.library.Footprint import Footprint -from faebryk.library.has_kicad_manual_footprint import has_kicad_manual_footprint -from faebryk.library.Pad import Pad +import faebryk.library._F as F +from faebryk.libs.library import L from faebryk.libs.util import times -class KicadFootprint(Footprint): +class KicadFootprint(F.Footprint): def __init__(self, kicad_identifier: str, pin_names: list[str]) -> None: super().__init__() unique_pin_names = sorted(set(pin_names)) + self.pin_names_sorted = list(enumerate(unique_pin_names)) + self.kicad_identifier = kicad_identifier - class _IFS(Footprint.IFS()): - pins = times(len(unique_pin_names), Pad) + @classmethod + def with_simple_names(cls, kicad_identifier: str, pin_cnt: int): + return cls(kicad_identifier, [str(i + 1) for i in range(pin_cnt)]) - pin_names_sorted = list(enumerate(unique_pin_names)) + @L.rt_field + def pins(self): + return times(len(self.pin_names_sorted), F.Pad) - self.IFs = _IFS(self) - self.add_trait( - can_attach_via_pinmap_pinlist( - {pin_name: self.IFs.pins[i] for i, pin_name in pin_names_sorted} - ) - ) - self.add_trait( - has_kicad_manual_footprint( - kicad_identifier, - {self.IFs.pins[i]: pin_name for i, pin_name in pin_names_sorted}, - ) + @L.rt_field + def attach_via_pinmap(self): + return F.can_attach_via_pinmap_pinlist( + {pin_name: self.pins[i] for i, pin_name in self.pin_names_sorted} ) - @classmethod - def with_simple_names(cls, kicad_identifier: str, pin_cnt: int): - return cls(kicad_identifier, [str(i + 1) for i in range(pin_cnt)]) + @L.rt_field + def kicad_footprint(self): + return F.has_kicad_manual_footprint( + self.kicad_identifier, + {self.pins[i]: pin_name for i, pin_name in self.pin_names_sorted}, + ) diff --git a/src/faebryk/library/LDO.py b/src/faebryk/library/LDO.py index ca84b12e..2e98fbd0 100644 --- a/src/faebryk/library/LDO.py +++ b/src/faebryk/library/LDO.py @@ -3,20 +3,9 @@ from enum import Enum, auto -from faebryk.core.core import Module -from faebryk.core.util import as_unit, as_unit_with_tolerance -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.has_pin_association_heuristic_lookup_table import ( - has_pin_association_heuristic_lookup_table, -) -from faebryk.library.has_simple_value_representation_based_on_params import ( - has_simple_value_representation_based_on_params, -) -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import Quantity @@ -29,80 +18,81 @@ class OutputPolarity(Enum): POSITIVE = auto() NEGATIVE = auto() - @classmethod - def PARAMS(cls): - class _PARAMs(super().PARAMS()): - max_input_voltage = TBD[Quantity]() - output_voltage = TBD[Quantity]() - output_polarity = TBD[LDO.OutputPolarity]() - output_type = TBD[LDO.OutputType]() - output_current = TBD[Quantity]() - psrr = TBD[Quantity]() - dropout_voltage = TBD[Quantity]() - quiescent_current = TBD[Quantity]() - - return _PARAMs - - def __init__(self): - super().__init__() - - self.PARAMs = self.PARAMS()(self) - - class _IFs(super().IFS()): - enable = ElectricLogic() - power_in = ElectricPower() - power_out = ElectricPower() - - self.IFs = _IFs(self) - - self.IFs.power_in.PARAMs.voltage.merge(self.PARAMs.max_input_voltage) - self.IFs.power_out.PARAMs.voltage.merge(self.PARAMs.output_voltage) - - self.IFs.power_in.get_trait(can_be_decoupled).decouple() - self.IFs.power_out.get_trait(can_be_decoupled).decouple() - - self.IFs.enable.IFs.reference.connect(self.IFs.power_in) - if self.PARAMs.output_polarity == self.OutputPolarity.POSITIVE: - self.IFs.power_in.IFs.lv.connect(self.IFs.power_out.IFs.lv) - else: - self.IFs.power_in.IFs.hv.connect(self.IFs.power_out.IFs.hv) - - self.add_trait(can_bridge_defined(self.IFs.power_in, self.IFs.power_out)) - self.add_trait( - has_simple_value_representation_based_on_params( - ( - self.PARAMs.output_polarity, - self.PARAMs.output_type, - self.PARAMs.output_voltage, - self.PARAMs.output_current, - self.PARAMs.psrr, - self.PARAMs.dropout_voltage, - self.PARAMs.max_input_voltage, - self.PARAMs.quiescent_current, - ), - lambda ps: "LDO " - + " ".join( - [ - as_unit_with_tolerance(ps[2], "V"), - as_unit(ps[3], "A"), - as_unit(ps[4], "dB"), - as_unit(ps[5], "V"), - f"Vin max {as_unit(ps[6], 'V')}", - f"Iq {as_unit(ps[7], 'A')}", - ] - ), - ) + max_input_voltage: F.TBD[Quantity] + output_voltage: F.TBD[Quantity] + output_polarity: F.TBD[OutputPolarity] + output_type: F.TBD[OutputType] + output_current: F.TBD[Quantity] + psrr: F.TBD[Quantity] + dropout_voltage: F.TBD[Quantity] + quiescent_current: F.TBD[Quantity] + + enable: F.ElectricLogic + power_in: F.ElectricPower + power_out = L.d_field(lambda: F.ElectricPower().make_source()) + + def __preinit__(self): + self.power_in.voltage.merge(self.max_input_voltage) + self.power_out.voltage.merge(self.output_voltage) + + self.power_in.decoupled.decouple() + self.power_out.decoupled.decouple() + + self.enable.reference.connect(self.power_in) + # TODO: should be implemented differently (see below) + # if self.output_polarity == self.OutputPolarity.NEGATIVE: + # self.power_in.hv.connect(self.power_out.hv) + # else: + # self.power_in.lv.connect(self.power_out.lv) + + # LDO in & out share gnd reference + F.ElectricLogic.connect_all_node_references( + [self.power_in, self.power_out], gnd_only=True ) - self.add_trait(has_designator_prefix_defined("U")) - self.add_trait( - has_pin_association_heuristic_lookup_table( - mapping={ - self.IFs.power_in.IFs.hv: ["Vin", "Vi", "in"], - self.IFs.power_out.IFs.hv: ["Vout", "Vo", "out"], - self.IFs.power_in.IFs.lv: ["GND", "V-"], - self.IFs.enable.IFs.signal: ["EN", "Enable"], - }, - accept_prefix=False, - case_sensitive=False, - ) + + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(self.power_in, self.power_out) + + @L.rt_field + def simple_value_representation(self): + from faebryk.core.util import as_unit, as_unit_with_tolerance + + return F.has_simple_value_representation_based_on_params( + ( + self.output_polarity, + self.output_type, + self.output_voltage, + self.output_current, + self.psrr, + self.dropout_voltage, + self.max_input_voltage, + self.quiescent_current, + ), + lambda ps: "LDO " + + " ".join( + [ + as_unit_with_tolerance(ps[2], "V"), + as_unit(ps[3], "A"), + as_unit(ps[4], "dB"), + as_unit(ps[5], "V"), + f"Vin max {as_unit(ps[6], 'V')}", + f"Iq {as_unit(ps[7], 'A')}", + ] + ), + ) + + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") + + @L.rt_field + def pin_association_heuristic(self): + return F.has_pin_association_heuristic_lookup_table( + mapping={ + self.power_in.hv: ["Vin", "Vi", "in"], + self.power_out.hv: ["Vout", "Vo", "out"], + self.power_in.lv: ["GND", "V-"], + self.enable.signal: ["EN", "Enable"], + }, + accept_prefix=False, + case_sensitive=False, ) diff --git a/src/faebryk/library/LED.py b/src/faebryk/library/LED.py index 264f847d..2d9c0749 100644 --- a/src/faebryk/library/LED.py +++ b/src/faebryk/library/LED.py @@ -4,16 +4,12 @@ from enum import Enum, auto -from faebryk.core.core import Parameter -from faebryk.library.Diode import Diode -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.Resistor import Resistor -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.parameter import Parameter from faebryk.libs.units import Quantity -class LED(Diode): +class LED(F.Diode): class Color(Enum): RED = auto() EMERALD = auto() @@ -22,57 +18,42 @@ class Color(Enum): YELLOW = auto() WHITE = auto() - @classmethod - def PARAMS(cls): - class _PARAMs(super().PARAMS()): - brightness = TBD[Quantity]() - max_brightness = TBD[Quantity]() - color = TBD[cls.Color]() + brightness: F.TBD[Quantity] + max_brightness: F.TBD[Quantity] + color: F.TBD[Color] - return _PARAMs + def __preinit__(self): + self.current.merge(self.brightness / self.max_brightness * self.max_current) - def __init__(self) -> None: - super().__init__() - - self.PARAMs = self.PARAMS()(self) - - self.PARAMs.current.merge( - self.PARAMs.brightness - / self.PARAMs.max_brightness - * self.PARAMs.max_current - ) - - self.inherit() - - # self.PARAMs.brightness.merge( - # Range(0 * P.millicandela, self.PARAMs.max_brightness) + # self.brightness.merge( + # F.Range(0 * P.millicandela, self.max_brightness) # ) def set_intensity(self, intensity: Parameter[Quantity]) -> None: - self.PARAMs.brightness.merge(intensity * self.PARAMs.max_brightness) + self.brightness.merge(intensity * self.max_brightness) def connect_via_current_limiting_resistor( self, input_voltage: Parameter[Quantity], - resistor: Resistor, - target: Electrical, + resistor: F.Resistor, + target: F.Electrical, low_side: bool, ): if low_side: - self.IFs.cathode.connect_via(resistor, target) + self.cathode.connect_via(resistor, target) else: - self.IFs.anode.connect_via(resistor, target) + self.anode.connect_via(resistor, target) - resistor.PARAMs.resistance.merge( + resistor.resistance.merge( self.get_needed_series_resistance_for_current_limit(input_voltage), ) def connect_via_current_limiting_resistor_to_power( - self, resistor: Resistor, power: ElectricPower, low_side: bool + self, resistor: F.Resistor, power: F.ElectricPower, low_side: bool ): self.connect_via_current_limiting_resistor( - power.PARAMs.voltage, + power.voltage, resistor, - power.IFs.lv if low_side else power.IFs.hv, + power.lv if low_side else power.hv, low_side, ) diff --git a/src/faebryk/library/LEDIndicator.py b/src/faebryk/library/LEDIndicator.py index 80103fac..fa1f66bb 100644 --- a/src/faebryk/library/LEDIndicator.py +++ b/src/faebryk/library/LEDIndicator.py @@ -1,30 +1,24 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.PoweredLED import PoweredLED -from faebryk.library.PowerSwitchMOSFET import PowerSwitchMOSFET + +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L class LEDIndicator(Module): - def __init__(self) -> None: - super().__init__() + # interfaces - # interfaces - class _IFs(Module.IFS()): - logic_in = ElectricLogic() - power_in = ElectricPower() + logic_in: F.ElectricLogic + power_in: F.ElectricPower - self.IFs = _IFs(self) + # components - # components - class _NODEs(Module.NODES()): - led = PoweredLED() - # TODO make generic - power_switch = PowerSwitchMOSFET(lowside=True, normally_closed=False) + led: F.PoweredLED - self.NODEs = _NODEs(self) + # TODO make generic + power_switch = L.f_field(F.PowerSwitchMOSFET)(lowside=True, normally_closed=False) - self.IFs.power_in.connect_via(self.NODEs.power_switch, self.NODEs.led.IFs.power) - self.NODEs.power_switch.IFs.logic_in.connect(self.IFs.logic_in) + def __preinit__(self): + self.power_in.connect_via(self.power_switch, self.led.power) + self.power_switch.logic_in.connect(self.logic_in) diff --git a/src/faebryk/library/Logic.py b/src/faebryk/library/Logic.py index 848b5deb..67c3a2fc 100644 --- a/src/faebryk/library/Logic.py +++ b/src/faebryk/library/Logic.py @@ -1,22 +1,13 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface -from faebryk.library.Range import Range +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L class Logic(ModuleInterface): - @staticmethod - def PARAMS(): - class _PARAMS(ModuleInterface.PARAMS()): - state = Range(False, True) - - return _PARAMS - - def __init__(self) -> None: - super().__init__() - - self.PARAMs = self.PARAMS() + state = L.f_field(F.Range)(False, True) def set(self, on: bool): - self.PARAMs.state.merge(on) + self.state.merge(on) diff --git a/src/faebryk/library/Logic74xx.py b/src/faebryk/library/Logic74xx.py index 3c12afca..7dcb0e52 100644 --- a/src/faebryk/library/Logic74xx.py +++ b/src/faebryk/library/Logic74xx.py @@ -4,17 +4,9 @@ from enum import Enum, auto from typing import Callable, Sequence -from faebryk.core.core import Module -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricLogicGate import ElectricLogicGate -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_designator_prefix_defined import ( - has_designator_prefix_defined, -) -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L class Logic74xx(Module): @@ -57,31 +49,25 @@ class Family(Enum): TTL = auto() CD4000 = auto() + power: F.ElectricPower + logic_family: F.TBD[Family] + + designator = L.f_field(F.has_designator_prefix_defined)("U") + + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) + ) + def __init__( self, - gates_factory: Sequence[Callable[[], ElectricLogicGate]], + gates_factory: Sequence[Callable[[], F.ElectricLogicGate]], ) -> None: super().__init__() - class _IFs(Module.IFS()): - power = ElectricPower() - - self.IFs = _IFs(self) - - class _NODEs(Module.NODES()): - gates = [g() for g in gates_factory] + self.gates_factory = gates_factory - self.NODEs = _NODEs(self) - - class _PARAMs(Module.PARAMS()): - logic_family = TBD[Logic74xx.Family]() - - self.PARAMs = _PARAMs(self) - - self.add_trait(has_designator_prefix_defined("U")) - - self.add_trait( - has_single_electric_reference_defined( - ElectricLogic.connect_all_module_references(self) - ) - ) + @L.rt_field + def gates(self): + return [g() for g in self.gates_factory] diff --git a/src/faebryk/library/LogicGate.py b/src/faebryk/library/LogicGate.py index 9c145a74..d9917341 100644 --- a/src/faebryk/library/LogicGate.py +++ b/src/faebryk/library/LogicGate.py @@ -1,79 +1,83 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from typing import Sequence, TypeVar +from typing import Sequence -from faebryk.core.core import Module, TraitImpl -from faebryk.library.Constant import Constant -from faebryk.library.Logic import Logic -from faebryk.library.LogicOps import LogicOps +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.core.trait import TraitImpl +from faebryk.libs.library import L from faebryk.libs.util import times -T = TypeVar("T", bound=Logic) - class LogicGate(Module): - class can_logic_or_gate(LogicOps.can_logic_or.impl()): + class can_logic_or_gate(F.LogicOps.can_logic_or.impl()): def on_obj_set(self) -> None: - assert isinstance(self.get_obj(), LogicGate) + assert isinstance(self.obj, LogicGate) - def or_(self, *ins: Logic): - obj = self.get_obj() + def or_(self, *ins: F.Logic): + obj = self.obj assert isinstance(obj, LogicGate) return obj.op(*ins)[0] - class can_logic_nor_gate(LogicOps.can_logic_nor.impl()): + class can_logic_nor_gate(F.LogicOps.can_logic_nor.impl()): def on_obj_set(self) -> None: - assert isinstance(self.get_obj(), LogicGate) + assert isinstance(self.obj, LogicGate) - def nor(self, *ins: Logic): - obj = self.get_obj() + def nor(self, *ins: F.Logic): + obj = self.obj assert isinstance(obj, LogicGate) return obj.op(*ins)[0] - class can_logic_nand_gate(LogicOps.can_logic_nand.impl()): + class can_logic_nand_gate(F.LogicOps.can_logic_nand.impl()): def on_obj_set(self) -> None: - assert isinstance(self.get_obj(), LogicGate) + assert isinstance(self.obj, LogicGate) - def nand(self, *ins: Logic): - obj = self.get_obj() + def nand(self, *ins: F.Logic): + obj = self.obj assert isinstance(obj, LogicGate) return obj.op(*ins)[0] - class can_logic_xor_gate(LogicOps.can_logic_xor.impl()): + class can_logic_xor_gate(F.LogicOps.can_logic_xor.impl()): def on_obj_set(self) -> None: - assert isinstance(self.get_obj(), LogicGate) + assert isinstance(self.obj, LogicGate) - def xor(self, *ins: Logic): - obj = self.get_obj() + def xor(self, *ins: F.Logic): + obj = self.obj assert isinstance(obj, LogicGate) return obj.op(*ins)[0] def __init__( self, - input_cnt: Constant[int], - output_cnt: Constant[int], + input_cnt: F.Constant[int], + output_cnt: F.Constant[int], *functions: TraitImpl, ) -> None: super().__init__() + self._input_cnt = input_cnt + self._output_cnt = output_cnt + self._functions = list(functions) - class IFS(Module.IFS()): - inputs = times(input_cnt, Logic) - outputs = times(output_cnt, Logic) + @L.rt_field + def functions(self): + return self._functions - self.IFs = IFS(self) + @L.rt_field + def inputs(self): + return times(self._input_cnt, F.Logic) - for f in functions: - self.add_trait(f) + @L.rt_field + def outputs(self): + return times(self._output_cnt, F.Logic) @staticmethod - def op_( - ins1: Sequence[Logic], ins2: Sequence[Logic], out: Sequence[T] + def op_[T: F.Logic]( + ins1: Sequence[F.Logic], ins2: Sequence[F.Logic], out: Sequence[T] ) -> Sequence[T]: assert len(ins1) == len(ins2) for in_if_mod, in_if in zip(ins1, ins2): in_if_mod.connect(in_if) return out - def op(self, *ins: Logic): - return self.op_(ins, self.IFs.inputs, self.IFs.outputs) + def op(self, *ins: F.Logic): + return self.op_(ins, self.inputs, self.outputs) diff --git a/src/faebryk/library/LogicGates.py b/src/faebryk/library/LogicGates.py index d051194a..16c0ef08 100644 --- a/src/faebryk/library/LogicGates.py +++ b/src/faebryk/library/LogicGates.py @@ -1,28 +1,25 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from typing import TypeVar +import faebryk.library._F as F +from faebryk.core.core import Namespace -from faebryk.library.Constant import Constant -from faebryk.library.Logic import Logic -from faebryk.library.LogicGate import LogicGate -T = TypeVar("T", bound=Logic) +class LogicGates(Namespace): + class OR(F.LogicGate): + def __init__(self, input_cnt: F.Constant[int]): + super().__init__(input_cnt, F.Constant(1), F.LogicGate.can_logic_or_gate()) + class NOR(F.LogicGate): + def __init__(self, input_cnt: F.Constant[int]): + super().__init__(input_cnt, F.Constant(1), F.LogicGate.can_logic_nor_gate()) -class LogicGates: - class OR(LogicGate): - def __init__(self, input_cnt: Constant[int]): - super().__init__(input_cnt, Constant(1), LogicGate.can_logic_or_gate()) + class NAND(F.LogicGate): + def __init__(self, input_cnt: F.Constant[int]): + super().__init__( + input_cnt, F.Constant(1), F.LogicGate.can_logic_nand_gate() + ) - class NOR(LogicGate): - def __init__(self, input_cnt: Constant[int]): - super().__init__(input_cnt, Constant(1), LogicGate.can_logic_nor_gate()) - - class NAND(LogicGate): - def __init__(self, input_cnt: Constant[int]): - super().__init__(input_cnt, Constant(1), LogicGate.can_logic_nand_gate()) - - class XOR(LogicGate): - def __init__(self, input_cnt: Constant[int]): - super().__init__(input_cnt, Constant(1), LogicGate.can_logic_xor_gate()) + class XOR(F.LogicGate): + def __init__(self, input_cnt: F.Constant[int]): + super().__init__(input_cnt, F.Constant(1), F.LogicGate.can_logic_xor_gate()) diff --git a/src/faebryk/library/LogicOps.py b/src/faebryk/library/LogicOps.py index 5aec6622..ca141d30 100644 --- a/src/faebryk/library/LogicOps.py +++ b/src/faebryk/library/LogicOps.py @@ -2,50 +2,48 @@ # SPDX-License-Identifier: MIT from abc import abstractmethod -from typing import TypeVar -from faebryk.core.core import NodeTrait -from faebryk.library.Logic import Logic +import faebryk.library._F as F +from faebryk.core.core import Namespace +from faebryk.core.trait import Trait -T = TypeVar("T", bound=Logic) - -class LogicOps: - class can_logic(NodeTrait): +class LogicOps(Namespace): + class can_logic(Trait): @abstractmethod - def op(self, *ins: Logic) -> Logic: ... + def op(self, *ins: F.Logic) -> F.Logic: ... class can_logic_or(can_logic): @abstractmethod - def or_(self, *ins: Logic) -> Logic: ... + def or_(self, *ins: F.Logic) -> F.Logic: ... - def op(self, *ins: Logic) -> Logic: + def op(self, *ins: F.Logic) -> F.Logic: return self.or_(*ins) class can_logic_and(can_logic): @abstractmethod - def and_(self, *ins: Logic) -> Logic: ... + def and_(self, *ins: F.Logic) -> F.Logic: ... - def op(self, *ins: Logic) -> Logic: + def op(self, *ins: F.Logic) -> F.Logic: return self.and_(*ins) class can_logic_nor(can_logic): @abstractmethod - def nor(self, *ins: Logic) -> Logic: ... + def nor(self, *ins: F.Logic) -> F.Logic: ... - def op(self, *ins: Logic) -> Logic: + def op(self, *ins: F.Logic) -> F.Logic: return self.nor(*ins) class can_logic_nand(can_logic): @abstractmethod - def nand(self, *ins: Logic) -> Logic: ... + def nand(self, *ins: F.Logic) -> F.Logic: ... - def op(self, *ins: Logic) -> Logic: + def op(self, *ins: F.Logic) -> F.Logic: return self.nand(*ins) class can_logic_xor(can_logic): @abstractmethod - def xor(self, *ins: Logic) -> Logic: ... + def xor(self, *ins: F.Logic) -> F.Logic: ... - def op(self, *ins: Logic) -> Logic: + def op(self, *ins: F.Logic) -> F.Logic: return self.xor(*ins) diff --git a/src/faebryk/library/M24C08_FMN6TP.py b/src/faebryk/library/M24C08_FMN6TP.py index e52f35bd..c331f51d 100644 --- a/src/faebryk/library/M24C08_FMN6TP.py +++ b/src/faebryk/library/M24C08_FMN6TP.py @@ -3,69 +3,55 @@ import logging -from faebryk.core.core import Module -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_designator_prefix_defined import ( - has_designator_prefix_defined, -) -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.library.I2C import I2C -from faebryk.library.SOIC import SOIC +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import P -from faebryk.libs.util import times logger = logging.getLogger(__name__) # TODO remove generic stuff into EEPROM/i2c device etc class M24C08_FMN6TP(Module): - def __init__(self) -> None: - super().__init__() - - class _IFs(Module.IFS()): - power = ElectricPower() - data = I2C() - nwc = ElectricLogic() - e = times(3, ElectricLogic) + power: F.ElectricPower + data: F.I2C + nwc: F.ElectricLogic + e = L.list_field(3, F.ElectricLogic) + + @L.rt_field + def attach_to_footprint(self): + x = self + return F.can_attach_to_footprint_via_pinmap( + { + "1": x.e[0].signal, + "2": x.e[1].signal, + "3": x.e[2].signal, + "4": x.power.lv, + "5": x.data.sda.signal, + "6": x.data.scl.signal, + "7": x.nwc.signal, + "8": x.power.hv, + } + ) - self.IFs = _IFs(self) + def __preinit__(self): + self.attach_to_footprint.attach( + F.SOIC(8, size_xy=(3.9 * P.mm, 4.9 * P.mm), pitch=1.27 * P.mm) + ) - x = self.IFs - self.add_trait( - can_attach_to_footprint_via_pinmap( - { - "1": x.e[0].IFs.signal, - "2": x.e[1].IFs.signal, - "3": x.e[2].IFs.signal, - "4": x.power.IFs.lv, - "5": x.data.IFs.sda.IFs.signal, - "6": x.data.IFs.scl.IFs.signal, - "7": x.nwc.IFs.signal, - "8": x.power.IFs.hv, - } - ) - ).attach(SOIC(8, size_xy=(3.9 * P.mm, 4.9 * P.mm), pitch=1.27 * P.mm)) + self.data.terminate() + self.power.decoupled.decouple() - self.add_trait( - has_single_electric_reference_defined( - ElectricLogic.connect_all_module_references(self) - ) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) ) - self.IFs.data.terminate() - self.IFs.power.get_trait(can_be_decoupled).decouple() - - self.add_trait(has_designator_prefix_defined("U")) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") def set_address(self, addr: int): - assert addr < (1 << len(self.IFs.e)) + assert addr < (1 << len(self.e)) - for i, e in enumerate(self.IFs.e): + for i, e in enumerate(self.e): e.set(addr & (1 << i) != 0) diff --git a/src/faebryk/library/MCP2221A.py b/src/faebryk/library/MCP2221A.py index 53a9c93a..349ff3e4 100644 --- a/src/faebryk/library/MCP2221A.py +++ b/src/faebryk/library/MCP2221A.py @@ -3,46 +3,25 @@ import logging -from faebryk.core.core import Module -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.I2C import I2C -from faebryk.library.UART_Base import UART_Base -from faebryk.library.USB2_0 import USB2_0 -from faebryk.libs.util import times +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L logger = logging.getLogger(__name__) class MCP2221A(Module): - def __init__(self) -> None: - super().__init__() - - class _NODEs(Module.NODES()): ... - - self.NODEs = _NODEs(self) - - class _IFs(Module.IFS()): - power = ElectricPower() - power_vusb = ElectricPower() - uart = UART_Base() - i2c = I2C() - gpio = times(4, Electrical) - reset = ElectricLogic() - usb = USB2_0() - - self.IFs = _IFs(self) - - class _PARAMs(Module.PARAMS()): ... - - self.PARAMs = _PARAMs(self) - - self.IFs.power.get_trait(can_be_decoupled).decouple() - self.IFs.power_vusb.get_trait(can_be_decoupled).decouple() - - self.add_trait(has_designator_prefix_defined("U")) - - self.IFs.power.IFs.lv.connect(self.IFs.power_vusb.IFs.lv) + power: F.ElectricPower + power_vusb: F.ElectricPower + uart: F.UART_Base + i2c: F.I2C + gpio = L.list_field(4, F.Electrical) + reset: F.ElectricLogic + usb: F.USB2_0 + + def __preinit__(self): + self.power.decoupled.decouple() + self.power_vusb.decoupled.decouple() + self.power.lv.connect(self.power_vusb.lv) + + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") diff --git a/src/faebryk/library/ME6211C33M5G_N.py b/src/faebryk/library/ME6211C33M5G_N.py index 5748764f..8d81ebd0 100644 --- a/src/faebryk/library/ME6211C33M5G_N.py +++ b/src/faebryk/library/ME6211C33M5G_N.py @@ -1,78 +1,42 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.core.util import connect_to_all_interfaces -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_designator_prefix_defined import ( - has_designator_prefix_defined, -) -from faebryk.library.Range import Range +import faebryk.library._F as F +from faebryk.libs.library import L from faebryk.libs.units import P -class ME6211C33M5G_N(Module): +class ME6211C33M5G_N(F.LDO): """ 3.3V 600mA LDO """ + # components + def __init__(self, default_enabled: bool = True) -> None: super().__init__() + self._default_enabled = default_enabled - # interfaces - class _IFs(Module.IFS()): - power_in = ElectricPower() - power_out = ElectricPower() - enable = Electrical() - - self.IFs = _IFs(self) - - # components - class _NODEs(Module.NODES()): ... - - self.NODEs = _NODEs(self) - - class _PARAMs(Module.PARAMS()): ... - - self.PARAMs = _PARAMs(self) - + def __preinit__(self): # set constraints - self.IFs.power_out.PARAMs.voltage.merge( - Range(3.3 * 0.98 * P.V, 3.3 * 1.02 * P.V) + self.output_voltage.merge(F.Range(3.3 * 0.98 * P.V, 3.3 * 1.02 * P.V)) + + if self._default_enabled: + self.enable.set(True) + + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") + + @L.rt_field + def attach_to_footprint(self): + return F.can_attach_to_footprint_via_pinmap( + { + "1": self.power_in.hv, + "2": self.power_in.lv, + "3": self.enable, + "5": self.power_out.hv, + } ) - # connect decouple capacitor - self.IFs.power_in.get_trait(can_be_decoupled).decouple() - self.IFs.power_out.get_trait(can_be_decoupled).decouple() - - # LDO in & out share gnd reference - self.IFs.power_in.IFs.lv.connect(self.IFs.power_out.IFs.lv) - - self.add_trait(has_designator_prefix_defined("U")) - self.add_trait( - can_attach_to_footprint_via_pinmap( - { - "1": self.IFs.power_in.IFs.hv, - "2": self.IFs.power_in.IFs.lv, - "3": self.IFs.enable, - "5": self.IFs.power_out.IFs.hv, - } - ) - ) - - self.add_trait( - has_datasheet_defined( - "https://datasheet.lcsc.com/lcsc/1811131510_MICRONE-Nanjing-Micro-One-Elec-ME6211C33M5G-N_C82942.pdf" - ) - ) - - if default_enabled: - self.IFs.enable.connect(self.IFs.power_in.IFs.hv) - - connect_to_all_interfaces(self.IFs.power_in.IFs.lv, [self.IFs.power_out.IFs.lv]) + datasheet = L.f_field(F.has_datasheet_defined)( + "https://datasheet.lcsc.com/lcsc/1811131510_MICRONE-Nanjing-Micro-One-Elec-ME6211C33M5G-N_C82942.pdf" + ) diff --git a/src/faebryk/library/MOSFET.py b/src/faebryk/library/MOSFET.py index 67a2c344..48e293ac 100644 --- a/src/faebryk/library/MOSFET.py +++ b/src/faebryk/library/MOSFET.py @@ -3,14 +3,9 @@ from enum import Enum, auto -from faebryk.core.core import Module -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.Electrical import Electrical -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.has_pin_association_heuristic_lookup_table import ( - has_pin_association_heuristic_lookup_table, -) -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import Quantity @@ -23,38 +18,32 @@ class SaturationType(Enum): ENHANCEMENT = auto() DEPLETION = auto() - def __init__(self): - super().__init__() - - class _PARAMs(Module.PARAMS()): - channel_type = TBD[MOSFET.ChannelType]() - saturation_type = TBD[MOSFET.SaturationType]() - gate_source_threshold_voltage = TBD[Quantity]() - max_drain_source_voltage = TBD[Quantity]() - max_continuous_drain_current = TBD[Quantity]() - on_resistance = TBD[Quantity]() - - self.PARAMs = _PARAMs(self) - - class _IFs(Module.IFS()): - source = Electrical() - gate = Electrical() - drain = Electrical() - - self.IFs = _IFs(self) - - self.add_trait(has_designator_prefix_defined("Q")) - # TODO pretty confusing - self.add_trait(can_bridge_defined(in_if=self.IFs.source, out_if=self.IFs.drain)) - - self.add_trait( - has_pin_association_heuristic_lookup_table( - mapping={ - self.IFs.source: ["S", "Source"], - self.IFs.gate: ["G", "Gate"], - self.IFs.drain: ["D", "Drain"], - }, - accept_prefix=False, - case_sensitive=False, - ) + channel_type: F.TBD[ChannelType] + saturation_type: F.TBD[SaturationType] + gate_source_threshold_voltage: F.TBD[Quantity] + max_drain_source_voltage: F.TBD[Quantity] + max_continuous_drain_current: F.TBD[Quantity] + on_resistance: F.TBD[Quantity] + + source: F.Electrical + gate: F.Electrical + drain: F.Electrical + + designator_prefix = L.f_field(F.has_designator_prefix_defined)("Q") + + # TODO pretty confusing + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(in_if=self.source, out_if=self.drain) + + @L.rt_field + def pin_association_heuristic(self): + return F.has_pin_association_heuristic_lookup_table( + mapping={ + self.source: ["S", "Source"], + self.gate: ["G", "Gate"], + self.drain: ["D", "Drain"], + }, + accept_prefix=False, + case_sensitive=False, ) diff --git a/src/faebryk/library/Mechanical.py b/src/faebryk/library/Mechanical.py index 5f13ae48..4cca33fb 100644 --- a/src/faebryk/library/Mechanical.py +++ b/src/faebryk/library/Mechanical.py @@ -1,7 +1,7 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface +from faebryk.core.moduleinterface import ModuleInterface class Mechanical(ModuleInterface): ... diff --git a/src/faebryk/library/Mounting_Hole.py b/src/faebryk/library/Mounting_Hole.py index fab77341..2babd569 100644 --- a/src/faebryk/library/Mounting_Hole.py +++ b/src/faebryk/library/Mounting_Hole.py @@ -3,39 +3,31 @@ import logging -from faebryk.core.core import Module -from faebryk.library.can_attach_to_footprint_symmetrically import ( - can_attach_to_footprint_symmetrically, -) -from faebryk.library.Constant import Constant -from faebryk.library.has_defined_footprint import has_defined_footprint -from faebryk.library.has_designator_prefix_defined import ( - has_designator_prefix_defined, -) -from faebryk.library.KicadFootprint import KicadFootprint -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import P, Quantity logger = logging.getLogger(__name__) class Mounting_Hole(Module): - def __init__(self) -> None: - super().__init__() + diameter: F.TBD[Quantity] - class PARAMs(Module.PARAMS()): - diameter = TBD[Quantity]() - - self.PARAMs = PARAMs(self) - - self.add_trait(can_attach_to_footprint_symmetrically()) - self.add_trait(has_designator_prefix_defined("H")) + attach_to_footprint: F.can_attach_to_footprint_symmetrically + designator_prefix = L.f_field(F.has_designator_prefix_defined)("H") + def __preinit__(self): # Only 3.2mm supported for now - self.PARAMs.diameter.merge(Constant(3.2 * P.mm)) + self.diameter.merge(F.Constant(3.2 * P.mm)) + + # footprint = L.f_field(F.has_footprint_defined)( + # F.KicadFootprint("MountingHole:MountingHole_3.2mm_M3_Pad", pin_names=[]) + # ) - self.add_trait( - has_defined_footprint( - KicadFootprint("MountingHole:MountingHole_3.2mm_M3_Pad", pin_names=[]) - ) + # TODO make back to f_field, rt_field because of imports + @L.rt_field + def footprint(self): + return F.has_footprint_defined( + F.KicadFootprint("MountingHole:MountingHole_3.2mm_M3_Pad", pin_names=[]) ) diff --git a/src/faebryk/library/MultiSPI.py b/src/faebryk/library/MultiSPI.py index 065938e9..b7b3c34a 100644 --- a/src/faebryk/library/MultiSPI.py +++ b/src/faebryk/library/MultiSPI.py @@ -1,22 +1,20 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface -from faebryk.library.ElectricLogic import ElectricLogic +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L from faebryk.libs.util import times class MultiSPI(ModuleInterface): def __init__(self, data_lane_count: int) -> None: super().__init__() + self._data_lane_count = data_lane_count - class IFS(ModuleInterface.IFS()): - clk = ElectricLogic() - data = times(data_lane_count, ElectricLogic) - cs = ElectricLogic() + clk: F.ElectricLogic + cs: F.ElectricLogic - self.IFs = IFS(self) - - class PARAMS(ModuleInterface.PARAMS()): ... - - self.PARAMs = PARAMS(self) + @L.rt_field + def data(self): + return times(self._data_lane_count, F.ElectricLogic) diff --git a/src/faebryk/library/Net.py b/src/faebryk/library/Net.py index 2d195b32..7528e9ee 100644 --- a/src/faebryk/library/Net.py +++ b/src/faebryk/library/Net.py @@ -3,27 +3,19 @@ import logging -from faebryk.core.core import Module -from faebryk.core.util import get_connected_mifs, get_parent_of_type -from faebryk.library.Electrical import Electrical -from faebryk.library.Footprint import Footprint -from faebryk.library.has_overriden_name import has_overriden_name -from faebryk.library.has_overriden_name_defined import has_overriden_name_defined -from faebryk.library.Pad import Pad +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L logger = logging.getLogger(__name__) class Net(Module): - def __init__(self) -> None: - super().__init__() + part_of: F.Electrical - class _IFs(super().IFS()): - part_of = Electrical() - - self.IFs = _IFs(self) - - class _(has_overriden_name.impl()): + @L.rt_field + def overriden_name(self): + class _(F.has_overriden_name.impl()): def get_name(_self): from faebryk.exporters.netlist.graph import ( can_represent_kicad_footprint, @@ -47,33 +39,37 @@ def get_name(_self): return name - self.add_trait(_()) + return _() def get_fps(self): + from faebryk.core.util import get_parent_of_type + return { pad: fp for mif in self.get_connected_interfaces() - if (fp := get_parent_of_type(mif, Footprint)) is not None - and (pad := get_parent_of_type(mif, Pad)) is not None + if (fp := get_parent_of_type(mif, F.Footprint)) is not None + and (pad := get_parent_of_type(mif, F.Pad)) is not None } # TODO should this be here? def get_connected_interfaces(self): + from faebryk.core.util import get_connected_mifs + return { mif - for mif in get_connected_mifs(self.IFs.part_of.GIFs.connected) - if isinstance(mif, type(self.IFs.part_of)) + for mif in get_connected_mifs(self.part_of.connected) + if isinstance(mif, type(self.part_of)) } def __repr__(self) -> str: up = super().__repr__() - if self.has_trait(has_overriden_name): - return f"{up}'{self.get_trait(has_overriden_name).get_name()}'" + if self.has_trait(F.has_overriden_name): + return f"{up}'{self.get_trait(F.has_overriden_name).get_name()}'" else: return up @classmethod def with_name(cls, name: str) -> "Net": n = cls() - n.add_trait(has_overriden_name_defined(name)) + n.add_trait(F.has_overriden_name_defined(name)) return n diff --git a/src/faebryk/library/OLED_Module.py b/src/faebryk/library/OLED_Module.py index 7360594f..ca5a3fd7 100644 --- a/src/faebryk/library/OLED_Module.py +++ b/src/faebryk/library/OLED_Module.py @@ -4,50 +4,46 @@ import logging from enum import Enum, auto -from faebryk.core.core import Module -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.I2C import I2C -from faebryk.library.Range import Range -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import P logger = logging.getLogger(__name__) class OLED_Module(Module): - class Resolution(Enum): + class DisplayResolution(Enum): H64xV32 = auto() H128xV32 = auto() H128xV64 = auto() H256xV64 = auto() + class DisplaySize(Enum): + INCH_0_96 = auto() + INCH_1_12 = auto() + INCH_1_27 = auto() + INCH_1_3 = auto() + INCH_1_5 = auto() + INCH_2_23 = auto() + INCH_2_3 = auto() + INCH_2_42 = auto() + INCH_2_7 = auto() + class DisplayController(Enum): SSD1315 = auto() SSD1306 = auto() + SSD1309 = auto() - def __init__(self) -> None: - super().__init__() - - class _NODEs(Module.NODES()): ... - - self.NODEs = _NODEs(self) - - class _IFs(Module.IFS()): - power = ElectricPower() - i2c = I2C() - - self.IFs = _IFs(self) - - class _PARAMs(Module.PARAMS()): - resolution = TBD[self.Resolution]() - display_controller = TBD[self.DisplayController]() - - self.PARAMs = _PARAMs(self) + power: F.ElectricPower + i2c: F.I2C - self.IFs.power.PARAMs.voltage.merge(Range(3.0 * P.V, 5 * P.V)) + display_resolution: F.TBD[DisplayResolution] + display_controller: F.TBD[DisplayController] + display_size: F.TBD[DisplaySize] - self.IFs.power.get_trait(can_be_decoupled).decouple() + def __preinit__(self): + self.power.voltage.merge(F.Range(3.0 * P.V, 5 * P.V)) + self.power.decoupled.decouple() - self.add_trait(has_designator_prefix_defined("OLED")) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("OLED") diff --git a/src/faebryk/library/OpAmp.py b/src/faebryk/library/OpAmp.py index a0cad8b4..5dfef26d 100644 --- a/src/faebryk/library/OpAmp.py +++ b/src/faebryk/library/OpAmp.py @@ -1,73 +1,59 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.core.util import as_unit -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.has_pin_association_heuristic_lookup_table import ( - has_pin_association_heuristic_lookup_table, -) -from faebryk.library.has_simple_value_representation_based_on_params import ( - has_simple_value_representation_based_on_params, -) -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import Quantity class OpAmp(Module): - def __init__(self): - super().__init__() - - class _PARAMs(self.PARAMS()): - bandwidth = TBD[Quantity]() - common_mode_rejection_ratio = TBD[Quantity]() - input_bias_current = TBD[Quantity]() - input_offset_voltage = TBD[Quantity]() - gain_bandwidth_product = TBD[Quantity]() - output_current = TBD[Quantity]() - slew_rate = TBD[Quantity]() - - self.PARAMs = _PARAMs(self) - - class _IFs(super().IFS()): - power = ElectricPower() - inverting_input = Electrical() - non_inverting_input = Electrical() - output = Electrical() - - self.IFs = _IFs(self) - - self.add_trait( - has_simple_value_representation_based_on_params( - [ - self.PARAMs.bandwidth, - self.PARAMs.common_mode_rejection_ratio, - self.PARAMs.input_bias_current, - self.PARAMs.input_offset_voltage, - self.PARAMs.gain_bandwidth_product, - self.PARAMs.output_current, - self.PARAMs.slew_rate, - ], - lambda p: ( - f"{as_unit(p[0], 'Hz')} BW, {p[1]} CMRR, {as_unit(p[2], 'A')} Ib, " - f"{as_unit(p[3], 'V')} Vos, {as_unit(p[4], 'Hz')} GBW, " - f"{as_unit(p[5], 'A')} Iout, {as_unit(p[6], 'V/s')} SR" - ), - ) + bandwidth: F.TBD[Quantity] + common_mode_rejection_ratio: F.TBD[Quantity] + input_bias_current: F.TBD[Quantity] + input_offset_voltage: F.TBD[Quantity] + gain_bandwidth_product: F.TBD[Quantity] + output_current: F.TBD[Quantity] + slew_rate: F.TBD[Quantity] + + power: F.ElectricPower + inverting_input: F.Electrical + non_inverting_input: F.Electrical + output: F.Electrical + + @L.rt_field + def simple_value_representation(self): + from faebryk.core.util import as_unit + + return F.has_simple_value_representation_based_on_params( + [ + self.bandwidth, + self.common_mode_rejection_ratio, + self.input_bias_current, + self.input_offset_voltage, + self.gain_bandwidth_product, + self.output_current, + self.slew_rate, + ], + lambda p: ( + f"{as_unit(p[0], 'Hz')} BW, {p[1]} CMRR, {as_unit(p[2], 'A')} Ib, " + f"{as_unit(p[3], 'V')} Vos, {as_unit(p[4], 'Hz')} GBW, " + f"{as_unit(p[5], 'A')} Iout, {as_unit(p[6], 'V/s')} SR" + ), ) - self.add_trait( - has_pin_association_heuristic_lookup_table( - mapping={ - self.IFs.power.IFs.hv: ["V+", "Vcc", "Vdd"], - self.IFs.power.IFs.lv: ["V-", "Vee", "Vss", "GND"], - self.IFs.inverting_input: ["-", "IN-"], - self.IFs.non_inverting_input: ["+", "IN+"], - self.IFs.output: ["OUT"], - }, - accept_prefix=False, - case_sensitive=False, - ) + + @L.rt_field + def pin_association_heuristic(self): + return F.has_pin_association_heuristic_lookup_table( + mapping={ + self.power.hv: ["V+", "Vcc", "Vdd"], + self.power.lv: ["V-", "Vee", "Vss", "GND"], + self.inverting_input: ["-", "IN-"], + self.non_inverting_input: ["+", "IN+"], + self.output: ["OUT"], + }, + accept_prefix=False, + case_sensitive=False, ) - self.add_trait(has_designator_prefix_defined("U")) + + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") diff --git a/src/faebryk/library/Operation.py b/src/faebryk/library/Operation.py index 4dfdac7b..8bb9829e 100644 --- a/src/faebryk/library/Operation.py +++ b/src/faebryk/library/Operation.py @@ -5,7 +5,7 @@ import typing from textwrap import indent -from faebryk.core.core import Parameter +from faebryk.core.parameter import Parameter from faebryk.libs.util import TwistArgs, find, try_avoid_endless_recursion logger = logging.getLogger(__name__) diff --git a/src/faebryk/library/PJ398SM.py b/src/faebryk/library/PJ398SM.py index 12f1ba46..e1e80c93 100644 --- a/src/faebryk/library/PJ398SM.py +++ b/src/faebryk/library/PJ398SM.py @@ -1,22 +1,14 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.Electrical import Electrical -from faebryk.library.has_designator_prefix_defined import ( - has_designator_prefix_defined, -) +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L class PJ398SM(Module): - def __init__(self) -> None: - super().__init__() + tip: F.Electrical + sleeve: F.Electrical + switch: F.Electrical - class _IFs(Module.IFS()): - tip = Electrical() - sleeve = Electrical() - switch = Electrical() - - self.IFs = _IFs(self) - - self.add_trait(has_designator_prefix_defined("J")) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("J") diff --git a/src/faebryk/library/PM1006.py b/src/faebryk/library/PM1006.py index 351f6652..df12656c 100644 --- a/src/faebryk/library/PM1006.py +++ b/src/faebryk/library/PM1006.py @@ -1,17 +1,10 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from dataclasses import dataclass, field -from faebryk.core.core import Module, Parameter -from faebryk.library.Constant import Constant -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_esphome_config import has_esphome_config -from faebryk.library.is_esphome_bus import is_esphome_bus -from faebryk.library.Range import Range -from faebryk.library.TBD import TBD -from faebryk.library.UART_Base import UART_Base +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import P @@ -32,58 +25,41 @@ class PM1006(Module): or UART signal. """ - @dataclass - class _pm1006_esphome_config(has_esphome_config.impl()): - update_interval_s: Parameter = field(default_factory=TBD) - - def __post_init__(self) -> None: - super().__init__() + class _pm1006_esphome_config(F.has_esphome_config.impl()): + update_interval: F.TBD def get_config(self) -> dict: - assert isinstance( - self.update_interval_s, Constant - ), "No update interval set!" + val = self.update_interval.get_most_narrow() + assert isinstance(val, F.Constant), "No update interval set!" - obj = self.get_obj() + obj = self.obj assert isinstance(obj, PM1006), "This is not an PM1006!" - uart = is_esphome_bus.find_connected_bus(obj.IFs.data) + uart = F.is_esphome_bus.find_connected_bus(obj.data) return { "sensor": [ { "platform": "pm1006", - "update_interval": f"{self.update_interval_s.value}s", - "uart_id": uart.get_trait(is_esphome_bus).get_bus_id(), + "update_interval": f"{val.value.to('s')}", + "uart_id": uart.get_trait(F.is_esphome_bus).get_bus_id(), } ] } - def __init__(self) -> None: - super().__init__() - - class _IFs(Module.IFS()): - power = ElectricPower() - data = UART_Base() - - self.IFs = _IFs(self) - - # components - class _NODEs(Module.NODES()): ... - - self.NODEs = _NODEs(self) - # --------------------------------------------------------------------- + esphome_config: _pm1006_esphome_config - self.add_trait( - has_datasheet_defined( - "http://www.jdscompany.co.kr/download.asp?gubun=07&filename=PM1006_F.LED_PARTICLE_SENSOR_MODULE_SPECIFICATIONS.pdf" - ) - ) + power: F.ElectricPower + data: F.UART_Base - self.esphome = self._pm1006_esphome_config() - self.add_trait(self.esphome) - # --------------------------------------------------------------------- + # components - self.IFs.power.PARAMs.voltage.merge(Range.from_center(5, 0.2)) + # --------------------------------------------------------------------- + datasheet = L.f_field(F.has_datasheet_defined)( + "http://www.jdscompany.co.kr/download.asp?gubun=07&filename=PM1006_LED_PARTICLE_SENSOR_MODULE_SPECIFICATIONS.pdf" + ) + # --------------------------------------------------------------------- - self.IFs.data.PARAMs.baud.merge(Constant(9600 * P.baud)) + def __preinit__(self): + self.power.voltage.merge(F.Range.from_center(5, 0.2)) + self.data.baud.merge(F.Constant(9600 * P.baud)) diff --git a/src/faebryk/library/Pad.py b/src/faebryk/library/Pad.py index e41cf884..e7ac5703 100644 --- a/src/faebryk/library/Pad.py +++ b/src/faebryk/library/Pad.py @@ -2,27 +2,17 @@ # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface -from faebryk.core.util import get_parent_of_type -from faebryk.library.Electrical import Electrical -from faebryk.library.Footprint import Footprint +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface class Pad(ModuleInterface): - def __init__(self) -> None: - super().__init__() + net: F.Electrical + pcb: ModuleInterface - class _IFS(super().IFS()): - net = Electrical() - pcb = ModuleInterface() - - self.IFs = _IFS(self) - - def attach(self, intf: Electrical): - from faebryk.library.has_linked_pad_defined import has_linked_pad_defined - - self.IFs.net.connect(intf) - intf.add_trait(has_linked_pad_defined(self)) + def attach(self, intf: F.Electrical): + self.net.connect(intf) + intf.add_trait(F.has_linked_pad_defined(self)) @staticmethod def find_pad_for_intf_with_parent_that_has_footprint_unique( @@ -37,23 +27,23 @@ def find_pad_for_intf_with_parent_that_has_footprint_unique( def find_pad_for_intf_with_parent_that_has_footprint( intf: ModuleInterface, ) -> list["Pad"]: - from faebryk.library.has_linked_pad import has_linked_pad - # This only finds directly attached pads # -> misses from parents / children nodes - if intf.has_trait(has_linked_pad): - return [intf.get_trait(has_linked_pad).get_pad()] + if intf.has_trait(F.has_linked_pad): + return [intf.get_trait(F.has_linked_pad).get_pad()] # This is a bit slower, but finds them all - _, footprint = Footprint.get_footprint_of_parent(intf) + _, footprint = F.Footprint.get_footprint_of_parent(intf) pads = [ pad - for pad in footprint.IFs.get_all() - if isinstance(pad, Pad) and pad.IFs.net.is_connected_to(intf) is not None + for pad in footprint.get_children(direct_only=True, types=Pad) + if pad.net.is_connected_to(intf) is not None ] return pads - def get_fp(self) -> Footprint: - fp = get_parent_of_type(self, Footprint) + def get_fp(self) -> F.Footprint: + from faebryk.core.util import get_parent_of_type + + fp = get_parent_of_type(self, F.Footprint) assert fp return fp diff --git a/src/faebryk/library/Potentiometer.py b/src/faebryk/library/Potentiometer.py index 1bb6642b..587859d6 100644 --- a/src/faebryk/library/Potentiometer.py +++ b/src/faebryk/library/Potentiometer.py @@ -1,43 +1,28 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.Electrical import Electrical -from faebryk.library.Resistor import Resistor -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import Quantity -from faebryk.libs.util import times class Potentiometer(Module): - def __init__(self) -> None: - super().__init__() + resistors_ifs = L.list_field(2, F.Electrical) + wiper: F.Electrical + total_resistance: F.TBD[Quantity] + resistors = L.list_field(2, F.Resistor) - class _IFs(Module.IFS()): - resistors = times(2, Electrical) - wiper = Electrical() - - self.IFs = _IFs(self) - - class _PARAMs(Module.PARAMS()): - total_resistance = TBD[Quantity]() - - self.PARAMs = _PARAMs(self) - - class _NODEs(Module.NODES()): - resistors = times(2, Resistor) - - self.NODEs = _NODEs(self) - - for i, resistor in enumerate(self.NODEs.resistors): - self.IFs.resistors[i].connect_via(resistor, self.IFs.wiper) + def __preinit__(self): + for i, resistor in enumerate(self.resistors): + self.resistors_ifs[i].connect_via(resistor, self.wiper) # TODO use range(0, total_resistance) - resistor.PARAMs.resistance.merge(self.PARAMs.total_resistance) + resistor.resistance.merge(self.total_resistance) def connect_as_voltage_divider( - self, high: Electrical, low: Electrical, out: Electrical + self, high: F.Electrical, low: F.Electrical, out: F.Electrical ): - self.IFs.resistors[0].connect(high) - self.IFs.resistors[1].connect(low) - self.IFs.wiper.connect(out) + self.resistors_ifs[0].connect(high) + self.resistors_ifs[1].connect(low) + self.wiper.connect(out) diff --git a/src/faebryk/library/Power.py b/src/faebryk/library/Power.py index 4a6e546c..abf48d0a 100644 --- a/src/faebryk/library/Power.py +++ b/src/faebryk/library/Power.py @@ -1,7 +1,22 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface +from faebryk.core.moduleinterface import ModuleInterface -class Power(ModuleInterface): ... +class Power(ModuleInterface): + class PowerSourcesShortedError(Exception): ... + + class is_power_source(ModuleInterface.TraitT): ... + + class is_power_source_defined(is_power_source.impl()): ... + + def make_source(self): + self.add(self.is_power_source_defined()) + return self + + def _on_connect(self, other: "Power"): + if self.has_trait(self.is_power_source) and other.has_trait( + self.is_power_source + ): + raise self.PowerSourcesShortedError(self, other) diff --git a/src/faebryk/library/PowerSwitch.py b/src/faebryk/library/PowerSwitch.py index 102ac2b9..f264e8b2 100644 --- a/src/faebryk/library/PowerSwitch.py +++ b/src/faebryk/library/PowerSwitch.py @@ -1,10 +1,9 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.can_switch_power_defined import can_switch_power_defined -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L class PowerSwitch(Module): @@ -18,21 +17,17 @@ class PowerSwitch(Module): def __init__(self, normally_closed: bool) -> None: super().__init__() - self.normally_closed = normally_closed + self._normally_closed = normally_closed - class _IFs(Module.IFS()): - logic_in = ElectricLogic() - power_in = ElectricPower() - switched_power_out = ElectricPower() + logic_in: F.ElectricLogic + power_in: F.ElectricPower + switched_power_out: F.ElectricPower - self.IFs = _IFs(self) - - self.add_trait( - can_switch_power_defined( - self.IFs.power_in, self.IFs.switched_power_out, self.IFs.logic_in - ) + @L.rt_field + def switch_power(self): + return F.can_switch_power_defined( + self.power_in, self.switched_power_out, self.logic_in ) - self.IFs.switched_power_out.PARAMs.voltage.merge( - self.IFs.power_in.PARAMs.voltage - ) + def __preinit__(self): + self.switched_power_out.voltage.merge(self.power_in.voltage) diff --git a/src/faebryk/library/PowerSwitchMOSFET.py b/src/faebryk/library/PowerSwitchMOSFET.py index 42a72fc2..2dc452ce 100644 --- a/src/faebryk/library/PowerSwitchMOSFET.py +++ b/src/faebryk/library/PowerSwitchMOSFET.py @@ -1,14 +1,10 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.Constant import Constant -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.MOSFET import MOSFET -from faebryk.library.PowerSwitch import PowerSwitch +import faebryk.library._F as F -class PowerSwitchMOSFET(PowerSwitch): +class PowerSwitchMOSFET(F.PowerSwitch): """ Power switch using a MOSFET @@ -18,23 +14,22 @@ class PowerSwitchMOSFET(PowerSwitch): def __init__(self, lowside: bool, normally_closed: bool) -> None: super().__init__(normally_closed=normally_closed) - self.lowside = lowside + self._lowside = lowside - # components - class _NODEs(Module.NODES()): - mosfet = MOSFET() + # components - self.NODEs = _NODEs(self) + mosfet: F.MOSFET - self.NODEs.mosfet.PARAMs.channel_type.merge( - Constant( - MOSFET.ChannelType.N_CHANNEL - if lowside - else MOSFET.ChannelType.P_CHANNEL + def __preinit__(self): + self.mosfet.channel_type.merge( + F.Constant( + F.MOSFET.ChannelType.N_CHANNEL + if self._lowside + else F.MOSFET.ChannelType.P_CHANNEL ) ) - self.NODEs.mosfet.PARAMs.saturation_type.merge( - Constant(MOSFET.SaturationType.ENHANCEMENT) + self.mosfet.saturation_type.merge( + F.Constant(F.MOSFET.SaturationType.ENHANCEMENT) ) # pull gate @@ -43,24 +38,18 @@ class _NODEs(Module.NODES()): # True False False # False True False # False False True - self.IFs.logic_in.get_trait(ElectricLogic.can_be_pulled).pull( - lowside == normally_closed - ) + self.logic_in.pulled.pull(self._lowside == self._normally_closed) # connect gate to logic - self.IFs.logic_in.IFs.signal.connect(self.NODEs.mosfet.IFs.gate) + self.logic_in.signal.connect(self.mosfet.gate) # passthrough non-switched side, bridge switched side - if lowside: - self.IFs.power_in.IFs.hv.connect(self.IFs.switched_power_out.IFs.hv) - self.IFs.power_in.IFs.lv.connect_via( - self.NODEs.mosfet, self.IFs.switched_power_out.IFs.lv - ) + if self._lowside: + self.power_in.hv.connect(self.switched_power_out.hv) + self.power_in.lv.connect_via(self.mosfet, self.switched_power_out.lv) else: - self.IFs.power_in.IFs.lv.connect(self.IFs.switched_power_out.IFs.lv) - self.IFs.power_in.IFs.hv.connect_via( - self.NODEs.mosfet, self.IFs.switched_power_out.IFs.hv - ) + self.power_in.lv.connect(self.switched_power_out.lv) + self.power_in.hv.connect_via(self.mosfet, self.switched_power_out.hv) # TODO do more with logic # e.g check reference being same as power diff --git a/src/faebryk/library/PowerSwitchStatic.py b/src/faebryk/library/PowerSwitchStatic.py index 62c21d2e..20ea6c98 100644 --- a/src/faebryk/library/PowerSwitchStatic.py +++ b/src/faebryk/library/PowerSwitchStatic.py @@ -1,19 +1,19 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT +import faebryk.library._F as F -from faebryk.library.PowerSwitch import PowerSwitch - -class PowerSwitchStatic(PowerSwitch): +class PowerSwitchStatic(F.PowerSwitch): """ A power switch that bridges power through statically - This is useful when transforming an ElectricLogic to an ElectricPower + This is useful when transforming an F.ElectricLogic to an F.ElectricPower """ def __init__(self) -> None: super().__init__(normally_closed=False) - self.IFs.power_in.connect(self.IFs.switched_power_out) - self.IFs.logic_in.connect_reference(self.IFs.power_in) - self.IFs.logic_in.set(True) + def __preinit__(self): + self.power_in.connect(self.switched_power_out) + self.logic_in.connect_reference(self.power_in) + self.logic_in.set(True) diff --git a/src/faebryk/library/PoweredLED.py b/src/faebryk/library/PoweredLED.py index fbb73001..95db8264 100644 --- a/src/faebryk/library/PoweredLED.py +++ b/src/faebryk/library/PoweredLED.py @@ -1,34 +1,25 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.LED import LED -from faebryk.library.Resistor import Resistor +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L class PoweredLED(Module): - def __init__(self) -> None: - super().__init__() - - class _IFs(Module.IFS()): - power = ElectricPower() - - self.IFs = _IFs(self) - - class _NODEs(Module.NODES()): - current_limiting_resistor = Resistor() - led = LED() - - self.NODEs = _NODEs(self) - - self.IFs.power.IFs.hv.connect(self.NODEs.led.IFs.anode) - self.NODEs.led.connect_via_current_limiting_resistor_to_power( - self.NODEs.current_limiting_resistor, - self.IFs.power, + power: F.ElectricPower + current_limiting_resistor: F.Resistor + led: F.LED + + def __preinit__(self): + self.power.hv.connect(self.led.anode) + self.led.connect_via_current_limiting_resistor_to_power( + self.current_limiting_resistor, + self.power, low_side=True, ) + self.current_limiting_resistor.allow_removal_if_zero() - self.add_trait(can_bridge_defined(self.IFs.power.IFs.hv, self.IFs.power.IFs.lv)) - self.NODEs.current_limiting_resistor.allow_removal_if_zero() + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(self.power.hv, self.power.lv) diff --git a/src/faebryk/library/Powered_Relay.py b/src/faebryk/library/Powered_Relay.py index 855bc3d3..91dca3fe 100644 --- a/src/faebryk/library/Powered_Relay.py +++ b/src/faebryk/library/Powered_Relay.py @@ -3,66 +3,43 @@ import logging -from faebryk.core.core import Module -from faebryk.library.Diode import Diode -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.PoweredLED import PoweredLED -from faebryk.library.PowerSwitchMOSFET import PowerSwitchMOSFET -from faebryk.library.Relay import Relay +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L logger = logging.getLogger(__name__) class Powered_Relay(Module): - def __init__(self) -> None: - super().__init__() - - class _NODEs(Module.NODES()): - relay = Relay() - indicator = PoweredLED() - flyback_diode = Diode() - relay_driver = PowerSwitchMOSFET(lowside=True, normally_closed=False) - - self.NODEs = _NODEs(self) - - class _IFs(Module.IFS()): - switch_a_nc = Electrical() - switch_a_common = Electrical() - switch_a_no = Electrical() - switch_b_no = Electrical() - switch_b_common = Electrical() - switch_b_nc = Electrical() - enable = ElectricLogic() - power = ElectricPower() - - self.IFs = _IFs(self) - - class _PARAMs(Module.PARAMS()): ... - - self.PARAMs = _PARAMs(self) - - self.NODEs.relay.IFs.switch_a_common.connect(self.IFs.switch_a_common) - self.NODEs.relay.IFs.switch_a_nc.connect(self.IFs.switch_a_nc) - self.NODEs.relay.IFs.switch_a_no.connect(self.IFs.switch_a_no) - self.NODEs.relay.IFs.switch_b_common.connect(self.IFs.switch_b_common) - self.NODEs.relay.IFs.switch_b_nc.connect(self.IFs.switch_b_nc) - self.NODEs.relay.IFs.switch_b_no.connect(self.IFs.switch_b_no) - - self.NODEs.relay_driver.IFs.power_in.connect(self.IFs.power) - self.NODEs.relay_driver.IFs.logic_in.connect(self.IFs.enable) - self.NODEs.relay_driver.IFs.switched_power_out.IFs.lv.connect( - self.NODEs.relay.IFs.coil_n - ) - self.NODEs.relay_driver.IFs.switched_power_out.IFs.hv.connect( - self.NODEs.relay.IFs.coil_p - ) - - self.NODEs.relay.IFs.coil_n.connect_via( - self.NODEs.flyback_diode, self.NODEs.relay.IFs.coil_p - ) - - self.NODEs.indicator.IFs.power.connect( - self.NODEs.relay_driver.IFs.switched_power_out + relay: F.Relay + indicator: F.PoweredLED + flyback_diode: F.Diode + relay_driver = L.f_field(F.PowerSwitchMOSFET)(lowside=True, normally_closed=False) + + switch_a_nc: F.Electrical + switch_a_common: F.Electrical + switch_a_no: F.Electrical + switch_b_no: F.Electrical + switch_b_common: F.Electrical + switch_b_nc: F.Electrical + enable: F.ElectricLogic + power: F.ElectricPower + + def __preinit__(self): + from faebryk.core.util import connect_module_mifs_by_name + + connect_module_mifs_by_name(self, self.relay, allow_partial=True) + + self.relay_driver.power_in.connect(self.power) + self.relay_driver.logic_in.connect(self.enable) + self.relay_driver.switched_power_out.lv.connect(self.relay.coil_n) + self.relay_driver.switched_power_out.hv.connect(self.relay.coil_p) + + self.relay.coil_n.connect_via(self.flyback_diode, self.relay.coil_p) + self.indicator.power.connect(self.relay_driver.switched_power_out) + + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self, gnd_only=True) ) diff --git a/src/faebryk/library/QFN.py b/src/faebryk/library/QFN.py index b55d0b72..b0a22d18 100644 --- a/src/faebryk/library/QFN.py +++ b/src/faebryk/library/QFN.py @@ -1,15 +1,14 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.can_attach_via_pinmap_equal import can_attach_via_pinmap_equal -from faebryk.library.Footprint import Footprint -from faebryk.library.has_equal_pins_in_ifs import has_equal_pins_in_ifs -from faebryk.library.Pad import Pad + +import faebryk.library._F as F +from faebryk.libs.library import L from faebryk.libs.units import P, Quantity from faebryk.libs.util import times -class QFN(Footprint): +class QFN(F.Footprint): def __init__( self, pin_cnt: int, @@ -21,33 +20,40 @@ def __init__( ) -> None: super().__init__() - class _IFs(Footprint.IFS()): - pins = times(pin_cnt, Pad) + self._pin_cnt = pin_cnt + self._exposed_thermal_pad_cnt = exposed_thermal_pad_cnt + self._size_xy = size_xy + self._pitch = pitch + self._exposed_thermal_pad_dimensions = exposed_thermal_pad_dimensions + self._has_thermal_vias = has_thermal_vias - self.IFs = _IFs(self) assert exposed_thermal_pad_cnt > 0 or not has_thermal_vias assert ( exposed_thermal_pad_dimensions[0] < size_xy[0] and exposed_thermal_pad_dimensions[1] < size_xy[1] ) - from faebryk.library.has_kicad_footprint_equal_ifs import ( - has_kicad_footprint_equal_ifs, - ) - class _has_kicad_footprint(has_kicad_footprint_equal_ifs): + @L.rt_field + def pins(self): + return times(self._pin_cnt, F.Pad) + + equal_pins: F.has_equal_pins_in_ifs + attach_via_pinmap: F.can_attach_via_pinmap_equal + + @L.rt_field + def kicad_footprint(self): + class _has_kicad_footprint(F.has_kicad_footprint_equal_ifs): @staticmethod def get_kicad_footprint() -> str: return "Package_DFN_QFN:QFN-{leads}-{ep}EP_{size_x}x{size_y}mm_P{pitch}mm_EP{ep_x}x{ep_y}mm{vias}".format( # noqa: E501 - leads=pin_cnt, - ep=exposed_thermal_pad_cnt, - size_x=size_xy[0].to(P.mm).m, - size_y=size_xy[1].to(P.mm).m, - pitch=pitch.to(P.mm).m, - ep_x=exposed_thermal_pad_dimensions[0].to(P.mm).m, - ep_y=exposed_thermal_pad_dimensions[1].to(P.mm).m, - vias="_ThermalVias" if has_thermal_vias else "", + leads=self._pin_cnt, + ep=self._exposed_thermal_pad_cnt, + size_x=self._size_xy[0].to(P.mm).m, + size_y=self._size_xy[1].to(P.mm).m, + pitch=self._pitch.to(P.mm).m, + ep_x=self._exposed_thermal_pad_dimensions[0].to(P.mm).m, + ep_y=self._exposed_thermal_pad_dimensions[1].to(P.mm).m, + vias="_ThermalVias" if self._has_thermal_vias else "", ) - self.add_trait(_has_kicad_footprint()) - self.add_trait(has_equal_pins_in_ifs()) - self.add_trait(can_attach_via_pinmap_equal()) + return _has_kicad_footprint() diff --git a/src/faebryk/library/QWIIC.py b/src/faebryk/library/QWIIC.py index 12831bbf..a2ae539c 100644 --- a/src/faebryk/library/QWIIC.py +++ b/src/faebryk/library/QWIIC.py @@ -1,37 +1,27 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.Constant import Constant -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_designator_prefix_defined import ( - has_designator_prefix_defined, -) -from faebryk.library.I2C import I2C +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import P class QWIIC(Module): """ Sparkfun QWIIC connection spec. Also compatible with Adafruits STEMMA QT. - Delivers 3.3V power + I2C over JST SH 1mm pitch 4 pin connectors + Delivers 3.3V power + F.I2C over JST SH 1mm pitch 4 pin connectors """ - def __init__(self) -> None: - super().__init__() - - # interfaces - class _IFs(Module.IFS()): - i2c = I2C() - power = ElectricPower() - - self.IFs = _IFs(self) + # interfaces + i2c: F.I2C + power: F.ElectricPower + def __preinit__(self): # set constraints - self.IFs.power.PARAMs.voltage.merge(Constant(3.3 * P.V)) - # TODO: self.IFs.power.PARAMs.source_current.merge(Constant(226 * P.mA)) + self.power.voltage.merge(F.Constant(3.3 * P.V)) + # TODO: self.power.source_current.merge(F.Constant(226 * P.mA)) - self.add_trait(has_designator_prefix_defined("J")) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("J") - self.add_trait(has_datasheet_defined("https://www.sparkfun.com/qwiic")) + datasheet = L.f_field(F.has_datasheet_defined)("https://www.sparkfun.com/qwiic") diff --git a/src/faebryk/library/QWIIC_Connector.py b/src/faebryk/library/QWIIC_Connector.py index 48bc1e82..644de83f 100644 --- a/src/faebryk/library/QWIIC_Connector.py +++ b/src/faebryk/library/QWIIC_Connector.py @@ -3,30 +3,15 @@ import logging -from faebryk.core.core import Module -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.I2C import I2C +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L logger = logging.getLogger(__name__) class QWIIC_Connector(Module): - def __init__(self) -> None: - super().__init__() + power: F.ElectricPower + i2c: F.I2C - class _NODEs(Module.NODES()): ... - - self.NODEs = _NODEs(self) - - class _IFs(Module.IFS()): - power = ElectricPower() - i2c = I2C() - - self.IFs = _IFs(self) - - class _PARAMs(Module.PARAMS()): ... - - self.PARAMs = _PARAMs(self) - - self.add_trait(has_designator_prefix_defined("J")) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("J") diff --git a/src/faebryk/library/RJ45_Receptacle.py b/src/faebryk/library/RJ45_Receptacle.py index aec77159..fe311f50 100644 --- a/src/faebryk/library/RJ45_Receptacle.py +++ b/src/faebryk/library/RJ45_Receptacle.py @@ -3,11 +3,9 @@ from enum import Enum, auto -from faebryk.core.core import Module -from faebryk.library.Electrical import Electrical -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.TBD import TBD -from faebryk.libs.util import times +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L class RJ45_Receptacle(Module): @@ -15,19 +13,10 @@ class Mounting(Enum): TH = auto() SMD = auto() - def __init__(self) -> None: - super().__init__() + # interfaces - # interfaces - class _IFs(Module.IFS()): - pin = times(8, Electrical) - shield = Electrical() + pin = L.list_field(8, F.Electrical) + shield: F.Electrical - self.IFs = _IFs(self) - - self.add_trait(has_designator_prefix_defined("J")) - - class _PARAMS(super().PARAMS()): - mounting = TBD[self.Mounting]() - - self.PARAMs = _PARAMS(self) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("J") + mounting: F.TBD[Mounting] diff --git a/src/faebryk/library/RP2040.py b/src/faebryk/library/RP2040.py index 0f998e3e..de4f6323 100644 --- a/src/faebryk/library/RP2040.py +++ b/src/faebryk/library/RP2040.py @@ -3,76 +3,125 @@ import logging -from faebryk.core.core import Module -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.I2C import I2C -from faebryk.library.MultiSPI import MultiSPI -from faebryk.library.SWD import SWD -from faebryk.library.UART_Base import UART_Base -from faebryk.library.USB2_0 import USB2_0 -from faebryk.libs.util import times +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L +from faebryk.libs.units import P logger = logging.getLogger(__name__) class RP2040(Module): - def __init__(self) -> None: - super().__init__() - - class _NODEs(Module.NODES()): ... - - self.NODEs = _NODEs(self) - - class _IFs(Module.IFS()): - io_vdd = ElectricPower() - adc_vdd = ElectricPower() - core_vdd = ElectricPower() - vreg_in = ElectricPower() - vreg_out = ElectricPower() - power_vusb = ElectricPower() - gpio = times(30, Electrical) - run = ElectricLogic() - usb = USB2_0() - qspi = MultiSPI(data_lane_count=4) - xin = Electrical() - xout = Electrical() - test = Electrical() - swd = SWD() - # TODO: these peripherals and more can be mapped to different pins - i2c = I2C() - uart = UART_Base() - - self.IFs = _IFs(self) - - class _PARAMs(Module.PARAMS()): ... - - self.PARAMs = _PARAMs(self) + io_vdd: F.ElectricPower + adc_vdd: F.ElectricPower + core_vdd: F.ElectricPower + vreg_in: F.ElectricPower + vreg_out: F.ElectricPower + power_vusb: F.ElectricPower + gpio = L.list_field(30, F.Electrical) + run: F.ElectricLogic + usb: F.USB2_0 + qspi = L.f_field(F.MultiSPI)(data_lane_count=4) + xin: F.Electrical + xout: F.Electrical + test: F.Electrical + swd: F.SWD + # TODO: these peripherals and more can be mapped to different pins + i2c: F.I2C + uart: F.UART_Base + def __preinit__(self): + # TODO + return # decouple power rails and connect GNDs toghether - gnd = self.IFs.io_vdd.IFs.lv + gnd = self.io_vdd.lv for pwrrail in [ - self.IFs.io_vdd, - self.IFs.adc_vdd, - self.IFs.core_vdd, - self.IFs.vreg_in, - self.IFs.vreg_out, - self.IFs.usb.IFs.usb_if.IFs.buspower, + self.io_vdd, + self.adc_vdd, + self.core_vdd, + self.vreg_in, + self.vreg_out, + self.usb.usb_if.buspower, ]: - pwrrail.IFs.lv.connect(gnd) - pwrrail.get_trait(can_be_decoupled).decouple() + pwrrail.lv.connect(gnd) + pwrrail.decoupled.decouple() - self.add_trait(has_designator_prefix_defined("U")) + # set parameters + self.vreg_out.voltage.merge(1.1 * P.V) + self.io_vdd.voltage.merge(3.3 * P.V) - self.add_trait( - has_datasheet_defined( - "https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf" + F.ElectricLogic.connect_all_node_references( + self.get_children(direct_only=True, types=ModuleInterface).difference( + {self.adc_vdd, self.core_vdd} ) ) - # set parameters - # self.IFs.io_vdd.PARAMs.voltage.merge(Range(1.8*P.V, 3.63*P.V)) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") + datasheet = L.f_field(F.has_datasheet_defined)( + "https://datasheets.raspberrypi.com/rp2040/rp2040-datasheet.pdf" + ) + + @L.rt_field + def attach_to_footprint(self): + return F.can_attach_to_footprint_via_pinmap( + { + "1": self.io_vdd.hv, + "2": self.gpio[0], + "3": self.gpio[1], + "4": self.gpio[2], + "5": self.gpio[3], + "6": self.gpio[4], + "7": self.gpio[5], + "8": self.gpio[6], + "9": self.gpio[7], + "10": self.io_vdd.hv, + "11": self.gpio[8], + "12": self.gpio[9], + "13": self.gpio[10], + "14": self.gpio[11], + "15": self.gpio[12], + "16": self.gpio[13], + "17": self.gpio[14], + "18": self.gpio[15], + "19": self.xin, + "20": self.xout, + "21": self.test, + "22": self.io_vdd.hv, + "23": self.core_vdd.hv, + "24": self.swd.clk.signal, + "25": self.swd.dio.signal, + "26": self.run.signal, + "27": self.gpio[16], + "28": self.gpio[17], + "29": self.gpio[18], + "30": self.gpio[19], + "31": self.gpio[20], + "32": self.gpio[21], + "33": self.io_vdd.hv, + "34": self.gpio[22], + "35": self.gpio[23], + "36": self.gpio[24], + "37": self.gpio[25], + "38": self.gpio[26], + "39": self.gpio[27], + "40": self.gpio[28], + "41": self.gpio[29], + "42": self.io_vdd.hv, + "43": self.adc_vdd.hv, + "44": self.vreg_in.hv, + "45": self.vreg_out.hv, + "46": self.usb.usb_if.d.n, + "47": self.usb.usb_if.d.p, + "48": self.usb.usb_if.buspower.hv, + "49": self.io_vdd.hv, + "50": self.core_vdd.hv, + "51": self.qspi.data[3].signal, + "52": self.qspi.clk.signal, + "53": self.qspi.data[0].signal, + "54": self.qspi.data[2].signal, + "55": self.qspi.data[1].signal, + "56": self.qspi.cs.signal, + "57": self.io_vdd.lv, + } + ) diff --git a/src/faebryk/library/RP2040_Reference_Design.py b/src/faebryk/library/RP2040_Reference_Design.py index f40e9c07..7873e96c 100644 --- a/src/faebryk/library/RP2040_Reference_Design.py +++ b/src/faebryk/library/RP2040_Reference_Design.py @@ -3,19 +3,11 @@ import logging -from faebryk.core.core import Module -from faebryk.library.Constant import Constant -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.LED import LED -from faebryk.library.PoweredLED import PoweredLED -from faebryk.library.Resistor import Resistor -from faebryk.library.RP2040 import RP2040 -from faebryk.library.SPIFlash import SPIFlash -from faebryk.library.USB2_0 import USB2_0 +import faebryk.library._F as F +from faebryk.core.module import Module from faebryk.libs.brightness import TypicalLuminousIntensity +from faebryk.libs.library import L from faebryk.libs.units import P -from faebryk.libs.util import times logger = logging.getLogger(__name__) @@ -24,92 +16,201 @@ class RP2040_Reference_Design(Module): """Minimal required design for the Raspberry Pi RP2040 microcontroller. Based on the official Raspberry Pi RP2040 hardware design guidlines""" - def __init__(self) -> None: - super().__init__() - - # ---------------------------------------- - # modules, interfaces, parameters - # ---------------------------------------- - class _IFs(Module.IFS()): - power = ElectricPower() - usb = USB2_0() - - self.IFs = _IFs(self) - - class _NODES(Module.NODES()): - rp2040 = RP2040() - flash = SPIFlash() - led = PoweredLED() - usb_current_limmit_resistor = times(2, Resistor) - # TODO: add crystal oscillator - # TODO: add voltage divider with switch - # TODO: add boot button - # TODO: add reset button - # TODO: add optional LM4040 voltage reference or voltage divider - - self.NODEs = _NODES(self) - - class _PARAMs(Module.PARAMS()): ... - - self.PARAMs = _PARAMs(self) - + # ---------------------------------------- + # modules, interfaces, parameters + # ---------------------------------------- + + usb: F.USB2_0 + + rp2040: F.RP2040 + flash: F.SPIFlash + led: F.PoweredLED + usb_current_limit_resistor = L.list_field(2, F.Resistor) + reset_button: F.Button + boot_button: F.Button + boot_resistor: F.Resistor + ldo: F.LDO + crystal_oscillator: F.Crystal_Oscillator + oscillator_resistor: F.Resistor + + # TODO: add voltage divider with switch + # TODO: add optional LM4040 voltage reference or voltage divider + + def __preinit__(self): + # TODO + return # ---------------------------------------- # aliasess # ---------------------------------------- - gnd = self.IFs.power.IFs.lv + power_3v3 = self.ldo.power_out + power_5v = self.ldo.power_in + power_vbus = self.usb.usb_if.buspower + gnd = power_vbus.lv # ---------------------------------------- # parametrization # ---------------------------------------- - self.IFs.power.PARAMs.voltage.merge(Constant(3.3 * P.V)) + self.ldo.output_voltage.merge(3.3 * P.V) + self.ldo.output_current.merge(600 * P.mA) + self.crystal_oscillator.crystal.frequency.merge(12 * P.MHz) + self.crystal_oscillator.crystal.load_impedance.merge(10 * P.pF) - self.NODEs.flash.PARAMs.memory_size.merge(Constant(16 * P.Mbit)) + # for cap in self.crystal_oscillator.capacitors: + # cap.capacitance.merge(15 * P.pF) # TODO: remove? - self.NODEs.led.NODEs.led.PARAMs.color.merge(LED.Color.GREEN) - self.NODEs.led.NODEs.led.PARAMs.brightness.merge( + self.oscillator_resistor.resistance.merge(F.Constant(1 * P.kohm)) + + self.flash.memory_size.merge(F.Constant(16 * P.Mbit)) + + self.led.led.color.merge(F.LED.Color.GREEN) + self.led.led.brightness.merge( TypicalLuminousIntensity.APPLICATION_LED_INDICATOR_INSIDE.value.value ) + # TODO: remove: #poweredled voltage merge issue + self.led.power.voltage.merge(power_3v3.voltage) - self.NODEs.usb_current_limmit_resistor[0].PARAMs.resistance.merge( - Constant(27 * P.ohm) - ) - self.NODEs.usb_current_limmit_resistor[1].PARAMs.resistance.merge( - Constant(27 * P.ohm) - ) + self.usb_current_limit_resistor[0].resistance.merge(F.Constant(27 * P.ohm)) + self.usb_current_limit_resistor[1].resistance.merge(F.Constant(27 * P.ohm)) # ---------------------------------------- # connections # ---------------------------------------- # connect power rails - main_power_rail = self.IFs.power + power_vbus.connect(power_5v) + + # connect rp2040 power rails for pwrrail in [ - self.NODEs.rp2040.IFs.io_vdd, - self.NODEs.rp2040.IFs.adc_vdd, - self.NODEs.rp2040.IFs.vreg_in, - self.NODEs.rp2040.IFs.usb.IFs.usb_if.IFs.buspower, + self.rp2040.io_vdd, + self.rp2040.adc_vdd, + self.rp2040.vreg_in, + self.rp2040.usb.usb_if.buspower, ]: - pwrrail.connect(main_power_rail) + pwrrail.connect(power_3v3) - self.NODEs.rp2040.IFs.vreg_out.connect(self.NODEs.rp2040.IFs.core_vdd) + self.rp2040.vreg_out.connect(self.rp2040.core_vdd) # connect flash - self.NODEs.flash.IFs.spi.connect(self.NODEs.rp2040.IFs.qspi) - self.NODEs.flash.IFs.power.connect(main_power_rail) + self.flash.spi.connect(self.rp2040.qspi) + self.flash.power.connect(power_3v3) # connect led - self.NODEs.rp2040.IFs.gpio[25].connect_via(self.NODEs.led, gnd) + self.rp2040.gpio[25].connect_via(self.led, gnd) + + # crystal oscillator + self.rp2040.xin.connect_via( + [self.crystal_oscillator, self.oscillator_resistor], + self.rp2040.xout, + ) + gnd.connect(self.crystal_oscillator.power.lv) + + # buttons + self.rp2040.qspi.cs.signal.connect_via( + [self.boot_resistor, self.boot_button], gnd + ) + self.boot_resistor.resistance.merge(F.Constant(1 * P.kohm)) + self.rp2040.run.signal.connect_via(self.reset_button, gnd) # connect usb - self.IFs.usb.IFs.usb_if.IFs.d.IFs.p.connect_via( - self.NODEs.usb_current_limmit_resistor[0], - self.NODEs.rp2040.IFs.usb.IFs.usb_if.IFs.d.IFs.p, + self.usb.usb_if.d.p.connect_via( + self.usb_current_limit_resistor[0], + self.rp2040.usb.usb_if.d.p, ) - self.IFs.usb.IFs.usb_if.IFs.d.IFs.n.connect_via( - self.NODEs.usb_current_limmit_resistor[1], - self.NODEs.rp2040.IFs.usb.IFs.usb_if.IFs.d.IFs.n, + self.usb.usb_if.d.n.connect_via( + self.usb_current_limit_resistor[1], + self.rp2040.usb.usb_if.d.n, ) - self.add_trait( - has_datasheet_defined( - "https://datasheets.raspberrypi.com/rp2040/hardware-design-with-rp2040.pdf" + datasheet = L.f_field(F.has_datasheet_defined)( + "https://datasheets.raspberrypi.com/rp2040/hardware-design-with-rp2040.pdf" + ) + + @L.rt_field + def pcb_layout(self): + from faebryk.exporters.pcb.layout.absolute import LayoutAbsolute + from faebryk.exporters.pcb.layout.extrude import LayoutExtrude + from faebryk.exporters.pcb.layout.typehierarchy import LayoutTypeHierarchy + + Point = F.has_pcb_position.Point + L = F.has_pcb_position.layer_type + + return F.has_pcb_layout_defined( + layout=LayoutTypeHierarchy( + layouts=[ + LayoutTypeHierarchy.Level( + mod_type=F.RP2040, + layout=LayoutAbsolute( + Point((0, 0, 0, L.NONE)), + ), + ), + LayoutTypeHierarchy.Level( + mod_type=F.LDO, + layout=LayoutAbsolute(Point((0, 14, 0, L.NONE))), + ), + LayoutTypeHierarchy.Level( + mod_type=F.Button, + layout=LayoutExtrude( + base=Point((-1.75, -11.5, 0, L.NONE)), + vector=(3.5, 0, 90), + ), + ), + LayoutTypeHierarchy.Level( + mod_type=F.SPIFlash, + layout=LayoutAbsolute( + Point((-1.95, -6.5, 0, L.NONE)), + ), + ), + LayoutTypeHierarchy.Level( + mod_type=F.PoweredLED, + layout=LayoutAbsolute( + Point((6.5, -1.5, 270, L.NONE)), + ), + children_layout=LayoutTypeHierarchy( + layouts=[ + LayoutTypeHierarchy.Level( + mod_type=F.LED, + layout=LayoutAbsolute( + Point((0, 0, 0, L.NONE)), + ), + ), + LayoutTypeHierarchy.Level( + mod_type=F.Resistor, + layout=LayoutAbsolute( + Point((-2.75, 0, 180, L.NONE)) + ), + ), + ] + ), + ), + LayoutTypeHierarchy.Level( + mod_type=F.Crystal_Oscillator, + layout=LayoutAbsolute( + Point((0, 7, 0, L.NONE)), + ), + children_layout=LayoutTypeHierarchy( + layouts=[ + LayoutTypeHierarchy.Level( + mod_type=F.Crystal, + layout=LayoutAbsolute( + Point((0, 0, 0, L.NONE)), + ), + ), + LayoutTypeHierarchy.Level( + mod_type=F.Capacitor, + layout=LayoutExtrude( + base=Point((-3, 0, 90, L.NONE)), + vector=(0, 6, 180), + dynamic_rotation=True, + ), + ), + ] + ), + ), + LayoutTypeHierarchy.Level( + mod_type=F.Resistor, + layout=LayoutExtrude( + base=Point((0.75, -6, 0, L.NONE)), + vector=(1.25, 0, 270), + ), + ), + ] ) ) diff --git a/src/faebryk/library/RS232.py b/src/faebryk/library/RS232.py index 3f67a49e..33fad49f 100644 --- a/src/faebryk/library/RS232.py +++ b/src/faebryk/library/RS232.py @@ -1,22 +1,17 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module, ModuleInterface -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L class RS232(ModuleInterface): - def __init__(self) -> None: - super().__init__() + tx: F.ElectricLogic + rx: F.ElectricLogic - class IFS(Module.IFS()): - tx = ElectricLogic() - rx = ElectricLogic() - - self.IFs = IFS(self) - - ref = ElectricLogic.connect_all_module_references(self) - self.add_trait(has_single_electric_reference_defined(ref)) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) + ) diff --git a/src/faebryk/library/RS485.py b/src/faebryk/library/RS485.py index 33e61b3c..270b6a41 100644 --- a/src/faebryk/library/RS485.py +++ b/src/faebryk/library/RS485.py @@ -1,15 +1,9 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module, ModuleInterface -from faebryk.library.DifferentialPair import DifferentialPair +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface class RS485(ModuleInterface): - def __init__(self) -> None: - super().__init__() - - class IFS(Module.IFS()): - diff_pair = DifferentialPair() - - self.IFs = IFS(self) + diff_pair: F.DifferentialPair diff --git a/src/faebryk/library/RS485_Bus_Protection.py b/src/faebryk/library/RS485_Bus_Protection.py index 8ac32dc2..70c2a01c 100644 --- a/src/faebryk/library/RS485_Bus_Protection.py +++ b/src/faebryk/library/RS485_Bus_Protection.py @@ -3,26 +3,13 @@ import logging -from faebryk.core.core import Module +import faebryk.library._F as F +from faebryk.core.module import Module from faebryk.exporters.pcb.layout.absolute import LayoutAbsolute from faebryk.exporters.pcb.layout.extrude import LayoutExtrude from faebryk.exporters.pcb.layout.typehierarchy import LayoutTypeHierarchy -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.Capacitor import Capacitor -from faebryk.library.Common_Mode_Filter import Common_Mode_Filter -from faebryk.library.Constant import Constant -from faebryk.library.Diode import Diode -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.GDT import GDT -from faebryk.library.has_pcb_layout_defined import has_pcb_layout_defined -from faebryk.library.has_pcb_position import has_pcb_position -from faebryk.library.Range import Range -from faebryk.library.Resistor import Resistor -from faebryk.library.RS485 import RS485 -from faebryk.library.TVS import TVS +from faebryk.libs.library import L from faebryk.libs.units import P -from faebryk.libs.util import times logger = logging.getLogger(__name__) @@ -42,182 +29,155 @@ class RS485_Bus_Protection(Module): def __init__(self, termination: bool = True, polarization: bool = True) -> None: super().__init__() - - class _NODEs(Module.NODES()): - gdt = GDT() - tvs = TVS() - current_limmiter_resistors = times(2, Resistor) - common_mode_filter = Common_Mode_Filter() - gnd_couple_resistor = Resistor() - gnd_couple_capacitor = Capacitor() - clamping_diodes = times(2, Diode) - if termination: - termination_resistor = Resistor() - if polarization: - polarization_resistors = times(2, Resistor) - - self.NODEs = _NODEs(self) - - class _IFs(Module.IFS()): - power = ElectricPower() - rs485_in = RS485() - rs485_out = RS485() - earth = Electrical() - - self.IFs = _IFs(self) - - class _PARAMs(Module.PARAMS()): ... - - self.PARAMs = _PARAMs(self) - - if termination: - self.NODEs.termination_resistor.PARAMs.resistance.merge( - Constant(120 * P.ohm) + self._termination = termination + self._polarization = polarization + + gdt: F.GDT + tvs: F.TVS + current_limmiter_resistors = L.list_field(2, F.Resistor) + common_mode_filter: F.Common_Mode_Filter + gnd_couple_resistor: F.Resistor + gnd_couple_capacitor: F.Capacitor + clamping_diodes = L.list_field(2, F.Diode) + power: F.ElectricPower + rs485_in: F.RS485 + rs485_out: F.RS485 + earth: F.Electrical + + def __preinit__(self): + if self._termination: + termination_resistor = self.add(F.Resistor(), name="termination_resistor") + termination_resistor.resistance.merge(F.Constant(120 * P.ohm)) + self.rs485_out.diff_pair.p.connect_via( + termination_resistor, self.rs485_out.diff_pair.n ) - self.IFs.rs485_out.IFs.diff_pair.IFs.p.connect_via( - self.NODEs.termination_resistor, self.IFs.rs485_out.IFs.diff_pair.IFs.n - ) - if polarization: - self.NODEs.polarization_resistors[0].PARAMs.resistance.merge( - Range(380 * P.ohm, 420 * P.ohm) + if self._polarization: + polarization_resistors = self.add_to_container(2, F.Resistor) + + polarization_resistors[0].resistance.merge( + F.Range(380 * P.ohm, 420 * P.ohm) ) - self.NODEs.polarization_resistors[1].PARAMs.resistance.merge( - Range(380 * P.ohm, 420 * P.ohm) + polarization_resistors[1].resistance.merge( + F.Range(380 * P.ohm, 420 * P.ohm) ) - self.IFs.rs485_in.IFs.diff_pair.IFs.p.connect_via( - self.NODEs.polarization_resistors[0], self.IFs.power.IFs.hv + self.rs485_in.diff_pair.p.connect_via( + polarization_resistors[0], self.power.hv ) - self.IFs.rs485_in.IFs.diff_pair.IFs.n.connect_via( - self.NODEs.polarization_resistors[1], self.IFs.power.IFs.lv + self.rs485_in.diff_pair.n.connect_via( + polarization_resistors[1], self.power.lv ) - self.NODEs.current_limmiter_resistors[0].PARAMs.resistance.merge( - Constant(2.7 * P.ohm) - ) + self.current_limmiter_resistors[0].resistance.merge(F.Constant(2.7 * P.ohm)) # TODO: set power dissipation of resistor to 2W - self.NODEs.current_limmiter_resistors[1].PARAMs.resistance.merge( - Constant(2.7 * P.ohm) - ) + self.current_limmiter_resistors[1].resistance.merge(F.Constant(2.7 * P.ohm)) # TODO: set power dissipation of resistor to 2W - self.NODEs.gnd_couple_resistor.PARAMs.resistance.merge(Constant(1 * P.Mohm)) - self.NODEs.gnd_couple_capacitor.PARAMs.capacitance.merge(Constant(1 * P.uF)) - self.NODEs.gnd_couple_capacitor.PARAMs.rated_voltage.merge(Constant(2 * P.kV)) + self.gnd_couple_resistor.resistance.merge(F.Constant(1 * P.Mohm)) + self.gnd_couple_capacitor.capacitance.merge(F.Constant(1 * P.uF)) + self.gnd_couple_capacitor.rated_voltage.merge(F.Range.lower_bound(2 * P.kV)) - self.NODEs.tvs.PARAMs.reverse_working_voltage.merge(Constant(8.5 * P.V)) - # self.NODEs.tvs.PARAMs.max_current.merge(Constant(41.7*P.A)) - # self.NODEs.tvs.PARAMs.forward_voltage.merge(Range(9.44*P.V, 10.40*P.V)) + self.tvs.reverse_working_voltage.merge(F.Constant(8.5 * P.V)) + # self.tvs.max_current.merge(F.Constant(41.7*P.A)) + # self.tvs.forward_voltage.merge(F.Range(9.44*P.V, 10.40*P.V)) - for diode in self.NODEs.clamping_diodes: - diode.PARAMs.forward_voltage.merge(Constant(1.1 * P.V)) - diode.PARAMs.max_current.merge(Constant(1 * P.A)) - diode.PARAMs.reverse_working_voltage.merge(Constant(1 * P.kV)) + for diode in self.clamping_diodes: + diode.forward_voltage.merge(F.Constant(1.1 * P.V)) + diode.max_current.merge(F.Constant(1 * P.A)) + diode.reverse_working_voltage.merge(F.Constant(1 * P.kV)) # connections # earth connections - self.IFs.power.IFs.lv.connect_via( - self.NODEs.gnd_couple_resistor, self.IFs.earth - ) - self.IFs.power.IFs.lv.connect_via( - self.NODEs.gnd_couple_capacitor, self.IFs.earth - ) - self.NODEs.gdt.IFs.common.connect(self.IFs.earth) + self.power.lv.connect_via(self.gnd_couple_resistor, self.earth) + self.power.lv.connect_via(self.gnd_couple_capacitor, self.earth) + self.gdt.common.connect(self.earth) # rs485_in connections - self.IFs.rs485_in.IFs.diff_pair.IFs.p.connect( - self.NODEs.common_mode_filter.IFs.c_a[0] - ) - self.IFs.rs485_in.IFs.diff_pair.IFs.n.connect( - self.NODEs.common_mode_filter.IFs.c_b[0] - ) + self.rs485_in.diff_pair.n.connect(self.common_mode_filter.c_a[0]) + self.rs485_in.diff_pair.p.connect(self.common_mode_filter.c_b[0]) # rs485_out connections - self.NODEs.common_mode_filter.IFs.c_a[1].connect_via( - self.NODEs.current_limmiter_resistors[0], - self.IFs.rs485_out.IFs.diff_pair.IFs.p, - ) - self.NODEs.common_mode_filter.IFs.c_b[1].connect_via( - self.NODEs.current_limmiter_resistors[1], - self.IFs.rs485_out.IFs.diff_pair.IFs.n, + self.common_mode_filter.c_a[1].connect_via( + self.current_limmiter_resistors[0], + self.rs485_out.diff_pair.n, ) - self.IFs.rs485_out.IFs.diff_pair.IFs.n.connect_via( - self.NODEs.gdt, self.IFs.rs485_out.IFs.diff_pair.IFs.p + self.common_mode_filter.c_b[1].connect_via( + self.current_limmiter_resistors[1], + self.rs485_out.diff_pair.p, ) + self.rs485_out.diff_pair.n.connect_via(self.gdt, self.rs485_out.diff_pair.p) # tvs connections # TODO: fix this, super ugly.... - diode_junction = self.NODEs.clamping_diodes[0].IFs.anode - diode_junction.connect(self.NODEs.clamping_diodes[1].IFs.cathode) - self.NODEs.common_mode_filter.IFs.c_a[1].connect_via( - self.NODEs.tvs, diode_junction - ) - self.NODEs.common_mode_filter.IFs.c_b[1].connect_via( - self.NODEs.clamping_diodes[0], diode_junction - ) - self.NODEs.common_mode_filter.IFs.c_b[1].connect( - self.NODEs.clamping_diodes[1].IFs.cathode + diode_junction = self.clamping_diodes[0].anode + diode_junction.connect(self.clamping_diodes[1].cathode) + self.common_mode_filter.c_a[1].connect_via(self.tvs, diode_junction) + self.common_mode_filter.c_b[1].connect_via( + self.clamping_diodes[0], diode_junction ) - self.NODEs.clamping_diodes[1].IFs.anode.connect(diode_junction) - - self.add_trait(can_bridge_defined(self.IFs.rs485_in, self.IFs.rs485_out)) + self.common_mode_filter.c_b[1].connect(self.clamping_diodes[1].cathode) + self.clamping_diodes[1].anode.connect(diode_junction) # TODO: layout is only working when bbox is implemented or # when using specific components # PCB layout - Point = has_pcb_position.Point - L = has_pcb_position.layer_type - self.NODEs.gnd_couple_resistor.add_trait( - has_pcb_layout_defined( + Point = F.has_pcb_position.Point + L = F.has_pcb_position.layer_type + self.gnd_couple_resistor.add_trait( + F.has_pcb_layout_defined( LayoutAbsolute( Point((-10, 0, 90, L.NONE)), ) ) ) self.add_trait( - has_pcb_layout_defined( + F.has_pcb_layout_defined( LayoutTypeHierarchy( layouts=[ LayoutTypeHierarchy.Level( - mod_type=GDT, + mod_type=F.GDT, layout=LayoutAbsolute( Point((0, 0, 0, L.NONE)), ), ), # TODO: fix # LayoutTypeHierarchy.Level( - # mod_type=TVS, + # mod_type=F.TVS, # layout=LayoutAbsolute( # Point((0, 11, 0, L.NONE)), # ), # ), LayoutTypeHierarchy.Level( - mod_type=Common_Mode_Filter, + mod_type=F.Common_Mode_Filter, layout=LayoutAbsolute( - Point((0, 25.5, 90, L.NONE)), + Point((0, 19, 90, L.NONE)), ), ), LayoutTypeHierarchy.Level( - mod_type=Diode, + mod_type=F.Diode, layout=LayoutExtrude( - base=Point((0, 14.5, 0, L.NONE)), + base=Point((-3.5, 14.5, 90, L.NONE)), vector=(0, 3.5, 0), ), ), LayoutTypeHierarchy.Level( - mod_type=Resistor, + mod_type=F.Resistor, layout=LayoutExtrude( - base=Point((-2, 8, 90, L.NONE)), + base=Point((-2, 7, 90, L.NONE)), vector=(0, 4, 0), ), ), - LayoutTypeHierarchy.Level( - mod_type=Capacitor, - layout=LayoutAbsolute( - Point((10, 0, 90, L.NONE)), - ), - ), + # LayoutTypeHierarchy.Level( + # mod_type=F.Capacitor, + # layout=LayoutAbsolute( + # Point((10, 0, 90, L.NONE)), + # ), + # ), ] ), ) ) + + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(self.rs485_in, self.rs485_out) diff --git a/src/faebryk/library/Range.py b/src/faebryk/library/Range.py index a44b49ff..37928733 100644 --- a/src/faebryk/library/Range.py +++ b/src/faebryk/library/Range.py @@ -4,7 +4,7 @@ from math import inf from typing import Any, Protocol, Self -from faebryk.core.core import Parameter +from faebryk.core.parameter import Parameter class _SupportsRangeOps(Protocol): diff --git a/src/faebryk/library/Relay.py b/src/faebryk/library/Relay.py index 4d10f74f..6661a18e 100644 --- a/src/faebryk/library/Relay.py +++ b/src/faebryk/library/Relay.py @@ -3,10 +3,9 @@ import logging -from faebryk.core.core import Module -from faebryk.library.Electrical import Electrical -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import Quantity logger = logging.getLogger(__name__) @@ -14,33 +13,20 @@ # TODO: make generic (use Switch module, different switch models, bistable, etc.) class Relay(Module): - def __init__(self) -> None: - super().__init__() - - class _NODEs(Module.NODES()): ... - - self.NODEs = _NODEs(self) - - class _IFs(Module.IFS()): - switch_a_nc = Electrical() - switch_a_common = Electrical() - switch_a_no = Electrical() - switch_b_no = Electrical() - switch_b_common = Electrical() - switch_b_nc = Electrical() - coil_p = Electrical() - coil_n = Electrical() - - self.IFs = _IFs(self) - - class _PARAMs(Module.PARAMS()): - coil_rated_voltage = TBD[Quantity]() - coil_rated_current = TBD[Quantity]() - coil_resistance = TBD[Quantity]() - contact_max_switching_voltage = TBD[Quantity]() - contact_rated_switching_current = TBD[Quantity]() - contact_max_switchng_current = TBD[Quantity]() - - self.PARAMs = _PARAMs(self) - - self.add_trait(has_designator_prefix_defined("RELAY")) + switch_a_nc: F.Electrical + switch_a_common: F.Electrical + switch_a_no: F.Electrical + switch_b_no: F.Electrical + switch_b_common: F.Electrical + switch_b_nc: F.Electrical + coil_p: F.Electrical + coil_n: F.Electrical + + coil_rated_voltage: F.TBD[Quantity] + coil_rated_current: F.TBD[Quantity] + coil_resistance: F.TBD[Quantity] + contact_max_switching_voltage: F.TBD[Quantity] + contact_rated_switching_current: F.TBD[Quantity] + contact_max_switchng_current: F.TBD[Quantity] + + designator_prefix = L.f_field(F.has_designator_prefix_defined)("RELAY") diff --git a/src/faebryk/library/Resistor.py b/src/faebryk/library/Resistor.py index 5836b53d..465e3328 100644 --- a/src/faebryk/library/Resistor.py +++ b/src/faebryk/library/Resistor.py @@ -3,60 +3,47 @@ from math import sqrt -from faebryk.core.core import Module, Parameter -from faebryk.core.util import ( - as_unit, - as_unit_with_tolerance, -) -from faebryk.library.can_attach_to_footprint_symmetrically import ( - can_attach_to_footprint_symmetrically, -) -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.Electrical import Electrical -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.has_multi_picker import has_multi_picker -from faebryk.library.has_simple_value_representation_based_on_params import ( - has_simple_value_representation_based_on_params, -) -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.core.parameter import Parameter +from faebryk.libs.library import L from faebryk.libs.picker.picker import PickError, has_part_picked_remove from faebryk.libs.units import P, Quantity -from faebryk.libs.util import times class Resistor(Module): - def __init__(self): - super().__init__() - - class _IFs(super().IFS()): - unnamed = times(2, Electrical) - - self.IFs = _IFs(self) - self.add_trait(can_bridge_defined(*self.IFs.unnamed)) - - class PARAMS(super().PARAMS()): - resistance = TBD[Quantity]() - rated_power = TBD[Quantity]() - rated_voltage = TBD[Quantity]() - - self.PARAMs = PARAMS(self) - - self.add_trait(can_attach_to_footprint_symmetrically()) - self.add_trait( - has_simple_value_representation_based_on_params( - ( - self.PARAMs.resistance, - self.PARAMs.rated_power, - ), - lambda ps: " ".join( - filter( - None, - [as_unit_with_tolerance(ps[0], "Ω"), as_unit(ps[1], "W")], - ) - ), - ) + unnamed = L.list_field(2, F.Electrical) + + resistance: F.TBD[Quantity] + rated_power: F.TBD[Quantity] + rated_voltage: F.TBD[Quantity] + + attach_to_footprint: F.can_attach_to_footprint_symmetrically + designator_prefix = L.f_field(F.has_designator_prefix_defined)("R") + + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(*self.unnamed) + + @L.rt_field + def simple_value_representation(self): + from faebryk.core.util import ( + as_unit, + as_unit_with_tolerance, + ) + + return F.has_simple_value_representation_based_on_params( + ( + self.resistance, + self.rated_power, + ), + lambda ps: " ".join( + filter( + None, + [as_unit_with_tolerance(ps[0], "Ω"), as_unit(ps[1], "W")], + ) + ), ) - self.add_trait(has_designator_prefix_defined("R")) def allow_removal_if_zero(self): import faebryk.library._F as F @@ -64,23 +51,23 @@ def allow_removal_if_zero(self): def replace_zero(m: Module): assert m is self - r = self.PARAMs.resistance.get_most_narrow() + r = self.resistance.get_most_narrow() if not F.Constant(0.0 * P.ohm).is_subset_of(r): raise PickError("", self) - self.PARAMs.resistance.override(F.Constant(0.0 * P.ohm)) - self.IFs.unnamed[0].connect(self.IFs.unnamed[1]) + self.resistance.override(F.Constant(0.0 * P.ohm)) + self.unnamed[0].connect(self.unnamed[1]) self.add_trait(has_part_picked_remove()) - has_multi_picker.add_to_module( - self, -100, has_multi_picker.FunctionPicker(replace_zero) + F.has_multi_picker.add_to_module( + self, -100, F.has_multi_picker.FunctionPicker(replace_zero) ) def get_voltage_drop_by_current_resistance(self, current_A: Parameter) -> Parameter: - return current_A * self.PARAMs.resistance + return current_A * self.resistance def get_voltage_drop_by_power_resistance(self, power_W: Parameter) -> Parameter: - return sqrt(power_W * self.PARAMs.resistance) + return sqrt(power_W * self.resistance) @staticmethod def set_voltage_drop_by_power_current( @@ -91,10 +78,10 @@ def set_voltage_drop_by_power_current( def get_current_flow_by_voltage_resistance( self, voltage_drop_V: Parameter ) -> Parameter: - return voltage_drop_V / self.PARAMs.resistance + return voltage_drop_V / self.resistance def get_current_flow_by_power_resistance(self, power_W: Parameter) -> Parameter: - return sqrt(power_W / self.PARAMs.resistance) + return sqrt(power_W / self.resistance) @staticmethod def get_current_flow_by_voltage_power( @@ -105,30 +92,30 @@ def get_current_flow_by_voltage_power( def set_resistance_by_voltage_current( self, voltage_drop_V: Parameter, current_A: Parameter ) -> Parameter: - self.PARAMs.resistance.merge(voltage_drop_V / current_A) - return self.PARAMs.resistance.get_most_narrow() + self.resistance.merge(voltage_drop_V / current_A) + return self.resistance.get_most_narrow() def set_resistance_by_voltage_power( self, voltage_drop_V: Parameter, power_W: Parameter ) -> Parameter: - self.PARAMs.resistance.merge(pow(voltage_drop_V, 2) / power_W) - return self.PARAMs.resistance.get_most_narrow() + self.resistance.merge(pow(voltage_drop_V, 2) / power_W) + return self.resistance.get_most_narrow() def set_resistance_by_power_current( self, current_A: Parameter, power_W: Parameter ) -> Parameter: - self.PARAMs.resistance.merge(power_W / pow(current_A, 2)) - return self.PARAMs.resistance.get_most_narrow() + self.resistance.merge(power_W / pow(current_A, 2)) + return self.resistance.get_most_narrow() def get_power_dissipation_by_voltage_resistance( self, voltage_drop_V: Parameter ) -> Parameter: - return pow(voltage_drop_V, 2) / self.PARAMs.resistance + return pow(voltage_drop_V, 2) / self.resistance def get_power_dissipation_by_current_resistance( self, current_A: Parameter ) -> Parameter: - return pow(current_A, 2) * self.PARAMs.resistance + return pow(current_A, 2) * self.resistance @staticmethod def get_power_dissipation_by_voltage_current( @@ -137,11 +124,11 @@ def get_power_dissipation_by_voltage_current( return voltage_drop_V * current_A def set_rated_power_by_voltage_resistance(self, voltage_drop_V: Parameter): - self.PARAMs.rated_power.merge( + self.rated_power.merge( self.get_power_dissipation_by_voltage_resistance(voltage_drop_V) ) def set_rated_power_by_current_resistance(self, current_A: Parameter): - self.PARAMs.rated_power.merge( + self.rated_power.merge( self.get_power_dissipation_by_current_resistance(current_A) ) diff --git a/src/faebryk/library/Resistor_Voltage_Divider.py b/src/faebryk/library/Resistor_Voltage_Divider.py index be63e5b6..cc7dd64a 100644 --- a/src/faebryk/library/Resistor_Voltage_Divider.py +++ b/src/faebryk/library/Resistor_Voltage_Divider.py @@ -3,38 +3,25 @@ import logging -from faebryk.core.core import Module -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.Electrical import Electrical -from faebryk.library.Resistor import Resistor -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import Quantity -from faebryk.libs.util import times logger = logging.getLogger(__name__) class Resistor_Voltage_Divider(Module): - def __init__(self) -> None: - super().__init__() + resistor = L.list_field(2, F.Resistor) + node = L.list_field(3, F.Electrical) - class _NODEs(Module.NODES()): - resistor = times(2, Resistor) + ratio: F.TBD[Quantity] + max_current: F.TBD[Quantity] - self.NODEs = _NODEs(self) + def __preinit__(self): + self.node[0].connect_via(self.resistor[0], self.node[1]) + self.node[1].connect_via(self.resistor[1], self.node[2]) - class _IFs(Module.IFS()): - node = times(3, Electrical) - - self.IFs = _IFs(self) - - class _PARAMs(Module.PARAMS()): - ratio = TBD[Quantity]() - max_current = TBD[Quantity]() - - self.PARAMs = _PARAMs(self) - - self.IFs.node[0].connect_via(self.NODEs.resistor[0], self.IFs.node[1]) - self.IFs.node[1].connect_via(self.NODEs.resistor[1], self.IFs.node[2]) - - self.add_trait(can_bridge_defined(self.IFs.node[0], self.IFs.node[1])) + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(self.node[0], self.node[1]) diff --git a/src/faebryk/library/SCD40.py b/src/faebryk/library/SCD40.py index 438ee6cc..40f82456 100644 --- a/src/faebryk/library/SCD40.py +++ b/src/faebryk/library/SCD40.py @@ -1,22 +1,11 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from dataclasses import dataclass, field -from faebryk.core.core import Module, Parameter -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.Constant import Constant -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.has_esphome_config import has_esphome_config -from faebryk.library.I2C import I2C -from faebryk.library.is_esphome_bus import is_esphome_bus -from faebryk.library.TBD import TBD -from faebryk.libs.units import P +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L +from faebryk.libs.units import P, Quantity class SCD40(Module): @@ -24,22 +13,17 @@ class SCD40(Module): Sensirion SCD4x NIR CO2 sensor """ - @dataclass - class _scd4x_esphome_config(has_esphome_config.impl()): - update_interval_s: Parameter = field(default_factory=TBD) - - def __post_init__(self) -> None: - super().__init__() + class _scd4x_esphome_config(F.has_esphome_config.impl()): + update_interval: F.TBD[Quantity] def get_config(self) -> dict: - assert isinstance( - self.update_interval_s, Constant - ), "No update interval set!" + val = self.update_interval.get_most_narrow() + assert isinstance(val, F.Constant), "No update interval set!" - obj = self.get_obj() + obj = self.obj assert isinstance(obj, SCD40) - i2c = is_esphome_bus.find_connected_bus(obj.IFs.i2c) + i2c = F.is_esphome_bus.find_connected_bus(obj.i2c) return { "sensor": [ @@ -55,51 +39,41 @@ def get_config(self) -> dict: "name": "Humidity", }, "address": 0x62, - "i2c_id": i2c.get_trait(is_esphome_bus).get_bus_id(), - "update_interval": f"{self.update_interval_s.value}s", + "i2c_id": i2c.get_trait(F.is_esphome_bus).get_bus_id(), + "update_interval": f"{val.value.to('s')}", } ] } - def __init__(self) -> None: - super().__init__() - - # interfaces - class _IFs(Module.IFS()): - power = ElectricPower() - i2c = I2C() - - self.IFs = _IFs(self) - - self.add_trait( - can_attach_to_footprint_via_pinmap( - { - "6": self.IFs.power.IFs.lv, - "20": self.IFs.power.IFs.lv, - "21": self.IFs.power.IFs.lv, - "7": self.IFs.power.IFs.hv, - "19": self.IFs.power.IFs.hv, - "9": self.IFs.i2c.IFs.scl.IFs.signal, - "10": self.IFs.i2c.IFs.sda.IFs.signal, - } - ) + esphome_config: _scd4x_esphome_config + + # interfaces + power: F.ElectricPower + i2c: F.I2C + + @L.rt_field + def attach_to_footprint(self): + return F.can_attach_to_footprint_via_pinmap( + { + "6": self.power.lv, + "20": self.power.lv, + "21": self.power.lv, + "7": self.power.hv, + "19": self.power.hv, + "9": self.i2c.scl.signal, + "10": self.i2c.sda.signal, + } ) - self.IFs.power.PARAMs.voltage.merge(Constant(3.3 * P.V)) - - self.IFs.i2c.terminate() - self.IFs.power.get_trait(can_be_decoupled).decouple() - - self.add_trait(has_designator_prefix_defined("U")) - self.IFs.i2c.PARAMs.frequency.merge( - I2C.define_max_frequency_capability(I2C.SpeedMode.fast_speed) - ) - self.add_trait( - has_datasheet_defined( - "https://sensirion.com/media/documents/48C4B7FB/64C134E7/Sensirion_SCD4x_Datasheet.pdf" - ) + def __preinit__(self): + self.power.voltage.merge(F.Constant(3.3 * P.V)) + self.i2c.terminate() + self.power.decoupled.decouple() + self.i2c.frequency.merge( + F.I2C.define_max_frequency_capability(F.I2C.SpeedMode.fast_speed) ) - self.IFs.i2c.add_trait(is_esphome_bus.impl()()) - self.esphome = self._scd4x_esphome_config() - self.add_trait(self.esphome) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") + datasheet = L.f_field(F.has_datasheet_defined)( + "https://sensirion.com/media/documents/48C4B7FB/64C134E7/Sensirion_SCD4x_Datasheet.pdf" + ) diff --git a/src/faebryk/library/SK9822_EC20.py b/src/faebryk/library/SK9822_EC20.py index d37fb275..964b2ba8 100644 --- a/src/faebryk/library/SK9822_EC20.py +++ b/src/faebryk/library/SK9822_EC20.py @@ -1,19 +1,9 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_designator_prefix_defined import ( - has_designator_prefix_defined, -) -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L class SK9822_EC20(Module): @@ -22,7 +12,7 @@ class SK9822_EC20(Module): (RGB) driving intelligent control circuit and the light emitting circuit in one of the LED light source control. Products containing a signal - decoding module, data buffer, a built-in Constant + decoding module, data buffer, a built-in F.Constant current circuit and RC oscillator; CMOS, low voltage, low power consumption; 256 level grayscale PWM adjustment and 32 brightness adjustment; @@ -31,41 +21,35 @@ class SK9822_EC20(Module): output action synchronization. """ - def __init__(self) -> None: - super().__init__() - - # interfaces - class _IFs(Module.IFS()): - power = ElectricPower() - sdo = ElectricLogic() - sdi = ElectricLogic() - cko = ElectricLogic() - ckl = ElectricLogic() - - self.IFs = _IFs(self) - - x = self.IFs - self.add_trait( - can_attach_to_footprint_via_pinmap( - { - "1": x.sdo.IFs.signal, - "2": x.power.IFs.lv, - "3": x.sdi.IFs.signal, - "4": x.ckl.IFs.signal, - "5": x.power.IFs.hv, - "6": x.cko.IFs.signal, - } - ) + # interfaces + power: F.ElectricPower + sdo: F.ElectricLogic + sdi: F.ElectricLogic + cko: F.ElectricLogic + ckl: F.ElectricLogic + + @L.rt_field + def attach_to_footprint(self): + x = self + return F.can_attach_to_footprint_via_pinmap( + { + "1": x.sdo.signal, + "2": x.power.lv, + "3": x.sdi.signal, + "4": x.ckl.signal, + "5": x.power.hv, + "6": x.cko.signal, + } ) - # connect all logic references - ref = ElectricLogic.connect_all_module_references(self) - self.add_trait(has_single_electric_reference_defined(ref)) - - self.add_trait( - has_datasheet_defined( - "https://datasheet.lcsc.com/lcsc/2110250930_OPSCO-Optoelectronics-SK9822-EC20_C2909059.pdf" - ) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) ) - self.add_trait(has_designator_prefix_defined("LED")) + datasheet = L.f_field(F.has_datasheet_defined)( + "https://datasheet.lcsc.com/lcsc/2110250930_OPSCO-Optoelectronics-SK9822-EC20_C2909059.pdf" + ) + + designator_prefix = L.f_field(F.has_designator_prefix_defined)("LED") diff --git a/src/faebryk/library/SMDTwoPin.py b/src/faebryk/library/SMDTwoPin.py index befcad5b..03a4e29e 100644 --- a/src/faebryk/library/SMDTwoPin.py +++ b/src/faebryk/library/SMDTwoPin.py @@ -3,14 +3,11 @@ from enum import Enum -from faebryk.library.can_attach_via_pinmap_equal import can_attach_via_pinmap_equal -from faebryk.library.Footprint import Footprint -from faebryk.library.has_equal_pins_in_ifs import has_equal_pins_in_ifs -from faebryk.library.Pad import Pad -from faebryk.libs.util import times +import faebryk.library._F as F +from faebryk.libs.library import L -class SMDTwoPin(Footprint): +class SMDTwoPin(F.Footprint): class Type(Enum): _01005 = 0 _0201 = 1 @@ -25,34 +22,30 @@ class Type(Enum): def __init__(self, type: Type) -> None: super().__init__() - - class _IFs(Footprint.IFS()): - pins = times(2, Pad) - - self.IFs = _IFs(self) - from faebryk.library.has_kicad_footprint_equal_ifs import ( - has_kicad_footprint_equal_ifs, - ) - - class _has_kicad_footprint(has_kicad_footprint_equal_ifs): - @staticmethod - def get_kicad_footprint() -> str: - table = { - self.Type._01005: "0402", - self.Type._0201: "0603", - self.Type._0402: "1005", - self.Type._0603: "1608", - self.Type._0805: "2012", - self.Type._1206: "3216", - self.Type._1210: "3225", - self.Type._1218: "3246", - self.Type._2010: "5025", - self.Type._2512: "6332", - } - return "Resistor_SMD:R_{imperial}_{metric}Metric".format( - imperial=type.name[1:], metric=table[type] - ) - - self.add_trait(_has_kicad_footprint()) - self.add_trait(has_equal_pins_in_ifs()) - self.add_trait(can_attach_via_pinmap_equal()) + self._type = type + + pins = L.list_field(2, F.Pad) + + class _has_kicad_footprint(F.has_kicad_footprint_equal_ifs): + def get_kicad_footprint(self) -> str: + obj = self.obj + assert isinstance(obj, SMDTwoPin) + table = { + SMDTwoPin.Type._01005: "0402", + SMDTwoPin.Type._0201: "0603", + SMDTwoPin.Type._0402: "1005", + SMDTwoPin.Type._0603: "1608", + SMDTwoPin.Type._0805: "2012", + SMDTwoPin.Type._1206: "3216", + SMDTwoPin.Type._1210: "3225", + SMDTwoPin.Type._1218: "3246", + SMDTwoPin.Type._2010: "5025", + SMDTwoPin.Type._2512: "6332", + } + return "Resistor_SMD:R_{imperial}_{metric}Metric".format( + imperial=obj._type.name[1:], metric=table[obj._type] + ) + + kicad_footprint: _has_kicad_footprint + equal_pins: F.has_equal_pins_in_ifs + attach_via_pinmap: F.can_attach_via_pinmap_equal diff --git a/src/faebryk/library/SNx4LVC541A.py b/src/faebryk/library/SNx4LVC541A.py index 76ee8c0d..a561eda6 100644 --- a/src/faebryk/library/SNx4LVC541A.py +++ b/src/faebryk/library/SNx4LVC541A.py @@ -2,9 +2,9 @@ # SPDX-License-Identifier: MIT import faebryk.library._F as F -from faebryk.core.core import Module +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import P -from faebryk.libs.util import times class SNx4LVC541A(Module): @@ -14,40 +14,29 @@ class SNx4LVC541A(Module): octal buffer/driver is designed for 1.65-V to 3.6-V VCC operation. """ - def __init__(self): - super().__init__() + # ---------------------------------------- + # modules, interfaces, parameters + # ---------------------------------------- + A = L.list_field(8, F.ElectricLogic) + Y = L.list_field(8, F.ElectricLogic) - # ---------------------------------------- - # modules, interfaces, parameters - # ---------------------------------------- - class _PARAMs(Module.PARAMS()): ... - - self.PARAMs = _PARAMs(self) - - class _IFs(Module.IFS()): - A = times(8, F.ElectricLogic) - Y = times(8, F.ElectricLogic) - - vcc = F.ElectricPower() + power: F.ElectricPower - OE = times(2, F.ElectricLogic) + OE = L.list_field(2, F.ElectricLogic) - self.IFs = _IFs(self) - - # ---------------------------------------- - # traits - # ---------------------------------------- - self.add_trait(F.has_designator_prefix_defined("U")) - self.add_trait( - F.has_datasheet_defined( - "https://www.ti.com/lit/ds/symlink/sn74lvc541a.pdf?ts=1718881644774&ref_url=https%253A%252F%252Fwww.mouser.ie%252F" - ) - ) + # ---------------------------------------- + # traits + # ---------------------------------------- + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") + datasheet = L.f_field(F.has_datasheet_defined)( + "https://www.ti.com/lit/ds/symlink/sn74lvc541a.pdf?ts=1718881644774&ref_url=https%253A%252F%252Fwww.mouser.ie%252F" + ) + def __preinit__(self): # ---------------------------------------- # parameters # ---------------------------------------- - self.IFs.vcc.PARAMs.voltage.merge(F.Range.upper_bound(3.6 * P.V)) + self.power.voltage.merge(F.Range.upper_bound(3.6 * P.V)) # ---------------------------------------- # aliases @@ -56,10 +45,10 @@ class _IFs(Module.IFS()): # ---------------------------------------- # connections # ---------------------------------------- - self.IFs.vcc.get_trait(F.can_be_decoupled).decouple() + self.power.decoupled.decouple() - # set all electric logic references - for a, y, oe in zip(self.IFs.A, self.IFs.Y, self.IFs.OE): - a.connect_reference(self.IFs.vcc) - y.connect_reference(self.IFs.vcc) - oe.connect_reference(self.IFs.vcc) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) + ) diff --git a/src/faebryk/library/SOIC.py b/src/faebryk/library/SOIC.py index 402eab2f..81254336 100644 --- a/src/faebryk/library/SOIC.py +++ b/src/faebryk/library/SOIC.py @@ -1,15 +1,14 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.can_attach_via_pinmap_equal import can_attach_via_pinmap_equal -from faebryk.library.Footprint import Footprint -from faebryk.library.has_equal_pins_in_ifs import has_equal_pins_in_ifs -from faebryk.library.Pad import Pad + +import faebryk.library._F as F +from faebryk.libs.library import L from faebryk.libs.units import P, Quantity from faebryk.libs.util import times -class SOIC(Footprint): +class SOIC(F.Footprint): def __init__( self, pin_cnt: int, @@ -17,25 +16,25 @@ def __init__( pitch: Quantity, ) -> None: super().__init__() + self._pin_cnt = pin_cnt + self._size_xy = size_xy + self._pitch = pitch - class _IFs(Footprint.IFS()): - pins = times(pin_cnt, Pad) - - self.IFs = _IFs(self) - from faebryk.library.has_kicad_footprint_equal_ifs import ( - has_kicad_footprint_equal_ifs, - ) + @L.rt_field + def pins(self): + return times(self._pin_cnt, F.Pad) - class _has_kicad_footprint(has_kicad_footprint_equal_ifs): - @staticmethod - def get_kicad_footprint() -> str: - return "Package_SO:SOIC-{leads}_{size_x:.1f}x{size_y:.1f}mm_P{pitch:.2f}mm".format( # noqa: E501 - leads=pin_cnt, - size_x=size_xy[0].to(P.mm).m, - size_y=size_xy[1].to(P.mm).m, - pitch=pitch.to(P.mm).m, - ) + class _has_kicad_footprint(F.has_kicad_footprint_equal_ifs): + def get_kicad_footprint(self) -> str: + obj = self.obj + assert isinstance(obj, SOIC) + return "Package_SO:SOIC-{leads}_{size_x:.1f}x{size_y:.1f}mm_P{pitch:.2f}mm".format( # noqa: E501 + leads=obj._pin_cnt, + size_x=obj._size_xy[0].to(P.mm).m, + size_y=obj._size_xy[1].to(P.mm).m, + pitch=obj._pitch.to(P.mm).m, + ) - self.add_trait(_has_kicad_footprint()) - self.add_trait(has_equal_pins_in_ifs()) - self.add_trait(can_attach_via_pinmap_equal()) + kicad_footprint: _has_kicad_footprint + attach_via_pinmap: F.can_attach_via_pinmap_equal + equal_pins: F.has_equal_pins_in_ifs diff --git a/src/faebryk/library/SPI.py b/src/faebryk/library/SPI.py index c0b31c2a..c7c2307b 100644 --- a/src/faebryk/library/SPI.py +++ b/src/faebryk/library/SPI.py @@ -1,18 +1,12 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface -from faebryk.library.Electrical import Electrical +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface class SPI(ModuleInterface): - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - - class IFS(ModuleInterface.IFS()): - sclk = Electrical() - miso = Electrical() - mosi = Electrical() - gnd = Electrical() - - self.IFs = IFS(self) + sclk: F.Electrical + miso: F.Electrical + mosi: F.Electrical + gnd: F.Electrical diff --git a/src/faebryk/library/SPIFlash.py b/src/faebryk/library/SPIFlash.py index b2e73590..8c8a1dc4 100644 --- a/src/faebryk/library/SPIFlash.py +++ b/src/faebryk/library/SPIFlash.py @@ -1,27 +1,15 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module, ModuleInterface -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.MultiSPI import MultiSPI -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import Quantity class SPIFlash(Module): - def __init__(self) -> None: - super().__init__() + power: F.ElectricPower + spi = L.f_field(F.MultiSPI)(4) - class IFS(ModuleInterface.IFS()): - power = ElectricPower() - spi = MultiSPI() - - self.IFs = IFS(self) - - class PARAMS(ModuleInterface.PARAMS()): - memory_size = TBD[Quantity]() - - self.PARAMs = PARAMS(self) - - self.add_trait(has_designator_prefix_defined("U")) + memory_size: F.TBD[Quantity] + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") diff --git a/src/faebryk/library/SWD.py b/src/faebryk/library/SWD.py index b8750acc..cf5c2d0e 100644 --- a/src/faebryk/library/SWD.py +++ b/src/faebryk/library/SWD.py @@ -1,31 +1,19 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L class SWD(ModuleInterface): - def __init__(self) -> None: - super().__init__() - - class IFS(ModuleInterface.IFS()): - clk = ElectricLogic() - dio = ElectricLogic() - swo = ElectricLogic() - reset = ElectricLogic() - - self.IFs = IFS(self) - - class PARAMS(ModuleInterface.PARAMS()): ... - - self.PARAMs = PARAMS(self) - - self.add_trait( - has_single_electric_reference_defined( - ElectricLogic.connect_all_module_references(self) - ) + clk: F.ElectricLogic + dio: F.ElectricLogic + swo: F.ElectricLogic + reset: F.ElectricLogic + + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) ) diff --git a/src/faebryk/library/SWDConnector.py b/src/faebryk/library/SWDConnector.py index b6c67179..8b51791e 100644 --- a/src/faebryk/library/SWDConnector.py +++ b/src/faebryk/library/SWDConnector.py @@ -1,35 +1,20 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module, ModuleInterface -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.library.SWD import SWD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L class SWDConnector(Module): - def __init__(self) -> None: - super().__init__() + swd: F.SWD + gnd_detect: F.ElectricLogic + vcc: F.ElectricPower - class IFS(ModuleInterface.IFS()): - swd = SWD() - gnd_detect = ElectricLogic() - vcc = ElectricPower() + designator_prefix = L.f_field(F.has_designator_prefix_defined)("J") - self.IFs = IFS(self) - - class PARAMS(ModuleInterface.PARAMS()): ... - - self.PARAMs = PARAMS(self) - - self.add_trait(has_designator_prefix_defined("J")) - - self.add_trait( - has_single_electric_reference_defined( - ElectricLogic.connect_all_module_references(self) - ) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) ) diff --git a/src/faebryk/library/Sercom.py b/src/faebryk/library/Sercom.py index 011c56da..ca3b42df 100644 --- a/src/faebryk/library/Sercom.py +++ b/src/faebryk/library/Sercom.py @@ -1,22 +1,16 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module, ModuleInterface -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.libs.util import times +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L class Sercom(ModuleInterface): - def __init__(self) -> None: - super().__init__() + unnamed = L.list_field(4, F.ElectricLogic) - class IFS(Module.IFS()): - unnamed = times(4, ElectricLogic) - - self.IFs = IFS(self) - - ref = ElectricLogic.connect_all_module_references(self) - self.add_trait(has_single_electric_reference_defined(ref)) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) + ) diff --git a/src/faebryk/library/Set.py b/src/faebryk/library/Set.py index cd6796a1..48b44966 100644 --- a/src/faebryk/library/Set.py +++ b/src/faebryk/library/Set.py @@ -3,20 +3,19 @@ from typing import Iterable, Self -from faebryk.core.core import Parameter, _resolved +import faebryk.library._F as F +from faebryk.core.parameter import Parameter, _resolved class Set[PV](Parameter[PV], Parameter[PV].SupportsSetOps): type LIT_OR_PARAM = Parameter[PV].LIT_OR_PARAM def __init__(self, params: Iterable[Parameter[LIT_OR_PARAM]]) -> None: - from faebryk.library.Constant import Constant - super().__init__() # make primitves to constants self._params = set( - p if isinstance(p, Parameter) else Constant(p) for p in params + p if isinstance(p, Parameter) else F.Constant(p) for p in params ) @staticmethod @@ -68,12 +67,10 @@ def copy(self) -> Self: @_resolved def __contains__(self, other: Parameter[PV]) -> bool: - from faebryk.library.Range import Range - def nested_in(p): if other == p: return True - if isinstance(p, Range): + if isinstance(p, F.Range): return other in p return False diff --git a/src/faebryk/library/Switch.py b/src/faebryk/library/Switch.py index 4312e879..bc461bdc 100644 --- a/src/faebryk/library/Switch.py +++ b/src/faebryk/library/Switch.py @@ -1,23 +1,16 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from functools import cache -from typing import Generic, TypeGuard, TypeVar +from typing import TypeGuard -from faebryk.core.core import Module, ModuleInterface -from faebryk.library.can_attach_to_footprint_symmetrically import ( - can_attach_to_footprint_symmetrically, -) -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.has_designator_prefix_defined import ( - has_designator_prefix_defined, -) -from faebryk.libs.util import times +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L +from faebryk.libs.util import once -T = TypeVar("T", bound=ModuleInterface) - -class _TSwitch(Generic[T], Module): +class _TSwitch[T: ModuleInterface](Module): def __init__(self, t: type[T]): super().__init__() self.t = t @@ -27,20 +20,20 @@ def is_instance(obj: Module, t: type[T]) -> bool: return isinstance(obj, _TSwitch) and issubclass(obj.t, t) -@cache # This means we can use a normal "isinstance" to test for them -def Switch(interface_type: type[T]): +@once # This means we can use a normal "isinstance" to test for them +def Switch[T: ModuleInterface](interface_type: type[T]): class _Switch(_TSwitch[interface_type]): def __init__(self) -> None: super().__init__(interface_type) - self.add_trait(has_designator_prefix_defined("SW")) - self.add_trait(can_attach_to_footprint_symmetrically()) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("SW") + attach_to_footprint: F.can_attach_to_footprint_symmetrically - class _IFs(super().IFS()): - unnamed = times(2, interface_type) + unnamed = L.list_field(2, interface_type) - self.IFs = _IFs(self) - self.add_trait(can_bridge_defined(*self.IFs.unnamed)) + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(*self.unnamed) @staticmethod def is_instance(obj: Module) -> "TypeGuard[_Switch]": diff --git a/src/faebryk/library/TBD.py b/src/faebryk/library/TBD.py index 1303c0d2..56139ffd 100644 --- a/src/faebryk/library/TBD.py +++ b/src/faebryk/library/TBD.py @@ -2,17 +2,11 @@ # SPDX-License-Identifier: MIT from textwrap import indent -from typing import Generic, TypeVar -from faebryk.core.core import Parameter +from faebryk.core.parameter import Parameter -PV = TypeVar("PV") - - -class TBD(Generic[PV], Parameter[PV]): - def __init__(self) -> None: - super().__init__() +class TBD[PV](Parameter[PV]): def __eq__(self, __value: object) -> bool: if isinstance(__value, TBD): return True diff --git a/src/faebryk/library/TD541S485H.py b/src/faebryk/library/TD541S485H.py index d07f92bf..a6c203b6 100644 --- a/src/faebryk/library/TD541S485H.py +++ b/src/faebryk/library/TD541S485H.py @@ -3,69 +3,55 @@ import logging -from faebryk.core.core import Module -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.RS485 import RS485 -from faebryk.library.UART_Base import UART_Base +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L +from faebryk.libs.units import P logger = logging.getLogger(__name__) class TD541S485H(Module): - def __init__(self) -> None: - super().__init__() - - class _NODEs(Module.NODES()): ... - - self.NODEs = _NODEs(self) - - class _IFs(Module.IFS()): - power = ElectricPower() - power_iso_in = ElectricPower() - power_iso_out = ElectricPower() - uart = UART_Base() - rs485 = RS485() - read_enable = Electrical() - write_enable = Electrical() - - self.IFs = _IFs(self) - - class _PARAMs(Module.PARAMS()): ... - - self.PARAMs = _PARAMs(self) - - self.IFs.power.get_trait(can_be_decoupled).decouple() - self.IFs.power_iso_in.get_trait(can_be_decoupled).decouple() - self.IFs.power_iso_out.get_trait(can_be_decoupled).decouple() - - self.add_trait(has_designator_prefix_defined("U")) - - self.IFs.power_iso_in.IFs.lv.connect(self.IFs.power_iso_out.IFs.lv) - - x = self.IFs - self.add_trait( - can_attach_to_footprint_via_pinmap( - { - "1": x.power.IFs.lv, - "2": x.power.IFs.hv, - "3": x.uart.IFs.rx.IFs.signal, - "4": x.read_enable, - "5": x.write_enable, - "6": x.uart.IFs.tx.IFs.signal, - "7": x.power.IFs.hv, - "8": x.power.IFs.lv, - "9": x.power_iso_out.IFs.lv, - "10": x.power_iso_out.IFs.hv, - "13": x.rs485.IFs.diff_pair.IFs.n, - "14": x.rs485.IFs.diff_pair.IFs.p, - "15": x.power_iso_in.IFs.hv, - "16": x.power_iso_in.IFs.lv, - } - ) + power: F.ElectricPower + power_iso_in: F.ElectricPower + power_iso_out: F.ElectricPower + uart: F.UART_Base + rs485: F.RS485 + read_enable: F.ElectricLogic + write_enable: F.ElectricLogic + + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") + + def __preinit__(self): + self.power.decoupled.decouple() + self.power_iso_in.decoupled.decouple() + self.power_iso_out.decoupled.decouple() + + self.power_iso_in.lv.connect(self.power_iso_out.lv) + self.power_iso_out.voltage.merge(5 * P.V) + + @L.rt_field + def attach_to_footprint(self): + x = self + return F.can_attach_to_footprint_via_pinmap( + { + "1": x.power.lv, + "2": x.power.hv, + "3": x.uart.rx.signal, + "4": x.read_enable.signal, + "5": x.write_enable.signal, + "6": x.uart.tx.signal, + "7": x.power.hv, + "8": x.power.lv, + "9": x.power_iso_out.lv, + "10": x.power_iso_out.hv, + "13": x.rs485.diff_pair.n, + "14": x.rs485.diff_pair.p, + "15": x.power_iso_in.hv, + "16": x.power_iso_in.lv, + } ) + + datasheet = L.f_field(F.has_datasheet_defined)( + "https://www.mornsun-power.com/public/uploads/pdf/TD(H)541S485H.pdf" + ) diff --git a/src/faebryk/library/TI_CD4011BE.py b/src/faebryk/library/TI_CD4011BE.py index 24216257..c75462da 100644 --- a/src/faebryk/library/TI_CD4011BE.py +++ b/src/faebryk/library/TI_CD4011BE.py @@ -1,42 +1,39 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.can_attach_via_pinmap import can_attach_via_pinmap -from faebryk.library.CD4011 import CD4011 -from faebryk.library.DIP import DIP -from faebryk.library.has_defined_descriptive_properties import ( - has_defined_descriptive_properties, -) -from faebryk.library.has_defined_footprint import has_defined_footprint + +import faebryk.library._F as F +from faebryk.libs.library import L from faebryk.libs.picker.picker import DescriptiveProperties from faebryk.libs.units import P -class TI_CD4011BE(CD4011): - def __init__(self): - super().__init__() - fp = DIP(pin_cnt=14, spacing=7.62 * P.mm, long_pads=False) - self.add_trait(has_defined_footprint(fp)) - fp.get_trait(can_attach_via_pinmap).attach( +class TI_CD4011BE(F.CD4011): + footprint = L.f_field(F.has_footprint_defined)( + F.DIP(pin_cnt=14, spacing=7.62 * P.mm, long_pads=False) + ) + + def __preinit__(self): + self.footprint.get_footprint().get_trait(F.can_attach_via_pinmap).attach( { - "7": self.IFs.power.IFs.lv, - "14": self.IFs.power.IFs.hv, - "3": self.NODEs.gates[0].IFs.outputs[0].IFs.signal, - "4": self.NODEs.gates[1].IFs.outputs[0].IFs.signal, - "11": self.NODEs.gates[2].IFs.outputs[0].IFs.signal, - "10": self.NODEs.gates[3].IFs.outputs[0].IFs.signal, - "1": self.NODEs.gates[0].IFs.inputs[0].IFs.signal, - "2": self.NODEs.gates[0].IFs.inputs[1].IFs.signal, - "5": self.NODEs.gates[1].IFs.inputs[0].IFs.signal, - "6": self.NODEs.gates[1].IFs.inputs[1].IFs.signal, - "12": self.NODEs.gates[2].IFs.inputs[0].IFs.signal, - "13": self.NODEs.gates[2].IFs.inputs[1].IFs.signal, - "9": self.NODEs.gates[3].IFs.inputs[0].IFs.signal, - "8": self.NODEs.gates[3].IFs.inputs[1].IFs.signal, + "7": self.power.lv, + "14": self.power.hv, + "3": self.gates[0].outputs[0].signal, + "4": self.gates[1].outputs[0].signal, + "11": self.gates[2].outputs[0].signal, + "10": self.gates[3].outputs[0].signal, + "1": self.gates[0].inputs[0].signal, + "2": self.gates[0].inputs[1].signal, + "5": self.gates[1].inputs[0].signal, + "6": self.gates[1].inputs[1].signal, + "12": self.gates[2].inputs[0].signal, + "13": self.gates[2].inputs[1].signal, + "9": self.gates[3].inputs[0].signal, + "8": self.gates[3].inputs[1].signal, } ) - has_defined_descriptive_properties.add_properties_to( + F.has_descriptive_properties_defined.add_properties_to( self, { DescriptiveProperties.manufacturer: "Texas Instruments", diff --git a/src/faebryk/library/TVS.py b/src/faebryk/library/TVS.py index 4c29db42..205643a5 100644 --- a/src/faebryk/library/TVS.py +++ b/src/faebryk/library/TVS.py @@ -3,18 +3,11 @@ import logging -from faebryk.library.Diode import Diode -from faebryk.library.TBD import TBD +import faebryk.library._F as F from faebryk.libs.units import Quantity logger = logging.getLogger(__name__) -class TVS(Diode): - def __init__(self): - super().__init__() - - class _PARAMs(Diode.PARAMS()): - reverse_breakdown_voltage = TBD[Quantity]() - - self.PARAMs = _PARAMs(self) +class TVS(F.Diode): + reverse_breakdown_voltage: F.TBD[Quantity] diff --git a/src/faebryk/library/TXS0102DCUR.py b/src/faebryk/library/TXS0102DCUR.py index 9f3c5e82..2fa0bf11 100644 --- a/src/faebryk/library/TXS0102DCUR.py +++ b/src/faebryk/library/TXS0102DCUR.py @@ -1,16 +1,9 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.libs.util import times +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L class TXS0102DCUR(Module): @@ -20,64 +13,47 @@ class TXS0102DCUR(Module): """ class _BidirectionalLevelShifter(Module): - def __init__(self) -> None: - super().__init__() - - # interfaces - class _IFs(Module.IFS()): - io_a = ElectricLogic() - io_b = ElectricLogic() - - self.IFs = _IFs(self) - - # TODO: bridge shallow - # self.add_trait(can_bridge_defined(self.IFs.io_a, self.IFs.io_b)) - - def __init__(self) -> None: - super().__init__() - # interfaces - class _IFs(Module.IFS()): - voltage_a_power = ElectricPower() - voltage_b_power = ElectricPower() - n_oe = ElectricLogic() + io_a: F.ElectricLogic + io_b: F.ElectricLogic - self.IFs = _IFs(self) + # TODO: bridge shallow - class _NODEs(Module.NODES()): - shifters = times(2, self._BidirectionalLevelShifter) + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(self.io_a, self.io_b) - self.NODEs = _NODEs(self) + # interfaces - class _PARAMs(Module.PARAMS()): ... + voltage_a_power: F.ElectricPower + voltage_b_power: F.ElectricPower + n_oe: F.ElectricLogic - self.PARAMs = _PARAMs(self) + shifters = L.list_field(2, _BidirectionalLevelShifter) - gnd = self.IFs.voltage_a_power.IFs.lv - gnd.connect(self.IFs.voltage_b_power.IFs.lv) + def __preinit__(self): + gnd = self.voltage_a_power.lv + gnd.connect(self.voltage_b_power.lv) - self.IFs.voltage_a_power.get_trait(can_be_decoupled).decouple() - self.IFs.voltage_b_power.get_trait(can_be_decoupled).decouple() + self.voltage_a_power.decoupled.decouple() + self.voltage_b_power.decoupled.decouple() # eo is referenced to voltage_a_power (active high) - self.IFs.n_oe.connect_reference(self.IFs.voltage_a_power) + self.n_oe.connect_reference(self.voltage_a_power) - for shifter in self.NODEs.shifters: - side_a = shifter.IFs.io_a - # side_a.IFs.reference.connect(self.IFs.voltage_a_power) + for shifter in self.shifters: + side_a = shifter.io_a + # side_a.reference.connect(self.voltage_a_power) side_a.add_trait( - has_single_electric_reference_defined(self.IFs.voltage_a_power) + F.has_single_electric_reference_defined(self.voltage_a_power) ) - side_b = shifter.IFs.io_b - # side_b.IFs.reference.connect(self.IFs.voltage_b_power) + side_b = shifter.io_b + # side_b.reference.connect(self.voltage_b_power) side_b.add_trait( - has_single_electric_reference_defined(self.IFs.voltage_b_power) + F.has_single_electric_reference_defined(self.voltage_b_power) ) - self.add_trait(has_designator_prefix_defined("U")) - - self.add_trait( - has_datasheet_defined( - "https://datasheet.lcsc.com/lcsc/1810292010_Texas-Instruments-TXS0102DCUR_C53434.pdf" - ) - ) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") + datasheet = L.f_field(F.has_datasheet_defined)( + "https://datasheet.lcsc.com/lcsc/1810292010_Texas-Instruments-TXS0102DCUR_C53434.pdf" + ) diff --git a/src/faebryk/library/TXS0102DCUR_UART.py b/src/faebryk/library/TXS0102DCUR_UART.py index b2b717b3..b9eec658 100644 --- a/src/faebryk/library/TXS0102DCUR_UART.py +++ b/src/faebryk/library/TXS0102DCUR_UART.py @@ -1,10 +1,8 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.TXS0102DCUR import TXS0102DCUR -from faebryk.library.UART_Base import UART_Base +import faebryk.library._F as F +from faebryk.core.module import Module class TXS0102DCUR_UART(Module): @@ -13,43 +11,27 @@ class TXS0102DCUR_UART(Module): - Output enabled by default """ - def __init__(self) -> None: - super().__init__() + voltage_a_power: F.ElectricPower + voltage_b_power: F.ElectricPower + voltage_a_bus: F.UART_Base + voltage_b_bus: F.UART_Base - class _IFs(Module.IFS()): - voltage_a_power = ElectricPower() - voltage_b_power = ElectricPower() - voltage_a_bus = UART_Base() - voltage_b_bus = UART_Base() + buffer: F.TXS0102DCUR - self.IFs = _IFs(self) - - class _NODEs(Module.NODES()): - buffer = TXS0102DCUR() - - self.NODEs = _NODEs(self) - - self.IFs.voltage_a_power.connect(self.NODEs.buffer.IFs.voltage_a_power) - self.IFs.voltage_b_power.connect(self.NODEs.buffer.IFs.voltage_b_power) + def __preinit__(self): + self.voltage_a_power.connect(self.buffer.voltage_a_power) + self.voltage_b_power.connect(self.buffer.voltage_b_power) # enable output by default - self.NODEs.buffer.IFs.n_oe.set(True) + self.buffer.n_oe.set(True) # connect UART interfaces to level shifter - self.IFs.voltage_a_bus.IFs.rx.connect( - self.NODEs.buffer.NODEs.shifters[0].IFs.io_a - ) - self.IFs.voltage_a_bus.IFs.tx.connect( - self.NODEs.buffer.NODEs.shifters[1].IFs.io_a - ) - self.IFs.voltage_b_bus.IFs.rx.connect( - self.NODEs.buffer.NODEs.shifters[0].IFs.io_b - ) - self.IFs.voltage_b_bus.IFs.tx.connect( - self.NODEs.buffer.NODEs.shifters[1].IFs.io_b - ) + self.voltage_a_bus.rx.connect(self.buffer.shifters[0].io_a) + self.voltage_a_bus.tx.connect(self.buffer.shifters[1].io_a) + self.voltage_b_bus.rx.connect(self.buffer.shifters[0].io_b) + self.voltage_b_bus.tx.connect(self.buffer.shifters[1].io_b) # TODO # self.add_trait( - # can_bridge_defined(self.IFs.voltage_a_bus, self.IFs.voltage_b_bus) + # can_bridge_defined(self.voltage_a_bus, self.voltage_b_bus) # ) diff --git a/src/faebryk/library/UART.py b/src/faebryk/library/UART.py index 3e6caa1c..8d7975c2 100644 --- a/src/faebryk/library/UART.py +++ b/src/faebryk/library/UART.py @@ -1,20 +1,13 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface -from faebryk.library.Electrical import Electrical -from faebryk.library.UART_Base import UART_Base +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface class UART(ModuleInterface): - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) - - class IFS(super().IFS()): - base_uart = UART_Base() - rts = Electrical() - cts = Electrical() - dtr = Electrical() - dsr = Electrical() - - self.IFs = IFS(self) + base_uart: F.UART_Base + rts: F.ElectricLogic + cts: F.ElectricLogic + dtr: F.ElectricLogic + dsr: F.ElectricLogic diff --git a/src/faebryk/library/UART_Base.py b/src/faebryk/library/UART_Base.py index 0e04e179..025fe017 100644 --- a/src/faebryk/library/UART_Base.py +++ b/src/faebryk/library/UART_Base.py @@ -1,34 +1,25 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L from faebryk.libs.units import Quantity class UART_Base(ModuleInterface): - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) + rx: F.ElectricLogic + tx: F.ElectricLogic - class IFS(ModuleInterface.IFS()): - rx = ElectricLogic() - tx = ElectricLogic() + baud: F.TBD[Quantity] - self.IFs = IFS(self) - - class PARAMS(ModuleInterface.PARAMS()): - baud = TBD[Quantity]() - - self.PARAMs = PARAMS(self) - - ref = ElectricLogic.connect_all_module_references(self) - self.add_trait(has_single_electric_reference_defined(ref)) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) + ) def _on_connect(self, other: "UART_Base"): super()._on_connect(other) - self.PARAMs.baud.merge(other.PARAMs.baud) + self.baud.merge(other.baud) diff --git a/src/faebryk/library/UART_RS485.py b/src/faebryk/library/UART_RS485.py index 15d58dae..312dc6fc 100644 --- a/src/faebryk/library/UART_RS485.py +++ b/src/faebryk/library/UART_RS485.py @@ -3,45 +3,26 @@ import logging -from faebryk.core.core import Module -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.Range import Range -from faebryk.library.RS485 import RS485 -from faebryk.library.TBD import TBD -from faebryk.library.UART_Base import UART_Base +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import P, Quantity logger = logging.getLogger(__name__) class UART_RS485(Module): - def __init__(self) -> None: - super().__init__() + power: F.ElectricPower + uart: F.UART_Base + rs485: F.RS485 + read_enable: F.ElectricLogic + write_enable: F.ElectricLogic - class _NODEs(Module.NODES()): ... + max_data_rate: F.TBD[Quantity] + gpio_voltage: F.TBD[Quantity] - self.NODEs = _NODEs(self) + def __preinit__(self): + self.power.voltage.merge(F.Range(3.3 * P.V, 5.0 * P.V)) + self.power.decoupled.decouple() - class _IFs(Module.IFS()): - power = ElectricPower() - uart = UART_Base() - rs485 = RS485() - read_enable = Electrical() - write_enable = Electrical() - - self.IFs = _IFs(self) - - class _PARAMs(Module.PARAMS()): - max_data_rate = TBD[Quantity]() - gpio_voltage = TBD[Quantity]() - - self.PARAMs = _PARAMs(self) - - self.IFs.power.PARAMs.voltage.merge(Range(3.3 * P.V, 5.0 * P.V)) - - self.IFs.power.get_trait(can_be_decoupled).decouple() - - self.add_trait(has_designator_prefix_defined("U")) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") diff --git a/src/faebryk/library/USB2514B.py b/src/faebryk/library/USB2514B.py index f2dbb6bd..ab68fa63 100644 --- a/src/faebryk/library/USB2514B.py +++ b/src/faebryk/library/USB2514B.py @@ -4,17 +4,10 @@ import logging from enum import Enum, auto -from faebryk.core.core import Module -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.DifferentialPair import DifferentialPair -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.I2C import I2C -from faebryk.library.TBD import TBD -from faebryk.libs.util import times +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L +from faebryk.libs.units import P logger = logging.getLogger(__name__) @@ -26,100 +19,110 @@ class InterfaceConfiguration(Enum): BUS_POWERED = auto() EEPROM = auto() - def __init__(self) -> None: - super().__init__() + class NonRemovablePortConfiguration(Enum): + ALL_PORTS_REMOVABLE = auto() + PORT_1_NOT_REMOVABLE = auto() + PORT_1_2_NOT_REMOVABLE = auto() + PORT_1_2_3_NOT_REMOVABLE = auto() - class _NODEs(Module.NODES()): ... + VDD33: F.ElectricPower + VDDA33: F.ElectricPower - self.NODEs = _NODEs(self) + PLLFILT: F.ElectricPower + CRFILT: F.ElectricPower - class _IFs(Module.IFS()): - VDD33 = ElectricPower() - VDDA33 = ElectricPower() + VBUS_DET: F.Electrical - PLLFILT = ElectricPower() - CRFILT = ElectricPower() + usb_downstream = L.list_field(4, F.DifferentialPair) + usb_upstream = F.DifferentialPair - VBUS_DET = Electrical() + XTALIN: F.Electrical + XTALOUT: F.Electrical - usb_downstream = times(4, DifferentialPair) - usb_upstream = DifferentialPair() + TEST: F.Electrical + SUSP_IND: F.ElectricLogic + RESET_N: F.Electrical + RBIAS: F.Electrical + NON_REM = L.list_field(2, F.ElectricLogic) + LOCAL_PWR: F.Electrical + CLKIN: F.Electrical + CFG_SEL = L.list_field(2, F.ElectricLogic) - XTALIN = Electrical() - XTALOUT = Electrical() + HS_IND: F.ElectricLogic - TEST = Electrical() - SUSP_IND = ElectricLogic() - RESET_N = Electrical() - RBIAS = Electrical() - NON_REM = times(2, ElectricLogic) - LOCAL_PWR = Electrical() - CLKIN = Electrical() - CFG_SEL = times(2, ElectricLogic) + PRTPWR = L.list_field(4, F.ElectricLogic) + PRT_DIS_P = L.list_field(4, F.ElectricLogic) + PRT_DIS_M = L.list_field(4, F.ElectricLogic) + OCS_N = L.list_field(4, F.ElectricLogic) + BC_EN = L.list_field(4, F.ElectricLogic) - HS_IND = ElectricLogic() + i2c: F.I2C + gnd: F.Electrical - PRTPWR = times(4, ElectricLogic) - PRT_DIS_P = times(4, ElectricLogic) - PRT_DIS_M = times(4, ElectricLogic) - OCS_N = times(4, ElectricLogic) - BC_EN = times(4, ElectricLogic) + interface_configuration: F.TBD[InterfaceConfiguration] + non_removable_port_configuration: F.TBD[NonRemovablePortConfiguration] - i2c = I2C() + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") - self.IFs = _IFs(self) + def __preinit__(self): + if self.interface_configuration == USB2514B.InterfaceConfiguration.DEFAULT: + self.CFG_SEL[0].pulled.pull(up=False) + self.CFG_SEL[1].pulled.pull(up=False) + elif self.interface_configuration == USB2514B.InterfaceConfiguration.SMBUS: + self.CFG_SEL[0].pulled.pull(up=True) + self.CFG_SEL[1].pulled.pull(up=False) + elif ( + self.interface_configuration == USB2514B.InterfaceConfiguration.BUS_POWERED + ): + self.CFG_SEL[0].pulled.pull(up=False) + self.CFG_SEL[1].pulled.pull(up=True) + elif self.interface_configuration == USB2514B.InterfaceConfiguration.EEPROM: + self.CFG_SEL[0].pulled.pull(up=True) + self.CFG_SEL[1].pulled.pull(up=True) - class _PARAMs(Module.PARAMS()): - interface_configuration = TBD[USB2514B.InterfaceConfiguration]() + # Add decoupling capacitors to power pins and connect all lv to gnd + # TODO: decouple with 1.0uF and 0.1uF and maybe 4.7uF + for g in self.get_children(direct_only=True, types=F.ElectricPower): + g.decoupled.decouple() + g.lv.connect(self.gnd) - self.PARAMs = _PARAMs(self) + x = self - self.add_trait(has_designator_prefix_defined("U")) + x.CFG_SEL[0].connect(x.i2c.scl) + x.CFG_SEL[1].connect(x.HS_IND) + x.NON_REM[0].connect(x.SUSP_IND) + x.NON_REM[1].connect(x.i2c.sda) + + x.RESET_N.connect(self.gnd) + + self.PLLFILT.voltage.merge(1.8 * P.V) + self.CRFILT.voltage.merge(1.8 * P.V) if ( - self.PARAMs.interface_configuration - == USB2514B.InterfaceConfiguration.DEFAULT + self.non_removable_port_configuration + == USB2514B.NonRemovablePortConfiguration.ALL_PORTS_REMOVABLE ): - self.IFs.CFG_SEL[0].get_trait(ElectricLogic.can_be_pulled).pull(up=False) - self.IFs.CFG_SEL[1].get_trait(ElectricLogic.can_be_pulled).pull(up=False) + self.NON_REM[0].get_trait(F.ElectricLogic.can_be_pulled).pull(up=False) + self.NON_REM[1].get_trait(F.ElectricLogic.can_be_pulled).pull(up=False) elif ( - self.PARAMs.interface_configuration == USB2514B.InterfaceConfiguration.SMBUS + self.non_removable_port_configuration + == USB2514B.NonRemovablePortConfiguration.PORT_1_NOT_REMOVABLE ): - self.IFs.CFG_SEL[0].get_trait(ElectricLogic.can_be_pulled).pull(up=True) - self.IFs.CFG_SEL[1].get_trait(ElectricLogic.can_be_pulled).pull(up=False) + self.NON_REM[0].get_trait(F.ElectricLogic.can_be_pulled).pull(up=True) + self.NON_REM[1].get_trait(F.ElectricLogic.can_be_pulled).pull(up=False) elif ( - self.PARAMs.interface_configuration - == USB2514B.InterfaceConfiguration.BUS_POWERED + self.non_removable_port_configuration + == USB2514B.NonRemovablePortConfiguration.PORT_1_2_NOT_REMOVABLE ): - self.IFs.CFG_SEL[0].get_trait(ElectricLogic.can_be_pulled).pull(up=False) - self.IFs.CFG_SEL[1].get_trait(ElectricLogic.can_be_pulled).pull(up=True) + self.NON_REM[0].get_trait(F.ElectricLogic.can_be_pulled).pull(up=False) + self.NON_REM[1].get_trait(F.ElectricLogic.can_be_pulled).pull(up=True) elif ( - self.PARAMs.interface_configuration - == USB2514B.InterfaceConfiguration.EEPROM + self.non_removable_port_configuration + == USB2514B.NonRemovablePortConfiguration.PORT_1_2_3_NOT_REMOVABLE ): - self.IFs.CFG_SEL[0].get_trait(ElectricLogic.can_be_pulled).pull(up=True) - self.IFs.CFG_SEL[1].get_trait(ElectricLogic.can_be_pulled).pull(up=True) - - gnd = Electrical() - - # Add decoupling capacitors to power pins and connect all lv to gnd - # TODO: decouple with 1.0uF and 0.1uF and maybe 4.7uF - for g in self.IFs.get_all(): - if isinstance(g, ElectricPower): - g.get_trait(can_be_decoupled).decouple() - g.IFs.lv.connect(gnd) - - x = self.IFs - - x.CFG_SEL[0].connect(x.i2c.IFs.scl) - x.CFG_SEL[1].connect(x.HS_IND) - x.NON_REM[0].connect(x.SUSP_IND) - x.NON_REM[1].connect(x.i2c.IFs.sda) - - x.RESET_N.connect(gnd) + self.NON_REM[0].get_trait(F.ElectricLogic.can_be_pulled).pull(up=True) + self.NON_REM[1].get_trait(F.ElectricLogic.can_be_pulled).pull(up=True) - self.add_trait( - has_datasheet_defined( - "https://ww1.microchip.com/downloads/aemDocuments/documents/OTH/ProductDocuments/DataSheets/00001692C.pdf" - ) - ) + datasheet = L.f_field(F.has_datasheet_defined)( + "https://ww1.microchip.com/downloads/aemDocuments/documents/UNG/ProductDocuments/DataSheets/USB251xB-xBi-Data-Sheet-DS00001692.pdf" + ) diff --git a/src/faebryk/library/USB2_0.py b/src/faebryk/library/USB2_0.py index 1f45ddba..36048ff2 100644 --- a/src/faebryk/library/USB2_0.py +++ b/src/faebryk/library/USB2_0.py @@ -1,18 +1,13 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface -from faebryk.library.Range import Range -from faebryk.library.USB2_0_IF import USB2_0_IF +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.units import P class USB2_0(ModuleInterface): - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) + usb_if: F.USB2_0_IF - class IFS(ModuleInterface.IFS()): - usb_if = USB2_0_IF() - - self.IFs = IFS(self) - - self.IFs.usb_if.IFs.buspower.PARAMs.voltage.merge(Range.from_center(5, 0.25)) + def __preinit__(self): + self.usb_if.buspower.voltage.merge(F.Range.from_center(5 * P.V, 0.25 * P.V)) diff --git a/src/faebryk/library/USB2_0_ESD_Protection.py b/src/faebryk/library/USB2_0_ESD_Protection.py index cdb680ea..295c2c02 100644 --- a/src/faebryk/library/USB2_0_ESD_Protection.py +++ b/src/faebryk/library/USB2_0_ESD_Protection.py @@ -3,53 +3,28 @@ import logging -from faebryk.core.core import Module -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.Range import Range -from faebryk.library.TBD import TBD -from faebryk.library.USB2_0 import USB2_0 +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import P -from faebryk.libs.util import times logger = logging.getLogger(__name__) class USB2_0_ESD_Protection(Module): - def __init__(self) -> None: - super().__init__() + usb = L.list_field(2, F.USB2_0) - class _NODEs(Module.NODES()): ... + vbus_esd_protection: F.TBD[bool] + data_esd_protection: F.TBD[bool] - self.NODEs = _NODEs(self) + def __preinit__(self): + self.usb[0].usb_if.buspower.voltage.merge(F.Range(4.75 * P.V, 5.25 * P.V)) + self.usb[0].connect(self.usb[1]) + self.usb[0].usb_if.buspower.connect(self.usb[1].usb_if.buspower) + self.usb[0].usb_if.buspower.decoupled.decouple() - class _IFs(Module.IFS()): - usb = times(2, USB2_0) + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(self.usb[0].usb_if.d, self.usb[1].usb_if.d) - self.IFs = _IFs(self) - - class _PARAMs(Module.PARAMS()): - vbus_esd_protection = TBD[bool]() - data_esd_protection = TBD[bool]() - - self.PARAMs = _PARAMs(self) - - self.IFs.usb[0].IFs.usb_if.IFs.buspower.PARAMs.voltage.merge( - Range(4.75 * P.V, 5.25 * P.V) - ) - - self.add_trait( - can_bridge_defined( - self.IFs.usb[0].IFs.usb_if.IFs.d, self.IFs.usb[1].IFs.usb_if.IFs.d - ) - ) - self.IFs.usb[0].connect(self.IFs.usb[1]) - - self.IFs.usb[0].IFs.usb_if.IFs.buspower.connect( - self.IFs.usb[1].IFs.usb_if.IFs.buspower - ) - - self.IFs.usb[0].IFs.usb_if.IFs.buspower.get_trait(can_be_decoupled).decouple() - - self.add_trait(has_designator_prefix_defined("U")) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") diff --git a/src/faebryk/library/USB2_0_IF.py b/src/faebryk/library/USB2_0_IF.py index 83f477a3..222124bf 100644 --- a/src/faebryk/library/USB2_0_IF.py +++ b/src/faebryk/library/USB2_0_IF.py @@ -1,17 +1,17 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface -from faebryk.library.DifferentialPair import DifferentialPair -from faebryk.library.ElectricPower import ElectricPower +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L class USB2_0_IF(ModuleInterface): - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) + d: F.DifferentialPair + buspower: F.ElectricPower - class IFS(ModuleInterface.IFS()): - d = DifferentialPair() - buspower = ElectricPower() - - self.IFs = IFS(self) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) + ) diff --git a/src/faebryk/library/USB3.py b/src/faebryk/library/USB3.py index 178183e7..7626ca2a 100644 --- a/src/faebryk/library/USB3.py +++ b/src/faebryk/library/USB3.py @@ -1,25 +1,21 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface -from faebryk.library.Range import Range -from faebryk.library.USB3_IF import USB3_IF +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L from faebryk.libs.units import P class USB3(ModuleInterface): - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) + usb3_if: F.USB3_IF - class IFS(ModuleInterface.IFS()): - usb3_if = USB3_IF() + def __preinit__(self): + self.usb3_if.gnd_drain.connect(self.usb3_if.usb_if.buspower.lv) + self.usb3_if.usb_if.buspower.voltage.merge(F.Range(4.75 * P.V, 5.5 * P.V)) - self.IFs = IFS(self) - - self.IFs.usb3_if.IFs.gnd_drain.connect( - self.IFs.usb3_if.IFs.usb_if.IFs.buspower.IFs.lv - ) - - self.IFs.usb3_if.IFs.usb_if.IFs.buspower.PARAMs.voltage.merge( - Range(4.75 * P.V, 5.5 * P.V) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) ) diff --git a/src/faebryk/library/USB3_IF.py b/src/faebryk/library/USB3_IF.py index 30dc4441..13421a96 100644 --- a/src/faebryk/library/USB3_IF.py +++ b/src/faebryk/library/USB3_IF.py @@ -1,20 +1,19 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface -from faebryk.library.DifferentialPair import DifferentialPair -from faebryk.library.Electrical import Electrical -from faebryk.library.USB2_0_IF import USB2_0_IF +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L class USB3_IF(ModuleInterface): - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) + usb_if: F.USB2_0_IF + rx: F.DifferentialPair + tx: F.DifferentialPair + gnd_drain: F.Electrical - class IFS(ModuleInterface.IFS()): - usb_if = USB2_0_IF() - rx = DifferentialPair() - tx = DifferentialPair() - gnd_drain = Electrical() - - self.IFs = IFS(self) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) + ) diff --git a/src/faebryk/library/USB3_connector.py b/src/faebryk/library/USB3_connector.py index 44c3a6b4..13f6c416 100644 --- a/src/faebryk/library/USB3_connector.py +++ b/src/faebryk/library/USB3_connector.py @@ -3,40 +3,21 @@ import logging -from faebryk.core.core import Module -from faebryk.library.Electrical import Electrical -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.Range import Range -from faebryk.library.USB3 import USB3 +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import P logger = logging.getLogger(__name__) class USB3_connector(Module): - def __init__(self) -> None: - super().__init__() + usb3: F.USB3 + shield: F.Electrical - class _NODEs(Module.NODES()): ... + def __preinit__(self): + self.usb3.usb3_if.usb_if.buspower.voltage.merge(F.Range(4.75 * P.V, 5.25 * P.V)) - self.NODEs = _NODEs(self) + self.usb3.usb3_if.usb_if.buspower.lv.connect(self.usb3.usb3_if.gnd_drain) - class _IFs(Module.IFS()): - usb3 = USB3() - shield = Electrical() - - self.IFs = _IFs(self) - - class _PARAMs(Module.PARAMS()): ... - - self.PARAMs = _PARAMs(self) - - self.IFs.usb3.IFs.usb3_if.IFs.usb_if.IFs.buspower.PARAMs.voltage.merge( - Range(4.75 * P.V, 5.25 * P.V) - ) - - self.IFs.usb3.IFs.usb3_if.IFs.usb_if.IFs.buspower.IFs.lv.connect( - self.IFs.usb3.IFs.usb3_if.IFs.gnd_drain - ) - - self.add_trait(has_designator_prefix_defined("J")) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("J") diff --git a/src/faebryk/library/USBLC6_2P6.py b/src/faebryk/library/USBLC6_2P6.py index 8d741074..86484a52 100644 --- a/src/faebryk/library/USBLC6_2P6.py +++ b/src/faebryk/library/USBLC6_2P6.py @@ -1,13 +1,9 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.USB2_0 import USB2_0 +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L class USBLC6_2P6(Module): @@ -15,33 +11,24 @@ class USBLC6_2P6(Module): Low capacitance TVS diode array (for USB2.0) """ - def __init__(self) -> None: - super().__init__() - - # interfaces - class _IFs(Module.IFS()): - usb = USB2_0() - - self.IFs = _IFs(self) - - x = self.IFs - self.add_trait( - can_attach_to_footprint_via_pinmap( - { - "1": x.usb.IFs.usb_if.IFs.d.IFs.p, - "2": x.usb.IFs.usb_if.IFs.buspower.IFs.lv, - "3": x.usb.IFs.usb_if.IFs.d.IFs.n, - "4": x.usb.IFs.usb_if.IFs.d.IFs.n, - "5": x.usb.IFs.usb_if.IFs.buspower.IFs.hv, - "6": x.usb.IFs.usb_if.IFs.d.IFs.p, - } - ) + # interfaces + usb: F.USB2_0 + + @L.rt_field + def attach_to_footprint(self): + x = self + return F.can_attach_to_footprint_via_pinmap( + { + "1": x.usb.usb_if.d.p, + "2": x.usb.usb_if.buspower.lv, + "3": x.usb.usb_if.d.n, + "4": x.usb.usb_if.d.n, + "5": x.usb.usb_if.buspower.hv, + "6": x.usb.usb_if.d.p, + } ) - self.add_trait(has_designator_prefix_defined("U")) - - self.add_trait( - has_datasheet_defined( - "https://datasheet.lcsc.com/lcsc/2108132230_TECH-PUBLIC-USBLC6-2P6_C2827693.pdf" - ) - ) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") + datasheet = L.f_field(F.has_datasheet_defined)( + "https://datasheet.lcsc.com/lcsc/2108132230_TECH-PUBLIC-USBLC6-2P6_C2827693.pdf" + ) diff --git a/src/faebryk/library/USB_C.py b/src/faebryk/library/USB_C.py index 9144d962..5fa63fee 100644 --- a/src/faebryk/library/USB_C.py +++ b/src/faebryk/library/USB_C.py @@ -1,33 +1,22 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface -from faebryk.library.DifferentialPair import DifferentialPair -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.library.USB3 import USB3 +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L class USB_C(ModuleInterface): - def __init__(self, *args, **kwargs) -> None: - super().__init__(*args, **kwargs) + usb3: F.USB3 + cc1: F.Electrical + cc2: F.Electrical + sbu1: F.Electrical + sbu2: F.Electrical + rx: F.DifferentialPair + tx: F.DifferentialPair - class IFS(ModuleInterface.IFS()): - usb3 = USB3() - cc1 = Electrical() - cc2 = Electrical() - sbu1 = Electrical() - sbu2 = Electrical() - rx = DifferentialPair() - tx = DifferentialPair() - - self.IFs = IFS(self) - - self.add_trait( - has_single_electric_reference_defined( - ElectricLogic.connect_all_module_references(self) - ) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) ) diff --git a/src/faebryk/library/USB_C_5V_PSU.py b/src/faebryk/library/USB_C_5V_PSU.py index 97256dbd..f4b91241 100644 --- a/src/faebryk/library/USB_C_5V_PSU.py +++ b/src/faebryk/library/USB_C_5V_PSU.py @@ -1,51 +1,32 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.Constant import Constant -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.library.Resistor import Resistor -from faebryk.library.USB_C import USB_C +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import P -from faebryk.libs.util import times class USB_C_5V_PSU(Module): - def __init__(self) -> None: - super().__init__() - - # interfaces - class _IFs(Module.IFS()): - power_out = ElectricPower() - usb = USB_C() - - self.IFs = _IFs(self) - - # components - class _NODEs(Module.NODES()): - configuration_resistors = times( - 2, - lambda: Resistor().builder( - lambda r: r.PARAMs.resistance.merge(Constant(5.1 * P.kohm)) - ), - ) - - self.NODEs = _NODEs(self) - - self.add_trait( - has_single_electric_reference_defined( - ElectricLogic.connect_all_module_references(self) - ) + # interfaces + power_out: F.ElectricPower + usb: F.USB_C + + # components + configuration_resistors = L.list_field( + 2, + lambda: F.Resistor().builder( + lambda r: r.resistance.merge(F.Constant(5.1 * P.kohm)) + ), + ) + + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) ) + def __preinit__(self): # configure as ufp with 5V@max3A - self.IFs.usb.IFs.cc1.connect_via( - self.NODEs.configuration_resistors[0], self.IFs.power_out.IFs.lv - ) - self.IFs.usb.IFs.cc2.connect_via( - self.NODEs.configuration_resistors[1], self.IFs.power_out.IFs.lv - ) + self.usb.cc1.connect_via(self.configuration_resistors[0], self.power_out.lv) + self.usb.cc2.connect_via(self.configuration_resistors[1], self.power_out.lv) diff --git a/src/faebryk/library/USB_C_PSU_Vertical.py b/src/faebryk/library/USB_C_PSU_Vertical.py index 77e2e9d8..02626e07 100644 --- a/src/faebryk/library/USB_C_PSU_Vertical.py +++ b/src/faebryk/library/USB_C_PSU_Vertical.py @@ -1,82 +1,61 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.core.util import connect_all_interfaces -from faebryk.library.Capacitor import Capacitor -from faebryk.library.Constant import Constant -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.Fuse import Fuse -from faebryk.library.Resistor import Resistor -from faebryk.library.USB2_0 import USB2_0 -from faebryk.library.USB2_0_ESD_Protection import USB2_0_ESD_Protection -from faebryk.library.USB_Type_C_Receptacle_14_pin_Vertical import ( - USB_Type_C_Receptacle_14_pin_Vertical, -) +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import P -from faebryk.libs.util import times class USB_C_PSU_Vertical(Module): - def __init__(self) -> None: - super().__init__() - - # interfaces - class _IFs(Module.IFS()): - power_out = ElectricPower() - usb = USB2_0() - - self.IFs = _IFs(self) - - # components - class _NODEs(Module.NODES()): - usb_connector = ( - USB_Type_C_Receptacle_14_pin_Vertical() - ) # TODO: make generic - configuration_resistors = times(2, Resistor) - gnd_resistor = Resistor() - gnd_capacitor = Capacitor() - esd = USB2_0_ESD_Protection() - fuse = Fuse() - - self.NODEs = _NODEs(self) - - self.NODEs.gnd_capacitor.PARAMs.capacitance.merge(100 * P.nF) - self.NODEs.gnd_capacitor.PARAMs.rated_voltage.merge(16 * P.V) - self.NODEs.gnd_resistor.PARAMs.resistance.merge(1 * P.Mohm) - for res in self.NODEs.configuration_resistors: - res.PARAMs.resistance.merge(5.1 * P.kohm) - self.NODEs.fuse.PARAMs.fuse_type.merge(Fuse.FuseType.RESETTABLE) - self.NODEs.fuse.PARAMs.trip_current.merge(Constant(1 * P.A)) + # interfaces + power_out: F.ElectricPower + usb: F.USB2_0 + + # components + + usb_connector: F.USB_Type_C_Receptacle_14_pin_Vertical # TODO: make generic + configuration_resistors = L.list_field(2, F.Resistor) + gnd_resistor: F.Resistor + gnd_capacitor: F.Capacitor + esd: F.USB2_0_ESD_Protection + fuse: F.Fuse + + def __preinit__(self): + from faebryk.core.util import connect_all_interfaces + + self.gnd_capacitor.capacitance.merge(100 * P.nF) + self.gnd_capacitor.rated_voltage.merge(16 * P.V) + self.gnd_resistor.resistance.merge(1 * P.Mohm) + for res in self.configuration_resistors: + res.resistance.merge(5.1 * P.kohm) + self.fuse.fuse_type.merge(F.Fuse.FuseType.RESETTABLE) + self.fuse.trip_current.merge(F.Constant(1 * P.A)) # alliases - vcon = self.NODEs.usb_connector.IFs.vbus - vusb = self.IFs.usb.IFs.usb_if.IFs.buspower - v5 = self.IFs.power_out - gnd = v5.IFs.lv + vcon = self.usb_connector.vbus + vusb = self.usb.usb_if.buspower + v5 = self.power_out + gnd = v5.lv - vcon.IFs.hv.connect_via(self.NODEs.fuse, v5.IFs.hv) - vcon.IFs.lv.connect(gnd) - vusb.IFs.lv.connect(gnd) - v5.connect(self.NODEs.esd.IFs.usb[0].IFs.usb_if.IFs.buspower) + vcon.hv.connect_via(self.fuse, v5.hv) + vcon.lv.connect(gnd) + vusb.lv.connect(gnd) + v5.connect(self.esd.usb[0].usb_if.buspower) # connect usb data connect_all_interfaces( [ - self.NODEs.usb_connector.IFs.usb.IFs.usb_if.IFs.d, - self.IFs.usb.IFs.usb_if.IFs.d, - self.NODEs.esd.IFs.usb[0].IFs.usb_if.IFs.d, + self.usb_connector.usb.usb_if.d, + self.usb.usb_if.d, + self.esd.usb[0].usb_if.d, ] ) # configure as ufp with 5V@max3A - self.NODEs.usb_connector.IFs.cc1.connect_via( - self.NODEs.configuration_resistors[0], gnd - ) - self.NODEs.usb_connector.IFs.cc2.connect_via( - self.NODEs.configuration_resistors[1], gnd - ) + self.usb_connector.cc1.connect_via(self.configuration_resistors[0], gnd) + self.usb_connector.cc2.connect_via(self.configuration_resistors[1], gnd) # EMI shielding - self.NODEs.usb_connector.IFs.shield.connect_via(self.NODEs.gnd_resistor, gnd) - self.NODEs.usb_connector.IFs.shield.connect_via(self.NODEs.gnd_capacitor, gnd) + self.usb_connector.shield.connect_via(self.gnd_resistor, gnd) + self.usb_connector.shield.connect_via(self.gnd_capacitor, gnd) diff --git a/src/faebryk/library/USB_C_PowerOnly.py b/src/faebryk/library/USB_C_PowerOnly.py index af52054e..02f5f1f6 100644 --- a/src/faebryk/library/USB_C_PowerOnly.py +++ b/src/faebryk/library/USB_C_PowerOnly.py @@ -1,47 +1,33 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface -from faebryk.library.can_be_surge_protected_defined import ( - can_be_surge_protected_defined, -) -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.library.USB_C import USB_C +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L class USB_C_PowerOnly(ModuleInterface): - def __init__(self) -> None: - super().__init__() - - class IFS(ModuleInterface.IFS()): - power = ElectricPower() - cc1 = Electrical() - cc2 = Electrical() - - self.IFs = IFS(self) - - self.add_trait( - can_be_surge_protected_defined( - self.IFs.power.IFs.lv, - self.IFs.power.IFs.hv, - self.IFs.cc1, - self.IFs.cc2, - ) + power: F.ElectricPower + cc1: F.Electrical + cc2: F.Electrical + + @L.rt_field + def surge_protected(self): + return F.can_be_surge_protected_defined( + self.power.lv, + self.power.hv, + self.cc1, + self.cc2, ) - self.add_trait( - has_single_electric_reference_defined( - ElectricLogic.connect_all_module_references(self) - ) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) ) - def connect_to_full_usb_c(self, usb_c: USB_C): - self.IFs.power.connect(usb_c.IFs.usb3.IFs.usb3_if.IFs.usb_if.IFs.buspower) - self.IFs.cc1.connect(usb_c.IFs.cc1) - self.IFs.cc2.connect(usb_c.IFs.cc2) + def connect_to_full_usb_c(self, usb_c: F.USB_C): + self.power.connect(usb_c.usb3.usb3_if.usb_if.buspower) + self.cc1.connect(usb_c.cc1) + self.cc2.connect(usb_c.cc2) return self diff --git a/src/faebryk/library/USB_RS485.py b/src/faebryk/library/USB_RS485.py index 27c67a75..3c5cb885 100644 --- a/src/faebryk/library/USB_RS485.py +++ b/src/faebryk/library/USB_RS485.py @@ -3,78 +3,52 @@ import logging -from faebryk.core.core import Module -from faebryk.library.CH340x import CH340x -from faebryk.library.Range import Range -from faebryk.library.Resistor import Resistor -from faebryk.library.RS485 import RS485 -from faebryk.library.UART_RS485 import UART_RS485 -from faebryk.library.USB2_0 import USB2_0 +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import P -from faebryk.libs.util import times logger = logging.getLogger(__name__) class USB_RS485(Module): - def __init__(self) -> None: - super().__init__() + usb_uart: F.CH340x + uart_rs485: F.UART_RS485 + termination: F.Resistor + polarization = L.list_field(2, F.Resistor) + usb: F.USB2_0 + rs485: F.RS485 - class _NODEs(Module.NODES()): - usb_uart = CH340x() - uart_rs485 = UART_RS485() - termination = Resistor() - polarization = times(2, Resistor) + def __preinit__(self): + self.usb.connect(self.usb_uart.usb) + self.usb_uart.uart.base_uart.connect(self.uart_rs485.uart) + self.rs485.connect(self.uart_rs485.rs485) - self.NODEs = _NODEs(self) + self.usb_uart.tnow.connect(self.uart_rs485.read_enable) + self.usb_uart.tnow.connect(self.uart_rs485.write_enable) - class _IFs(Module.IFS()): - usb = USB2_0() - rs485 = RS485() - - self.IFs = _IFs(self) - - class _PARAMs(Module.PARAMS()): ... - - self.PARAMs = _PARAMs(self) - - self.IFs.usb.connect(self.NODEs.usb_uart.IFs.usb) - self.NODEs.usb_uart.IFs.uart.IFs.base_uart.connect( - self.NODEs.uart_rs485.IFs.uart - ) - self.IFs.rs485.connect(self.NODEs.uart_rs485.IFs.rs485) - - self.NODEs.usb_uart.IFs.tnow.connect(self.NODEs.uart_rs485.IFs.read_enable) - self.NODEs.usb_uart.IFs.tnow.connect(self.NODEs.uart_rs485.IFs.write_enable) - - self.NODEs.usb_uart.IFs.usb.IFs.usb_if.IFs.buspower.connect( - self.NODEs.uart_rs485.IFs.power - ) - self.IFs.usb.IFs.usb_if.IFs.buspower.connect( - self.NODEs.usb_uart.IFs.usb.IFs.usb_if.IFs.buspower - ) + self.usb_uart.usb.usb_if.buspower.connect(self.uart_rs485.power) + self.usb.usb_if.buspower.connect(self.usb_uart.usb.usb_if.buspower) # connect termination resistor between RS485 A and B - self.NODEs.uart_rs485.IFs.rs485.IFs.diff_pair.IFs.n.connect_via( - self.NODEs.termination, self.NODEs.uart_rs485.IFs.rs485.IFs.diff_pair.IFs.p + self.uart_rs485.rs485.diff_pair.n.connect_via( + self.termination, self.uart_rs485.rs485.diff_pair.p ) # connect polarization resistors to RS485 A and B - self.NODEs.uart_rs485.IFs.rs485.IFs.diff_pair.IFs.p.connect_via( - self.NODEs.polarization[0], - self.NODEs.uart_rs485.IFs.power.IFs.hv, + self.uart_rs485.rs485.diff_pair.p.connect_via( + self.polarization[0], + self.uart_rs485.power.hv, ) - self.NODEs.uart_rs485.IFs.rs485.IFs.diff_pair.IFs.n.connect_via( - self.NODEs.polarization[1], - self.NODEs.uart_rs485.IFs.power.IFs.lv, + self.uart_rs485.rs485.diff_pair.n.connect_via( + self.polarization[1], + self.uart_rs485.power.lv, ) - self.NODEs.termination.PARAMs.resistance.merge( - Range.from_center(150 * P.ohm, 1.5 * P.ohm) - ) - self.NODEs.polarization[0].PARAMs.resistance.merge( - Range.from_center(680 * P.ohm, 6.8 * P.ohm) + self.termination.resistance.merge(F.Range.from_center(150 * P.ohm, 1.5 * P.ohm)) + self.polarization[0].resistance.merge( + F.Range.from_center(680 * P.ohm, 6.8 * P.ohm) ) - self.NODEs.polarization[1].PARAMs.resistance.merge( - Range.from_center(680 * P.ohm, 6.8 * P.ohm) + self.polarization[1].resistance.merge( + F.Range.from_center(680 * P.ohm, 6.8 * P.ohm) ) diff --git a/src/faebryk/library/USB_Type_C_Receptacle_14_pin_Vertical.py b/src/faebryk/library/USB_Type_C_Receptacle_14_pin_Vertical.py index 24fb4f9b..6bc59937 100644 --- a/src/faebryk/library/USB_Type_C_Receptacle_14_pin_Vertical.py +++ b/src/faebryk/library/USB_Type_C_Receptacle_14_pin_Vertical.py @@ -1,16 +1,9 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_designator_prefix_defined import ( - has_designator_prefix_defined, -) -from faebryk.library.USB2_0 import USB2_0 +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L class USB_Type_C_Receptacle_14_pin_Vertical(Module): @@ -19,42 +12,37 @@ class USB_Type_C_Receptacle_14_pin_Vertical(Module): 918-418K2022Y40000 """ - def __init__(self) -> None: - super().__init__() - - # interfaces - class _IFs(Module.IFS()): - # TODO make arrays? - cc1 = Electrical() - cc2 = Electrical() - shield = Electrical() - # power - vbus = ElectricPower() - # diffpairs: p, n - usb = USB2_0() - - self.IFs = _IFs(self) - - self.add_trait( - can_attach_to_footprint_via_pinmap( - { - "1": self.IFs.vbus.IFs.lv, - "2": self.IFs.vbus.IFs.hv, - "3": self.IFs.usb.IFs.usb_if.IFs.d.IFs.n, - "4": self.IFs.usb.IFs.usb_if.IFs.d.IFs.p, - "5": self.IFs.cc2, - "6": self.IFs.vbus.IFs.hv, - "7": self.IFs.vbus.IFs.lv, - "8": self.IFs.vbus.IFs.lv, - "9": self.IFs.vbus.IFs.hv, - "10": self.IFs.usb.IFs.usb_if.IFs.d.IFs.n, - "11": self.IFs.usb.IFs.usb_if.IFs.d.IFs.p, - "12": self.IFs.cc1, - "13": self.IFs.vbus.IFs.hv, - "14": self.IFs.vbus.IFs.lv, - "0": self.IFs.shield, - } - ) + # interfaces + + # TODO make arrays? + cc1: F.Electrical + cc2: F.Electrical + shield: F.Electrical + # power + vbus: F.ElectricPower + # diffpairs: p, n + usb: F.USB2_0 + + @L.rt_field + def attach_to_footprint(self): + return F.can_attach_to_footprint_via_pinmap( + { + "1": self.vbus.lv, + "2": self.vbus.hv, + "3": self.usb.usb_if.d.n, + "4": self.usb.usb_if.d.p, + "5": self.cc2, + "6": self.vbus.hv, + "7": self.vbus.lv, + "8": self.vbus.lv, + "9": self.vbus.hv, + "10": self.usb.usb_if.d.n, + "11": self.usb.usb_if.d.p, + "12": self.cc1, + "13": self.vbus.hv, + "14": self.vbus.lv, + "0": self.shield, + } ) - self.add_trait(has_designator_prefix_defined("J")) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("J") diff --git a/src/faebryk/library/USB_Type_C_Receptacle_16_pin.py b/src/faebryk/library/USB_Type_C_Receptacle_16_pin.py index d2ffd712..5eb5a6dc 100644 --- a/src/faebryk/library/USB_Type_C_Receptacle_16_pin.py +++ b/src/faebryk/library/USB_Type_C_Receptacle_16_pin.py @@ -2,63 +2,53 @@ # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.DifferentialPair import DifferentialPair -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L class USB_Type_C_Receptacle_16_pin(Module): - def __init__(self) -> None: - super().__init__() - - # interfaces - class _IFs(Module.IFS()): - # TODO make arrays? - cc1 = Electrical() - cc2 = Electrical() - sbu1 = Electrical() - sbu2 = Electrical() - shield = Electrical() - # power - power = ElectricPower() - # ds: p, n - d = DifferentialPair() - - self.IFs = _IFs(self) - - vbus = self.IFs.power.IFs.hv - gnd = self.IFs.power.IFs.lv - - self.add_trait( - can_attach_to_footprint_via_pinmap( - { - "A1": gnd, - "A4": vbus, - "A5": self.IFs.cc1, - "A6": self.IFs.d.IFs.p, - "A7": self.IFs.d.IFs.n, - "A8": self.IFs.sbu1, - "A9": vbus, - "A12": gnd, - "B1": gnd, - "B4": vbus, - "B5": self.IFs.cc2, - "B6": self.IFs.d.IFs.p, - "B7": self.IFs.d.IFs.n, - "B8": self.IFs.sbu2, - "B9": vbus, - "B12": gnd, - "0": self.IFs.shield, - "1": self.IFs.shield, - "2": self.IFs.shield, - "3": self.IFs.shield, - } - ) + # interfaces + + # TODO make arrays? + cc1: F.Electrical + cc2: F.Electrical + sbu1: F.Electrical + sbu2: F.Electrical + shield: F.Electrical + # power + power: F.ElectricPower + # ds: p, n + d: F.DifferentialPair + + @L.rt_field + def attach_to_footprint(self): + vbus = self.power.hv + gnd = self.power.lv + + return F.can_attach_to_footprint_via_pinmap( + { + "A1": gnd, + "A4": vbus, + "A5": self.cc1, + "A6": self.d.p, + "A7": self.d.n, + "A8": self.sbu1, + "A9": vbus, + "A12": gnd, + "B1": gnd, + "B4": vbus, + "B5": self.cc2, + "B6": self.d.p, + "B7": self.d.n, + "B8": self.sbu2, + "B9": vbus, + "B12": gnd, + "0": self.shield, + "1": self.shield, + "2": self.shield, + "3": self.shield, + } ) - self.add_trait(has_designator_prefix_defined("J")) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("J") diff --git a/src/faebryk/library/USB_Type_C_Receptacle_24_pin.py b/src/faebryk/library/USB_Type_C_Receptacle_24_pin.py index 317bc22d..a0b0774d 100644 --- a/src/faebryk/library/USB_Type_C_Receptacle_24_pin.py +++ b/src/faebryk/library/USB_Type_C_Receptacle_24_pin.py @@ -1,75 +1,61 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.DifferentialPair import ( - DifferentialPair, -) -from faebryk.library.Electrical import Electrical -from faebryk.library.has_designator_prefix_defined import ( - has_designator_prefix_defined, -) -from faebryk.libs.util import times +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L class USB_Type_C_Receptacle_24_pin(Module): - def __init__(self) -> None: - super().__init__() + # interfaces - # interfaces - class _IFs(Module.IFS()): - # TODO make arrays? - cc1 = Electrical() - cc2 = Electrical() - sbu1 = Electrical() - sbu2 = Electrical() - shield = Electrical() - # power - gnd = times(4, Electrical) - vbus = times(4, Electrical) - # diffpairs: p, n - rx1 = DifferentialPair() - rx2 = DifferentialPair() - tx1 = DifferentialPair() - tx2 = DifferentialPair() - d1 = DifferentialPair() - d2 = DifferentialPair() + # TODO make arrays? + cc1: F.Electrical + cc2: F.Electrical + sbu1: F.Electrical + sbu2: F.Electrical + shield: F.Electrical + # power + gnd = L.list_field(4, F.Electrical) + vbus = L.list_field(4, F.Electrical) + # diffpairs: p, n + rx1: F.DifferentialPair + rx2: F.DifferentialPair + tx1: F.DifferentialPair + tx2: F.DifferentialPair + d1: F.DifferentialPair + d2: F.DifferentialPair - self.IFs = _IFs(self) - - self.add_trait( - can_attach_to_footprint_via_pinmap( - { - "A1": self.IFs.gnd[0], - "A2": self.IFs.tx1.IFs.p, - "A3": self.IFs.tx1.IFs.n, - "A4": self.IFs.vbus[0], - "A5": self.IFs.cc1, - "A6": self.IFs.d1.IFs.p, - "A7": self.IFs.d1.IFs.n, - "A8": self.IFs.sbu1, - "A9": self.IFs.vbus[1], - "A10": self.IFs.rx2.IFs.n, - "A11": self.IFs.rx2.IFs.p, - "A12": self.IFs.gnd[1], - "B1": self.IFs.gnd[2], - "B2": self.IFs.tx2.IFs.p, - "B3": self.IFs.tx2.IFs.n, - "B4": self.IFs.vbus[2], - "B5": self.IFs.cc2, - "B6": self.IFs.d2.IFs.p, - "B7": self.IFs.d2.IFs.n, - "B8": self.IFs.sbu2, - "B9": self.IFs.vbus[3], - "B10": self.IFs.rx1.IFs.n, - "B11": self.IFs.rx1.IFs.p, - "B12": self.IFs.gnd[3], - "0": self.IFs.shield, - } - ) + @L.rt_field + def attach_to_footprint(self): + return F.can_attach_to_footprint_via_pinmap( + { + "A1": self.gnd[0], + "A2": self.tx1.p, + "A3": self.tx1.n, + "A4": self.vbus[0], + "A5": self.cc1, + "A6": self.d1.p, + "A7": self.d1.n, + "A8": self.sbu1, + "A9": self.vbus[1], + "A10": self.rx2.n, + "A11": self.rx2.p, + "A12": self.gnd[1], + "B1": self.gnd[2], + "B2": self.tx2.p, + "B3": self.tx2.n, + "B4": self.vbus[2], + "B5": self.cc2, + "B6": self.d2.p, + "B7": self.d2.n, + "B8": self.sbu2, + "B9": self.vbus[3], + "B10": self.rx1.n, + "B11": self.rx1.p, + "B12": self.gnd[3], + "0": self.shield, + } ) - self.add_trait(has_designator_prefix_defined("J")) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("J") diff --git a/src/faebryk/library/XL_3528RGBW_WS2812B.py b/src/faebryk/library/XL_3528RGBW_WS2812B.py index 2dd9f973..4a0d4ad9 100644 --- a/src/faebryk/library/XL_3528RGBW_WS2812B.py +++ b/src/faebryk/library/XL_3528RGBW_WS2812B.py @@ -1,95 +1,75 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from dataclasses import dataclass, field - -from faebryk.core.core import Module, Parameter -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.Constant import Constant -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.has_esphome_config import has_esphome_config -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.library.is_esphome_bus import is_esphome_bus -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L class XL_3528RGBW_WS2812B(Module): - @dataclass - class _ws2812b_esphome_config(has_esphome_config.impl()): - update_interval_s: Parameter = field(default_factory=TBD) - - def __post_init__(self) -> None: - super().__init__() + class _ws2812b_esphome_config(F.has_esphome_config.impl()): + update_interval: F.TBD def get_config(self) -> dict: assert isinstance( - self.update_interval_s, Constant + self.update_interval, F.Constant ), "No update interval set!" - obj = self.get_obj() + obj = self.obj assert isinstance(obj, XL_3528RGBW_WS2812B), "This is not a WS2812B RGBW!" - data_pin = is_esphome_bus.find_connected_bus(obj.IFs.di.IFs.signal) + data_pin = F.is_esphome_bus.find_connected_bus(obj.di.signal) return { "light": [ { "platform": "esp32_rmt_led_strip", - "update_interval": f"{self.update_interval_s.value}s", + "update_interval": f"{self.update_interval.value.to('s')}", "num_leds": 1, # TODO: make dynamic "rmt_channel": 0, # TODO: make dynamic "chipset": "WS2812", "rgb_order": "RGB", "is_rgbw": "true", - "pin": data_pin.get_trait(is_esphome_bus).get_bus_id(), + "pin": data_pin.get_trait(F.is_esphome_bus).get_bus_id(), } ] } - def __init__(self) -> None: - super().__init__() - - # interfaces - class _IFs(Module.IFS()): - power = ElectricPower() - do = ElectricLogic() - di = ElectricLogic() - - self.IFs = _IFs(self) - - # connect all logic references - ref = ElectricLogic.connect_all_module_references(self) - self.add_trait(has_single_electric_reference_defined(ref)) + # interfaces - self.add_trait(has_designator_prefix_defined("LED")) + power: F.ElectricPower + do: F.ElectricLogic + di: F.ElectricLogic - # Add bridge trait - self.add_trait(can_bridge_defined(self.IFs.di, self.IFs.do)) - - self.add_trait( - can_attach_to_footprint_via_pinmap( - { - "1": self.IFs.power.IFs.lv, - "2": self.IFs.di.IFs.signal, - "3": self.IFs.power.IFs.hv, - "4": self.IFs.do.IFs.signal, - } - ) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) ) - self.add_trait( - has_datasheet_defined( - "https://wmsc.lcsc.com/wmsc/upload/file/pdf/v2/lcsc/2402181504_XINGLIGHT-XL-3528RGBW-WS2812B_C2890364.pdf" - ) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("LED") + + # Add bridge trait + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(self.di, self.do) + + @L.rt_field + def attach_to_footprint(self): + return F.can_attach_to_footprint_via_pinmap( + { + "1": self.power.lv, + "2": self.di.signal, + "3": self.power.hv, + "4": self.do.signal, + } ) - self.esphome = self._ws2812b_esphome_config() - self.add_trait(self.esphome) + datasheet = L.f_field(F.has_datasheet_defined)( + "https://wmsc.lcsc.com/wmsc/upload/file/pdf/v2/lcsc/2402181504_XINGLIGHT-XL-3528RGBW-WS2812B_C2890364.pdf" + ) + + esphome_config: _ws2812b_esphome_config + + def __preinit__(self): + self.power.decoupled.decouple() diff --git a/src/faebryk/library/_F.py b/src/faebryk/library/_F.py index 56fd2718..e2727a32 100644 --- a/src/faebryk/library/_F.py +++ b/src/faebryk/library/_F.py @@ -15,203 +15,198 @@ # flake8: noqa: I001 # flake8: noqa: E501 +from faebryk.library.TBD import TBD +from faebryk.library.Constant import Constant +from faebryk.library.Range import Range +from faebryk.library.has_esphome_config import has_esphome_config +from faebryk.library.is_esphome_bus import is_esphome_bus +from faebryk.library.has_single_electric_reference import has_single_electric_reference +from faebryk.library.Power import Power +from faebryk.library.has_footprint import has_footprint +from faebryk.library.Mechanical import Mechanical +from faebryk.library.has_overriden_name import has_overriden_name +from faebryk.library.Operation import Operation +from faebryk.library.has_linked_pad import has_linked_pad +from faebryk.library.has_pcb_position import has_pcb_position +from faebryk.library.can_bridge import can_bridge +from faebryk.library.has_designator import has_designator +from faebryk.library.has_designator_prefix import has_designator_prefix +from faebryk.library.has_descriptive_properties import has_descriptive_properties +from faebryk.library.has_simple_value_representation import has_simple_value_representation +from faebryk.library.has_capacitance import has_capacitance +from faebryk.library.has_datasheet import has_datasheet +from faebryk.library.has_footprint_requirement import has_footprint_requirement +from faebryk.library.has_kicad_ref import has_kicad_ref +from faebryk.library.has_picker import has_picker +from faebryk.library.has_pcb_layout import has_pcb_layout +from faebryk.library.has_pcb_routing_strategy import has_pcb_routing_strategy +from faebryk.library.has_resistance import has_resistance +from faebryk.library.has_single_connection import has_single_connection +from faebryk.library.is_representable_by_single_value import is_representable_by_single_value from faebryk.library.ANY import ANY -from faebryk.library.B4B_ZR_SM4_TF import B4B_ZR_SM4_TF -from faebryk.library.BH1750FVI_TR import BH1750FVI_TR -from faebryk.library.BJT import BJT -from faebryk.library.Battery import Battery +from faebryk.library.Electrical import Electrical +from faebryk.library.Logic import Logic +from faebryk.library.Set import Set +from faebryk.library.has_esphome_config_defined import has_esphome_config_defined +from faebryk.library.is_esphome_bus_defined import is_esphome_bus_defined +from faebryk.library.has_single_electric_reference_defined import has_single_electric_reference_defined +from faebryk.library.Footprint import Footprint +from faebryk.library.has_overriden_name_defined import has_overriden_name_defined +from faebryk.library.has_linked_pad_defined import has_linked_pad_defined +from faebryk.library.has_pcb_position_defined import has_pcb_position_defined +from faebryk.library.has_pcb_position_defined_relative import has_pcb_position_defined_relative +from faebryk.library.has_pcb_position_defined_relative_to_parent import has_pcb_position_defined_relative_to_parent +from faebryk.library.can_bridge_defined import can_bridge_defined +from faebryk.library.has_designator_defined import has_designator_defined +from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined +from faebryk.library.has_descriptive_properties_defined import has_descriptive_properties_defined +from faebryk.library.has_simple_value_representation_based_on_param import has_simple_value_representation_based_on_param +from faebryk.library.has_simple_value_representation_based_on_params import has_simple_value_representation_based_on_params +from faebryk.library.has_simple_value_representation_defined import has_simple_value_representation_defined +from faebryk.library.has_datasheet_defined import has_datasheet_defined +from faebryk.library.has_footprint_requirement_defined import has_footprint_requirement_defined +from faebryk.library.has_multi_picker import has_multi_picker +from faebryk.library.has_pcb_layout_defined import has_pcb_layout_defined +from faebryk.library.has_pcb_routing_strategy_greedy_direct_line import has_pcb_routing_strategy_greedy_direct_line +from faebryk.library.has_single_connection_impl import has_single_connection_impl +from faebryk.library.is_representable_by_single_value_defined import is_representable_by_single_value_defined +from faebryk.library.DifferentialPair import DifferentialPair +from faebryk.library.SPI import SPI +from faebryk.library.has_pin_association_heuristic import has_pin_association_heuristic +from faebryk.library.LogicOps import LogicOps +from faebryk.library.can_attach_to_footprint import can_attach_to_footprint +from faebryk.library.can_attach_via_pinmap import can_attach_via_pinmap +from faebryk.library.has_footprint_impl import has_footprint_impl +from faebryk.library.has_kicad_footprint import has_kicad_footprint +from faebryk.library.Pad import Pad from faebryk.library.Button import Button -from faebryk.library.ButtonCell import ButtonCell -from faebryk.library.CBM9002A_56ILG import CBM9002A_56ILG -from faebryk.library.CBM9002A_56ILG_Reference_Design import CBM9002A_56ILG_Reference_Design -from faebryk.library.CD4011 import CD4011 -from faebryk.library.CH340x import CH340x -from faebryk.library.Capacitor import Capacitor from faebryk.library.Common_Mode_Filter import Common_Mode_Filter -from faebryk.library.Comparator import Comparator -from faebryk.library.Constant import Constant from faebryk.library.Crystal import Crystal -from faebryk.library.Crystal_Oscillator import Crystal_Oscillator -from faebryk.library.DIP import DIP -from faebryk.library.DifferentialPair import DifferentialPair +from faebryk.library.GDT import GDT +from faebryk.library.Header import Header +from faebryk.library.PJ398SM import PJ398SM +from faebryk.library.RJ45_Receptacle import RJ45_Receptacle +from faebryk.library.Relay import Relay +from faebryk.library.Ethernet import Ethernet +from faebryk.library.RS485 import RS485 +from faebryk.library.has_pin_association_heuristic_lookup_table import has_pin_association_heuristic_lookup_table +from faebryk.library.LogicGate import LogicGate +from faebryk.library.has_footprint_defined import has_footprint_defined +from faebryk.library.Net import Net +from faebryk.library.can_attach_via_pinmap_pinlist import can_attach_via_pinmap_pinlist +from faebryk.library.has_equal_pins import has_equal_pins +from faebryk.library.has_kicad_manual_footprint import has_kicad_manual_footprint +from faebryk.library.BJT import BJT from faebryk.library.Diode import Diode -from faebryk.library.EEPROM import EEPROM -from faebryk.library.ESP32 import ESP32 -from faebryk.library.ESP32_C3 import ESP32_C3 -from faebryk.library.ESP32_C3_MINI_1 import ESP32_C3_MINI_1 -from faebryk.library.ESP32_C3_MINI_1_Reference_Design import ESP32_C3_MINI_1_Reference_Design -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricLogicGate import ElectricLogicGate -from faebryk.library.ElectricLogicGates import ElectricLogicGates +from faebryk.library.MOSFET import MOSFET +from faebryk.library.LogicGates import LogicGates +from faebryk.library.can_attach_to_footprint_symmetrically import can_attach_to_footprint_symmetrically +from faebryk.library.can_attach_to_footprint_via_pinmap import can_attach_to_footprint_via_pinmap +from faebryk.library.has_pcb_routing_strategy_manual import has_pcb_routing_strategy_manual +from faebryk.library.has_pcb_routing_strategy_via_to_layer import has_pcb_routing_strategy_via_to_layer +from faebryk.library.can_attach_via_pinmap_equal import can_attach_via_pinmap_equal +from faebryk.library.has_equal_pins_in_ifs import has_equal_pins_in_ifs +from faebryk.library.has_kicad_footprint_equal_ifs import has_kicad_footprint_equal_ifs +from faebryk.library.KicadFootprint import KicadFootprint +from faebryk.library.TVS import TVS +from faebryk.library.Capacitor import Capacitor +from faebryk.library.Fuse import Fuse +from faebryk.library.Inductor import Inductor +from faebryk.library.Resistor import Resistor +from faebryk.library.Switch import Switch +from faebryk.library.B4B_ZR_SM4_TF import B4B_ZR_SM4_TF +from faebryk.library.USB_Type_C_Receptacle_24_pin import USB_Type_C_Receptacle_24_pin +from faebryk.library.pf_533984002 import pf_533984002 +from faebryk.library.DIP import DIP +from faebryk.library.QFN import QFN +from faebryk.library.SMDTwoPin import SMDTwoPin +from faebryk.library.SOIC import SOIC +from faebryk.library.has_kicad_footprint_equal_ifs_defined import has_kicad_footprint_equal_ifs_defined +from faebryk.library.Mounting_Hole import Mounting_Hole +from faebryk.library.can_be_surge_protected import can_be_surge_protected +from faebryk.library.is_surge_protected import is_surge_protected +from faebryk.library.can_be_decoupled import can_be_decoupled +from faebryk.library.is_decoupled import is_decoupled +from faebryk.library.Potentiometer import Potentiometer +from faebryk.library.Resistor_Voltage_Divider import Resistor_Voltage_Divider +from faebryk.library.is_surge_protected_defined import is_surge_protected_defined +from faebryk.library.is_decoupled_nodes import is_decoupled_nodes +from faebryk.library.can_be_surge_protected_defined import can_be_surge_protected_defined +from faebryk.library.can_be_decoupled_defined import can_be_decoupled_defined from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.Electrical import Electrical -from faebryk.library.Ethernet import Ethernet +from faebryk.library.Battery import Battery +from faebryk.library.Comparator import Comparator +from faebryk.library.Crystal_Oscillator import Crystal_Oscillator +from faebryk.library.ElectricLogic import ElectricLogic from faebryk.library.Fan import Fan -from faebryk.library.Footprint import Footprint -from faebryk.library.FootprintTrait import FootprintTrait -from faebryk.library.Fuse import Fuse -from faebryk.library.GDT import GDT +from faebryk.library.LED import LED +from faebryk.library.OpAmp import OpAmp +from faebryk.library.RS485_Bus_Protection import RS485_Bus_Protection +from faebryk.library.USB_Type_C_Receptacle_16_pin import USB_Type_C_Receptacle_16_pin +from faebryk.library.ButtonCell import ButtonCell +from faebryk.library.ElectricLogicGate import ElectricLogicGate from faebryk.library.GenericBusProtection import GenericBusProtection -from faebryk.library.HLK_LD2410B_P import HLK_LD2410B_P -from faebryk.library.Header import Header from faebryk.library.I2C import I2C -from faebryk.library.Inductor import Inductor from faebryk.library.JTAG import JTAG -from faebryk.library.KicadFootprint import KicadFootprint from faebryk.library.LDO import LDO -from faebryk.library.LED import LED -from faebryk.library.LEDIndicator import LEDIndicator -from faebryk.library.Logic import Logic +from faebryk.library.MultiSPI import MultiSPI +from faebryk.library.RS232 import RS232 +from faebryk.library.SK9822_EC20 import SK9822_EC20 +from faebryk.library.SNx4LVC541A import SNx4LVC541A +from faebryk.library.SWD import SWD +from faebryk.library.Sercom import Sercom +from faebryk.library.TXS0102DCUR import TXS0102DCUR +from faebryk.library.UART_Base import UART_Base +from faebryk.library.USB2_0_IF import USB2_0_IF +from faebryk.library.XL_3528RGBW_WS2812B import XL_3528RGBW_WS2812B +from faebryk.library.can_switch_power import can_switch_power +from faebryk.library.pf_74AHCT2G125 import pf_74AHCT2G125 +from faebryk.library.PoweredLED import PoweredLED +from faebryk.library.ElectricLogicGates import ElectricLogicGates from faebryk.library.Logic74xx import Logic74xx -from faebryk.library.LogicGate import LogicGate -from faebryk.library.LogicGates import LogicGates -from faebryk.library.LogicOps import LogicOps +from faebryk.library.BH1750FVI_TR import BH1750FVI_TR +from faebryk.library.EEPROM import EEPROM from faebryk.library.M24C08_FMN6TP import M24C08_FMN6TP -from faebryk.library.MCP2221A import MCP2221A -from faebryk.library.ME6211C33M5G_N import ME6211C33M5G_N -from faebryk.library.MOSFET import MOSFET -from faebryk.library.Mechanical import Mechanical -from faebryk.library.Mounting_Hole import Mounting_Hole -from faebryk.library.MultiSPI import MultiSPI -from faebryk.library.Net import Net from faebryk.library.OLED_Module import OLED_Module -from faebryk.library.OpAmp import OpAmp -from faebryk.library.Operation import Operation -from faebryk.library.PJ398SM import PJ398SM -from faebryk.library.PM1006 import PM1006 -from faebryk.library.Pad import Pad -from faebryk.library.Potentiometer import Potentiometer -from faebryk.library.Power import Power -from faebryk.library.PowerSwitch import PowerSwitch -from faebryk.library.PowerSwitchMOSFET import PowerSwitchMOSFET -from faebryk.library.PowerSwitchStatic import PowerSwitchStatic -from faebryk.library.PoweredLED import PoweredLED -from faebryk.library.Powered_Relay import Powered_Relay -from faebryk.library.QFN import QFN from faebryk.library.QWIIC import QWIIC from faebryk.library.QWIIC_Connector import QWIIC_Connector -from faebryk.library.RJ45_Receptacle import RJ45_Receptacle -from faebryk.library.RP2040 import RP2040 -from faebryk.library.RP2040_Reference_Design import RP2040_Reference_Design -from faebryk.library.RS232 import RS232 -from faebryk.library.RS485 import RS485 -from faebryk.library.RS485_Bus_Protection import RS485_Bus_Protection -from faebryk.library.Range import Range -from faebryk.library.Relay import Relay -from faebryk.library.Resistor import Resistor -from faebryk.library.Resistor_Voltage_Divider import Resistor_Voltage_Divider from faebryk.library.SCD40 import SCD40 -from faebryk.library.SK9822_EC20 import SK9822_EC20 -from faebryk.library.SMDTwoPin import SMDTwoPin -from faebryk.library.SNx4LVC541A import SNx4LVC541A -from faebryk.library.SOIC import SOIC -from faebryk.library.SPI import SPI +from faebryk.library.USB2514B import USB2514B +from faebryk.library.ME6211C33M5G_N import ME6211C33M5G_N from faebryk.library.SPIFlash import SPIFlash -from faebryk.library.SWD import SWD from faebryk.library.SWDConnector import SWDConnector -from faebryk.library.Sercom import Sercom -from faebryk.library.Set import Set -from faebryk.library.Switch import Switch -from faebryk.library.TBD import TBD +from faebryk.library.HLK_LD2410B_P import HLK_LD2410B_P +from faebryk.library.PM1006 import PM1006 from faebryk.library.TD541S485H import TD541S485H -from faebryk.library.TI_CD4011BE import TI_CD4011BE -from faebryk.library.TVS import TVS -from faebryk.library.TXS0102DCUR import TXS0102DCUR from faebryk.library.TXS0102DCUR_UART import TXS0102DCUR_UART from faebryk.library.UART import UART -from faebryk.library.UART_Base import UART_Base from faebryk.library.UART_RS485 import UART_RS485 -from faebryk.library.USB2514B import USB2514B from faebryk.library.USB2_0 import USB2_0 +from faebryk.library.USB3_IF import USB3_IF +from faebryk.library.can_switch_power_defined import can_switch_power_defined +from faebryk.library.CD4011 import CD4011 +from faebryk.library.CBM9002A_56ILG import CBM9002A_56ILG +from faebryk.library.CH340x import CH340x +from faebryk.library.ESP32_C3 import ESP32_C3 +from faebryk.library.MCP2221A import MCP2221A +from faebryk.library.RP2040 import RP2040 from faebryk.library.USB2_0_ESD_Protection import USB2_0_ESD_Protection -from faebryk.library.USB2_0_IF import USB2_0_IF +from faebryk.library.USBLC6_2P6 import USBLC6_2P6 +from faebryk.library.USB_Type_C_Receptacle_14_pin_Vertical import USB_Type_C_Receptacle_14_pin_Vertical from faebryk.library.USB3 import USB3 -from faebryk.library.USB3_IF import USB3_IF +from faebryk.library.PowerSwitch import PowerSwitch +from faebryk.library.TI_CD4011BE import TI_CD4011BE +from faebryk.library.CBM9002A_56ILG_Reference_Design import CBM9002A_56ILG_Reference_Design +from faebryk.library.USB_RS485 import USB_RS485 +from faebryk.library.ESP32_C3_MINI_1 import ESP32_C3_MINI_1 +from faebryk.library.RP2040_Reference_Design import RP2040_Reference_Design +from faebryk.library.USB_C_PSU_Vertical import USB_C_PSU_Vertical from faebryk.library.USB3_connector import USB3_connector -from faebryk.library.USBLC6_2P6 import USBLC6_2P6 from faebryk.library.USB_C import USB_C +from faebryk.library.PowerSwitchMOSFET import PowerSwitchMOSFET +from faebryk.library.PowerSwitchStatic import PowerSwitchStatic +from faebryk.library.ESP32_C3_MINI_1_Reference_Design import ESP32_C3_MINI_1_Reference_Design from faebryk.library.USB_C_5V_PSU import USB_C_5V_PSU -from faebryk.library.USB_C_PSU_Vertical import USB_C_PSU_Vertical from faebryk.library.USB_C_PowerOnly import USB_C_PowerOnly -from faebryk.library.USB_RS485 import USB_RS485 -from faebryk.library.USB_Type_C_Receptacle_14_pin_Vertical import USB_Type_C_Receptacle_14_pin_Vertical -from faebryk.library.USB_Type_C_Receptacle_16_pin import USB_Type_C_Receptacle_16_pin -from faebryk.library.USB_Type_C_Receptacle_24_pin import USB_Type_C_Receptacle_24_pin -from faebryk.library.XL_3528RGBW_WS2812B import XL_3528RGBW_WS2812B -from faebryk.library.can_attach_to_footprint import can_attach_to_footprint -from faebryk.library.can_attach_to_footprint_symmetrically import can_attach_to_footprint_symmetrically -from faebryk.library.can_attach_to_footprint_via_pinmap import can_attach_to_footprint_via_pinmap -from faebryk.library.can_attach_via_pinmap import can_attach_via_pinmap -from faebryk.library.can_attach_via_pinmap_equal import can_attach_via_pinmap_equal -from faebryk.library.can_attach_via_pinmap_pinlist import can_attach_via_pinmap_pinlist -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.can_be_decoupled_defined import can_be_decoupled_defined -from faebryk.library.can_be_surge_protected import can_be_surge_protected -from faebryk.library.can_be_surge_protected_defined import can_be_surge_protected_defined -from faebryk.library.can_bridge import can_bridge -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.can_switch_power import can_switch_power -from faebryk.library.can_switch_power_defined import can_switch_power_defined -from faebryk.library.has_capacitance import has_capacitance -from faebryk.library.has_datasheet import has_datasheet -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_defined_capacitance import has_defined_capacitance -from faebryk.library.has_defined_descriptive_properties import has_defined_descriptive_properties -from faebryk.library.has_defined_footprint import has_defined_footprint -from faebryk.library.has_defined_kicad_ref import has_defined_kicad_ref -from faebryk.library.has_defined_resistance import has_defined_resistance -from faebryk.library.has_descriptive_properties import has_descriptive_properties -from faebryk.library.has_designator import has_designator -from faebryk.library.has_designator_defined import has_designator_defined -from faebryk.library.has_designator_prefix import has_designator_prefix -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.has_equal_pins import has_equal_pins -from faebryk.library.has_equal_pins_in_ifs import has_equal_pins_in_ifs -from faebryk.library.has_esphome_config import has_esphome_config -from faebryk.library.has_esphome_config_defined import has_esphome_config_defined -from faebryk.library.has_footprint import has_footprint -from faebryk.library.has_footprint_impl import has_footprint_impl -from faebryk.library.has_footprint_requirement import has_footprint_requirement -from faebryk.library.has_footprint_requirement_defined import has_footprint_requirement_defined -from faebryk.library.has_kicad_footprint import has_kicad_footprint -from faebryk.library.has_kicad_footprint_equal_ifs import has_kicad_footprint_equal_ifs -from faebryk.library.has_kicad_footprint_equal_ifs_defined import has_kicad_footprint_equal_ifs_defined -from faebryk.library.has_kicad_manual_footprint import has_kicad_manual_footprint -from faebryk.library.has_kicad_ref import has_kicad_ref -from faebryk.library.has_linked_pad import has_linked_pad -from faebryk.library.has_linked_pad_defined import has_linked_pad_defined -from faebryk.library.has_multi_picker import has_multi_picker -from faebryk.library.has_overriden_name import has_overriden_name -from faebryk.library.has_overriden_name_defined import has_overriden_name_defined -from faebryk.library.has_pcb_layout import has_pcb_layout -from faebryk.library.has_pcb_layout_defined import has_pcb_layout_defined -from faebryk.library.has_pcb_position import has_pcb_position -from faebryk.library.has_pcb_position_defined import has_pcb_position_defined -from faebryk.library.has_pcb_position_defined_relative import has_pcb_position_defined_relative -from faebryk.library.has_pcb_position_defined_relative_to_parent import has_pcb_position_defined_relative_to_parent -from faebryk.library.has_pcb_routing_strategy import has_pcb_routing_strategy -from faebryk.library.has_pcb_routing_strategy_greedy_direct_line import has_pcb_routing_strategy_greedy_direct_line -from faebryk.library.has_pcb_routing_strategy_manual import has_pcb_routing_strategy_manual -from faebryk.library.has_pcb_routing_strategy_via_to_layer import has_pcb_routing_strategy_via_to_layer -from faebryk.library.has_picker import has_picker -from faebryk.library.has_pin_association_heuristic import has_pin_association_heuristic -from faebryk.library.has_pin_association_heuristic_lookup_table import has_pin_association_heuristic_lookup_table -from faebryk.library.has_resistance import has_resistance -from faebryk.library.has_simple_value_representation import has_simple_value_representation -from faebryk.library.has_simple_value_representation_based_on_param import has_simple_value_representation_based_on_param -from faebryk.library.has_simple_value_representation_based_on_params import has_simple_value_representation_based_on_params -from faebryk.library.has_simple_value_representation_defined import has_simple_value_representation_defined -from faebryk.library.has_single_connection import has_single_connection -from faebryk.library.has_single_connection_impl import has_single_connection_impl -from faebryk.library.has_single_electric_reference import has_single_electric_reference -from faebryk.library.has_single_electric_reference_defined import has_single_electric_reference_defined -from faebryk.library.is_decoupled import is_decoupled -from faebryk.library.is_decoupled_nodes import is_decoupled_nodes -from faebryk.library.is_esphome_bus import is_esphome_bus -from faebryk.library.is_esphome_bus_defined import is_esphome_bus_defined -from faebryk.library.is_representable_by_single_value import is_representable_by_single_value -from faebryk.library.is_representable_by_single_value_defined import is_representable_by_single_value_defined -from faebryk.library.is_surge_protected import is_surge_protected -from faebryk.library.is_surge_protected_defined import is_surge_protected_defined -from faebryk.library.pf_533984002 import pf_533984002 -from faebryk.library.pf_74AHCT2G125 import pf_74AHCT2G125 +from faebryk.library.LEDIndicator import LEDIndicator +from faebryk.library.Powered_Relay import Powered_Relay diff --git a/src/faebryk/library/can_attach_to_footprint.py b/src/faebryk/library/can_attach_to_footprint.py index 7594320f..2c118c24 100644 --- a/src/faebryk/library/can_attach_to_footprint.py +++ b/src/faebryk/library/can_attach_to_footprint.py @@ -3,10 +3,10 @@ from abc import abstractmethod -from faebryk.core.core import ModuleTrait -from faebryk.library.Footprint import Footprint +import faebryk.library._F as F +from faebryk.core.module import Module -class can_attach_to_footprint(ModuleTrait): +class can_attach_to_footprint(Module.TraitT): @abstractmethod - def attach(self, footprint: Footprint): ... + def attach(self, footprint: F.Footprint): ... diff --git a/src/faebryk/library/can_attach_to_footprint_symmetrically.py b/src/faebryk/library/can_attach_to_footprint_symmetrically.py index 2bb54dc1..5b2ee040 100644 --- a/src/faebryk/library/can_attach_to_footprint_symmetrically.py +++ b/src/faebryk/library/can_attach_to_footprint_symmetrically.py @@ -1,18 +1,15 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.can_attach_to_footprint import can_attach_to_footprint -from faebryk.library.Electrical import Electrical -from faebryk.library.Footprint import Footprint -from faebryk.library.has_defined_footprint import has_defined_footprint -from faebryk.library.Pad import Pad +import faebryk.library._F as F -class can_attach_to_footprint_symmetrically(can_attach_to_footprint.impl()): - def attach(self, footprint: Footprint): - self.get_obj().add_trait(has_defined_footprint(footprint)) - for i, j in zip(footprint.IFs.get_all(), self.get_obj().IFs.get_all()): - assert isinstance(i, Pad) - assert isinstance(j, Electrical) - assert type(i.IFs.net) is type(j) +class can_attach_to_footprint_symmetrically(F.can_attach_to_footprint.impl()): + def attach(self, footprint: F.Footprint): + self.obj.add_trait(F.has_footprint_defined(footprint)) + + for i, j in zip( + footprint.get_children(direct_only=True, types=F.Pad), + self.obj.get_children(direct_only=True, types=F.Electrical), + ): i.attach(j) diff --git a/src/faebryk/library/can_attach_to_footprint_via_pinmap.py b/src/faebryk/library/can_attach_to_footprint_via_pinmap.py index af6490ea..3e9b022e 100644 --- a/src/faebryk/library/can_attach_to_footprint_via_pinmap.py +++ b/src/faebryk/library/can_attach_to_footprint_via_pinmap.py @@ -1,18 +1,14 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.can_attach_to_footprint import can_attach_to_footprint -from faebryk.library.can_attach_via_pinmap import can_attach_via_pinmap -from faebryk.library.Electrical import Electrical -from faebryk.library.Footprint import Footprint -from faebryk.library.has_defined_footprint import has_defined_footprint +import faebryk.library._F as F -class can_attach_to_footprint_via_pinmap(can_attach_to_footprint.impl()): - def __init__(self, pinmap: dict[str, Electrical]) -> None: +class can_attach_to_footprint_via_pinmap(F.can_attach_to_footprint.impl()): + def __init__(self, pinmap: dict[str, F.Electrical]) -> None: super().__init__() self.pinmap = pinmap - def attach(self, footprint: Footprint): - self.get_obj().add_trait(has_defined_footprint(footprint)) - footprint.get_trait(can_attach_via_pinmap).attach(self.pinmap) + def attach(self, footprint: F.Footprint): + self.obj.add_trait(F.has_footprint_defined(footprint)) + footprint.get_trait(F.can_attach_via_pinmap).attach(self.pinmap) diff --git a/src/faebryk/library/can_attach_via_pinmap.py b/src/faebryk/library/can_attach_via_pinmap.py index 595b3cc3..bc1b9334 100644 --- a/src/faebryk/library/can_attach_via_pinmap.py +++ b/src/faebryk/library/can_attach_via_pinmap.py @@ -3,10 +3,9 @@ from abc import abstractmethod -from faebryk.library.Electrical import Electrical -from faebryk.library.FootprintTrait import FootprintTrait +import faebryk.library._F as F -class can_attach_via_pinmap(FootprintTrait): +class can_attach_via_pinmap(F.Footprint.TraitT): @abstractmethod - def attach(self, pinmap: dict[str, Electrical]): ... + def attach(self, pinmap: dict[str, F.Electrical]): ... diff --git a/src/faebryk/library/can_attach_via_pinmap_equal.py b/src/faebryk/library/can_attach_via_pinmap_equal.py index 3bf75208..5ec3d392 100644 --- a/src/faebryk/library/can_attach_via_pinmap_equal.py +++ b/src/faebryk/library/can_attach_via_pinmap_equal.py @@ -1,16 +1,13 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.can_attach_via_pinmap import can_attach_via_pinmap -from faebryk.library.Electrical import Electrical -from faebryk.library.has_equal_pins import has_equal_pins +import faebryk.library._F as F -class can_attach_via_pinmap_equal(can_attach_via_pinmap.impl()): - def attach(self, pinmap: dict[str, Electrical]): +class can_attach_via_pinmap_equal(F.can_attach_via_pinmap.impl()): + def attach(self, pinmap: dict[str, F.Electrical]): pin_list = { - v: k - for k, v in self.get_obj().get_trait(has_equal_pins).get_pin_map().items() + v: k for k, v in self.obj.get_trait(F.has_equal_pins).get_pin_map().items() } for no, intf in pinmap.items(): pin_list[no].attach(intf) diff --git a/src/faebryk/library/can_attach_via_pinmap_pinlist.py b/src/faebryk/library/can_attach_via_pinmap_pinlist.py index 804f041e..5fad9689 100644 --- a/src/faebryk/library/can_attach_via_pinmap_pinlist.py +++ b/src/faebryk/library/can_attach_via_pinmap_pinlist.py @@ -1,17 +1,15 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.can_attach_via_pinmap import can_attach_via_pinmap -from faebryk.library.Electrical import Electrical -from faebryk.library.Pad import Pad +import faebryk.library._F as F -class can_attach_via_pinmap_pinlist(can_attach_via_pinmap.impl()): - def __init__(self, pin_list: dict[str, Pad]) -> None: +class can_attach_via_pinmap_pinlist(F.can_attach_via_pinmap.impl()): + def __init__(self, pin_list: dict[str, F.Pad]) -> None: super().__init__() self.pin_list = pin_list - def attach(self, pinmap: dict[str, Electrical]): + def attach(self, pinmap: dict[str, F.Electrical]): for no, intf in pinmap.items(): assert ( no in self.pin_list diff --git a/src/faebryk/library/can_be_decoupled.py b/src/faebryk/library/can_be_decoupled.py index 31c62ba7..703cd8aa 100644 --- a/src/faebryk/library/can_be_decoupled.py +++ b/src/faebryk/library/can_be_decoupled.py @@ -4,8 +4,8 @@ import logging from abc import abstractmethod -from faebryk.core.core import Trait -from faebryk.library.Capacitor import Capacitor +import faebryk.library._F as F +from faebryk.core.trait import Trait logger = logging.getLogger(__name__) @@ -13,4 +13,4 @@ # TODO better name class can_be_decoupled(Trait): @abstractmethod - def decouple(self) -> Capacitor: ... + def decouple(self) -> F.Capacitor: ... diff --git a/src/faebryk/library/can_be_decoupled_defined.py b/src/faebryk/library/can_be_decoupled_defined.py index 53ae3bdf..228d292c 100644 --- a/src/faebryk/library/can_be_decoupled_defined.py +++ b/src/faebryk/library/can_be_decoupled_defined.py @@ -3,30 +3,25 @@ import logging -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.Capacitor import Capacitor -from faebryk.library.Electrical import Electrical -from faebryk.library.is_decoupled import is_decoupled -from faebryk.library.is_decoupled_nodes import is_decoupled_nodes +import faebryk.library._F as F logger = logging.getLogger(__name__) -class can_be_decoupled_defined(can_be_decoupled.impl()): - def __init__(self, hv: Electrical, lv: Electrical) -> None: +class can_be_decoupled_defined(F.can_be_decoupled.impl()): + def __init__(self, hv: F.Electrical, lv: F.Electrical) -> None: super().__init__() self.hv = hv self.lv = lv def decouple(self): - obj = self.get_obj() + obj = self.obj - capacitor = Capacitor() - obj.NODEs.capacitor = capacitor + capacitor = obj.add(F.Capacitor(), "capacitor") self.hv.connect_via(capacitor, self.lv) - obj.add_trait(is_decoupled_nodes()) + obj.add_trait(F.is_decoupled_nodes()) return capacitor def is_implemented(self): - return not self.get_obj().has_trait(is_decoupled) + return not self.obj.has_trait(F.is_decoupled) diff --git a/src/faebryk/library/can_be_surge_protected.py b/src/faebryk/library/can_be_surge_protected.py index 1b124ae4..db161dae 100644 --- a/src/faebryk/library/can_be_surge_protected.py +++ b/src/faebryk/library/can_be_surge_protected.py @@ -5,12 +5,12 @@ from abc import abstractmethod from typing import Sequence -from faebryk.core.core import Trait -from faebryk.library.TVS import TVS +import faebryk.library._F as F +from faebryk.core.trait import Trait logger = logging.getLogger(__name__) class can_be_surge_protected(Trait): @abstractmethod - def protect(self) -> Sequence[TVS]: ... + def protect(self) -> Sequence[F.TVS]: ... diff --git a/src/faebryk/library/can_be_surge_protected_defined.py b/src/faebryk/library/can_be_surge_protected_defined.py index 599e7a43..77ba53e8 100644 --- a/src/faebryk/library/can_be_surge_protected_defined.py +++ b/src/faebryk/library/can_be_surge_protected_defined.py @@ -3,36 +3,31 @@ import logging -from faebryk.library.can_be_surge_protected import can_be_surge_protected -from faebryk.library.Electrical import Electrical -from faebryk.library.is_surge_protected import is_surge_protected -from faebryk.library.is_surge_protected_defined import is_surge_protected_defined -from faebryk.library.TVS import TVS +import faebryk.library._F as F logger = logging.getLogger(__name__) -class can_be_surge_protected_defined(can_be_surge_protected.impl()): - def __init__(self, low_potential: Electrical, *protect_if: Electrical) -> None: +class can_be_surge_protected_defined(F.can_be_surge_protected.impl()): + def __init__(self, low_potential: F.Electrical, *protect_if: F.Electrical) -> None: super().__init__() self.protect_if = protect_if self.low_potential = low_potential def protect(self): - obj = self.get_obj() + obj = self.obj tvss = [] for protect_if in self.protect_if: - if protect_if.has_trait(can_be_surge_protected): - tvss.extend(protect_if.get_trait(can_be_surge_protected).protect()) + if protect_if.has_trait(F.can_be_surge_protected): + tvss.extend(protect_if.get_trait(F.can_be_surge_protected).protect()) else: - tvs = TVS() - protect_if.NODEs.tvs = tvs + tvs = protect_if.add(F.TVS(), "tvs") protect_if.connect_via(tvs, self.low_potential) tvss.append(tvs) - obj.add_trait(is_surge_protected_defined(tvss)) + obj.add_trait(F.is_surge_protected_defined(tvss)) return tvss def is_implemented(self): - return not self.get_obj().has_trait(is_surge_protected) + return not self.obj.has_trait(F.is_surge_protected) diff --git a/src/faebryk/library/can_bridge.py b/src/faebryk/library/can_bridge.py index 6de41f25..3204d27b 100644 --- a/src/faebryk/library/can_bridge.py +++ b/src/faebryk/library/can_bridge.py @@ -4,10 +4,10 @@ from abc import abstractmethod from typing import Any -from faebryk.core.core import ModuleTrait +from faebryk.core.module import Module -class can_bridge(ModuleTrait): +class can_bridge(Module.TraitT): def bridge(self, _in, out): _in.connect(self.get_in()) out.connect(self.get_out()) diff --git a/src/faebryk/library/can_bridge_defined.py b/src/faebryk/library/can_bridge_defined.py index 6f798eaa..3104c5b5 100644 --- a/src/faebryk/library/can_bridge_defined.py +++ b/src/faebryk/library/can_bridge_defined.py @@ -1,11 +1,11 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleInterface -from faebryk.library.can_bridge import can_bridge +import faebryk.library._F as F +from faebryk.core.moduleinterface import ModuleInterface -class can_bridge_defined(can_bridge.impl()): +class can_bridge_defined(F.can_bridge.impl()): def __init__(self, in_if: ModuleInterface, out_if: ModuleInterface) -> None: super().__init__() self.get_in = lambda: in_if diff --git a/src/faebryk/library/can_switch_power.py b/src/faebryk/library/can_switch_power.py index 7114d76c..86893f74 100644 --- a/src/faebryk/library/can_switch_power.py +++ b/src/faebryk/library/can_switch_power.py @@ -3,10 +3,9 @@ from abc import abstractmethod -from faebryk.library.can_bridge import can_bridge -from faebryk.library.ElectricLogic import ElectricLogic +import faebryk.library._F as F -class can_switch_power(can_bridge): +class can_switch_power(F.can_bridge): @abstractmethod - def get_logic_in(self) -> ElectricLogic: ... + def get_logic_in(self) -> F.ElectricLogic: ... diff --git a/src/faebryk/library/can_switch_power_defined.py b/src/faebryk/library/can_switch_power_defined.py index ed4a13b3..5feffed5 100644 --- a/src/faebryk/library/can_switch_power_defined.py +++ b/src/faebryk/library/can_switch_power_defined.py @@ -1,14 +1,15 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.can_switch_power import can_switch_power -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower +import faebryk.library._F as F -class can_switch_power_defined(can_switch_power.impl()): +class can_switch_power_defined(F.can_switch_power.impl()): def __init__( - self, in_power: ElectricPower, out_power: ElectricPower, in_logic: ElectricLogic + self, + in_power: F.ElectricPower, + out_power: F.ElectricPower, + in_logic: F.ElectricLogic, ) -> None: super().__init__() @@ -16,13 +17,13 @@ def __init__( self.out_power = out_power self.in_logic = in_logic - out_power.PARAMs.voltage.merge(in_power.PARAMs.voltage) + out_power.voltage.merge(in_power.voltage) - def get_logic_in(self) -> ElectricLogic: + def get_logic_in(self) -> F.ElectricLogic: return self.in_logic - def get_in(self) -> ElectricPower: + def get_in(self) -> F.ElectricPower: return self.in_power - def get_out(self) -> ElectricPower: + def get_out(self) -> F.ElectricPower: return self.out_power diff --git a/src/faebryk/library/has_capacitance.py b/src/faebryk/library/has_capacitance.py index f05dc2a5..c034c9fb 100644 --- a/src/faebryk/library/has_capacitance.py +++ b/src/faebryk/library/has_capacitance.py @@ -3,9 +3,10 @@ from abc import abstractmethod -from faebryk.core.core import ModuleTrait, Parameter +from faebryk.core.module import Module +from faebryk.core.parameter import Parameter -class has_capacitance(ModuleTrait): +class has_capacitance(Module.TraitT): @abstractmethod def get_capacitance(self) -> Parameter: ... diff --git a/src/faebryk/library/has_datasheet.py b/src/faebryk/library/has_datasheet.py index db9b6af7..28a55f2a 100644 --- a/src/faebryk/library/has_datasheet.py +++ b/src/faebryk/library/has_datasheet.py @@ -3,9 +3,9 @@ from abc import abstractmethod -from faebryk.core.core import ModuleTrait +from faebryk.core.module import Module -class has_datasheet(ModuleTrait): +class has_datasheet(Module.TraitT): @abstractmethod def get_datasheet(self) -> str: ... diff --git a/src/faebryk/library/has_datasheet_defined.py b/src/faebryk/library/has_datasheet_defined.py index 577d8b86..c385db8b 100644 --- a/src/faebryk/library/has_datasheet_defined.py +++ b/src/faebryk/library/has_datasheet_defined.py @@ -1,10 +1,10 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.has_datasheet import has_datasheet +import faebryk.library._F as F -class has_datasheet_defined(has_datasheet.impl()): +class has_datasheet_defined(F.has_datasheet.impl()): def __init__(self, datasheet: str) -> None: super().__init__() self.datasheet = datasheet diff --git a/src/faebryk/library/has_defined_capacitance.py b/src/faebryk/library/has_defined_capacitance.py deleted file mode 100644 index e24a83a6..00000000 --- a/src/faebryk/library/has_defined_capacitance.py +++ /dev/null @@ -1,14 +0,0 @@ -# This file is part of the faebryk project -# SPDX-License-Identifier: MIT - -from faebryk.core.core import Parameter -from faebryk.library.has_capacitance import has_capacitance - - -class has_defined_capacitance(has_capacitance.impl()): - def __init__(self, capacitance: Parameter) -> None: - super().__init__() - self.component_capacitance = capacitance - - def get_capacitance(self): - return self.component_capacitance diff --git a/src/faebryk/library/has_defined_footprint.py b/src/faebryk/library/has_defined_footprint.py deleted file mode 100644 index 1f27f152..00000000 --- a/src/faebryk/library/has_defined_footprint.py +++ /dev/null @@ -1,14 +0,0 @@ -# This file is part of the faebryk project -# SPDX-License-Identifier: MIT - -from faebryk.library.Footprint import Footprint -from faebryk.library.has_footprint_impl import has_footprint_impl - - -class has_defined_footprint(has_footprint_impl): - def __init__(self, fp: Footprint) -> None: - super().__init__() - self.fp = fp - - def on_obj_set(self): - self.set_footprint(self.fp) diff --git a/src/faebryk/library/has_defined_kicad_ref.py b/src/faebryk/library/has_defined_kicad_ref.py deleted file mode 100644 index 99c30ace..00000000 --- a/src/faebryk/library/has_defined_kicad_ref.py +++ /dev/null @@ -1,13 +0,0 @@ -# This file is part of the faebryk project -# SPDX-License-Identifier: MIT - -from faebryk.library.has_kicad_ref import has_kicad_ref - - -class has_defined_kicad_ref(has_kicad_ref.impl()): - def __init__(self, ref: str) -> None: - super().__init__() - self.ref = ref - - def get_ref(self) -> str: - return self.ref diff --git a/src/faebryk/library/has_defined_resistance.py b/src/faebryk/library/has_defined_resistance.py deleted file mode 100644 index c8c5b5ac..00000000 --- a/src/faebryk/library/has_defined_resistance.py +++ /dev/null @@ -1,14 +0,0 @@ -# This file is part of the faebryk project -# SPDX-License-Identifier: MIT - -from faebryk.core.core import Parameter -from faebryk.library.has_resistance import has_resistance - - -class has_defined_resistance(has_resistance.impl()): - def __init__(self, resistance: Parameter) -> None: - super().__init__() - self.component_resistance = resistance - - def get_resistance(self): - return self.component_resistance diff --git a/src/faebryk/library/has_descriptive_properties.py b/src/faebryk/library/has_descriptive_properties.py index 8292e34b..67ddf128 100644 --- a/src/faebryk/library/has_descriptive_properties.py +++ b/src/faebryk/library/has_descriptive_properties.py @@ -1,10 +1,10 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import ModuleTrait +from faebryk.core.module import Module -class has_descriptive_properties(ModuleTrait): +class has_descriptive_properties(Module.TraitT): def get_properties(self) -> dict[str, str]: raise NotImplementedError() diff --git a/src/faebryk/library/has_defined_descriptive_properties.py b/src/faebryk/library/has_descriptive_properties_defined.py similarity index 61% rename from src/faebryk/library/has_defined_descriptive_properties.py rename to src/faebryk/library/has_descriptive_properties_defined.py index 4eade1ff..cab8aafe 100644 --- a/src/faebryk/library/has_defined_descriptive_properties.py +++ b/src/faebryk/library/has_descriptive_properties_defined.py @@ -2,11 +2,11 @@ # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.has_descriptive_properties import has_descriptive_properties +import faebryk.library._F as F +from faebryk.core.module import Module -class has_defined_descriptive_properties(has_descriptive_properties.impl()): +class has_descriptive_properties_defined(F.has_descriptive_properties.impl()): def __init__(self, properties: dict[str, str]) -> None: super().__init__() self.properties = properties @@ -19,7 +19,7 @@ def get_properties(self) -> dict[str, str]: @classmethod def add_properties_to(cls, module: Module, properties: dict[str, str]): - if not module.has_trait(has_descriptive_properties): + if not module.has_trait(F.has_descriptive_properties): module.add_trait(cls(properties)) else: - module.get_trait(has_descriptive_properties).add_properties(properties) + module.get_trait(F.has_descriptive_properties).add_properties(properties) diff --git a/src/faebryk/library/has_designator.py b/src/faebryk/library/has_designator.py index 5946d6d8..fc75c365 100644 --- a/src/faebryk/library/has_designator.py +++ b/src/faebryk/library/has_designator.py @@ -3,9 +3,9 @@ from abc import abstractmethod -from faebryk.core.core import ModuleTrait +from faebryk.core.module import Module -class has_designator(ModuleTrait): +class has_designator(Module.TraitT): @abstractmethod def get_designator(self) -> str: ... diff --git a/src/faebryk/library/has_designator_defined.py b/src/faebryk/library/has_designator_defined.py index 2a2a46a3..1665338d 100644 --- a/src/faebryk/library/has_designator_defined.py +++ b/src/faebryk/library/has_designator_defined.py @@ -1,10 +1,10 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.has_designator import has_designator +import faebryk.library._F as F -class has_designator_defined(has_designator.impl()): +class has_designator_defined(F.has_designator.impl()): def __init__(self, value: str) -> None: super().__init__() self.value = value diff --git a/src/faebryk/library/has_designator_prefix.py b/src/faebryk/library/has_designator_prefix.py index 82b77ebd..5d28121c 100644 --- a/src/faebryk/library/has_designator_prefix.py +++ b/src/faebryk/library/has_designator_prefix.py @@ -3,9 +3,9 @@ from abc import abstractmethod -from faebryk.core.core import ModuleTrait +from faebryk.core.module import Module -class has_designator_prefix(ModuleTrait): +class has_designator_prefix(Module.TraitT): @abstractmethod def get_prefix(self) -> str: ... diff --git a/src/faebryk/library/has_designator_prefix_defined.py b/src/faebryk/library/has_designator_prefix_defined.py index 89c5ea38..ed174514 100644 --- a/src/faebryk/library/has_designator_prefix_defined.py +++ b/src/faebryk/library/has_designator_prefix_defined.py @@ -1,10 +1,10 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.has_designator_prefix import has_designator_prefix +import faebryk.library._F as F -class has_designator_prefix_defined(has_designator_prefix.impl()): +class has_designator_prefix_defined(F.has_designator_prefix.impl()): def __init__(self, prefix: str) -> None: super().__init__() self.prefix = prefix diff --git a/src/faebryk/library/has_equal_pins.py b/src/faebryk/library/has_equal_pins.py index 59a05585..d78f0aaa 100644 --- a/src/faebryk/library/has_equal_pins.py +++ b/src/faebryk/library/has_equal_pins.py @@ -3,10 +3,9 @@ from abc import abstractmethod -from faebryk.library.FootprintTrait import FootprintTrait -from faebryk.library.Pad import Pad +import faebryk.library._F as F -class has_equal_pins(FootprintTrait): +class has_equal_pins(F.Footprint.TraitT): @abstractmethod - def get_pin_map(self) -> dict[Pad, str]: ... + def get_pin_map(self) -> dict[F.Pad, str]: ... diff --git a/src/faebryk/library/has_equal_pins_in_ifs.py b/src/faebryk/library/has_equal_pins_in_ifs.py index 6d1101cd..0de72169 100644 --- a/src/faebryk/library/has_equal_pins_in_ifs.py +++ b/src/faebryk/library/has_equal_pins_in_ifs.py @@ -1,14 +1,12 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.has_equal_pins import has_equal_pins -from faebryk.library.Pad import Pad +import faebryk.library._F as F -class has_equal_pins_in_ifs(has_equal_pins.impl()): +class has_equal_pins_in_ifs(F.has_equal_pins.impl()): def get_pin_map(self): return { p: str(i + 1) - for i, p in enumerate(self.get_obj().IFs.get_all()) - if isinstance(p, Pad) + for i, p in enumerate(self.obj.get_children(direct_only=True, types=F.Pad)) } diff --git a/src/faebryk/library/has_esphome_config.py b/src/faebryk/library/has_esphome_config.py index f031f079..e1d75c0e 100644 --- a/src/faebryk/library/has_esphome_config.py +++ b/src/faebryk/library/has_esphome_config.py @@ -3,9 +3,9 @@ from abc import abstractmethod -from faebryk.core.core import NodeTrait +from faebryk.core.trait import Trait -class has_esphome_config(NodeTrait): +class has_esphome_config(Trait): @abstractmethod def get_config(self) -> dict: ... diff --git a/src/faebryk/library/has_esphome_config_defined.py b/src/faebryk/library/has_esphome_config_defined.py index f4655af5..216e9daa 100644 --- a/src/faebryk/library/has_esphome_config_defined.py +++ b/src/faebryk/library/has_esphome_config_defined.py @@ -1,11 +1,10 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT +import faebryk.library._F as F -from faebryk.library.has_esphome_config import has_esphome_config - -class has_esphome_config_defined(has_esphome_config.impl()): +class has_esphome_config_defined(F.has_esphome_config.impl()): def __init__(self, config: dict): super().__init__() self._config = config diff --git a/src/faebryk/library/has_footprint.py b/src/faebryk/library/has_footprint.py index 95c2a8a8..5d320f45 100644 --- a/src/faebryk/library/has_footprint.py +++ b/src/faebryk/library/has_footprint.py @@ -2,11 +2,14 @@ # SPDX-License-Identifier: MIT from abc import abstractmethod +from typing import TYPE_CHECKING -from faebryk.core.core import ModuleTrait -from faebryk.library.Footprint import Footprint +from faebryk.core.module import Module +if TYPE_CHECKING: + from faebryk.library.Footprint import Footprint -class has_footprint(ModuleTrait): + +class has_footprint(Module.TraitT): @abstractmethod - def get_footprint(self) -> Footprint: ... + def get_footprint(self) -> "Footprint": ... diff --git a/src/faebryk/library/has_footprint_defined.py b/src/faebryk/library/has_footprint_defined.py new file mode 100644 index 00000000..71940e62 --- /dev/null +++ b/src/faebryk/library/has_footprint_defined.py @@ -0,0 +1,13 @@ +# This file is part of the faebryk project +# SPDX-License-Identifier: MIT + +import faebryk.library._F as F + + +class has_footprint_defined(F.has_footprint_impl): + def __init__(self, fp: F.Footprint) -> None: + super().__init__() + self.fp = fp + + def on_obj_set(self): + self.set_footprint(self.fp) diff --git a/src/faebryk/library/has_footprint_impl.py b/src/faebryk/library/has_footprint_impl.py index 711dc77d..6d9bb2f3 100644 --- a/src/faebryk/library/has_footprint_impl.py +++ b/src/faebryk/library/has_footprint_impl.py @@ -1,25 +1,14 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from abc import abstractmethod +import faebryk.library._F as F -from faebryk.core.core import LinkNamedParent -from faebryk.library.Footprint import Footprint -from faebryk.library.has_footprint import has_footprint +class has_footprint_impl(F.has_footprint.impl()): + def set_footprint(self, fp: F.Footprint): + self.obj.add(fp, name="footprint") -class has_footprint_impl(has_footprint.impl()): - @abstractmethod - def __init__(self) -> None: - super().__init__() - - def set_footprint(self, fp: Footprint): - self.get_obj().GIFs.children.connect( - fp.GIFs.parent, LinkNamedParent.curry("footprint") - ) - - def get_footprint(self) -> Footprint: - children = self.get_obj().GIFs.children.get_children() - fps = [c for _, c in children if isinstance(c, Footprint)] - assert len(fps) == 1, f"candidates: {fps}" - return fps[0] + def get_footprint(self) -> F.Footprint: + fps = self.obj.get_children(direct_only=True, types=F.Footprint) + assert len(fps) == 1, f"In obj: {self.obj}: candidates: {fps}" + return next(iter(fps)) diff --git a/src/faebryk/library/has_footprint_requirement.py b/src/faebryk/library/has_footprint_requirement.py index 3ba93324..34f40446 100644 --- a/src/faebryk/library/has_footprint_requirement.py +++ b/src/faebryk/library/has_footprint_requirement.py @@ -4,10 +4,10 @@ from abc import abstractmethod from typing import Sequence -from faebryk.core.core import ModuleTrait +from faebryk.core.module import Module -class has_footprint_requirement(ModuleTrait): +class has_footprint_requirement(Module.TraitT): @abstractmethod def get_footprint_requirement(self) -> Sequence[tuple[str, int]]: """ diff --git a/src/faebryk/library/has_footprint_requirement_defined.py b/src/faebryk/library/has_footprint_requirement_defined.py index f57e5189..ec2a4ced 100644 --- a/src/faebryk/library/has_footprint_requirement_defined.py +++ b/src/faebryk/library/has_footprint_requirement_defined.py @@ -4,12 +4,12 @@ import logging from typing import Sequence -from faebryk.library.has_footprint_requirement import has_footprint_requirement +import faebryk.library._F as F logger = logging.getLogger(__name__) -class has_footprint_requirement_defined(has_footprint_requirement.impl()): +class has_footprint_requirement_defined(F.has_footprint_requirement.impl()): def __init__(self, req: Sequence[tuple[str, int]]) -> None: super().__init__() self.req = req diff --git a/src/faebryk/library/has_kicad_footprint.py b/src/faebryk/library/has_kicad_footprint.py index bb992b8a..dd8fad7d 100644 --- a/src/faebryk/library/has_kicad_footprint.py +++ b/src/faebryk/library/has_kicad_footprint.py @@ -2,17 +2,20 @@ # SPDX-License-Identifier: MIT from abc import abstractmethod +from typing import TYPE_CHECKING -from faebryk.library.FootprintTrait import FootprintTrait -from faebryk.library.Pad import Pad +import faebryk.library._F as F +if TYPE_CHECKING: + from faebryk.library.Pad import Pad -class has_kicad_footprint(FootprintTrait): + +class has_kicad_footprint(F.Footprint.TraitT): @abstractmethod def get_kicad_footprint(self) -> str: ... @abstractmethod - def get_pin_names(self) -> dict[Pad, str]: ... + def get_pin_names(self) -> dict["Pad", str]: ... def get_kicad_footprint_name(self) -> str: return self.get_kicad_footprint().split(":")[-1] diff --git a/src/faebryk/library/has_kicad_footprint_equal_ifs.py b/src/faebryk/library/has_kicad_footprint_equal_ifs.py index 20b556f1..900bcd5f 100644 --- a/src/faebryk/library/has_kicad_footprint_equal_ifs.py +++ b/src/faebryk/library/has_kicad_footprint_equal_ifs.py @@ -1,11 +1,9 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.has_kicad_footprint import has_kicad_footprint +import faebryk.library._F as F -class has_kicad_footprint_equal_ifs(has_kicad_footprint.impl()): +class has_kicad_footprint_equal_ifs(F.has_kicad_footprint.impl()): def get_pin_names(self): - from faebryk.library.has_equal_pins import has_equal_pins - - return self.get_obj().get_trait(has_equal_pins).get_pin_map() + return self.obj.get_trait(F.has_equal_pins).get_pin_map() diff --git a/src/faebryk/library/has_kicad_footprint_equal_ifs_defined.py b/src/faebryk/library/has_kicad_footprint_equal_ifs_defined.py index e7ca9720..bce6b7c0 100644 --- a/src/faebryk/library/has_kicad_footprint_equal_ifs_defined.py +++ b/src/faebryk/library/has_kicad_footprint_equal_ifs_defined.py @@ -1,10 +1,10 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.has_kicad_footprint_equal_ifs import has_kicad_footprint_equal_ifs +import faebryk.library._F as F -class has_kicad_footprint_equal_ifs_defined(has_kicad_footprint_equal_ifs): +class has_kicad_footprint_equal_ifs_defined(F.has_kicad_footprint_equal_ifs): def __init__(self, str) -> None: super().__init__() self.str = str diff --git a/src/faebryk/library/has_kicad_manual_footprint.py b/src/faebryk/library/has_kicad_manual_footprint.py index 6995278e..9fce01bc 100644 --- a/src/faebryk/library/has_kicad_manual_footprint.py +++ b/src/faebryk/library/has_kicad_manual_footprint.py @@ -1,12 +1,11 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.has_kicad_footprint import has_kicad_footprint -from faebryk.library.Pad import Pad +import faebryk.library._F as F -class has_kicad_manual_footprint(has_kicad_footprint.impl()): - def __init__(self, str, pinmap: dict[Pad, str]) -> None: +class has_kicad_manual_footprint(F.has_kicad_footprint.impl()): + def __init__(self, str, pinmap: dict[F.Pad, str]) -> None: super().__init__() self.str = str self.pinmap = pinmap diff --git a/src/faebryk/library/has_kicad_ref.py b/src/faebryk/library/has_kicad_ref.py index 95c531b1..7e63b6b3 100644 --- a/src/faebryk/library/has_kicad_ref.py +++ b/src/faebryk/library/has_kicad_ref.py @@ -1,9 +1,9 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import NodeTrait +from faebryk.core.trait import Trait -class has_kicad_ref(NodeTrait): +class has_kicad_ref(Trait): def get_ref(self) -> str: raise NotImplementedError() diff --git a/src/faebryk/library/has_linked_pad.py b/src/faebryk/library/has_linked_pad.py index d8d29bb6..9164f19e 100644 --- a/src/faebryk/library/has_linked_pad.py +++ b/src/faebryk/library/has_linked_pad.py @@ -2,13 +2,14 @@ # SPDX-License-Identifier: MIT from abc import abstractmethod +from typing import TYPE_CHECKING -from faebryk.core.core import ( - ModuleInterfaceTrait, -) -from faebryk.library.Pad import Pad +from faebryk.core.moduleinterface import ModuleInterface +if TYPE_CHECKING: + from faebryk.library.Pad import Pad -class has_linked_pad(ModuleInterfaceTrait): + +class has_linked_pad(ModuleInterface.TraitT): @abstractmethod - def get_pad(self) -> Pad: ... + def get_pad(self) -> "Pad": ... diff --git a/src/faebryk/library/has_linked_pad_defined.py b/src/faebryk/library/has_linked_pad_defined.py index 7273c35b..68db2681 100644 --- a/src/faebryk/library/has_linked_pad_defined.py +++ b/src/faebryk/library/has_linked_pad_defined.py @@ -2,14 +2,18 @@ # SPDX-License-Identifier: MIT -from faebryk.library.has_linked_pad import has_linked_pad -from faebryk.library.Pad import Pad +from typing import TYPE_CHECKING +import faebryk.library._F as F -class has_linked_pad_defined(has_linked_pad.impl()): - def __init__(self, pad: Pad) -> None: +if TYPE_CHECKING: + from faebryk.library.Pad import Pad + + +class has_linked_pad_defined(F.has_linked_pad.impl()): + def __init__(self, pad: "Pad") -> None: super().__init__() self.pad = pad - def get_pad(self) -> Pad: + def get_pad(self) -> "Pad": return self.pad diff --git a/src/faebryk/library/has_multi_picker.py b/src/faebryk/library/has_multi_picker.py index a0761996..c8c759a6 100644 --- a/src/faebryk/library/has_multi_picker.py +++ b/src/faebryk/library/has_multi_picker.py @@ -6,16 +6,16 @@ from abc import abstractmethod from typing import Callable, Mapping -from faebryk.core.core import Module -from faebryk.library.has_picker import has_picker +import faebryk.library._F as F +from faebryk.core.module import Module from faebryk.libs.picker.picker import PickError logger = logging.getLogger(__name__) -class has_multi_picker(has_picker.impl()): +class has_multi_picker(F.has_picker.impl()): def pick(self): - module = self.get_obj() + module = self.get_obj(Module) es = [] for _, picker in self.pickers: logger.debug(f"Trying picker for {module}: {picker}") @@ -32,9 +32,7 @@ class Picker: @abstractmethod def pick(self, module: Module): ... - def __init__(self) -> None: - super().__init__() - + def __preinit__(self): self.pickers: list[tuple[int, has_multi_picker.Picker]] = [] def add_picker(self, prio: int, picker: Picker): @@ -43,10 +41,10 @@ def add_picker(self, prio: int, picker: Picker): @classmethod def add_to_module(cls, module: Module, prio: int, picker: Picker): - if not module.has_trait(has_picker): + if not module.has_trait(F.has_picker): module.add_trait(cls()) - t = module.get_trait(has_picker) + t = module.get_trait(F.has_picker) assert isinstance(t, has_multi_picker) t.add_picker(prio, picker) diff --git a/src/faebryk/library/has_overriden_name.py b/src/faebryk/library/has_overriden_name.py index 8f9991f4..b7693e4b 100644 --- a/src/faebryk/library/has_overriden_name.py +++ b/src/faebryk/library/has_overriden_name.py @@ -3,9 +3,9 @@ from abc import abstractmethod -from faebryk.core.core import ModuleTrait +from faebryk.core.module import Module -class has_overriden_name(ModuleTrait): +class has_overriden_name(Module.TraitT): @abstractmethod def get_name(self) -> str: ... diff --git a/src/faebryk/library/has_overriden_name_defined.py b/src/faebryk/library/has_overriden_name_defined.py index 9defd1e6..6180e3bd 100644 --- a/src/faebryk/library/has_overriden_name_defined.py +++ b/src/faebryk/library/has_overriden_name_defined.py @@ -1,10 +1,10 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.has_overriden_name import has_overriden_name +import faebryk.library._F as F -class has_overriden_name_defined(has_overriden_name.impl()): +class has_overriden_name_defined(F.has_overriden_name.impl()): def __init__(self, name: str) -> None: super().__init__() self.component_name = name diff --git a/src/faebryk/library/has_pcb_layout.py b/src/faebryk/library/has_pcb_layout.py index 44f76af8..d760ff25 100644 --- a/src/faebryk/library/has_pcb_layout.py +++ b/src/faebryk/library/has_pcb_layout.py @@ -3,9 +3,9 @@ from abc import abstractmethod -from faebryk.core.core import ModuleTrait +from faebryk.core.module import Module -class has_pcb_layout(ModuleTrait): +class has_pcb_layout(Module.TraitT): @abstractmethod def apply(self): ... diff --git a/src/faebryk/library/has_pcb_layout_defined.py b/src/faebryk/library/has_pcb_layout_defined.py index e65a5e04..44b7b1b6 100644 --- a/src/faebryk/library/has_pcb_layout_defined.py +++ b/src/faebryk/library/has_pcb_layout_defined.py @@ -1,15 +1,16 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT + +import faebryk.library._F as F from faebryk.exporters.pcb.layout.layout import Layout -from faebryk.library.has_pcb_layout import has_pcb_layout -class has_pcb_layout_defined(has_pcb_layout.impl()): +class has_pcb_layout_defined(F.has_pcb_layout.impl()): def __init__(self, layout: Layout) -> None: super().__init__() self.layout = layout def apply(self): - node = self.get_obj() + node = self.obj return self.layout.apply(node) diff --git a/src/faebryk/library/has_pcb_position.py b/src/faebryk/library/has_pcb_position.py index 47b190e3..ca4b621d 100644 --- a/src/faebryk/library/has_pcb_position.py +++ b/src/faebryk/library/has_pcb_position.py @@ -4,10 +4,10 @@ from abc import abstractmethod from enum import IntEnum -from faebryk.core.core import ModuleTrait +from faebryk.core.module import Module -class has_pcb_position(ModuleTrait): +class has_pcb_position(Module.TraitT): class layer_type(IntEnum): NONE = 0 TOP_LAYER = -1 diff --git a/src/faebryk/library/has_pcb_position_defined.py b/src/faebryk/library/has_pcb_position_defined.py index 8d8e9755..4b3f478b 100644 --- a/src/faebryk/library/has_pcb_position_defined.py +++ b/src/faebryk/library/has_pcb_position_defined.py @@ -1,13 +1,13 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.has_pcb_position import has_pcb_position +import faebryk.library._F as F -class has_pcb_position_defined(has_pcb_position.impl()): - def __init__(self, position: has_pcb_position.Point) -> None: +class has_pcb_position_defined(F.has_pcb_position.impl()): + def __init__(self, position: F.has_pcb_position.Point) -> None: super().__init__() self.position = position - def get_position(self) -> has_pcb_position.Point: + def get_position(self) -> F.has_pcb_position.Point: return self.position diff --git a/src/faebryk/library/has_pcb_position_defined_relative.py b/src/faebryk/library/has_pcb_position_defined_relative.py index 1202bd5a..6b3ee11d 100644 --- a/src/faebryk/library/has_pcb_position_defined_relative.py +++ b/src/faebryk/library/has_pcb_position_defined_relative.py @@ -2,19 +2,19 @@ # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.has_pcb_position import has_pcb_position +import faebryk.library._F as F +from faebryk.core.module import Module -class has_pcb_position_defined_relative(has_pcb_position.impl()): - def __init__(self, position_relative: has_pcb_position.Point, to: Module) -> None: +class has_pcb_position_defined_relative(F.has_pcb_position.impl()): + def __init__(self, position_relative: F.has_pcb_position.Point, to: Module) -> None: super().__init__() self.position_relative = position_relative self.to = to - def get_position(self) -> has_pcb_position.Point: + def get_position(self) -> F.has_pcb_position.Point: from faebryk.libs.geometry.basic import Geometry return Geometry.abs_pos( - self.to.get_trait(has_pcb_position).get_position(), self.position_relative + self.to.get_trait(F.has_pcb_position).get_position(), self.position_relative ) diff --git a/src/faebryk/library/has_pcb_position_defined_relative_to_parent.py b/src/faebryk/library/has_pcb_position_defined_relative_to_parent.py index d50051c5..72a6b029 100644 --- a/src/faebryk/library/has_pcb_position_defined_relative_to_parent.py +++ b/src/faebryk/library/has_pcb_position_defined_relative_to_parent.py @@ -3,24 +3,24 @@ import logging -from faebryk.library.has_pcb_position import has_pcb_position +import faebryk.library._F as F logger = logging.getLogger(__name__) -class has_pcb_position_defined_relative_to_parent(has_pcb_position.impl()): - def __init__(self, position_relative: has_pcb_position.Point): +class has_pcb_position_defined_relative_to_parent(F.has_pcb_position.impl()): + def __init__(self, position_relative: F.has_pcb_position.Point): super().__init__() self.position_relative = position_relative - def get_position(self) -> has_pcb_position.Point: + def get_position(self) -> F.has_pcb_position.Point: from faebryk.libs.geometry.basic import Geometry - for parent, _ in reversed(self.get_obj().get_hierarchy()[:-1]): - if parent.has_trait(has_pcb_position): - pos = parent.get_trait(has_pcb_position).get_position() + for parent, _ in reversed(self.obj.get_hierarchy()[:-1]): + if parent.has_trait(F.has_pcb_position): + pos = parent.get_trait(F.has_pcb_position).get_position() logger.debug( - f"Found parent position for: {self.get_obj().get_full_name()}:" + f"Found parent position for: {self.obj.get_full_name()}:" f"{pos} [{parent.get_full_name()}]" ) return Geometry.abs_pos( @@ -28,6 +28,6 @@ def get_position(self) -> has_pcb_position.Point: self.position_relative, ) raise Exception( - f"Component of type {type(self.get_obj())} with relative to parent position" + f"Component of type {type(self.obj)} with relative to parent position" " has no (valid) parent" ) diff --git a/src/faebryk/library/has_pcb_routing_strategy.py b/src/faebryk/library/has_pcb_routing_strategy.py index 7062952e..26301f98 100644 --- a/src/faebryk/library/has_pcb_routing_strategy.py +++ b/src/faebryk/library/has_pcb_routing_strategy.py @@ -2,19 +2,21 @@ # SPDX-License-Identifier: MIT from abc import abstractmethod +from typing import TYPE_CHECKING -from faebryk.core.core import NodeTrait -from faebryk.exporters.pcb.kicad.transformer import PCB_Transformer -from faebryk.exporters.pcb.routing.util import Route +from faebryk.core.trait import Trait + +if TYPE_CHECKING: + from faebryk.exporters.pcb.kicad.transformer import PCB_Transformer + from faebryk.exporters.pcb.routing.util import Route # TODO remove transformer from here -class has_pcb_routing_strategy(NodeTrait): +class has_pcb_routing_strategy(Trait): @abstractmethod - def calculate(self, transformer: PCB_Transformer) -> list[Route]: ... + def calculate(self, transformer: "PCB_Transformer") -> list["Route"]: ... - def __init__(self) -> None: - super().__init__() + def __preinit__(self): self.priority = 0.0 def __repr__(self) -> str: diff --git a/src/faebryk/library/has_pcb_routing_strategy_greedy_direct_line.py b/src/faebryk/library/has_pcb_routing_strategy_greedy_direct_line.py index fcd1aea5..031ff73b 100644 --- a/src/faebryk/library/has_pcb_routing_strategy_greedy_direct_line.py +++ b/src/faebryk/library/has_pcb_routing_strategy_greedy_direct_line.py @@ -3,23 +3,18 @@ import logging from enum import Enum, auto +from typing import TYPE_CHECKING -from faebryk.exporters.pcb.kicad.transformer import PCB_Transformer -from faebryk.exporters.pcb.routing.util import ( - DEFAULT_TRACE_WIDTH, - Path, - Route, - get_internal_nets_of_node, - get_pads_pos_of_mifs, - group_pads_that_are_connected_already, -) -from faebryk.library.has_pcb_routing_strategy import has_pcb_routing_strategy +import faebryk.library._F as F from faebryk.libs.geometry.basic import Geometry +if TYPE_CHECKING: + from faebryk.exporters.pcb.kicad.transformer import PCB_Transformer + logger = logging.getLogger(__name__) -class has_pcb_routing_strategy_greedy_direct_line(has_pcb_routing_strategy.impl()): +class has_pcb_routing_strategy_greedy_direct_line(F.has_pcb_routing_strategy.impl()): class Topology(Enum): STAR = auto() DIRECT = auto() @@ -29,8 +24,17 @@ def __init__(self, topology: Topology = Topology.DIRECT, priority: float = 0.0): super().__init__() self.topology = topology - def calculate(self, transformer: PCB_Transformer): - node = self.get_obj() + def calculate(self, transformer: "PCB_Transformer"): + from faebryk.exporters.pcb.routing.util import ( + DEFAULT_TRACE_WIDTH, + Path, + Route, + get_internal_nets_of_node, + get_pads_pos_of_mifs, + group_pads_that_are_connected_already, + ) + + node = self.obj nets = get_internal_nets_of_node(node) logger.debug(f"Routing {node} {'-'*40}") diff --git a/src/faebryk/library/has_pcb_routing_strategy_manual.py b/src/faebryk/library/has_pcb_routing_strategy_manual.py index 1a2bd0e6..317706f6 100644 --- a/src/faebryk/library/has_pcb_routing_strategy_manual.py +++ b/src/faebryk/library/has_pcb_routing_strategy_manual.py @@ -2,9 +2,10 @@ # SPDX-License-Identifier: MIT import logging +from typing import Sequence -from faebryk.core.core import Node -from faebryk.core.util import get_parent_with_trait +import faebryk.library._F as F +from faebryk.core.node import Node from faebryk.exporters.pcb.kicad.transformer import PCB_Transformer from faebryk.exporters.pcb.routing.util import ( Path, @@ -12,18 +13,14 @@ get_internal_nets_of_node, get_pads_pos_of_mifs, ) -from faebryk.library.Electrical import Electrical -from faebryk.library.has_pcb_position import has_pcb_position -from faebryk.library.has_pcb_routing_strategy import has_pcb_routing_strategy -from faebryk.library.Net import Net logger = logging.getLogger(__name__) -class has_pcb_routing_strategy_manual(has_pcb_routing_strategy.impl()): +class has_pcb_routing_strategy_manual(F.has_pcb_routing_strategy.impl()): def __init__( self, - paths: list[tuple[Net | list[Electrical], Path]], + paths: Sequence[tuple[F.Net | Sequence[F.Electrical], Path]], relative_to: Node | None = None, absolute: bool = False, ): @@ -34,15 +31,17 @@ def __init__( self.absolute = absolute def calculate(self, transformer: PCB_Transformer): - node = self.get_obj() + from faebryk.core.util import get_parent_with_trait + + node = self.obj nets = get_internal_nets_of_node(node) - relative_to = ( - (self.relative_to or self.get_obj()) if not self.absolute else None - ) + relative_to = (self.relative_to or self.obj) if not self.absolute else None if relative_to: - pos = get_parent_with_trait(relative_to, has_pcb_position)[1].get_position() + pos = get_parent_with_trait(relative_to, F.has_pcb_position)[ + 1 + ].get_position() for _, path in self.paths_rel: path.abs_pos(pos) @@ -59,7 +58,9 @@ def get_route_for_mifs_in_net(mifs, path): for net_or_mifs, path in self.paths_rel if ( route := get_route_for_mifs_in_net( - nets[net_or_mifs] if isinstance(net_or_mifs, Net) else net_or_mifs, + nets[net_or_mifs] + if isinstance(net_or_mifs, F.Net) + else net_or_mifs, path, ) ) diff --git a/src/faebryk/library/has_pcb_routing_strategy_via_to_layer.py b/src/faebryk/library/has_pcb_routing_strategy_via_to_layer.py index f430619f..559079f9 100644 --- a/src/faebryk/library/has_pcb_routing_strategy_via_to_layer.py +++ b/src/faebryk/library/has_pcb_routing_strategy_via_to_layer.py @@ -3,6 +3,7 @@ import logging +import faebryk.library._F as F from faebryk.exporters.pcb.kicad.transformer import PCB_Transformer from faebryk.exporters.pcb.routing.util import ( DEFAULT_TRACE_WIDTH, @@ -14,15 +15,12 @@ get_routes_of_pad, group_pads_that_are_connected_already, ) -from faebryk.library.has_overriden_name import has_overriden_name -from faebryk.library.has_pcb_routing_strategy import has_pcb_routing_strategy -from faebryk.library.Net import Net from faebryk.libs.geometry.basic import Geometry logger = logging.getLogger(__name__) -class has_pcb_routing_strategy_via_to_layer(has_pcb_routing_strategy.impl()): +class has_pcb_routing_strategy_via_to_layer(F.has_pcb_routing_strategy.impl()): def __init__(self, layer: str, vec: Geometry.Point2D): super().__init__() self.vec = vec @@ -31,13 +29,13 @@ def __init__(self, layer: str, vec: Geometry.Point2D): def calculate(self, transformer: PCB_Transformer): layer = transformer.get_layer_id(self.layer) - node = self.get_obj() + node = self.obj nets = get_internal_nets_of_node(node) logger.debug(f"Routing {node} {'-'*40}") - def get_route_for_net(net: Net, mifs) -> Route | None: - net_name = net.get_trait(has_overriden_name).get_name() + def get_route_for_net(net: F.Net, mifs) -> Route | None: + net_name = net.get_trait(F.has_overriden_name).get_name() pads = get_pads_pos_of_mifs(mifs) pad_groups = group_pads_that_are_connected_already(pads) diff --git a/src/faebryk/library/has_picker.py b/src/faebryk/library/has_picker.py index c3d27bfb..14046934 100644 --- a/src/faebryk/library/has_picker.py +++ b/src/faebryk/library/has_picker.py @@ -3,9 +3,9 @@ from abc import abstractmethod -from faebryk.core.core import ModuleTrait +from faebryk.core.module import Module -class has_picker(ModuleTrait): +class has_picker(Module.TraitT): @abstractmethod def pick(self) -> None: ... diff --git a/src/faebryk/library/has_pin_association_heuristic.py b/src/faebryk/library/has_pin_association_heuristic.py index 352d4197..57cff597 100644 --- a/src/faebryk/library/has_pin_association_heuristic.py +++ b/src/faebryk/library/has_pin_association_heuristic.py @@ -3,11 +3,11 @@ from abc import abstractmethod -from faebryk.core.core import ModuleTrait -from faebryk.library.Electrical import Electrical +import faebryk.library._F as F +from faebryk.core.module import Module -class has_pin_association_heuristic(ModuleTrait): +class has_pin_association_heuristic(Module.TraitT): class PinMatchException(Exception): ... """ @@ -18,4 +18,4 @@ class PinMatchException(Exception): ... def get_pins( self, pins: list[tuple[str, str]], - ) -> dict[str, Electrical]: ... + ) -> dict[str, F.Electrical]: ... diff --git a/src/faebryk/library/has_pin_association_heuristic_lookup_table.py b/src/faebryk/library/has_pin_association_heuristic_lookup_table.py index 8de375c5..69027322 100644 --- a/src/faebryk/library/has_pin_association_heuristic_lookup_table.py +++ b/src/faebryk/library/has_pin_association_heuristic_lookup_table.py @@ -3,16 +3,17 @@ import logging -from faebryk.library.Electrical import Electrical -from faebryk.library.has_pin_association_heuristic import has_pin_association_heuristic +import faebryk.library._F as F logger = logging.getLogger(__name__) -class has_pin_association_heuristic_lookup_table(has_pin_association_heuristic.impl()): +class has_pin_association_heuristic_lookup_table( + F.has_pin_association_heuristic.impl() +): def __init__( self, - mapping: dict[Electrical, list[str]], + mapping: dict[F.Electrical, list[str]], accept_prefix: bool, case_sensitive: bool, nc: list[str] | None = None, @@ -26,7 +27,7 @@ def __init__( def get_pins( self, pins: list[tuple[str, str]], - ) -> dict[str, Electrical]: + ) -> dict[str, F.Electrical]: """ Get the pinmapping for a list of pins based on a lookup table. @@ -51,7 +52,7 @@ def get_pins( match = (mif, alt_name) break if not match: - raise has_pin_association_heuristic.PinMatchException( + raise F.has_pin_association_heuristic.PinMatchException( f"Could not find a match for pin {number} with name {name}" f" in the mapping {self.mapping}" ) diff --git a/src/faebryk/library/has_resistance.py b/src/faebryk/library/has_resistance.py index 4a4ad56f..35da9994 100644 --- a/src/faebryk/library/has_resistance.py +++ b/src/faebryk/library/has_resistance.py @@ -3,9 +3,10 @@ from abc import abstractmethod -from faebryk.core.core import ModuleTrait, Parameter +from faebryk.core.module import Module +from faebryk.core.parameter import Parameter -class has_resistance(ModuleTrait): +class has_resistance(Module.TraitT): @abstractmethod def get_resistance(self) -> Parameter: ... diff --git a/src/faebryk/library/has_simple_value_representation.py b/src/faebryk/library/has_simple_value_representation.py index b1e27d23..7f12d288 100644 --- a/src/faebryk/library/has_simple_value_representation.py +++ b/src/faebryk/library/has_simple_value_representation.py @@ -3,9 +3,9 @@ from abc import abstractmethod -from faebryk.core.core import ModuleTrait +from faebryk.core.module import Module -class has_simple_value_representation(ModuleTrait): +class has_simple_value_representation(Module.TraitT): @abstractmethod def get_value(self) -> str: ... diff --git a/src/faebryk/library/has_simple_value_representation_based_on_param.py b/src/faebryk/library/has_simple_value_representation_based_on_param.py index 4a315330..ffcb1829 100644 --- a/src/faebryk/library/has_simple_value_representation_based_on_param.py +++ b/src/faebryk/library/has_simple_value_representation_based_on_param.py @@ -3,18 +3,15 @@ from typing import Callable -from faebryk.core.core import Parameter -from faebryk.library.Constant import Constant -from faebryk.library.has_simple_value_representation import ( - has_simple_value_representation, -) +import faebryk.library._F as F +from faebryk.core.parameter import Parameter class has_simple_value_representation_based_on_param( - has_simple_value_representation.impl() + F.has_simple_value_representation.impl() ): def __init__( - self, param: Parameter, transformer: Callable[[Constant], str] + self, param: Parameter, transformer: Callable[[F.Constant], str] ) -> None: super().__init__() self.param = param @@ -22,8 +19,8 @@ def __init__( def get_value(self) -> str: param_const = self.param.get_most_narrow() - assert isinstance(param_const, Constant) + assert isinstance(param_const, F.Constant) return self.transformer(param_const) def is_implemented(self): - return isinstance(self.param.get_most_narrow(), Constant) + return isinstance(self.param.get_most_narrow(), F.Constant) diff --git a/src/faebryk/library/has_simple_value_representation_based_on_params.py b/src/faebryk/library/has_simple_value_representation_based_on_params.py index 4df048fb..70fa30ab 100644 --- a/src/faebryk/library/has_simple_value_representation_based_on_params.py +++ b/src/faebryk/library/has_simple_value_representation_based_on_params.py @@ -3,14 +3,12 @@ from typing import Callable, Sequence -from faebryk.core.core import Parameter -from faebryk.library.has_simple_value_representation import ( - has_simple_value_representation, -) +import faebryk.library._F as F +from faebryk.core.parameter import Parameter class has_simple_value_representation_based_on_params( - has_simple_value_representation.impl() + F.has_simple_value_representation.impl() ): def __init__( self, diff --git a/src/faebryk/library/has_simple_value_representation_defined.py b/src/faebryk/library/has_simple_value_representation_defined.py index cce81325..572d28be 100644 --- a/src/faebryk/library/has_simple_value_representation_defined.py +++ b/src/faebryk/library/has_simple_value_representation_defined.py @@ -1,12 +1,10 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.has_simple_value_representation import ( - has_simple_value_representation, -) +import faebryk.library._F as F -class has_simple_value_representation_defined(has_simple_value_representation.impl()): +class has_simple_value_representation_defined(F.has_simple_value_representation.impl()): def __init__(self, value: str) -> None: super().__init__() self.value = value diff --git a/src/faebryk/library/has_single_connection.py b/src/faebryk/library/has_single_connection.py index 75b5df96..6800871d 100644 --- a/src/faebryk/library/has_single_connection.py +++ b/src/faebryk/library/has_single_connection.py @@ -3,9 +3,10 @@ from abc import abstractmethod -from faebryk.core.core import InterfaceTrait, Link +from faebryk.core.link import Link +from faebryk.core.moduleinterface import ModuleInterface -class has_single_connection(InterfaceTrait): +class has_single_connection(ModuleInterface.TraitT): @abstractmethod def get_connection(self) -> Link: ... diff --git a/src/faebryk/library/has_single_connection_impl.py b/src/faebryk/library/has_single_connection_impl.py index c926e636..833e3ae4 100644 --- a/src/faebryk/library/has_single_connection_impl.py +++ b/src/faebryk/library/has_single_connection_impl.py @@ -1,11 +1,11 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.library.has_single_connection import has_single_connection +import faebryk.library._F as F -class has_single_connection_impl(has_single_connection.impl()): +class has_single_connection_impl(F.has_single_connection.impl()): def get_connection(self): - conns = self.get_obj().connections + conns = self.obj.connections assert len(conns) == 1 return conns[0] diff --git a/src/faebryk/library/has_single_electric_reference.py b/src/faebryk/library/has_single_electric_reference.py index bdcf8e43..1f6aefcc 100644 --- a/src/faebryk/library/has_single_electric_reference.py +++ b/src/faebryk/library/has_single_electric_reference.py @@ -3,11 +3,14 @@ from abc import abstractmethod +from typing import TYPE_CHECKING -from faebryk.core.core import NodeTrait -from faebryk.library.ElectricPower import ElectricPower +from faebryk.core.trait import Trait +if TYPE_CHECKING: + from faebryk.library.ElectricPower import ElectricPower -class has_single_electric_reference(NodeTrait): + +class has_single_electric_reference(Trait): @abstractmethod - def get_reference(self) -> ElectricPower: ... + def get_reference(self) -> "ElectricPower": ... diff --git a/src/faebryk/library/has_single_electric_reference_defined.py b/src/faebryk/library/has_single_electric_reference_defined.py index 493d4676..0aa751fd 100644 --- a/src/faebryk/library/has_single_electric_reference_defined.py +++ b/src/faebryk/library/has_single_electric_reference_defined.py @@ -1,15 +1,18 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT +from typing import TYPE_CHECKING -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_single_electric_reference import has_single_electric_reference +import faebryk.library._F as F +if TYPE_CHECKING: + from faebryk.library.ElectricPower import ElectricPower -class has_single_electric_reference_defined(has_single_electric_reference.impl()): - def __init__(self, reference: ElectricPower) -> None: + +class has_single_electric_reference_defined(F.has_single_electric_reference.impl()): + def __init__(self, reference: "ElectricPower") -> None: super().__init__() self.reference = reference - def get_reference(self) -> ElectricPower: + def get_reference(self) -> "ElectricPower": return self.reference diff --git a/src/faebryk/library/is_decoupled.py b/src/faebryk/library/is_decoupled.py index 09309897..4c4dec6a 100644 --- a/src/faebryk/library/is_decoupled.py +++ b/src/faebryk/library/is_decoupled.py @@ -4,12 +4,12 @@ import logging from abc import abstractmethod -from faebryk.core.core import Trait -from faebryk.library.Capacitor import Capacitor +import faebryk.library._F as F +from faebryk.core.trait import Trait logger = logging.getLogger(__name__) class is_decoupled(Trait): @abstractmethod - def get_capacitor(self) -> Capacitor: ... + def get_capacitor(self) -> F.Capacitor: ... diff --git a/src/faebryk/library/is_decoupled_nodes.py b/src/faebryk/library/is_decoupled_nodes.py index 1eac341f..5fae541a 100644 --- a/src/faebryk/library/is_decoupled_nodes.py +++ b/src/faebryk/library/is_decoupled_nodes.py @@ -3,15 +3,15 @@ import logging -from faebryk.library.Capacitor import Capacitor -from faebryk.library.is_decoupled import is_decoupled +import faebryk.library._F as F +from faebryk.libs.util import cast_assert logger = logging.getLogger(__name__) -class is_decoupled_nodes(is_decoupled.impl()): +class is_decoupled_nodes(F.is_decoupled.impl()): def on_obj_set(self) -> None: - assert hasattr(self.get_obj().NODEs, "capacitor") + assert "capacitor" in self.obj.runtime - def get_capacitor(self) -> Capacitor: - return self.get_obj().NODEs.capacitor + def get_capacitor(self) -> F.Capacitor: + return cast_assert(F.Capacitor, self.obj.runtime["capacitor"]) diff --git a/src/faebryk/library/is_esphome_bus.py b/src/faebryk/library/is_esphome_bus.py index 3a3bca21..d42104a2 100644 --- a/src/faebryk/library/is_esphome_bus.py +++ b/src/faebryk/library/is_esphome_bus.py @@ -3,11 +3,11 @@ from abc import abstractmethod -from faebryk.core.core import ModuleInterface, ModuleInterfaceTrait +from faebryk.core.moduleinterface import ModuleInterface from faebryk.libs.util import find -class is_esphome_bus(ModuleInterfaceTrait): +class is_esphome_bus(ModuleInterface.TraitT): ... @abstractmethod diff --git a/src/faebryk/library/is_esphome_bus_defined.py b/src/faebryk/library/is_esphome_bus_defined.py index 1aab4f93..1c6b2ed9 100644 --- a/src/faebryk/library/is_esphome_bus_defined.py +++ b/src/faebryk/library/is_esphome_bus_defined.py @@ -1,11 +1,10 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT +import faebryk.library._F as F -from faebryk.library.is_esphome_bus import is_esphome_bus - -class is_esphome_bus_defined(is_esphome_bus.impl()): +class is_esphome_bus_defined(F.is_esphome_bus.impl()): def __init__(self, bus_id: str): super().__init__() self._bus_id = bus_id diff --git a/src/faebryk/library/is_representable_by_single_value.py b/src/faebryk/library/is_representable_by_single_value.py index f6173206..e5c1cfdc 100644 --- a/src/faebryk/library/is_representable_by_single_value.py +++ b/src/faebryk/library/is_representable_by_single_value.py @@ -3,9 +3,9 @@ from abc import abstractmethod -from faebryk.core.core import ParameterTrait +from faebryk.core.parameter import Parameter -class is_representable_by_single_value(ParameterTrait): +class is_representable_by_single_value(Parameter.TraitT): @abstractmethod def get_single_representing_value(self): ... diff --git a/src/faebryk/library/is_representable_by_single_value_defined.py b/src/faebryk/library/is_representable_by_single_value_defined.py index 620a9b8c..3d3410d1 100644 --- a/src/faebryk/library/is_representable_by_single_value_defined.py +++ b/src/faebryk/library/is_representable_by_single_value_defined.py @@ -3,12 +3,12 @@ import typing -from faebryk.library.is_representable_by_single_value import ( - is_representable_by_single_value, -) +import faebryk.library._F as F -class is_representable_by_single_value_defined(is_representable_by_single_value.impl()): +class is_representable_by_single_value_defined( + F.is_representable_by_single_value.impl() +): def __init__(self, value: typing.Any) -> None: super().__init__() self.value = value diff --git a/src/faebryk/library/is_surge_protected.py b/src/faebryk/library/is_surge_protected.py index 54e62b82..a4a42964 100644 --- a/src/faebryk/library/is_surge_protected.py +++ b/src/faebryk/library/is_surge_protected.py @@ -5,12 +5,12 @@ from abc import abstractmethod from typing import Sequence -from faebryk.core.core import Trait -from faebryk.library.TVS import TVS +import faebryk.library._F as F +from faebryk.core.trait import Trait logger = logging.getLogger(__name__) class is_surge_protected(Trait): @abstractmethod - def get_tvs(self) -> Sequence[TVS]: ... + def get_tvs(self) -> Sequence[F.TVS]: ... diff --git a/src/faebryk/library/is_surge_protected_defined.py b/src/faebryk/library/is_surge_protected_defined.py index b05d8653..1d5129f6 100644 --- a/src/faebryk/library/is_surge_protected_defined.py +++ b/src/faebryk/library/is_surge_protected_defined.py @@ -4,14 +4,13 @@ import logging from typing import Sequence -from faebryk.library.is_surge_protected import is_surge_protected -from faebryk.library.TVS import TVS +import faebryk.library._F as F logger = logging.getLogger(__name__) -class is_surge_protected_defined(is_surge_protected.impl()): - def __init__(self, tvss: Sequence[TVS]) -> None: +class is_surge_protected_defined(F.is_surge_protected.impl()): + def __init__(self, tvss: Sequence[F.TVS]) -> None: super().__init__() self.tvss = tvss diff --git a/src/faebryk/library/pf_533984002.py b/src/faebryk/library/pf_533984002.py index 7257b839..df90b1ff 100644 --- a/src/faebryk/library/pf_533984002.py +++ b/src/faebryk/library/pf_533984002.py @@ -1,45 +1,30 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.Electrical import Electrical -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_designator_prefix_defined import ( - has_designator_prefix_defined, -) -from faebryk.libs.util import times +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L class pf_533984002(Module): - def __init__(self) -> None: - super().__init__() - - # interfaces - class _IFs(Module.IFS()): - pin = times(2, Electrical) - mount = times(2, Electrical) - - self.IFs = _IFs(self) - - x = self.IFs - self.add_trait( - can_attach_to_footprint_via_pinmap( - { - "1": x.pin[0], - "2": x.pin[1], - "3": x.mount[0], - "4": x.mount[1], - } - ) + # interfaces + pin = L.list_field(2, F.Electrical) + mount = L.list_field(2, F.Electrical) + + @L.rt_field + def attach_to_footprint(self): + x = self + return F.can_attach_to_footprint_via_pinmap( + { + "1": x.pin[0], + "2": x.pin[1], + "3": x.mount[0], + "4": x.mount[1], + } ) - self.add_trait( - has_datasheet_defined( - "https://wmsc.lcsc.com/wmsc/upload/file/pdf/v2/lcsc/1912111437_SHOU-HAN-1-25-2P_C393945.pdf" - ) - ) + datasheet = L.f_field(F.has_datasheet_defined)( + "https://wmsc.lcsc.com/wmsc/upload/file/pdf/v2/lcsc/1912111437_SHOU-HAN-1-25-2P_C393945.pdf" + ) - self.add_trait(has_designator_prefix_defined("J")) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("J") diff --git a/src/faebryk/library/pf_74AHCT2G125.py b/src/faebryk/library/pf_74AHCT2G125.py index 1206ac5c..8307e8f8 100644 --- a/src/faebryk/library/pf_74AHCT2G125.py +++ b/src/faebryk/library/pf_74AHCT2G125.py @@ -1,20 +1,9 @@ # This file is part of the faebryk project # SPDX-License-Identifier: MIT -from faebryk.core.core import Module -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.can_be_decoupled import can_be_decoupled -from faebryk.library.can_bridge_defined import can_bridge_defined -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_datasheet_defined import has_datasheet_defined -from faebryk.library.has_designator_prefix_defined import has_designator_prefix_defined -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.library.Range import Range +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.libs.library import L from faebryk.libs.units import P @@ -27,45 +16,42 @@ class pf_74AHCT2G125(Module): output to assume a high-impedance OFF-state. """ - def __init__(self) -> None: - super().__init__() - - # interfaces - class _IFs(Module.IFS()): - power = ElectricPower() - a = ElectricLogic() # IN - y = ElectricLogic() # OUT - oe = ElectricLogic() # enable, active low - - self.IFs = _IFs(self) - - x = self.IFs - self.add_trait( - can_attach_to_footprint_via_pinmap( - { - "1": x.oe.IFs.signal, - "2": x.a.IFs.signal, - "3": x.power.IFs.lv, - "4": x.y.IFs.signal, - "5": x.power.IFs.hv, - } - ) + # interfaces + + power: F.ElectricPower + a: F.ElectricLogic # IN + y: F.ElectricLogic # OUT + oe: F.ElectricLogic # enable, active low + + @L.rt_field + def attach_to_footprint(self): + x = self + return F.can_attach_to_footprint_via_pinmap( + { + "1": x.oe.signal, + "2": x.a.signal, + "3": x.power.lv, + "4": x.y.signal, + "5": x.power.hv, + } ) - self.IFs.power.PARAMs.voltage.merge(Range(4.5 * P.V, 5.5 * P.V)) + def __preinit__(self): + self.power.voltage.merge(F.Range(4.5 * P.V, 5.5 * P.V)) + self.power.decoupled.decouple() - self.IFs.power.get_trait(can_be_decoupled).decouple() - - # connect all logic references - ref = ElectricLogic.connect_all_module_references(self) - self.add_trait(has_single_electric_reference_defined(ref)) + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) + ) - self.add_trait(has_designator_prefix_defined("U")) + designator_prefix = L.f_field(F.has_designator_prefix_defined)("U") - self.add_trait(can_bridge_defined(self.IFs.a, self.IFs.y)) + @L.rt_field + def can_bridge(self): + return F.can_bridge_defined(self.a, self.y) - self.add_trait( - has_datasheet_defined( - "https://datasheet.lcsc.com/lcsc/2304140030_Nexperia-74AHCT1G125GV-125_C12494.pdf" - ) - ) + datasheet = L.f_field(F.has_datasheet_defined)( + "https://datasheet.lcsc.com/lcsc/2304140030_Nexperia-74AHCT1G125GV-125_C12494.pdf" + ) diff --git a/src/faebryk/libs/app/checks.py b/src/faebryk/libs/app/checks.py index 18aef218..44439aaf 100644 --- a/src/faebryk/libs/app/checks.py +++ b/src/faebryk/libs/app/checks.py @@ -2,8 +2,8 @@ # SPDX-License-Identifier: MIT -from faebryk.core.core import Module from faebryk.core.graph import Graph +from faebryk.core.module import Module from faebryk.libs.app.erc import simple_erc diff --git a/src/faebryk/libs/app/designators.py b/src/faebryk/libs/app/designators.py index 7e93f49c..02026e5e 100644 --- a/src/faebryk/libs/app/designators.py +++ b/src/faebryk/libs/app/designators.py @@ -8,18 +8,13 @@ from pathlib import Path from typing import cast -from faebryk.core.core import Graph +import faebryk.library._F as F +from faebryk.core.graphinterface import Graph from faebryk.core.util import ( get_all_nodes_by_names, get_all_nodes_with_trait, ) from faebryk.exporters.netlist.netlist import T2Netlist -from faebryk.library.has_designator import has_designator -from faebryk.library.has_designator_defined import has_designator_defined -from faebryk.library.has_designator_prefix import has_designator_prefix -from faebryk.library.has_footprint import has_footprint -from faebryk.library.has_overriden_name import has_overriden_name -from faebryk.library.has_overriden_name_defined import has_overriden_name_defined from faebryk.libs.kicad.fileformats import C_kicad_pcb_file from faebryk.libs.util import duplicates, get_key, groupby @@ -31,12 +26,12 @@ def attach_random_designators(graph: Graph): sorts nodes by path and then sequentially assigns designators """ - nodes = {n for n, _ in get_all_nodes_with_trait(graph, has_footprint)} + nodes = {n for n, _ in get_all_nodes_with_trait(graph, F.has_footprint)} in_use = { - n.get_trait(has_designator).get_designator() + n.get_trait(F.has_designator).get_designator() for n in nodes - if n.has_trait(has_designator) + if n.has_trait(F.has_designator) } pattern = re.compile(r"([A-Z]+)([0-9]+)") @@ -60,35 +55,35 @@ def _get_first_hole(used: list[int]): nodes_sorted = sorted(nodes, key=lambda x: x.get_full_name()) for n in nodes_sorted: - if n.has_trait(has_designator): + if n.has_trait(F.has_designator): continue - if not n.has_trait(has_designator_prefix): + if not n.has_trait(F.has_designator_prefix): prefix = type(n).__name__ logger.warning(f"Node {prefix} has no designator prefix") - prefix = n.get_trait(has_designator_prefix).get_prefix() + prefix = n.get_trait(F.has_designator_prefix).get_prefix() next_num = _get_first_hole(assigned[prefix]) designator = f"{prefix}{next_num}" - n.add_trait(has_designator_defined(designator)) + n.add_trait(F.has_designator_defined(designator)) assigned[prefix].append(next_num) - no_designator = {n for n in nodes if not n.has_trait(has_designator)} + no_designator = {n for n in nodes if not n.has_trait(F.has_designator)} assert not no_designator - dupes = duplicates(nodes, lambda n: n.get_trait(has_designator).get_designator()) + dupes = duplicates(nodes, lambda n: n.get_trait(F.has_designator).get_designator()) assert not dupes, f"Duplcicate designators: {dupes}" def override_names_with_designators(graph: Graph): - for n, t in get_all_nodes_with_trait(graph, has_designator): + for n, t in get_all_nodes_with_trait(graph, F.has_designator): name = t.get_designator() - if n.has_trait(has_overriden_name): + if n.has_trait(F.has_overriden_name): logger.warning( - f"Renaming: {n.get_trait(has_overriden_name).get_name()} -> {name}" + f"Renaming: {n.get_trait(F.has_overriden_name).get_name()} -> {name}" ) - n.add_trait(has_overriden_name_defined(name)) + n.add_trait(F.has_overriden_name_defined(name)) def attach_hierarchical_designators(graph: Graph): @@ -112,7 +107,7 @@ def load_designators_from_netlist( for _, (n, designator) in matched_nodes.items(): logger.debug(f"Matched {n} to {designator}") - n.add_trait(has_designator_defined(designator)) + n.add_trait(F.has_designator_defined(designator)) logger.info(f"Matched {len(matched_nodes)}/{len(designators)} designators") nomatch = { @@ -131,7 +126,7 @@ def replace_faebryk_names_with_designators_in_kicad_pcb(graph: Graph, pcbfile: P pattern = re.compile(r"^(.*)\[[^\]]*\]$") translation = { n.get_full_name(): t.get_name() - for n, t in get_all_nodes_with_trait(graph, has_overriden_name) + for n, t in get_all_nodes_with_trait(graph, F.has_overriden_name) } for fp in pcb.kicad_pcb.footprints: diff --git a/src/faebryk/libs/app/erc.py b/src/faebryk/libs/app/erc.py index 4b337725..c182e9d1 100644 --- a/src/faebryk/libs/app/erc.py +++ b/src/faebryk/libs/app/erc.py @@ -5,12 +5,14 @@ import logging from typing import Callable, Iterable, Sequence -from faebryk.core.core import Graph, Module, ModuleInterface +import faebryk.library._F as F +from faebryk.core.graphinterface import Graph +from faebryk.core.module import Module +from faebryk.core.moduleinterface import ModuleInterface from faebryk.core.util import ( get_all_nodes_of_type, get_all_nodes_of_types, ) -from faebryk.library.has_overriden_name import has_overriden_name from faebryk.libs.picker.picker import has_part_picked from faebryk.libs.util import groupby, print_stack @@ -66,7 +68,7 @@ def simple_erc(G: Graph): electricpower = get_all_nodes_of_type(G, ElectricPower) logger.info(f"Checking {len(electricpower)} Power") for ep in electricpower: - if ep.IFs.lv.is_connected_to(ep.IFs.hv): + if ep.lv.is_connected_to(ep.hv): raise ERCFaultShort([ep], "shorted power") # shorted nets @@ -75,21 +77,21 @@ def simple_erc(G: Graph): for net in nets: collisions = { p[0] - for mif in net.IFs.part_of.get_direct_connections() + for mif in net.part_of.get_direct_connections() if (p := mif.get_parent()) and isinstance(p[0], Net) } if collisions: shorted = collisions | {net} raise ERCFaultShort( - [n.IFs.part_of for n in shorted], f"shorted nets: {shorted}" + [n.part_of for n in shorted], f"shorted nets: {shorted}" ) # net name collisions net_name_collisions = { k: v for k, v in groupby( - nets, lambda n: n.get_trait(has_overriden_name).get_name() + nets, lambda n: n.get_trait(F.has_overriden_name).get_name() ).items() if len(v) > 1 } @@ -105,7 +107,7 @@ def simple_erc(G: Graph): # ] # logger.info(f"Checking {len(sym_fps)} symmetric footprints") # for fp in sym_fps: - # mifs = set(fp.IFs.get_all()) + # mifs = set(fp.get_all()) # checked = set() # for mif in mifs: # checked.add(mif) @@ -120,14 +122,14 @@ def simple_erc(G: Graph): and comp.get_trait(has_part_picked).get_part().partno == "REMOVE" ): continue - if comp.IFs.unnamed[0].is_connected_to(comp.IFs.unnamed[1]): - raise ERCFaultShort(comp.IFs.unnamed, "shorted component") + if comp.unnamed[0].is_connected_to(comp.unnamed[1]): + raise ERCFaultShort(comp.unnamed, "shorted component") ## unmapped Electricals # fps = [n for n in nodes if isinstance(n, Footprint)] # logger.info(f"Checking {len(fps)} footprints") # for fp in fps: - # for mif in fp.IFs.get_all(): + # for mif in fp.get_all(): # if not mif.get_direct_connections(): # raise ERCFault([mif], "no connections") diff --git a/src/faebryk/libs/app/manufacturing.py b/src/faebryk/libs/app/manufacturing.py index b89a0996..a3070dda 100644 --- a/src/faebryk/libs/app/manufacturing.py +++ b/src/faebryk/libs/app/manufacturing.py @@ -4,7 +4,7 @@ import logging from pathlib import Path -from faebryk.core.core import Module +from faebryk.core.module import Module from faebryk.core.util import get_all_modules from faebryk.exporters.bom.jlcpcb import write_bom_jlcpcb from faebryk.exporters.pcb.kicad.artifacts import ( diff --git a/src/faebryk/libs/app/parameters.py b/src/faebryk/libs/app/parameters.py index 0a783601..4af45c73 100644 --- a/src/faebryk/libs/app/parameters.py +++ b/src/faebryk/libs/app/parameters.py @@ -3,31 +3,32 @@ import logging -from faebryk.core.core import Module -from faebryk.core.util import get_all_modules -from faebryk.library.ANY import ANY -from faebryk.library.TBD import TBD +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.core.parameter import Parameter logger = logging.getLogger(__name__) def replace_tbd_with_any(module: Module, recursive: bool, loglvl: int | None = None): """ - Replace all TBD instances with ANY instances in the given module. + Replace all F.TBD instances with F.ANY instances in the given module. - :param module: The module to replace TBD instances in. - :param recursive: If True, replace TBD instances in submodules as well. + :param module: The module to replace F.TBD instances in. + :param recursive: If True, replace F.TBD instances in submodules as well. """ + from faebryk.core.util import get_all_modules + lvl = logger.getEffectiveLevel() if loglvl is not None: logger.setLevel(loglvl) module = module.get_most_special() - for param in module.PARAMs.get_all(): - if isinstance(param.get_most_narrow(), TBD): - logger.debug(f"Replacing in {module}: {param} with ANY") - param.merge(ANY()) + for param in module.get_children(direct_only=True, types=Parameter): + if isinstance(param.get_most_narrow(), F.TBD): + logger.debug(f"Replacing in {module}: {param} with F.ANY") + param.merge(F.ANY()) logger.setLevel(lvl) diff --git a/src/faebryk/libs/app/pcb.py b/src/faebryk/libs/app/pcb.py index daa4e4e8..8820d265 100644 --- a/src/faebryk/libs/app/pcb.py +++ b/src/faebryk/libs/app/pcb.py @@ -7,15 +7,12 @@ from pathlib import Path from typing import Any, Callable -from faebryk.core.core import Module +import faebryk.library._F as F from faebryk.core.graph import Graph +from faebryk.core.module import Module from faebryk.core.util import get_node_tree, iter_tree_by_depth from faebryk.exporters.pcb.kicad.transformer import PCB_Transformer from faebryk.exporters.pcb.routing.util import apply_route_in_pcb -from faebryk.library.has_pcb_layout import has_pcb_layout -from faebryk.library.has_pcb_position import has_pcb_position -from faebryk.library.has_pcb_position_defined import has_pcb_position_defined -from faebryk.library.has_pcb_routing_strategy import has_pcb_routing_strategy from faebryk.libs.app.kicad_netlist import write_netlist from faebryk.libs.kicad.fileformats import ( C_kicad_fp_lib_table_file, @@ -27,30 +24,30 @@ def apply_layouts(app: Module): - if not app.has_trait(has_pcb_position): + if not app.has_trait(F.has_pcb_position): app.add_trait( - has_pcb_position_defined( - has_pcb_position.Point((0, 0, 0, has_pcb_position.layer_type.NONE)) + F.has_pcb_position_defined( + F.has_pcb_position.Point((0, 0, 0, F.has_pcb_position.layer_type.NONE)) ) ) tree = get_node_tree(app) for level in iter_tree_by_depth(tree): for n in level: - if n.has_trait(has_pcb_layout): - n.get_trait(has_pcb_layout).apply() + if n.has_trait(F.has_pcb_layout): + n.get_trait(F.has_pcb_layout).apply() def apply_routing(app: Module, transformer: PCB_Transformer): - strategies: list[tuple[has_pcb_routing_strategy, int]] = [] + strategies: list[tuple[F.has_pcb_routing_strategy, int]] = [] tree = get_node_tree(app) for i, level in enumerate(list(iter_tree_by_depth(tree))): for n in level: - if not n.has_trait(has_pcb_routing_strategy): + if not n.has_trait(F.has_pcb_routing_strategy): continue - strategies.append((n.get_trait(has_pcb_routing_strategy), i)) + strategies.append((n.get_trait(F.has_pcb_routing_strategy), i)) logger.info("Applying routes") diff --git a/src/faebryk/libs/brightness.py b/src/faebryk/libs/brightness.py index 1a16a7be..678e31b4 100644 --- a/src/faebryk/libs/brightness.py +++ b/src/faebryk/libs/brightness.py @@ -4,9 +4,8 @@ from copy import copy from enum import Enum -from faebryk.core.core import Parameter -from faebryk.library.Constant import Constant -from faebryk.library.Range import Range +import faebryk.library._F as F +from faebryk.core.parameter import Parameter from faebryk.libs.units import P, Quantity """ @@ -80,50 +79,50 @@ class TypicalLuminousIntensity(Enum): Well known luminous intensities in candela. """ - CANDLE = LuminousFlux(Constant(1 * P.candela)) + CANDLE = LuminousFlux(F.Constant(1 * P.candela)) - CREE_SMD_LED_EXTREMELY_DIM = LuminousFlux(Constant(10 * P.millicandela)) - CREE_SMD_LED_VERY_DIM = LuminousFlux(Constant(25 * P.millicandela)) - CREE_SMD_LED_DIM = LuminousFlux(Constant(50 * P.millicandela)) - CREE_SMD_LED_NORMAL = LuminousFlux(Constant(100 * P.millicandela)) - CREE_SMD_LED_BRIGHT = LuminousFlux(Constant(250 * P.millicandela)) - CREE_SMD_LED_VERY_BRIGHT = LuminousFlux(Constant(2 * P.candela)) - CREE_SMD_LED_EXTREMELY_BRIGHT = LuminousFlux(Constant(14 * P.candela)) + CREE_SMD_LED_EXTREMELY_DIM = LuminousFlux(F.Constant(10 * P.millicandela)) + CREE_SMD_LED_VERY_DIM = LuminousFlux(F.Constant(25 * P.millicandela)) + CREE_SMD_LED_DIM = LuminousFlux(F.Constant(50 * P.millicandela)) + CREE_SMD_LED_NORMAL = LuminousFlux(F.Constant(100 * P.millicandela)) + CREE_SMD_LED_BRIGHT = LuminousFlux(F.Constant(250 * P.millicandela)) + CREE_SMD_LED_VERY_BRIGHT = LuminousFlux(F.Constant(2 * P.candela)) + CREE_SMD_LED_EXTREMELY_BRIGHT = LuminousFlux(F.Constant(14 * P.candela)) TYPICAL_SMD_LED_MAX_BRIGHTNESS = LuminousFlux( - Range(60 * P.millicandela, 800 * P.mcandela) + F.Range(60 * P.millicandela, 800 * P.mcandela) ) - WS2812B_LED_RED = LuminousFlux(Constant(420 * P.millicandela)) - WS2812B_LED_GREEN = LuminousFlux(Constant(720 * P.millicandela)) - WS2812B_LED_BLUE = LuminousFlux(Constant(200 * P.millicandela)) + WS2812B_LED_RED = LuminousFlux(F.Constant(420 * P.millicandela)) + WS2812B_LED_GREEN = LuminousFlux(F.Constant(720 * P.millicandela)) + WS2812B_LED_BLUE = LuminousFlux(F.Constant(200 * P.millicandela)) APPLICATION_CAR_HEADLIGHTS_HALOGEN_LOW_BEAM_MEDIUM = LuminousFlux( - Constant(20 * P.kcandela) + F.Constant(20 * P.kcandela) ) APPLICATION_CAR_HEADLIGHTS_HALOGEN_HIGH_BEAM_MEDIUM = LuminousFlux( - Constant(40 * P.kcandela) + F.Constant(40 * P.kcandela) ) - APPLICATION_CAR_TURN_INDICATOR_DIM = LuminousFlux(Constant(1 * P.kcandela)) - APPLICATION_CAR_TURN_INDICATOR_BRIGHT = LuminousFlux(Constant(10 * P.kcandela)) - APPLICATION_CAR_BREAK_LIGHT_DIM = LuminousFlux(Constant(5 * P.kcandela)) - APPLICATION_CAR_BREAK_LIGHT_BRIGHT = LuminousFlux(Constant(50 * P.kcandela)) + APPLICATION_CAR_TURN_INDICATOR_DIM = LuminousFlux(F.Constant(1 * P.kcandela)) + APPLICATION_CAR_TURN_INDICATOR_BRIGHT = LuminousFlux(F.Constant(10 * P.kcandela)) + APPLICATION_CAR_BREAK_LIGHT_DIM = LuminousFlux(F.Constant(5 * P.kcandela)) + APPLICATION_CAR_BREAK_LIGHT_BRIGHT = LuminousFlux(F.Constant(50 * P.kcandela)) # not sure about these values - APPLICATION_LED_STANDBY = LuminousFlux(Range(1 * P.millicandela, 10 * P.mcandela)) + APPLICATION_LED_STANDBY = LuminousFlux(F.Range(1 * P.millicandela, 10 * P.mcandela)) APPLICATION_LED_INDICATOR_INSIDE = LuminousFlux( - Range(10 * P.millicandela, 100 * P.mcandela) + F.Range(10 * P.millicandela, 100 * P.mcandela) ) APPLICATION_LED_KEYBOARD_BACKLIGHT = LuminousFlux( - Range(50 * P.millicandela, 500 * P.mcandela) + F.Range(50 * P.millicandela, 500 * P.mcandela) ) APPLICATION_LED_INDICATOR_OUTSIDE = LuminousFlux( - Range(100 * P.millicandela, 1 * P.candela) + F.Range(100 * P.millicandela, 1 * P.candela) ) APPLICATION_LED_DECORATIVE_LIGHTING = LuminousFlux( - Range(100 * P.millicandela, 1 * P.candela) + F.Range(100 * P.millicandela, 1 * P.candela) ) - APPLICATION_LED_FLASHLIGHT = LuminousFlux(Range(10 * P.candela, 1 * P.kcandela)) + APPLICATION_LED_FLASHLIGHT = LuminousFlux(F.Range(10 * P.candela, 1 * P.kcandela)) class TypicalLuminousFlux(Enum): @@ -131,21 +130,21 @@ class TypicalLuminousFlux(Enum): Well known luminous flux in lumen. """ - IKEA_E14_BULB_LED_DIM = LuminousFlux(Constant(100 * P.lm)) - IKEA_E14_BULB_LED_MEDIUM = LuminousFlux(Constant(250 * P.lm)) - IKEA_E14_BULB_LED_BRIGHT = LuminousFlux(Constant(470 * P.lm)) - IKEA_GU10_BULB_LED_DIM = LuminousFlux(Constant(230 * P.lm)) - IKEA_GU10_BULB_LED_MEDIUM = LuminousFlux(Constant(345 * P.lm)) - IKEA_E27_BULB_LED_DIM = LuminousFlux(Constant(470 * P.lm)) - IKEA_E27_BULB_LED_MEDIUM = LuminousFlux(Constant(806 * P.lm)) - IKEA_E27_BULB_LED_BRIGHT = LuminousFlux(Constant(1500 * P.lm)) + IKEA_E14_BULB_LED_DIM = LuminousFlux(F.Constant(100 * P.lm)) + IKEA_E14_BULB_LED_MEDIUM = LuminousFlux(F.Constant(250 * P.lm)) + IKEA_E14_BULB_LED_BRIGHT = LuminousFlux(F.Constant(470 * P.lm)) + IKEA_GU10_BULB_LED_DIM = LuminousFlux(F.Constant(230 * P.lm)) + IKEA_GU10_BULB_LED_MEDIUM = LuminousFlux(F.Constant(345 * P.lm)) + IKEA_E27_BULB_LED_DIM = LuminousFlux(F.Constant(470 * P.lm)) + IKEA_E27_BULB_LED_MEDIUM = LuminousFlux(F.Constant(806 * P.lm)) + IKEA_E27_BULB_LED_BRIGHT = LuminousFlux(F.Constant(1500 * P.lm)) - CREE_SMD_LED_VERY_BRIGHT = LuminousFlux(Constant(6000 * P.lm)) + CREE_SMD_LED_VERY_BRIGHT = LuminousFlux(F.Constant(6000 * P.lm)) - LASER_POINTER_GREEN_5MW = LuminousFlux(Constant(3.4 * P.lm)) + LASER_POINTER_GREEN_5MW = LuminousFlux(F.Constant(3.4 * P.lm)) - CAR_HEADLIGHTS_HALOGEN_LOW_BEAM_MEDIUM = LuminousFlux(Constant(1000 * P.lm)) - CAR_HEADLIGHTS_HALOGEN_HIGH_BEAM_MEDIUM = LuminousFlux(Constant(1300 * P.lm)) + CAR_HEADLIGHTS_HALOGEN_LOW_BEAM_MEDIUM = LuminousFlux(F.Constant(1000 * P.lm)) + CAR_HEADLIGHTS_HALOGEN_HIGH_BEAM_MEDIUM = LuminousFlux(F.Constant(1300 * P.lm)) class TypicalIlluminance(Enum): @@ -154,17 +153,17 @@ class TypicalIlluminance(Enum): """ # https://en.wikipedia.org/wiki/Lux - MOONLESS_OVERCAST_NIGHT_SKY_STARLIGHT = Illuminance(Constant(0.0001 * P.lx)) - MOONLESS_CLEAR_NIGHT_SKY_WITH_AIRGLOW = Illuminance(Constant(0.002 * P.lx)) - FULL_MOON_ON_A_CLEAR_NIGHT = Illuminance(Constant(0.05 * P.lx)) - DARK_LIMIT_OF_CIVIL_TWILIGHT_UNDER_A_CLEAR_SKY = Illuminance(Constant(3.4 * P.lx)) - PUBLIC_AREAS_WITH_DARK_SURROUNDINGS = Illuminance(Constant(20 * P.lx)) - FAMILY_LIVING_ROOM_LIGHTS = Illuminance(Constant(50 * P.lx)) - OFFICE_BUILDING_HALLWAY_TOILET_LIGHTING = Illuminance(Constant(80 * P.lx)) - VERY_DARK_OVERCAST_DAY = Illuminance(Constant(100 * P.lx)) - TRAIN_STATION_PLATFORMS = Illuminance(Constant(150 * P.lx)) - OFFICE_LIGHTING = Illuminance(Constant(320 * P.lx)) - SUNRISE_OR_SUNSET_ON_A_CLEAR_DAY = Illuminance(Constant(400 * P.lx)) - OVERCAST_DAY = Illuminance(Constant(1000 * P.lx)) - FULL_DAYLIGHT = Illuminance(Constant(25000 * P.lx)) - DIRECT_SUNLIGHT = Illuminance(Constant(100000 * P.lx)) + MOONLESS_OVERCAST_NIGHT_SKY_STARLIGHT = Illuminance(F.Constant(0.0001 * P.lx)) + MOONLESS_CLEAR_NIGHT_SKY_WITH_AIRGLOW = Illuminance(F.Constant(0.002 * P.lx)) + FULL_MOON_ON_A_CLEAR_NIGHT = Illuminance(F.Constant(0.05 * P.lx)) + DARK_LIMIT_OF_CIVIL_TWILIGHT_UNDER_A_CLEAR_SKY = Illuminance(F.Constant(3.4 * P.lx)) + PUBLIC_AREAS_WITH_DARK_SURROUNDINGS = Illuminance(F.Constant(20 * P.lx)) + FAMILY_LIVING_ROOM_LIGHTS = Illuminance(F.Constant(50 * P.lx)) + OFFICE_BUILDING_HALLWAY_TOILET_LIGHTING = Illuminance(F.Constant(80 * P.lx)) + VERY_DARK_OVERCAST_DAY = Illuminance(F.Constant(100 * P.lx)) + TRAIN_STATION_PLATFORMS = Illuminance(F.Constant(150 * P.lx)) + OFFICE_LIGHTING = Illuminance(F.Constant(320 * P.lx)) + SUNRISE_OR_SUNSET_ON_A_CLEAR_DAY = Illuminance(F.Constant(400 * P.lx)) + OVERCAST_DAY = Illuminance(F.Constant(1000 * P.lx)) + FULL_DAYLIGHT = Illuminance(F.Constant(25000 * P.lx)) + DIRECT_SUNLIGHT = Illuminance(F.Constant(100000 * P.lx)) diff --git a/src/faebryk/libs/e_series.py b/src/faebryk/libs/e_series.py index 65a70fb7..a3990712 100644 --- a/src/faebryk/libs/e_series.py +++ b/src/faebryk/libs/e_series.py @@ -3,7 +3,7 @@ from typing import Tuple import faebryk.library._F as F -from faebryk.core.core import Parameter +from faebryk.core.parameter import Parameter from faebryk.libs.units import Quantity logger = logging.getLogger(__name__) diff --git a/src/faebryk/libs/examples/buildutil.py b/src/faebryk/libs/examples/buildutil.py index cab76811..b86c64f6 100644 --- a/src/faebryk/libs/examples/buildutil.py +++ b/src/faebryk/libs/examples/buildutil.py @@ -6,9 +6,8 @@ from pathlib import Path import faebryk.libs.picker.lcsc as lcsc -from faebryk.core.core import Module +from faebryk.core.module import Module from faebryk.core.util import get_all_modules -from faebryk.exporters.visualize.graph import render_sidebyside from faebryk.libs.app.checks import run_checks from faebryk.libs.app.parameters import replace_tbd_with_any from faebryk.libs.app.pcb import apply_design @@ -84,14 +83,4 @@ def apply_design_to_pcb(m: Module): def export_graph(g, show): - plt = render_sidebyside(g) - - GRAPH_OUT.parent.mkdir( - parents=True, - exist_ok=True, - ) - logging.info("Writing Experiment graph to {}".format(GRAPH_OUT.absolute())) - plt.savefig(GRAPH_OUT, format="png", bbox_inches="tight") - - if show: - plt.show() + raise NotImplementedError() diff --git a/src/faebryk/libs/examples/pickers.py b/src/faebryk/libs/examples/pickers.py index 52773700..f30b10d1 100644 --- a/src/faebryk/libs/examples/pickers.py +++ b/src/faebryk/libs/examples/pickers.py @@ -6,17 +6,20 @@ """ import logging +from typing import TYPE_CHECKING import faebryk.library._F as F -from faebryk.core.core import Module +from faebryk.core.module import Module from faebryk.core.util import specialize_module from faebryk.library._F import Constant, Range -from faebryk.library.Switch import _TSwitch from faebryk.libs.app.parameters import replace_tbd_with_any from faebryk.libs.picker.lcsc import LCSC_Part from faebryk.libs.picker.picker import PickerOption, pick_module_by_params from faebryk.libs.units import P +if TYPE_CHECKING: + from faebryk.library.Switch import _TSwitch + logger = logging.getLogger(__name__) @@ -46,9 +49,9 @@ def pick_fuse(module: F.Fuse): def pick_mosfet(module: F.MOSFET): standard_pinmap = { - "1": module.IFs.gate, - "2": module.IFs.source, - "3": module.IFs.drain, + "1": module.gate, + "2": module.source, + "3": module.drain, } pick_module_by_params( module, @@ -189,7 +192,7 @@ def pick_led(module: F.LED): "forward_voltage": Constant(3.7 * P.volt), "max_current": Constant(100 * P.mA), }, - pinmap={"1": module.IFs.cathode, "2": module.IFs.anode}, + pinmap={"1": module.cathode, "2": module.anode}, ), PickerOption( part=LCSC_Part(partno="C72041"), @@ -199,7 +202,7 @@ def pick_led(module: F.LED): "forward_voltage": Constant(3.1 * P.volt), "max_current": Constant(100 * P.mA), }, - pinmap={"1": module.IFs.cathode, "2": module.IFs.anode}, + pinmap={"1": module.cathode, "2": module.anode}, ), PickerOption( part=LCSC_Part(partno="C72038"), @@ -209,7 +212,7 @@ def pick_led(module: F.LED): "forward_voltage": Constant(2.3 * P.volt), "max_current": Constant(60 * P.mA), }, - pinmap={"1": module.IFs.cathode, "2": module.IFs.anode}, + pinmap={"1": module.cathode, "2": module.anode}, ), ], ) @@ -225,8 +228,8 @@ def pick_tvs(module: F.TVS): "reverse_working_voltage": Constant(5 * P.V), }, pinmap={ - "1": module.IFs.cathode, - "2": module.IFs.anode, + "1": module.cathode, + "2": module.anode, }, ), ], @@ -256,15 +259,15 @@ def pick_battery(module: F.Battery): "shape": Constant(F.ButtonCell.Shape.Round), }, pinmap={ - "1": module.IFs.power.IFs.lv, - "2": module.IFs.power.IFs.hv, + "1": module.power.lv, + "2": module.power.hv, }, ), ], ) -def pick_switch(module: _TSwitch[F.Electrical]): +def pick_switch(module: "_TSwitch[F.Electrical]"): module.add_trait(F.can_attach_to_footprint_symmetrically()) pick_module_by_params( module, diff --git a/src/faebryk/libs/library/L.py b/src/faebryk/libs/library/L.py new file mode 100644 index 00000000..bc331c4e --- /dev/null +++ b/src/faebryk/libs/library/L.py @@ -0,0 +1,15 @@ +# This file is part of the faebryk project +# SPDX-License-Identifier: MIT + +import logging + +from faebryk.core.module import Module # noqa: F401 +from faebryk.core.node import ( # noqa: F401 + Node, + d_field, + f_field, + list_field, + rt_field, +) + +logger = logging.getLogger(__name__) diff --git a/src/faebryk/libs/logging.py b/src/faebryk/libs/logging.py index a53477f0..84b01aa8 100644 --- a/src/faebryk/libs/logging.py +++ b/src/faebryk/libs/logging.py @@ -8,6 +8,8 @@ from rich.logging import RichHandler from rich.theme import Theme +from faebryk.libs.util import ConfigFlag + class NodeHighlighter(RegexHighlighter): """ @@ -44,6 +46,9 @@ class NodeHighlighter(RegexHighlighter): } ) +PLOG = ConfigFlag("PLOG", descr="Enable picker debug log") +JLOG = ConfigFlag("JLOG", descr="Enable jlcpcb picker debug log") + def setup_basic_logging(rich: bool = True): logging.basicConfig( @@ -61,3 +66,15 @@ def setup_basic_logging(rich: bool = True): if rich else None, ) + + if PLOG: + from faebryk.library.has_multi_picker import logger as plog + + plog.setLevel(logging.DEBUG) + from faebryk.libs.picker.picker import logger as rlog + + rlog.setLevel(logging.DEBUG) + if JLOG: + from faebryk.libs.picker.jlcpcb.jlcpcb import logger as jlog + + jlog.setLevel(logging.DEBUG) diff --git a/src/faebryk/libs/picker/jlcpcb/jlcpcb.py b/src/faebryk/libs/picker/jlcpcb/jlcpcb.py index ccf9f9d1..6120d2e6 100644 --- a/src/faebryk/libs/picker/jlcpcb/jlcpcb.py +++ b/src/faebryk/libs/picker/jlcpcb/jlcpcb.py @@ -21,9 +21,9 @@ from tortoise.models import Model import faebryk.library._F as F -from faebryk.core.core import Module, Parameter +from faebryk.core.module import Module +from faebryk.core.parameter import Parameter from faebryk.core.util import pretty_param_tree, pretty_params -from faebryk.library.Set import Set from faebryk.libs.e_series import ( E_SERIES_VALUES, ParamNotResolvedError, @@ -337,9 +337,9 @@ def attach( ) for name, value in zip([m.param_name for m in mapping], params): - getattr(module.PARAMs, name).override(value) + getattr(module, name).override(value) - F.has_defined_descriptive_properties.add_properties_to( + F.has_descriptive_properties_defined.add_properties_to( module, { DescriptiveProperties.partno: self.mfr, @@ -355,8 +355,8 @@ def attach( }, ) - module.add_trait(has_part_picked_defined(JLCPCB_Part(self.partno))) attach(module, self.partno) + module.add_trait(has_part_picked_defined(JLCPCB_Part(self.partno))) if logger.isEnabledFor(logging.DEBUG): logger.debug( f"Attached component {self.partno} to module {module}: \n" @@ -417,7 +417,7 @@ def filter_by_value( assert not self.results value_query = Q() try: - intersection = Set( + intersection = F.Set( [e_series_intersect(value, e_series or E_SERIES_VALUES.E_ALL)] ).params except ParamNotResolvedError as e: @@ -511,7 +511,7 @@ def filter_by_module_params( if not all( pm := [ - p.is_subset_of(getattr(module.PARAMs, m.param_name)) + p.is_subset_of(getattr(module, m.param_name)) for p, m in zip(params, mapping) ] ): @@ -623,7 +623,7 @@ def init(self) -> None: else: raise FileNotFoundError(f"No JLCPCB database found at {self.db_file}") elif not self.is_db_up_to_date(): - if no_download_prompt or self.prompt_db_update( + if not no_download_prompt and self.prompt_db_update( f"JLCPCB database at {self.db_file} is older than 7 days, update?" ): self.download() diff --git a/src/faebryk/libs/picker/jlcpcb/picker_lib.py b/src/faebryk/libs/picker/jlcpcb/picker_lib.py index 04d6a07b..03c35426 100644 --- a/src/faebryk/libs/picker/jlcpcb/picker_lib.py +++ b/src/faebryk/libs/picker/jlcpcb/picker_lib.py @@ -3,7 +3,7 @@ from typing import Callable import faebryk.library._F as F -from faebryk.core.core import Module +from faebryk.core.module import Module from faebryk.libs.e_series import E_SERIES_VALUES from faebryk.libs.picker.jlcpcb.jlcpcb import ( ComponentQuery, @@ -154,7 +154,7 @@ def find_resistor(cmp: Module): ComponentQuery() .filter_by_category("Resistors", "Chip Resistor - Surface Mount") .filter_by_stock(qty) - .filter_by_value(cmp.PARAMs.resistance, "Ω", E_SERIES_VALUES.E96) + .filter_by_value(cmp.resistance, "Ω", E_SERIES_VALUES.E96) .filter_by_traits(cmp) .sort_by_price(qty) .filter_by_module_params_and_attach(cmp, mapping, qty) @@ -192,7 +192,7 @@ def find_capacitor(cmp: Module): ) .filter_by_stock(qty) .filter_by_traits(cmp) - .filter_by_value(cmp.PARAMs.capacitance, "F", E_SERIES_VALUES.E24) + .filter_by_value(cmp.capacitance, "F", E_SERIES_VALUES.E24) .sort_by_price(qty) .filter_by_module_params_and_attach(cmp, mapping, qty) ) @@ -236,7 +236,7 @@ def find_inductor(cmp: Module): .filter_by_category("Inductors", "Inductors") .filter_by_stock(qty) .filter_by_traits(cmp) - .filter_by_value(cmp.PARAMs.inductance, "H", E_SERIES_VALUES.E24) + .filter_by_value(cmp.inductance, "H", E_SERIES_VALUES.E24) .sort_by_price(qty) .filter_by_module_params_and_attach(cmp, mapping, qty) ) diff --git a/src/faebryk/libs/picker/jlcpcb/pickers.py b/src/faebryk/libs/picker/jlcpcb/pickers.py index c24a74c9..0e1d4ca1 100644 --- a/src/faebryk/libs/picker/jlcpcb/pickers.py +++ b/src/faebryk/libs/picker/jlcpcb/pickers.py @@ -2,7 +2,7 @@ import faebryk.library._F as F import faebryk.libs.picker.jlcpcb.picker_lib as P -from faebryk.core.core import Module +from faebryk.core.module import Module from faebryk.libs.picker.jlcpcb.jlcpcb import JLCPCB_DB, ComponentQuery from faebryk.libs.picker.picker import PickError @@ -24,6 +24,7 @@ class StaticJLCPCBPartPicker(F.has_multi_picker.Picker): Use this if you want to specify a specific part to the multi-picker eg. a default switch """ + def __init__( self, *, diff --git a/src/faebryk/libs/picker/lcsc.py b/src/faebryk/libs/picker/lcsc.py index 5d0cb12c..9e29e921 100644 --- a/src/faebryk/libs/picker/lcsc.py +++ b/src/faebryk/libs/picker/lcsc.py @@ -14,17 +14,8 @@ from easyeda2kicad.kicad.export_kicad_3d_model import Exporter3dModelKicad from easyeda2kicad.kicad.export_kicad_footprint import ExporterFootprintKicad -from faebryk.core.core import Module -from faebryk.library.can_attach_to_footprint import can_attach_to_footprint -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.has_defined_descriptive_properties import ( - has_defined_descriptive_properties, -) -from faebryk.library.has_footprint import has_footprint -from faebryk.library.has_pin_association_heuristic import has_pin_association_heuristic -from faebryk.library.KicadFootprint import KicadFootprint +import faebryk.library._F as F +from faebryk.core.module import Module from faebryk.libs.picker.picker import ( Part, PickerOption, @@ -180,13 +171,13 @@ def attach(component: Module, partno: str, get_model: bool = True): ) # symbol - if not component.has_trait(has_footprint): - if not component.has_trait(can_attach_to_footprint): - if not component.has_trait(has_pin_association_heuristic): + if not component.has_trait(F.has_footprint): + if not component.has_trait(F.can_attach_to_footprint): + if not component.has_trait(F.has_pin_association_heuristic): raise LCSCException( partno, - f"Need either can_attach_to_footprint or " - "has_pin_association_heuristic" + f"Need either F.can_attach_to_footprint or " + "F.has_pin_association_heuristic" f" for {component} with partno {partno}", ) @@ -196,21 +187,21 @@ def attach(component: Module, partno: str, get_model: bool = True): for pin in easyeda_symbol.pins ] try: - pinmap = component.get_trait(has_pin_association_heuristic).get_pins( + pinmap = component.get_trait(F.has_pin_association_heuristic).get_pins( pins ) - except has_pin_association_heuristic.PinMatchException as e: + except F.has_pin_association_heuristic.PinMatchException as e: raise LCSC_PinmapException(partno, f"Failed to get pinmap: {e}") from e - component.add_trait(can_attach_to_footprint_via_pinmap(pinmap)) + component.add_trait(F.can_attach_to_footprint_via_pinmap(pinmap)) # footprint - fp = KicadFootprint( + fp = F.KicadFootprint( f"lcsc:{easyeda_footprint.info.name}", [p.number for p in easyeda_footprint.pads], ) - component.get_trait(can_attach_to_footprint).attach(fp) + component.get_trait(F.can_attach_to_footprint).attach(fp) - has_defined_descriptive_properties.add_properties_to(component, {"LCSC": partno}) + F.has_descriptive_properties_defined.add_properties_to(component, {"LCSC": partno}) # model done by kicad (in fp) @@ -220,7 +211,7 @@ def attach(self, module: Module, part: PickerOption): assert isinstance(part.part, LCSC_Part) attach(component=module, partno=part.part.partno) if part.info is not None: - has_defined_descriptive_properties.add_properties_to(module, part.info) + F.has_descriptive_properties_defined.add_properties_to(module, part.info) class LCSC_Part(Part): diff --git a/src/faebryk/libs/picker/picker.py b/src/faebryk/libs/picker/picker.py index edab5b05..afa2d933 100644 --- a/src/faebryk/libs/picker/picker.py +++ b/src/faebryk/libs/picker/picker.py @@ -12,18 +12,10 @@ from rich.progress import Progress -from faebryk.core.core import Module, ModuleInterface, ModuleTrait, Parameter -from faebryk.core.util import ( - get_all_modules, - get_children, - pretty_params, -) -from faebryk.library.ANY import ANY -from faebryk.library.can_attach_to_footprint_via_pinmap import ( - can_attach_to_footprint_via_pinmap, -) -from faebryk.library.Electrical import Electrical -from faebryk.library.has_picker import has_picker +import faebryk.library._F as F +from faebryk.core.module import Module +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.core.parameter import Parameter from faebryk.libs.util import NotNone, flatten logger = logging.getLogger(__name__) @@ -51,7 +43,7 @@ class PickerOption: part: Part params: dict[str, Parameter] | None = None filter: Callable[[Module], bool] | None = None - pinmap: dict[str, Electrical] | None = None + pinmap: dict[str, F.Electrical] | None = None info: dict[str | DescriptiveProperties, str] | None = None @@ -95,6 +87,8 @@ def get_all_children(self): class PickErrorParams(PickError): def __init__(self, module: Module, options: list[PickerOption]): + from faebryk.core.util import pretty_params + self.options = options MAX = 5 @@ -113,7 +107,7 @@ def __init__(self, module: Module, options: list[PickerOption]): super().__init__(message, module) -class has_part_picked(ModuleTrait): +class has_part_picked(Module.TraitT): @abstractmethod def get_part(self) -> Part: ... @@ -150,7 +144,8 @@ def pick_module_by_params(module: Module, options: Iterable[PickerOption]): return params = { - NotNone(p.get_parent())[1]: p.get_most_narrow() for p in module.PARAMs.get_all() + NotNone(p.get_parent())[1]: p.get_most_narrow() + for p in module.get_children(direct_only=True, types=Parameter) } options = list(options) @@ -160,7 +155,7 @@ def pick_module_by_params(module: Module, options: Iterable[PickerOption]): filter( lambda o: (not o.filter or o.filter(module)) and all( - v.is_subset_of(params.get(k, ANY())) + v.is_subset_of(params.get(k, F.ANY())) for k, v in (o.params or {}).items() if not k.startswith("_") ), @@ -171,7 +166,7 @@ def pick_module_by_params(module: Module, options: Iterable[PickerOption]): raise PickErrorParams(module, options) if option.pinmap: - module.add_trait(can_attach_to_footprint_via_pinmap(option.pinmap)) + module.add_trait(F.can_attach_to_footprint_via_pinmap(option.pinmap)) option.part.supplier.attach(module, option) module.add_trait(has_part_picked_defined(option.part)) @@ -186,10 +181,12 @@ def pick_module_by_params(module: Module, options: Iterable[PickerOption]): return option -def _get_mif_top_level_modules(mif: ModuleInterface): - return [n for n in mif.NODEs.get_all() if isinstance(n, Module)] + [ - m for nmif in mif.IFs.get_all() for m in _get_mif_top_level_modules(nmif) - ] +def _get_mif_top_level_modules(mif: ModuleInterface) -> set[Module]: + return mif.get_children(direct_only=True, types=Module) | { + m + for nmif in mif.get_children(direct_only=True, types=ModuleInterface) + for m in _get_mif_top_level_modules(nmif) + } class PickerProgress: @@ -200,10 +197,14 @@ def __init__(self): @classmethod def from_module(cls, module: Module) -> "PickerProgress": self = cls() + from faebryk.core.util import get_all_modules + self.progress.update(self.task, total=len(get_all_modules(module))) return self def advance(self, module: Module): + from faebryk.core.util import get_all_modules + self.progress.advance(self.task, len(get_all_modules(module))) @contextmanager @@ -239,7 +240,7 @@ def get_not_picked(m: Module): out = flatten( [ get_not_picked(mod) - for mif in m.IFs.get_all() + for mif in m.get_children(direct_only=True, types=ModuleInterface) for mod in _get_mif_top_level_modules(mif) ] ) @@ -247,7 +248,7 @@ def get_not_picked(m: Module): if m.has_trait(has_part_picked): return out - children = [c for c in m.NODEs.get_all() if isinstance(c, Module)] + children = m.get_children(direct_only=True, types=Module) if not children: return out + [m] @@ -269,19 +270,19 @@ def _pick_part_recursively(module: Module, progress: PickerProgress | None = Non # pick mif module parts - for mif in module.IFs.get_all(): + for mif in module.get_children(direct_only=True, types=ModuleInterface): for mod in _get_mif_top_level_modules(mif): _pick_part_recursively(mod, progress) # pick - if module.has_trait(has_picker): + if module.has_trait(F.has_picker): try: - module.get_trait(has_picker).pick() + module.get_trait(F.has_picker).pick() except PickError as e: # if no children, raise # This whole logic will be so much easier if the recursive # picker is just a normal picker - if not module.NODEs.get_all(): + if not module.get_children(direct_only=True, types=Module): raise e if module.has_trait(has_part_picked): @@ -297,7 +298,7 @@ def _pick_part_recursively(module: Module, progress: PickerProgress | None = Non # go level lower to_pick: set[Module] = { c - for c in get_children(module, types=Module, direct_only=True) + for c in module.get_children(types=Module, direct_only=True) if not c.has_trait(has_part_picked) } failed: dict[Module, PickError] = {} diff --git a/src/faebryk/libs/sexp/dataclass_sexp.py b/src/faebryk/libs/sexp/dataclass_sexp.py index 7ce714c3..343d3ea5 100644 --- a/src/faebryk/libs/sexp/dataclass_sexp.py +++ b/src/faebryk/libs/sexp/dataclass_sexp.py @@ -3,7 +3,7 @@ from enum import Enum, IntEnum, StrEnum from pathlib import Path from types import UnionType -from typing import Any, Callable, Iterator, TypeVar, Union, get_args, get_origin +from typing import Any, Callable, Iterator, Union, get_args, get_origin import sexpdata from sexpdata import Symbol @@ -61,9 +61,6 @@ def from_field(cls, f: Field): class SymEnum(StrEnum): ... -T = TypeVar("T") - - def _convert(val, t): # Recurse (GenericAlias e.g list[]) if (origin := get_origin(t)) is not None: @@ -100,7 +97,7 @@ def _convert(val, t): netlist_type = list[netlist_obj] -def _decode(sexp: netlist_type, t: type[T]) -> T: +def _decode[T](sexp: netlist_type, t: type[T]) -> T: if logger.isEnabledFor(logging.DEBUG): logger.debug(f"parse into: {t.__name__} {'-'*40}") logger.debug(f"sexp: {sexp}") @@ -310,7 +307,7 @@ def _append_kv(name, v): return sexp -def loads(s: str | Path | list, t: type[T]) -> T: +def loads[T](s: str | Path | list, t: type[T]) -> T: text = s sexp = s if isinstance(s, Path): @@ -342,7 +339,7 @@ def dumps(self, path: Path | None = None): # TODO move class JSON_File: @classmethod - def loads(cls: type[T], path: Path | str) -> T: + def loads[T](cls: type[T], path: Path | str) -> T: text = path if isinstance(path, Path): text = path.read_text() diff --git a/src/faebryk/libs/util.py b/src/faebryk/libs/util.py index 85531dfa..c82cd782 100644 --- a/src/faebryk/libs/util.py +++ b/src/faebryk/libs/util.py @@ -13,7 +13,6 @@ from typing import ( Any, Callable, - Generic, Iterable, Iterator, List, @@ -22,7 +21,6 @@ SupportsFloat, SupportsInt, Type, - TypeVar, get_origin, ) @@ -96,11 +94,7 @@ def flatten(obj: Iterable, depth=1) -> List: return [nested for top in obj for nested in flatten(top, depth=depth - 1)] -T = TypeVar("T") -U = TypeVar("U") - - -def get_key(haystack: dict[T, U], needle: U) -> T: +def get_key[T, U](haystack: dict[T, U], needle: U) -> T: return find(haystack.items(), lambda x: x[1] == needle)[0] @@ -113,7 +107,7 @@ def __init__(self, duplicates: list, *args: object) -> None: self.duplicates = duplicates -def find(haystack: Iterable[T], needle: Callable[[T], bool]) -> T: +def find[T](haystack: Iterable[T], needle: Callable[[T], bool]) -> T: results = list(filter(needle, haystack)) if not results: raise KeyErrorNotFound() @@ -122,14 +116,14 @@ def find(haystack: Iterable[T], needle: Callable[[T], bool]) -> T: return results[0] -def find_or(haystack: Iterable[T], needle: Callable[[T], bool], default: T) -> T: +def find_or[T](haystack: Iterable[T], needle: Callable[[T], bool], default: T) -> T: try: return find(haystack, needle) except KeyErrorNotFound: return default -def groupby(it: Iterable[T], key: Callable[[T], U]) -> dict[U, list[T]]: +def groupby[T, U](it: Iterable[T], key: Callable[[T], U]) -> dict[U, list[T]]: out = defaultdict(list) for i in it: out[key(i)].append(i) @@ -164,11 +158,7 @@ def __setattr__(self, __name, __value) -> None: self._callback(__name, __value) -T = TypeVar("T") -P = TypeVar("P") - - -class _wrapper(NotifiesOnPropertyChange, Generic[T, P]): +class _wrapper[T, P](NotifiesOnPropertyChange): @abstractmethod def __init__(self, parent: P) -> None: raise NotImplementedError @@ -190,11 +180,8 @@ def extend_list(self, list_name: str, *objs: T) -> None: raise NotImplementedError -def Holder(_type: Type[T], _ptype: Type[P]) -> Type[_wrapper[T, P]]: - _T = TypeVar("_T") - _P = TypeVar("_P") - - class __wrapper(_wrapper[_T, _P]): +def Holder[T, P](_type: Type[T], _ptype: Type[P]) -> Type[_wrapper[T, P]]: + class __wrapper[_T, _P](_wrapper[_T, _P]): def __init__(self, parent: P) -> None: self._list: list[T] = [] self._type = _type @@ -278,24 +265,17 @@ def NotNone(x): return x -T = TypeVar("T") - - -def cast_assert(t: type[T], obj) -> T: +def cast_assert[T](t: type[T], obj) -> T: assert isinstance(obj, t) return obj -def times(cnt: SupportsInt, lamb: Callable[[], T]) -> list[T]: +def times[T](cnt: SupportsInt, lamb: Callable[[], T]) -> list[T]: return [lamb() for _ in range(int(cnt))] -T = TypeVar("T") -U = TypeVar("U") - - @staticmethod -def is_type_pair( +def is_type_pair[T, U]( param1: Any, param2: Any, type1: type[T], type2: type[U] ) -> Optional[tuple[T, U]]: o1 = get_origin(type1) or type1 @@ -484,14 +464,11 @@ def _f_no_rec(*args, **kwargs): return _f_no_rec -def zip_non_locked(left: Iterable[T], right: Iterable[U]): - TS = TypeVar("TS") - US = TypeVar("US") - +def zip_non_locked[T, U](left: Iterable[T], right: Iterable[U]): # Theoretically supports any amount of iters, # but for type hinting limit to two for now - class _Iter(Iterator[tuple[TS, US]]): + class _Iter[TS, US](Iterator[tuple[TS, US]]): class _NONDEFAULT: ... def __init__(self, args: list[Iterable]): @@ -533,7 +510,7 @@ def __next__(self): return _Iter[T, U]([left, right]) -def try_or( +def try_or[T]( func: Callable[..., T], default: T | None = None, default_f: Callable[[Exception], T] | None = None, @@ -787,3 +764,30 @@ async def get_page(page: int): yield r page += 1 + + +def factory[T, **P](con: Callable[P, T]) -> Callable[P, Callable[[], T]]: + def _(*args: P.args, **kwargs: P.kwargs) -> Callable[[], T]: + def __() -> T: + return con(*args, **kwargs) + + return __ + + return _ + + +def once[T, **P](f: Callable[P, T]) -> Callable[P, T]: + class _once: + def __init__(self) -> None: + self.cache = {} + + def __call__(self, *args: P.args, **kwds: P.kwargs) -> Any: + lookup = (args, tuple(kwds.items())) + if lookup in self.cache: + return self.cache[lookup] + + result = f(*args, **kwds) + self.cache[lookup] = result + return result + + return _once() diff --git a/src/faebryk/tools/libadd.py b/src/faebryk/tools/libadd.py index d48b936a..937b5ded 100644 --- a/src/faebryk/tools/libadd.py +++ b/src/faebryk/tools/libadd.py @@ -90,45 +90,35 @@ def module(ctx: typer.Context, interface: bool = False): import logging - from faebryk.core.core import {base} + import faebryk.library._F as F # noqa: F401 + from faebryk.core.{base.lower()} import {base} + from faebryk.libs.library import L # noqa: F401 + from faebryk.libs.units import P # noqa: F401 logger = logging.getLogger(__name__) class {get_name(ctx)}({base}): - @classmethod - def NODES(cls): - # submodules - class _NODES(super().NODES()): - pass - - return _NODES - - @classmethod - def PARAMS(cls): - # parameters - class _PARAMS(super().PARAMS()): - pass - - return _PARAMS - - @classmethod - def IFS(cls): - # interfaces - class _IFS(super().IFS()): - pass - - return _IFS - - def __init__(self): - # boilerplate - super().__init__() - self.IFs = self.IFS()(self) - self.PARAMs = self.PARAMS()(self) - self.NODEs = self.NODES()(self) - - # connections - - # traits + \"\"\" + Docstring describing your module + \"\"\" + + # ---------------------------------------- + # modules, interfaces, parameters + # ---------------------------------------- + + # ---------------------------------------- + # traits + # ---------------------------------------- + + def __preinit__(self): + # ------------------------------------ + # connections + # ------------------------------------ + + # ------------------------------------ + # parametrization + # ------------------------------------ + pass """) write(ctx, out) @@ -144,13 +134,21 @@ def trait(ctx: typer.Context, defined: bool = False): import logging from abc import abstractmethod - from faebryk.core.core import Trait + from faebryk.core.trait import Trait logger = logging.getLogger(__name__) class {traitname}(Trait): + \"\"\" + Docstring describing your module + \"\"\" + @abstractmethod - def DO_SOMETHING(self) -> None: ... + def DO_SOMETHING(self) -> None: + \"\"\" + Docstring describing the function + \"\"\" + pass """) write(ctx, out) diff --git a/src/faebryk/tools/main.py b/src/faebryk/tools/main.py index 4e70677a..d824741b 100644 --- a/src/faebryk/tools/main.py +++ b/src/faebryk/tools/main.py @@ -1,6 +1,7 @@ from faebryk.libs.tools.typer import typer_callback from faebryk.tools.libadd import main as libadd_main from faebryk.tools.project import main as project_main +from faebryk.tools.refactor import main as refactor_main @typer_callback(None) @@ -12,6 +13,7 @@ def main(): def __main__(): main.add_typer(libadd_main, name="libadd") main.add_typer(project_main, name="project") + main.add_typer(refactor_main, name="refactor") main() diff --git a/src/faebryk/tools/refactor.py b/src/faebryk/tools/refactor.py new file mode 100644 index 00000000..c27af925 --- /dev/null +++ b/src/faebryk/tools/refactor.py @@ -0,0 +1,222 @@ +# This file is part of the faebryk project +# SPDX-License-Identifier: MIT + +import re +import subprocess +from dataclasses import dataclass +from pathlib import Path +from textwrap import dedent, indent + +import typer + +from faebryk.libs.tools.typer import typer_callback + + +@dataclass +class CTX: ... + + +def get_ctx(ctx: typer.Context) -> "CTX": + return ctx.obj + + +@typer_callback(None) +def main(ctx: typer.Context): + """ + Can be called like this: > faebryk refactor + """ + pass + + +pyname = r"[_a-zA-Z][_a-zA-Z0-9]*" + + +@main.command() +def libtof(ctx: typer.Context, root: Path): + file_paths = list(root.rglob("**/*.py")) + print(f"Found {len(file_paths)} files in path.") + + detection_pattern = re.compile(r"^from faebryk.library.[^_]") + + refactor_files = [ + path + for path in file_paths + if not path.stem.startswith("_") + and any(detection_pattern.match(line) for line in path.read_text().splitlines()) + ] + + print(f"Found {len(refactor_files)} files to refactor.") + + # TO match: + # from faebryk.library.has_kicad_footprint import has_kicad_footprint + # from faebryk.library.has_simple_value_representation import ( + # has_simple_value_representation, + # ) + + import_pattern = re.compile( + r"^from faebryk.library.([^_][^ ]*) import (" + f"{pyname}$" + "|" + f"\\([\n ]*{pyname},[\n ]*\\)$" # multiline import + r")", + re.MULTILINE, + ) + + def refactor_file(path: Path): + text = path.read_text() + import_symbols = [m[0] for m in import_pattern.findall(text)] + text = import_pattern.subn("import faebryk.library._F as F", text, count=1)[0] + text = import_pattern.sub("", text) + + for sym in import_symbols: + if re.search(rf"from faebryk.library.{sym} import {sym} as", text): + print(f"Warning: skipping {sym} in {path}") + continue + + text = re.sub(rf"^\s*from faebryk.library.{sym} import {sym}$", "", text) + text = re.sub( + rf"^\s*from faebryk.library.{sym} import \(\n\s*{sym},\n\)$", "", text + ) + text = re.sub(rf"(? str: + # print("Block", "|||") + header, fields = m.groups() + fields: str = fields + "\n" + out = [] + for f in fields.split(")\n"): + if not f: + continue + f += ")" + # print(f) + # print(indent("|\nv", " " * 12)) + + if "times" in f: + f = re.sub(r"\n\s*", "", f) + f = f.replace(",)", ")") + + # case 3: screw_holes = times(3, lambda: F.Mounting_Hole()) + f = re.sub(r"times\((\d+),", r"L.list_field(\1,", f) + + # case 5: leds = times(\n self.pixels.value, \n ...,\n) + f = re.sub( + rf"\s*({pyname}) = (times\(.*\))", + r"@L.rt_field\ndef \1(self):\n return \2", + f, + ) + + # case 1: uart_in = F.UART_Base() + # case 4: if buffered:\n power_data = F.ElectricPower() + f = re.sub(rf" = ({maybe_f}{pyname})\(\)", r": \1", f) + + # case 2: fan_power_switch = F.PowerSwitchMOSFET(lowside=True, ...) + f = re.sub( + rf" = ({maybe_f}{pyname})\((.*)\)", r" = L.f_field(\1)(\2)", f + ) + + # print(f) + # print("--") + out.append(dedent(f)) + + outstr = indent("\n".join(out), " " * 4) + # print(outstr) + return outstr + + # Holders ------------------------------------- + text = holder_pattern.sub(process, text) + text = instance_holder.sub("", text) + text = holder_attr.sub(".", text) + + # Init ---------------------------------------- + # convert empty constructor to __preinit__ + text = re.sub( + r"def __init__\(self\).*?:(.*?)super\(\).__init__\(\)", + r"def __preinit__(self):\1pass", + text, + flags=re.DOTALL | re.MULTILINE, + ) + + # remove -> None from init + text = re.sub( + r"(def __init__\(self.*?\)).*?:", + r"\1:", + text, + ) + + def process_constructor(m: re.Match) -> str: + str_: str = m.group(0) + args = m.group(1) + out = str_ + prefix = re.match(r"^\s*", str_).group(0) + + # find names of args + arg_names = re.findall(rf",\s*({pyname})\s*(?:[:,]|$)", args) + for arg in arg_names: + out += indent(f"\n self._{arg} = {arg}", prefix) + + out += indent("\n\ndef __preinit__(self):\n pass", prefix) + return out + + # split args of constructor into init and pre_init + text = re.sub( + r"^[ ]*def __init__\(self(, .*?)\):.*?super\(\).__init__\(\)", + process_constructor, + text, + flags=re.DOTALL | re.MULTILINE, + ) + + # Done ---------------------------------------- + text = "\n".join(line.rstrip() for line in text.splitlines()) + text = text.strip() + "\n" + path.write_text(text) + + for path in refactor_files: + refactor_file(path) + + +if __name__ == "__main__": + main() diff --git a/test/core/test_core.py b/test/core/test_core.py index 622da9c5..c7bb67be 100644 --- a/test/core/test_core.py +++ b/test/core/test_core.py @@ -5,12 +5,20 @@ from abc import abstractmethod from typing import cast -from faebryk.core.core import LinkDirect, LinkParent, LinkSibling, TraitImpl +from faebryk.core.link import LinkDirect, LinkParent, LinkSibling +from faebryk.core.node import Node, NodeAlreadyBound +from faebryk.core.trait import ( + Trait, + TraitImpl, + TraitNotFound, + TraitUnbound, +) +from faebryk.libs.library import L class TestTraits(unittest.TestCase): def test_equality(self): - from faebryk.core.core import Trait + from faebryk.core.trait import Trait class _trait1(Trait): pass @@ -82,9 +90,7 @@ def assertCmpFalse(one, two): assertCmpTrue(impl_1_1(), impl1()) def test_obj_traits(self): - from faebryk.core.core import FaebrykLibObject, Trait - - obj = FaebrykLibObject() + obj = Node() class trait1(Trait): @abstractmethod @@ -111,7 +117,7 @@ class impl2(trait2.impl()): # Test failure on getting non existent self.assertFalse(obj.has_trait(trait1)) - self.assertRaises(AssertionError, lambda: obj.get_trait(trait1)) + self.assertRaises(TraitNotFound, lambda: obj.get_trait(trait1)) trait1_inst = trait1impl() cfgtrait1_inst = cfgtrait1(5) @@ -124,7 +130,7 @@ class impl2(trait2.impl()): self.assertEqual(trait1_inst.do(), obj.get_trait(trait1).do()) # Test double add - self.assertRaises(AssertionError, lambda: obj.add_trait(trait1_inst)) + self.assertRaises(NodeAlreadyBound, lambda: obj.add_trait(trait1_inst)) # Test replace obj.add_trait(cfgtrait1_inst) @@ -140,12 +146,12 @@ class impl2(trait2.impl()): self.assertFalse(obj.has_trait(trait1)) # Test get obj - self.assertRaises(AssertionError, lambda: trait1_inst.get_obj()) + self.assertRaises(TraitUnbound, lambda: trait1_inst.obj) obj.add_trait(trait1_inst) _impl: TraitImpl = cast(TraitImpl, obj.get_trait(trait1)) - self.assertEqual(_impl.get_obj(), obj) + self.assertEqual(_impl.obj, obj) obj.del_trait(trait1) - self.assertRaises(AssertionError, lambda: trait1_inst.get_obj()) + self.assertRaises(TraitUnbound, lambda: trait1_inst.obj) # Test specific override obj.add_trait(impl2_inst) @@ -159,7 +165,7 @@ class impl2(trait2.impl()): class TestGraph(unittest.TestCase): def test_gifs(self): - from faebryk.core.core import GraphInterface as GIF + from faebryk.core.graphinterface import GraphInterface as GIF gif1 = GIF() gif2 = GIF() @@ -184,17 +190,17 @@ class linkcls(LinkDirect): self.assertEqual(gif1.G, gif2.G) def test_node_gifs(self): - from faebryk.core.core import Node + from faebryk.core.node import Node n1 = Node() - self.assertIsInstance(n1.GIFs.self.is_connected(n1.GIFs.parent), LinkSibling) - self.assertIsInstance(n1.GIFs.self.is_connected(n1.GIFs.children), LinkSibling) + self.assertIsInstance(n1.self_gif.is_connected(n1.parent), LinkSibling) + self.assertIsInstance(n1.self_gif.is_connected(n1.children), LinkSibling) n2 = Node() - n1.NODEs.n2 = n2 + n1.add(n2, name="n2") - self.assertIsInstance(n1.GIFs.children.is_connected(n2.GIFs.parent), LinkParent) + self.assertIsInstance(n1.children.is_connected(n2.parent), LinkParent) print(n1.get_graph()) @@ -203,7 +209,44 @@ def test_node_gifs(self): assert p is not None self.assertIs(p[0], n1) - self.assertEqual(n1.GIFs.self.G, n2.GIFs.self.G) + self.assertEqual(n1.self_gif.G, n2.self_gif.G) + + # TODO move to own file + def test_fab_ll_simple_hierarchy(self): + class N(Node): + SN1: Node + SN2: Node + SN3 = L.list_field(2, Node) + + @L.rt_field + def SN4(self): + return Node() + + n = N() + children = n.get_children(direct_only=True, types=Node) + self.assertEqual(children, {n.SN1, n.SN2, n.SN3[0], n.SN3[1], n.SN4}) + + def test_fab_ll_chain_names(self): + root = Node() + x = root + for i in range(10): + y = Node() + x.add(y, f"i{i}") + x = y + + self.assertEqual(x.get_full_name(), "*.i0.i1.i2.i3.i4.i5.i6.i7.i8.i9") + + def test_fab_ll_chain_tree(self): + root = Node() + x = root + for i in range(10): + y = Node() + z = Node() + x.add(y, f"i{i}") + x.add(z, f"j{i}") + x = y + + self.assertEqual(x.get_full_name(), "*.i0.i1.i2.i3.i4.i5.i6.i7.i8.i9") if __name__ == "__main__": diff --git a/test/core/test_hierarchy_connect.py b/test/core/test_hierarchy_connect.py index d03d220a..3df37346 100644 --- a/test/core/test_hierarchy_connect.py +++ b/test/core/test_hierarchy_connect.py @@ -5,21 +5,13 @@ import unittest from itertools import chain -from faebryk.core.core import ( - LinkDirect, - LinkDirectShallow, - Module, - ModuleInterface, - _TLinkDirectShallow, -) +import faebryk.library._F as F from faebryk.core.core import logger as core_logger +from faebryk.core.link import LinkDirect, LinkDirectShallow, _TLinkDirectShallow +from faebryk.core.module import Module +from faebryk.core.moduleinterface import ModuleInterface from faebryk.core.util import specialize_interface -from faebryk.library.Electrical import Electrical -from faebryk.library.ElectricLogic import ElectricLogic -from faebryk.library.has_single_electric_reference_defined import ( - has_single_electric_reference_defined, -) -from faebryk.library.UART_Base import UART_Base +from faebryk.libs.library import L from faebryk.libs.util import print_stack, times logger = logging.getLogger(__name__) @@ -29,27 +21,22 @@ class TestHierarchy(unittest.TestCase): def test_up_connect(self): class UARTBuffer(Module): - def __init__(self) -> None: - super().__init__() + bus_in: F.UART_Base + bus_out: F.UART_Base - class _IFs(super().IFS()): - bus_in = UART_Base() - bus_out = UART_Base() + def __preinit__(self) -> None: + bus_in = self.bus_in + bus_out = self.bus_out - self.IFs = _IFs(self) - - bus_in = self.IFs.bus_in - bus_out = self.IFs.bus_out - - bus_in.IFs.rx.IFs.signal.connect(bus_out.IFs.rx.IFs.signal) - bus_in.IFs.tx.IFs.signal.connect(bus_out.IFs.tx.IFs.signal) - bus_in.IFs.rx.IFs.reference.connect(bus_out.IFs.rx.IFs.reference) + bus_in.rx.signal.connect(bus_out.rx.signal) + bus_in.tx.signal.connect(bus_out.tx.signal) + bus_in.rx.reference.connect(bus_out.rx.reference) app = UARTBuffer() - self.assertTrue(app.IFs.bus_in.IFs.rx.is_connected_to(app.IFs.bus_out.IFs.rx)) - self.assertTrue(app.IFs.bus_in.IFs.tx.is_connected_to(app.IFs.bus_out.IFs.tx)) - self.assertTrue(app.IFs.bus_in.is_connected_to(app.IFs.bus_out)) + self.assertTrue(app.bus_in.rx.is_connected_to(app.bus_out.rx)) + self.assertTrue(app.bus_in.tx.is_connected_to(app.bus_out.tx)) + self.assertTrue(app.bus_in.is_connected_to(app.bus_out)) def test_chains(self): mifs = times(3, ModuleInterface) @@ -70,75 +57,81 @@ def test_chains(self): self.assertIsInstance(mifs[0].is_connected_to(mifs[2]), _TLinkDirectShallow) # Test hierarchy down filter & chain resolution - mifs = times(3, ElectricLogic) + mifs = times(3, F.ElectricLogic) mifs[0].connect_shallow(mifs[1]) mifs[1].connect(mifs[2]) self.assertTrue(mifs[0].is_connected_to(mifs[2])) self.assertIsInstance(mifs[0].is_connected_to(mifs[2]), _TLinkDirectShallow) - self.assertTrue(mifs[1].IFs.signal.is_connected_to(mifs[2].IFs.signal)) - self.assertTrue(mifs[1].IFs.reference.is_connected_to(mifs[2].IFs.reference)) - self.assertFalse(mifs[0].IFs.signal.is_connected_to(mifs[1].IFs.signal)) - self.assertFalse(mifs[0].IFs.reference.is_connected_to(mifs[1].IFs.reference)) - self.assertFalse(mifs[0].IFs.signal.is_connected_to(mifs[2].IFs.signal)) - self.assertFalse(mifs[0].IFs.reference.is_connected_to(mifs[2].IFs.reference)) + self.assertTrue(mifs[1].signal.is_connected_to(mifs[2].signal)) + self.assertTrue(mifs[1].reference.is_connected_to(mifs[2].reference)) + self.assertFalse(mifs[0].signal.is_connected_to(mifs[1].signal)) + self.assertFalse(mifs[0].reference.is_connected_to(mifs[1].reference)) + self.assertFalse(mifs[0].signal.is_connected_to(mifs[2].signal)) + self.assertFalse(mifs[0].reference.is_connected_to(mifs[2].reference)) # Test duplicate resolution - mifs[0].IFs.signal.connect(mifs[1].IFs.signal) - mifs[0].IFs.reference.connect(mifs[1].IFs.reference) + mifs[0].signal.connect(mifs[1].signal) + mifs[0].reference.connect(mifs[1].reference) self.assertIsInstance(mifs[0].is_connected_to(mifs[1]), LinkDirect) self.assertIsInstance(mifs[0].is_connected_to(mifs[2]), LinkDirect) def test_bridge(self): - class Buffer(Module): - def __init__(self) -> None: - super().__init__() + self_ = self - class _IFs(super().IFS()): - ins = times(2, Electrical) - outs = times(2, Electrical) + # U1 ---> _________B________ ---> U2 + # TX IL ===> OL TX + # S --> I -> S S -> O --> S + # R -------- R ----- R -------- R - ins_l = times(2, ElectricLogic) - outs_l = times(2, ElectricLogic) + class Buffer(Module): + ins = L.list_field(2, F.Electrical) + outs = L.list_field(2, F.Electrical) - self.IFs = _IFs(self) + ins_l = L.list_field(2, F.ElectricLogic) + outs_l = L.list_field(2, F.ElectricLogic) - ref = ElectricLogic.connect_all_module_references(self) - self.add_trait(has_single_electric_reference_defined(ref)) + def __preinit__(self) -> None: + self_.assertIs( + self.ins_l[0].reference, + self.ins_l[0].single_electric_reference.get_reference(), + ) for el, lo in chain( - zip(self.IFs.ins, self.IFs.ins_l), - zip(self.IFs.outs, self.IFs.outs_l), + zip(self.ins, self.ins_l), + zip(self.outs, self.outs_l), ): - lo.IFs.signal.connect(el) + lo.signal.connect(el) - for l1, l2 in zip(self.IFs.ins_l, self.IFs.outs_l): + for l1, l2 in zip(self.ins_l, self.outs_l): l1.connect_shallow(l2) - class UARTBuffer(Module): - def __init__(self) -> None: - super().__init__() - - class _NODES(super().NODES()): - buf = Buffer() + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) + ) - class _IFs(super().IFS()): - bus_in = UART_Base() - bus_out = UART_Base() - - self.IFs = _IFs(self) - self.NODEs = _NODES(self) - - ElectricLogic.connect_all_module_references(self) - - bus1 = self.IFs.bus_in - bus2 = self.IFs.bus_out - buf = self.NODEs.buf - - bus1.IFs.tx.IFs.signal.connect(buf.IFs.ins[0]) - bus1.IFs.rx.IFs.signal.connect(buf.IFs.ins[1]) - bus2.IFs.tx.IFs.signal.connect(buf.IFs.outs[0]) - bus2.IFs.rx.IFs.signal.connect(buf.IFs.outs[1]) + class UARTBuffer(Module): + buf: Buffer + bus_in: F.UART_Base + bus_out: F.UART_Base + + def __preinit__(self) -> None: + bus1 = self.bus_in + bus2 = self.bus_out + buf = self.buf + + bus1.tx.signal.connect(buf.ins[0]) + bus1.rx.signal.connect(buf.ins[1]) + bus2.tx.signal.connect(buf.outs[0]) + bus2.rx.signal.connect(buf.outs[1]) + + @L.rt_field + def single_electric_reference(self): + return F.has_single_electric_reference_defined( + F.ElectricLogic.connect_all_module_references(self) + ) import faebryk.core.core as c @@ -153,20 +146,49 @@ def _assert_no_link(mif1, mif2): err = "\n" + print_stack(link.tb) self.assertFalse(link, err) - bus1 = app.IFs.bus_in - bus2 = app.IFs.bus_out - buf = app.NODEs.buf + def _assert_link(mif1: ModuleInterface, mif2: ModuleInterface, link=None): + out = mif1.is_connected_to(mif2) + if link: + self.assertIsInstance(out, link) + return + self.assertIsNotNone(out) + + bus1 = app.bus_in + bus2 = app.bus_out + buf = app.buf # Check that the two buffer sides are not connected electrically - _assert_no_link(buf.IFs.ins[0], buf.IFs.outs[0]) - _assert_no_link(buf.IFs.ins[1], buf.IFs.outs[1]) - _assert_no_link(bus1.IFs.rx.IFs.signal, bus2.IFs.rx.IFs.signal) - _assert_no_link(bus1.IFs.tx.IFs.signal, bus2.IFs.tx.IFs.signal) + _assert_no_link(buf.ins[0], buf.outs[0]) + _assert_no_link(buf.ins[1], buf.outs[1]) + _assert_no_link(bus1.rx.signal, bus2.rx.signal) + _assert_no_link(bus1.tx.signal, bus2.tx.signal) + + # direct connect + _assert_link(bus1.tx.signal, buf.ins[0]) + _assert_link(bus1.rx.signal, buf.ins[1]) + _assert_link(bus2.tx.signal, buf.outs[0]) + _assert_link(bus2.rx.signal, buf.outs[1]) + + # connect through trait + self.assertIs( + buf.ins_l[0].single_electric_reference.get_reference(), + buf.ins_l[0].reference, + ) + _assert_link(buf.ins_l[0].reference, buf.outs_l[0].reference) + _assert_link(buf.outs_l[1].reference, buf.ins_l[0].reference) + _assert_link(bus1.rx.reference, bus2.rx.reference, LinkDirect) + + # connect through up + _assert_link(bus1.tx, buf.ins_l[0], LinkDirect) + _assert_link(bus2.tx, buf.outs_l[0], LinkDirect) + + # connect shallow + _assert_link(buf.ins_l[0], buf.outs_l[0], _TLinkDirectShallow) # Check that the two buffer sides are connected logically - self.assertTrue(bus1.IFs.rx.is_connected_to(bus2.IFs.rx)) - self.assertTrue(bus1.IFs.tx.is_connected_to(bus2.IFs.tx)) - self.assertTrue(bus1.is_connected_to(bus2)) + _assert_link(bus1.tx, bus2.tx) + _assert_link(bus1.rx, bus2.rx) + _assert_link(bus1, bus2) def test_specialize(self): class Specialized(ModuleInterface): ... @@ -209,6 +231,57 @@ class _Link(LinkDirectShallow(lambda link, gif: True)): ... self.assertIsInstance(mifs_special[0].is_connected_to(mifs_special[2]), _Link) + def test_isolated_connect(self): + x1 = F.ElectricLogic() + x2 = F.ElectricLogic() + x1.connect(x2, linkcls=F.ElectricLogic.LinkIsolatedReference) + self.assertIsInstance( + x1.is_connected_to(x2), F.ElectricLogic.LinkIsolatedReference + ) + + self.assertIsInstance( + x1.signal.is_connected_to(x2.signal), + F.ElectricLogic.LinkIsolatedReference, + ) + + self.assertIsNone(x1.reference.is_connected_to(x2.reference)) + + self.assertIsNone(x1.reference.hv.is_connected_to(x2.reference.hv)) + + y1 = F.ElectricPower() + y2 = F.ElectricPower() + + y1.make_source() + y2.make_source() + + with self.assertRaises(F.Power.PowerSourcesShortedError): + y1.connect(y2) + + ldo1 = F.LDO() + ldo2 = F.LDO() + + with self.assertRaises(F.Power.PowerSourcesShortedError): + ldo1.power_out.connect(ldo2.power_out) + + a1 = F.I2C() + b1 = F.I2C() + + a1.connect(b1, linkcls=F.ElectricLogic.LinkIsolatedReference) + self.assertIsInstance( + a1.is_connected_to(b1), F.ElectricLogic.LinkIsolatedReference + ) + self.assertIsInstance( + a1.scl.signal.is_connected_to(b1.scl.signal), + F.ElectricLogic.LinkIsolatedReference, + ) + self.assertIsInstance( + a1.sda.signal.is_connected_to(b1.sda.signal), + F.ElectricLogic.LinkIsolatedReference, + ) + + self.assertIsNone(a1.scl.reference.is_connected_to(b1.scl.reference)) + self.assertIsNone(a1.sda.reference.is_connected_to(b1.sda.reference)) + if __name__ == "__main__": unittest.main() diff --git a/test/core/test_parameters.py b/test/core/test_parameters.py index 78d64490..f7bf74bf 100644 --- a/test/core/test_parameters.py +++ b/test/core/test_parameters.py @@ -4,10 +4,10 @@ import logging import unittest from operator import add -from typing import TypeVar -from faebryk.core.core import Module, Parameter from faebryk.core.core import logger as core_logger +from faebryk.core.module import Module +from faebryk.core.parameter import Parameter from faebryk.core.util import specialize_module from faebryk.library.ANY import ANY from faebryk.library.Constant import Constant @@ -134,9 +134,7 @@ def assertIsInstance[T: Parameter](obj: Parameter, cls: type[T]) -> T: self.assertEqual(Set([1, 2]) * P.baud, Set([1 * P.baud, 2 * P.baud])) def test_resolution(self): - T = TypeVar("T") - - def assertIsInstance(obj, cls: type[T]) -> T: + def assertIsInstance[T](obj, cls: type[T]) -> T: self.assertIsInstance(obj, cls) assert isinstance(obj, cls) return obj @@ -251,46 +249,38 @@ def test_comp( test_comp(Constant(Set([Range(Range(1))])), 1) def test_modules(self): - T = TypeVar("T") - - def assertIsInstance(obj, cls: type[T]) -> T: + def assertIsInstance[T](obj, cls: type[T]) -> T: self.assertIsInstance(obj, cls) assert isinstance(obj, cls) return obj class Modules(Module): - def __init__(self) -> None: - super().__init__() - - class NODES(super().NODES()): - UART_A = UART_Base() - UART_B = UART_Base() - UART_C = UART_Base() - - self.NODEs = NODES(self) + UART_A: UART_Base + UART_B: UART_Base + UART_C: UART_Base m = Modules() - UART_A = m.NODEs.UART_A - UART_B = m.NODEs.UART_B - UART_C = m.NODEs.UART_C + UART_A = m.UART_A + UART_B = m.UART_B + UART_C = m.UART_C UART_A.connect(UART_B) - UART_A.PARAMs.baud.merge(Constant(9600 * P.baud)) + UART_A.baud.merge(Constant(9600 * P.baud)) for uart in [UART_A, UART_B]: self.assertEqual( - assertIsInstance(uart.PARAMs.baud.get_most_narrow(), Constant).value, + assertIsInstance(uart.baud.get_most_narrow(), Constant).value, 9600 * P.baud, ) - UART_C.PARAMs.baud.merge(Range(1200 * P.baud, 115200 * P.baud)) + UART_C.baud.merge(Range(1200 * P.baud, 115200 * P.baud)) UART_A.connect(UART_C) for uart in [UART_A, UART_B, UART_C]: self.assertEqual( - assertIsInstance(uart.PARAMs.baud.get_most_narrow(), Constant).value, + assertIsInstance(uart.baud.get_most_narrow(), Constant).value, 9600 * P.baud, ) @@ -334,43 +324,38 @@ def test_specialize(self): for i in range(10): class App(Module): - def __init__(self) -> None: - super().__init__() - - class _NODES(Module.NODES()): - led = F.PoweredLED() - battery = F.Battery() - - self.NODEs = _NODES(self) + led: F.PoweredLED + battery: F.Battery - self.NODEs.led.IFs.power.connect(self.NODEs.battery.IFs.power) + def __preinit__(self) -> None: + self.led.power.connect(self.battery.power) # Parametrize - self.NODEs.led.NODEs.led.PARAMs.color.merge(F.LED.Color.YELLOW) - self.NODEs.led.NODEs.led.PARAMs.brightness.merge( + self.led.led.color.merge(F.LED.Color.YELLOW) + self.led.led.brightness.merge( TypicalLuminousIntensity.APPLICATION_LED_INDICATOR_INSIDE.value.value ) app = App() - bcell = specialize_module(app.NODEs.battery, F.ButtonCell()) - bcell.PARAMs.voltage.merge(3 * P.V) - bcell.PARAMs.capacity.merge(Range.from_center(225 * P.mAh, 50 * P.mAh)) - bcell.PARAMs.material.merge(F.ButtonCell.Material.Lithium) - bcell.PARAMs.size.merge(F.ButtonCell.Size.N_2032) - bcell.PARAMs.shape.merge(F.ButtonCell.Shape.Round) + bcell = specialize_module(app.battery, F.ButtonCell()) + bcell.voltage.merge(3 * P.V) + bcell.capacity.merge(Range.from_center(225 * P.mAh, 50 * P.mAh)) + bcell.material.merge(F.ButtonCell.Material.Lithium) + bcell.size.merge(F.ButtonCell.Size.N_2032) + bcell.shape.merge(F.ButtonCell.Shape.Round) - app.NODEs.led.NODEs.led.PARAMs.color.merge(F.LED.Color.YELLOW) - app.NODEs.led.NODEs.led.PARAMs.max_brightness.merge(500 * P.millicandela) - app.NODEs.led.NODEs.led.PARAMs.forward_voltage.merge(1.2 * P.V) - app.NODEs.led.NODEs.led.PARAMs.max_current.merge(20 * P.mA) + app.led.led.color.merge(F.LED.Color.YELLOW) + app.led.led.max_brightness.merge(500 * P.millicandela) + app.led.led.forward_voltage.merge(1.2 * P.V) + app.led.led.max_current.merge(20 * P.mA) - v = app.NODEs.battery.PARAMs.voltage - # vbcell = bcell.PARAMs.voltage + v = app.battery.voltage + # vbcell = bcell.voltage # print(pretty_param_tree_top(v)) # print(pretty_param_tree_top(vbcell)) self.assertEqual(v.get_most_narrow(), 3 * P.V) - r = app.NODEs.led.NODEs.current_limiting_resistor.PARAMs.resistance + r = app.led.current_limiting_resistor.resistance r = r.get_most_narrow() self.assertIsInstance(r, Range, f"{type(r)}") diff --git a/test/core/test_performance.py b/test/core/test_performance.py index af32fe29..e4883167 100644 --- a/test/core/test_performance.py +++ b/test/core/test_performance.py @@ -8,8 +8,11 @@ from typing import Callable import faebryk.core.util as core_util -from faebryk.core.core import GraphInterface, Module, ModuleInterface -from faebryk.library.Resistor import Resistor +import faebryk.library._F as F +from faebryk.core.graphinterface import GraphInterface +from faebryk.core.module import Module +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.libs.library import L from faebryk.libs.util import times @@ -44,36 +47,32 @@ class TestPerformance(unittest.TestCase): def test_get_all(self): def _factory_simple_resistors(count: int): class App(Module): + resistors = L.list_field(count, F.Resistor) + def __init__(self, timings: Times) -> None: super().__init__() + self._timings = timings - class NODES(super().NODES()): - resistors = times(count, Resistor) - - timings.add("NODES") - - self.NODEs = NODES(self) - timings.add("set NODES") + def __preinit__(self): + self._timings.add("setup") return App def _factory_interconnected_resistors(count: int): class App(Module): + resistors = L.list_field(count, F.Resistor) + def __init__(self, timings: Times) -> None: super().__init__() + self._timings = timings - class NODES(super().NODES()): - resistors = times(count, Resistor) - - timings.add("NODES") - - self.NODEs = NODES(self) - timings.add("set NODES") + def __preinit__(self): + self._timings.add("setup") core_util.connect_all_interfaces( - r.IFs.unnamed[0] for r in self.NODEs.resistors + r.unnamed[0] for r in self.resistors ) - timings.add("connect") + self._timings.add("connect") return App @@ -82,11 +81,11 @@ def _common_timings( ): timings = Times() - App = factory() + AppF = factory() timings.add("classdef") now = time.time() - app = App(timings) + app = AppF(timings) timings.times["instance"] = time.time() - now G = app.get_graph() @@ -95,10 +94,10 @@ def _common_timings( core_util.node_projected_graph(G) timings.add("get_all_nodes_graph") - for n in [app, app.NODEs.resistors[0]]: + for n in [app, app.resistors[0]]: name = type(n).__name__[0] - core_util.get_node_children_all(n) + n.get_node_children_all() timings.add(f"get_node_children_all {name}") core_util.get_node_tree(n) @@ -110,7 +109,7 @@ def _common_timings( core_util.get_node_direct_mods_or_mifs(n) timings.add(f"get_module_direct_children {name}") - core_util.get_children(n, direct_only=True, types=ModuleInterface) + n.get_children(direct_only=True, types=ModuleInterface) timings.add(f"get_mifs {name}") print(f"{test_name:-<80}") @@ -177,7 +176,7 @@ def rec_connect(gs_sub: list[GraphInterface]): # self.assertLess(timings.times["connect"], 1200e-3) print(timings) print(f"----> Avg/connect: {per_connect*1e6:.2f} us") - from faebryk.core.core import GraphImpl + from faebryk.core.graphinterface import GraphImpl print("Counter", GraphImpl.counter, GraphImpl.counter - count) @@ -203,7 +202,7 @@ def test_graph_merge_it(self): print(timings) print(f"----> Avg/connect: {per_connect*1e6:.2f} us") - from faebryk.core.core import GraphImpl + from faebryk.core.graphinterface import GraphImpl print("Counter", GraphImpl.counter, GraphImpl.counter - count) diff --git a/test/core/test_util.py b/test/core/test_util.py index b8d419fc..7b10f1fe 100644 --- a/test/core/test_util.py +++ b/test/core/test_util.py @@ -2,31 +2,29 @@ # SPDX-License-Identifier: MIT import unittest -from enum import StrEnum -from typing import Iterable -from faebryk.core.core import Module, ModuleInterface, Node, Parameter -from faebryk.core.util import get_children, get_node_tree, iter_tree_by_depth +from faebryk.core.module import Module +from faebryk.core.moduleinterface import ModuleInterface +from faebryk.core.node import Node +from faebryk.core.util import get_node_tree, iter_tree_by_depth +from faebryk.libs.library import L class TestUtil(unittest.TestCase): def test_trees(self): class N(Module): - def __init__(self, depth: int): - super().__init__() - - class _IFs(Module.IFS()): - mif = ModuleInterface() - - self.IFs = _IFs(self) + mif: ModuleInterface - if depth == 0: - return + @L.rt_field + def n(self): + if self._depth == 0: + # TODO does this work? + return [] + return N(self._depth - 1) - class _NODES(Module.NODES()): - n = N(depth - 1) - - self.NODEs = _NODES(self) + def __init__(self, depth: int): + super().__init__() + self._depth = depth level_count = 5 n = N(level_count) @@ -45,140 +43,6 @@ def assertEqual(n1: list[Node], n2: list[Node]): assertEqual(levels[0], [n]) n_i = n for i in range(1, level_count + 1): - assertEqual(levels[i], [n_i.NODEs.n, n_i.IFs.mif]) - n_i = n_i.NODEs.n - assertEqual(levels[level_count + 1], [n_i.IFs.mif]) - - def test_children(self): - # TODO this is a really annoying to debug test - - class E(StrEnum): - I_ = "IF" - N = "NODE" - P = "PARAM" - - EI = E.I_ - EN = E.N - EP = E.P - - def moduleFromTree( - t: dict[E, dict | type] | type, root_type: E - ) -> type[Module]: - root_type_t = { - EN: Module, - EI: ModuleInterface, - EP: Parameter, - }[root_type] - - class _(root_type_t): - inner_tree = t - - def __init__(self): - super().__init__() - - if isinstance(t, dict): - for k, vs in t.items(): - assert isinstance(vs, list) - name = f"{k}s" - for i, v in enumerate(vs): - setattr( - getattr(self, name), f"i{i}", moduleFromTree(v, k)() - ) - - return _ - - def assertEqual(n1: Iterable[Node], n2: list): - t = list(sorted([n.inner_tree for n in n1], key=id)) - t2 = list(sorted(n2, key=id)) - from pprint import pformat - - if t != t2: - print("Compare", "-" * 40) - for x in t: - print(f"{id(x)} \n{pformat(x, indent=4)}") - print("=" * 20) - for x in t2: - print(f"{id(x)} \n{pformat(x, indent=4)}") - print("~" * 40) - for x1, x2 in zip(t, t2): - print( - f"{x1==x2:>5}| " - f"{id(x1):<20} {pformat(x1, indent=4).splitlines()[0]:<80}" - " -- " - f"{id(x2):<20} {pformat(x2, indent=4).splitlines()[0]}" - ) - self.assertEqual(t, t2) - - tree = { - EI: [ - ModuleInterface, - { - EN: [Module, Module], - EI: [ModuleInterface], - }, - ], - EN: [ - { - EN: [Module, Module], - EI: [ModuleInterface], - EP: [ - { - EP: [Parameter], - } - ], - }, - { - EN: [Module, Module], - EI: [ModuleInterface], - }, - { - EN: [Module, Module], - EI: [ModuleInterface], - }, - ], - EP: [], - } - - def visit_tree(t, keys=None, typ=EN): - if not keys or typ in keys: - yield t - - if isinstance(t, dict): - for k, vs in t.items(): - for v in vs: - yield from visit_tree(v, keys=keys, typ=k) - else: - assert isinstance(t, type) - - mod = moduleFromTree(tree, EN)() - - direct_children_top = get_children(mod, direct_only=True, types=Module) - assertEqual(direct_children_top, tree[EN]) - - direct_children_top_all_types = get_children(mod, direct_only=True) - assertEqual(direct_children_top_all_types, tree[EN] + tree[EI] + tree[EP]) - - all_children_top = get_children(mod, direct_only=False, include_root=True) - assertEqual(all_children_top, list(visit_tree(tree))) - - all_children_top_typed = get_children( - mod, direct_only=False, types=Module, include_root=True - ) - assertEqual(all_children_top_typed, list(visit_tree(tree, [EN]))) - - direct_children_middle = get_children(mod.NODEs.i0, direct_only=True) - assertEqual( - direct_children_middle, tree[EN][0][EN] + tree[EN][0][EI] + tree[EN][0][EP] - ) - - all_children_middle = get_children( - mod.NODEs.i0, direct_only=False, include_root=True - ) - assertEqual( - all_children_middle, - list(visit_tree(tree[EN][0])), - ) - - -if __name__ == "__main__": - unittest.main() + assertEqual(levels[i], [n_i.n, n_i.mif]) + n_i = n_i.n + assertEqual(levels[level_count + 1], [n_i.mif]) diff --git a/test/exporters/netlist/kicad/test_netlist_kicad.py b/test/exporters/netlist/kicad/test_netlist_kicad.py index 61811df5..2118a5fd 100644 --- a/test/exporters/netlist/kicad/test_netlist_kicad.py +++ b/test/exporters/netlist/kicad/test_netlist_kicad.py @@ -4,49 +4,45 @@ import logging import unittest +import faebryk.library._F as F from faebryk.exporters.netlist.graph import attach_nets_and_kicad_info from faebryk.exporters.netlist.kicad.netlist_kicad import from_faebryk_t2_netlist from faebryk.exporters.netlist.netlist import make_t2_netlist_from_graph -from faebryk.library.can_attach_to_footprint import can_attach_to_footprint -from faebryk.library.ElectricPower import ElectricPower -from faebryk.library.has_designator_defined import has_designator_defined -from faebryk.library.has_designator_prefix import has_designator_prefix -from faebryk.library.Net import Net from faebryk.libs.app.designators import ( attach_random_designators, override_names_with_designators, ) +from faebryk.libs.units import P logger = logging.getLogger(__name__) # Netlists -------------------------------------------------------------------- def _test_netlist_graph(): - from faebryk.library.Resistor import Resistor - from faebryk.library.SMDTwoPin import SMDTwoPin - - resistor1 = Resistor().builder(lambda r: r.PARAMs.resistance.merge(100)) - resistor2 = Resistor().builder(lambda r: r.PARAMs.resistance.merge(200)) - power = ElectricPower() + resistor1 = F.Resistor().builder(lambda r: r.resistance.merge(100 * P.ohm)) + resistor2 = F.Resistor().builder(lambda r: r.resistance.merge(200 * P.ohm)) + power = F.ElectricPower() # net labels - vcc = Net.with_name("+3V3") - gnd = Net.with_name("GND") - power.IFs.hv.connect(vcc.IFs.part_of) - power.IFs.lv.connect(gnd.IFs.part_of) + vcc = F.Net.with_name("+3V3") + gnd = F.Net.with_name("GND") + power.hv.connect(vcc.part_of) + power.lv.connect(gnd.part_of) # connect - resistor1.IFs.unnamed[0].connect(power.IFs.hv) - resistor1.IFs.unnamed[1].connect(power.IFs.lv) - resistor2.IFs.unnamed[0].connect(resistor1.IFs.unnamed[0]) - resistor2.IFs.unnamed[1].connect(resistor1.IFs.unnamed[1]) + resistor1.unnamed[0].connect(power.hv) + resistor1.unnamed[1].connect(power.lv) + resistor2.unnamed[0].connect(resistor1.unnamed[0]) + resistor2.unnamed[1].connect(resistor1.unnamed[1]) # attach footprint & designator for i, r in enumerate([resistor1, resistor2]): - r.get_trait(can_attach_to_footprint).attach(SMDTwoPin(SMDTwoPin.Type._0805)) + r.get_trait(F.can_attach_to_footprint).attach( + F.SMDTwoPin(F.SMDTwoPin.Type._0805) + ) r.add_trait( - has_designator_defined( - resistor1.get_trait(has_designator_prefix).get_prefix() + str(i + 1) + F.has_designator_defined( + resistor1.get_trait(F.has_designator_prefix).get_prefix() + str(i + 1) ) ) @@ -67,7 +63,7 @@ def _test_netlist_graph(): if not success: logger.error("Graph != T2") logger.error("T2: %s", kicad_netlist_t2) - logger.error("Graph: %s", netlist) + logger.error("Gr: %s", netlist) return success, netlist diff --git a/test/library/test_basic.py b/test/library/test_basic.py new file mode 100644 index 00000000..56516a57 --- /dev/null +++ b/test/library/test_basic.py @@ -0,0 +1,48 @@ +# This file is part of the faebryk project +# SPDX-License-Identifier: MIT + +import unittest + +from faebryk.core.core import Namespace +from faebryk.core.node import Node + + +class TestBasicLibrary(unittest.TestCase): + def test_load_library(self): + import faebryk.library._F # noqa: F401 + + def test_symbol_types(self): + import faebryk.library._F as F + + symbols = { + k: v + for k, v in vars(F).items() + if not k.startswith("_") + and (not isinstance(v, type) or not issubclass(v, (Node, Namespace))) + and not type(v).__name__ == "_once" + } + self.assertFalse(symbols, f"Found unexpected symbols: {symbols}") + + def test_imports(self): + import faebryk.library._F as F + + # get all symbols in F + symbols = { + k: v + for k, v in vars(F).items() + if not k.startswith("_") + and isinstance(v, type) + and issubclass(v, Node) + # check if constructor has no args + and v.__init__.__code__.co_argcount == 1 + } + + for k, v in symbols.items(): + try: + v() + except Exception as e: + self.fail(f"Failed to instantiate {k}: {e}") + + +if __name__ == "__main__": + unittest.main() diff --git a/test/libs/picker/test_jlcpcb.py b/test/libs/picker/test_jlcpcb.py index a30e6c41..44b77bf9 100644 --- a/test/libs/picker/test_jlcpcb.py +++ b/test/libs/picker/test_jlcpcb.py @@ -7,7 +7,8 @@ import faebryk.library._F as F import faebryk.libs.picker.lcsc as lcsc -from faebryk.core.core import Module +from faebryk.core.module import Module +from faebryk.core.parameter import Parameter from faebryk.libs.logging import setup_basic_logging from faebryk.libs.picker.jlcpcb.jlcpcb import JLCPCB_DB from faebryk.libs.picker.jlcpcb.pickers import add_jlcpcb_pickers @@ -92,7 +93,8 @@ def satisfies_requirements(self): ) for req, res in zip( - self.requirement.PARAMs.get_all(), self.result.PARAMs.get_all() + self.requirement.get_children(direct_only=True, types=Parameter), + self.result.get_children(direct_only=True, types=Parameter), ): req = req.get_most_narrow() res = res.get_most_narrow() @@ -157,21 +159,19 @@ def test(self): def test_find_manufacturer_partnumber(self): requirement = F.OpAmp().builder( lambda r: ( - r.PARAMs.bandwidth.merge(F.Range.upper_bound(1 * P.Mhertz)), - r.PARAMs.common_mode_rejection_ratio.merge( + r.bandwidth.merge(F.Range.upper_bound(1 * P.Mhertz)), + r.common_mode_rejection_ratio.merge( F.Range.lower_bound(Quantity(50, P.dB)) ), - r.PARAMs.input_bias_current.merge(F.Range.upper_bound(1 * P.nA)), - r.PARAMs.input_offset_voltage.merge(F.Range.upper_bound(1 * P.mV)), - r.PARAMs.gain_bandwidth_product.merge( - F.Range.upper_bound(1 * P.Mhertz) - ), - r.PARAMs.output_current.merge(F.Range.upper_bound(1 * P.mA)), - r.PARAMs.slew_rate.merge(F.Range.upper_bound(1 * P.MV / P.us)), + r.input_bias_current.merge(F.Range.upper_bound(1 * P.nA)), + r.input_offset_voltage.merge(F.Range.upper_bound(1 * P.mV)), + r.gain_bandwidth_product.merge(F.Range.upper_bound(1 * P.Mhertz)), + r.output_current.merge(F.Range.upper_bound(1 * P.mA)), + r.slew_rate.merge(F.Range.upper_bound(1 * P.MV / P.us)), ) ) requirement.add_trait( - F.has_defined_descriptive_properties( + F.has_descriptive_properties_defined( { DescriptiveProperties.partno: "LMV321IDBVR", DescriptiveProperties.manufacturer: "Texas Instruments", @@ -187,21 +187,19 @@ def test_find_manufacturer_partnumber(self): def test_find_lcsc_partnumber(self): requirement = F.OpAmp().builder( lambda r: ( - r.PARAMs.bandwidth.merge(F.Range.upper_bound(1 * P.Mhertz)), - r.PARAMs.common_mode_rejection_ratio.merge( + r.bandwidth.merge(F.Range.upper_bound(1 * P.Mhertz)), + r.common_mode_rejection_ratio.merge( F.Range.lower_bound(Quantity(50, P.dB)) ), - r.PARAMs.input_bias_current.merge(F.Range.upper_bound(1 * P.nA)), - r.PARAMs.input_offset_voltage.merge(F.Range.upper_bound(1 * P.mV)), - r.PARAMs.gain_bandwidth_product.merge( - F.Range.upper_bound(1 * P.Mhertz) - ), - r.PARAMs.output_current.merge(F.Range.upper_bound(1 * P.mA)), - r.PARAMs.slew_rate.merge(F.Range.upper_bound(1 * P.MV / P.us)), + r.input_bias_current.merge(F.Range.upper_bound(1 * P.nA)), + r.input_offset_voltage.merge(F.Range.upper_bound(1 * P.mV)), + r.gain_bandwidth_product.merge(F.Range.upper_bound(1 * P.Mhertz)), + r.output_current.merge(F.Range.upper_bound(1 * P.mA)), + r.slew_rate.merge(F.Range.upper_bound(1 * P.MV / P.us)), ) ) requirement.add_trait( - F.has_defined_descriptive_properties( + F.has_descriptive_properties_defined( { "LCSC": "C7972", } @@ -218,11 +216,9 @@ def test_find_resistor(self): self, requirement=F.Resistor().builder( lambda r: ( - r.PARAMs.resistance.merge( - F.Range.from_center(10 * P.kohm, 1 * P.kohm) - ), - r.PARAMs.rated_power.merge(F.Range.lower_bound(0.05 * P.W)), - r.PARAMs.rated_voltage.merge(F.Range.lower_bound(25 * P.V)), + r.resistance.merge(F.Range.from_center(10 * P.kohm, 1 * P.kohm)), + r.rated_power.merge(F.Range.lower_bound(0.05 * P.W)), + r.rated_voltage.merge(F.Range.lower_bound(25 * P.V)), ) ), footprint=[("0402", 2)], @@ -232,11 +228,9 @@ def test_find_resistor(self): self, requirement=F.Resistor().builder( lambda r: ( - r.PARAMs.resistance.merge( - F.Range.from_center(69 * P.kohm, 2 * P.kohm) - ), - r.PARAMs.rated_power.merge(F.Range.lower_bound(0.1 * P.W)), - r.PARAMs.rated_voltage.merge(F.Range.lower_bound(50 * P.V)), + r.resistance.merge(F.Range.from_center(69 * P.kohm, 2 * P.kohm)), + r.rated_power.merge(F.Range.lower_bound(0.1 * P.W)), + r.rated_voltage.merge(F.Range.lower_bound(50 * P.V)), ) ), footprint=[("0603", 2)], @@ -247,11 +241,9 @@ def test_find_capacitor(self): self, requirement=F.Capacitor().builder( lambda c: ( - c.PARAMs.capacitance.merge( - F.Range.from_center(100 * P.nF, 10 * P.nF) - ), - c.PARAMs.rated_voltage.merge(F.Range.lower_bound(25 * P.V)), - c.PARAMs.temperature_coefficient.merge( + c.capacitance.merge(F.Range.from_center(100 * P.nF, 10 * P.nF)), + c.rated_voltage.merge(F.Range.lower_bound(25 * P.V)), + c.temperature_coefficient.merge( F.Range.lower_bound(F.Capacitor.TemperatureCoefficient.X7R) ), ) @@ -263,11 +255,9 @@ def test_find_capacitor(self): self, requirement=F.Capacitor().builder( lambda c: ( - c.PARAMs.capacitance.merge( - F.Range.from_center(47 * P.pF, 4.7 * P.pF) - ), - c.PARAMs.rated_voltage.merge(F.Range.lower_bound(50 * P.V)), - c.PARAMs.temperature_coefficient.merge( + c.capacitance.merge(F.Range.from_center(47 * P.pF, 4.7 * P.pF)), + c.rated_voltage.merge(F.Range.lower_bound(50 * P.V)), + c.temperature_coefficient.merge( F.Range.lower_bound(F.Capacitor.TemperatureCoefficient.C0G) ), ) @@ -280,12 +270,10 @@ def test_find_inductor(self): self, requirement=F.Inductor().builder( lambda i: ( - i.PARAMs.inductance.merge( - F.Range.from_center(4.7 * P.nH, 0.47 * P.nH) - ), - i.PARAMs.rated_current.merge(F.Range.lower_bound(0.01 * P.A)), - i.PARAMs.dc_resistance.merge(F.Range.upper_bound(1 * P.ohm)), - i.PARAMs.self_resonant_frequency.merge( + i.inductance.merge(F.Range.from_center(4.7 * P.nH, 0.47 * P.nH)), + i.rated_current.merge(F.Range.lower_bound(0.01 * P.A)), + i.dc_resistance.merge(F.Range.upper_bound(1 * P.ohm)), + i.self_resonant_frequency.merge( F.Range.lower_bound(100 * P.Mhertz) ), ) @@ -298,22 +286,14 @@ def test_find_mosfet(self): self, requirement=F.MOSFET().builder( lambda m: ( - m.PARAMs.channel_type.merge( - F.Constant(F.MOSFET.ChannelType.N_CHANNEL) - ), - m.PARAMs.saturation_type.merge( + m.channel_type.merge(F.Constant(F.MOSFET.ChannelType.N_CHANNEL)), + m.saturation_type.merge( F.Constant(F.MOSFET.SaturationType.ENHANCEMENT) ), - m.PARAMs.gate_source_threshold_voltage.merge( - F.Range(0.4 * P.V, 3 * P.V) - ), - m.PARAMs.max_drain_source_voltage.merge( - F.Range.lower_bound(20 * P.V) - ), - m.PARAMs.max_continuous_drain_current.merge( - F.Range.lower_bound(2 * P.A) - ), - m.PARAMs.on_resistance.merge(F.Range.upper_bound(0.1 * P.ohm)), + m.gate_source_threshold_voltage.merge(F.Range(0.4 * P.V, 3 * P.V)), + m.max_drain_source_voltage.merge(F.Range.lower_bound(20 * P.V)), + m.max_continuous_drain_current.merge(F.Range.lower_bound(2 * P.A)), + m.on_resistance.merge(F.Range.upper_bound(0.1 * P.ohm)), ) ), footprint=[("SOT-23", 3)], @@ -324,15 +304,11 @@ def test_find_diode(self): self, requirement=F.Diode().builder( lambda d: ( - d.PARAMs.current.merge(F.Range.lower_bound(1 * P.A)), - d.PARAMs.forward_voltage.merge(F.Range.upper_bound(1.7 * P.V)), - d.PARAMs.reverse_working_voltage.merge( - F.Range.lower_bound(20 * P.V) - ), - d.PARAMs.reverse_leakage_current.merge( - F.Range.upper_bound(100 * P.uA) - ), - d.PARAMs.max_current.merge(F.Range.lower_bound(1 * P.A)), + d.current.merge(F.Range.lower_bound(1 * P.A)), + d.forward_voltage.merge(F.Range.upper_bound(1.7 * P.V)), + d.reverse_working_voltage.merge(F.Range.lower_bound(20 * P.V)), + d.reverse_leakage_current.merge(F.Range.upper_bound(100 * P.uA)), + d.max_current.merge(F.Range.lower_bound(1 * P.A)), ) ), footprint=[("SOD-123", 2)], @@ -345,16 +321,12 @@ def test_find_tvs(self): lambda t: ( # TODO: There is no current specified for TVS diodes, only peak # current - t.PARAMs.current.merge(F.ANY()), - t.PARAMs.forward_voltage.merge(F.ANY()), - t.PARAMs.reverse_working_voltage.merge( - F.Range.lower_bound(5 * P.V) - ), - t.PARAMs.reverse_leakage_current.merge(F.ANY()), - t.PARAMs.max_current.merge(F.Range.lower_bound(10 * P.A)), - t.PARAMs.reverse_breakdown_voltage.merge( - F.Range.upper_bound(8 * P.V) - ), + t.current.merge(F.ANY()), + t.forward_voltage.merge(F.ANY()), + t.reverse_working_voltage.merge(F.Range.lower_bound(5 * P.V)), + t.reverse_leakage_current.merge(F.ANY()), + t.max_current.merge(F.Range.lower_bound(10 * P.A)), + t.reverse_breakdown_voltage.merge(F.Range.upper_bound(8 * P.V)), ) ), footprint=[("SMB(DO-214AA)", 2)], @@ -365,18 +337,14 @@ def test_find_ldo(self): self, F.LDO().builder( lambda u: ( - u.PARAMs.output_voltage.merge( - F.Range.from_center(3.3 * P.V, 0.1 * P.V) - ), - u.PARAMs.output_current.merge(F.Range.lower_bound(0.1 * P.A)), - u.PARAMs.max_input_voltage.merge(F.Range.lower_bound(5 * P.V)), - u.PARAMs.dropout_voltage.merge(F.Range.upper_bound(1 * P.V)), - u.PARAMs.output_polarity.merge( - F.Constant(F.LDO.OutputPolarity.POSITIVE) - ), - u.PARAMs.output_type.merge(F.Constant(F.LDO.OutputType.FIXED)), - u.PARAMs.psrr.merge(F.ANY()), - u.PARAMs.quiescent_current.merge(F.ANY()), + u.output_voltage.merge(F.Range.from_center(3.3 * P.V, 0.1 * P.V)), + u.output_current.merge(F.Range.lower_bound(0.1 * P.A)), + u.max_input_voltage.merge(F.Range.lower_bound(5 * P.V)), + u.dropout_voltage.merge(F.Range.upper_bound(1 * P.V)), + u.output_polarity.merge(F.Constant(F.LDO.OutputPolarity.POSITIVE)), + u.output_type.merge(F.Constant(F.LDO.OutputType.FIXED)), + u.psrr.merge(F.ANY()), + u.quiescent_current.merge(F.ANY()), ) ), footprint=[ diff --git a/test/libs/util.py b/test/libs/util.py index 97c3fb37..445c4bf6 100644 --- a/test/libs/util.py +++ b/test/libs/util.py @@ -5,7 +5,7 @@ from itertools import combinations from faebryk.libs.logging import setup_basic_logging -from faebryk.libs.util import SharedReference, zip_non_locked +from faebryk.libs.util import SharedReference, once, zip_non_locked class TestUtil(unittest.TestCase): @@ -54,6 +54,44 @@ def all_equal(*args: SharedReference): all_equal(r1, r2, r3, r4, r5) + def test_once(self): + global ran + ran = False + + @once + def do(val: int): + global ran + ran = True + return val + + self.assertFalse(ran) + + self.assertEqual(do(5), 5) + self.assertTrue(ran) + ran = False + + self.assertEqual(do(5), 5) + self.assertFalse(ran) + + self.assertEqual(do(6), 6) + self.assertTrue(ran) + ran = False + + class A: + @classmethod + @once + def do(cls): + global ran + ran = True + return cls + + self.assertEqual(A.do(), A) + self.assertTrue(ran) + ran = False + + self.assertEqual(A.do(), A) + self.assertFalse(ran) + if __name__ == "__main__": setup_basic_logging() diff --git a/tools/library/gen_F.py b/tools/library/gen_F.py index 4601923a..77e08db0 100755 --- a/tools/library/gen_F.py +++ b/tools/library/gen_F.py @@ -4,10 +4,11 @@ This file generates faebryk/src/faebryk/library/__init__.py """ -import glob import logging -import os +import re +from graphlib import TopologicalSorter from pathlib import Path +from typing import Iterable logger = logging.getLogger(__name__) @@ -15,19 +16,72 @@ OUT = DIR / "_F.py" +def try_(stmt: str, exc: str | type[Exception] | Iterable[type[Exception]]): + if isinstance(exc, type): + exc = exc.__name__ + if not isinstance(exc, str): + exc = f'({", ".join(e.__name__ for e in exc)})' + + return ( + f"try:\n {stmt}\nexcept {exc} as e:\n print('{stmt.split(' ')[-1]}', e)" + ) + + +def topo_sort(modules_out: dict[str, tuple[Path, str]]): + def find_deps(module_path: Path) -> set[str]: + f = module_path.read_text() + p = re.compile(r"[^a-zA-Z_0-9]F\.([a-zA-Z_][a-zA-Z_0-9]*)") + return set(p.findall(f)) + + if True: + # TODO careful collisions + all_modules = [ + (p.stem, p) + for p in DIR.parent.parent.rglob("*.py") + if not p.stem.startswith("_") + ] + else: + all_modules = [ + (module_name, module_path) + for module_name, (module_path, _) in modules_out.items() + ] + + topo_graph = { + module_name: find_deps(module_path) for module_name, module_path in all_modules + } + topo_graph = { + k: list(sorted(v)) + for k, v in sorted(topo_graph.items(), key=lambda item: item[0]) + } + order = list(TopologicalSorter(topo_graph).static_order()) + + # TEST + seen = set() + for m in order: + if m not in topo_graph: + continue + for sub in topo_graph[m]: + if sub not in seen and sub in topo_graph: + raise Exception(f"Collision: {sub} after {m}") + seen.add(m) + + return [ + (module_name, modules_out[module_name][1]) + for module_name in order + if module_name in modules_out + ] + + def main(): assert DIR.exists() logger.info(f"Scanning {DIR} for modules") - module_files = glob.glob(str(DIR / "*.py")) - module_files = [ - os.path.basename(f)[:-3] for f in module_files if os.path.basename(f) != "_F.py" - ] + module_files = [p for p in DIR.glob("*.py") if not p.name.startswith("_")] logger.info(f"Found {len(module_files)} modules") - modules_out: dict[str, str] = {} + modules_out: dict[str, tuple[Path, str]] = {} # Import each module and add its class to the current namespace # for module_name in module_files: @@ -39,7 +93,13 @@ def main(): # # globals()[class_name] = getattr(module, class_name) # modules_out[module_name] = class_name - modules_out = {module_name: module_name for module_name in module_files} + # assuming class name is equal to file stem + modules_out = { + module_path.stem: (module_path, module_path.stem) + for module_path in module_files + } + + modules_ordered = topo_sort(modules_out) logger.info(f"Found {len(modules_out)} classes") @@ -62,8 +122,11 @@ def main(): "# flake8: noqa: E501\n" "\n" + "\n".join( + # try_( f"from faebryk.library.{module} import {class_}" - for module, class_ in sorted(modules_out.items(), key=lambda x: x[0]) + # (AttributeError,), + # ) + for module, class_ in modules_ordered ) + "\n" )