Skip to content

Commit

Permalink
Merge pull request #1 from INTI-CMNB/drc_errors
Browse files Browse the repository at this point in the history
DRC/ERC error filters closes johnbeard#18
  • Loading branch information
set-soft committed Jun 11, 2020
2 parents 46248a6 + 157e23c commit cef002b
Show file tree
Hide file tree
Showing 9 changed files with 534 additions and 30 deletions.
30 changes: 10 additions & 20 deletions .github/workflows/pythonapp.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,27 +5,17 @@ name: Python application

on:
push:
branches: [ master ]
tags-ignore:
- "v*"
paths:
- '**.py'
- 'src/kiplot'
- 'tests/**'
- '.github/workflows/pythonapp.yml'
pull_request:
branches: [ master ]

# on:
# push:
# paths:
# - '**.py'
# - 'src/kiplot'
# - 'tests/**'
# - '.github/workflows/pythonapp.yml'
# tags-ignore:
# - "v*"
# pull_request:
# paths:
# - '**.py'
# - 'src/kiplot'
# - 'tests/**'
# - '.github/workflows/pythonapp.yml'
paths:
- '**.py'
- 'src/kiplot'
- 'tests/**'
- '.github/workflows/pythonapp.yml'

jobs:
test:
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
### Added
- Tolerate config files without outputs
- Mechanism to filter ERC/DRC errors

### Fixed
- All pcbnew plot formats generated gerber job files
Expand Down
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,53 @@ preflight:
ignore_unconnected: false
```

### Filtering DRC/ERC errors

Sometimes KiCad reports DRC or ERC errors that you can't get rid off. This could be just because you are part of a team including lazzy people that doesn't want to take the extra effort to solve some errors that aren't in fact errors, just small violations made on purpose. In this case you could exclude some known errors.

For this you must declare `filters` entry in the `preflight` section. Then you can add as many `filter` entries as you want. Each filter entry has an optional description and defines to which error type is applied (`number`) and a regular expression that the error must match to be ignored (`regex`). Like this:

```
filters:
- filter: 'Optional filter description'
number: Numeric_error_type
regex: 'Expression to match'
```

Here is an example, suppose you are getting the following errors:

```
** Found 1 DRC errors **
ErrType(4): Track too close to pad
@(177.185 mm, 78.315 mm): Track 1.000 mm [Net-(C3-Pad1)] on F.Cu, length: 1.591 mm
@(177.185 mm, 80.715 mm): Pad 2 of C3 on F.Cu and others
** Found 1 unconnected pads **
ErrType(2): Unconnected items
@(177.185 mm, 73.965 mm): Pad 2 of C4 on F.Cu and others
@(177.185 mm, 80.715 mm): Pad 2 of C3 on F.Cu and others
```

And you want to ignore them. You can add the following filters:

```
filters:
- filter: 'Ignore C3 pad 2 too close to anything'
number: 4
regex: 'Pad 2 of C3'
- filter: 'Ignore unconnected pad 2 of C4'
number: 2
regex: 'Pad 2 of C4'
```

If you need to match text from two different lines in the error message try using `(?s)TEXT(.*)TEXT_IN_OTHER_LINE`.

If you have two or more different options for a text to match try using `(OPTION1|OPTION2)`.

A complete Python regular expressions explanation is out the scope of this manual. For a complete reference consult the [Python manual](https://docs.python.org/3/library/re.html).

**Important note**: this will create a file named *kiplot_errors.filter* in the output directory.

### The *outputs* section

In this section you put all the things that you want to generate. This section contains one or more **outputs**. Each output contain the following data:
Expand Down
23 changes: 23 additions & 0 deletions kiplot/config_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -537,6 +537,26 @@ def _parse_output(self, o_obj):

return o_cfg

def _parse_filters(self, filters, cfg):
for filter in filters:
if 'filter' in filter:
comment = filter['filter']
if 'number' in filter:
number = filter['number']
if number is None:
config_error("empty 'number' in 'filter' definition ("+str(filter)+")")
else:
config_error("missing 'number' for 'filter' definition ("+str(filter)+")")
if 'regex' in filter:
regex = filter['regex']
if regex is None:
config_error("empty 'regex' in 'filter' definition ("+str(filter)+")")
else:
config_error("missing 'regex' for 'filter' definition ("+str(filter)+")")
cfg.add_filter(comment, number, regex)
else:
config_error("'filters' section of 'preflight' must contain 'filter' definitions (not "+str(filter)+")")

def _parse_preflight(self, pf, cfg):

logger.debug("Parsing preflight options: {}".format(pf))
Expand All @@ -556,6 +576,9 @@ def _parse_preflight(self, pf, cfg):
if 'ignore_unconnected' in pf:
cfg.ignore_unconnected = pf['ignore_unconnected']

if 'filters' in pf:
self._parse_filters(pf['filters'], cfg)

def read(self, fstream):
"""
Read a file object into a config object
Expand Down
30 changes: 20 additions & 10 deletions kiplot/kiplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def check_script(cmd, url, version=None):


def check_eeschema_do(file):
check_script(misc.CMD_EESCHEMA_DO, misc.URL_EESCHEMA_DO, '1.1.1')
check_script(misc.CMD_EESCHEMA_DO, misc.URL_EESCHEMA_DO, '1.4.0')
sch_file = os.path.splitext(file)[0] + '.sch'
if not os.path.isfile(sch_file):
logger.error('Missing schematic file: ' + sch_file)
Expand Down Expand Up @@ -157,17 +157,26 @@ def _preflight_checks(self, brd_file, skip_pre):
else:
logger.error('Unknown action to skip: '+skip)
exit(misc.EXIT_BAD_ARGS)
# Create the filters file
filter_file = None
if (self.cfg.run_erc or self.cfg.run_drc) and self.cfg.filters:
filter_file = os.path.join(self.cfg.outdir, 'kiplot_errors.filter')
with open(filter_file, 'w') as f:
f.write(self.cfg.filters)
if self.cfg.run_erc:
self._run_erc(brd_file)
self._run_erc(brd_file, filter_file)
if self.cfg.update_xml:
self._update_xml(brd_file)
if self.cfg.run_drc:
self._run_drc(brd_file, self.cfg.ignore_unconnected,
self.cfg.check_zone_fills)
self.cfg.check_zone_fills, filter_file)

def _run_erc(self, brd_file):
def _run_erc(self, brd_file, filter_file):
sch_file = check_eeschema_do(brd_file)
cmd = [misc.CMD_EESCHEMA_DO, 'run_erc', sch_file, self.cfg.outdir]
cmd = [misc.CMD_EESCHEMA_DO, 'run_erc']
if filter_file:
cmd.extend(['-f', filter_file])
cmd.extend([sch_file, self.cfg.outdir])
# If we are in verbose mode enable debug in the child
if logger.getEffectiveLevel() <= logging.DEBUG:
cmd.insert(1, '-vv')
Expand Down Expand Up @@ -196,9 +205,12 @@ def _update_xml(self, brd_file):
logger.error('Failed to update the BoM, error %d', ret)
exit(misc.BOM_ERROR)

def _run_drc(self, brd_file, ignore_unconnected, check_zone_fills):
check_script(misc.CMD_PCBNEW_RUN_DRC, misc.URL_PCBNEW_RUN_DRC, '1.3.1')
cmd = [misc.CMD_PCBNEW_RUN_DRC, 'run_drc', brd_file, self.cfg.outdir]
def _run_drc(self, brd_file, ignore_unconnected, check_zone_fills, filter_file):
check_script(misc.CMD_PCBNEW_RUN_DRC, misc.URL_PCBNEW_RUN_DRC, '1.4.0')
cmd = [misc.CMD_PCBNEW_RUN_DRC, 'run_drc']
if filter_file:
cmd.extend(['-f', filter_file])
cmd.extend([brd_file, self.cfg.outdir])
# If we are in verbose mode enable debug in the child
if logger.getEffectiveLevel() <= logging.DEBUG:
cmd.insert(1, '-vv')
Expand Down Expand Up @@ -407,8 +419,6 @@ def _do_position_plot_ascii(self, board, plot_ctrl, output, columns,
modulesStr, maxSizes):
to = output.options.type_options
outdir = plot_ctrl.GetPlotOptions().GetOutputDirectory()
if not os.path.exists(outdir):
os.makedirs(outdir)
name = os.path.splitext(os.path.basename(board.GetFileName()))[0]

topf = None
Expand Down
12 changes: 12 additions & 0 deletions kiplot/plot_config.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
import pcbnew

from . import error
from . import log

logger = log.get_logger(__name__)


class KiPlotConfigurationError(error.KiPlotError):
Expand Down Expand Up @@ -504,10 +507,19 @@ def __init__(self):
self.update_xml = False
self.ignore_unconnected = False
self.run_erc = False
self.filters = None

def add_output(self, new_op):
self._outputs.append(new_op)

def add_filter(self, comment, number, regex):
logger.debug("Adding DRC/ERC filter '{}','{}','{}'".format(comment, number, regex))
if self.filters is None:
self.filters = ''
if comment:
self.filters += '# '+comment+'\n'
self.filters += '{},{}\n'.format(number, regex)

def validate(self):
errs = []
for o in self._outputs:
Expand Down
Loading

0 comments on commit cef002b

Please sign in to comment.