Skip to content

Commit 5ac5c56

Browse files
authored
Parts Table Refactor (#376)
Cleans up the parts table type hierarchy, with more orthogonality between the part, abstract-table, footprint-spec, footprint-area, and footprint-generation functionality. Other changes: - Adds ClassVar[...] to some abstract class variables that are to be implemented by subclasses, but it seems mypy won't detect those without ABCmeta, which breaks a bunch of other stuff. Sad. - Moves the standard-footprint functionality to its own class, and the HasStandardFootprint interface. HasStandardFootprint now implemented by abstract classes like Bjt, Capacitor, ..., instead of the more specific table. Also includes the refdes prefix. - Uniformly support footprint-spec and footprint-area filters on the base JlcTable class. The footprint generation must be mixed into each subclass independently, since some devices do not support standard footprints (antenna) or generate sub-blocks (oscillator) - Makes the PartsTablePart (and hierarchy) abstract library parts (instead of non-library) and browseable. - Adds *args, **kwargs forwarding in base FootprintBlock and GeneratorBlock classes.
1 parent beb9736 commit 5ac5c56

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

49 files changed

+594
-493
lines changed

edg/abstract_parts/AbstractBjt.py

Lines changed: 28 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,38 @@
33
from ..electronics_model import *
44
from .Categories import *
55
from .PartsTable import PartsTableColumn, PartsTableRow
6-
from .PartsTablePart import PartsTableFootprintSelector
7-
from .StandardFootprint import StandardFootprint
6+
from .PartsTablePart import PartsTableSelector
7+
from .StandardFootprint import StandardFootprint, HasStandardFootprint
8+
9+
10+
class BjtStandardFootprint(StandardFootprint['Bjt']):
11+
REFDES_PREFIX = 'Q'
12+
13+
FOOTPRINT_PINNING_MAP = {
14+
'Package_TO_SOT_SMD:SOT-23': lambda block: {
15+
'1': block.base,
16+
'2': block.emitter,
17+
'3': block.collector,
18+
},
19+
'Package_TO_SOT_SMD:SOT-89-3': lambda block: {
20+
'1': block.base,
21+
'2': block.collector,
22+
'3': block.emitter,
23+
},
24+
'Package_TO_SOT_SMD:SOT-323_SC-70': lambda block: {
25+
'1': block.base,
26+
'2': block.emitter,
27+
'3': block.collector,
28+
},
29+
}
830

931

1032
@abstract_block
11-
class Bjt(KiCadImportableBlock, DiscreteSemiconductor):
33+
class Bjt(KiCadImportableBlock, DiscreteSemiconductor, HasStandardFootprint):
1234
"""Base class for untyped BJTs
1335
"""
36+
_STANDARD_FOOTPRINT = BjtStandardFootprint
37+
1438
def symbol_pinning(self, symbol_name: str) -> Dict[str, BasePort]:
1539
# TODO actually check that the device channel corresponds with the schematic?
1640
assert symbol_name.startswith('Device:Q_NPN_') or symbol_name.startswith('Device:Q_PNP_')
@@ -62,30 +86,7 @@ def contents(self):
6286
)
6387

6488

65-
@non_library
66-
class BjtStandardFootprint(Bjt, StandardFootprint[Bjt]):
67-
REFDES_PREFIX = 'Q'
68-
69-
FOOTPRINT_PINNING_MAP = {
70-
'Package_TO_SOT_SMD:SOT-23': lambda block: {
71-
'1': block.base,
72-
'2': block.emitter,
73-
'3': block.collector,
74-
},
75-
'Package_TO_SOT_SMD:SOT-89-3': lambda block: {
76-
'1': block.base,
77-
'2': block.collector,
78-
'3': block.emitter,
79-
},
80-
'Package_TO_SOT_SMD:SOT-323_SC-70': lambda block: {
81-
'1': block.base,
82-
'2': block.emitter,
83-
'3': block.collector,
84-
},
85-
}
86-
87-
88-
class TableBjt(BjtStandardFootprint, PartsTableFootprintSelector):
89+
class TableBjt(PartsTableSelector, Bjt):
8990
VCE_RATING = PartsTableColumn(Range)
9091
ICE_RATING = PartsTableColumn(Range)
9192
GAIN = PartsTableColumn(Range)

edg/abstract_parts/AbstractCapacitor.py

Lines changed: 78 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,84 @@
11
import re
22
from abc import abstractmethod
3-
from typing import Optional, cast, Dict, Any, List, Tuple, Mapping
3+
from typing import Optional, cast, Dict, Any, Tuple, Mapping
44
import math
55

66
from ..electronics_model import *
77
from .PartsTable import PartsTableColumn, PartsTableRow, PartsTable
8-
from .PartsTablePart import PartsTableFootprintSelector
8+
from .PartsTablePart import PartsTableSelector
99
from .Categories import *
10-
from .StandardFootprint import StandardFootprint
10+
from .StandardFootprint import StandardFootprint, HasStandardFootprint
11+
12+
13+
class CapacitorStandardFootprint(StandardFootprint['Capacitor']):
14+
REFDES_PREFIX = 'C'
15+
16+
# IMPORTANT! DummyFootprint doesn't use this, it will break on anything that isn't this pinning
17+
FOOTPRINT_PINNING_MAP = {
18+
(
19+
'Capacitor_SMD:C_0201_0603Metric',
20+
'Capacitor_SMD:C_0402_1005Metric',
21+
'Capacitor_SMD:C_0603_1608Metric',
22+
'Capacitor_SMD:C_0805_2012Metric',
23+
'Capacitor_SMD:C_1206_3216Metric',
24+
'Capacitor_SMD:C_1210_3225Metric',
25+
'Capacitor_SMD:C_1812_4532Metric',
26+
'Capacitor_SMD:C_2512_6332Metric',
27+
28+
'Capacitor_SMD:CP_Elec_3x5.3',
29+
'Capacitor_SMD:CP_Elec_3x5.4',
30+
'Capacitor_SMD:CP_Elec_4x3',
31+
'Capacitor_SMD:CP_Elec_4x3.9',
32+
'Capacitor_SMD:CP_Elec_4x4.5',
33+
'Capacitor_SMD:CP_Elec_4x5.3',
34+
'Capacitor_SMD:CP_Elec_4x5.4',
35+
'Capacitor_SMD:CP_Elec_4x5.7',
36+
'Capacitor_SMD:CP_Elec_4x5.8',
37+
'Capacitor_SMD:CP_Elec_5x3',
38+
'Capacitor_SMD:CP_Elec_5x3.9',
39+
'Capacitor_SMD:CP_Elec_5x4.4',
40+
'Capacitor_SMD:CP_Elec_5x4.5',
41+
'Capacitor_SMD:CP_Elec_5x5.3',
42+
'Capacitor_SMD:CP_Elec_5x5.4',
43+
'Capacitor_SMD:CP_Elec_5x5.7',
44+
'Capacitor_SMD:CP_Elec_5x5.8',
45+
'Capacitor_SMD:CP_Elec_5x5.9',
46+
'Capacitor_SMD:CP_Elec_6.3x3',
47+
'Capacitor_SMD:CP_Elec_6.3x3.9',
48+
'Capacitor_SMD:CP_Elec_6.3x4.5',
49+
'Capacitor_SMD:CP_Elec_6.3x4.9',
50+
'Capacitor_SMD:CP_Elec_6.3x5.2',
51+
'Capacitor_SMD:CP_Elec_6.3x5.3',
52+
'Capacitor_SMD:CP_Elec_6.3x5.4',
53+
'Capacitor_SMD:CP_Elec_6.3x5.7',
54+
'Capacitor_SMD:CP_Elec_6.3x5.8',
55+
'Capacitor_SMD:CP_Elec_6.3x5.9',
56+
'Capacitor_SMD:CP_Elec_6.3x7.7',
57+
'Capacitor_SMD:CP_Elec_6.3x9.9',
58+
'Capacitor_SMD:CP_Elec_8x5.4',
59+
'Capacitor_SMD:CP_Elec_8x6.2',
60+
'Capacitor_SMD:CP_Elec_8x6.5',
61+
'Capacitor_SMD:CP_Elec_8x6.7',
62+
'Capacitor_SMD:CP_Elec_8x6.9',
63+
'Capacitor_SMD:CP_Elec_8x10',
64+
'Capacitor_SMD:CP_Elec_8x10.5',
65+
'Capacitor_SMD:CP_Elec_8x11.9',
66+
'Capacitor_SMD:CP_Elec_10x7.7',
67+
'Capacitor_SMD:CP_Elec_10x7.9',
68+
'Capacitor_SMD:CP_Elec_10x10',
69+
'Capacitor_SMD:CP_Elec_10x10.5',
70+
'Capacitor_SMD:CP_Elec_10x12.5',
71+
'Capacitor_SMD:CP_Elec_10x12.6',
72+
'Capacitor_SMD:CP_Elec_10x14.3',
73+
'Capacitor_SMD:CP_Elec_16x17.5',
74+
'Capacitor_SMD:CP_Elec_16x22',
75+
'Capacitor_SMD:CP_Elec_18x7.5',
76+
'Capacitor_SMD:CP_Elec_18x22',
77+
): lambda block: {
78+
'1': block.pos,
79+
'2': block.neg,
80+
},
81+
}
1182

1283

1384
@abstract_block
@@ -46,8 +117,10 @@ def contents(self):
46117
)
47118

48119
@abstract_block
49-
class Capacitor(UnpolarizedCapacitor, KiCadInstantiableBlock):
120+
class Capacitor(UnpolarizedCapacitor, KiCadInstantiableBlock, HasStandardFootprint):
50121
"""Polarized capacitor, which we assume will be the default"""
122+
_STANDARD_FOOTPRINT = CapacitorStandardFootprint
123+
51124
CAPACITOR_REGEX = re.compile("^" + f"([\d.{PartParserUtil.SI_PREFIXES}]+)\s*F?" +
52125
"\s*" + "((?:\+-|\+/-|±)?\s*[\d.]+\s*%)?" +
53126
"\s*" + f"([\d.{PartParserUtil.SI_PREFIXES}]+\s*V)" + "$")
@@ -98,79 +171,7 @@ class AluminumCapacitor(Capacitor):
98171

99172

100173
@non_library
101-
class CapacitorStandardFootprint(Capacitor, StandardFootprint[Capacitor]):
102-
REFDES_PREFIX = 'C'
103-
104-
# IMPORTANT! DummyFootprint doesn't use this, it will break on anything that isn't this pinning
105-
FOOTPRINT_PINNING_MAP = {
106-
(
107-
'Capacitor_SMD:C_0201_0603Metric',
108-
'Capacitor_SMD:C_0402_1005Metric',
109-
'Capacitor_SMD:C_0603_1608Metric',
110-
'Capacitor_SMD:C_0805_2012Metric',
111-
'Capacitor_SMD:C_1206_3216Metric',
112-
'Capacitor_SMD:C_1210_3225Metric',
113-
'Capacitor_SMD:C_1812_4532Metric',
114-
'Capacitor_SMD:C_2512_6332Metric',
115-
116-
'Capacitor_SMD:CP_Elec_3x5.3',
117-
'Capacitor_SMD:CP_Elec_3x5.4',
118-
'Capacitor_SMD:CP_Elec_4x3',
119-
'Capacitor_SMD:CP_Elec_4x3.9',
120-
'Capacitor_SMD:CP_Elec_4x4.5',
121-
'Capacitor_SMD:CP_Elec_4x5.3',
122-
'Capacitor_SMD:CP_Elec_4x5.4',
123-
'Capacitor_SMD:CP_Elec_4x5.7',
124-
'Capacitor_SMD:CP_Elec_4x5.8',
125-
'Capacitor_SMD:CP_Elec_5x3',
126-
'Capacitor_SMD:CP_Elec_5x3.9',
127-
'Capacitor_SMD:CP_Elec_5x4.4',
128-
'Capacitor_SMD:CP_Elec_5x4.5',
129-
'Capacitor_SMD:CP_Elec_5x5.3',
130-
'Capacitor_SMD:CP_Elec_5x5.4',
131-
'Capacitor_SMD:CP_Elec_5x5.7',
132-
'Capacitor_SMD:CP_Elec_5x5.8',
133-
'Capacitor_SMD:CP_Elec_5x5.9',
134-
'Capacitor_SMD:CP_Elec_6.3x3',
135-
'Capacitor_SMD:CP_Elec_6.3x3.9',
136-
'Capacitor_SMD:CP_Elec_6.3x4.5',
137-
'Capacitor_SMD:CP_Elec_6.3x4.9',
138-
'Capacitor_SMD:CP_Elec_6.3x5.2',
139-
'Capacitor_SMD:CP_Elec_6.3x5.3',
140-
'Capacitor_SMD:CP_Elec_6.3x5.4',
141-
'Capacitor_SMD:CP_Elec_6.3x5.7',
142-
'Capacitor_SMD:CP_Elec_6.3x5.8',
143-
'Capacitor_SMD:CP_Elec_6.3x5.9',
144-
'Capacitor_SMD:CP_Elec_6.3x7.7',
145-
'Capacitor_SMD:CP_Elec_6.3x9.9',
146-
'Capacitor_SMD:CP_Elec_8x5.4',
147-
'Capacitor_SMD:CP_Elec_8x6.2',
148-
'Capacitor_SMD:CP_Elec_8x6.5',
149-
'Capacitor_SMD:CP_Elec_8x6.7',
150-
'Capacitor_SMD:CP_Elec_8x6.9',
151-
'Capacitor_SMD:CP_Elec_8x10',
152-
'Capacitor_SMD:CP_Elec_8x10.5',
153-
'Capacitor_SMD:CP_Elec_8x11.9',
154-
'Capacitor_SMD:CP_Elec_10x7.7',
155-
'Capacitor_SMD:CP_Elec_10x7.9',
156-
'Capacitor_SMD:CP_Elec_10x10',
157-
'Capacitor_SMD:CP_Elec_10x10.5',
158-
'Capacitor_SMD:CP_Elec_10x12.5',
159-
'Capacitor_SMD:CP_Elec_10x12.6',
160-
'Capacitor_SMD:CP_Elec_10x14.3',
161-
'Capacitor_SMD:CP_Elec_16x17.5',
162-
'Capacitor_SMD:CP_Elec_16x22',
163-
'Capacitor_SMD:CP_Elec_18x7.5',
164-
'Capacitor_SMD:CP_Elec_18x22',
165-
): lambda block: {
166-
'1': block.pos,
167-
'2': block.neg,
168-
},
169-
}
170-
171-
172-
@non_library
173-
class TableCapacitor(CapacitorStandardFootprint, PartsTableFootprintSelector):
174+
class TableCapacitor(PartsTableSelector, Capacitor):
174175
"""Abstract table-based capacitor, providing some interface column definitions."""
175176
CAPACITANCE = PartsTableColumn(Range)
176177
NOMINAL_CAPACITANCE = PartsTableColumn(float) # nominal capacitance, even with asymmetrical tolerances

edg/abstract_parts/AbstractCrystal.py

Lines changed: 30 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,10 @@
11
from ..electronics_model import *
2-
from . import PartsTableFootprintSelector, PartsTableColumn, Capacitor, PartsTableRow
2+
from . import PartsTableSelector, PartsTableColumn, Capacitor, PartsTableRow
33
from .Categories import *
4-
from .StandardFootprint import StandardFootprint
4+
from .StandardFootprint import StandardFootprint, HasStandardFootprint
55

66

7-
@abstract_block
8-
class Crystal(DiscreteComponent):
9-
@init_in_parent
10-
def __init__(self, frequency: RangeLike) -> None:
11-
"""Discrete crystal component."""
12-
super().__init__()
13-
14-
self.frequency = self.ArgParameter(frequency)
15-
self.actual_frequency = self.Parameter(RangeExpr())
16-
self.actual_capacitance = self.Parameter(FloatExpr())
17-
18-
self.crystal = self.Port(CrystalPort(self.actual_frequency), [InOut]) # set by subclass
19-
self.gnd = self.Port(Ground(), [Common])
20-
21-
def contents(self):
22-
super().contents()
23-
24-
self.description = DescriptionString(
25-
"<b>frequency:</b> ", DescriptionString.FormatUnits(self.actual_frequency, "Hz"),
26-
" <b>of spec:</b> ", DescriptionString.FormatUnits(self.frequency, "Hz"), "\n",
27-
"<b>capacitance:</b> ", DescriptionString.FormatUnits(self.actual_capacitance, "F")
28-
)
29-
30-
31-
@non_library
32-
class CrystalStandardFootprint(Crystal, StandardFootprint[Crystal]):
7+
class CrystalStandardFootprint(StandardFootprint['Crystal']):
338
REFDES_PREFIX = 'X'
349

3510
FOOTPRINT_PINNING_MAP = {
@@ -54,8 +29,34 @@ class CrystalStandardFootprint(Crystal, StandardFootprint[Crystal]):
5429
}
5530

5631

32+
@abstract_block
33+
class Crystal(DiscreteComponent, HasStandardFootprint):
34+
_STANDARD_FOOTPRINT = CrystalStandardFootprint
35+
36+
@init_in_parent
37+
def __init__(self, frequency: RangeLike) -> None:
38+
"""Discrete crystal component."""
39+
super().__init__()
40+
41+
self.frequency = self.ArgParameter(frequency)
42+
self.actual_frequency = self.Parameter(RangeExpr())
43+
self.actual_capacitance = self.Parameter(FloatExpr())
44+
45+
self.crystal = self.Port(CrystalPort(self.actual_frequency), [InOut]) # set by subclass
46+
self.gnd = self.Port(Ground(), [Common])
47+
48+
def contents(self):
49+
super().contents()
50+
51+
self.description = DescriptionString(
52+
"<b>frequency:</b> ", DescriptionString.FormatUnits(self.actual_frequency, "Hz"),
53+
" <b>of spec:</b> ", DescriptionString.FormatUnits(self.frequency, "Hz"), "\n",
54+
"<b>capacitance:</b> ", DescriptionString.FormatUnits(self.actual_capacitance, "F")
55+
)
56+
57+
5758
@non_library
58-
class TableCrystal(CrystalStandardFootprint, PartsTableFootprintSelector):
59+
class TableCrystal(PartsTableSelector, Crystal):
5960
FREQUENCY = PartsTableColumn(Range)
6061
CAPACITANCE = PartsTableColumn(float)
6162

edg/abstract_parts/AbstractDiodes.py

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,11 @@
55
from .DummyDevices import ForcedAnalogVoltage
66
from .Categories import *
77
from .PartsTable import PartsTableColumn, PartsTableRow
8-
from .PartsTablePart import PartsTableFootprintSelector
9-
from .StandardFootprint import StandardFootprint
8+
from .PartsTablePart import PartsTableSelector
9+
from .StandardFootprint import StandardFootprint, HasStandardFootprint
1010

1111

12-
@non_library
13-
class BaseDiode(DiscreteSemiconductor):
14-
"""Base class for diodes, with anode and cathode pins, including a very wide range of devices.
15-
"""
16-
@init_in_parent
17-
def __init__(self) -> None:
18-
super().__init__()
19-
20-
self.anode = self.Port(Passive.empty())
21-
self.cathode = self.Port(Passive.empty())
22-
23-
24-
@non_library
25-
class BaseDiodeStandardFootprint(BaseDiode, StandardFootprint[BaseDiode]):
12+
class DiodeStandardFootprint(StandardFootprint['BaseDiode']):
2613
REFDES_PREFIX = 'D'
2714

2815
FOOTPRINT_PINNING_MAP = {
@@ -48,6 +35,20 @@ class BaseDiodeStandardFootprint(BaseDiode, StandardFootprint[BaseDiode]):
4835
}
4936

5037

38+
@non_library
39+
class BaseDiode(DiscreteSemiconductor, HasStandardFootprint):
40+
"""Base class for diodes, with anode and cathode pins, including a very wide range of devices.
41+
"""
42+
_STANDARD_FOOTPRINT = DiodeStandardFootprint
43+
44+
@init_in_parent
45+
def __init__(self) -> None:
46+
super().__init__()
47+
48+
self.anode = self.Port(Passive.empty())
49+
self.cathode = self.Port(Passive.empty())
50+
51+
5152
@abstract_block
5253
class Diode(KiCadImportableBlock, BaseDiode):
5354
"""Base class for untyped diodes
@@ -88,7 +89,7 @@ def contents(self):
8889

8990

9091
@non_library
91-
class TableDiode(Diode, BaseDiodeStandardFootprint, PartsTableFootprintSelector, GeneratorBlock):
92+
class TableDiode(PartsTableSelector, Diode):
9293
VOLTAGE_RATING = PartsTableColumn(Range) # tolerable blocking voltages, positive
9394
CURRENT_RATING = PartsTableColumn(Range) # tolerable currents, average
9495
FORWARD_VOLTAGE = PartsTableColumn(Range) # possible forward voltage range
@@ -144,7 +145,7 @@ def contents(self):
144145

145146

146147
@non_library
147-
class TableZenerDiode(ZenerDiode, BaseDiodeStandardFootprint, PartsTableFootprintSelector, GeneratorBlock):
148+
class TableZenerDiode(PartsTableSelector, ZenerDiode):
148149
ZENER_VOLTAGE = PartsTableColumn(Range)
149150
POWER_RATING = PartsTableColumn(Range) # tolerable power
150151

0 commit comments

Comments
 (0)