def generate_netlist_component(self, tool=None): """ Generate the part information for inclusion in a netlist. Args: tool: The format for the netlist file (e.g., KICAD). """ import skidl if tool is None: tool = skidl.get_default_tool() # Create part value as a string so including it in netlist isn't a problem. self.value_str = self._value_to_str() try: gen_func = getattr(self, "_gen_netlist_comp_{}".format(tool)) except AttributeError: log_and_raise( logger, ValueError, "Can't generate netlist in an unknown ECAD tool format ({}).".format( tool ), ) return gen_func()
def generate_xml_net(self, tool=None): """ Generate the net information for inclusion in an XML file. Args: tool: The format for the XML file (e.g., KICAD). """ import skidl if tool is None: tool = skidl.get_default_tool() self.test_validity() # Don't add anything to the XML if no pins are on this net. if not self.get_pins(): return try: gen_func = getattr(self, '_gen_xml_net_{}'.format(tool)) return gen_func() except AttributeError: logger.error( "Can't generate XML in an unknown ECAD tool format ({}).". format(tool)) raise Exception
def generate_netlist_net(self, tool=None): """ Generate the net information for inclusion in a netlist. Args: tool: The format for the netlist file (e.g., KICAD). """ import skidl if tool is None: tool = skidl.get_default_tool() self.test_validity() # Don't add anything to the netlist if no pins are on this net. if not self.get_pins(): return try: gen_func = getattr(self, "_gen_netlist_net_{}".format(tool)) return gen_func() except AttributeError: log_and_raise( logger, ValueError, "Can't generate netlist in an unknown ECAD tool format ({}).". format(tool), )
def generate_netlist(self, **kwargs): """ Return a netlist and also write it to a file/stream. Args: file_: Either a file object that can be written to, or a string containing a file name, or None. tool: The EDA tool the netlist will be generated for. do_backup: If true, create a library with all the parts in the circuit. Returns: A netlist. """ # Reset the counters to clear any warnings/errors from previous run. logger.error.reset() logger.warning.reset() # Before anything else, clean-up names for multi-segment nets. self._merge_net_names() import skidl # Extract arguments: # Get EDA tool the netlist will be generated for. # Get file the netlist will be stored in (if any). # Get flag controlling the generation of a backup library. tool = kwargs.pop('tool', skidl.get_default_tool()) file_ = kwargs.pop('file_', None) do_backup = kwargs.pop('do_backup', True) try: gen_func = getattr(self, '_gen_netlist_{}'.format(tool)) netlist = gen_func(**kwargs) # Pass any remaining arguments. except KeyError: logger.error( "Can't generate netlist in an unknown ECAD tool format ({}).". format(tool)) raise Exception if (logger.error.count, logger.warning.count) == (0, 0): sys.stderr.write( '\nNo errors or warnings found during netlist generation.\n\n') else: sys.stderr.write( '\n{} warnings found during netlist generation.\n'.format( logger.warning.count)) sys.stderr.write( '{} errors found during netlist generation.\n\n'.format( logger.error.count)) with opened(file_ or (get_script_name() + '.net'), 'w') as f: f.write(str(netlist)) if do_backup: self.backup_parts() # Create a new backup lib for the circuit parts. global backup_lib # Clear out any old backup lib so the new one backup_lib = None # will get reloaded when it's needed. return netlist
def generate_xml_net(self, tool=None): """ Generate the net information for inclusion in an XML file. Args: tool: The format for the XML file (e.g., KICAD). """ import skidl if tool is None: tool = skidl.get_default_tool() self.test_validity() # Don't add anything to the XML if no pins are on this net. if not self.get_pins(): return try: gen_func = getattr(self, '_gen_xml_net_{}'.format(tool)) return gen_func() except AttributeError: logger.error( "Can't generate XML in an unknown ECAD tool format ({}).". format(tool)) raise Exception
def generate_netlist_net(self, tool=None): """NO_CONNECT nets don't generate anything for netlists.""" import skidl if tool is None: tool = skidl.get_default_tool() return ''
def generate_netlist(self, **kwargs): """ Return a netlist and also write it to a file/stream. Args: file_: Either a file object that can be written to, or a string containing a file name, or None. tool: The EDA tool the netlist will be generated for. do_backup: If true, create a library with all the parts in the circuit. Returns: A netlist. """ # Before anything else, clean-up names for multi-segment nets. self._merge_net_names() import skidl # Extract arguments: # Get EDA tool the netlist will be generated for. # Get file the netlist will be stored in (if any). # Get flag controlling the generation of a backup library. tool = kwargs.pop('tool', skidl.get_default_tool()) file_ = kwargs.pop('file_', None) do_backup = kwargs.pop('do_backup', True) try: gen_func = getattr(self, '_gen_netlist_{}'.format(tool)) netlist = gen_func(**kwargs) # Pass any remaining arguments. except KeyError: logger.error( "Can't generate netlist in an unknown ECAD tool format ({}).". format(tool)) raise Exception if (logger.error.count, logger.warning.count) == (0, 0): sys.stderr.write( '\nNo errors or warnings found during netlist generation.\n\n') else: sys.stderr.write( '\n{} warnings found during netlist generation.\n'.format( logger.warning.count)) sys.stderr.write( '{} errors found during netlist generation.\n\n'.format( logger.error.count)) with opened(file_ or (get_script_name() + '.net'), 'w') as f: f.write(str(netlist)) if do_backup: self.backup_parts( ) # Create a new backup lib for the circuit parts. global backup_lib # Clear out any old backup lib so the new one backup_lib = None # will get reloaded when it's needed. return netlist
def generate_netlist_net(self, tool=None): """NO_CONNECT nets don't generate anything for netlists.""" import skidl if tool is None: tool = skidl.get_default_tool() return ''
def generate_xml(self, file_=None, tool=None): """ Return netlist as an XML string and also write it to a file/stream. Args: file_: Either a file object that can be written to, or a string containing a file name, or None. Returns: A string containing the netlist. """ # Reset the counters to clear any warnings/errors from previous run. logger.error.reset() logger.warning.reset() # Before anything else, clean-up names for multi-segment nets. self._merge_net_names() import skidl if tool is None: tool = skidl.get_default_tool() try: gen_func = getattr(self, "_gen_xml_{}".format(tool)) netlist = gen_func() except KeyError: log_and_raise( logger, ValueError, "Can't generate XML in an unknown ECAD tool format ({}).". format(tool), ) if (logger.error.count, logger.warning.count) == (0, 0): sys.stderr.write( "\nNo errors or warnings found during XML generation.\n\n") else: sys.stderr.write( "\n{} warnings found during XML generation.\n".format( logger.warning.count)) sys.stderr.write( "{} errors found during XML generation.\n\n".format( logger.error.count)) if not self.no_files: with opened(file_ or (get_script_name() + ".xml"), "w") as f: f.write(netlist) return netlist
def __init__(self, filename=None, tool=None, lib_section=None, **attribs): """ Load the parts from a library file. """ import skidl if tool is None: tool = skidl.get_default_tool() # Library starts off empty of parts. self.parts = [] # Attach attributes to the library. for k, v in list(attribs.items()): setattr(self, k, v) # If no filename, create an empty library. if not filename: pass # Load this SchLib with an existing SchLib object if the file name # matches one in the cache. elif filename in self._cache: self.__dict__.update(self._cache[filename].__dict__) # Otherwise, load from a schematic library file. else: try: # Use the tool name to find the function for loading the library. load_func = getattr(self, "_load_sch_lib_{}".format(tool)) except AttributeError: # OK, that didn't work so well... log_and_raise( logger, ValueError, "Unsupported ECAD tool library: {}.".format(tool), ) else: load_func(filename, skidl.lib_search_paths[tool], lib_section=lib_section) self.filename = filename # Cache a reference to the library. self._cache[filename] = self
def generate_xml(self, file_=None, tool=None): """ Return netlist as an XML string and also write it to a file/stream. Args: file_: Either a file object that can be written to, or a string containing a file name, or None. Returns: A string containing the netlist. """ # Before anything else, clean-up names for multi-segment nets. self._merge_net_names() import skidl if tool is None: tool = skidl.get_default_tool() try: gen_func = getattr(self, '_gen_xml_{}'.format(tool)) netlist = gen_func() except KeyError: logger.error( "Can't generate XML in an unknown ECAD tool format ({}).". format(tool)) raise Exception if (logger.error.count, logger.warning.count) == (0, 0): sys.stderr.write( '\nNo errors or warnings found during XML generation.\n\n') else: sys.stderr.write( '\n{} warnings found during XML generation.\n'.format( logger.warning.count)) sys.stderr.write( '{} errors found during XML generation.\n\n'.format( logger.error.count)) with opened(file_ or (get_script_name() + '.xml'), 'w') as f: f.write(netlist) return netlist
def generate_xml(self, file_=None, tool=None): """ Return netlist as an XML string and also write it to a file/stream. Args: file_: Either a file object that can be written to, or a string containing a file name, or None. Returns: A string containing the netlist. """ # Before anything else, clean-up names for multi-segment nets. self._merge_net_names() import skidl if tool is None: tool = skidl.get_default_tool() try: gen_func = getattr(self, '_gen_xml_{}'.format(tool)) netlist = gen_func() except KeyError: logger.error( "Can't generate XML in an unknown ECAD tool format ({}).". format(tool)) raise Exception if (logger.error.count, logger.warning.count) == (0, 0): sys.stderr.write( '\nNo errors or warnings found during XML generation.\n\n') else: sys.stderr.write( '\n{} warnings found during XML generation.\n'.format( logger.warning.count)) sys.stderr.write( '{} errors found during XML generation.\n\n'.format( logger.error.count)) with opened(file_ or (get_script_name() + '.xml'), 'w') as f: f.write(netlist) return netlist
def show_footprint(lib, module_name, tool=None): """ Print the pads for a given module in a library. Args: lib: The name of a library. module_name: The name of the footprint in the library. tool: The ECAD tool format for the library. Returns: A Part object. """ import skidl if tool is None: tool = skidl.get_default_tool() os.environ["KISYSMOD"] = os.pathsep.join(skidl.footprint_search_paths[tool]) return pym.Module.from_library(lib, module_name)
def generate_xml_component(self, tool=None): """ Generate the part information for inclusion in an XML file. Args: tool: The format for the XML file (e.g., KICAD). """ import skidl if tool is None: tool = skidl.get_default_tool() try: gen_func = getattr(self, '_gen_xml_comp_{}'.format(tool)) return gen_func() except AttributeError: logger.error( "Can't generate XML in an unknown ECAD tool format ({}).". format(tool)) raise Exception
def generate_xml_component(self, tool=None): """ Generate the part information for inclusion in an XML file. Args: tool: The format for the XML file (e.g., KICAD). """ import skidl if tool is None: tool = skidl.get_default_tool() try: gen_func = getattr(self, '_gen_xml_comp_{}'.format(tool)) return gen_func() except AttributeError: logger.error( "Can't generate XML in an unknown ECAD tool format ({}).". format(tool)) raise Exception
def __init__(self, filename=None, tool=None, **attribs): """ Load the parts from a library file. """ import skidl if tool is None: tool = skidl.get_default_tool() # Library starts off empty of parts. self.parts = [] # Attach attributes to the library. for k, v in attribs.items(): setattr(self, k, v) # If no filename, create an empty library. if not filename: pass # Load this SchLib with an existing SchLib object if the file name # matches one in the cache. elif filename in self._cache: self.__dict__.update(self._cache[filename].__dict__) # Otherwise, load from a schematic library file. else: try: # Use the tool name to find the function for loading the library. load_func = getattr(self, '_load_sch_lib_{}'.format(tool)) except AttributeError: # OK, that didn't work so well... logger.error('Unsupported ECAD tool library: {}.'.format(tool)) raise Exception else: load_func(filename, skidl.lib_search_paths[tool]) self.filename = filename # Cache a reference to the library. self._cache[filename] = self
def generate_svg_component(self, symtx="", tool=None, net_stubs=None): """ Generate the SVG for displaying a part in an SVG schematic. """ import skidl if tool is None: tool = skidl.get_default_tool() try: gen_func = getattr(self, "_gen_svg_comp_{}".format(tool)) except AttributeError: log_and_raise( logger, ValueError, "Can't generate SVG for a component in an unknown ECAD tool format({}).".format( tool ), ) return gen_func(symtx=symtx, net_stubs=net_stubs)
def generate_pinboxes(self, tool=None): """ Generate the pinboxes for arranging parts in a schematic. """ import skidl if tool is None: tool = skidl.get_default_tool() try: gen_func = getattr(self, "_gen_pinboxes_{}".format(tool)) except AttributeError: log_and_raise( logger, ValueError, "Can't generate pinboxes for a component in an unknown ECAD tool format({}).".format( tool ), ) return gen_func()
def show_part(lib, part_name, tool=None): """ Print the I/O pins for a given part in a library. Args: lib: Either a SchLib object or the name of a library. part_name: The name of the part in the library. tool: The ECAD tool format for the library. Returns: A Part object. """ import skidl from .Part import Part from .defines import TEMPLATE if tool is None: tool = skidl.get_default_tool() try: return Part(lib, re.escape(part_name), tool=tool, dest=TEMPLATE) except Exception: return None
def show(lib, part_name, tool=None): """ Print the I/O pins for a given part in a library. Args: lib: Either a SchLib object or the name of a library. part_name: The name of the part in the library. tool: The ECAD tool format for the library. Returns: A Part object. """ import skidl from .Part import Part from .defines import TEMPLATE if tool is None: tool = skidl.get_default_tool() try: return Part( lib, re.escape(part_name), tool=tool, dest=TEMPLATE) except Exception: return None
def __init__(self, lib=None, name=None, dest=NETLIST, tool=None, connections=None, part_defn=None, circuit=None, **attribs): import skidl from .SchLib import SchLib super().__init__() if tool is None: tool = skidl.get_default_tool() # Setup some part attributes that might be overwritten later on. self.do_erc = True # Allow part to be included in ERC. self.unit = { } # Dictionary for storing subunits of the part, if desired. self.pins = [] # Start with no pins, but a place to store them. self.p = PinNumberSearch( self) # Does pin search using only pin numbers. self.n = PinNameSearch(self) # Does pin search using only pin names. self.name = name # Assign initial part name. self.description = "" # Make sure there is a description, even if empty. self._ref = "" # Provide a member for holding a reference. self.ref_prefix = "" # Provide a member for holding the part reference prefix. self.tool = tool # Initial type of part (SKIDL, KICAD, etc.) self.circuit = None # Part starts off unassociated with any circuit. self.match_pin_substring = False # Only select pins with exact name matches. # Create a Part from a library entry. if lib: # If the lib argument is a string, then create a library using the # string as the library file name. if isinstance(lib, basestring): libname = lib try: lib = SchLib(filename=libname, tool=tool) except FileNotFoundError as e: if skidl.QUERY_BACKUP_LIB: logger.warning( 'Could not load KiCad schematic library "{}", falling back to backup library.' .format(libname)) lib = skidl.load_backup_lib() if not lib: raise e else: raise e # Make a copy of the part from the library but don't add it to the netlist. part = lib[name].copy(dest=TEMPLATE) # Overwrite self with the new part. self.__dict__.update(part.__dict__) # Replace the fields with a copy that points to self. self.fields = part.fields.copy(attr_obj=self) # Make sure all the pins have a valid reference to this part. self.associate_pins() # Store the library name of this part. self.lib = getattr(lib, "filename", None) # Otherwise, create a Part from a part definition. If the part is # destined for a library, then just get its name. If it's going into # a netlist, then parse the entire part definition. elif part_defn: self.part_defn = part_defn self.parse(get_name_only=(dest != NETLIST)) # If the part is destined for a SKiDL library, then it will be defined # by the additional attribute values that are passed. elif tool == SKIDL and name: pass else: log_and_raise( logger, ValueError, "Can't make a part without a library & part name or a part definition.", ) # If the part is going to be an element in a circuit, then add it to the # the circuit and make any indicated pin/net connections. if dest != LIBRARY: # If no Circuit object is given, then use the default Circuit that always exists. circuit = circuit or default_circuit if dest == NETLIST: circuit += self elif dest == TEMPLATE: # If this is just a part template, don't add the part to the circuit. # Just place the reference to the Circuit object in the template. self.circuit = circuit # Add any net/pin connections to this part that were passed as arguments. if isinstance(connections, dict): for pin, net in list(connections.items()): net += self[pin] # Add any XSPICE I/O as pins. (This only happens with SPICE simulations.) self.add_xspice_io(attribs.pop("io", [])) # Add any other passed-in attributes to the part. for k, v in list(attribs.items()): setattr(self, k, v)
def search(term, tool=None): """ Print a list of components with the regex term within their name, alias, description or keywords. """ import skidl from .SchLib import SchLib def search_libraries(term, tool): """Search for a regex term in part libraries.""" # Set of parts and their containing libraries found with the term. parts = set() for lib_dir in skidl.lib_search_paths[tool]: # Get all the library files in the search path. try: lib_files = os.listdir(lib_dir) except (FileNotFoundError, OSError): logger.warning("Could not open directory '{}'".format(lib_dir)) lib_files = list() # Empty list since library directory was not found. lib_files = [l for l in lib_files if l.endswith(skidl.lib_suffixes[tool])] for lib_file in lib_files: print(" " * 79, "\rSearching {} ...".format(lib_file), end="\r") lib = SchLib( os.path.join(lib_dir, lib_file), tool=tool ) # Open the library file. def mk_list(l): """Make a list out of whatever is given.""" if isinstance(l, (list, tuple)): return l if not l: return [] return [l] # Search the current library for parts with the given term in # each of the these categories. for category in ["name", "aliases", "description", "keywords"]: for part in mk_list( lib.get_parts(use_backup_lib=False, **{category: term}) ): # Parse the part to instantiate the complete object. part.parse(get_name_only=True) # Store the library name and part object. parts.add((lib_file, part)) print(" " * 79, end="\r") # Return the list of parts and their containing libraries. return list(parts) if tool is None: tool = skidl.get_default_tool() term = ".*" + term + ".*" # Use the given term as a substring. parts = search_libraries(term, tool) # Search for parts with that substring. # Print each part name sorted by the library where it was found. for lib_file, p in sorted(parts, key=lambda p: p[0]): print( "{}: {} ({})".format( lib_file, getattr(p, "name", "???"), getattr(p, "description", "???") ) )
def search_parts_iter(terms, tool=None): """Return a list of (lib, part) sequences that match a regex term.""" import skidl from .SchLib import SchLib if tool is None: tool = skidl.get_default_tool() terms = parse_search_terms(terms) def mk_list(l): """Make a list out of whatever is given.""" if isinstance(l, (list, tuple)): return l if not l: return [] return [l] # Gather all the lib files from all the directories in the search paths. lib_files = list() for lib_dir in skidl.lib_search_paths[tool]: # Get all the library files in the search path. try: files = os.listdir(lib_dir) except (FileNotFoundError, OSError): logger.warning("Could not open directory '{}'".format(lib_dir)) files = [] files = [(lib_dir, l) for l in files if l.endswith(skidl.lib_suffixes[tool])] lib_files.extend(files) num_lib_files = len(lib_files) # Now search through the lib files for parts that match the search terms. for idx, (lib_dir, lib_file) in enumerate(lib_files): # If just entered a new lib file, yield the name of the file and # where it is within the total number of files to search. # (This is used for progress indicators.) yield "LIB", lib_file, idx + 1, num_lib_files # Parse the lib file to create a part library. lib = SchLib( os.path.join(lib_dir, lib_file), tool=tool ) # Open the library file. # Search the current library for parts with the given terms. for part in mk_list( # Get any matching parts from the library file. lib.get_parts(use_backup_lib=False, search_text=terms) ): # Parse the part to instantiate the complete object. part.parse(get_name_only=True) # Yield the part and its containing library. yield "PART", lib_file, part, part.name # Also return aliases. for alias in list(part.aliases): yield "PART", lib_file, part, alias
def search_footprints_iter(terms, tool=None): """Return a list of (lib, footprint) sequences that match a regex term.""" import skidl if tool is None: tool = skidl.get_default_tool() terms = parse_search_terms(terms) # If the cache isn't valid, then make it valid by gathering all the # footprint files from all the directories in the search paths. if not footprint_cache.valid: footprint_cache.clear() for path in skidl.footprint_search_paths[tool]: footprint_cache.load(os.path.join(path, "fp-lib-table")) # Get the number of footprint libraries to be searched.. num_fp_libs = len(footprint_cache) # Now search through the libraries for footprints that match the search terms. for idx, fp_lib in enumerate(footprint_cache): # If just entered a new library, yield the name of the lib and # where it is within the total number of libs to search. # (This is used for progress indicators.) yield "LIB", fp_lib, idx + 1, num_fp_libs # Get path to library directory and dict of footprint modules. path = footprint_cache[fp_lib]["path"] modules = footprint_cache[fp_lib]["modules"] # Search each module in the library. for module_name in modules: # If the cache isn't valid, then read each footprint file and store # it's contents in the cache. if not footprint_cache.valid: file = os.path.join(path, module_name + ".kicad_mod") with open(file, "r") as fp: try: # Remove any linefeeds that would interfere with fullmatch() later on. modules[module_name] = [l.rstrip() for l in fp.readlines()] except UnicodeDecodeError: try: modules[module_name] = [ l.decode("utf-8").rstrip() for l in fp.readlines() ] except AttributeError: modules[module_name] = "" # Get the contents of the footprint file from the cache. module_text = tuple(modules[module_name]) # Count the pads so it can be added to the text being searched. # Join all the module text lines, search for the number of # occurrences of "(pad", and then count them. # A set is used so pads with the same num/name are only counted once. # Place the pad count before everything else so the space that # terminates it won't be stripped off later. This is necessary # so (for example) "#pads=20 " won't match "#pads=208". num_pads = len( set(re.findall(r"\(\s*pad\s+([^\s)]+)", " ".join(module_text))) ) num_pads_str = "#pads={}".format(num_pads) # Create a string with the module name, library name, number of pads, # description and tags. search_text = "\n".join([num_pads_str, fp_lib, module_name]) for line in module_text: if "(descr " in line or "(tags " in line: search_text = "\n".join([search_text, line]) # Search the string for a match with the search terms. if fullmatch( terms, search_text, flags=re.IGNORECASE | re.MULTILINE | re.DOTALL ): yield "MODULE", fp_lib, module_text, module_name # At the end, all modules have been scanned and the footprint cache is valid. footprint_cache.valid = True
def search(term, tool=None): """ Print a list of components with the regex term within their name, alias, description or keywords. """ import skidl from .SchLib import SchLib def search_libraries(term, tool): """Search for a regex term in part libraries.""" parts = set( ) # Set of parts and their containing libraries found with the term. for lib_dir in skidl.lib_search_paths[tool]: print('lib_dir = {}'.format(lib_dir)) # Get all the library files in the search path. try: lib_files = os.listdir(lib_dir) except (FileNotFoundError, OSError): logger.warning("Could not open directory '{}'".format(lib_dir)) lib_files = list( ) # Empty list since library directory was not found. lib_files = [ l for l in lib_files if l.endswith(skidl.lib_suffixes[tool]) ] for lib_file in lib_files: print( ' ' * 79, '\rSearching {} ...'.format(lib_file), end='\r') lib = SchLib( os.path.join(lib_dir, lib_file), tool=tool) # Open the library file. def mk_list(l): """Make a list out of whatever is given.""" if isinstance(l, (list, tuple)): return l if not l: return [] return [l] # Search the current library for parts with the given term in # each of the these categories. for category in ['name', 'alias', 'description', 'keywords']: for part in mk_list(lib.get_parts(**{category: term})): # Parse the part to instantiate the complete object. part.parse() # Store the library name and part object. parts.add((lib_file, part)) print(' ' * 79, end='\r') # Return the list of parts and their containing libraries. return list(parts) if tool is None: tool = skidl.get_default_tool() term = '.*' + term + '.*' # Use the given term as a substring. parts = search_libraries(term, tool) # Search for parts with that substring. # Print each part name sorted by the library where it was found. for lib_file, p in sorted(parts, key=lambda p: p[0]): print('{}: {} ({})'.format(lib_file, getattr(p, 'name', '???'), getattr(p, 'description', '???')))
def __init__(self, lib=None, name=None, dest=NETLIST, tool=None, connections=None, part_defn=None, circuit=None, **attribs): import skidl from .SchLib import SchLib from .defines import TEMPLATE, NETLIST, LIBRARY, SKIDL super(Part, self).__init__() if tool is None: tool = skidl.get_default_tool() # Setup some part attributes that might be overwritten later on. self.do_erc = True # Allow part to be included in ERC. self.unit = { } # Dictionary for storing subunits of the part, if desired. self.pins = [] # Start with no pins, but a place to store them. self.name = name # Assign initial part name. (Must come after circuit is assigned.) self.description = '' # Make sure there is a description, even if empty. self._ref = '' # Provide a member for holding a reference. self.ref_prefix = '' # Provide a member for holding the part reference prefix. self.tool = tool # Initial type of part (SKIDL, KICAD, etc.) self.circuit = None # Part starts off unassociated with any circuit. # Create a Part from a library entry. if lib: # If the lib argument is a string, then create a library using the # string as the library file name. if isinstance(lib, basestring): try: libname = lib lib = SchLib(filename=libname, tool=tool) except Exception as e: if skidl.QUERY_BACKUP_LIB: logger.warning( 'Could not load KiCad schematic library "{}", falling back to backup library.' .format(libname)) lib = skidl.load_backup_lib() if not lib: raise e else: raise e # Make a copy of the part from the library but don't add it to the netlist. part = lib[name].copy(dest=TEMPLATE) # Overwrite self with the new part. self.__dict__.update(part.__dict__) # Make sure all the pins have a valid reference to this part. self.associate_pins() # Store the library name of this part. self.lib = getattr(lib, 'filename', None) # Otherwise, create a Part from a part definition. If the part is # destined for a library, then just get its name. If it's going into # a netlist, then parse the entire part definition. elif part_defn: self.part_defn = part_defn self.parse(just_get_name=(dest != NETLIST)) # If the part is destined for a SKiDL library, then it will be defined # by the additional attribute values that are passed. elif tool == SKIDL and name: pass else: logger.error( "Can't make a part without a library & part name or a part definition." ) raise Exception # If the part is going to be an element in a circuit, then add it to the # the circuit and make any indicated pin/net connections. if dest != LIBRARY: if dest == NETLIST: # If no Circuit object is given, then use the default Circuit that always exists. # Always set circuit first because naming the part requires a lookup # of existing names in the circuit. if not circuit: circuit = default_circuit # pylint: disable=undefined-variable circuit += self elif dest == TEMPLATE: # If this is just a part template, don't add the part to the circuit. # Just place the reference to the Circuit object in the template. if not circuit: self.circuit = default_circuit # pylint: disable=undefined-variable self.circuit = circuit # Add any net/pin connections to this part that were passed as arguments. if isinstance(connections, dict): for pin, net in connections.items(): net += self[pin] # Add any other passed-in attributes to the part. for k, v in attribs.items(): setattr(self, k, v)
def __init__(self, lib=None, name=None, dest=NETLIST, tool=None, connections=None, part_defn=None, circuit=None, **attribs): import skidl from .SchLib import SchLib from .defines import TEMPLATE, NETLIST, LIBRARY, SKIDL if tool is None: tool = skidl.get_default_tool() # Setup some part attributes that might be overwritten later on. self.do_erc = True # Allow part to be included in ERC. self.unit = { } # Dictionary for storing subunits of the part, if desired. self.pins = [] # Start with no pins, but a place to store them. self.name = name # Assign initial part name. (Must come after circuit is assigned.) self.description = '' # Make sure there is a description, even if empty. self._ref = '' # Provide a member for holding a reference. self.ref_prefix = '' # Provide a member for holding the part reference prefix. self.tool = tool # Initial type of part (SKIDL, KICAD, etc.) self.circuit = None # Part starts off unassociated with any circuit. # Create a Part from a library entry. if lib: # If the lib argument is a string, then create a library using the # string as the library file name. if isinstance(lib, basestring): try: libname = lib lib = SchLib(filename=libname, tool=tool) except Exception as e: if skidl.QUERY_BACKUP_LIB: logger.warning( 'Could not load KiCad schematic library "{}", falling back to backup library.' .format(libname)) lib = skidl.load_backup_lib() if not lib: raise e else: raise e # Make a copy of the part from the library but don't add it to the netlist. part = lib[name].copy(dest=TEMPLATE) # Overwrite self with the new part. self.__dict__.update(part.__dict__) # Make sure all the pins have a valid reference to this part. self.associate_pins() # Store the library name of this part. self.lib = getattr(lib, 'filename', None) # Otherwise, create a Part from a part definition. If the part is # destined for a library, then just get its name. If it's going into # a netlist, then parse the entire part definition. elif part_defn: self.part_defn = part_defn self.parse(just_get_name=(dest != NETLIST)) # If the part is destined for a SKiDL library, then it will be defined # by the additional attribute values that are passed. elif tool == SKIDL and name: pass else: logger.error( "Can't make a part without a library & part name or a part definition." ) raise Exception # If the part is going to be an element in a circuit, then add it to the # the circuit and make any indicated pin/net connections. if dest != LIBRARY: if dest == NETLIST: # If no Circuit object is given, then use the default Circuit that always exists. # Always set circuit first because naming the part requires a lookup # of existing names in the circuit. if not circuit: circuit = default_circuit # pylint: disable=undefined-variable circuit += self elif dest == TEMPLATE: # If this is just a part template, don't add the part to the circuit. # Just place the reference to the Circuit object in the template. if not circuit: self.circuit = default_circuit # pylint: disable=undefined-variable self.circuit = circuit # Add any net/pin connections to this part that were passed as arguments. if isinstance(connections, dict): for pin, net in connections.items(): net += self[pin] # Add any other passed-in attributes to the part. for k, v in attribs.items(): setattr(self, k, v)
def search(term, tool=None): """ Print a list of components with the regex term within their name, alias, description or keywords. """ import skidl from .SchLib import SchLib def search_libraries(term, tool): """Search for a regex term in part libraries.""" parts = set( ) # Set of parts and their containing libraries found with the term. for lib_dir in skidl.lib_search_paths[tool]: print('lib_dir = {}'.format(lib_dir)) # Get all the library files in the search path. try: lib_files = os.listdir(lib_dir) except (FileNotFoundError, OSError): logger.warning("Could not open directory '{}'".format(lib_dir)) lib_files = list( ) # Empty list since library directory was not found. lib_files = [ l for l in lib_files if l.endswith(skidl.lib_suffixes[tool]) ] for lib_file in lib_files: print(' ' * 79, '\rSearching {} ...'.format(lib_file), end='\r') lib = SchLib(os.path.join(lib_dir, lib_file), tool=tool) # Open the library file. def mk_list(l): """Make a list out of whatever is given.""" if isinstance(l, (list, tuple)): return l if not l: return [] return [l] # Search the current library for parts with the given term in # each of the these categories. for category in ['name', 'alias', 'description', 'keywords']: for part in mk_list(lib.get_parts(**{category: term})): part.parse( ) # Parse the part to instantiate the complete object. parts.add( (lib_file, part)) # Store the library name and part object. print(' ' * 79, end='\r') return list( parts) # Return the list of parts and their containing libraries. if tool is None: tool = skidl.get_default_tool() term = '.*' + term + '.*' # Use the given term as a substring. parts = search_libraries(term, tool) # Search for parts with that substring. # Print each part name sorted by the library where it was found. for lib_file, p in sorted(parts, key=lambda p: p[0]): print('{}: {} ({})'.format(lib_file, getattr(p, 'name', '???'), getattr(p, 'description', '???')))
def __init__( self, lib=None, name=None, dest=NETLIST, tool=None, connections=None, part_defn=None, circuit=None, **kwargs ): import skidl from .schlib import SchLib super().__init__() if tool is None: tool = skidl.get_default_tool() # Setup some part attributes that might be overwritten later on. self.do_erc = True # Allow part to be included in ERC. self.unit = {} # Dictionary for storing subunits of the part, if desired. self.pins = [] # Start with no pins, but a place to store them. self.p = PinNumberSearch(self) # Does pin search using only pin numbers. self.n = PinNameSearch(self) # Does pin search using only pin names. self.name = name # Assign initial part name. self.description = "" # Make sure there is a description, even if empty. self._ref = "" # Provide a member for holding a reference. self.ref_prefix = "" # Provide a member for holding the part reference prefix. self.tool = tool # Initial type of part (SKIDL, KICAD, etc.) self.circuit = None # Part starts off unassociated with any circuit. self.match_pin_regex = False # Don't allow regex matches of pin names. # Remove a part reference if it has been explicitly set as None. # Otherwise, this causes the assigned part reference to be incremented twice: # once by Circuit.add_parts() and again by setattr(). ref = kwargs.pop("ref", None) if ref: kwargs["ref"] = ref # Create a Part from a library entry. if lib: # If the lib argument is a string, then create a library using the # string as the library file name. if isinstance(lib, basestring): libname = lib try: lib = SchLib(filename=libname, tool=tool) except FileNotFoundError as e: if skidl.QUERY_BACKUP_LIB: logger.warning( 'Could not load KiCad schematic library "{}", falling back to backup library.'.format( libname ) ) lib = skidl.load_backup_lib() if not lib: raise e else: raise e # Make a copy of the part from the library but don't add it to the netlist. part = lib[name].copy(dest=TEMPLATE) # Overwrite self with the new part. self.__dict__.update(part.__dict__) # Make sure all the pins have a valid reference to this part. self.associate_pins() # Copy part units so all the pin and part references stay valid. self.copy_units(part) # Otherwise, create a Part from a part definition. If the part is # destined for a library, then just get its name. If it's going into # a netlist, then parse the entire part definition. elif part_defn: self.part_defn = part_defn # If given, set the tool version before parsing the part definition. # At this time, this is done to support differences between KiCad V5 and V6. tool_version = kwargs.pop("tool_version", None) if tool_version: self.tool_version = tool_version self.parse(get_name_only=(dest != NETLIST)) # If the part is destined for a SKiDL library, then it will be defined # by the additional attribute values that are passed. elif tool == SKIDL and name: pass else: log_and_raise( logger, ValueError, "Can't make a part without a library & part name or a part definition.", ) # Split multi-part pin names into individual pin aliases. self.split_pin_names(kwargs.pop("pin_splitters", None)) # Setup the tag for tieing the part to a footprint in a pcb editor. # Use the user specified tag if present. tag = kwargs.pop("tag", None) if tag is not None: self.tag = tag else: self.tag = str(randint(0, 2 ** 64 - 1)) if dest != LIBRARY: if dest == NETLIST: # If the part is going to be an element in a circuit, then add it to the # the circuit and make any indicated pin/net connections. # If no Circuit object is given, then use the default Circuit that always exists. circuit = circuit or default_circuit circuit += self elif dest == TEMPLATE: # If this is just a part template, don't add the part to the circuit. self.circuit = None # Add any net/pin connections to this part that were passed as arguments. if isinstance(connections, dict): for pin, net in list(connections.items()): net += self[pin] # Add any XSPICE I/O as pins. (This only happens with SPICE simulations.) self.add_xspice_io(kwargs.pop("io", [])) # Add any other passed-in attributes to the part. for k, v in list(kwargs.items()): setattr(self, k, v)