aboutsummaryrefslogtreecommitdiff
path: root/trygvis/eda/kicad/export/__init__.py
diff options
context:
space:
mode:
authorTrygve Laugstøl <trygvis@inamo.no>2017-01-07 14:00:46 +0100
committerTrygve Laugstøl <trygvis@inamo.no>2017-01-07 14:00:46 +0100
commit0958273a71dd19c2a90471a182ccc5b90b14e5b4 (patch)
tree8e33385ca9df94b80ce9b1f8ba06438b807f137a /trygvis/eda/kicad/export/__init__.py
parent5d7fc9c4b14536006f2435b1379887f95937e096 (diff)
downloadeda-rdf-0958273a71dd19c2a90471a182ccc5b90b14e5b4.tar.gz
eda-rdf-0958273a71dd19c2a90471a182ccc5b90b14e5b4.tar.bz2
eda-rdf-0958273a71dd19c2a90471a182ccc5b90b14e5b4.tar.xz
eda-rdf-0958273a71dd19c2a90471a182ccc5b90b14e5b4.zip
Renaming 'schematic' to 'project'.
Renaming 'kicad-bom-to-ttl' to 'kicad-import-project'. Renaming 'digikey-download-for-schematic' to 'digikey-download-for-project'. Splitting out the Export xml file code into its own module. init: putting project.url and project.file in config.ini. init: putting db.update-url in config.ini if given on the command line. kicad-import-project: by default, assume that the user want to update local database, optionally write the ttl file to disk. cli.write_graph: create any missing parent directories.
Diffstat (limited to 'trygvis/eda/kicad/export/__init__.py')
-rw-r--r--trygvis/eda/kicad/export/__init__.py250
1 files changed, 250 insertions, 0 deletions
diff --git a/trygvis/eda/kicad/export/__init__.py b/trygvis/eda/kicad/export/__init__.py
new file mode 100644
index 0000000..64b82d2
--- /dev/null
+++ b/trygvis/eda/kicad/export/__init__.py
@@ -0,0 +1,250 @@
+from typing import List
+import functools
+import re
+import os.path
+from trygvis.eda import EdaException
+from trygvis.eda.kicad import rdf as kicad_rdf
+from urllib.parse import quote_plus
+import xml.etree.ElementTree
+
+
+def _clean(s):
+ if s is None:
+ return None
+ s = s.strip()
+ return None if len(s) == 0 else s
+
+
+def _cleaned_text(e, name):
+ child = e.find(name)
+ if child is None:
+ return None
+ return child.text
+
+
+def _cleaned_attr(e, name):
+ value = e.get(name)
+ if value is None:
+ return None
+ return _clean(value)
+
+
+def _iso_size_to_factor(size):
+ if size == 'k':
+ return 3
+ if size == 'M':
+ return 6
+ if size == 'm':
+ return -3
+ if size == 'u' or size == '\N{GREEK SMALL LETTER MU}' or size == '\N{MICRO SIGN}':
+ return -6
+ if size == 'n':
+ return -9
+ if size == 'p':
+ return -12
+ return None
+
+
+@functools.total_ordering
+class Value:
+ valueRe = re.compile('([0-9]+\.?[0-9]*)(.*)')
+ typeRe = re.compile('(.*)(H|Hz)$')
+
+ def __init__(self, text, value, factor, typ):
+ self.text = text
+ self.value = value
+ self.factor = factor
+ self.type = typ
+
+ def __eq__(self, other):
+ return self.text == other.text
+
+ def __lt__(self, other):
+ if self.value is not None:
+ if other.value is None:
+ return 1
+ else:
+ return (self.factor, self.value) < (other.factor, other.value)
+ else:
+ if other.value is None:
+ return self.text < other.text
+ else:
+ return -1
+
+ def __str__(self):
+ if self.value is not None:
+ s = str(self.value) + 'e' + str(self.factor)
+ if self.type is not None:
+ s = s + ' ' + self.type
+ return s
+ if self.text is not None:
+ return self.text
+ return 'unknown'
+
+ @staticmethod
+ def parse(text):
+ if text is None:
+ return Value(None, None, None, None)
+
+ m = Value.valueRe.match(text)
+ if m:
+ value_int = float(m.group(1))
+ suffix = m.group(2)
+ m = Value.typeRe.match(suffix)
+ if m:
+ suffix = m.group(1)
+ typ = m.group(2)
+ else:
+ typ = None
+
+ factor = _iso_size_to_factor(suffix)
+ # cli.info(text + ': suffix:' + suffix + ' => value_int: ' + str(value_int) + ', factor: ' + str(
+ # factor) + ', type=' + str(type))
+
+ if value_int is not None and factor is not None:
+ return Value(text, value_int, factor, typ)
+
+ return Value(text, None, None, None)
+
+
+class Component:
+ refRe = re.compile('([a-zA-Z]+)([0-9]+)')
+
+ def __init__(self, ref, value, footprint, fields):
+ self.ref = ref # type: str
+ self.value = Value.parse(value) # type: Value
+ self.footprint = footprint # type: str
+ self.fields = fields
+ m = Component.refRe.match(ref)
+ if m:
+ self.ref_class = m.group(1)
+ self.ref_instance = int(m.group(2))
+ else:
+ self.ref_class = None
+ self.ref_instance = None
+
+ def __str__(self):
+ s = 'ref=' + self.ref
+ if self.value is not None:
+ s += ', value=' + str(self.value)
+ if self.footprint is not None:
+ s += ', footprint=' + self.footprint
+ for name, value in self.fields.items():
+ s += ', ' + name + '=' + value
+ return s
+
+ def find_field(self, key):
+ return self.fields[key] if key in self.fields else None
+
+ @staticmethod
+ def from_xml(e):
+ ref = e.get('ref')
+ value = _cleaned_text(e, 'value')
+ footprint = _cleaned_text(e, 'footprint')
+ fs = {}
+
+ fields = e.find('fields')
+ if fields is not None:
+ for field in fields.findall('field'):
+ fs[field.get('name')] = field.text
+
+ if ref is not None:
+ return Component(ref, value, footprint, fs)
+
+ return None
+
+
+class TitleBlock(object):
+ def __init__(self, title=None, rev=None, date=None, source=None):
+ self.title = title
+ self.rev = rev
+ self.date = date
+ self.source = source
+
+ @staticmethod
+ def from_xml(tb):
+ title = _cleaned_text(tb, 'title')
+ rev = _cleaned_text(tb, 'rev')
+ date = _cleaned_text(tb, 'date')
+ source = _cleaned_text(tb, 'source')
+ return TitleBlock(title, rev, date, source)
+
+
+class Sheet(object):
+ def __init__(self, number, name, title_block):
+ self.number = number
+ self.name = name
+ self.title_block = title_block
+
+ @staticmethod
+ def from_xml(s):
+ number = _cleaned_attr(s, 'number')
+ name = _cleaned_attr(s, 'name')
+ node = s.find('title_block')
+ title_block = TitleBlock.from_xml(node) if node is not None else TitleBlock()
+ return Sheet(int(number) if number is not None else None, name, title_block)
+
+
+class Design(object):
+ def __init__(self, source=None, date=None, tool=None, sheets=None):
+ self.source = source
+ self.date = date
+ self.tool = tool
+ self.sheets = sheets if sheets is not None else []
+
+ @staticmethod
+ def from_xml(d):
+ source = _cleaned_text(d, 'source')
+ date = _cleaned_text(d, 'date')
+ tool = _cleaned_text(d, 'tool')
+ sheets = []
+ for s in d.iterfind('sheet'):
+ sheets.append(Sheet.from_xml(s))
+ return Design(source, date, tool, sheets)
+
+
+class Export(object):
+ def __init__(self, design, components):
+ self.design = design if design is not None else Design() # type: Design
+ self.components = components # type: List[Component]
+
+ @staticmethod
+ def from_xml_document(doc) -> 'Export':
+ cs = []
+
+ node = doc.find('design')
+ design = Design.from_xml(node) if node is not None else None
+
+ components = doc.find('components')
+ for e in components.iter('comp'):
+ c = Component.from_xml(e)
+ if c is not None:
+ cs.append(c)
+
+ return Export(design, cs)
+
+ @staticmethod
+ def from_xml_file(src) -> 'Export':
+ tree = xml.etree.ElementTree.parse(src)
+ root = tree.getroot()
+ return Export.from_xml_document(root)
+
+ def component_fields(self):
+ fs = set()
+ for c in self.components:
+ for key, value in c.fields.items():
+ fs.add(key)
+ return fs
+
+ # def generate_project_url(self):
+ # s = next((s for s in self.design.sheets if s.number == 1), None)
+ # title = s.title_block.title if s is not None else None
+ # if title is None:
+ # if self.design.source is not None:
+ # title = _clean(os.path.basename(self.design.source))
+ #
+ # if title is None:
+ # raise EdaException('Could not generate a stable, identifying URL for the project')
+ #
+ # title = quote_plus(title)
+ # return kicad_rdf.KICAD_BOARD[title]