import os.path from operator import attrgetter import itertools from uuid import uuid4, UUID import datetime import rdflib from rdflib import Literal, URIRef # from ..kicad import rdf as kicad_rdf from . import * from ..kicad.export import * class KicadImportProjectCommand(CliCommand): def __init__(self): super().__init__("kicad-import-project", "Import a KiCAD project") def run(self, argv): p = argparse.ArgumentParser(prog=self.key, description=self.description) p.add_argument("-o", "--output-dir", dest="output_dir", required=False) p.add_argument("-i", "--input", required=False) args = p.parse_args(argv) run(args) def run(args): config = read_config() version = None timestamp = None # type: datetime.datetime if args.input is "-": src = sys.stdin else: if args.input is None: (filename, _) = os.path.splitext(config['project']['file']) filename += '.xml' else: filename = args.input if not isfile(filename): raise CliException("No such file: %s. Did you export the BOM?" % filename) src = open(filename, 'r') s = os.stat(filename) timestamp = datetime.datetime.fromtimestamp(s.st_mtime) project_url = config['project']['url'] if version is None: if timestamp is None: version = uuid4() timestamp = datetime.datetime.now() else: from random import Random r = Random(timestamp.year + timestamp.month + timestamp.day + timestamp.hour + timestamp.minute + timestamp.second + timestamp.microsecond) s = ["0123456789abcdef"[int(r.random() * 16)] for _ in range(0, 32)] version = UUID(''.join(s)) info("Version: %s, timestamp=%s" % (version, timestamp)) with src: data_url = URIRef(kicad_rdf.gen_random(timestamp)) graph = export_to_graph(src, data_url) graph.add((data_url, kicad_rdf.KICAD['version-uuid'], Literal(version))) graph.add((data_url, kicad_rdf.KICAD['version-of'], URIRef(project_url))) graph.add((data_url, kicad_rdf.KICAD['timestamp'], Literal(timestamp))) debug('Loaded %s tuples' % len(graph)) if args.output_dir is not None: parent = args.output_dir if not os.path.exists(parent): os.mkdir(parent) v = timestamp.strftime("%Y-%m-%d %H:%M:%S") output_file = os.path.join(parent, "project-%s.ttl" % v) write_graph(lambda: graph, filename=output_file, force_write=True) def import_project(g): for idx, t in enumerate(graph.triples((None, None, None))): g.add(t) with_database(import_project) def export_to_graph(src: object, url: URIRef) -> rdflib.Graph: export = Export.from_xml_file(src) # print('components:') # for c in export.components: # print(c) # for name in export.component_fields(): # cli.info(name) # cli.info('components:') # fmt = '%2s%3s' # cli.info(fmt % ('Ref', '')) components = sorted(export.components, key=attrgetter('ref')) component_count = 0 part_count = 0 has_part = 0 has_digikey = 0 for cls, cs in itertools.groupby(components, key=attrgetter('ref_class')): # cli.info('--- Class: %s ---' % cls) cs = sorted(cs, key=attrgetter('value')) for c in cs: component_count += 1 value = str(c.value) part = c.find_field('part') digikey = c.find_field('digikey') footprint = c.footprint if c.ref_class is not None: ref_class = c.ref_class ref_instance = c.ref_instance else: ref_class = c.ref ref_instance = '' # cli.info(fmt % (ref_class, ref_instance)) # cli.info(' Value: %s' % c.value.text) # if c.footprint: # cli.info(' Footprint: %s' % c.footprint) if part is not None: if part != 'NA': has_part += 1 if part is not None else 0 part_count += 1 if digikey is not None: has_digikey += 1 else: part = None else: part_count += 1 part = 'MISSING' # if part is not None: # cli.info(' Part: %s' % part) # if digikey is not None: # cli.info(' Digikey: %s' % digikey) # cli.info() # cli.info('=== Summary ===') # cli.info('Project URL: %s' % project_url) # cli.info('Number of components: %d' % component_count) # cli.info('Number of parts: %d' % part_count) # cli.info('Assigned part: %d / %2.f%%' % (has_part, (has_part / part_count) * 100)) # cli.info('Assigned digikey: %d / %2.f%%' % (has_digikey, (has_digikey / part_count) * 100)) g = create_graph(kicad=True) g.add((url, RDF.type, kicad_rdf.KICAD["project"])) # The components and fields could/should have been a BNodes. Their generated names are not very nice. # TODO: try using a hash of the current value and put that under a special generated namespace. # 'http://example.org/my-board' + ref='C10' => 'http://example.org/my-boardC10' # hash('http://example.org/my-boardC10') => 123456 # add_prefix(hash) => 'https://trygvis.io/purl/kicad/generated#123456' footprints = set() component_ns = rdflib.Namespace(str(url + "/component")) sheet_ns = rdflib.Namespace(str(url + "/sheet")) for c in export.components: node = component_ns[c.ref] g.add((url, kicad_rdf.KICAD['component'], node)) g.add((node, RDF.type, kicad_rdf.KICAD['component'])) g.add((node, RDFS.label, Literal(c.ref))) g.add((node, kicad_rdf.KICAD['value'], Literal(c.value.text))) footprint_uri = URIRef(kicad_rdf.KICAD_FOOTPRINT[quote_plus(c.footprint)]) if footprint_uri not in footprints: g.add((footprint_uri, RDF.type, kicad_rdf.KICAD['footprint'])) g.add((footprint_uri, RDFS.label, Literal(c.footprint))) footprints.add(footprint_uri) g.add((node, kicad_rdf.KICAD['footprint'], footprint_uri)) for name, value in c.fields.items(): # f = ns['%s-%s' % (c.ref, name)] f = kicad_rdf.gen_random('%s-%s' % (c.ref, name)) g.add((node, kicad_rdf.KICAD['field'], f)) g.add((f, RDF.type, kicad_rdf.KICAD['field'])) g.add((f, kicad_rdf.KICAD['field-name'], Literal(name))) g.add((f, kicad_rdf.KICAD['field-value'], Literal(value))) d = export.design if d is not None: g.add((url, kicad_rdf.KICAD['design-date'], Literal(d.date))) for s in d.sheets: label = s.title_block.title if s.title_block.title is not None and len(s.title_block.title) > 0 else s.name sheet_uri = sheet_ns[s.number] g.add((url, kicad_rdf.KICAD['sheet'], sheet_uri)) g.add((sheet_uri, RDF.type, kicad_rdf.KICAD['sheet'])) g.add((sheet_uri, RDFS.label, Literal(label))) g.add((sheet_uri, kicad_rdf.KICAD['sheet-name'], Literal(s.name))) g.add((sheet_uri, kicad_rdf.KICAD['sheet-number'], Literal(int(s.number)))) g.add((sheet_uri, kicad_rdf.KICAD['title'], Literal(s.title_block.title))) g.add((sheet_uri, kicad_rdf.KICAD['rev'], Literal(s.title_block.rev))) g.add((sheet_uri, kicad_rdf.KICAD['date'], Literal(s.title_block.date))) return g