aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ee/__init__.py261
-rw-r--r--src/ee/__main__.py69
-rw-r--r--src/ee/formatting/__init__.py132
-rw-r--r--src/ee/kicad/bom/__init__.py221
-rw-r--r--src/ee/kicad/bom/io.py81
-rw-r--r--src/ee/kicad/bom_tool/__init__.py185
-rw-r--r--src/ee/kicad/bom_tool/predef.py5
-rwxr-xr-xsrc/ee/tools/kicad_gerber.py9
-rw-r--r--src/ee/tools/read_ltspice_raw.py16
-rw-r--r--test/test_bom.py82
-rw-r--r--test/test_digikey.py26
-rw-r--r--test/test_formatting.py24
12 files changed, 578 insertions, 533 deletions
diff --git a/src/ee/__init__.py b/src/ee/__init__.py
index a22ecc6..e5519f8 100644
--- a/src/ee/__init__.py
+++ b/src/ee/__init__.py
@@ -1,145 +1,148 @@
import numpy as np
__all__ = [
- 'EeError',
- 'read_ltspice_raw'
+ 'EeError',
+ 'read_ltspice_raw'
]
+
class EeError(Exception):
pass
+
class LtSpiceRaw(object):
- def __init__(self, variables, values_first, values_rest):
- self.variables = variables
- self.values_first = values_first
- self.values_rest = values_rest
-
- def get_values(self, variable):
- return self.values[variable.idx]
-
- def get_variable(self, idx=None, expression=None):
- if idx is not None:
- return self.variables[idx]
- if expression is not None:
- v = [v for v in self.variables if v.expression == expression]
- if len(v) != 1:
- raise Exception('Unknown variable: ' + str(variable))
- return v[0]
- raise Exception('idx or expression must be given')
-
- def to_pandas(self):
- import pandas
- data = []
- for (i, v) in enumerate(self.values_first):
- data.append(pandas.Series(v, dtype='float64'))
-
- data = {}
- for (i, v) in enumerate(self.values_first):
- data[self.variables[i].expression] = v
-
- return pandas.DataFrame(data = data)
+ def __init__(self, variables, values_first, values_rest):
+ self.variables = variables
+ self.values_first = values_first
+ self.values_rest = values_rest
+
+ def get_values(self, variable):
+ return self.values[variable.idx]
+
+ def get_variable(self, idx=None, expression=None):
+ if idx is not None:
+ return self.variables[idx]
+ if expression is not None:
+ v = [v for v in self.variables if v.expression == expression]
+ if len(v) != 1:
+ raise Exception('Unknown variable: ' + str(variable))
+ return v[0]
+ raise Exception('idx or expression must be given')
+
+ def to_pandas(self):
+ import pandas
+ data = []
+ for (i, v) in enumerate(self.values_first):
+ data.append(pandas.Series(v, dtype='float64'))
+
+ data = {}
+ for (i, v) in enumerate(self.values_first):
+ data[self.variables[i].expression] = v
+
+ return pandas.DataFrame(data=data)
+
class LtSpiceVariable(object):
- def __init__(self, idx, expression, kind):
- self.idx = idx
- self.expression = expression
- self.kind = kind
+ def __init__(self, idx, expression, kind):
+ self.idx = idx
+ self.expression = expression
+ self.kind = kind
+
class LtSpiceReader(object):
+ def __init__(self, f):
+ self.f = f
+ self.mode = 'header'
+ self.headers = {}
+ self.variables = []
+ self.values = []
+
+ def read_header(self, line):
+ sep = line.find(':')
+ key = line[0:sep]
+ value = line[sep + 1:].strip()
+
+ # print("key='{}', value='{}'".format(key, str(value)))
+
+ if key == 'Binary':
+ self.set_binary_mode()
+ elif key == 'Variables':
+ self.no_variables = int(self.headers['No. Variables'])
+ self.mode = 'variables'
+ else:
+ self.headers[key] = value
+
+ def read_variable(self, line):
+ parts = line.split('\t')
+ idx = int(parts[1])
+ expression = parts[2]
+ kind = parts[3]
+ self.variables.append(LtSpiceVariable(idx, expression, kind))
+
+ if len(self.variables) == self.no_variables:
+ self.mode = 'header'
+
+ def set_binary_mode(self):
+ self.mode = 'binary'
+
+ def read_binary(self):
+ pos = self.f.tell()
+ no_points = int(self.headers['No. Points'])
+ no_variables = int(self.headers['No. Variables'])
+
+ # print("no_points={}, no_variables={}".format(no_points, no_variables))
+
+ if True:
+ self.values = np.zeros((no_variables, no_points), dtype='float32')
+
+ for p in range(no_points):
+ self.values[0][p] = np.fromfile(self.f, count=1, dtype='float64')
+ row = np.fromfile(self.f, count=no_variables - 1, dtype='float32');
+ for v in range(0, len(row)):
+ self.values[v + 1][p] = row[v]
+ else:
+ self.values = np.zeros((no_points, no_variables), dtype='float64')
+
+ for p in range(no_points):
+ self.values[p][0] = np.fromfile(self.f, count=1, dtype='float64')
+ row = np.fromfile(self.f, count=no_variables - 1, dtype='float32');
+ for v in range(0, len(row)):
+ self.values[p][v + 1] = row[v]
+
+ def read(self):
+ while True:
+ line = self.f.readline();
+
+ if len(line) == 0:
+ break
+
+ self.f.read(1) # The data is utf16 encoded so read the extra null byte
+
+ # if this wasn't the last line .readline() includes the newline
+ if line[-1] == '\n' or line[-1] == 0x0a:
+ line = line[:-1]
+
+ # print("len(line)={}, line={}".format(len(line), str(line)))
+ line = line.decode(encoding="utf-16")
+
+ if self.mode == 'header':
+ self.read_header(line)
+ elif self.mode == 'variables':
+ self.read_variable(line)
+
+ # The binary data can't be read with readline()
+ if self.mode == 'binary':
+ # self.f.read()
+ self.read_binary()
+ break
+
+ if len(self.variables) == 0:
+ raise Exception("Something didn't quite work when parsing file, no variables found")
+
+ return LtSpiceRaw(self.variables, self.values, [])
- def __init__(self, f):
- self.f = f
- self.mode = 'header'
- self.headers = {}
- self.variables = []
- self.values = []
-
- def read_header(self, line):
- sep = line.find(':')
- key = line[0:sep]
- value = line[sep + 1:].strip()
-
-# print("key='{}', value='{}'".format(key, str(value)))
-
- if key == 'Binary':
- self.set_binary_mode()
- elif key == 'Variables':
- self.no_variables = int(self.headers['No. Variables'])
- self.mode = 'variables'
- else:
- self.headers[key] = value
-
- def read_variable(self, line):
- parts = line.split('\t')
- idx = int(parts[1])
- expression = parts[2]
- kind = parts[3]
- self.variables.append(LtSpiceVariable(idx, expression, kind))
-
- if len(self.variables) == self.no_variables:
- self.mode = 'header'
-
- def set_binary_mode(self):
- self.mode = 'binary'
-
- def read_binary(self):
- pos = self.f.tell()
- no_points = int(self.headers['No. Points'])
- no_variables = int(self.headers['No. Variables'])
-
-# print("no_points={}, no_variables={}".format(no_points, no_variables))
-
- if True:
- self.values = np.zeros((no_variables, no_points), dtype='float32')
-
- for p in range(no_points):
- self.values[0][p] = np.fromfile(self.f, count=1, dtype='float64')
- row = np.fromfile(self.f, count=no_variables - 1, dtype='float32');
- for v in range(0, len(row)):
- self.values[v + 1][p] = row[v]
- else:
- self.values = np.zeros((no_points, no_variables), dtype='float64')
-
- for p in range(no_points):
- self.values[p][0] = np.fromfile(self.f, count=1, dtype='float64')
- row = np.fromfile(self.f, count=no_variables - 1, dtype='float32');
- for v in range(0, len(row)):
- self.values[p][v + 1] = row[v]
-
- def read(self):
- while True:
- line = self.f.readline();
-
- if len(line) == 0:
- break
-
- self.f.read(1) # The data is utf16 encoded so read the extra null byte
-
- # if this wasn't the last line .readline() includes the newline
- if line[-1] == '\n' or line[-1] == 0x0a:
- line = line[:-1]
-
-# print("len(line)={}, line={}".format(len(line), str(line)))
- line = line.decode(encoding="utf-16")
-
- if self.mode == 'header':
- self.read_header(line)
- elif self.mode == 'variables':
- self.read_variable(line)
-
- # The binary data can't be read with readline()
- if self.mode == 'binary':
-# self.f.read()
- self.read_binary()
- break
-
- if len(self.variables) == 0:
- raise Exception("Something didn't quite work when parsing file, no variables found")
-
- return LtSpiceRaw(self.variables, self.values, [])
def read_ltspice_raw(filename):
-
- with open(filename, mode="rb") as f:
- r = LtSpiceReader(f)
- return r.read()
+ with open(filename, mode="rb") as f:
+ r = LtSpiceReader(f)
+ return r.read()
diff --git a/src/ee/__main__.py b/src/ee/__main__.py
index 6bb40b0..ecc0a98 100644
--- a/src/ee/__main__.py
+++ b/src/ee/__main__.py
@@ -5,51 +5,56 @@ import logging
import pkgutil
import sys
+
def eprint(*args, **kwargs):
- print(*args, file=sys.stderr, **kwargs)
+ print(*args, file=sys.stderr, **kwargs)
+
@total_ordering
class Tool(object):
- def __init__(self, module_name, name):
- self.module_name = module_name
- self.name = name
+ def __init__(self, module_name, name):
+ self.module_name = module_name
+ self.name = name
+
+ def __eq__(self, other):
+ return self.name == other.name
- def __eq__(self, other):
- return self.name == other.name
+ def __lt__(self, other):
+ return self.name < other.name
- def __lt__(self, other):
- return self.name < other.name
def find_tools():
- prefix = ee.tools.__name__ + '.'
- ps = pkgutil.walk_packages(ee.tools.__path__, prefix)
- tools = []
- for (module_loader, module_name, ispkg) in ps:
- name = module_name.replace(prefix, '').replace('_', '-')
- tools.append(Tool(module_name, name))
- return sorted(tools)
+ prefix = ee.tools.__name__ + '.'
+ ps = pkgutil.walk_packages(ee.tools.__path__, prefix)
+ tools = []
+ for (module_loader, module_name, ispkg) in ps:
+ name = module_name.replace(prefix, '').replace('_', '-')
+ tools.append(Tool(module_name, name))
+ return sorted(tools)
+
def main():
- logging.basicConfig() # you need to initialize logging, otherwise you will not see anything from requests
- logging.getLogger().setLevel(logging.DEBUG)
- requests_log = logging.getLogger("requests.packages.urllib3")
- requests_log.setLevel(logging.DEBUG)
- requests_log.propagate = True
+ logging.basicConfig() # you need to initialize logging, otherwise you will not see anything from requests
+ logging.getLogger().setLevel(logging.DEBUG)
+ requests_log = logging.getLogger("requests.packages.urllib3")
+ requests_log.setLevel(logging.DEBUG)
+ requests_log.propagate = True
+
+ tools = find_tools()
- tools = find_tools()
+ name = sys.argv[1]
+ del sys.argv[1]
- name = sys.argv[1]
- del sys.argv[1]
+ for t in tools:
+ if t.name != name:
+ continue
+ sys.argv[0] = t.name
+ importlib.import_module(t.module_name)
+ exit(0)
- for t in tools:
- if t.name != name:
- continue
- sys.argv[0] = t.name
- importlib.import_module(t.module_name)
- exit(0)
+ eprint("No such tool: {}".format(name))
+ exit(1)
- eprint("No such tool: {}".format(name))
- exit(1)
if __name__ == "__main__":
- main()
+ main()
diff --git a/src/ee/formatting/__init__.py b/src/ee/formatting/__init__.py
index 42bf48d..32eaf64 100644
--- a/src/ee/formatting/__init__.py
+++ b/src/ee/formatting/__init__.py
@@ -6,42 +6,45 @@ __all__ = [
'eng_str',
]
+
class ESeries(object):
- def __init__(self, series):
- self.series = series
+ def __init__(self, series):
+ self.series = series
- def closest(self, value):
- e = math.floor(math.log10(value))
- value = float(value/(10**e))
+ def closest(self, value):
+ e = math.floor(math.log10(value))
+ value = float(value / (10 ** e))
+
+ return min(self.series, key=lambda v: abs(v - value)) * 10 ** e
- return min(self.series, key=lambda v: abs(v - value)) * 10**e
# https://en.wikipedia.org/wiki/E-series_of_preferred_numbers
-_e_series_24 = [1.0, 1.1, 1.2, 1.3, 1.5, 1.6, 1.8, 2.0, 2.2, 2.4, 2.7, 3.0, 3.3, 3.6, 3.9, 4.3, 4.7, 5.1, 5.6, 6.2, 6.8, 7.5, 8.2, 9.1]
+_e_series_24 = [1.0, 1.1, 1.2, 1.3, 1.5, 1.6, 1.8, 2.0, 2.2, 2.4, 2.7, 3.0,
+ 3.3, 3.6, 3.9, 4.3, 4.7, 5.1, 5.6, 6.2, 6.8, 7.5, 8.2, 9.1]
_e_series_12 = [v for i, v in enumerate(_e_series_24) if i % 2 == 0]
_e_series_6 = [v for i, v in enumerate(_e_series_24) if i % 4 == 0]
_e_series_192 = [
- 1.00, 1.01, 1.02, 1.04, 1.05, 1.06, 1.07, 1.09, 1.10, 1.11,
- 1.13, 1.14, 1.15, 1.17, 1.18, 1.20, 1.21, 1.23, 1.24, 1.26,
- 1.27, 1.29, 1.30, 1.32, 1.33, 1.35, 1.37, 1.38, 1.40, 1.42,
- 1.43, 1.45, 1.47, 1.49, 1.50, 1.52, 1.54, 1.56, 1.58, 1.60,
- 1.62, 1.64, 1.65, 1.67, 1.69, 1.72, 1.74, 1.76, 1.78, 1.80,
- 1.82, 1.84, 1.87, 1.89, 1.91, 1.93, 1.96, 1.98, 2.00, 2.03,
- 2.05, 2.08, 2.10, 2.13, 2.15, 2.18, 2.21, 2.23, 2.26, 2.29,
- 2.32, 2.34, 2.37, 2.40, 2.43, 2.46, 2.49, 2.52, 2.55, 2.58,
- 2.61, 2.64, 2.67, 2.71, 2.74, 2.77, 2.80, 2.84, 2.87, 2.91,
- 2.94, 2.98, 3.01, 3.05, 3.09, 3.12, 3.16, 3.20, 3.24, 3.28,
- 3.32, 3.36, 3.40, 3.44, 3.48, 3.52, 3.57, 3.61, 3.65, 3.70,
- 3.74, 3.79, 3.83, 3.88, 3.92, 3.97, 4.02, 4.07, 4.12, 4.17,
- 4.22, 4.27, 4.32, 4.37, 4.42, 4.48, 4.53, 4.59, 4.64, 4.70,
- 4.75, 4.81, 4.87, 4.93, 4.99, 5.05, 5.11, 5.17, 5.23, 5.30,
- 5.36, 5.42, 5.49, 5.56, 5.62, 5.69, 5.76, 5.83, 5.90, 5.97,
- 6.04, 6.12, 6.19, 6.26, 6.34, 6.42, 6.49, 6.57, 6.65, 6.73,
- 6.81, 6.90, 6.98, 7.06, 7.15, 7.23, 7.32, 7.41, 7.50, 7.59,
- 7.68, 7.77, 7.87, 7.96, 8.06, 8.16, 8.25, 8.35, 8.45, 8.56,
- 8.66, 8.76, 8.87, 8.98, 9.09, 9.20, 9.31, 9.42, 9.53, 9.65,
- 9.76, 9.88,
+ 1.00, 1.01, 1.02, 1.04, 1.05, 1.06, 1.07, 1.09, 1.10, 1.11,
+ 1.13, 1.14, 1.15, 1.17, 1.18, 1.20, 1.21, 1.23, 1.24, 1.26,
+ 1.27, 1.29, 1.30, 1.32, 1.33, 1.35, 1.37, 1.38, 1.40, 1.42,
+ 1.43, 1.45, 1.47, 1.49, 1.50, 1.52, 1.54, 1.56, 1.58, 1.60,
+ 1.62, 1.64, 1.65, 1.67, 1.69, 1.72, 1.74, 1.76, 1.78, 1.80,
+ 1.82, 1.84, 1.87, 1.89, 1.91, 1.93, 1.96, 1.98, 2.00, 2.03,
+ 2.05, 2.08, 2.10, 2.13, 2.15, 2.18, 2.21, 2.23, 2.26, 2.29,
+ 2.32, 2.34, 2.37, 2.40, 2.43, 2.46, 2.49, 2.52, 2.55, 2.58,
+ 2.61, 2.64, 2.67, 2.71, 2.74, 2.77, 2.80, 2.84, 2.87, 2.91,
+ 2.94, 2.98, 3.01, 3.05, 3.09, 3.12, 3.16, 3.20, 3.24, 3.28,
+ 3.32, 3.36, 3.40, 3.44, 3.48, 3.52, 3.57, 3.61, 3.65, 3.70,
+ 3.74, 3.79, 3.83, 3.88, 3.92, 3.97, 4.02, 4.07, 4.12, 4.17,
+ 4.22, 4.27, 4.32, 4.37, 4.42, 4.48, 4.53, 4.59, 4.64, 4.70,
+ 4.75, 4.81, 4.87, 4.93, 4.99, 5.05, 5.11, 5.17, 5.23, 5.30,
+ 5.36, 5.42, 5.49, 5.56, 5.62, 5.69, 5.76, 5.83, 5.90, 5.97,
+ 6.04, 6.12, 6.19, 6.26, 6.34, 6.42, 6.49, 6.57, 6.65, 6.73,
+ 6.81, 6.90, 6.98, 7.06, 7.15, 7.23, 7.32, 7.41, 7.50, 7.59,
+ 7.68, 7.77, 7.87, 7.96, 8.06, 8.16, 8.25, 8.35, 8.45, 8.56,
+ 8.66, 8.76, 8.87, 8.98, 9.09, 9.20, 9.31, 9.42, 9.53, 9.65,
+ 9.76, 9.88,
]
_e_series_96 = [v for i, v in enumerate(_e_series_192) if i % 2 == 0]
_e_series_48 = [v for i, v in enumerate(_e_series_192) if i % 4 == 0]
@@ -53,46 +56,49 @@ e48 = ESeries(_e_series_48)
e96 = ESeries(_e_series_96)
e192 = ESeries(_e_series_192)
-def e_series_find_closest(value):
- e = math.floor(math.log10(value))
- value = float(value/(10**e))
-
- return min(series, key=lambda v: abs(v - value)) * 10**e
-
-import numpy
-def eng_str(value, unit = None):
- if value == 0:
- s = '0' + (' ' + unit if unit is not None else '')
- else:
- big = ['k', 'M', 'G', 'T']
- small = ['m', 'u', 'p']
+def e_series_find_closest(value):
+ e = math.floor(math.log10(value))
+ value = float(value / (10 ** e))
- if unit is not None:
- big = [' ' + unit] + [' ' + x + unit for x in big]
- small = [' ' + unit] + [' ' + x + unit for x in small]
- else:
- big = [''] + [' ' + x for x in big]
- small = [''] + [' ' + x for x in small]
+ return min(series, key=lambda v: abs(v - value)) * 10 ** e
- e_floor = math.floor(math.log10(value))
- div3 = math.floor(e_floor/3)
- if div3 > 0:
- suffixes = big
- idx = int(div3)
- else:
- suffixes = small
- idx = int(-div3)
+import numpy
- suffix = suffixes[idx]
- scale = 10**(div3*3)
- scaled = value/scale
- if (scaled - math.floor(scaled)) < 0.0001:
-# if scaled == math.floor(scaled):
- s = "{:1.0f}{}".format(scaled, suffix)
+def eng_str(value, unit=None):
+ if value == 0:
+ s = '0' + (' ' + unit if unit is not None else '')
else:
- s = "{:1.1f}{}".format(scaled, suffix)
-
- return s
+ big = ['k', 'M', 'G', 'T']
+ small = ['m', 'u', 'p']
+
+ if unit is not None:
+ big = [' ' + unit] + [' ' + x + unit for x in big]
+ small = [' ' + unit] + [' ' + x + unit for x in small]
+ else:
+ big = [''] + [' ' + x for x in big]
+ small = [''] + [' ' + x for x in small]
+
+ e_floor = math.floor(math.log10(value))
+
+ div3 = math.floor(e_floor / 3)
+ if div3 > 0:
+ suffixes = big
+ idx = int(div3)
+ else:
+ suffixes = small
+ idx = int(-div3)
+
+ suffix = suffixes[idx]
+ scale = 10 ** (div3 * 3)
+ scaled = value / scale
+
+ if (scaled - math.floor(scaled)) < 0.0001:
+ # if scaled == math.floor(scaled):
+ s = "{:1.0f}{}".format(scaled, suffix)
+ else:
+ s = "{:1.1f}{}".format(scaled, suffix)
+
+ return s
diff --git a/src/ee/kicad/bom/__init__.py b/src/ee/kicad/bom/__init__.py
index d0798a4..2a32521 100644
--- a/src/ee/kicad/bom/__init__.py
+++ b/src/ee/kicad/bom/__init__.py
@@ -2,124 +2,131 @@ import re
import sys
__all__ = [
- 'Part',
- 'Library',
- 'Bom',
- 'Comp',
- 'split_ref',
+ 'Part',
+ 'Library',
+ 'Bom',
+ 'Comp',
+ 'split_ref',
]
+
def split_ref(ref):
- """Split "C12" into a tuple that's useful for sorting by component reference.
-
- For example: "C12" => ("C", 12, None). "Cfoo" => ("C", sys.maxsize, "").
- """
- m = split_ref.r.match(ref)
- if not m:
- return (ref, sys.maxsize, "")
- groups = m.groups()
- ref = groups[0]
- val = groups[1]
- rest = groups[2]
- try:
- return (ref, int(val), rest)
- except ValueError:
- pass
-
- return (ref, val, rest)
-split_ref.r = re.compile("([A-Za-z]+)([0-9]+)(.*)")
+ """Split "C12" into a tuple that's useful for sorting by component reference.
+
+ For example: "C12" => ("C", 12, None). "Cfoo" => ("C", sys.maxsize, "").
+ """
+ m = split_ref.r.match(ref)
+ if not m:
+ return (ref, sys.maxsize, "")
+ groups = m.groups()
+ ref = groups[0]
+ val = groups[1]
+ rest = groups[2]
+ try:
+ return (ref, int(val), rest)
+ except ValueError:
+ pass
-class Part:
- def __init__(self, name):
- self.name = name
+ return (ref, val, rest)
-class Library:
- def __init__(self, name):
- self.name = name
- self.parts = {}
- def add_part(self, part):
- try:
- return self.parts[part]
- except KeyError:
- p = Part(part)
- self.parts[part] = p
- return p
+split_ref.r = re.compile("([A-Za-z]+)([0-9]+)(.*)")
-class Bom(object):
- def __init__(self):
- self.libraries = {}
- self._components = {}
- def add_component(self, component):
- self._components[component.ref] = component
+class Part:
+ def __init__(self, name):
+ self.name = name
- def get_component(self, name):
- return self._components[name]
- def get_components(self):
- return self._components
+class Library:
+ def __init__(self, name):
+ self.name = name
+ self.parts = {}
- def all_field_names(self):
- fields = set(['ref', 'value'])
- for c in self._components.values():
- for f in c.fields:
- fields.add(f)
- return fields
+ def add_part(self, part):
+ try:
+ return self.parts[part]
+ except KeyError:
+ p = Part(part)
+ self.parts[part] = p
+ return p
+
+
+class Bom(object):
+ def __init__(self):
+ self.libraries = {}
+ self._components = {}
+
+ def add_component(self, component):
+ self._components[component.ref] = component
+
+ def get_component(self, name):
+ return self._components[name]
+
+ def get_components(self):
+ return self._components
+
+ def all_field_names(self):
+ fields = set(['ref', 'value'])
+ for c in self._components.values():
+ for f in c.fields:
+ fields.add(f)
+ return fields
+
+ def find_library(self, name):
+ try:
+ return self.libraries[name]
+ except KeyError:
+ lib = Library(name)
+ self.libraries[name] = lib
+ return lib
+
+ def to_pandas(self, ref_field_name=None, value_field_name=None):
+ import pandas
+
+ ref_field_name = ref_field_name or "ref"
+ value_field_name = value_field_name or "value"
+
+ fields = self.all_field_names()
+ data = {k: [] for k in fields}
+ refs = []
+ values = []
+ for ref, c in self.get_components().items():
+ refs.append(c.ref)
+ values.append(c.value)
+ for field in fields:
+ data[field].append(c[field] if field in c else None)
+
+ # del data[ref_field_name]
+ data[ref_field_name] = refs
+ data[value_field_name] = values
+ return pandas.DataFrame(data=data, index=refs)
- def find_library(self, name):
- try:
- return self.libraries[name]
- except KeyError:
- lib = Library(name)
- self.libraries[name] = lib
- return lib
-
- def to_pandas(self, ref_field_name = None, value_field_name = None):
- import pandas
-
- ref_field_name = ref_field_name or "ref"
- value_field_name = value_field_name or "value"
-
- fields = self.all_field_names()
- data = {k: [] for k in fields}
- refs = []
- values = []
- for ref, c in self.get_components().items():
- refs.append(c.ref)
- values.append(c.value)
- for field in fields:
- data[field].append(c[field] if field in c else None)
-
-# del data[ref_field_name]
- data[ref_field_name] = refs
- data[value_field_name] = values
- return pandas.DataFrame(data=data, index=refs)
class Comp:
- def __init__(self, ref, value, library, part, footprint):
- self.ref = ref
- self.value = value
- self.footprint = footprint
- self.library = library
- self.part = part
- self.fields = {}
-
- def add_field(self, key, value):
- self.fields[key] = value
-
- def __contains__(self, key):
- if key == 'ref':
- return self.ref is not None
- elif key == 'value':
- return self.value is not None
- else:
- return key in self.fields
-
- def __getitem__(self, key):
- if key == 'ref':
- return self.ref
- elif key == 'value':
- return self.value
- else:
- return self.fields[key]
+ def __init__(self, ref, value, library, part, footprint):
+ self.ref = ref
+ self.value = value
+ self.footprint = footprint
+ self.library = library
+ self.part = part
+ self.fields = {}
+
+ def add_field(self, key, value):
+ self.fields[key] = value
+
+ def __contains__(self, key):
+ if key == 'ref':
+ return self.ref is not None
+ elif key == 'value':
+ return self.value is not None
+ else:
+ return key in self.fields
+
+ def __getitem__(self, key):
+ if key == 'ref':
+ return self.ref
+ elif key == 'value':
+ return self.value
+ else:
+ return self.fields[key]
diff --git a/src/ee/kicad/bom/io.py b/src/ee/kicad/bom/io.py
index c7eef44..d7c0367 100644
--- a/src/ee/kicad/bom/io.py
+++ b/src/ee/kicad/bom/io.py
@@ -1,44 +1,45 @@
import xml.etree.ElementTree as ElementTree
from ee.kicad.bom import *
+
def read_bom(path):
- def child_text(e, child_tag):
- child = e.find(child_tag)
-
- return child.text if child is not None else None
-
- def add_comp(b, comp):
- ref = comp.get("ref")
- value = child_text(comp, "value")
- footprint = child_text(comp, "footprint")
- libsource = comp.find("libsource")
- l, p = (None, None)
- if libsource is not None:
- lib = libsource.get("lib")
- part = libsource.get("part")
- if lib is not None and part is not None:
- l = b.find_library(lib)
- p = l.add_part(part)
-
- c = Comp(ref, value, l, p, footprint)
-
- fields = comp.find("fields")
- if fields is not None:
- for f in fields.findall("field"):
- key = f.get("name")
- value = f.text
- c.add_field(key, value)
-
- b.add_component(c)
- return c
-
- with open(path) as f:
- tree = ElementTree.parse(path)
- root = tree.getroot()
-
- b = Bom()
- comp = root.find("components").findall("comp")
- for c in comp:
- add_comp(b, c)
-
- return b
+ def child_text(e, child_tag):
+ child = e.find(child_tag)
+
+ return child.text if child is not None else None
+
+ def add_comp(b, comp):
+ ref = comp.get("ref")
+ value = child_text(comp, "value")
+ footprint = child_text(comp, "footprint")
+ libsource = comp.find("libsource")
+ l, p = (None, None)
+ if libsource is not None:
+ lib = libsource.get("lib")
+ part = libsource.get("part")
+ if lib is not None and part is not None:
+ l = b.find_library(lib)
+ p = l.add_part(part)
+
+ c = Comp(ref, value, l, p, footprint)
+
+ fields = comp.find("fields")
+ if fields is not None:
+ for f in fields.findall("field"):
+ key = f.get("name")
+ value = f.text
+ c.add_field(key, value)
+
+ b.add_component(c)
+ return c
+
+ with open(path) as f:
+ tree = ElementTree.parse(path)
+ root = tree.getroot()
+
+ b = Bom()
+ comp = root.find("components").findall("comp")
+ for c in comp:
+ add_comp(b, c)
+
+ return b
diff --git a/src/ee/kicad/bom_tool/__init__.py b/src/ee/kicad/bom_tool/__init__.py
index 9f450c9..f5d2e4f 100644
--- a/src/ee/kicad/bom_tool/__init__.py
+++ b/src/ee/kicad/bom_tool/__init__.py
@@ -3,108 +3,115 @@ import functools
import itertools
import pandas as pd
+
def _none_if_empty(l):
- return l if l is not None and len(l) > 0 else None
+ return l if l is not None and len(l) > 0 else None
+
class Supplier():
- def __init__(self, name, bom_field = None):
- self._name = name
- self._bom_field = bom_field if not None else name
+ def __init__(self, name, bom_field=None):
+ self._name = name
+ self._bom_field = bom_field if not None else name
+
+ @property
+ def bom_field(self):
+ return self._bom_field
- @property
- def bom_field(self):
- return self._bom_field
class Settings():
- def __init__(self, suppliers = None, part_field = None):
- self._suppliers = suppliers if suppliers is not None else []
- self._part_field = part_field if part_field is not None else 'part'
+ def __init__(self, suppliers=None, part_field=None):
+ self._suppliers = suppliers if suppliers is not None else []
+ self._part_field = part_field if part_field is not None else 'part'
- @property
- def suppliers(self):
- return self._suppliers
+ @property
+ def suppliers(self):
+ return self._suppliers
- @property
- def part_field(self):
- return self._part_field
+ @property
+ def part_field(self):
+ return self._part_field
+
+ @part_field.setter
+ def part_field(self, value):
+ self._part_field = value
- @part_field.setter
- def part_field(self, value):
- self._part_field = value
class CsvFormat():
- def __init__(self, supplier = None, group_by_fields = None, required_fields = None):
- self._supplier = supplier
- self._group_by_fields = _none_if_empty(group_by_fields)
- self._required_fields = _none_if_empty(required_fields)
+ def __init__(self, supplier=None, group_by_fields=None, required_fields=None):
+ self._supplier = supplier
+ self._group_by_fields = _none_if_empty(group_by_fields)
+ self._required_fields = _none_if_empty(required_fields)
+
+ @property
+ def supplier(self):
+ return self._supplier
- @property
- def supplier(self):
- return self._supplier
+ @property
+ def group_by_fields(self):
+ return self._group_by_fields
- @property
- def group_by_fields(self):
- return self._group_by_fields
+ @property
+ def required_fields(self):
+ return self._required_fields
- @property
- def required_fields(self):
- return self._required_fields
def _all(fs, c):
- for f in fs:
- if not f(c):
- return False
- return True
-
-def to_panda(bom, bom_settings, csv_format): # type: (Bom, BomSettings, CsvFormat) -> None
- filters = []
-
- print("csv_format.supplier.bom_field={}".format(csv_format.supplier.bom_field))
- if csv_format.supplier is not None:
- filters.append(lambda c: csv_format.supplier.bom_field in c)
-
- if csv_format.group_by_fields is not None:
- filters.append(lambda c: all([field in c for field in csv_format.group_by_fields]))
-
- if csv_format.required_fields is not None:
- filters.append(lambda c: all([field in c for field in csv_format.required_fields]))
-
- f = functools.partial(_all, filters)
- print("len(filters)={}".format(len(filters)))
- print("len(bom.get_components())={}".format(len(bom.get_components())))
-
- filtered = [c for ref, c in bom.get_components().items() if f(c)]
- print("filtered:, len={}".format(len(filtered)))
- filtered.sort(key=lambda c: c.ref)
- print("sorted filtered:, len={}".format(len(filtered)))
-
- counts=None
- parts=[]
-
- frame = {}
- for n in bom.all_field_names():
- frame[n] = []
-
- if csv_format.group_by_fields is not None:
- counts, parts, refs=([],[],[])
- for group, comps in itertools.groupby(filtered, key=lambda c: [field for field in c for c in csv_format.group_by_fields]):
- gs = list(group)
- cs = list(comps)
-# counts.append(len(cs))
-# dpns.append(part)
-# mpns.append(part)
-# refs.append(functools.reduce(lambda a, b: a + ' ' + b, [c.ref for c in cs], ''))
- print("group={}".format(gs))
-
-# return pd.DataFrame(data={
-# 'count': counts,
-# 'mpn': mpns,
-# 'dpn': dpns,
-# 'refs': refs,
-# })
- else:
- for ref, c in filtered:
- for key, value in c:
- frame[key] = value
-
- return pd.DataFrame(data=frame)
+ for f in fs:
+ if not f(c):
+ return False
+ return True
+
+
+def to_panda(bom, bom_settings, csv_format): # type: (Bom, BomSettings, CsvFormat) -> None
+ filters = []
+
+ print("csv_format.supplier.bom_field={}".format(csv_format.supplier.bom_field))
+ if csv_format.supplier is not None:
+ filters.append(lambda c: csv_format.supplier.bom_field in c)
+
+ if csv_format.group_by_fields is not None:
+ filters.append(lambda c: all([field in c for field in csv_format.group_by_fields]))
+
+ if csv_format.required_fields is not None:
+ filters.append(lambda c: all([field in c for field in csv_format.required_fields]))
+
+ f = functools.partial(_all, filters)
+ print("len(filters)={}".format(len(filters)))
+ print("len(bom.get_components())={}".format(len(bom.get_components())))
+
+ filtered = [c for ref, c in bom.get_components().items() if f(c)]
+ print("filtered:, len={}".format(len(filtered)))
+ filtered.sort(key=lambda c: c.ref)
+ print("sorted filtered:, len={}".format(len(filtered)))
+
+ counts = None
+ parts = []
+
+ frame = {}
+ for n in bom.all_field_names():
+ frame[n] = []
+
+ if csv_format.group_by_fields is not None:
+ counts, parts, refs = ([], [], [])
+ for group, comps in itertools.groupby(filtered,
+ key=lambda c: [field for field in c for c in csv_format.group_by_fields]):
+ gs = list(group)
+ cs = list(comps)
+ # counts.append(len(cs))
+ # dpns.append(part)
+ # mpns.append(part)
+ # refs.append(functools.reduce(lambda a, b: a + ' ' + b, [c.ref for c in cs], ''))
+ print("group={}".format(gs))
+
+ # return pd.DataFrame(data={
+ # 'count': counts,
+ # 'mpn': mpns,
+ # 'dpn': dpns,
+ # 'refs': refs,
+ # })
+ else:
+ for ref, c in filtered:
+ for key, value in c:
+ frame[key] = value
+
+ return pd.DataFrame(data=frame)
diff --git a/src/ee/kicad/bom_tool/predef.py b/src/ee/kicad/bom_tool/predef.py
index 717f6d3..bfe8631 100644
--- a/src/ee/kicad/bom_tool/predef.py
+++ b/src/ee/kicad/bom_tool/predef.py
@@ -1,6 +1,7 @@
from ee.kicad.bom_tool import *
-digikey = Supplier('Digi-Key', bom_field = 'digikey')
+digikey = Supplier('Digi-Key', bom_field='digikey')
+
def digikeyCsvFormat(digikey_supplier):
- return CsvFormat(supplier = digikey, group_by_fields = [digikey_supplier.bom_field])
+ return CsvFormat(supplier=digikey, group_by_fields=[digikey_supplier.bom_field])
diff --git a/src/ee/tools/kicad_gerber.py b/src/ee/tools/kicad_gerber.py
index e8604b8..be0e34d 100755
--- a/src/ee/tools/kicad_gerber.py
+++ b/src/ee/tools/kicad_gerber.py
@@ -164,11 +164,14 @@ for plan in plot_plan:
if args.detect_files_only:
for plan in plot_plan:
- print plan.filename
+ print
+ plan.filename
if args.protel_extensions:
- print drlFileOut
- print drlNpthFileOut
+ print
+ drlFileOut
+ print
+ drlNpthFileOut
sys.exit(0)
# Set some important plot options:
diff --git a/src/ee/tools/read_ltspice_raw.py b/src/ee/tools/read_ltspice_raw.py
index 279fe73..f04abb0 100644
--- a/src/ee/tools/read_ltspice_raw.py
+++ b/src/ee/tools/read_ltspice_raw.py
@@ -3,24 +3,26 @@ import sys
raw = ee.read_ltspice_raw(sys.argv[1])
-#print("Variables:")
-#for i, v in enumerate(raw.variables):
+# print("Variables:")
+# for i, v in enumerate(raw.variables):
# print("{:2}: kind: {:20} expression: {}".format(i, v.kind, v.expression))
-#for i, v in enumerate(raw.variables):
+# for i, v in enumerate(raw.variables):
# print("{:2}: kind: {:20} expression: {}".format(i, v.kind, v.expression))
# for p in raw.values[i]:
# print(" {}".format(p))
-x = raw.get_variable(idx = 0)
-y = raw.get_variable(expression = 'V(load)')
+x = raw.get_variable(idx=0)
+y = raw.get_variable(expression='V(load)')
xs = raw.get_values(y)
import matplotlib
-matplotlib.use('Agg')
+
+matplotlib.use('Agg')
import matplotlib.pyplot as plt
from matplotlib.ticker import FuncFormatter, MaxNLocator
+
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(xs)
@@ -28,4 +30,4 @@ ax.set_xlabel(x.expression)
ax.set_ylabel(y.expression)
with open("ltspice.png", "wb") as f:
- plt.savefig(f, format="png")
+ plt.savefig(f, format="png")
diff --git a/test/test_bom.py b/test/test_bom.py
index 6da7798..fd8c6c6 100644
--- a/test/test_bom.py
+++ b/test/test_bom.py
@@ -7,58 +7,64 @@ from ee.kicad.bom.io import read_bom
basedir = os.path.dirname(os.path.abspath(__file__))
+
@pytest.mark.parametrize("s, ref, val, rest", [
("C12", "C", 12, ""),
("C12n", "C", 12, "n"),
("C", "C", sys.maxsize, ""),
("Foo", "Foo", sys.maxsize, ""),
("+3.0VA1", "+3.0VA1", sys.maxsize, ""),
- ])
+])
def test_split_ref(s, ref, val, rest):
- assert split_ref(s) == (ref, val, rest)
+ assert split_ref(s) == (ref, val, rest)
+
def test_read_bom_1():
- bom = read_bom(basedir + '/../demo/kicad/bom/A64-OlinuXino_Rev_C.xml')
- assert len(bom.get_components()) == 425
+ bom = read_bom(basedir + '/../demo/kicad/bom/A64-OlinuXino_Rev_C.xml')
+ assert len(bom.get_components()) == 425
+
def test_read_bom_2():
- bom = read_bom(basedir + '/../demo/kicad/bom/gw.xml')
- assert len(bom.get_components()) == 165
+ bom = read_bom(basedir + '/../demo/kicad/bom/gw.xml')
+ assert len(bom.get_components()) == 165
+
+ r5 = bom.get_component("R5")
+ assert r5.ref == "R5"
+ assert r5.value == "R0402_100R"
+ assert r5["value"] == "R0402_100R"
+ assert r5.footprint == "Resistors_SMD:R_0402"
+ assert r5.library.name == "gw-cache"
+ assert len(r5.fields) == 4
+ assert r5.fields["Part Number"] == "CRCW0402100RFKED"
+ assert r5["Part Number"] == "CRCW0402100RFKED"
+ assert set(
+ ['ref', 'value', 'Capacitance', 'Color', 'Description', 'Frequency', 'Impedance', 'Inductance', 'Manufacturer',
+ 'Part Number', 'Resistance']) == bom.all_field_names()
- r5 = bom.get_component("R5")
- assert r5.ref == "R5"
- assert r5.value == "R0402_100R"
- assert r5["value"] == "R0402_100R"
- assert r5.footprint == "Resistors_SMD:R_0402"
- assert r5.library.name == "gw-cache"
- assert len(r5.fields) == 4
- assert r5.fields["Part Number"] == "CRCW0402100RFKED"
- assert r5["Part Number"] == "CRCW0402100RFKED"
- assert set(['ref', 'value', 'Capacitance', 'Color', 'Description', 'Frequency', 'Impedance', 'Inductance', 'Manufacturer', 'Part Number', 'Resistance']) == bom.all_field_names()
+ assert not "foo" in r5
+ with pytest.raises(KeyError):
+ r5["foo"]
- assert not "foo" in r5
- with pytest.raises(KeyError):
- r5["foo"]
def test_read_bom_2_pandas():
- bom = read_bom(basedir + '/../demo/kicad/bom/gw.xml').to_pandas()
- assert len(bom) == 165
+ bom = read_bom(basedir + '/../demo/kicad/bom/gw.xml').to_pandas()
+ assert len(bom) == 165
- print("bom")
- print(str(bom))
- r5 = bom.loc["R5"]
- print(str(r5.index))
-# assert r5.index == "R5"
- assert r5["ref"] == "R5"
- assert r5["value"] == "R0402_100R"
- assert r5["value"] == "R0402_100R"
-# assert r5["footprint"] == "Resistors_SMD:R_0402"
-# assert r5.library.name == "gw-cache"
-# assert len(r5.fields) == 4
-# assert r5.fields["Part Number"] == "CRCW0402100RFKED"
- assert r5["Part Number"] == "CRCW0402100RFKED"
-# assert set(['ref', 'value', 'Capacitance', 'Color', 'Description', 'Frequency', 'Impedance', 'Inductance', 'Manufacturer', 'Part Number', 'Resistance']) == bom.all_field_names()
+ print("bom")
+ print(str(bom))
+ r5 = bom.loc["R5"]
+ print(str(r5.index))
+ # assert r5.index == "R5"
+ assert r5["ref"] == "R5"
+ assert r5["value"] == "R0402_100R"
+ assert r5["value"] == "R0402_100R"
+ # assert r5["footprint"] == "Resistors_SMD:R_0402"
+ # assert r5.library.name == "gw-cache"
+ # assert len(r5.fields) == 4
+ # assert r5.fields["Part Number"] == "CRCW0402100RFKED"
+ assert r5["Part Number"] == "CRCW0402100RFKED"
+ # assert set(['ref', 'value', 'Capacitance', 'Color', 'Description', 'Frequency', 'Impedance', 'Inductance', 'Manufacturer', 'Part Number', 'Resistance']) == bom.all_field_names()
- assert not "foo" in r5
- with pytest.raises(KeyError):
- r5["foo"]
+ assert not "foo" in r5
+ with pytest.raises(KeyError):
+ r5["foo"]
diff --git a/test/test_digikey.py b/test/test_digikey.py
index 0b79777..6073e25 100644
--- a/test/test_digikey.py
+++ b/test/test_digikey.py
@@ -10,19 +10,19 @@ client = dk.DigikeyClient(digikey)
def test_digikey_1():
- p = client.search("TCR2LF18LM(CTTR-ND")
- assert isinstance(p, dk.DigikeyProduct)
- assert p.part_number == "TCR2LF18LM(CTTR-ND"
- assert len(p.attributes) > 5
- x = p.to_yaml()
- print(type(x))
- print("{}".format(x))
- yaml.dump(x, sys.stdout)
+ p = client.search("TCR2LF18LM(CTTR-ND")
+ assert isinstance(p, dk.DigikeyProduct)
+ assert p.part_number == "TCR2LF18LM(CTTR-ND"
+ assert len(p.attributes) > 5
+ x = p.to_yaml()
+ print(type(x))
+ print("{}".format(x))
+ yaml.dump(x, sys.stdout)
def test_digikey_2():
- response = client.search("TCR2LF")
- [print(p.part_id) for p in response.products]
- assert len(response.products) == 28
- # p = products[0]
- # assert p.part_number == "TCR2LF18LM(CTTR-ND"
+ response = client.search("TCR2LF")
+ [print(p.part_id) for p in response.products]
+ assert len(response.products) == 28
+ # p = products[0]
+ # assert p.part_number == "TCR2LF18LM(CTTR-ND"
diff --git a/test/test_formatting.py b/test/test_formatting.py
index 6fa0b07..18f582c 100644
--- a/test/test_formatting.py
+++ b/test/test_formatting.py
@@ -2,6 +2,7 @@ import pytest
import numpy as np
from ee.formatting import eng_str
+
@pytest.mark.parametrize("input,expected", [
(0, "0"),
(5.5, "5.5"),
@@ -18,29 +19,32 @@ from ee.formatting import eng_str
(0.000055, "55 u"),
(0.0000055, "5.5 u"),
(0.00000055, "550 p"),
- ])
+])
def test_eng_str(input, expected):
- assert expected == eng_str(input)
- npinput = np.float64(input)
- assert expected == eng_str(npinput)
+ assert expected == eng_str(input)
+ npinput = np.float64(input)
+ assert expected == eng_str(npinput)
+
@pytest.mark.parametrize("input,expected", [
(0, "0 A"),
(5.5, "5.5 A"),
(1100, "1.1 kA"),
(0.05, "50 mA"),
- ])
+])
def test_eng_str_with_unit(input, expected):
- assert expected == eng_str(input, unit = 'A')
+ assert expected == eng_str(input, unit='A')
+
@pytest.mark.parametrize("input,expected", [
(100, ''), (101, ''), (102, ''), (103, ''), (104, ''), (105, ''), (106, ''), (107, ''), (108, ''), (109, ''),
(110, ''), (111, ''), (112, ''), (113, ''), (114, ''), (115, ''), (116, ''), (117, ''), (118, ''), (119, ''),
(120, ''), (121, ''), (122, ''), (123, ''), (124, ''), (125, ''), (126, ''), (127, ''), (128, ''), (129, ''),
(130, ''), (131, ''), (132, ''), (133, ''), (134, ''), (135, ''), (136, ''), (137, ''), (138, ''), (139, ''),
- ])
+])
def xx_test_eng_str2(input, expected):
- assert expected == eng_str(input)
+ assert expected == eng_str(input)
+
@pytest.mark.parametrize("input,expected", [
(10, '10'),
@@ -53,6 +57,6 @@ def xx_test_eng_str2(input, expected):
(17, '17'),
(18, '18'),
(19, '19'),
- ])
+])
def xx_test_eng_str3(input, expected):
- assert expected == eng_str(input)
+ assert expected == eng_str(input)