Skip to content

Commit

Permalink
Add new keywords (#11)
Browse files Browse the repository at this point in the history
Added new keywords:
`Modules Should Have Values Matching` - validate that selected modules have valid values.
`Complement Modules By Reference` - perform set complement operation on module lists.
  • Loading branch information
Martin Kjellstrand authored Dec 27, 2018
1 parent 47373c9 commit 187a155
Show file tree
Hide file tree
Showing 5 changed files with 291 additions and 13 deletions.
19 changes: 19 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,22 @@ We use [SemVer](http://semver.org/) for versioning. For the versions available,
## License

This project is licensed under the MIT License - see the [LICENSE.txt](LICENSE.txt) file for details

## Developer information / Preparing a release

To create a release please follow this outline:

* Do your development work in a separate branch.
* Write unit-tests (`tests/`) and/or integration tests (`examples/`) for your code.
* Once all local tests validate, and you have 100% code coverage, push to GitHub.

### If you have commit access to the main repository

* Once build hooks at Github/Sonarcloud/pyup etc have completed, tag a pre-release (`x.y.zrc0`)
* If that build completes, perform a PR into `master`, squashing the commit history.
* Tag the `master` branch with the new release version, ahdering to semantic versioning.
* Remove any `pre` artifacts from Docker Hub and PyPI.

### If you don't have commit access to the main repository

* Submit a PR towards the `master` branch of the [main repository](https://github.com/madworx/robotframework-kicadlibrary/).
62 changes: 54 additions & 8 deletions src/KiCadLibrary/KiCadLibrary.py
Original file line number Diff line number Diff line change
Expand Up @@ -344,14 +344,15 @@ def find_modules_by_value(self, regexp):

def intersect_modules_by_reference(self, list1, list2):
# pylint: disable=line-too-long
"""*DEPRECATED!!* Use keyword `Find Modules` instead.
Perform a set intersection of the two module lists ``list1`` and
``list2`` based on the module ``references``. (I.e. returns a new
list containing only the modules that exist in both lists.
"""Perform a set intersection of the two module lists ``list1``
and ``list2`` based on the module ``references``. (I.e. returns
a new list containing only the modules that exist in both lists.
If either of the two lists are equal to `None`, the other
argument will be returned.
*You normally don't need to use this keyword, see documentation
on module selection.*
Examples:
| `${connectors}=` | `Find Modules By Value` | `Conn_02x20_Odd_Even` |
| `${connectors2}=` | `Find Modules By Reference` | `regexp=^J[0-9]+$` |
Expand All @@ -366,6 +367,27 @@ def intersect_modules_by_reference(self, list1, list2):
list1_ref_set = set(m.GetReference() for m in list1)
return [m for m in list2 if m.GetReference() in list1_ref_set]

def complement_modules_by_reference(self, list1, list2):
# pylint: disable=line-too-long
"""Perform a set complement operation on the two module lists
``list1`` and ``list2`` based on the module ``references``.
(I.e. returns a new list containing only the modules that exist
in ``list1`` but not in ``list2``)
Examples:
| `${connectors}=` | `Find Modules By Value` | `Conn_02x20_Odd_Even` |
| `${connectors2}=` | `Find Modules By Reference` | `regexp=^J[0-9]+$` |
| `${non_bus_connectors}=` | `Complement Modules By Reference` | `${connectors}` | `${connectors2}` |
"""

if not bool(list1):
return []
if not bool(list2):
return list1

list2_ref_set = set(m.GetReference() for m in list2)
return [m for m in list1 if m.GetReference() not in list2_ref_set]

def get_pad_netnames_for_module(self, module):
"""Return a _dict_ mapping pad names to the short form of
netnames for the given module.
Expand Down Expand Up @@ -457,6 +479,30 @@ def find_modules_by_pad_netname(self, regexp):
ret.append(mod)
return ret

# pylint: disable=too-many-arguments,line-too-long
def modules_should_have_values_matching(self, matching, modules=None,
value=None, reference=None,
pad_netname=None):
"""Validate that the selected modules have values matching the
regular expression given in `matching`.
This can be used, for example, to validate that all resistors
have been given a value which looks like an actual ohm value.
Examples:
| `Modules Should Have Values Matching` | `[0-9]+(.[0-9]+)? *([kM])?$` | `reference=R[0-9]+` |
| `Modules Should Have Values Matching` | `[0-9]+(.[0-9]+)? *[munp]?F$` | `reference=C[0-9]+` |
"""
modlist = self.find_modules(modules, value, reference, pad_netname)
ret = True
for mod in modlist:
if not re.match(matching, mod.GetValue()):
logger.error("Module {0} has invalid value [{1}]."
.format(mod, mod.GetValue()))
ret = False
if not ret:
raise AssertionError("Modules with incorrect values detected.")

# pylint: disable=too-many-arguments
def modules_should_have_orientation(self, orientation, modules=None,
value=None, reference=None,
Expand All @@ -473,14 +519,14 @@ def modules_should_have_orientation(self, orientation, modules=None,
for mod in modlist:
mod_orient = float(mod.GetOrientation()) / 10.0
if mod_orient != float(orientation):
logger.error("Orientation of component %s is %s, should be %s."
logger.error("Orientation of module %s is %s, should be %s."
% (mod, mod_orient, orientation))
ret = False
else:
logger.info("Orientation of component {0} is {1}".
logger.info("Orientation of module {0} is {1}".
format(mod, mod_orient))
if not ret:
raise AssertionError("Component orientation(s) are wrong")
raise AssertionError("Module orientation(s) are wrong")

def _get_all_edge_cuts(self):
"""Return a dict of all edge cuts"""
Expand Down
26 changes: 24 additions & 2 deletions src/tests/test_modules.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,21 @@ def test_module_intersection():
assert lib.intersect_modules_by_reference(list2, list1) == list1
assert lib.intersect_modules_by_reference(list1, list1) == list1

def test_module_complement():
list1 = lib.find_modules(reference="U.+")
list2 = lib.find_modules(reference="nonexistent")
list3 = lib.find_modules(reference="U3")
assert len(list1) == 3
assert bool(list2) is False
assert len(list3) == 1
assert lib.complement_modules_by_reference(list1, list2) == list1
assert lib.complement_modules_by_reference(list2, list1) == []
assert lib.complement_modules_by_reference(list1, list1) == []
c = lib.complement_modules_by_reference(list1, list3)
assert len(c) == 2
assert ((c[0].GetReference() == 'U2' and c[1].GetReference() == 'U1')
or (c[0].GetReference() == 'U1' and c[1].GetReference() == 'U2'))

def test_module_pads_should_have_same_netnames_should_fail():
with pytest.raises(AssertionError, match=r'have matching pad'):
lib.matching_modules_should_have_same_pads_and_netnames(reference=r'U.*')
Expand Down Expand Up @@ -57,8 +72,8 @@ def test_module_pads_should_be_on_grid_should_work():
lib.module_pads_should_be_on_grid("25mil", reference=r'.*')

def test_modules_should_have_orientation_should_fail():
with pytest.raises(AssertionError, match=r'Component orientation\(s\) are wrong'):
lib.modules_should_have_orientation(180, reference=r'.*')
with pytest.raises(AssertionError, match=r'Module orientation\(s\) are wrong'):
lib.modules_should_have_orientation(180, reference=r'[^R].*')

def test_modules_should_have_orientation_should_work():
lib.modules_should_have_orientation(0, value='LM555')
Expand All @@ -70,3 +85,10 @@ def test_internal_error_get_component_pins_for_module():
module.GetReference = mock.MagicMock(return_value="error")
with pytest.raises(AssertionError, match=r"we couldn't find it\?!"):
lib.get_component_pins_for_module(module)

def test_modules_should_have_matching_values_should_work():
lib.modules_should_have_values_matching(r'[0-9]+(.[0-9]+)? *([kM])?$',
reference=r'R[1-3]+')
with pytest.raises(AssertionError, match=r'incorrect values detected'):
lib.modules_should_have_values_matching(r'[0-9]+(.[0-9]+)? *([kM])?$',
reference=r'R4$')
152 changes: 150 additions & 2 deletions src/tests/test_modules/test_modules.kicad_pcb
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
(drawings 4)
(tracks 39)
(zones 0)
(modules 3)
(nets 49)
(modules 7)
(nets 57)
)

(page A4)
Expand Down Expand Up @@ -138,6 +138,14 @@
(net 46 "Net-(U3-Pad39)")
(net 47 "Net-(U3-Pad20)")
(net 48 "Net-(U3-Pad40)")
(net 49 "Net-(R1-Pad1)")
(net 50 "Net-(R1-Pad2)")
(net 51 "Net-(R2-Pad2)")
(net 52 "Net-(R2-Pad1)")
(net 53 "Net-(R3-Pad1)")
(net 54 "Net-(R3-Pad2)")
(net 55 "Net-(R4-Pad2)")
(net 56 "Net-(R4-Pad1)")

(net_class Default "This is the default net class."
(clearance 0.2)
Expand All @@ -153,6 +161,14 @@
(add_net /TR)
(add_net /~R)
(add_net GND)
(add_net "Net-(R1-Pad1)")
(add_net "Net-(R1-Pad2)")
(add_net "Net-(R2-Pad1)")
(add_net "Net-(R2-Pad2)")
(add_net "Net-(R3-Pad1)")
(add_net "Net-(R3-Pad2)")
(add_net "Net-(R4-Pad1)")
(add_net "Net-(R4-Pad2)")
(add_net "Net-(U3-Pad1)")
(add_net "Net-(U3-Pad10)")
(add_net "Net-(U3-Pad11)")
Expand Down Expand Up @@ -196,6 +212,138 @@
(add_net VCC)
)

(module Resistor_THT:R_Axial_DIN0922_L20.0mm_D9.0mm_P7.62mm_Vertical (layer F.Cu) (tedit 5AE5139B) (tstamp 5C3144D4)
(at 87.63 86.36)
(descr "Resistor, Axial_DIN0922 series, Axial, Vertical, pin pitch=7.62mm, 5W, length*diameter=20*9mm^2, http://www.vishay.com/docs/20128/wkxwrx.pdf")
(tags "Resistor Axial_DIN0922 series Axial Vertical pin pitch 7.62mm 5W length 20mm diameter 9mm")
(path /5C24FB98)
(fp_text reference R4 (at 3.81 -5.62) (layer F.SilkS)
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_text value 0.1uF (at 3.81 5.62) (layer F.Fab)
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_text user %R (at 0 -1.7) (layer F.Fab)
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_line (start 9.07 -4.75) (end -4.75 -4.75) (layer F.CrtYd) (width 0.05))
(fp_line (start 9.07 4.75) (end 9.07 -4.75) (layer F.CrtYd) (width 0.05))
(fp_line (start -4.75 4.75) (end 9.07 4.75) (layer F.CrtYd) (width 0.05))
(fp_line (start -4.75 -4.75) (end -4.75 4.75) (layer F.CrtYd) (width 0.05))
(fp_line (start 4.62 0) (end 6.12 0) (layer F.SilkS) (width 0.12))
(fp_line (start 0 0) (end 7.62 0) (layer F.Fab) (width 0.1))
(fp_circle (center 0 0) (end 4.62 0) (layer F.SilkS) (width 0.12))
(fp_circle (center 0 0) (end 4.5 0) (layer F.Fab) (width 0.1))
(pad 2 thru_hole oval (at 7.62 0) (size 2.4 2.4) (drill 1.2) (layers *.Cu *.Mask)
(net 55 "Net-(R4-Pad2)"))
(pad 1 thru_hole circle (at 0 0) (size 2.4 2.4) (drill 1.2) (layers *.Cu *.Mask)
(net 56 "Net-(R4-Pad1)"))
(model ${KISYS3DMOD}/Resistor_THT.3dshapes/R_Axial_DIN0922_L20.0mm_D9.0mm_P7.62mm_Vertical.wrl
(at (xyz 0 0 0))
(scale (xyz 1 1 1))
(rotate (xyz 0 0 0))
)
)

(module Resistor_THT:R_Axial_DIN0922_L20.0mm_D9.0mm_P7.62mm_Vertical (layer F.Cu) (tedit 5AE5139B) (tstamp 5C3144C5)
(at 87.63 74.93)
(descr "Resistor, Axial_DIN0922 series, Axial, Vertical, pin pitch=7.62mm, 5W, length*diameter=20*9mm^2, http://www.vishay.com/docs/20128/wkxwrx.pdf")
(tags "Resistor Axial_DIN0922 series Axial Vertical pin pitch 7.62mm 5W length 20mm diameter 9mm")
(path /5C24FB07)
(fp_text reference R3 (at 3.81 -5.62) (layer F.SilkS)
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_text value "2 M" (at 3.81 5.62) (layer F.Fab)
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_circle (center 0 0) (end 4.5 0) (layer F.Fab) (width 0.1))
(fp_circle (center 0 0) (end 4.62 0) (layer F.SilkS) (width 0.12))
(fp_line (start 0 0) (end 7.62 0) (layer F.Fab) (width 0.1))
(fp_line (start 4.62 0) (end 6.12 0) (layer F.SilkS) (width 0.12))
(fp_line (start -4.75 -4.75) (end -4.75 4.75) (layer F.CrtYd) (width 0.05))
(fp_line (start -4.75 4.75) (end 9.07 4.75) (layer F.CrtYd) (width 0.05))
(fp_line (start 9.07 4.75) (end 9.07 -4.75) (layer F.CrtYd) (width 0.05))
(fp_line (start 9.07 -4.75) (end -4.75 -4.75) (layer F.CrtYd) (width 0.05))
(fp_text user %R (at 0 -1.7) (layer F.Fab)
(effects (font (size 1 1) (thickness 0.15)))
)
(pad 1 thru_hole circle (at 0 0) (size 2.4 2.4) (drill 1.2) (layers *.Cu *.Mask)
(net 53 "Net-(R3-Pad1)"))
(pad 2 thru_hole oval (at 7.62 0) (size 2.4 2.4) (drill 1.2) (layers *.Cu *.Mask)
(net 54 "Net-(R3-Pad2)"))
(model ${KISYS3DMOD}/Resistor_THT.3dshapes/R_Axial_DIN0922_L20.0mm_D9.0mm_P7.62mm_Vertical.wrl
(at (xyz 0 0 0))
(scale (xyz 1 1 1))
(rotate (xyz 0 0 0))
)
)

(module Resistor_THT:R_Axial_DIN0922_L20.0mm_D9.0mm_P7.62mm_Vertical (layer F.Cu) (tedit 5AE5139B) (tstamp 5C3144B6)
(at 71.12 86.36)
(descr "Resistor, Axial_DIN0922 series, Axial, Vertical, pin pitch=7.62mm, 5W, length*diameter=20*9mm^2, http://www.vishay.com/docs/20128/wkxwrx.pdf")
(tags "Resistor Axial_DIN0922 series Axial Vertical pin pitch 7.62mm 5W length 20mm diameter 9mm")
(path /5C24FAD5)
(fp_text reference R2 (at 3.81 -5.62) (layer F.SilkS)
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_text value 330 (at 3.81 5.62) (layer F.Fab)
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_text user %R (at 0 -1.7) (layer F.Fab)
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_line (start 9.07 -4.75) (end -4.75 -4.75) (layer F.CrtYd) (width 0.05))
(fp_line (start 9.07 4.75) (end 9.07 -4.75) (layer F.CrtYd) (width 0.05))
(fp_line (start -4.75 4.75) (end 9.07 4.75) (layer F.CrtYd) (width 0.05))
(fp_line (start -4.75 -4.75) (end -4.75 4.75) (layer F.CrtYd) (width 0.05))
(fp_line (start 4.62 0) (end 6.12 0) (layer F.SilkS) (width 0.12))
(fp_line (start 0 0) (end 7.62 0) (layer F.Fab) (width 0.1))
(fp_circle (center 0 0) (end 4.62 0) (layer F.SilkS) (width 0.12))
(fp_circle (center 0 0) (end 4.5 0) (layer F.Fab) (width 0.1))
(pad 2 thru_hole oval (at 7.62 0) (size 2.4 2.4) (drill 1.2) (layers *.Cu *.Mask)
(net 51 "Net-(R2-Pad2)"))
(pad 1 thru_hole circle (at 0 0) (size 2.4 2.4) (drill 1.2) (layers *.Cu *.Mask)
(net 52 "Net-(R2-Pad1)"))
(model ${KISYS3DMOD}/Resistor_THT.3dshapes/R_Axial_DIN0922_L20.0mm_D9.0mm_P7.62mm_Vertical.wrl
(at (xyz 0 0 0))
(scale (xyz 1 1 1))
(rotate (xyz 0 0 0))
)
)

(module Resistor_THT:R_Axial_DIN0922_L20.0mm_D9.0mm_P7.62mm_Vertical (layer F.Cu) (tedit 5AE5139B) (tstamp 5C3144A7)
(at 71.12 74.93)
(descr "Resistor, Axial_DIN0922 series, Axial, Vertical, pin pitch=7.62mm, 5W, length*diameter=20*9mm^2, http://www.vishay.com/docs/20128/wkxwrx.pdf")
(tags "Resistor Axial_DIN0922 series Axial Vertical pin pitch 7.62mm 5W length 20mm diameter 9mm")
(path /5C24FA6F)
(fp_text reference R1 (at 3.81 -5.62) (layer F.SilkS)
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_text value 10k (at 3.81 5.62) (layer F.Fab)
(effects (font (size 1 1) (thickness 0.15)))
)
(fp_circle (center 0 0) (end 4.5 0) (layer F.Fab) (width 0.1))
(fp_circle (center 0 0) (end 4.62 0) (layer F.SilkS) (width 0.12))
(fp_line (start 0 0) (end 7.62 0) (layer F.Fab) (width 0.1))
(fp_line (start 4.62 0) (end 6.12 0) (layer F.SilkS) (width 0.12))
(fp_line (start -4.75 -4.75) (end -4.75 4.75) (layer F.CrtYd) (width 0.05))
(fp_line (start -4.75 4.75) (end 9.07 4.75) (layer F.CrtYd) (width 0.05))
(fp_line (start 9.07 4.75) (end 9.07 -4.75) (layer F.CrtYd) (width 0.05))
(fp_line (start 9.07 -4.75) (end -4.75 -4.75) (layer F.CrtYd) (width 0.05))
(fp_text user %R (at 0 -1.7) (layer F.Fab)
(effects (font (size 1 1) (thickness 0.15)))
)
(pad 1 thru_hole circle (at 0 0) (size 2.4 2.4) (drill 1.2) (layers *.Cu *.Mask)
(net 49 "Net-(R1-Pad1)"))
(pad 2 thru_hole oval (at 7.62 0) (size 2.4 2.4) (drill 1.2) (layers *.Cu *.Mask)
(net 50 "Net-(R1-Pad2)"))
(model ${KISYS3DMOD}/Resistor_THT.3dshapes/R_Axial_DIN0922_L20.0mm_D9.0mm_P7.62mm_Vertical.wrl
(at (xyz 0 0 0))
(scale (xyz 1 1 1))
(rotate (xyz 0 0 0))
)
)

(module Package_DIP:DIP-40_W15.24mm (layer F.Cu) (tedit 5A02E8C5) (tstamp 5C1A0EB7)
(at 109.22 59.69 90)
(descr "40-lead though-hole mounted DIP package, row spacing 15.24 mm (600 mils)")
Expand Down
Loading

0 comments on commit 187a155

Please sign in to comment.