diff --git a/pymomo/__version__.py b/pymomo/__version__.py index 64a2795..545d07d 100644 --- a/pymomo/__version__.py +++ b/pymomo/__version__.py @@ -1 +1 @@ -__version__ = "1.0.10" \ No newline at end of file +__version__ = "1.1.1" \ No newline at end of file diff --git a/pymomo/commander/commands/rpc.py b/pymomo/commander/commands/rpc.py index 63caa03..22767cc 100644 --- a/pymomo/commander/commands/rpc.py +++ b/pymomo/commander/commands/rpc.py @@ -1,4 +1,3 @@ - from command import Command from pymomo.commander.exceptions import * import base64 diff --git a/pymomo/config/pcb/eagle.json b/pymomo/config/pcb/eagle.json new file mode 100644 index 0000000..13b92e5 --- /dev/null +++ b/pymomo/config/pcb/eagle.json @@ -0,0 +1,75 @@ +{ + "templates": + { + "two_layer": + { + "layers": + { + "Top Solderpaste Layer": + { + "extension": "crm", + "program_layers": ["tCream"], + "type": "gerber", + "remove": "gpi" + }, + + "Top Silkscreen Layer": + { + "extension": "plc", + "program_layers": ["Dimension", "tPlace", "tNames"], + "type": "gerber", + "remove": "gpi" + }, + + "Top Copper Layer": + { + "extension": "cmp", + "program_layers": ["Top", "Pads", "Vias"], + "type": "gerber", + "remove": "gpi" + }, + + "Bottom Copper Layer": + { + "extension": "sol", + "program_layers": ["Bottom", "Pads", "Vias"], + "type": "gerber", + "remove": "gpi" + }, + + "Top Soldermask Layer": + { + "extension": "stc", + "program_layers": ["tStop"], + "type": "gerber", + "remove": "gpi" + }, + + "Bottom Soldermask Layer": + { + "extension": "sts", + "program_layers": ["bStop"], + "type": "gerber", + "remove": "gpi" + }, + + "Drill Information": + { + "extension": "drd", + "program_layers": ["Drills", "Holes"], + "type": "excellon", + "remove": "dri" + } + }, + + "assembly": + { + "program_layers": ["tPlace", "tNames", "tDocu", "Document", "Reference","Dimension"], + "type": "drawing", + "extension": "ps" + }, + + "description": "Two layer board, Top silkscreen only, Soldermask on both sides" + } + } +} \ No newline at end of file diff --git a/pymomo/pybom/__init__.py b/pymomo/cparser/__init__.py similarity index 100% rename from pymomo/pybom/__init__.py rename to pymomo/cparser/__init__.py diff --git a/pymomo/exceptions.py b/pymomo/exceptions.py index 925c5d6..5d2e253 100644 --- a/pymomo/exceptions.py +++ b/pymomo/exceptions.py @@ -31,6 +31,10 @@ def format_msg(self): return msg + def __str__(self): + msg = self.format() + return msg + class ValidationError(MoMoException): """ API routines can impose validation criteria on their arguments in @@ -78,11 +82,22 @@ class ArgumentError(MoMoException): pass +class DataError(MoMoException): + """ + The method relied on data pass in by the user and the data was invalid. + + This could be because a file was the wrong type or because a data provider + returned an unexpected result. The parameters passed with this exception + provide more detail on what occurred and where. + """ + + pass + class InternalError(MoMoException): """ The method could not be completed with the user input passed for an unexpected reason. This does not signify a bug in the API - method code. More details should be passed in the arguments + method code. More details should be passed in the arguments. """ pass @@ -109,4 +124,22 @@ class BuildError(MoMoException): that something is misconfigured. """ + pass + +class TypeSystemError(MoMoException): + """ + There was an error with the MoMo type system. This can be due to improperly + specifying an unknown type or because the required type was not properly loaded + from an external module before a function that used that type was needed. + """ + + pass + +class EnvironmentError(MoMoException): + """ + The environment is not properly configured for the MoMo API command that was called. + This can be because a required program was not installed or accessible or because + a required environment variable was not defined. + """ + pass \ No newline at end of file diff --git a/pymomo/pcb/__init__.py b/pymomo/pcb/__init__.py new file mode 100644 index 0000000..8d57553 --- /dev/null +++ b/pymomo/pcb/__init__.py @@ -0,0 +1,22 @@ +""" +A package for managing bills of materials and pcb fabrication + +Methods and objects for creating and pricing bills of materials +as well as automatically generating gerber files for pcb fabrication. + +- The CircuitBoard object provides a way to generate BOMs and production + files for pcb fabrication from ECAD files +- various BOM pricing and matching engines like OctopartMatcher allow you + to see how much your BOM would cost in different quantities. +""" + +_name_ = "pcb" + + +#Add in required types that we need +import types +import pymomo.utilities.typedargs +pymomo.utilities.typedargs.type_system.load_type_module(types) + +from match_engines import * +from board import CircuitBoard diff --git a/pymomo/pcb/board.py b/pymomo/pcb/board.py new file mode 100644 index 0000000..7041aa2 --- /dev/null +++ b/pymomo/pcb/board.py @@ -0,0 +1,775 @@ +#TODO: +# - add compare_descriptions function that compares the descriptions +# currently associated with each bom line with those returned from +# an online database side by side so that you can make sure that +# you're matching the right part. +# - Make updating metadata and attributes reflected realtime in board +# without reloading the board. + +from pymomo.utilities.typedargs import * +from pymomo.exceptions import * +from eagle.board import EagleBoard +from pricing import OfferRequirements +import match_engines +from bom_matcher import BOMMatcher +from decimal import Decimal +from production import ProductionFileGenerator +import itertools + +#List of all of the types of board file we know how to deal with +board_types = {} +board_types['eagle'] = EagleBoard + +@context() +class CircuitBoard: + """ + A CircuitBoard that can be used to create a BOM or produce gerbers + + The price of the BOM can be looked up using the Octopart API and + complex rules can be specifed to decide who to buy the parts from. + A CircuitBoard can be created from an annotated pcb CAD file from any + supported CAD program. Currently we support EAGLE only but more will + be added in the future. + """ + + DefaultEngine = match_engines.OctopartMatcher + + @param("file", "path", "readable", desc="PCB board file to load") + @param("type", "string", ("list", "eagle"), desc="type of file [eagle is only currently supported option]") + def __init__(self, file, type='eagle'): + self.board = board_types[type](file) + self.clear_pricemodel() + + self._match_engine = CircuitBoard.DefaultEngine + + self.errors = [] + self.warnings = [] + + #Process board into variants so that each variant is a list of unique parts + self.variants = {name: self._process_variant(parts) for name,parts in self.board.data['variants'].iteritems()} + + #Copy all of the attributes from the board into this object, logging errors for any missing data + self._set_attribute('company', self.board.data) + self._set_attribute('part', self.board.data) + self._set_attribute('width', self.board.data) + self._set_attribute('height', self.board.data) + self._set_attribute('units', self.board.data) + self._set_attribute('revision', self.board.data) + self._set_attribute('no_populate', self.board.data, default=[]) + self._set_attribute('unknown_parts', self.board.data, default=[]) + self._set_attribute('fab_template', self.board.data) + self._set_attribute('fab_engine', self.board.data) + + #Make sure there are no parts that we don't have information about + for x in self.unknown_parts: + self._add_error(x, "Does not have required part information like distributor or manufacturer part numbers") + + #Make sure all parts have enough information to be matchable and placeable + for variant, lines in self.variants.iteritems(): + for line in lines: + for part in line: + if not part.matchable(): + self._add_warning(part.name, "Does not have enough information to be matchable in variant %s" % variant) + if not part.placeable(): + self._add_warning(part.name, "Does not have enough information to be placeble in variant %s" % variant) + + #Build a set containing all of the parts in all variants so we know what components could be populated but should not be + self.all_parts = frozenset([x.name for x in self._iterate_all_parts()]) + self._build_lookup_table() + + @return_type("list(string)") + @param("variant", "string", desc="Assembly variant to consider") + def nonpopulated_parts(self, variant=None): + """ + Fetch the reference identifiers of all parts not populated in this variant. + + The list of strings that is returned does not include parts that are not populated + in any assembly variant since those are considered to be virtual parts that should + be ignored. They are listed in the instance attribute no_populate. + """ + parts = frozenset([x.name for x in self._iterate_parts(variant)]) + + nopop = self.all_parts - parts + return [x for x in nopop] + + @return_type("bool") + def is_clean(self): + """ + Return true if there are no errors or warnings about this board. + """ + + return len(self.errors) == 0 and len(self.warnings) == 0 + + @return_type("logical_part") + @param("reference", "string", "not_empty", desc="Reference identifier to find") + @param("variant", "string", desc="Assembly variant to search") + def find(self, reference, variant=None): + """ + Find a part on the board using its reference identifier. + + If the same reference identifier refers to different parts in different + assembly variants, then you must specify which one you want by passing + the optional variant name. + """ + + if reference not in self.part_index: + raise ArgumentError("reference id did not exist", reference=reference) + + matches = self.part_index[reference] + if len(matches) > 1 and variant is None: + raise ArgumentError("reference id exists in multiple assembly variants, you must specify one", + reference=reference, variants=matches.keys()) + + if len(matches) > 1: + if variant in matches: + return matches[variant] + else: + raise ArgumentError("reference id is not populated in specified assembly variant", + reference=reference, variants=matches.keys(), specified_variant=variant) + + return matches.values()[0] + + @return_type("list(physical_part)") + @param("reference", "string", "not_empty", desc="Reference identifier to find") + @param("variant", "string", desc="Assembly variant to search") + def lookup(self, reference, variant=None): + """ + Use the currently selected matching system to look up a component. + + The data returned can contain realtime price and stock information, + a list of distributors that carry the part and other details depending + on the matching system used. + """ + + matcher = self._get_matcher() + part = self.find(reference, variant) + + matcher.add_part(part) + matcher.match_all() + + if not matcher.is_matched(part.name): + raise DataError("part could not be matched") + + return matcher.match_info(part.name) + + @return_type("bool") + @param("variant", "string", desc="Assembly variant to search") + def match_status(self, variant=None): + """ + Return whether all parts are matched with a component database + """ + + results = {} + matcher = self._get_matcher() + + for part,num in self._iterate_lines(variant): + if not part.matchable(): + return False + + matcher.add_part(part) + + matcher.match_all() + return matcher.all_matched() + + @return_type("map(string, string)") + @param("variant", "string", desc="Assembly variant to search") + def match_details(self, variant=None): + """ + Attempt to match all BOM components with a component database. + + Return a dictionary mapping reference numbers to matching statuses. + This function will tell you if any of your parts were not successfully + matched or did not have enough metadata to match to a unique component. + """ + + results = {} + matcher = self._get_matcher() + + for part,num in self._iterate_lines(variant): + if not part.matchable(): + results[part.name] = 'Insufficient metadata to match' + continue + + matcher.add_part(part) + + matcher.match_all() + match_results = matcher.match_details() + + return dict(itertools.chain(results.iteritems(), match_results.iteritems())) + + @param("variant", "string", desc="Assembly variant to update") + @param("missing_only", "bool", desc="Only update missing metadata attributes") + def update_all_metadata(self, variant=None, missing=True): + """ + Update the entire board with additional metadata from the matching engine. + + This can be used to automatically fill in descriptions, manufacturers and mpns + if you only have a distributor's part number, for example. If missing is True, + the default, only missing data will be added, entered data will not be overwritten. + + If variant is None, all assembly variants are updated. If variant is passed, then + only a single variant is updated. + """ + + if isinstance(variant, basestring): + update_vars = [self._assure_valid_variant(variant)] + else: + update_vars = self.variants.keys() + + handle = self.board.start_update() + + for var in update_vars: + for part in self._iterate_parts(var): + self.update_metadata(part.name, var, missing, handle=handle) + + self.board.finish_update(handle) + + @param("reference", "string", "not_empty", desc="Reference identifier to find") + @param("variant", "string", desc="Assembly variant to search") + @param("missing_only", "bool", desc="Only update missing metadata attributes") + def update_metadata(self, reference, variant=None, missing=True, handle=None): + """ + Update a single part with additional metadata from the matching engine. + + This can be used to automatically fill in descriptions, manufacturers and mpns + if you only have a distributor's part number, for example. If missing is True, + the default, only missing data will be added, entered data will not be overwritten. + """ + + part = self.find(reference, variant) + matches = self.lookup(reference, variant) + if len(matches) > 1: + raise DataError("part matched multiple times in database", reference=reference, variant=variant, matches=matches) + + match = matches[0] + + if match.mpn is not None and (not missing or (missing and part.mpn is None)): + self.board.set_metadata(part, variant, 'MPN', match.mpn, handle=handle) + + if match.manu is not None and (not missing or (missing and part.manu is None)): + self.board.set_metadata(part, variant, 'MANU', match.manu, handle=handle) + + if match.desc is not None and (not missing or (missing and part.desc is None)): + self.board.set_metadata(part, variant, 'DESCRIPTION', match.desc, handle=handle) + + @param("output", "path", desc="Output directory for production files") + def generate_fab(self, output): + """ + Create Gerber,excellon and readme files so this board can be fabricated + + Files are saved into the specified directory output, which is created if + it does not exist. A readme.txt file is also generated describing the + contents of each file. + """ + + prod = ProductionFileGenerator(self) + prod.build_fab(output) + + @param("output", "path", desc="Output directory for production files") + @param("varaint", "string", desc="Assembly variant to process") + def generate_production(self, output, variant=None): + """ + Create Gerber files, BOMs and assembly drawings for this board. + """ + + variant = self._assure_valid_variant(variant) + + prod = ProductionFileGenerator(self) + prod.build_production(variant, output) + + @return_type("list(string)") + def get_errors(self): + """ + Return a list of all errors in this board file. + """ + + return map(self._format_msg, self.errors) + + @return_type("list(string)") + def get_warnings(self): + """ + Return a list of all warnings in this board file. + """ + + return map(self._format_msg, self.warnings) + + + @param('dist', 'string', 'not_empty', desc='a distributor\'s name') + def require_distributor(self, dist): + """ + When searching for prices, require one of these distributors + + This can be called multiple times to whitelist a number of + distributors. + """ + + self.required_dists.append(dist) + + @param('dist', 'string', 'not_empty', desc='a distributor\'s name') + def exclude_distributor(self, dist): + """ + When searching for prices, exclude offerings from this distributor + + This can be called multiple times to blacklist a number of distributors. + """ + + self.excluded_dists.append(dist) + + @param('packaging', 'string', 'not_empty', desc='a type of packaging') + def require_packaging(self, packaging): + """ + When searching for prices, require a certain type of packaging + + This can be called multiple times to whitelist a number of + packaging types like 'Cut Tape' or 'Tape & Reel'. + """ + + self.required_packages.append(packaging) + + @param('packaging', 'string', 'not_empty', desc='a type of packaging') + def exclude_packaging(self, packaging): + """ + When searching for prices, forbid a certain type of packaging + + This can be called multiple times to blacklist a number of + packaging types like 'Cut Tape' or 'Tape & Reel'. + """ + + self.excluded_packages.append(packaging) + + @param("in_stock", "bool", desc="whether parts must be in stock") + def require_stock(self, in_stock): + """ + Set whether parts must be in stock when pricing + """ + + self.require_stock = in_stock + + @return_type("unit_price") + @param("ref", "string", desc="part to lookup price information on") + @param("n", "integer", "nonnegative", desc="number of parts to quote") + @param("variant", "string", desc="Assembly variant to search") + @param("total", "bool", desc="Return the total or unit price") + def price(self, ref, n, variant=None, total=False): + """ + Return the best price of n of the part specified by the reference id, ref + + The part prices are searched according to the currently set price model + including whitelisted and blacklisted packaging types and distributors. + To find out information about what types of distributors and packaging a + part is available in, use lookup. + """ + + part = self.find(ref, variant) + matcher = self._get_matcher() + matcher.add_part(part) + matcher.match_all() + + result = matcher.price_advanced(ref, n, self._build_pricemodel()) + + if 'error' in result: + raise DataError("Could not get price", error=error, result=result) + + if total: + result['price'] *= n + + return type_system.convert_to_type(result, 'unit_price') + + @return_type("map(string, unit_price)") + @param("n", "integer", "nonnegative", desc="number of complete boards to quote") + @param("variant", "string", desc="Assembly variant to price") + @param("excess", "integer", "nonnegative", desc="percent excess to order of each part") + def detailed_prices(self, n, excess, variant=None): + """ + Return the price and seller for each part that minimizes cost for n boards + + Optionally order excess percent extra of each part to account for assembly + losses. + """ + + quote_quant = n + (excess/100.0) + + out_prices = {line[0].desc: self.price(line[0].name, int(quote_quant*line[1] + 0.5), variant, total=True) for line in self._iterate_lines(variant)} + return out_prices + + @return_type("map(string, price)") + @param("n", "integer", "nonnegative", desc="number of complete boards to quote") + @param("variant", "string", desc="Assembly variant to price") + @param("excess", "integer", "nonnegative", desc="percent excess to order of each part") + def total_price(self, n, excess, variant=None): + """ + Return the total cost and unit costs of n boards with excess percent excess + + The returned list contains 2 items, the first is the total price, the second + """ + + prices = self.detailed_prices(n, excess, variant) + + total = Decimal('0.0') + + for price in prices.itervalues(): + total += price.price + + return {'total': total, 'unit': total/n} + + @annotated + def clear_pricemodel(self): + """ + Remove all pricing restrictions + """ + + self.required_dists = [] + self.excluded_dists = [] + self.required_packages = [] + self.excluded_packages = [] + self.prices = False + self.require_stock = False + self.quote_n = 1 + self.quote_quant = 1.0 + self.quote_excess = 0 + + @param("quantity", "integer", "positive", desc="number of units to quote") + @param("excess", "integer", "nonnegative", desc="required excess in %") + def include_prices(self, quantity, excess): + """ + Include prices in generated BOMs + """ + + self.quote_n = quantity + self.quote_excess = excess + self.quote_quant = quantity * (1.0 + excess/100.) + self.prices = True + + @param("engine", "string", desc="name of component lookup engine to use") + def set_match_engine(self, engine): + """ + Select the service used to lookup components. + + Currently supported values are CacheOnlyMatcher and OctopartMatcher + to use only cached values or the Octopart API. Other methods will be + supported in the future. + """ + + if not hasattr(match_engines, engine): + raise ArgumentError("unknown component matching engine specified", engine=engine) + + matcher = getattr(match_engines, engine) + if not issubclass(matcher, BOMMatcher): + raise ArgumentError("invalid component matching engine specified", engine=engine, classname=matcher, object=matcher) + + self._match_engine = matcher + + @return_type("list(bom_line)") + @param("variant", "string", desc='assembly variant to build') + def bom_lines(self, variant=None): + """ + Get all of the BOM lines for the specified assembly variant + """ + + variant = self._assure_valid_variant(variant) + + outlines = [] + for line in self._iterate_linegroups(variant): + outline = type_system.convert_to_type(line, 'bom_line') + outlines.append(outline) + + return self._order_bom_lines(outlines) + + @return_type("list(string)") + def get_variants(self): + return self.variants.keys() + + @param("path", "path", "writeable", desc="The path to the BOM that should be created") + @param("variant", "string", desc='assembly variant to build') + @param("format", "string", ("list", ['excel']), desc="The file format that should be saved") + @param("hide_problems", "bool", desc="do not include errors or warnings processing this file") + def export_bom(self, path, variant=None, format="excel", hide_problems=False): + """ + Expore a Bill of Materials for this board in the specified format. + + BOMs can either be formatted as an industry standard excel spreadsheet + or using a custom Cheetah based template. Currently only excel spreadsheets + are supported. The optional format parameter chooses the format and the + result is saved to a file at path. + """ + + if format == 'excel': + self._save_excel_bom(variant, path, hide_problems=hide_problems) + + def _save_excel_bom(self, variant, path, hide_problems): + """ + Save a BOM in excel format. + """ + + import xlsxwriter + import reference + + lib = reference.PCBReferenceLibrary() + lines = self.bom_lines(variant) + + bk = xlsxwriter.Workbook(path) + sht = bk.add_worksheet("Bill of Materials") + + #formats + large = bk.add_format({'font_size': 16, 'bold':True}) + med = bk.add_format({'font_size': 14, 'bold':True}) + bold = bk.add_format({'bold': True}) + red = bk.add_format({'bg_color': 'red'}) + orange= bk.add_format({'bg_color': 'orange'}) + + #Write header + sht.write(0, 0, "Bill of Materials", large) + sht.write(1, 0, self.part, med) + sht.write(2, 0, self.company) + sht.write(3, 0, "Revision") + sht.write(3, 1, self.revision) + sht.write(4, 0, "Width") + sht.write(4, 1, self.width) + sht.write(4, 2, self.units) + sht.write(5, 0, "Height") + sht.write(5, 1, self.height) + sht.write(5, 2, self.units) + + #Write out all of the BOM lines + headers = ['', 'Qty', 'References', 'Value', 'Footprint', 'Description', 'Manufacturer', 'Part Number', 'Distributor', 'Dist. Part Number'] + row = 7 + col = 0 + + for h in headers: + sht.write(row, col, h, bold) + col += 1 + + row += 1 + for i,line in enumerate(lines): + sht.write(row, 0, i+1) + sht.write(row, 1, line.count) + sht.write(row, 2, ", ".join(line.refs)) + + if lib.has_value(line.type): + sht.write(row, 3, line.value) + + sht.write(row, 4, line.package.name) + sht.write(row, 5, line.desc) + + if line.manu is not None: + sht.write(row, 6, line.manu) + if line.mpn is not None: + sht.write(row, 7, line.mpn) + if line.dist is not None: + sht.write(row, 8, line.dist) + if line.distpn is not None: + sht.write(row, 9, line.distpn) + + row += 1 + + row += 1 + + #Write out all of the unpopulated parts + sht.write(row, 0, "Unpopulatd Parts", red) + sht.write(row, 1, ", ".join(self.nonpopulated_parts(variant)), red) + + if not hide_problems: + row += 2 + if len(self.errors) > 0: + errors = self.get_errors() + sht.write(row, 0, "BOM Errors", bold) + row += 1 + for error in errors: + sht.write(row, 0, error) + row += 1 + + row += 1 + if len(self.warnings) > 0: + warnings = self.get_warnings() + sht.write(row, 0, "BOM Warnings", bold) + row += 1 + for warning in warnings: + sht.write(row, 0, warning) + row += 1 + + bk.close() + + def _order_bom_lines(self, lines): + """ + Sort bom lines based on the type of each line + + Follow a standard order: R, C, L, D, U, other + This routine assumes that there will never be more than 100,000 + parts per board, which seems safe for the foreseeable future. + """ + + order_dict = {'R': 0, 'C': 1, 'L': 2, 'D': 3, 'U': 4} + + def ref_key(line): + mult = 100000 + ref = line.type + num = line.lowest + if ref in order_dict: + return order_dict[ref]*mult + num + + if ref in ref_key.others: + return ref_key.others[ref]*mult + num + + ind = ref_key.other_index + ref_key.others[ref] = ref_key.other_index + ref_key.other_index += 1 + return ind*mult + num + + ref_key.other_index = 20 + ref_key.others = {} + + return sorted(lines, key=ref_key) + + def _build_pricemodel(self): + return OfferRequirements(self.required_dists, self.excluded_dists, + self.required_packages, self.excluded_packages, + self.require_stock) + + def _default_variant(self): + """ + If there is only one variant, choose it by default + """ + + variants = self.variants.keys() + if len(variants) == 1: + return variants[0] + + return None + + def _assure_valid_variant(self, variant): + """ + If variant is None, attempt to use the default variant. + """ + + if variant is None: + variant = self._default_variant() + if variant is None: + raise ArgumentError('you must specify a variant when there is more than one option', variants=self.board.variants.keys()) + + return variant + + def _process_variant(self, parts): + """ + Group items by unique key and return tuples of identical parts so that you can + turn a list of parts into a BOM with identical line items. + """ + + bomitems = [] + + sparts = sorted(parts, key=lambda x:x.unique_id()) + for k,g in itertools.groupby(sparts, lambda x:x.unique_id()): + bomitems.append(list(g)) + + return bomitems + + def _add_warning(self, attribute, msg): + """ + Log a warning about something missing or not correct about this circuit board. + """ + + self.warnings.append(('WARNING', attribute, msg)) + + def _add_error(self, attribute, msg): + """ + Log an error about something missing or not correct about this circuit board. + """ + + self.errors.append(('ERROR', attribute, msg)) + + def _format_msg(self, msg_tuple): + """ + Format an error or warning message into a string + """ + + return "%s on attribute/part %s: %s" % msg_tuple + + def _set_attribute(self, attribute, data, default="Unknown Attribute", required=True): + value = default + + if required: + logger = self._add_error + else: + logger = self._add_warning + + if attribute not in data: + logger(attribute, "Board data did not contain attribute in dictionary") + else: + brd_value = data[attribute] + if brd_value is None: + logger(attribute, "Attribute was empty or not defined") + else: + value = brd_value + + setattr(self, attribute, value) + + def _build_lookup_table(self): + """ + Build a map to look up part information given its reference number + """ + + part_table = {} + + for variant, lines in self.variants.iteritems(): + for line in lines: + for part in line: + if part.name not in part_table: + part_table[part.name] = {} + + part_table[part.name][variant] = part + + self.part_index = part_table + + def _iterate_parts(self, variant=None): + """ + Iterate over all parts in the assembly variant specified + """ + + variant = self._assure_valid_variant(variant) + lines = self.variants[variant] + + for line in lines: + for part in line: + yield part + + def _iterate_lines(self, variant=None): + """ + Iterate over each distinct BOM line on the board returning + the first logical part and the number of identical parts + """ + + variant = self._assure_valid_variant(variant) + lines = self.variants[variant] + + for line in lines: + yield line[0], len(line) + + def _iterate_linegroups(self, variant=None): + """ + Iterate over all parts grouping them by their bom line + + This returns lists of parts corresponding to each BOM line + """ + + variant = self._assure_valid_variant(variant) + lines = self.variants[variant] + + for line in lines: + yield line + + def _iterate_all_parts(self): + """ + Iterate over all parts defined in any assembly variant of this board + """ + + for variant, lines in self.variants.iteritems(): + for line in lines: + for part in line: + yield part + + def _get_matcher(self): + """ + Get an instance of the currently supported match engine + """ + + return self._match_engine() + diff --git a/pymomo/pcb/bom_matcher.py b/pymomo/pcb/bom_matcher.py new file mode 100644 index 0000000..8fe95cc --- /dev/null +++ b/pymomo/pcb/bom_matcher.py @@ -0,0 +1,364 @@ +#bom_matcher.py +#A generic interface for services that take in identifying information about a +#part and return price offerings for that part. + +from pymomo.utilities.typedargs import * +from pymomo.exceptions import * +from partcache import PartCache +import uuid +from decimal import Decimal + +class BOMMatcher(object): + def __init__(self): + self.requests = [] + self.matched = {} + self.errors = {} + self.cache = PartCache() + + self.ref_index = {} + + def _canonicalize_name(self, name): + """ + Given a name, like a distributor's name, convert it to + lowercase and remove all spaces,_ or - characters + """ + + return name.lower().replace(' ', '').replace('-', '').replace('_', '') + + def _build_reference(self, part): + if "distpn" in part: + return "__SKU__" + self._canonicalize_name(str(part['distpn'])) + self._canonicalize_name(str(part['dist'])) + + else: + mpn = part['mpn'] + manu = part['manu'] + + return "__MPN__" + self._canonicalize_name(str(part['mpn'])) + self._canonicalize_name(str(part['manu'])) + + @param("mpn", "string", "not_empty", desc="manufacturer's part number") + @param("manu", "string", desc="manufacturer") + @param("ref", "string", desc="reference number") + def add_by_mpn(self, mpn, manu=None, ref=""): + """ + Find a part by its manufacturer's part number + + Optionally filter to make sure that the manufacturer is equal to the specified + 'manu' string in case the mpn is not globally unique. Comparison between + manufacturer strings, if given, is done in a case insensitive manner after removing + all - and _ characters from both strings, so e.g. Digi-Key matches digikey. + + If ref is given, the returned information will contain the reference number so that + it can be unambiguously matched with the source that generated the request for pricing + """ + + part = {'mpn': mpn, 'manu': manu, 'external_ref': ref} + part['ref'] = self._build_reference(part) + + self.requests.append(part) + if ref != "": + self._add_reference(part['ref'], ref) + + @param("distpn", "string", "not_empty", desc="distributor part number") + @param("dist", "string", desc="distributor name") + @param("ref", "string", desc="reference identifier") + def add_by_distpn(self, distpn, dist="", ref=""): + """ + Find a part by its distributor part number + + Optionally filter to make sure that the distributor is equal to the specified + 'dist' string in case the distpn is not globally unique. Comparison between + distributor strings, if given, is done in a case insensitive manner after removing + all - and _ characters from both strings, so e.g. Digi-Key matches digikey. + + If ref is given, the returned information will contain the reference number so that + it can be unambiguously matched with the source that generated the request for pricing + """ + + part = {'distpn': distpn, 'dist': dist, 'external_ref': ref} + part['ref'] = self._build_reference(part) + + self.requests.append(part) + if ref != "": + self._add_reference(part['ref'], ref) + + @param("part", "logical_part", "matchable", desc="component to look up") + def add_part(self, part): + """ + Find a logical part based on its embedded metadata + """ + + if part.manu is not None and part.mpn is not None: + self.add_by_mpn(part.mpn, part.manu, ref=part.name) + + #If we have information about both a distributor and a mpn + #save both bits of information so that we can filter in case the MPN + #is not uniquely identifying + if part.dist is not None: + self.requests[-1]['require_dist'] = part.dist + if part.distpn is not None: + self.requests[-1]['require_distpn'] = part.distpn + else: + self.add_by_distpn(part.distpn, part.dist, ref=part.name) + + def _add_reference(self, internal, external): + if external in self.ref_index: + raise ArgumentError("attempted to add same reference number twice", reference=external, old=self.ref_index, new=internal) + + self.ref_index[external] = internal + + @annotated + def clear(self): + """ + Clear all pending requests and results + """ + + self.requests = [] + self.matched = {} + self.errors = {} + self.ref_index = {} + + @annotated + def match_all(self): + """ + Attempt to match all parts. + + Parts that are present in the part cache already are not + passed on to the matching engine but instead returned directly + from the cache. Parts that are uniquely matched are cached + for future reference. + """ + + #Figure out which items are cached + cache_status = map(lambda x: self.cache.try_get(x['ref']), self.requests) + uncached = [x[1] for x in enumerate(self.requests) if cache_status[x[0]] is None] + + reqs = [x for x in self.requests] + self.requests = uncached + self._match() + + #Perform additional checks to make sure that the match engines are returning + #just the results that we want and not extras. + for req in reqs: + if req['ref'] in self.matched: + parts = self.matched[req['ref']] + + #Some match engines only match the MPN, not the manufacturer's name, so enforce that here + if 'manu' in req and req['manu'] is not None: + self.matched[req['ref']] = filter(lambda x: self._canonicalize_name(req['manu']) == self._canonicalize_name(x.manu), parts) + + #Make sure that if we specified a specific distributor that this part is carried by that distributor + dist = None + if 'require_dist' in req: + dist = req['require_dist'] + if 'dist' in req and req['dist'] is not None: + dist = req['dist'] + if dist is not None: + self.matched[req['ref']] = filter(lambda x: self._dist_in_offers(x, dist), parts) + + #Update the cache with the new results + for key, value in self.matched.iteritems(): + if len(value) == 1: + self.cache.set(key, value[0]) + + #Add in all of the cached results + for i, x in enumerate(cache_status): + if x is not None: + self.matched[reqs[i]['ref']] = [x] + + #Reset the list of requests to include cached and uncached values + self.requests = reqs + + @return_type("map(string, integer)") + def match_summary(self): + """ + Show which parts have been added, matched or had errors + """ + + total_reqs = len(self.requests) + total_matched = len(self.matched) + total_errors = len(self.errors) + + multimatch = filter(lambda x: len(x) > 1, self.matched.itervalues()) + total_multi = len(multimatch) + + ret = {'Requested Parts': total_reqs, 'Matched Parts': total_matched, + 'Parts with Errors': total_errors, 'Multiple Matches': total_multi} + + return ret + + @return_type("string") + def all_matched(self): + """ + Check if all requested parts have been matched to exactly 1 physical part + """ + + if len(self.matched) == len(self.requests): + multimatch = filter(lambda x: len(x) > 1, self.matched.itervalues()) + total_multi = len(multimatch) + + if total_multi == 0: + return True + + return False + + @param("reference", "string", "not_empty", desc="reference number") + @return_type("string") + def is_matched(self, reference): + """ + Check if reference number is matched to a physical part + """ + + if reference not in self.ref_index: + return False + + intref = self.ref_index[reference] + if intref in self.matched: + return True + + return False + + @return_type("map(string, string)") + def match_details(self): + """ + List the matching status of each part according to its reference number + """ + + stats = {} + + for r in self.requests: + refnum = r['ref'] + if 'external_ref' in r and r['external_ref'] != '': + refnum = r['external_ref'] + + if r['ref'] in self.matched: + if len(self.matched[r['ref']]) == 1: + stats[refnum] = 'Matched to a unique item' + else: + stats[refnum] = '%d separate matches' % (len(self.matched[r['ref']]),) + + elif r['ref'] in self.errors: + stats[refnum] = self.errors[r['ref']]['msg'] + else: + stats[refnum] = 'Match has not been attempted' + + return stats + + @param("reference", "string", "not_empty", desc="user reference number") + @return_type("list(physical_part)") + def match_info(self, reference): + """ + Return matched part info by reference number + + If the part matched multiple items, they are all returned. + """ + + if reference not in self.ref_index: + raise ArgumentError("unknown reference number passed", reference=reference) + + intref = self.ref_index[reference] + + if intref not in self.matched: + raise ArgumentError("part was not matched", reference=reference, internal_reference=intref) + + info = self.matched[intref] + + return info + + @param("quantity", "integer", "positive", desc="requested number of parts") + @param("reference", "string", "not_empty", desc="reference number") + @param("seller", "string", desc="required seller of the part") + @return_type("string") + def price(self, reference, quantity, seller): + """ + Return the best price for N parts from seller as a Decimal + """ + + info = self.match_info(reference) + can_seller = self._canonicalize_name(seller) + + if len(info) > 1: + raise ArgumentError("part is not uniquely matched", reference=reference) + + offers = filter(lambda x: self._canonicalize_name(x.seller)==can_seller, info[0].offers) + + best = Decimal.from_float(1e15) + for offer in offers: + price = offer.best_price(quantity) + if price is not None and price < best: + best = price + + return best + + def price_advanced(self, reference, quantity, requirements): + """ + Find the price of a single item subject to various requirements + + requirements must have a validate function that takes two arguments: an offer and the quantity + """ + + info = self.match_info(reference) + + if len(info) > 1: + return {'error': 'Part not uniquely matched'} + + offers = filter(lambda x: requirements.validate(x, quantity), info[0].offers) + + best = Decimal.from_float(1e15) + best_offer = None + for offer in offers: + price = offer.best_price(quantity) + if price is not None and price < best: + best = price + best_offer = offer + + if best_offer is None: + return {'error': 'No valid offers found'} + else: + return {'price': best, 'offer': offer} + + def prices(self, quantities, requirements): + """ + Return the prices for specified parts at specified quantities. + + requirements must have a validate function that takes two arguments: an offer and the quantity + quantities must be a dictionary mapping reference numbers to quantities + """ + + result = {} + + for ref,quantity in self.quantities.iteritems(): + info = self.match_info(ref) + + if len(info) > 1: + result[ref] = {'error': 'Part not uniquely matched'} + continue + + offers = filter(lambda x: requirements.validate(x, quantity), info[0].offers) + + best = Decimal.from_float(1e15) + best_offer = None + for offer in offers: + price = offer.best_price(quantity) + if price is not None and price < best: + best = price + best_offer = offer + + if best_offer is None: + result[ref] = {'error', 'No valid offers found'} + else: + result[ref] = {'price': best, 'offer': offer} + + return result + + def _dist_in_offers(self, part, dist): + """ + Given a physical_part, make sure it contains an offer from distributor dist + """ + + dist = self._canonicalize_name(dist) + + for offer in part.offers: + if self._canonicalize_name(offer.seller) == dist: + return True + + return False diff --git a/pymomo/pcb/eagle/__init__.py b/pymomo/pcb/eagle/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/pymomo/pcb/eagle/board.py b/pymomo/pcb/eagle/board.py new file mode 100644 index 0000000..b019802 --- /dev/null +++ b/pymomo/pcb/eagle/board.py @@ -0,0 +1,292 @@ +#board.py +#TODO +# - check for location of eagle in standard locations on Windows + +import itertools +import xml.etree.cElementTree as ElementTree + +import platform +import os +import subprocess + +from part import part_from_board_element +from ..reference import PCBReferenceLibrary +from ..package import Package +from pymomo.exceptions import * + +class EagleBoard: + def __init__(self, brd_file): + """ + Create a Board from EAGLE Board file by parsing attribute tags embedded with the + components. Create different assembly variants for each different configuration + specified in the file + """ + + tree = ElementTree.parse(brd_file) + if tree is None: + raise DataError("Eagle board file does not contain valid XML", file=brd_file) + + root = tree.getroot() + + #Find all of the defined packages in this file + packages =root.findall('.//packages') + + elems = root.find("./drawing/board/elements") + if elems is None: + raise DataError("Eagle board file does not contain a list of components", file=brd_file) + + parts = list(elems) + + unknown = [] + nopop = [] + constant = [] + variable = [] + variants = find_variants(root) + + all_vars = set(variants) + + #Create a part object for each part that has a valid digikey-pn + #If there are multiple digikey part numbers, create a part for each one + for part in parts: + valid_part = False + found_vars = set() + nopop_vars = set() + + for v in variants: + p,found = part_from_board_element(part, v, packages) + if p and found: + variable.append( (v, p) ) + found_vars.add(v) + valid_part = True + elif found: + #If this part has digipn == "" or Populate=no, then it should not be populated in this variant + valid_part = True + nopop_vars.add(v) + nopop.append(part.get('name',"Unknown Name")) + + #See if this is a constant part (with no changes in different variants) + p,found = part_from_board_element(part, "", packages) + if p and len(found_vars) == 0 and len(nopop_vars) == 0: + constant.append(p) + valid_part = True + elif p and len(found_vars) != len(all_vars): + #there are some variants that want the default part and some that want a change + for var in (all_vars - found_vars) - nopop_vars: + variable.append( (var, p) ) + valid_part = True + + #Make sure this part has information for at least one assembly variant + if not valid_part: + unknown.append(part.get('name',"Unknown Name")) + + vars = {} + #Create multiple variants + for var in variants: + vars[var] = constant + filter_sublists(variable, var) + + props = {} + props['company'] = find_attribute(root, 'COMPANY', None) + props['part'] = find_attribute(root, 'PARTNAME', None) + props['width'] = find_attribute(root, 'WIDTH', None) + props['height'] = find_attribute(root, 'HEIGHT', None) + props['units'] = find_attribute(root, 'DIM_UNITS', None) + props['revision'] = find_attribute(root, 'REVISION', None) + props['no_populate'] = nopop + props['unknown_parts'] = unknown + props['variants'] = vars + props['fab_engine'] = 'eagle' #Used by production.py to locate the templates for gerber generation + props['fab_template'] = find_attribute(root, 'FAB_TEMPLATE', None) + + self.data = props + self.file = brd_file + + def start_update(self): + """ + Open the underlying file for appending additional attributes + + Returns an opaque datatype that should be passed to set_metadata. + """ + + tree = ElementTree.parse(self.file) + if tree is None: + raise DataError("Eagle board file does not contain valid XML", file=brd_file) + + return tree + + def finish_update(self, handle): + """ + Save the changes to the board file specified in handle. + + handle must be the result of a call to start_update + """ + + handle.write(self.file) + + def set_metadata(self, part, variant, key, value, handle=None): + """ + Update this board file with additional attributes. + + The board must be reloaded for the attributes to be visible. + """ + + if handle is None: + tree = self.start_update() + root = tree.getroot() + else: + root = handle.getroot() + + element = root.find(".//element[@name='%s']" % part.name) + if element is None: + raise ArgumentError("Part was not found in board to update") + + att_name = key.upper() + + if att_name not in set(['MANU', 'MPN', 'DIST', 'DIST-PN', 'DIGIKEY-PN', 'FOOTPRINT', 'DESCRIPTION']): + raise ArgumentError("attempting to set unknown metadata attribute", file=self.file, attribute=att_name, value=value, variant=variant) + + if variant is not None and variant != 'MAIN': + att_name += '-%s' % variant.upper() + + att_elem = element.find("./attribute[@name='%s']" % att_name) + if att_elem is not None: + att_elem.set('value', value.upper()) + else: + x = element.get('x', '0.0') + y = element.get('y', '0.0') + + #Add in other default attributes for this xml element. + attribs = { 'x': x, 'y': y, 'name': att_name, 'value': value.upper(), + 'size': "1.778", 'layer': "27", 'display': "off"} + + ElementTree.SubElement(element, 'attribute', **attribs) + + #Update the file with the modification if we were called in standalone mode + if handle is None: + tree.write(self.file) + + def build_production_file(self, path, layers, type, **kwargs): + """ + Use the data in this board file to produce Gerbers and Excellon files + + layers should be the eagle layers to include in the file + type should be gerber, excellon or drawing + optional kwargs may be supported in the future to adjust output + """ + + #Options from EAGLE's built in gerber file for producing 2 layer boards + #and argument names from EAGLE -? command + args = ['-X', '-f+', '-c+', '-O+'] + + if type == 'gerber': + args.append('-dGERBER_RS274X') + elif type == 'excellon': + args.append('-dEXCELLON') + elif type == 'drawing': + args.append('-dPS') + args.append('-h11') + args.append('-w7.75') + args.append('-s2.5') + else: + raise ArgumentError("Invalid type specified for production file (must be gerber, excellon or drawing)", type=type) + + args.append('-o%s' % path) + args.append(self.file) + + for layer in layers: + args.append(layer) + + self._execute_eagle(args) + + def _find_eagle(self): + """ + Try to find the eagle executable on this system. + + If it's in the path, just return that, otherwise search in standard + locations on each architecture or fail. + """ + + from distutils.spawn import find_executable + + if platform.system() == 'Darwin': + eagle = find_executable('EAGLE') + if eagle is not None: + return eagle + + entries = os.listdir('/Applications') + possible_eagles = filter(lambda x: x.startswith('EAGLE-') and os.path.isdir(os.path.join('/Applications', x)), entries) + + #Try to get the latest EAGLE version if there are multiple installed + try: + possible_eagles.sort(key=lambda x: map(int, x[6:].split('.'))) + + eagle_dir = possible_eagles[-1] + eagle_path = os.path.join('/Applications', eagle_dir, 'EAGLE.app', 'Contents', 'MacOS', 'EAGLE') + + if os.path.isfile(eagle_path) and os.access(eagle_path, os.X_OK): + return eagle_path + except ValueError: + #Value errors mean we could not convert the version string to an int, indicating that it wasn't in the form X.Y.Z + #ignore this error and just say we couldn't find EAGLE since an eagle directory without an X.Y.Z form could be a + #different program altogether. + pass + elif platform.system() == 'Windows': + eagle = find_executable('EAGLECON') + if eagle is not None: + return eagle + + #TODO: Check for eagle location on windows and autofind + #Check for program files directory in 32 or 64 bit mode like so: + #http://stackoverflow.com/questions/1283664/python-get-wrong-value-for-os-environprogramfiles-on-64bit-vista + else: + #Otherwise assume that Eagle will be in $PATH + eagle = find_executable('EAGLECON') + if eagle is not None: + return eagle + + raise EnvironmentError("could not find installed version of Cadsoft EAGLE", suggestion='install EAGLE and add it to your $PATH') + + def _execute_eagle(self, args): + eagle = self._find_eagle() + + #TODO: Check what kinds of exceptions this can throw + with open(os.devnull, 'wb') as DEVNULL: + subprocess.check_call([eagle] + args, stdout=DEVNULL, stderr=DEVNULL) + + +#Helper functions +def remove_prefix(s, prefix): + if not s.startswith(prefix): + return s + + return s[len(prefix):] + +def filter_sublists(master, key): + """ + Given a list of lists where each of the sublist elements are tuples (key, value), + return a concatenated list of all values in tuples that match key + """ + + concat = itertools.chain(master) + + filtered = filter(lambda x: x[0] == key, concat) + + return map(lambda x:x[1], filtered) + +MISSING = object() +def find_attribute(root, name, default=MISSING): + attr = root.find(".//attribute[@name='%s']" % name) + if attr is None: + if default is not MISSING: + return default + + raise DataError("required board attribute not found", name=name) + + return attr.get('value') + +def find_variants(root): + vars = root.findall(".//attribute[@value='%s']" % 'ASSY-VARIANT') + + if len(vars) == 0: + return ["MAIN"] + else: + return map(lambda x: x.get('name'), vars) diff --git a/pymomo/pcb/eagle/part.py b/pymomo/pcb/eagle/part.py new file mode 100644 index 0000000..2f2bc8c --- /dev/null +++ b/pymomo/pcb/eagle/part.py @@ -0,0 +1,186 @@ +#Routines for building part objects from eagle board files +from pint import UnitRegistry +from pymomo.exceptions import * +from pymomo.utilities.typedargs import type_system + +from ..package import Pad, Pin, Package, Placement + +ureg = UnitRegistry() + +def part_from_board_element(elem, variant, packages): + """ + Create a Part object from an element in an EAGLE board file. + + Automatically extract all of the known attributes of the part to fill in the + reference identifier, package information, number of pads/pins, footprint, + description, etc. + + packages should be a list of XML elements corresponding to the various lists of packages + for this board. + + Returns a tuple of Part,Boolean + The Part describes this part and may be None if it is not populated in this variant + The Boolean is true is this part is valid and populated in this variant and false otherwise. + """ + + pkg = elem.get('package', None) + + name = elem.get('name', 'No Name') + value = elem.get('value', 'No Value') + + value_override = find_attribute(elem, 'VALUE', variant) + if value_override is not None: + value = value_override + + #allow overriding this package with a custom name + pkgname = find_attribute(elem, 'FOOTPRINT', variant, pkg) + if pkg is not None: + pkg_info = find_package(packages, pkg, display_name=pkgname) + else: + pkg_info = Package(str(pkgname), [], []) + + desc = find_attribute(elem, 'DESCRIPTION', variant, None) + pop_attr = find_attribute(elem, 'POPULATE', variant, "yes") + + if pop_attr.lower() == "no": + return None, True + elif pop_attr != "yes": + raise ValidationError("Unknown value in POPULATE attribute in element %s: %s" % (name, pop_attr)) + + manu, mpn = find_mpn(elem, variant) + dist, distpn = find_distpn(elem, variant) + + #Get physical location information + x = float(elem.get('x')) * ureg.millimeter + y = float(elem.get('y')) * ureg.millimeter + + rot = elem.get('rot') + if rot is None: + rot = 0.0 + else: + rot = float(elem.get('rot')[1:]) #Rotation is specified at R## where ## is the angle in degrees. + + placement = Placement(x, y, rot) + + if (mpn is not None and manu is not None) or (dist is not None and distpn is not None): + args = {} + args['name'] = name + args['package'] = pkg_info + args['placement'] = placement + args['dist'] = dist + args['distpn'] = distpn + args['mpn'] = mpn + args['manu'] = manu + args['value'] = value + args['desc'] = desc + + return type_system.convert_to_type(args, 'logical_part'), True + + return None, False + +def attrib_name(name, variant): + if variant == "MAIN" or variant == "": + return name + + return name + '-' + variant + +def find_attribute(part, name, variant, default=None): + """ + Find the attribute corresponding to the given variant, or if that doesn't exist, + the value corresponding to MAIN, otherwise return the given default + """ + + attrib_var = part.find("./attribute[@name='%s']" % attrib_name(name, variant)) + attrib_main = part.find("./attribute[@name='%s']" % attrib_name(name, 'MAIN')) + + if attrib_var is not None: + return attrib_var.get('value') + + if attrib_main is not None: + return attrib_main.get('value') + + return default + +def find_mpn(part, variant): + """ + See if this part has the variables MPN[-variant] and MANU[-variant] defined + return None if either is undefined. Otherwise return the tuple (manu, mpn) + """ + + mpn = find_attribute(part, 'MPN', variant) + manu = find_attribute(part, 'MANU', variant) + + if mpn is None or manu is None: + return (None, None) + + return (manu, mpn) + +def find_distpn(part, variant): + """ + Try to find a distributor part number for this part + + This can be specified either using a single combined attribute for Digikey + parts (DIGIKEY-PN) or by using 2 attributes specifying the distributor and + the part number (DIST and DIST-PN). If any of these are blank, they are returned + as None. + """ + + #Look for a digikey pn attribute as a shortcut for digikey parts + pn_elem = find_attribute(part, 'DIGIKEY-PN', variant) + if pn_elem is not None: + return ("Digikey", pn_elem) + + pn_elem = find_attribute(part, 'DIST-PN', variant) + dist_elem = find_attribute(part, 'DIST', variant) + + dist = None + pn = None + + if pn_elem is not None: + pn = pn_elem + if dist_elem is not None: + dist = dist_elem + + return (dist, pn) + +def find_package(packages, find_name, display_name): + """ + Create a Package object from the named package + """ + + found = False + + for packagelist in packages: + package = packagelist.find("./package[@name='%s']" % str(find_name)) + if package is not None: + found = True + break + + if found is False: + raise ValidationError("Invalid board file, a package was used but not defined", name=find_name) + + eagle_pads = package.findall("./smd") + eagle_pins = package.findall("./pad") + + pads = [] + pins = [] + + #Eagle boards store all coordinates in mm regardless of how it is displayed + for epad in eagle_pads: + x = float(epad.get('x')) * ureg.millimeter + y = float(epad.get('y')) * ureg.millimeter + w = float(epad.get('dx')) * ureg.millimeter + h = float(epad.get('dy')) * ureg.millimeter + + pad = Pad(x, y, w, h) + pads.append(pad) + + for epin in eagle_pins: + x = float(epin.get('x')) * ureg.millimeter + y = float(epin.get('y')) * ureg.millimeter + drill = float(epin.get('drill')) * ureg.millimeter + + pin = Pin(x,y, drill) + pins.append(pin) + + return Package(display_name, pins, pads) diff --git a/pymomo/pcb/match_engines/__init__.py b/pymomo/pcb/match_engines/__init__.py new file mode 100644 index 0000000..e2762f3 --- /dev/null +++ b/pymomo/pcb/match_engines/__init__.py @@ -0,0 +1,4 @@ +#__init__.py + +from octopart import OctopartMatcher +from cache_only import CacheOnlyMatcher \ No newline at end of file diff --git a/pymomo/pcb/match_engines/cache_only.py b/pymomo/pcb/match_engines/cache_only.py new file mode 100644 index 0000000..8a82ad4 --- /dev/null +++ b/pymomo/pcb/match_engines/cache_only.py @@ -0,0 +1,17 @@ +from pymomo.pcb.bom_matcher import BOMMatcher + +class CacheOnlyMatcher (BOMMatcher): + """ + Only return matches from the existing cache. + + If a component is not in the cache, do not attempt to match + it in any way. This is useful for offline purposes and writing + unit tests. + """ + + + def __init__(self): + super(CacheOnlyMatcher, self).__init__() + + def _match(self): + pass diff --git a/pymomo/pcb/match_engines/octopart.py b/pymomo/pcb/match_engines/octopart.py new file mode 100644 index 0000000..2387b72 --- /dev/null +++ b/pymomo/pcb/match_engines/octopart.py @@ -0,0 +1,137 @@ +#octopart.py +#Wrapper arround Octopart REST API + +import os +import json +import urllib +from pymomo.pcb.bom_matcher import BOMMatcher +from pymomo.utilities.typedargs import * +from pymomo.exceptions import * + +@context() +class OctopartMatcher (BOMMatcher): + """ + A service for pricing parts using the Octopart API. + + Currently parts can be matched using either a distributor's name and part number + or directly using manufacturer's part numbers and the manufacturer's + name. Realtime pricing information is returned as are any errors + during the matching process. + """ + + URL = 'http://octopart.com/api/v3/' + + @param("key", "string", desc="Octopart API Key") + def __init__(self, key=None): + super(OctopartMatcher, self).__init__() + + if key is not None: + self.api_key = key + elif 'OCTOPART_KEY' in os.environ: + self.api_key = os.environ['OCTOPART_KEY'] + else: + raise ArgumentError('You need to either specify an Octopart API Key or have the OCTOPART_KEY environment variable set') + + def _build_call(self, method, args): + """ + Build a URL call with arg=val pairs including the apikey to the url specifying the method + given. + """ + + url = OctopartMatcher.URL + method + '?' + + out_args = [] + out_args.extend(args) + out_args.append(('apikey', self.api_key)) + out_args.append(('include[]', 'short_description')) + out_strings = map(lambda x: urllib.quote(x[0])+'='+urllib.quote(x[1]), out_args) + + query_string = "&".join(out_strings) + + return url + query_string + + def _build_query(self, part): + query = {} + if "distpn" in part: + query['sku'] = part["distpn"] + elif "mpn" in part: + query['mpn'] = part["mpn"] + else: + raise ArgumentError("Octopart API cannot match a part without either a digikey part number or a manufacturer's part number", part=part) + + query['reference'] = part['ref'] + return query + + def _call(self, url): + data = urllib.urlopen(url).read() + response = json.loads(data) + + return response + + def _call_method(self, method, args): + url = self._build_call(method, args) + return self._call(url) + + def _build_offer(self, resp): + """ + Create a part_offer from an octopart api response + """ + + offer = {} + offer['seller'] = resp['seller']['name'] + offer['moq'] = resp['moq'] + offer['stock_quantity'] = resp['in_stock_quantity'] + offer['packaging'] = resp['packaging'] + offer['breaks'] = self._build_breaks(resp['prices']) + + return type_system.convert_to_type(offer, 'part_offer') + + def _build_breaks(self, resp): + """ + Create a price_list object from an octopart API response + """ + + if 'USD' in resp: + return type_system.convert_to_type(resp['USD'], 'price_list') + + return type_system.convert_to_type([], 'price_list') + + def _build_part(self, response): + """ + Create a physical_part object from an octopart api response + """ + + part = {} + + manuname = "" + if 'name' in response['manufacturer']: + manuname = response['manufacturer']['name'] + + part['manu'] = manuname + part['mpn'] = response['mpn'] + + part['desc'] = response['short_description'] + + offers = map(lambda x: self._build_offer(x), response['offers']) + part['offers'] = offers + + return type_system.convert_to_type(part, 'physical_part') + + def _match(self): + for i in xrange(0, len(self.requests), 20): + req = self.requests[i:i+20] + queries = map(lambda x: self._build_query(x), req) + + query = json.dumps(queries) + + resp = self._call_method('parts/match',[('queries',query), ('exact_only', 'true')]) + + for result in resp['results']: + ref = result['reference'] + + if len(result['items']) == 0: + self.errors[ref] = {'msg': 'Did not match'} + continue + + parts = map(lambda x: self._build_part(x), result['items']) + self.matched[ref] = parts diff --git a/pymomo/pcb/package.py b/pymomo/pcb/package.py new file mode 100644 index 0000000..d25c5c4 --- /dev/null +++ b/pymomo/pcb/package.py @@ -0,0 +1,159 @@ +#Objects for dealing with PCB packages, pads and pins + +from pymomo.exceptions import * +from reference import PCBReferenceLibrary +import itertools + +class Package: + """ + A pcb component package. + + This contains information on the pins and pads in the package + as well as the package type. + """ + + def __init__(self, name, pins, pads): + """ + Build a package from a name and a list of pins and pads + + - pins should be a list of Pin objects + - pads should be a list of Pad objects + - name should be an identifier that will let us look up information + about this package in our database of standard packages. + """ + + ref = PCBReferenceLibrary() + + self.pins = pins + self.pads = pads + + refined_name, found = ref.find_package(name) + + if found: + self.name = refined_name + else: + self.name = name + + def __str__(self): + return "%s Package (%d pins, %d pads)" % (self.name, len(self.pins), len(self.pads)) + + def __eq__(self, other): + try: + if self.name != other.name: + return False + + for p1, p2 in itertools.izip_longest(self.pins, other.pins, fillvalue=None): + if p1 != p2: + return False + + for p1, p2 in itertools.izip_longest(self.pads, other.pads, fillvalue=None): + if p1 != p2: + print p1 + print p2 + return False + except AttributeError: + return False + + return True + + def __ne__(self, other): + return not self == other + + +class Pin: + """ + A through hole pin + """ + + def __init__(self, x, y, drill=None, ring=None): + if is_quantity(x) and is_quantity(y): + self.x = x + self.y = y + else: + raise ValidationError("pin position is not specified as a dimensioned quantity", x=x, y=y) + + if drill is not None and not is_quantity(drill): + raise ValidationError("pin drill size is not specified as a dimensioned quantity", x=x, y=y) + + if ring is not None and not is_quantity(ring): + raise ValidationError("pin annular ring is not specified as a dimensioned quantity", x=x, y=y) + + self.drill = drill + self.ring = ring + + def __eq__(self, other): + try: + return self.x == other.x and self.y == other.y and self.drill == other.drill and self.ring == other.ring + except AttributeError: + return False + + def __ne__(self, other): + return not self == other + + +class Pad: + """ + A surface mount pad + """ + + def __init__(self, x, y, width, height): + if is_quantity(x) and is_quantity(y): + self.x = x + self.y = y + else: + raise ValidationError("smt pad position is not specified as a dimensioned quantity", x=x, y=y) + + if is_quantity(width) and is_quantity(height): + self.width = width + self.height = height + else: + raise ValidationError("width and heigh is not specified as a dimensioned quantity", width=width, height=height) + + def __str__(self): + return "SMT Pad at (%s, %s) size: (%s, %s)" % (str(self.x), str(self.y), str(self.width), str(self.height)) + + def __eq__(self, other): + try: + return (self.x == other.x) and (self.y == other.y) and (self.width == other.width) and (self.height == other.height) + except AttributeError: + return False + + def __ne__(self, other): + return not self == other + +class Placement: + """ + A class describing the physical placement and rotation of a PCB component. + """ + + def __init__(self, x, y, rotation=0.0): + """ + Store an x,y location and a rotation (in degrees) + """ + + if is_quantity(x) and is_quantity(y): + self.x = x + self.y = y + else: + raise ValidationError("placement position is not specified as a dimensioned quantity", x=x, y=y) + + self.rotation = rotation + + def __eq__(self, other): + try: + return self.x == other.x and self.y == other.y and self.rotation == other.rotation + except AttributeError: + return False + + def __ne__(self, other): + return not self == other + + def __str__(self): + return "(%s, %s) rotated %d degrees" % (str(self.x), str(self.y), self.rotation) + + +def is_quantity(val): + if hasattr(val, 'magnitude') and hasattr(val, 'units'): + return True + + return False diff --git a/pymomo/pcb/partcache.py b/pymomo/pcb/partcache.py new file mode 100644 index 0000000..9fd0b28 --- /dev/null +++ b/pymomo/pcb/partcache.py @@ -0,0 +1,123 @@ +#partcache.py +#A simple expiring cache built on top of sqlite + +import sys +import os.path + +from pymomo.utilities.paths import MomoPaths +from time import time +import sqlite3 +import cPickle + +overriden_cache = None +overriden_expiry = None + +class PartCache(object): + """ + A simple persistent key-value store with expiration times + + Persistence comes from a sqlite backend and each time the + file is opened, all entries are checked to see if they should + be expired and deleted if so. + """ + + DefaultCachePath = os.path.join(MomoPaths().settings, 'pcb_part_cache.db') + DefaultExpiry = 1*24*60*60 #Default expiration date is 1 day + + def __init__(self, cache=None, no_expire=None, expiration=None): + if cache is None: + if overriden_cache is not None: + cache = overriden_cache + else: + cache = PartCache.DefaultCachePath + + if expiration is None: + expiration = PartCache.DefaultExpiry + else: + expiration = int(expiration) + + self.connection = sqlite3.connect(cache) + self.cursor = self.connection.cursor() + + self.file = cache + self.expiration = expiration + + self._setup_table() + + if no_expire is None: + if overriden_expiry is None or overriden_expiry is False: + self._expire_old() + elif not no_expire: + self._expire_old() + + def _setup_table(self): + query = 'create table if not exists PartCache (key TEXT PRIMARY KEY, created INTEGER, part blob);' + self.cursor.execute(query) + self.connection.commit() + + def _expire_old(self): + query = "delete from PartCache where strftime('%%s','now') - created > %d" % self.expiration + self.cursor.execute(query) + self.connection.commit() + + return self.cursor.rowcount + + def size(self): + """ + Return the number of entries in this cache + """ + + query = 'select count(*) from PartCache' + self.cursor.execute(query) + return self.cursor.fetchone()[0] + + def _check_valid(self, obj): + exp = obj['expiration'] + + cur = time() + + if exp > cur: + return True + + return False + + def get(self, id): + query = 'select part from PartCache where key is ?' + self.cursor.execute(query, (id,)) + + val = self.cursor.fetchone() + if val is None: + raise KeyError("id not in cache: %s", str(id)) + + return cPickle.loads(str(val[0])) + + def try_get(self, id): + try: + return self.get(id) + except KeyError: + return None + + def set(self, id, obj): + now = time() + data = cPickle.dumps(obj) + + query = "insert into PartCache values (?, strftime('%s','now'), ?)" + self.cursor.execute(query, (id, sqlite3.Binary(data))) + self.connection.commit() + + +def default_cachefile(path): + """ + Set the default cache file used to back PartCache objects + """ + + global overriden_cache + overriden_cache = path + +def default_noexpire(do_expire): + """ + Set whether cache should not expire by default + """ + + global overriden_expiry + overriden_expiry = do_expire diff --git a/pymomo/pybom/pricing.py b/pymomo/pcb/pricing.py similarity index 52% rename from pymomo/pybom/pricing.py rename to pymomo/pcb/pricing.py index 863c2c5..3ea94dd 100644 --- a/pymomo/pybom/pricing.py +++ b/pymomo/pcb/pricing.py @@ -17,30 +17,44 @@ def __init__(self, valid_sellers=None, invalid_sellers=[], def _wrap_single(self, s): if (isinstance(s, basestring)): - return [s] + return [self._canonicalize_name(s)] if s is None: return [] - return s + return map(lambda x: self._canonicalize_name(x), s) + + def _canonicalize_name(self, name): + """ + Given a name, like a distributor's name, convert it to + lowercase and remove all spaces,_ or - characters + """ + + if name is None: + return "None" + + return name.lower().replace(' ', '').replace('-', '').replace('_', '') def validate(self, offer, quantity): if offer.invalid: return False - if offer.seller_name in self.invalid_sellers: + if offer.moq is not None and quantity < offer.moq: + return False + + if self._canonicalize_name(offer.seller) in self.invalid_sellers: return False - if offer.packaging in self.invalid_packages: + if self._canonicalize_name(offer.packaging) in self.invalid_packages: return False - if len(self.valid_sellers)>0 and offer.seller_name not in self.valid_sellers: + if len(self.valid_sellers)>0 and self._canonicalize_name(offer.seller) not in self.valid_sellers: return False - if len(self.valid_packages)>0 and offer.packaging not in self.valid_packages: + if len(self.valid_packages)>0 and self._canonicalize_name(offer.packaging) not in self.valid_packages: return False - if self.in_stock and quantity > offer.in_stock_quant: + if self.in_stock and quantity > offer.stock_quantity: return False return True \ No newline at end of file diff --git a/pymomo/pcb/production.py b/pymomo/pcb/production.py new file mode 100644 index 0000000..7e3af92 --- /dev/null +++ b/pymomo/pcb/production.py @@ -0,0 +1,125 @@ +#production.py +#A set of routines for building a set of production files for fabricating and assembling +#circuit boards + +import os.path +import zipfile +from pymomo.exceptions import * +from pymomo.utilities.paths import MomoPaths +from datetime import date +import platform +import subprocess + +class ProductionFileGenerator: + """ + Helpful routines for building production files for PCB fabrication and assembly + """ + + def __init__(self, board): + config = MomoPaths().config + + template_file = os.path.join(config, 'pcb', board.fab_engine + ".json") + self.template = self._load_template(template_file, board.fab_template) + self.board = board + + def save_readme(self, path): + """ + Create a README file for this board based on the information it contains + and the template for the production files. + """ + + basename = self._basename() + + with open(path, "w") as f: + f.write("PCB Fabrication Files\n") + f.write("%s\n" % self.board.company) + f.write("Name: %s\n" % self.board.part) + f.write("Revision: %s\n" % self.board.revision) + f.write("Dimensions: %sx%s %s\n" % (self.board.width, self.board.height, self.board.units)) + f.write("Date Created: %s\n\n" % str(date.today())) + f.write("Folder Contents:\n") + + for name, layer in self.template['layers'].iteritems(): + f.write("%s.%s: %s\n" % (basename, layer['extension'], name)) + + def _build_production(self, outdir, layer): + basename = self._basename() + path = os.path.join(outdir, "%s.%s" %(basename, layer['extension'])) + + + self.board.board.build_production_file(path, layer['program_layers'], layer['type']) + + if 'remove' in layer: + remname = "%s.%s" % (basename, layer['remove']) + os.remove(os.path.join(outdir, remname)) + + def build_fab(self, fab_dir): + """ + Create production Gerber and Excellon files for pcb fabrication + """ + + self._ensure_dir_exists(fab_dir) + self.save_readme(os.path.join(fab_dir, 'README.txt')) + + for name, layer in self.template['layers'].iteritems(): + self._build_production(fab_dir, layer) + + def build_assembly(self, ass_dir): + self._ensure_dir_exists(ass_dir) + self._build_production(ass_dir, self.template['assembly']) + + def build_production(self, variant, output_dir): + basename = self._basename() + + fab_dir = os.path.join(output_dir, 'fabrication') + ass_dir = os.path.join(output_dir, 'assembly') + + #Create fabrication files + self.build_fab(fab_dir) + self.zipfab(fab_dir, os.path.join(output_dir, basename + '_fab')) + + #Create assembly files + self.build_assembly(ass_dir) + + #Create BOM + bompath = os.path.join(ass_dir, "BOM_%s.xlsx" % basename) + self.board.export_bom(bompath, variant, format='excel') + + def _basename(self): + return self.board.part.replace(' ', '_').lower() + + def _ensure_dir_exists(self, output_dir): + if not os.path.isdir(output_dir): + os.makedirs(output_dir) + + def _load_template(self, templatefile, template_name): + """ + Load a list of possible templates for making gerbers from a json file + """ + + import json + with open(templatefile, "r") as f: + templates = json.load(f) + + if "templates" not in templates: + raise DataError("Unknown file format for gerber generation template file", path=templatefile, reason="did not contain a 'templates' key") + + options = templates['templates'] + + if template_name not in options: + raise ArgumentError("Unknown template specified for gerber generation", possible_names=options.keys(), name=template_name) + + return options[template_name] + + def zipfab(self, path, output): + """ + Create a zipfile of the direction path with the name output.zip that will expand into a directory + with the same name as output containing all of the files in path. + """ + + zip = zipfile.ZipFile(output+'.zip', 'w', zipfile.ZIP_DEFLATED) + for root, dirs, files in os.walk(path): + for file in files: + zip.write(os.path.join(root, file), os.path.join(os.path.basename(output), file)) + + zip.close() diff --git a/pymomo/pybom/reference.py b/pymomo/pcb/reference.py similarity index 95% rename from pymomo/pybom/reference.py rename to pymomo/pcb/reference.py index f5230a2..5398a11 100644 --- a/pymomo/pybom/reference.py +++ b/pymomo/pcb/reference.py @@ -7,7 +7,7 @@ import re import sys -from ..utilities.config import ConfigFile +from pymomo.utilities.config import ConfigFile data_file = ConfigFile('pcb_library') @@ -69,7 +69,7 @@ def has_value(self, name): def find_package(self, pkg): """ Look for the footprint in our list of known packages. Return a tuple - of (package name, bool found). + of (package name, bool found). """ for name, val in self.packages.iteritems(): if pkg in val: diff --git a/pymomo/pcb/types/__init__.py b/pymomo/pcb/types/__init__.py new file mode 100644 index 0000000..c98a49c --- /dev/null +++ b/pymomo/pcb/types/__init__.py @@ -0,0 +1,7 @@ +import physical_part +import part_offer +import price_list +import logical_part +import bom_line +import unit_price +import price \ No newline at end of file diff --git a/pymomo/pcb/types/bom_line.py b/pymomo/pcb/types/bom_line.py new file mode 100644 index 0000000..988fdb4 --- /dev/null +++ b/pymomo/pcb/types/bom_line.py @@ -0,0 +1,62 @@ +import collections +import string + +def convert(arg, **kwargs): + if arg is None: + return None + + if isinstance(arg, BOMLine): + return arg + elif isinstance(arg, collections.Sequence) and not isinstance(arg, basestring): + return BOMLine(arg) + + raise ValueError("Creating a BOMLine from any type other than a BOMLine object or list of parts is not supported") + +def default_formatter(arg, **kwargs): + return str(arg) + +class BOMLine: + digits = frozenset(string.digits) + + def __init__(self, parts): + self.parts = parts + + self.count = len(parts) + proto = parts[0] + + self.manu = proto.manu + self.mpn = proto.mpn + self.package = proto.package + self.desc = proto.desc + self.dist = proto.dist + self.distpn = proto.distpn + self.value = proto.value + self.refs = sorted([x.name for x in parts], key=self._ref_number) + self.type = self._extract_ref_type() + + if len(self.refs) > 0: + self.lowest = self._ref_number(self.refs[0]) + else: + self.lowest = 0 + + def _ref_number(self, id): + """ + Extract the numeric part of a reference number + """ + + return int(''.join([c for c in id if c in BOMLine.digits])) + + def _extract_ref_type(self): + if len(self.refs) == 0: + return None + + r = self.refs[0] + + for i in xrange(0, len(r)): + if not r[i].isalpha(): + break + + return r[:i] + + def __str__(self): + return "%d %s: %s" % (self.count, self.desc, self.refs) diff --git a/pymomo/pcb/types/logical_part.py b/pymomo/pcb/types/logical_part.py new file mode 100644 index 0000000..6ab57bf --- /dev/null +++ b/pymomo/pcb/types/logical_part.py @@ -0,0 +1,109 @@ +#logical_part.py + +import re +from pymomo.pcb import reference + +def convert(arg, **kwargs): + if arg is None: + return None + + if isinstance(arg, Part): + return arg + + elif isinstance(arg, dict): + return Part(**arg) + + raise ValueError("Creating a logical_part from any type other than a Part object is not supported") + +def validate_matchable(arg, **kwargs): + if not arg.matchable(): + raise ValueError("part does not have enough metadata to be matchable") + +def default_formatter(arg, **kwargs): + return str(arg) + +class Part: + """ + An electronic component. + """ + + ref = reference.PCBReferenceLibrary() + + def __init__(self, name, package, placement, mpn=None, manu=None, dist=None, distpn=None, value=None, desc=None): + """ + Create a part object from the data passed + """ + + self.name = name + self.package = package + self.mpn = mpn + self.manu = manu + self.value = value + self.distpn = distpn + self.dist = dist + self.placement = placement + self.desc = desc + + #If no description is given try creating a generic one based on the type of the part (resistor, etc) + if self.desc is None: + self.desc = Part.ref.find_description(self.name, self.value) + + def matchable(self): + """ + Return true if this part has enough information to be matched and priced online + """ + + return self.name != None and ((self.manu != None and self.mpn != None) or (self.dist != None and self.distpn != None)) + + def placeable(self): + """ + Return true if this part has enough information to be physically located on a board + """ + + return (self.package != None and self.placement != None) + + def complete(self): + """ + Return true if this part has all required information for BOM matching and placing + """ + + return self.matchable() and self.placeable() + + def unique_id(self): + """ + Return a unique key that can be used to group multiple parts that are identical. + """ + + if self.manu and self.mpn: + return "%s_%s" % (self._canonicalize(self.manu), self._canonicalize(self.mpn)) + + return "%s_%s" % (self._canonicalize(self.dist), self._canonicalize(self.distpn)) + + def _canonicalize(self, string): + """ + Given a string, remove all spaces, - and _ and make it lower case. + """ + + s = string.lower() + return s.replace(' ', '').replace('-','').replace('_','') + + def __str__(self): + string = "" + string += "Part %s" % str(self.name) + '\n' + string += " Manufacturer: " + str(self.manu) + '\n' + string += " MPN: " + str(self.mpn) + '\n' + string += " Distributor: " + str(self.dist) + '\n' + string += " Distributor PN: " + str(self.distpn) + '\n' + string += " Package: " + str(self.package) + '\n' + string += " Location: " + str(self.placement) + '\n' + string += " Description: " + str(self.desc) + + return string + + def __eq__(self, other): + try: + return self.name == other.name and self.package == other.package and self.mpn == other.mpn and \ + self.manu == other.manu and self.value == other.value and self.distpn == other.distpn and \ + self.dist == other.dist and self.placement == other.placement and self.desc == other.desc + except AttributeError: + raise False diff --git a/pymomo/pcb/types/part_offer.py b/pymomo/pcb/types/part_offer.py new file mode 100644 index 0000000..ae46a2a --- /dev/null +++ b/pymomo/pcb/types/part_offer.py @@ -0,0 +1,50 @@ +#partoffer.py +from pymomo.utilities.formatting import indent_block + +def convert(arg, **kwargs): + if isinstance(arg, PartOffer): + return arg + + if not isinstance(arg, dict): + raise ValueError("conversion to part_offer from anything other than dict not supported") + + req = ['seller', 'moq', 'packaging', 'breaks', 'stock_quantity'] + for r in req: + if r not in arg: + raise ValueError('invalid dictionary used to initialize part_offer') + + return PartOffer(arg['seller'], arg['packaging'], arg['breaks'], arg['stock_quantity'], arg['moq']) + +def default_formatter(arg, **kwargs): + return str(arg) + +class PartOffer: + def __init__(self, seller, packaging, breaks, stock, moq): + self.seller = seller + self.packaging = packaging + self.invalid = False + + self.breaks = breaks + self.stock_quantity = stock + + if moq is not None: + self.moq = moq + elif len(self.breaks.breaks) > 0: + self.moq = self.breaks.breaks[0][0] + else: + self.moq = None + + def best_price(self, quantity): + if self.invalid: + return None + + return self.breaks.unit_price(quantity) + + def __str__(self): + res = "Seller: %s\n Packaging: %s\n Stock: %d\n Minimum Order: %s" % (self.seller, self.packaging, self.stock_quantity, str(self.moq)) + breakstr = str(self.breaks) + if breakstr != "": + res += "\n Breaks\n" + res += indent_block(breakstr, 4) + + return res diff --git a/pymomo/pcb/types/physical_part.py b/pymomo/pcb/types/physical_part.py new file mode 100644 index 0000000..2641138 --- /dev/null +++ b/pymomo/pcb/types/physical_part.py @@ -0,0 +1,82 @@ +#physical_part.py +#An object representing a unique physical circuit part that can be sold by +#zero or more sellers + +def convert(arg, **kwargs): + if isinstance(arg, PhysicalPart): + return arg + + if not isinstance(arg, dict): + raise ValueError("conversion to physical_part from anything other than dict not supported") + + req = ['manu', 'mpn'] + for r in req: + if r not in arg: + raise ValueError('invalid dictionary used to initialize physical_part') + + desc = None + if 'desc' in arg: + desc = arg['desc'] + + offers = [] + if 'offers' in arg: + offers = arg['offers'] + + return PhysicalPart(arg['manu'], arg['mpn'], offers, desc=desc) + +def default_formatter(arg, **kwargs): + return str(arg) + +class PhysicalPart: + """ + A unique physical circuit component that can be purchased from zero or more suppliers. + This would correspond to a single BOM line on a Bill of Materials for a circuit board. + """ + + def __init__(self, manu, mpn, offers, desc=None): + self.offers = offers + self.manu = manu + self.mpn = mpn + self.desc = desc + + def best_price(self, quantity, requirements): + valid_offers = filter(lambda x: requirements.validate(x, quantity), self.offers) + prices = map(lambda x: (x.best_price(quantity), x), valid_offers) + valid_prices = filter(lambda x: x[0] is not None, prices) + + sorted_prices = sorted(valid_prices, key=lambda x:x[0]) + + if len(sorted_prices) > 0: + return sorted_prices[0] + + return None + + def __str__(self): + manu = self.manu + mpn = self.mpn + desc = self.desc + + #Make sure we get rid of non-ascii characters in case this + #string is printed since it would choke. + if isinstance(manu, unicode): + manu = manu.encode('ascii','ignore') + if isinstance(mpn, unicode): + mpn = mpn.encode('ascii','ignore') + if isinstance(desc, unicode): + desc = desc.encode('ascii','ignore') + + header = "Manufacturer: %s\nMPN: %s" % (manu, mpn) + + if self.desc is not None: + header += "\nDescription: %s" % desc + + offers = map(str, self.offers) + + if len(offers) > 0: + offerstr = "\n".join(offers) + offerstr = " " + offerstr.replace('\n', '\n ') + header += '\nOffers\n' + header += offerstr + + return header + \ No newline at end of file diff --git a/pymomo/pcb/types/price.py b/pymomo/pcb/types/price.py new file mode 100644 index 0000000..a59baf6 --- /dev/null +++ b/pymomo/pcb/types/price.py @@ -0,0 +1,15 @@ +#price.py +#An object encapsulating the price of something + +from decimal import Decimal + +two_places = Decimal('0.01') + +def convert(arg, **kwargs): + if isinstance(arg, Decimal): + return arg + + return Decimal(arg) + +def default_formatter(arg, **kwargs): + return str(arg.quantize(two_places)) diff --git a/pymomo/pybom/octopart/prices.py b/pymomo/pcb/types/price_list.py similarity index 52% rename from pymomo/pybom/octopart/prices.py rename to pymomo/pcb/types/price_list.py index 9253eb8..9ebcd5b 100644 --- a/pymomo/pybom/octopart/prices.py +++ b/pymomo/pcb/types/price_list.py @@ -3,22 +3,24 @@ from decimal import Decimal -class PriceList: - def __init__(self, octo_pricelist): - """ - Create from an octopart PriceList json object - Assume that we want to work in USD - """ +def convert(arg, **kwargs): + if isinstance(arg, PriceList): + return arg - self.breaks = [] + if isinstance(arg, basestring): + raise ValueError("Converting to price_list from string is not yet supported") - if 'USD' not in octo_pricelist: - raise ValueError('USD Not in Price Object') + return PriceList(arg) - plist = octo_pricelist['USD'] +def default_formatter(arg, **kwargs): + return str(arg) - for i in xrange(0, len(plist)): - q,val = plist[i] +class PriceList: + def __init__(self, breaks): + self.breaks = [] + + for i in xrange(0, len(breaks)): + q,val = breaks[i] price = Decimal(val) self.breaks.append((q, price)) @@ -41,4 +43,9 @@ def total_price(self, quantity): if up is None: return None - return quantity*up \ No newline at end of file + return quantity*up + + def __str__(self): + breaklist = map(lambda x: "%d: %s" % (x[0], x[1]), self.breaks) + + return "\n".join(breaklist) \ No newline at end of file diff --git a/pymomo/pcb/types/unit_price.py b/pymomo/pcb/types/unit_price.py new file mode 100644 index 0000000..983fae4 --- /dev/null +++ b/pymomo/pcb/types/unit_price.py @@ -0,0 +1,26 @@ +#unit_price.py +#An object encapsulating a list of pricebreaks + +from decimal import Decimal, getcontext + +def convert(arg, **kwargs): + if isinstance(arg, UnitPrice): + return arg + + if isinstance(arg, basestring): + raise ValueError("Converting to price_list from string is not yet supported") + + return UnitPrice(**arg) + +def default_formatter(arg, **kwargs): + return str(arg) + +class UnitPrice: + def __init__(self, price, offer): + self.price = price + self.offer = offer + + def __str__(self): + tp = Decimal('0.01') + pricestr = str(self.price.quantize(tp)) + return "$%s from %s as %s" % (pricestr, self.offer.seller, self.offer.packaging) diff --git a/pymomo/pybom/board.py b/pymomo/pybom/board.py deleted file mode 100644 index 46f9759..0000000 --- a/pymomo/pybom/board.py +++ /dev/null @@ -1,415 +0,0 @@ -#board.py - -import csv -import itertools -from part import Part -from xml.etree import ElementTree -from datetime import date -from octopart.identifier import PartIdentifier -from octopart.octopart import Octopart -from reference import PCBReferenceLibrary -from package import Package - -class Board: - def __init__(self, name, file, variants, partname, width, height, revision, unknown, nopop): - """ - Create a Board Object with 1 or more assembly variants from the variant dictionary passed in. - """ - - self.name = name - self.file = file - self.variants = {varname: self._process_variant(var) for (varname,var) in variants.iteritems()} - self.partname = partname - self.width = width - self.height = height - self.revision = revision - self.unknown = unknown - self.no_pop = nopop - - def list_variants(self): - print "Assembly Variants" - - for k in self.variants.iterkeys(): - print k - - def get_ids(self, variant=None): - """ - Return a set containing all of the reference identifiers (C1, R7, etc.) in - the variant passed, or in all variants if no variant is passed - """ - - if variant is None: - vars = self.variants.keys() - else: - vars = [variant] - - ids = set() - - for var in vars: - for line in self.variants[var]: - refs = map(lambda x: x.name, line) - ids.update(refs) - - #If we're asked for all of the ids on this board, also include those that - #may never be populated - if variant is None: - ids.update(self.no_pop) - - return ids - - def print_variant(self, key): - var = self.variants[key] - - print "Variant", key - for v in var: - print map(lambda x: x.name, v) - - def get_unique_parts(self): - parts = {} - - for v in self.variants.iterkeys(): - for line in self.variants[v]: - part = PartIdentifier(line[0]) - parts[part.build_reference()] = part - - return parts - - def lookup_parts(self): - parts = self.get_unique_parts() - - op = Octopart() - - self.oracle = op.match_identifiers(parts.values()) - - def price_variant(self, variant, multiplier, requirements=None): - if variant not in self.variants: - raise ValueError("Invalid variant passed to price_variant") - - self.lookup_parts() - unmatched = [] - - prices = [] - - for line in self.variants[variant]: - id = PartIdentifier(line[0]).build_reference() - - if id not in self.oracle: - unmatched.append(line) - continue - - quant = int(len(line)*multiplier + 0.5) #round up - unit_price = self.oracle[id].best_price(quant, requirements) - - if unit_price is None: - unmatched.append(line) - continue - - prices.append((line, unit_price)) - - return (prices, unmatched) - - def export_list(self, variant): - """ - Return an array with all of the digikey part numbers in this variant so that - it can be concatenated and used to order parts - """ - - export = [] - ignored = [] - - for line in self.variants[variant]: - pn = line[0].digipn - if pn is None or pn == "": - ignored.append(line[0].name) - continue - - export += [pn]*len(line) - - print "Ignoring Parts" - print ignored - - return export - - def variant_data(self, variant, include_costs=False, cost_quantity=1, requirements=None): - """ - Return a dictionary describing this variant, suitable for using to fill a template. - Create an array of BOM line items corresponding to this assembly variant - """ - - res = {} - - if include_costs: - self.lookup_parts() - res['include_costs'] = True - res['cost_quantity'] = cost_quantity - else: - res['include_costs'] = False - - var = self.variants[variant] - lib = PCBReferenceLibrary() - today = date.today() - - res['company'] = "WellDone International" - res['part'] = self.partname - res['variant'] = variant - res['revision'] = self.revision - res['date'] = today.strftime('%x') - res['lines'] = [] - for line in var: - num = len(line) - refs = map(lambda x: x.name, line) - foot = lib.find_package(line[0].package)[0] - value = line[0].value - manu = line[0].manu - mpn = line[0].mpn - distpn = line[0].digipn - dist = "" - descr = line[0].desc - if distpn: - dist = "Digikey" - - unitprice = "" - lineprice = "" - - if include_costs: - quantity = num*cost_quantity - id = PartIdentifier(line[0]) - if id.build_reference() in self.oracle: - price = self.oracle[id.build_reference()].best_price(quantity, requirements) - if price is not None: - unitprice = price[0] - lineprice = num*unitprice - - ldict = {} - ldict['qty'] = num - ldict['refs'] = refs - ldict['footprint'] = foot - ldict['value'] = value - ldict['manu'] = manu - ldict['mpn'] = mpn - ldict['dist'] = dist - ldict['description'] = descr - ldict['distpn'] = distpn - ldict['unitprice'] = unitprice - ldict['lineprice'] = lineprice - ldict['pkg_info'] = line[0].pkg_info - - res['lines'].append(ldict) - - return res - - def export_bom(self, variant, stream, include_costs=False, cost_quantity=1, requirements=None): - var = self.variants[variant] - - lib = PCBReferenceLibrary() - - lineno = 1 - - today = date.today() - - if include_costs: - self.lookup_parts() - - if isinstance(stream, str): - bom = open(stream, "wb") - close = True - else: - bom = stream - close = False - - writ = csv.writer(bom) - - writ.writerow(["WellDone"]) - writ.writerow(["BOM: %s (%s)" % (self.partname, variant)]) - writ.writerow(["Revision: %s" % self.revision]) - writ.writerow(['Date Created: %s' % (today.strftime('%x'))]) - - if include_costs: - writ.writerow(['Costs calculated for %d units' % cost_quantity]) - - writ.writerow([]) - writ.writerow([]) - - headers = ["Item", "Qty", "Reference Design", "Value", "Footprint", "Description", "Manufacturer", "Manu. Part", "Distributor", "Dist. Part"] - if include_costs: - headers += ['Unit Price', 'Line Price'] - - writ.writerow(headers) - - for line in var: - num = len(line) - refs = ", ".join(map(lambda x: x.name, line)) - foot = lib.find_package(line[0].package)[0] - value = line[0].value - manu = line[0].manu - mpn = line[0].mpn - distpn = line[0].digipn - dist = "" - descr = line[0].desc - if distpn: - dist = "Digikey" - - row = [lineno, num, refs, value, foot, descr, manu, mpn, dist, distpn] - - if include_costs: - quantity = num*cost_quantity - id = PartIdentifier(line[0]) - if id.build_reference() in self.oracle: - price = self.oracle[id.build_reference()].best_price(quantity, requirements) - if price is not None: - unitprice = price[0] - lineprice = num*unitprice - - row += [str(unitprice), str(lineprice)] - - writ.writerow(row) - - lineno += 1 - - #If there are unpopulated parts, list them here - unpopulated = self.get_ids() - self.get_ids(variant) - - if len(unpopulated) > 0: - writ.writerow([]) - - s = ", ".join(unpopulated) - writ.writerow(["Unpopulated Parts", s]) - - if close: - bom.close() - - @staticmethod - def FromEagle(brd_file): - """ - Create a Board from EAGLE Board file by parsing attribute tags embedded with the - components. Create different assembly variants for each different configuration - specified in the file - """ - - tree = ElementTree.parse(brd_file) - if tree is None: - raise ValueError("File %s is not a valid XML file" % brd_file) - - root = tree.getroot() - - #Find all of the defined packages in this file - packages = {x.name: x for x in map(lambda x: Package(x), root.findall('.//package'))} - - elems = root.find("./drawing/board/elements") - if elems is None: - raise ValueError("File %s does not contain a list of elements. This is an invalid board file" % brd_file) - - parts = list(elems) - - unknown = [] - nopop = [] - constant = [] - variable = [] - variants = find_variants(root) - - all_vars = set(variants) - - #Create a part object for each part that has a valid digikey-pn - #If there are multiple digikey part numbers, create a part for each one - for part in parts: - valid_part = False - found_vars = set() - nopop_vars = set() - - for v in variants: - p,found = Part.FromBoardElement(part, v, packages) - if p and found: - variable.append( (v, p) ) - found_vars.add(v) - valid_part = True - elif found: - #If this part has digipn == "" or Populate=no, then it should not be populated in this variant - valid_part = True - nopop_vars.add(v) - nopop.append(part.get('name',"Unknown Name")) - - #See if this is a constant part (with no changes in different variants) - p,found = Part.FromBoardElement(part, "", packages) - if p and len(found_vars) == 0 and len(nopop_vars) == 0: - constant.append(p) - valid_part = True - elif p and len(found_vars) != len(all_vars): - #there are some variants that want the default part and some that want a change - for var in (all_vars - found_vars) - nopop_vars: - variable.append( (var, p) ) - valid_part = True - - #Make sure this part has information for at least one assembly variant - if not valid_part: - unknown.append(part.get('name',"Unknown Name")) - - vars = {} - #Create multiple variants - for var in variants: - vars[var] = constant + filter_sublists(variable, var) - - return Board("test", 'test', vars, - partname=find_attribute(root, 'PARTNAME'), - width=find_attribute(root, 'WIDTH'), - height=find_attribute(root, 'HEIGHT'), - revision=find_attribute(root, 'REVISION'), - unknown=unknown, - nopop=nopop) - - def _process_variant(self, parts): - """ - Group items by unique key and return tuples of identical parts - """ - - bomitems = [] - - sparts = sorted(parts, key=lambda x:x.unique_id()) - for k,g in itertools.groupby(sparts, lambda x:x.unique_id()): - bomitems.append(list(g)) - - return bomitems - - -def remove_prefix(s, prefix): - if not s.startswith(prefix): - return s - - return s[len(prefix):] - -def filter_sublists(master, key): - """ - Given a list of lists where each of the sublist elements are tuples (key, value), - return a concatenated list of all values in tuples that match key - """ - - concat = itertools.chain(master) - - filtered = filter(lambda x: x[0] == key, concat) - - return map(lambda x:x[1], filtered) - -def get_variant_id(pn_name): - known_prefixes = ['DIGIKEY-PN'] - - for p in known_prefixes: - pn_name = remove_prefix(pn_name, p) - - if pn_name.startswith('-'): - pn_name = pn_name[1:] - - return pn_name - -def find_attribute(root, name): - attr = root.find(".//attribute[@name='%s']" % name) - if attr is None: - raise ValueError("Required global board attribute not found: %s" % name) - - return attr.get('value') - -def find_variants(root): - vars = root.findall(".//attribute[@value='%s']" % 'ASSY-VARIANT') - - if len(vars) == 0: - return ["MAIN"] - else: - return map(lambda x: x.get('name'), vars) \ No newline at end of file diff --git a/pymomo/pybom/octopart/__init__.py b/pymomo/pybom/octopart/__init__.py deleted file mode 100644 index 18cd8bb..0000000 --- a/pymomo/pybom/octopart/__init__.py +++ /dev/null @@ -1 +0,0 @@ -#__init__.py \ No newline at end of file diff --git a/pymomo/pybom/octopart/identifier.py b/pymomo/pybom/octopart/identifier.py deleted file mode 100644 index 08b729c..0000000 --- a/pymomo/pybom/octopart/identifier.py +++ /dev/null @@ -1,27 +0,0 @@ -#identifier.py - -class PartIdentifier: - def __init__(self, part=None, digipn=None): - - if digipn is not None: - self.sku = digipn - self.mpn = None - self.manu = None - else: - self.sku = part.digipn - self.mpn = part.mpn - self.manu = part.manu - - def build_reference(self): - if self.sku: - return "__SKU__" + self.sku - - else: - return "__MPN__" + str(self.mpn) - - def build_query(self): - if self.sku is not None: - return {'sku': self.sku, 'reference': self.build_reference()} - - else: - return {'mpn': self.mpn, 'reference': self.build_reference()} diff --git a/pymomo/pybom/octopart/octopart.py b/pymomo/pybom/octopart/octopart.py deleted file mode 100644 index 2f9916f..0000000 --- a/pymomo/pybom/octopart/octopart.py +++ /dev/null @@ -1,94 +0,0 @@ -#octopart.py -#Wrapper arround Octopart REST API - -import os -import json -import urllib -import pprint -from physicalpart import PhysicalPart -import sys -from partcache import PartCache - -from pymomo.utilities.paths import MomoPaths - -class Octopart: - URL = 'http://octopart.com/api/v3/' - - def __init__(self, key=None): - if key is not None: - self.api_key = key - elif 'OCTOPART_KEY' in os.environ: - self.api_key = os.environ['OCTOPART_KEY'] - else: - raise ValueError('You need to either specify an Octopart API Key or have OCTOPART_KEY set in the shell') - - self.cache = PartCache() - - def _build_call(self, method, args): - """ - Build a URL call with arg=val pairs including the apikey to the url specifying the method - given. - """ - - url = Octopart.URL + method + '?' - - out_args = [] - out_args.extend(args) - out_args.append(('apikey', self.api_key)) - out_strings = map(lambda x: urllib.quote(x[0])+'='+urllib.quote(x[1]), out_args) - - query_string = "&".join(out_strings) - - return url + query_string - - def _call(self, url): - data = urllib.urlopen(url).read() - response = json.loads(data) - - return response - - def _call_method(self, method, args): - url = self._build_call(method, args) - return self._call(url) - - def match_identifiers(self, skus): - matched = {} - unmatched = [] - uncached_skus = [] - - #Check if we have these parts in our cache - for sku in skus: - id = sku.build_reference() - part = self.cache.get(id) - - if part is None: - uncached_skus.append(sku) - else: - matched[id] = part - - #Otherwise fetch from Octopart and cache the results - for i in xrange(0, len(uncached_skus), 20): - req = uncached_skus[i:i+20] - queries = map(lambda x: x.build_query(), req) - - query = json.dumps(queries) - - resp = self._call_method('parts/match',[('queries',query)]) - - for result in resp['results']: - ref = result['reference'] - - if len(result['items']) == 0: - unmatched.append(ref) - print "Did not match %s" % ref - continue - - if len(result['items']) > 1: - print "Found %d parts for %s" % (len(result['items']), ref) - - part = PhysicalPart(result['items'][0]) - self.cache.set(ref, part) - - matched[ref] = part - - return matched \ No newline at end of file diff --git a/pymomo/pybom/octopart/partcache.py b/pymomo/pybom/octopart/partcache.py deleted file mode 100644 index d6921bc..0000000 --- a/pymomo/pybom/octopart/partcache.py +++ /dev/null @@ -1,60 +0,0 @@ -#partcache.py -#A simple expiring cache built on top of zodb - -import sys -import os.path - -from pymomo.utilities.paths import MomoPaths -from ZODB.FileStorage import FileStorage -from ZODB.DB import DB -from time import time -import transaction - -class PartCache(object): - URL = 'http://octopart.com/api/v3/' - CachePath = os.path.join(MomoPaths().config, 'part_lookup.cache') - DefaultExpiry = 10*24*60*60 #Default expiration date is 10 days - - def __init__(self): - storage = FileStorage(PartCache.CachePath) - self.db = DB(storage) - self.connection = self.db.open() - self.root = self.connection.root() - - def _check_valid(self, obj): - exp = obj['expiration'] - - cur = time() - - if exp > cur: - return True - - return False - - def get(self, id): - if id not in self.root: - return None - - obj = self.root[id] - - if self._check_valid(obj): - obj['last_used'] = time() - self.root[id] = obj - transaction.commit() - return obj['data'] - - #If it's expired, delete it - del self.root[id] - return None - - def set(self, id, obj, expire=None): - if expire is None: - expire = PartCache.DefaultExpiry - - entry = {} - entry['expiration'] = time() + expire - entry['data'] = obj - entry['last_used'] = time() - - self.root[id] = entry - transaction.commit() \ No newline at end of file diff --git a/pymomo/pybom/octopart/partoffer.py b/pymomo/pybom/octopart/partoffer.py deleted file mode 100644 index b69ddd0..0000000 --- a/pymomo/pybom/octopart/partoffer.py +++ /dev/null @@ -1,30 +0,0 @@ -#partoffer.py -#Class encapsulating an Octopart PartOffer response, exposing the -#price breaks - -from prices import PriceList -import utilities - -class PartOffer: - def __init__(self, response): - utilities.assert_class(response, 'PartOffer') - - self.seller_name = response['seller']['name'] - self.seller_uid = response['seller']['uid'] - self.packaging = response['packaging'] - self.invalid = False - - try: - self.breaks = PriceList(response['prices']) - except ValueError, e: - self.breaks = None - self.invalid = True - - self.in_stock_quant = response['in_stock_quantity'] - - - def best_price(self, quantity): - if self.invalid: - return None - - return self.breaks.unit_price(quantity) \ No newline at end of file diff --git a/pymomo/pybom/octopart/physicalpart.py b/pymomo/pybom/octopart/physicalpart.py deleted file mode 100644 index 0ee88e7..0000000 --- a/pymomo/pybom/octopart/physicalpart.py +++ /dev/null @@ -1,23 +0,0 @@ -#physicalpart.py -#A class representing a Part in the Octopart API - -from partoffer import PartOffer -import utilities - -class PhysicalPart: - def __init__(self, response): - utilities.assert_class(response, 'Part') - - self.offers = map(lambda x: PartOffer(x), response['offers']) - - def best_price(self, quantity, requirements): - valid_offers = filter(lambda x: requirements.validate(x, quantity), self.offers) - prices = map(lambda x: (x.best_price(quantity), x.seller_name, x.packaging), valid_offers) - valid_prices = filter(lambda x: x[0] is not None, prices) - - sorted_prices = sorted(valid_prices, key=lambda x:x[0]) - - if len(sorted_prices) > 0: - return sorted_prices[0] - - return None \ No newline at end of file diff --git a/pymomo/pybom/octopart/utilities.py b/pymomo/pybom/octopart/utilities.py deleted file mode 100644 index 0366475..0000000 --- a/pymomo/pybom/octopart/utilities.py +++ /dev/null @@ -1,5 +0,0 @@ -#utilities.py - -def assert_class(resp, classname): - if '__class__' not in resp or resp['__class__'] != classname: - raise ValueError('Creating class from invalid response dictionary') \ No newline at end of file diff --git a/pymomo/pybom/package.py b/pymomo/pybom/package.py deleted file mode 100644 index 550ba49..0000000 --- a/pymomo/pybom/package.py +++ /dev/null @@ -1,18 +0,0 @@ - -class Package: - """ - A pcb component package containing the number of pads and pins in the package - as well as its name. - """ - - def __init__(self, element): - if element.tag != 'package': - raise TypeError('You must pass an ElementTree element to Package.__init__') - - self.name = element.get('name', default="Unknown Name") - - pads = element.findall('./smd') - self.num_pads = len(pads) - - pins = element.findall('./pad') - self.num_pins = len(pins) \ No newline at end of file diff --git a/pymomo/pybom/part.py b/pymomo/pybom/part.py deleted file mode 100644 index ca2de49..0000000 --- a/pymomo/pybom/part.py +++ /dev/null @@ -1,159 +0,0 @@ -#part.py - -import re -import reference - -known_types = { - "C": "capacitor", - "R": "resistor", - "D": "diode", - "JP": "connector", - "U": "ic", - "XTAL": "crystal", - "L": "inductor", - "LED": "led" -} - -class_table = { - "" -} - -class Part: - """ - An electronic component. - """ - - ref = reference.PCBReferenceLibrary() - - @staticmethod - def FromBoardElement(elem, variant, packages): - """ - Create a Part object from an element in an EAGLE board file. - pn_attrib specifies the attribute name that contains the correct - manufacturer or distributor part number for this part. - - @TODO: - - parse pn_attrib to determine whether its a distributor pn or a manu pn - and set attributes accordingly - """ - - pkg = elem.get('package', 'Unknown Package') - pkg_info = None - - if pkg in packages: - pkg_info = packages[pkg] - - name = elem.get('name', 'Unnamed') - value = elem.get('value', 'No Value') - - #allow overriding this package with a custom attribute - pkg = find_attribute(elem, 'FOOTPRINT', variant, pkg) - desc = find_attribute(elem, 'DESCRIPTION', variant, None) - pop_attr = find_attribute(elem, 'POPULATE', variant, "yes") - - if pop_attr.lower() == "no": - return None, True - elif pop_attr != "yes": - raise ValueError("Unknown value in POPULATE attribute in element %s: %s" % (name, pop_attr)) - - #Only keep the value for part types where that is meaningful - if not Part.ref.has_value(name): - value = None - - (manu, mpn) = find_mpn(elem, variant) - digipn = find_digipn(elem, variant) - - if (mpn is not None and manu is not None) or (digipn is not None and digipn != ""): - return Part(name, pkg, digipn=digipn, mpn=mpn, manu=manu, value=value, desc=desc, pkg_info=pkg_info), True - elif mpn == "" or digipn == "": - return None, True - - return None, False - - def __init__(self, name, package, mpn=None, manu=None, digipn=None, value=None, desc=None, pkg_info=None): - """ - Create a part object from the data passed - """ - - self.name = name - self.package = package - self.mpn = mpn - self.manu = manu - self.value = value - self.digipn = digipn - self.desc = desc - self.pkg_info = pkg_info - - #If no description is given try creating a generic one based on the type of the part (resistor, etc) - if self.desc is None: - self.desc = Part.ref.find_description(self.name, self.value) - - def _parse_type(self, type): - refs = type.split(',') - alpha_pat = re.compile('[a-zA-Z]+') - - prefix = re.match(alpha_pat, refs[0]) - - if prefix is None: - return "unknown" - - def unique_id(self): - """ - Return a unique key that can be used to group multiple parts that are identical. - """ - - if self.manu and self.mpn: - return "%s_%s" % (self.manu, self.mpn) - - return "Digikey_%s" % self.digipn - - def package_info(self): - if self.pkg_info is None: - return {'pins': 0, 'pads': 0} - - return {'pins': self.pkg_info.num_pins, 'pads': self.pkg_info.num_pads} - -def attrib_name(name, variant): - if variant == "MAIN" or variant == "": - return name - - return name + '-' + variant - -def find_attribute(part, name, variant, default=None): - """ - Find the attribute corresponding the given variant, or if that doesn't exist, - the value corresponding to MAIN, otherwise return the given default - """ - - attrib_var = part.find("./attribute[@name='%s']" % attrib_name(name, variant)) - attrib_main = part.find("./attribute[@name='%s']" % attrib_name(name, 'MAIN')) - - if attrib_var is not None: - return attrib_var.get('value') - - if attrib_main is not None: - return attrib_main.get('value') - - return default - -def find_mpn(part, variant): - """ - See if this part has the variables MPN[-variant] and MANU[-variant] defined - return None if either is undefined. Otherwise return the tuple (manu, mpn) - """ - - mpn = part.find("./attribute[@name='%s']" % attrib_name('MPN', variant)) - manu = part.find("./attribute[@name='%s']" % attrib_name('MANU', variant)) - - if mpn is None or manu is None: - return (None, None) - - return (manu.get('value', 'Unknown'), mpn.get('value', 'Unknown')) - -def find_digipn(part, variant): - pn_elem = part.find("./attribute[@name='%s']" % attrib_name('DIGIKEY-PN', variant)) - - if pn_elem is None: - return None - - return pn_elem.get('value', "") \ No newline at end of file diff --git a/pymomo/pyeagle.py b/pymomo/pyeagle.py deleted file mode 100644 index 884fab1..0000000 --- a/pymomo/pyeagle.py +++ /dev/null @@ -1,181 +0,0 @@ -#pyeagle.py - -#Routines that encapsulate the use of EAGLE to automatically generate CAM files -#and assembly drawings. - -import shutil -import subprocess -import os.path -import os -from pybom.board import Board -import zipfile -import utilities.config - -settings = utilities.config.ConfigFile('settings') - -def execute_eagle(args): - eagle = 'eagle' - - with open(os.devnull, 'wb') as DEVNULL: - subprocess.check_call([eagle] + args, stdout=DEVNULL, stderr=DEVNULL) - -def export_image(board, output, layers): - process_section(board, output, 'PS', layers) - -def process_section(board, output, type, layers): - """ - Call the EAGLE program to process a board file and produce a - CAM output with the given layers on it. - - @param type can be either "gerber" or "excellon" to generate - either Gerber 274X files or excellon drill files - @param output complete path to the output file location - @param layers the EAGLE layers to include in this CAM file - @param board the complete path to the board file - """ - - #Options from EAGLE's built in gerber file for producing 2 layer boards - #and argument names from EAGLE -? command - args = ['-X', '-f+', '-c+', '-O+'] - - if type == 'gerber': - args.append('-dGERBER_RS274X') - remext = '.gpi' - elif type == 'excellon': - args.append('-dEXCELLON') - remext = '.dri' - elif type == 'PS': - args.append('-dPS') - args.append('-h11') - args.append('-w7.75') - args.append('-s2.5') - else: - raise ValueError("Invalid type specified (must be gerber or excellon), was: %s" % type) - - args.append('-o%s' % output) - - args.append(board) - - for layer in layers: - args.append(layer) - - execute_eagle(args) - - #if it's gerber, remove gpi file - #if it's excellon, remove drd file - if type is not 'PS': - (base, ext) = os.path.splitext(output) - remfile = base + remext - - os.remove(remfile) - -def process_2layer(board, output_dir, basename, paste=False): - """ - Process the eagle file board specified to produce the correct gerber files for - fabrication and assembly. All output files will have the same basename with - different extensions - """ - - top_silk = os.path.join(output_dir, basename + '.plc') - top_copper = os.path.join(output_dir, basename + '.cmp') - bot_copper = os.path.join(output_dir, basename + '.sol') - bot_mask = os.path.join(output_dir, basename + '.sts') - top_mask = os.path.join(output_dir, basename + '.stc') - drill = os.path.join(output_dir, basename + '.drd') - top_cream = os.path.join(output_dir, basename + '.crm') - - #Make sure the output dir exists - ensure_dir_exists(output_dir) - - process_section(board, top_copper, 'gerber', ['Top', 'Pads', 'Vias']) - process_section(board, bot_copper, 'gerber', ['Bottom', 'Pads', 'Vias']) - process_section(board, top_silk, 'gerber', ['Dimension', 'tPlace', 'tNames']) - process_section(board, top_mask, 'gerber', ['tStop']) - process_section(board, bot_mask, 'gerber', ['bStop']) - process_section(board, drill, 'excellon', ['Drills', 'Holes']) - - if paste: - process_section(board, top_cream, 'gerber', ['tCream']) - -def ensure_dir_exists(output_dir): - if not os.path.isdir(output_dir): - os.makedirs(output_dir) - -def create_readme(output_dir, basename, brd_obj, paste=False): - with open(os.path.join(output_dir, 'README.txt'), "w") as f: - f.write('WellDone\n') - f.write("PCB Fabrication Files\n") - f.write("Name: %s\n" % brd_obj.partname) - f.write("Revision: %s\n" % brd_obj.revision) - f.write("Dimensions: %sx%s inches\n" % (brd_obj.width, brd_obj.height)) - f.write("Contact: Tim Burke \n\n") - f.write("Folder Contents:\n") - f.write('%s: Top Silkscreen\n' % (basename+'.plc')) - f.write('%s: Top Copper\n' % (basename+'.cmp')) - f.write('%s: Top Soldermask\n' % (basename+'.stc')) - f.write('%s: Bottom Soldermask\n' % (basename+'.sts')) - f.write('%s: Bottom Copper\n' % (basename+'.sol')) - - if paste: - f.write('%s: Top Cream\n' % (basename+'.crm')) - - f.write('%s: Excellon Drill File\n' % (basename+'.drd')) - -def build_assembly_drawing(board, output): - """ - Create an assembly drawing for this board. File created will by a PS file - """ - - export_image(board, output, ['tPlace', 'tNames', 'tDocu', 'Document', 'Reference','Dimension']) - -def build_production(board, output_dir, paste=False): - """ - Build the set of production files associated with this EAGLE board file. - Directory structure will be: - output_dir - - fabrication - + CAM files - + README - - basename_fab.zip - - assembly - + basename_bom.csv - + basename_drawing.ps - """ - - board_obj = Board.FromEagle(board) - - basename = board_obj.partname - - fab_dir = os.path.join(output_dir, 'fabrication') - ass_dir = os.path.join(output_dir, 'assembly') - - #Ensure old fabrication and assembly files are removed - if os.path.isdir(fab_dir): - shutil.rmtree(fab_dir) - if os.path.isdir(ass_dir): - shutil.rmtree(ass_dir) - - #Create fabrication files - process_2layer(board, fab_dir, basename, paste=paste) - create_readme(fab_dir, basename, board_obj, paste=paste) - zipfab(fab_dir, os.path.join(output_dir, basename + '_fab')) - - #Create assembly files - ensure_dir_exists(ass_dir) - build_assembly_drawing(board, os.path.join(ass_dir, basename + '_drawing.ps')) - - #Build a BOM for each assembly variant - for var in board_obj.variants.keys(): - board_obj.export_bom(var,os.path.join(ass_dir, basename + "_" + var + "_bom.csv")) - -def zipfab(path, output): - """ - Create a zipfile of the direction path with the name output.zip that will expand into a directory - with the same name as output containing all of the files in path. - """ - zip = zipfile.ZipFile(output+'.zip', 'w', zipfile.ZIP_DEFLATED) - for root, dirs, files in os.walk(path): - for file in files: - zip.write(os.path.join(root, file), os.path.join(os.path.basename(output), file)) - - zip.close() \ No newline at end of file diff --git a/pymomo/scripts/modtool.py b/pymomo/scripts/modtool.py index 492073c..d1bceec 100644 --- a/pymomo/scripts/modtool.py +++ b/pymomo/scripts/modtool.py @@ -10,6 +10,7 @@ from pymomo.commander.meta import * from pymomo.commander.exceptions import * from pymomo.hex16.convert import * +from pymomo.utilities.typedargs import type_system import cmdln from colorama import Fore, Style import pytest @@ -430,5 +431,6 @@ def error(self, text): sys.exit(1) def main(): + type_system.interactive = True modtool = ModTool() return modtool.main() \ No newline at end of file diff --git a/pymomo/scripts/momo.py b/pymomo/scripts/momo.py index e322b66..69dde6e 100644 --- a/pymomo/scripts/momo.py +++ b/pymomo/scripts/momo.py @@ -6,11 +6,10 @@ from pymomo.exceptions import * from pymomo.utilities.typedargs import annotate from pymomo.commander.meta import initialization -from pymomo.hex import ControllerBlock, HexFile -from pymomo.sim.simulator import Simulator from pymomo.utilities import build from pymomo.utilities.rcfile import RCFile -import pymomo.syslog +from multiprocessing import freeze_support +import traceback def main(): line = sys.argv[1:] @@ -20,29 +19,32 @@ def main(): norc = True line = line[1:] - if len(line) > 0 and line[0] == '--rcdir': + if len(line) > 0 and line[0] == '--rcfile': rc = RCFile('momo') print rc.path return 0 shell = HierarchicalShell('momo', no_rc=norc) + shell.root_update(annotate.find_all(initialization)) - shell.root_update(annotate.find_all(build)) - name,con = annotate.context_from_module(pymomo.syslog) - shell.root_add(name, con) - shell.root_add('ControllerBlock', ControllerBlock) - shell.root_add('HexFile', HexFile) - shell.root_add('Simulator', Simulator) + shell.root_add("build", "pymomo.utilities.build,build") + shell.root_add("SystemLog", "pymomo.syslog") + shell.root_add("pcb", "pymomo.pcb") + shell.root_add('ControllerBlock', "pymomo.hex,ControllerBlock") + shell.root_add('HexFile', "pymomo.hex,HexFile") + shell.root_add('Simulator', "pymomo.sim,Simulator") finished = False try: while len(line) > 0: line, finished = shell.invoke(line) + except APIError: + traceback.print_exc() + return 1 except MoMoException as e: print e.format() - #if the command passed on the command line fails, then we should #just exit rather than drop the user into a shell. return 1 @@ -67,7 +69,7 @@ def complete(text, state): readline.set_completer_delims(' \t\n;') #Handle Mac OS X special libedit based readline #See: http://stackoverflow.com/questions/7116038/python-tab-completion-mac-osx-10-7-lion - if 'libedit' in readline.__doc__: + if readline.__doc__ is not None and 'libedit' in readline.__doc__: readline.parse_and_bind("bind ^I rl_complete") else: readline.parse_and_bind("tab: complete") @@ -86,6 +88,8 @@ def complete(text, state): try: while len(line) > 0: line, finished = shell.invoke(line) + except APIError as e: + traceback.print_exc() except MoMoException as e: print e.format() diff --git a/pymomo/scripts/pcbtool.py b/pymomo/scripts/pcbtool.py index c09ffa2..b583944 100644 --- a/pymomo/scripts/pcbtool.py +++ b/pymomo/scripts/pcbtool.py @@ -82,18 +82,14 @@ def do_bom(self, subcmd, opts, id): reqs = self.build_pricemodel(opts) brd = self.get_board(id) - if opts.format == 'csv': - out,close = self.build_outstream(opts) - brd.export_bom(opts.variant, out, include_costs=opts.prices, cost_quantity=opts.units, requirements=reqs) - if close: - out.close() - elif opts.format == 'pdf' or opts.format == 'html': + if opts.format == 'pdf' or opts.format == 'html': data = brd.variant_data(opts.variant, include_costs=opts.prices, cost_quantity=opts.units, requirements=reqs) - templ = RecursiveTemplate('bom_template.html') - templ.add(data) - formatted = templ.format_temp() + print data + #templ = RecursiveTemplate('bom_template.html') + #templ.add(data) + #formatted = templ.format_temp() - shutil.move(formatted, opts.output) + #shutil.move(formatted, opts.output) @cmdln.option('-v', '--variant', action='append', default=None, help="Print detailed information about the listed assembly variants.") def do_info(self, subcmd, opts, id): @@ -216,20 +212,20 @@ def do_quote(self, subcmd, opts, id): prices, unmatched = brd.price_variant(opts.variant, multiplier, model) for line in unmatched: - print Fore.RED + "\nCould not find:", map(lambda x: x.name, line), Style.RESET_ALL + '\n' + print Fore.RED + "Could not find:", map(lambda x: x.name, line), Style.RESET_ALL print "Price for %d Units with %.0f%% excess" % (units, opts.excess) total_price = 0.0 for i, line in enumerate(prices): parts, offer = line - price = float(offer[0])*multiplier + price = float(offer['price'])*multiplier if opts.lines: - desc = "from %s" % offer[1] - if offer[2] is not None: - desc += " in %s" % (offer[2]) + desc = "from %s" % offer['offer'].seller + if offer['offer'].packaging is not None and offer['offer'].packaging is not "": + desc += " in %s" % (offer['offer'].packaging,) - print "Line %d: $%.2f (%d @ $%.2f) %s" % (i+1, price, len(parts), float(offer[0]), desc), map(lambda x: x.name, parts) + print "Line %d: $%.2f (%d @ $%.2f) %s" % (i+1, price, len(parts), float(offer['price']), desc), map(lambda x: x.name, parts) total_price += price diff --git a/pymomo/syslog/logdefinition.py b/pymomo/syslog/logdefinition.py index 5e604a5..5b4ee0e 100644 --- a/pymomo/syslog/logdefinition.py +++ b/pymomo/syslog/logdefinition.py @@ -65,10 +65,10 @@ def is_valid_length(self, params): return True def add_data(self, name, type, format, is_list=False): - if not typedargs.is_known_type(type): + if not typedargs.type_system.is_known_type(type): raise ArgumentError("Parameter has unknown type", param=name, type=type) - if format is not None and not typedargs.is_known_format(type, format): + if format is not None and not typedargs.type_system.is_known_format(type, format): raise ArgumentError("Parameter has unknown format", param=name, type=type, format=format) definition = LogDataItem(name, type, format, is_list) diff --git a/pymomo/syslog/syslog.py b/pymomo/syslog/syslog.py index eeb5351..ae8c269 100644 --- a/pymomo/syslog/syslog.py +++ b/pymomo/syslog/syslog.py @@ -31,7 +31,7 @@ def __init__(self, raw_entries): self.entries = [] entries = [] - self.mapper = LogDefinitionMap() + self._mapper = LogDefinitionMap() for entry in raw_entries: if len(entries) != 0 and entry.typecode == RawLogEntry.ContinuationType: @@ -111,13 +111,13 @@ def add_ldf(self, file): Add an ldf file to help parse log entries """ - self.mapper.add_ldf(file) + self._mapper.add_ldf(file) def __str__(self): val = "" for entry in self.entries: - ldf = self.mapper.map(entry.header.msg) + ldf = self._mapper.map(entry.header.msg) if ldf.message is not None: desc = ldf.message else: diff --git a/pymomo/utilities/config.py b/pymomo/utilities/config.py index ffc6591..6347c4e 100644 --- a/pymomo/utilities/config.py +++ b/pymomo/utilities/config.py @@ -2,8 +2,9 @@ import os import json +from pymomo.utilities.paths import MomoPaths -conf_dir = os.path.join(os.path.dirname(__file__), '..', '..', '..', 'config') +conf_dir = MomoPaths().config class ConfigFile: """ diff --git a/pymomo/utilities/console.py b/pymomo/utilities/console.py index 7b7f59c..29775f7 100644 --- a/pymomo/utilities/console.py +++ b/pymomo/utilities/console.py @@ -1,8 +1,12 @@ import sys +from pymomo.utilities.typedargs import type_system class ProgressBar: """ - A simple progress bar that updates itself in the console + A simple progress bar that updates itself in the console. + + If the program is being run in interactive mode, display the progress_bar + otherwise do not display it """ def __init__(self, title, count=100): @@ -11,16 +15,22 @@ def __init__(self, title, count=100): self.count = count def start(self): - sys.stdout.write(self.title + ": [" + "-"*40 + "]" + chr(8)*41) - sys.stdout.flush() + if type_system.interactive: + sys.stdout.write(self.title + ": [" + "-"*40 + "]" + chr(8)*41) + sys.stdout.flush() + self.prog = 0 def progress(self, x): x = int(x * 40 // self.count) - sys.stdout.write("#" * (x - self.prog)) - sys.stdout.flush() + + if type_system.interactive: + sys.stdout.write("#" * (x - self.prog)) + sys.stdout.flush() + self.prog = x def end(self): - sys.stdout.write("#" * (40 - self.prog) + "]\n") - sys.stdout.flush() \ No newline at end of file + if type_system.interactive: + sys.stdout.write("#" * (40 - self.prog) + "]\n") + sys.stdout.flush() diff --git a/pymomo/utilities/formatting.py b/pymomo/utilities/formatting.py new file mode 100644 index 0000000..3c0bb9c --- /dev/null +++ b/pymomo/utilities/formatting.py @@ -0,0 +1,19 @@ +#formatting.py + +def indent_block(string_block, level): + indent = ' '*level + repstr = '\n' + indent + + retval = string_block.replace('\n', repstr) + return indent + retval + +def indent_list(list, level): + """ + Join a list of strings, one per line with 'level' spaces before each one + """ + + indent = ' '*level + joinstr = '\n' + indent + + retval = joinstr.join(list) + return indent + retval \ No newline at end of file diff --git a/pymomo/utilities/typedargs/__init__.py b/pymomo/utilities/typedargs/__init__.py index 669cba3..fdeb766 100644 --- a/pymomo/utilities/typedargs/__init__.py +++ b/pymomo/utilities/typedargs/__init__.py @@ -1,4 +1,4 @@ #External API functions from this package -from annotate import param, returns, context, finalizer, takes_cmdline, annotated -from typeinfo import is_known_type, is_known_format, convert_to_type, format_value, get_type_size, inject_type, load_external_types \ No newline at end of file +from annotate import param, returns, context, finalizer, takes_cmdline, annotated, return_type +from typeinfo import type_system \ No newline at end of file diff --git a/pymomo/utilities/typedargs/annotate.py b/pymomo/utilities/typedargs/annotate.py index a289c11..1998be3 100644 --- a/pymomo/utilities/typedargs/annotate.py +++ b/pymomo/utilities/typedargs/annotate.py @@ -1,10 +1,10 @@ #annotate.py - from decorator import decorator from pymomo.exceptions import * import inspect -import types +from typeinfo import type_system from collections import namedtuple +import sys class BasicContext(dict): pass @@ -31,10 +31,10 @@ def _check_and_execute(f, *args, **kwargs): #Ensure that only MoMoException subclasses are passed by the caller try: retval = f(*convargs, **convkw) - except MoMoException as e: - raise e + except MoMoException: + raise except Exception as unknown: - raise APIError(str(unknown.args)) + raise APIError(str(unknown.args)), None, sys.exc_info()[2] return retval @@ -47,7 +47,7 @@ def _process_arg(f, arg, value): """ if arg in f.params: - val = getattr(f.params[arg], 'convert')(value) + val = type_system.convert_to_type(value, f.types[arg]) else: val = value @@ -201,8 +201,13 @@ def print_help(f): print " - %s (%s): %s" % (key, type, desc) def print_retval(f, value): + if hasattr(f, 'typed_retval') and f.typed_retval == True: + print type_system.format_return_value(f, value) + return + if not hasattr(f, 'retval'): print str(value) + elif f.retval.printer[0] is not None: f.retval.printer[0](value) elif f.retval.desc != "": @@ -217,8 +222,8 @@ def find_all(container): context = BasicContext() for name in names: - #Ignore __ names - if name.startswith('__'): + #Ignore _ and __ names + if name.startswith('_'): continue if isinstance(container, dict): @@ -229,7 +234,12 @@ def find_all(container): #Check if this is an annotated object that should be included. Check the type of #annotated to avoid issues with module imports where someone did from annotate import * #into the module causing an annotated symbol to be defined as a decorator - if hasattr(obj, 'annotated') and isinstance(getattr(obj, 'annotated'), int): + + #If we are in a dict context then strings point to lazily loaded modules so include them + #too. + if isinstance(container, dict) and isinstance(obj, basestring): + context[name] = obj + elif hasattr(obj, 'annotated') and isinstance(getattr(obj, 'annotated'), int): context[name] = obj return context @@ -259,6 +269,9 @@ def context_from_module(module): def check_returns_data(f): + if hasattr(f, 'typed_retval') and f.typed_retval == True: + return True + if not hasattr(f, 'retval'): return False @@ -269,10 +282,7 @@ def param(name, type, *validators, **kwargs): def _param(f): f = annotated(f) - if not hasattr(types, type): - raise AttributeError('Unknown parameter type: %s' % str(type)) - - f.params[name] = getattr(types, type) + f.params[name] = type_system.get_type(type) f.types[name] = type f.valids[name] = _parse_validators(f.params[name], validators) @@ -304,6 +314,25 @@ def _returns(f): return _returns +def return_type(type, formatter=None): + """ + Specify that this function returns a typed value + + type must be a type known to the MoMo type system and formatter + must be a valid formatter for that type + """ + + def _returns(f): + annotated(f) + f.typed_retval = True + f.retval_type = type_system.get_type(type) + f.retval_typename = type + f.retval_formatter = formatter + + return f + + return _returns + def context(name=None): """ Declare that a class defines a MoMo context for use with the momo function for discovering diff --git a/pymomo/utilities/typedargs/shell.py b/pymomo/utilities/typedargs/shell.py index a41ce3b..ef34d7f 100644 --- a/pymomo/utilities/typedargs/shell.py +++ b/pymomo/utilities/typedargs/shell.py @@ -6,10 +6,11 @@ import annotate import inspect import shlex -import typeinfo +from typeinfo import type_system from pymomo.utilities.rcfile import RCFile import os.path import platform +import importlib posix_lex = platform.system() != 'Windows' @@ -57,7 +58,7 @@ def import_types(package, module=None): else: path = os.path.join(package, module) - typeinfo.load_external_types(path) + type_system.load_external_types(path) def print_dir(context): doc = inspect.getdoc(context) @@ -114,11 +115,35 @@ def _do_help(context, line): return [], True +def deferred_add(add_action): + """ + Perform a lazy import of a context so that we don't have a huge initial startup time + loading all of the modules that someone might want even though they probably only + will use a few of them. + """ + + module, sep, obj = add_action.partition(',') + + mod = importlib.import_module(module) + if obj == "": + name, con = annotate.context_from_module(mod) + return con + + if hasattr(mod, obj): + return getattr(mod, obj) + + raise ArgumentError("Attempted to import nonexistent object from module", module=module, object=obj) + def find_function(context, funname): func = None if isinstance(context, dict): if funname in context: func = context[funname] + + #Allowed lazy loading of functions + if isinstance(func, basestring): + func = deferred_add(func) + context[funname] = func elif hasattr(context, funname): func = getattr(context, funname) diff --git a/pymomo/utilities/typedargs/typeinfo.py b/pymomo/utilities/typedargs/typeinfo.py index 5090a13..b1a75a0 100644 --- a/pymomo/utilities/typedargs/typeinfo.py +++ b/pymomo/utilities/typedargs/typeinfo.py @@ -2,138 +2,278 @@ #Basic routines for converting information from string or other binary #formats to python types and for displaying those types in supported #formats +#TODO: +#- Extend the type system to use a recursive parser to allow complex +# types to be built from other complex types. from pymomo.exceptions import * -import annotate import types import os.path import imp -def convert_to_type(value, type, **kwargs): - """ - Convert value to type 'type' - - If the conversion routine takes various kwargs to - modify the conversion process, **kwargs is passed - through to the underlying conversion function - """ +#Start working on recursive parser +#import pyparsing +#symbolchars = pyparsing.Regex('[_a-zA-Z][_a-zA-Z0-9]*') +#typename = pyparsing.Word(symbolchars) - if not is_known_type(type): - raise ArgumentError("type is not known to type system", type=type) +#simpletype = typename +#complextype = pyparsing.Forward() - typeobj = getattr(types, type) +#typelist = pyparsing.delimitedList(simpletype | complextype, ',') +#complextype << typename + pyparsing.Literal('(').suppress() + typelist + pyparsing.Literal(')').suppress() - conv = typeobj.convert(value, **kwargs) - return conv +#statement = -def get_type_size(type): +class TypeSystem(object): """ - Get the size of this type for converting a hex string to the - type. Return 0 if the size is not known. + TypeSystem permits the inspection of defined types and supports + converted string and binary values to and from these types. """ - if not is_known_type(type): - raise ArgumentError("type is not known to type system", type=type) + def __init__(self, *args): + """ + Create a TypeSystem by importing all of the types defined in modules passed + as arguments to this function. Each module is imported using + """ - typeobj = getattr(types, type) + self.interactive = False + self.known_types = {} + self.type_factories = {} - if hasattr(typeobj, 'size'): - return typeobj.size() + for arg in args: + self.load_type_module(arg) - return 0 + def convert_to_type(self, value, type, **kwargs): + """ + Convert value to type 'type' -def format_value(value, type, format=None, **kwargs): - """ - Convert value to type and format it as a string + If the conversion routine takes various kwargs to + modify the conversion process, **kwargs is passed + through to the underlying conversion function + """ - type must be a known type in the type system and format, - if given, must specify a valid formatting option for the - specified type. - """ + typeobj = self.get_type(type) - typed_val = convert_to_type(value, type, **kwargs) - typeobj = getattr(types, type) + conv = typeobj.convert(value, **kwargs) + return conv - #Allow types to specify default formatting functions as 'default_formatter' - #otherwise if not format is specified, just convert the value to a string - if format is None: - if hasattr(typeobj, 'default_formatter'): - format_func = getattr(typeobj, 'default_formatter') - return format_func(typed_val, **kwargs) + def get_type_size(self, type): + """ + Get the size of this type for converting a hex string to the + type. Return 0 if the size is not known. + """ - return str(typed_val) + typeobj = self.get_type(type) - formatter = "format_%s" % str(format) - if not hasattr(typeobj, formatter): - raise ArgumentError("Unknown format for type", type=type, format=format, formatter_function=formatter) + if hasattr(typeobj, 'size'): + return typeobj.size() - format_func = getattr(typeobj, formatter) - return format_func(typed_val, **kwargs) + return 0 -def is_known_type(type): - """ - Check if type is known to the type system. + def format_value(self, value, type, format=None, **kwargs): + """ + Convert value to type and format it as a string - Returns boolean indicating if type is known. - """ + type must be a known type in the type system and format, + if given, must specify a valid formatting option for the + specified type. + """ - if not isinstance(type, basestring): - raise ArgumentError("type must be a string naming a known type", type=type) + typed_val = self.convert_to_type(value, type, **kwargs) + typeobj = self.get_type(type) - if not hasattr(types, type): - return False + #Allow types to specify default formatting functions as 'default_formatter' + #otherwise if not format is specified, just convert the value to a string + if format is None: + if hasattr(typeobj, 'default_formatter'): + format_func = getattr(typeobj, 'default_formatter') + return format_func(typed_val, **kwargs) - return True + return str(typed_val) -def is_known_format(type, format): - """ - Check if format is known for given type. + formatter = "format_%s" % str(format) + if not hasattr(typeobj, formatter): + raise ArgumentError("Unknown format for type", type=type, format=format, formatter_function=formatter) - Returns boolean indicating if format is valid for the specified type. - """ + format_func = getattr(typeobj, formatter) + return format_func(typed_val, **kwargs) + + def _validate_type(self, typeobj): + """ + Validate that all required type methods are implemented. + + At minimum a type must have: + - a convert() or convert_binary() function + - a default_formatter() function + + Raises an ArgumentError if the type is not valid + """ + + if not (hasattr(typeobj, "convert") or hasattr(typeobj, "convert_binary")): + raise ArgumentError("type is invalid, does not have convert or convert_binary function", type=typeobj, methods=dir(typeobj)) + + if not hasattr(typeobj, "default_formatter"): + raise ArgumentError("type is invalid, does not have default_formatter function", type=typeobj, methods=dir(typeobj)) + + def is_known_type(self, type): + """ + Check if type is known to the type system. + + Returns boolean indicating if type is known. + """ + + if not isinstance(type, basestring): + raise ArgumentError("type must be a string naming a known type", type=type) + + if type in self.known_types: + return True - if not is_known_type(type): return False - typeobj = getattr(types, type) - formatter = "format_%s" % str(format) - if not hasattr(typeobj, formatter): + def split_type(self, typename): + """ + Given a potentially complex type, split it into its base type and specializers + """ + + name = self._canonicalize_type(typename) + if '(' not in name: + return name, False, [] + + base,sub = name.split('(') + if len(sub) == 0 or sub[-1] != ')': + raise ArgumentError("syntax error in complex type, no matching ) found", passed_type=typename, basetype=base, subtype_string=sub) + + sub = sub[:-1] + + subs = sub.split(',') + return base, True, subs + + def instantiate_type(self, typename, base, subtypes): + """ + Instantiate a complex type + """ + + if base not in self.type_factories: + raise ArgumentError("unknown complex base type specified", passed_type=typename, base_type=base) + + BaseType = self.type_factories[base] + + #Make sure all of the subtypes are valid + for s in subtypes: + try: + self.get_type(s) + except MoMoException as e: + raise ArgumentError("could not instantiate subtype for complex type", passed_type=typename, sub_type=s, error=e) + + typeobj = BaseType.Build(*subtypes, type_system=self) + self.inject_type(typename, typeobj) + + def _canonicalize_type(self, typename): + return typename.replace(' ', '') + + def get_type(self, typename): + """ + Return the type object corresponding to a type name. + """ + + typename = self._canonicalize_type(typename) + + type, is_complex, subtypes = self.split_type(typename) + if not self.is_known_type(typename): + if is_complex: + self.instantiate_type(typename, type, subtypes) + else: + raise ArgumentError("get_type called on unknown type", type=typename) + + return self.known_types[typename] + + def is_known_format(self, type, format): + """ + Check if format is known for given type. + + Returns boolean indicating if format is valid for the specified type. + """ + + typeobj = self.get_type(type) + + formatter = "format_%s" % str(format) + if not hasattr(typeobj, formatter): + return False + + return True + + def _is_factory(self, typeobj): + """ + Determine if typeobj is a factory for producing complex types + """ + + if hasattr(typeobj, 'Build'): + return True + return False - return True + def format_return_value(self, function, value): + """ + Format the return value of a function based on the annotated type information + """ -def inject_type(name, typeobj): - """ - Given a module-like object that defines a type, add it to our type system so that - it can be used with the momo tool and with other annotated API functions. - """ + return self.format_value(value, function.retval_typename, function.retval_formatter) - #TODO add checking each type for the minimum required content like a default formatter, - #a conversion function, etc. + def inject_type(self, name, typeobj): + """ + Given a module-like object that defines a type, add it to our type system so that + it can be used with the momo tool and with other annotated API functions. + """ - if is_known_type(name): - raise ArgumentError("attempting to inject a type that is already defined", type=name) + name = self._canonicalize_type(name) + base,is_complex,subs = self.split_type(name) - setattr(types, name, typeobj) + if self.is_known_type(name): + raise ArgumentError("attempting to inject a type that is already defined", type=name) -def load_external_types(path, verbose=False): - """ - Given a path to a python package or module, load that module, search for all defined variables - inside of it that do not start with _ or __ and inject them into the type system. If any of the - types cannot be injected, silently ignore them unless verbose is True. If path points to a module - it should not contain the trailing .py since this is added automatically by the python import system - """ + if (not is_complex) and self._is_factory(typeobj): + if name in self.type_factories: + raise ArgumentError("attempted to inject a complex type factory that is already defined", type=name) + self.type_factories[name] = typeobj + else: + self._validate_type(typeobj) + self.known_types[name] = typeobj + + if not hasattr(typeobj, "default_formatter"): + raise ArgumentError("type is invalid, does not have default_formatter function", type=typeobj, methods=dir(typeobj)) + + def load_type_module(self, module, verbose=False): + """ + Given a module that contains a list of some types find all symbols in the module that + do not start with _ and attempt to import them as types. + """ + + for name in filter(lambda x: not x.startswith('_'), dir(module)): + typeobj = getattr(module, name) + self.inject_type(name, typeobj) + + def load_external_types(self, path, verbose=False): + """ + Given a path to a python package or module, load that module, search for all defined variables + inside of it that do not start with _ or __ and inject them into the type system. If any of the + types cannot be injected, silently ignore them unless verbose is True. If path points to a module + it should not contain the trailing .py since this is added automatically by the python import system + """ + + d,p = os.path.split(path) + + try: + fileobj,pathname,description = imp.find_module(p, [d]) + mod = imp.load_module(p, fileobj, pathname, description) + except ImportError as e: + raise ArgumentError("could not import module in order to load external types", module_path=path, parent_directory=p, module_name=p, error=str(e)) - d,p = os.path.split(path) + self.load_type_module(mod, verbose) - try: - fileobj,pathname,description = imp.find_module(p, [d]) - mod = imp.load_module(p, fileobj, pathname, description) - except ImportError: - raise ArgumentError("could not import module in order to load external types", module_path=path, parent_directory=p, module_name=p) + #TODO add checking for types that could not be injected and report them - for name in filter(lambda x: not x.startswith('_'), dir(mod)): - typeobj = getattr(mod, name) - inject_type(name, typeobj) +#In order to support function annotations that must be resolved to types when modules +#are imported, create a default TypeSystem object that is used globally to store type +#information - #TODO add checking for types that could not be injected and report them +type_system = TypeSystem(types) diff --git a/pymomo/utilities/typedargs/types/__init__.py b/pymomo/utilities/typedargs/types/__init__.py index 48ff018..9cddd98 100644 --- a/pymomo/utilities/typedargs/types/__init__.py +++ b/pymomo/utilities/typedargs/types/__init__.py @@ -1,4 +1,8 @@ #Known Types import integer import string -import path \ No newline at end of file +import path +import bool + +from map import map +from list import list diff --git a/pymomo/utilities/typedargs/types/bool.py b/pymomo/utilities/typedargs/types/bool.py new file mode 100644 index 0000000..359567d --- /dev/null +++ b/pymomo/utilities/typedargs/types/bool.py @@ -0,0 +1,20 @@ +#bool.py +#Simple boolean type + +def convert(arg, **kwargs): + if arg is None: + return arg + + if isinstance(arg, basestring): + comp = arg.lower() + if comp == 'true': + return True + elif comp == 'false': + return False + else: + raise ValueError("Unknown boolean value (should be true or false): %s" % arg) + + return bool(arg) + +def default_formatter(arg, **kwargs): + return str(arg) diff --git a/pymomo/utilities/typedargs/types/integer.py b/pymomo/utilities/typedargs/types/integer.py index 38356ca..cc832f0 100644 --- a/pymomo/utilities/typedargs/types/integer.py +++ b/pymomo/utilities/typedargs/types/integer.py @@ -34,6 +34,9 @@ def validate_range(arg, lower, upper): raise ValueError("not in required range [%d, %d]" %(int(lower), int(upper))) #Formatting functions +def default_formatter(arg, **kwarg): + return str(arg) + def format_unsigned(arg, **kwarg): return format(arg, 'd') diff --git a/pymomo/utilities/typedargs/types/list.py b/pymomo/utilities/typedargs/types/list.py new file mode 100644 index 0000000..1206d65 --- /dev/null +++ b/pymomo/utilities/typedargs/types/list.py @@ -0,0 +1,30 @@ +#list.py + +class list(object): + def __init__(self, valuetype, **kwargs): + + self.valuetype = valuetype + self.type_system = kwargs['type_system'] + + @staticmethod + def Build(*types, **kwargs): + if len(types) != 1: + raise ValueError("list must be created with 1 argument, a value type") + + return list(types[0], **kwargs) + + def convert(self, value, **kwargs): + converted = [] + for x in value: + y = self.type_system.convert_to_type(x, self.valuetype, **kwargs) + converted.append(y) + + return converted + + def default_formatter(self, value, **kwargs): + lines = [] + for x in value: + line = self.type_system.format_value(x, self.valuetype, **kwargs) + lines.append(line) + + return "\n".join(lines) diff --git a/pymomo/utilities/typedargs/types/map.py b/pymomo/utilities/typedargs/types/map.py new file mode 100644 index 0000000..fb3f476 --- /dev/null +++ b/pymomo/utilities/typedargs/types/map.py @@ -0,0 +1,31 @@ +#map.py +#a complex type wrapping a python dictionary + +class map(object): + def __init__(self, keytype, valuetype, **kwargs): + + self.keytype = keytype + self.valuetype = valuetype + self.type_system = kwargs['type_system'] + + @staticmethod + def Build(*types, **kwargs): + if len(types) != 2: + raise ValueError("map must be created with 2 arguments, a keytype and a valuetype") + + return map(types[0], types[1], **kwargs) + + def convert(self, value, **kwargs): + if isinstance(value, dict): + return value + + raise ValueError("Converting to map from string not yet supported") + + def default_formatter(self, value, **kwargs): + forms = [] + for key,val in value.iteritems(): + keyform = self.type_system.format_value(key, self.keytype) + valform = self.type_system.format_value(val, self.valuetype) + forms.append("%s: %s" % (keyform, valform)) + + return "\n".join(forms) \ No newline at end of file diff --git a/pymomo/utilities/typedargs/types/path.py b/pymomo/utilities/typedargs/types/path.py index aef5e4a..c94bf81 100644 --- a/pymomo/utilities/typedargs/types/path.py +++ b/pymomo/utilities/typedargs/types/path.py @@ -33,4 +33,7 @@ def validate_writeable(arg): parent = os.path.dirname(arg) if not os.path.isdir(parent): raise ValueError("Parent directory does not exist and path must be writeable") - \ No newline at end of file + +#Formatting functions +def default_formatter(arg, **kwargs): + return str(arg) \ No newline at end of file diff --git a/pymomo/utilities/typedargs/types/string.py b/pymomo/utilities/typedargs/types/string.py index da621f5..4b011bf 100644 --- a/pymomo/utilities/typedargs/types/string.py +++ b/pymomo/utilities/typedargs/types/string.py @@ -12,4 +12,15 @@ def validate_list(arg, choices): choice_set = set(choices) if arg not in choices: - raise ValueError('Value not in list: %s' % str(choices)) \ No newline at end of file + raise ValueError('Value not in list: %s' % str(choices)) + +def validate_not_empty(arg): + """ + Make sure the string is not empty + """ + + if len(arg) == 0: + raise ValueError("String cannot be empty") + +def default_formatter(arg, **kwargs): + return arg diff --git a/setup.py b/setup.py index 563725f..aed87c0 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,8 @@ +#Caveats and possible issues +#Mac OS X +# - when using a virtualenv, readline is not properly installed into the virtualenv +# and cannot be imported. You need to install it using easy_install as described here +# http://calvinx.com/tag/readline/ from setuptools import setup, find_packages import os @@ -30,7 +35,6 @@ def list_data_files(): license = "LGPLv3", install_requires=[ "beautifulsoup4==4.3.2", - "BTrees==4.1.1", "Cheetah==2.4.4", "cmdln==1.1.2", "colorama==0.3.3", @@ -38,21 +42,14 @@ def list_data_files(): "intelhex==1.5", "Markdown==2.5.2", "nose==1.3.4", - "persistent==4.0.8", "py==1.4.26", "pycparser==2.10", "pyparsing==2.0.3", "pyserial==2.7", "pytest==2.6.4", "six==1.9.0", - "transaction==1.4.3", - "zc.lockfile==1.1.0", - "ZConfig==3.0.4", - "zdaemon==4.0.1", - "ZEO==4.1.0", - "ZODB==4.1.0", - "ZODB3==3.11.0", - "zope.interface==4.1.2" + "xlsxwriter>=0.6.7", + "pint>=0.6" ], package_data={ #This could be better 'pymomo': list_data_files() diff --git a/test/test_pcb/__init__.py b/test/test_pcb/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_pcb/eagle/assyvars.brd b/test/test_pcb/eagle/assyvars.brd new file mode 100644 index 0000000..7e0d86e --- /dev/null +++ b/test/test_pcb/eagle/assyvars.brd @@ -0,0 +1,1328 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>Resistors, Capacitors, Inductors</b><p> +Based on the previous libraries: +<ul> +<li>r.lbr +<li>cap.lbr +<li>cap-fe.lbr +<li>captant.lbr +<li>polcap.lbr +<li>ipc-smd.lbr +</ul> +All SMD packages are defined according to the IPC specifications and CECC<p> +<author>Created by librarian@cadsoft.de</author><p> +<p> +for Electrolyt Capacitors see also :<p> +www.bccomponents.com <p> +www.panasonic.com<p> +www.kemet.com<p> +http://www.secc.co.jp/pdf/os_e/2004/e_os_all.pdf <b>(SANYO)</b> +<p> +for trimmer refence see : <u>www.electrospec-inc.com/cross_references/trimpotcrossref.asp</u><p> + +<table border=0 cellspacing=0 cellpadding=0 width="100%" cellpaddding=0> +<tr valign="top"> + +<! <td width="10">&nbsp;</td> +<td width="90%"> + +<b><font color="#0000FF" size="4">TRIM-POT CROSS REFERENCE</font></b> +<P> +<TABLE BORDER=0 CELLSPACING=1 CELLPADDING=2> + <TR> + <TD COLSPAN=8> + <FONT SIZE=3 FACE=ARIAL><B>RECTANGULAR MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">BOURNS</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">BI&nbsp;TECH</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">DALE-VISHAY</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">PHILIPS/MEPCO</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">MURATA</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">PANASONIC</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">SPECTROL</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">MILSPEC</FONT> + </B> + </TD><TD>&nbsp;</TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3 > + 3005P<BR> + 3006P<BR> + 3006W<BR> + 3006Y<BR> + 3009P<BR> + 3009W<BR> + 3009Y<BR> + 3057J<BR> + 3057L<BR> + 3057P<BR> + 3057Y<BR> + 3059J<BR> + 3059L<BR> + 3059P<BR> + 3059Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 89P<BR> + 89W<BR> + 89X<BR> + 89PH<BR> + 76P<BR> + 89XH<BR> + 78SLT<BR> + 78L&nbsp;ALT<BR> + 56P&nbsp;ALT<BR> + 78P&nbsp;ALT<BR> + T8S<BR> + 78L<BR> + 56P<BR> + 78P<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + T18/784<BR> + 783<BR> + 781<BR> + -<BR> + -<BR> + -<BR> + 2199<BR> + 1697/1897<BR> + 1680/1880<BR> + 2187<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 8035EKP/CT20/RJ-20P<BR> + -<BR> + RJ-20X<BR> + -<BR> + -<BR> + -<BR> + 1211L<BR> + 8012EKQ&nbsp;ALT<BR> + 8012EKR&nbsp;ALT<BR> + 1211P<BR> + 8012EKJ<BR> + 8012EKL<BR> + 8012EKQ<BR> + 8012EKR<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 2101P<BR> + 2101W<BR> + 2101Y<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 2102L<BR> + 2102S<BR> + 2102Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + EVMCOG<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 43P<BR> + 43W<BR> + 43Y<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 40L<BR> + 40P<BR> + 40Y<BR> + 70Y-T602<BR> + 70L<BR> + 70P<BR> + 70Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + RT/RTR12<BR> + RT/RTR12<BR> + RT/RTR12<BR> + -<BR> + RJ/RJR12<BR> + RJ/RJR12<BR> + RJ/RJR12<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=8>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=8> + <FONT SIZE=4 FACE=ARIAL><B>SQUARE MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BOURN</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MURATA</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>SPECTROL</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MILSPEC</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3250L<BR> + 3250P<BR> + 3250W<BR> + 3250X<BR> + 3252P<BR> + 3252W<BR> + 3252X<BR> + 3260P<BR> + 3260W<BR> + 3260X<BR> + 3262P<BR> + 3262W<BR> + 3262X<BR> + 3266P<BR> + 3266W<BR> + 3266X<BR> + 3290H<BR> + 3290P<BR> + 3290W<BR> + 3292P<BR> + 3292W<BR> + 3292X<BR> + 3296P<BR> + 3296W<BR> + 3296X<BR> + 3296Y<BR> + 3296Z<BR> + 3299P<BR> + 3299W<BR> + 3299X<BR> + 3299Y<BR> + 3299Z<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66X&nbsp;ALT<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66X&nbsp;ALT<BR> + -<BR> + 64W&nbsp;ALT<BR> + -<BR> + 64P&nbsp;ALT<BR> + 64W&nbsp;ALT<BR> + 64X&nbsp;ALT<BR> + 64P<BR> + 64W<BR> + 64X<BR> + 66X&nbsp;ALT<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66P<BR> + 66W<BR> + 66X<BR> + 67P<BR> + 67W<BR> + 67X<BR> + 67Y<BR> + 67Z<BR> + 68P<BR> + 68W<BR> + 68X<BR> + 67Y&nbsp;ALT<BR> + 67Z&nbsp;ALT<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 5050<BR> + 5091<BR> + 5080<BR> + 5087<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + T63YB<BR> + T63XB<BR> + -<BR> + -<BR> + -<BR> + 5887<BR> + 5891<BR> + 5880<BR> + -<BR> + -<BR> + -<BR> + T93Z<BR> + T93YA<BR> + T93XA<BR> + T93YB<BR> + T93XB<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 8026EKP<BR> + 8026EKW<BR> + 8026EKM<BR> + 8026EKP<BR> + 8026EKB<BR> + 8026EKM<BR> + 1309X<BR> + 1309P<BR> + 1309W<BR> + 8024EKP<BR> + 8024EKW<BR> + 8024EKN<BR> + RJ-9P/CT9P<BR> + RJ-9W<BR> + RJ-9X<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3103P<BR> + 3103Y<BR> + 3103Z<BR> + 3103P<BR> + 3103Y<BR> + 3103Z<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3105P/3106P<BR> + 3105W/3106W<BR> + 3105X/3106X<BR> + 3105Y/3106Y<BR> + 3105Z/3105Z<BR> + 3102P<BR> + 3102W<BR> + 3102X<BR> + 3102Y<BR> + 3102Z<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + EVMCBG<BR> + EVMCCG<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 55-1-X<BR> + 55-4-X<BR> + 55-3-X<BR> + 55-2-X<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 50-2-X<BR> + 50-4-X<BR> + 50-3-X<BR> + -<BR> + -<BR> + -<BR> + 64P<BR> + 64W<BR> + 64X<BR> + 64Y<BR> + 64Z<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + RT/RTR22<BR> + RT/RTR22<BR> + RT/RTR22<BR> + RT/RTR22<BR> + RJ/RJR22<BR> + RJ/RJR22<BR> + RJ/RJR22<BR> + RT/RTR26<BR> + RT/RTR26<BR> + RT/RTR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RT/RTR24<BR> + RT/RTR24<BR> + RT/RTR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=8>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=8> + <FONT SIZE=4 FACE=ARIAL><B>SINGLE TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BOURN</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MURATA</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>SPECTROL</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MILSPEC</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3323P<BR> + 3323S<BR> + 3323W<BR> + 3329H<BR> + 3329P<BR> + 3329W<BR> + 3339H<BR> + 3339P<BR> + 3339W<BR> + 3352E<BR> + 3352H<BR> + 3352K<BR> + 3352P<BR> + 3352T<BR> + 3352V<BR> + 3352W<BR> + 3362H<BR> + 3362M<BR> + 3362P<BR> + 3362R<BR> + 3362S<BR> + 3362U<BR> + 3362W<BR> + 3362X<BR> + 3386B<BR> + 3386C<BR> + 3386F<BR> + 3386H<BR> + 3386K<BR> + 3386M<BR> + 3386P<BR> + 3386S<BR> + 3386W<BR> + 3386X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 25P<BR> + 25S<BR> + 25RX<BR> + 82P<BR> + 82M<BR> + 82PA<BR> + -<BR> + -<BR> + -<BR> + 91E<BR> + 91X<BR> + 91T<BR> + 91B<BR> + 91A<BR> + 91V<BR> + 91W<BR> + 25W<BR> + 25V<BR> + 25P<BR> + -<BR> + 25S<BR> + 25U<BR> + 25RX<BR> + 25X<BR> + 72XW<BR> + 72XL<BR> + 72PM<BR> + 72RX<BR> + -<BR> + 72PX<BR> + 72P<BR> + 72RXW<BR> + 72RXL<BR> + 72X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + T7YB<BR> + T7YA<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + TXD<BR> + TYA<BR> + TYP<BR> + -<BR> + TYD<BR> + TX<BR> + -<BR> + 150SX<BR> + 100SX<BR> + 102T<BR> + 101S<BR> + 190T<BR> + 150TX<BR> + 101<BR> + -<BR> + -<BR> + 101SX<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ET6P<BR> + ET6S<BR> + ET6X<BR> + RJ-6W/8014EMW<BR> + RJ-6P/8014EMP<BR> + RJ-6X/8014EMX<BR> + TM7W<BR> + TM7P<BR> + TM7X<BR> + -<BR> + 8017SMS<BR> + -<BR> + 8017SMB<BR> + 8017SMA<BR> + -<BR> + -<BR> + CT-6W<BR> + CT-6H<BR> + CT-6P<BR> + CT-6R<BR> + -<BR> + CT-6V<BR> + CT-6X<BR> + -<BR> + -<BR> + 8038EKV<BR> + -<BR> + 8038EKX<BR> + -<BR> + -<BR> + 8038EKP<BR> + 8038EKZ<BR> + 8038EKW<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + 3321H<BR> + 3321P<BR> + 3321N<BR> + 1102H<BR> + 1102P<BR> + 1102T<BR> + RVA0911V304A<BR> + -<BR> + RVA0911H413A<BR> + RVG0707V100A<BR> + RVA0607V(H)306A<BR> + RVA1214H213A<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3104B<BR> + 3104C<BR> + 3104F<BR> + 3104H<BR> + -<BR> + 3104M<BR> + 3104P<BR> + 3104S<BR> + 3104W<BR> + 3104X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + EVMQ0G<BR> + EVMQIG<BR> + EVMQ3G<BR> + EVMS0G<BR> + EVMQ0G<BR> + EVMG0G<BR> + -<BR> + -<BR> + -<BR> + EVMK4GA00B<BR> + EVM30GA00B<BR> + EVMK0GA00B<BR> + EVM38GA00B<BR> + EVMB6<BR> + EVLQ0<BR> + -<BR> + EVMMSG<BR> + EVMMBG<BR> + EVMMAG<BR> + -<BR> + -<BR> + EVMMCS<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + EVMM1<BR> + -<BR> + -<BR> + EVMM0<BR> + -<BR> + -<BR> + EVMM3<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + 62-3-1<BR> + 62-1-2<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 67R<BR> + -<BR> + 67P<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 67X<BR> + 63V<BR> + 63S<BR> + 63M<BR> + -<BR> + -<BR> + 63H<BR> + 63P<BR> + -<BR> + -<BR> + 63X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + RJ/RJR50<BR> + RJ/RJR50<BR> + RJ/RJR50<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> +</TABLE> +<P>&nbsp;<P> +<TABLE BORDER=0 CELLSPACING=1 CELLPADDING=3> + <TR> + <TD COLSPAN=7> + <FONT color="#0000FF" SIZE=4 FACE=ARIAL><B>SMD TRIM-POT CROSS REFERENCE</B></FONT> + <P> + <FONT SIZE=4 FACE=ARIAL><B>MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BOURNS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>TOCOS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>AUX/KYOCERA</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3224G<BR> + 3224J<BR> + 3224W<BR> + 3269P<BR> + 3269W<BR> + 3269X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 44G<BR> + 44J<BR> + 44W<BR> + 84P<BR> + 84W<BR> + 84X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + ST63Z<BR> + ST63Y<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + ST5P<BR> + ST5W<BR> + ST5X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=7>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=7> + <FONT SIZE=4 FACE=ARIAL><B>SINGLE TURN</B></FONT> + </TD> + </TR> + <TR> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BOURNS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>TOCOS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>AUX/KYOCERA</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3314G<BR> + 3314J<BR> + 3364A/B<BR> + 3364C/D<BR> + 3364W/X<BR> + 3313G<BR> + 3313J<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 23B<BR> + 23A<BR> + 21X<BR> + 21W<BR> + -<BR> + 22B<BR> + 22A<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ST5YL/ST53YL<BR> + ST5YJ/5T53YJ<BR> + ST-23A<BR> + ST-22B<BR> + ST-22<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ST-4B<BR> + ST-4A<BR> + -<BR> + -<BR> + -<BR> + ST-3B<BR> + ST-3A<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + EVM-6YS<BR> + EVM-1E<BR> + EVM-1G<BR> + EVM-1D<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + G4B<BR> + G4A<BR> + TR04-3S1<BR> + TRG04-2S1<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + DVR-43A<BR> + CVR-42C<BR> + CVR-42A/C<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> +</TABLE> +<P> +<FONT SIZE=4 FACE=ARIAL><B>ALT =&nbsp;ALTERNATE</B></FONT> +<P> + +&nbsp; +<P> +</td> +</tr> +</table> + + +<b>CAPACITOR</b> + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + +<b>EAGLE Design Rules</b> +<p> +Die Standard-Design-Rules sind so gewählt, dass sie für +die meisten Anwendungen passen. Sollte ihre Platine +besondere Anforderungen haben, treffen Sie die erforderlichen +Einstellungen hier und speichern die Design Rules unter +einem neuen Namen ab. +<b>EAGLE Design Rules</b> +<p> +The default Design Rules have been set to cover +a wide range of applications. Your particular design +may have different requirements, so please make the +necessary adjustments and save your customized +design rules under a new name. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/test_pcb/eagle/blank_board.brd b/test/test_pcb/eagle/blank_board.brd new file mode 100644 index 0000000..a24ac9b --- /dev/null +++ b/test/test_pcb/eagle/blank_board.brd @@ -0,0 +1,244 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +<b>EAGLE Design Rules</b> +<p> +Die Standard-Design-Rules sind so gewählt, dass sie für +die meisten Anwendungen passen. Sollte ihre Platine +besondere Anforderungen haben, treffen Sie die erforderlichen +Einstellungen hier und speichern die Design Rules unter +einem neuen Namen ab. +<b>EAGLE Design Rules</b> +<p> +The default Design Rules have been set to cover +a wide range of applications. Your particular design +may have different requirements, so please make the +necessary adjustments and save your customized +design rules under a new name. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/test/test_pcb/eagle/controller_complete.brd b/test/test_pcb/eagle/controller_complete.brd new file mode 100644 index 0000000..880619c --- /dev/null +++ b/test/test_pcb/eagle/controller_complete.brd @@ -0,0 +1,3164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +BATT +3.6V ++ + + + + + + + + + + + + +WellDone +Controller v4.1 +11/20/2014 + +Solar ++ +- + + + + +Microchip PIC24ka101 +CAD and Schematic files for the PIC24Fka101 series of microcontrollers. + + +<b>SMALL OUTLINE PACKAGE</b> + + + + + + + + + + + + + + + + +>NAME +>VALUE +1 + + + + + + + + + + +<b>SMALL OUTLINE TRANSISTOR</b><p> +reflow soldering + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + +1 + + + + +>NAME + + +<b>PIN HEADER</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + +<b>Thin Quad Flat Pack</b><p> +package type TQ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME + + +<b>CRYSTAL</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + +<b>Resistors, Capacitors, Inductors</b><p> +Based on the previous libraries: +<ul> +<li>r.lbr +<li>cap.lbr +<li>cap-fe.lbr +<li>captant.lbr +<li>polcap.lbr +<li>ipc-smd.lbr +</ul> +All SMD packages are defined according to the IPC specifications and CECC<p> +<author>Created by librarian@cadsoft.de</author><p> +<p> +for Electrolyt Capacitors see also :<p> +www.bccomponents.com <p> +www.panasonic.com<p> +www.kemet.com<p> +http://www.secc.co.jp/pdf/os_e/2004/e_os_all.pdf <b>(SANYO)</b> +<p> +for trimmer refence see : <u>www.electrospec-inc.com/cross_references/trimpotcrossref.asp</u><p> + +<table border=0 cellspacing=0 cellpadding=0 width="100%" cellpaddding=0> +<tr valign="top"> + +<! <td width="10">&nbsp;</td> +<td width="90%"> + +<b><font color="#0000FF" size="4">TRIM-POT CROSS REFERENCE</font></b> +<P> +<TABLE BORDER=0 CELLSPACING=1 CELLPADDING=2> + <TR> + <TD COLSPAN=8> + <FONT SIZE=3 FACE=ARIAL><B>RECTANGULAR MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">BOURNS</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">BI&nbsp;TECH</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">DALE-VISHAY</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">PHILIPS/MEPCO</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">MURATA</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">PANASONIC</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">SPECTROL</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">MILSPEC</FONT> + </B> + </TD><TD>&nbsp;</TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3 > + 3005P<BR> + 3006P<BR> + 3006W<BR> + 3006Y<BR> + 3009P<BR> + 3009W<BR> + 3009Y<BR> + 3057J<BR> + 3057L<BR> + 3057P<BR> + 3057Y<BR> + 3059J<BR> + 3059L<BR> + 3059P<BR> + 3059Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 89P<BR> + 89W<BR> + 89X<BR> + 89PH<BR> + 76P<BR> + 89XH<BR> + 78SLT<BR> + 78L&nbsp;ALT<BR> + 56P&nbsp;ALT<BR> + 78P&nbsp;ALT<BR> + T8S<BR> + 78L<BR> + 56P<BR> + 78P<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + T18/784<BR> + 783<BR> + 781<BR> + -<BR> + -<BR> + -<BR> + 2199<BR> + 1697/1897<BR> + 1680/1880<BR> + 2187<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 8035EKP/CT20/RJ-20P<BR> + -<BR> + RJ-20X<BR> + -<BR> + -<BR> + -<BR> + 1211L<BR> + 8012EKQ&nbsp;ALT<BR> + 8012EKR&nbsp;ALT<BR> + 1211P<BR> + 8012EKJ<BR> + 8012EKL<BR> + 8012EKQ<BR> + 8012EKR<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 2101P<BR> + 2101W<BR> + 2101Y<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 2102L<BR> + 2102S<BR> + 2102Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + EVMCOG<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 43P<BR> + 43W<BR> + 43Y<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 40L<BR> + 40P<BR> + 40Y<BR> + 70Y-T602<BR> + 70L<BR> + 70P<BR> + 70Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + RT/RTR12<BR> + RT/RTR12<BR> + RT/RTR12<BR> + -<BR> + RJ/RJR12<BR> + RJ/RJR12<BR> + RJ/RJR12<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=8>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=8> + <FONT SIZE=4 FACE=ARIAL><B>SQUARE MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BOURN</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MURATA</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>SPECTROL</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MILSPEC</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3250L<BR> + 3250P<BR> + 3250W<BR> + 3250X<BR> + 3252P<BR> + 3252W<BR> + 3252X<BR> + 3260P<BR> + 3260W<BR> + 3260X<BR> + 3262P<BR> + 3262W<BR> + 3262X<BR> + 3266P<BR> + 3266W<BR> + 3266X<BR> + 3290H<BR> + 3290P<BR> + 3290W<BR> + 3292P<BR> + 3292W<BR> + 3292X<BR> + 3296P<BR> + 3296W<BR> + 3296X<BR> + 3296Y<BR> + 3296Z<BR> + 3299P<BR> + 3299W<BR> + 3299X<BR> + 3299Y<BR> + 3299Z<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66X&nbsp;ALT<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66X&nbsp;ALT<BR> + -<BR> + 64W&nbsp;ALT<BR> + -<BR> + 64P&nbsp;ALT<BR> + 64W&nbsp;ALT<BR> + 64X&nbsp;ALT<BR> + 64P<BR> + 64W<BR> + 64X<BR> + 66X&nbsp;ALT<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66P<BR> + 66W<BR> + 66X<BR> + 67P<BR> + 67W<BR> + 67X<BR> + 67Y<BR> + 67Z<BR> + 68P<BR> + 68W<BR> + 68X<BR> + 67Y&nbsp;ALT<BR> + 67Z&nbsp;ALT<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 5050<BR> + 5091<BR> + 5080<BR> + 5087<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + T63YB<BR> + T63XB<BR> + -<BR> + -<BR> + -<BR> + 5887<BR> + 5891<BR> + 5880<BR> + -<BR> + -<BR> + -<BR> + T93Z<BR> + T93YA<BR> + T93XA<BR> + T93YB<BR> + T93XB<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 8026EKP<BR> + 8026EKW<BR> + 8026EKM<BR> + 8026EKP<BR> + 8026EKB<BR> + 8026EKM<BR> + 1309X<BR> + 1309P<BR> + 1309W<BR> + 8024EKP<BR> + 8024EKW<BR> + 8024EKN<BR> + RJ-9P/CT9P<BR> + RJ-9W<BR> + RJ-9X<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3103P<BR> + 3103Y<BR> + 3103Z<BR> + 3103P<BR> + 3103Y<BR> + 3103Z<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3105P/3106P<BR> + 3105W/3106W<BR> + 3105X/3106X<BR> + 3105Y/3106Y<BR> + 3105Z/3105Z<BR> + 3102P<BR> + 3102W<BR> + 3102X<BR> + 3102Y<BR> + 3102Z<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + EVMCBG<BR> + EVMCCG<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 55-1-X<BR> + 55-4-X<BR> + 55-3-X<BR> + 55-2-X<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 50-2-X<BR> + 50-4-X<BR> + 50-3-X<BR> + -<BR> + -<BR> + -<BR> + 64P<BR> + 64W<BR> + 64X<BR> + 64Y<BR> + 64Z<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + RT/RTR22<BR> + RT/RTR22<BR> + RT/RTR22<BR> + RT/RTR22<BR> + RJ/RJR22<BR> + RJ/RJR22<BR> + RJ/RJR22<BR> + RT/RTR26<BR> + RT/RTR26<BR> + RT/RTR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RT/RTR24<BR> + RT/RTR24<BR> + RT/RTR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=8>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=8> + <FONT SIZE=4 FACE=ARIAL><B>SINGLE TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BOURN</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MURATA</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>SPECTROL</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MILSPEC</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3323P<BR> + 3323S<BR> + 3323W<BR> + 3329H<BR> + 3329P<BR> + 3329W<BR> + 3339H<BR> + 3339P<BR> + 3339W<BR> + 3352E<BR> + 3352H<BR> + 3352K<BR> + 3352P<BR> + 3352T<BR> + 3352V<BR> + 3352W<BR> + 3362H<BR> + 3362M<BR> + 3362P<BR> + 3362R<BR> + 3362S<BR> + 3362U<BR> + 3362W<BR> + 3362X<BR> + 3386B<BR> + 3386C<BR> + 3386F<BR> + 3386H<BR> + 3386K<BR> + 3386M<BR> + 3386P<BR> + 3386S<BR> + 3386W<BR> + 3386X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 25P<BR> + 25S<BR> + 25RX<BR> + 82P<BR> + 82M<BR> + 82PA<BR> + -<BR> + -<BR> + -<BR> + 91E<BR> + 91X<BR> + 91T<BR> + 91B<BR> + 91A<BR> + 91V<BR> + 91W<BR> + 25W<BR> + 25V<BR> + 25P<BR> + -<BR> + 25S<BR> + 25U<BR> + 25RX<BR> + 25X<BR> + 72XW<BR> + 72XL<BR> + 72PM<BR> + 72RX<BR> + -<BR> + 72PX<BR> + 72P<BR> + 72RXW<BR> + 72RXL<BR> + 72X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + T7YB<BR> + T7YA<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + TXD<BR> + TYA<BR> + TYP<BR> + -<BR> + TYD<BR> + TX<BR> + -<BR> + 150SX<BR> + 100SX<BR> + 102T<BR> + 101S<BR> + 190T<BR> + 150TX<BR> + 101<BR> + -<BR> + -<BR> + 101SX<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ET6P<BR> + ET6S<BR> + ET6X<BR> + RJ-6W/8014EMW<BR> + RJ-6P/8014EMP<BR> + RJ-6X/8014EMX<BR> + TM7W<BR> + TM7P<BR> + TM7X<BR> + -<BR> + 8017SMS<BR> + -<BR> + 8017SMB<BR> + 8017SMA<BR> + -<BR> + -<BR> + CT-6W<BR> + CT-6H<BR> + CT-6P<BR> + CT-6R<BR> + -<BR> + CT-6V<BR> + CT-6X<BR> + -<BR> + -<BR> + 8038EKV<BR> + -<BR> + 8038EKX<BR> + -<BR> + -<BR> + 8038EKP<BR> + 8038EKZ<BR> + 8038EKW<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + 3321H<BR> + 3321P<BR> + 3321N<BR> + 1102H<BR> + 1102P<BR> + 1102T<BR> + RVA0911V304A<BR> + -<BR> + RVA0911H413A<BR> + RVG0707V100A<BR> + RVA0607V(H)306A<BR> + RVA1214H213A<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3104B<BR> + 3104C<BR> + 3104F<BR> + 3104H<BR> + -<BR> + 3104M<BR> + 3104P<BR> + 3104S<BR> + 3104W<BR> + 3104X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + EVMQ0G<BR> + EVMQIG<BR> + EVMQ3G<BR> + EVMS0G<BR> + EVMQ0G<BR> + EVMG0G<BR> + -<BR> + -<BR> + -<BR> + EVMK4GA00B<BR> + EVM30GA00B<BR> + EVMK0GA00B<BR> + EVM38GA00B<BR> + EVMB6<BR> + EVLQ0<BR> + -<BR> + EVMMSG<BR> + EVMMBG<BR> + EVMMAG<BR> + -<BR> + -<BR> + EVMMCS<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + EVMM1<BR> + -<BR> + -<BR> + EVMM0<BR> + -<BR> + -<BR> + EVMM3<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + 62-3-1<BR> + 62-1-2<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 67R<BR> + -<BR> + 67P<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 67X<BR> + 63V<BR> + 63S<BR> + 63M<BR> + -<BR> + -<BR> + 63H<BR> + 63P<BR> + -<BR> + -<BR> + 63X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + RJ/RJR50<BR> + RJ/RJR50<BR> + RJ/RJR50<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> +</TABLE> +<P>&nbsp;<P> +<TABLE BORDER=0 CELLSPACING=1 CELLPADDING=3> + <TR> + <TD COLSPAN=7> + <FONT color="#0000FF" SIZE=4 FACE=ARIAL><B>SMD TRIM-POT CROSS REFERENCE</B></FONT> + <P> + <FONT SIZE=4 FACE=ARIAL><B>MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BOURNS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>TOCOS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>AUX/KYOCERA</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3224G<BR> + 3224J<BR> + 3224W<BR> + 3269P<BR> + 3269W<BR> + 3269X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 44G<BR> + 44J<BR> + 44W<BR> + 84P<BR> + 84W<BR> + 84X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + ST63Z<BR> + ST63Y<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + ST5P<BR> + ST5W<BR> + ST5X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=7>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=7> + <FONT SIZE=4 FACE=ARIAL><B>SINGLE TURN</B></FONT> + </TD> + </TR> + <TR> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BOURNS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>TOCOS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>AUX/KYOCERA</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3314G<BR> + 3314J<BR> + 3364A/B<BR> + 3364C/D<BR> + 3364W/X<BR> + 3313G<BR> + 3313J<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 23B<BR> + 23A<BR> + 21X<BR> + 21W<BR> + -<BR> + 22B<BR> + 22A<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ST5YL/ST53YL<BR> + ST5YJ/5T53YJ<BR> + ST-23A<BR> + ST-22B<BR> + ST-22<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ST-4B<BR> + ST-4A<BR> + -<BR> + -<BR> + -<BR> + ST-3B<BR> + ST-3A<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + EVM-6YS<BR> + EVM-1E<BR> + EVM-1G<BR> + EVM-1D<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + G4B<BR> + G4A<BR> + TR04-3S1<BR> + TRG04-2S1<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + DVR-43A<BR> + CVR-42C<BR> + CVR-42A/C<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> +</TABLE> +<P> +<FONT SIZE=4 FACE=ARIAL><B>ALT =&nbsp;ALTERNATE</B></FONT> +<P> + +&nbsp; +<P> +</td> +</tr> +</table> + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>Ceramic Chip Capacitor KEMET 0805 reflow solder</b><p> +Metric Code Size 2012 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 0204 reflow solder</b><p> +Metric Code Size 1005 + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + + + +<b>Pin Header Connectors</b><p> +<author>Created by librarian@cadsoft.de</author> + + +<b>PIN HEADER</b> + + + + + + + + + +>NAME +>VALUE + + + + + +<b>Diodes</b><p> +Based on the following sources: +<ul> +<li>Motorola : www.onsemi.com +<li>Fairchild : www.fairchildsemi.com +<li>Philips : www.semiconductors.com +<li>Vishay : www.vishay.de +</ul> +<author>Created by librarian@cadsoft.de</author> + + +<b>Diode Package</b> Reflow soldering<p> +INFINEON, www.infineon.com/cmc_upload/0/000/010/257/eh_db_5b.pdf + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME + + +RN4020 Bluetooth 4.0 module, surface mount castellation form-factor 19.5mm x 11.5mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Antenna + + + + +>NAME + + +SOT-23 package, 3 leads + + + + + + + +>NAME + + + + + + + + + + +<b>Small Outline Transistor</b><p> +package type OT + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + +<b>PNP Transistors</b><p> +<author>Created by librarian@cadsoft.de</author> + + +<b>SOT-23</b> + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + +<b>EAGLE Design Rules</b> +<p> +Die Standard-Design-Rules sind so gewählt, dass sie für +die meisten Anwendungen passen. Sollte ihre Platine +besondere Anforderungen haben, treffen Sie die erforderlichen +Einstellungen hier und speichern die Design Rules unter +einem neuen Namen ab. +<b>EAGLE Design Rules</b> +<p> +The default Design Rules have been set to cover +a wide range of applications. Your particular design +may have different requirements, so please make the +necessary adjustments and save your customized +design rules under a new name. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Since Version 6.2.2 text objects can contain more than one line, +which will not be processed correctly with this version. + + + diff --git a/test/test_pcb/eagle/controller_dist_distpn.brd b/test/test_pcb/eagle/controller_dist_distpn.brd new file mode 100644 index 0000000..4db5b1f --- /dev/null +++ b/test/test_pcb/eagle/controller_dist_distpn.brd @@ -0,0 +1,3162 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +BATT +3.6V ++ + + + + + + + + + + + + +WellDone +Controller v4.1 +11/20/2014 + +Solar ++ +- + + + + +Microchip PIC24ka101 +CAD and Schematic files for the PIC24Fka101 series of microcontrollers. + + +<b>SMALL OUTLINE PACKAGE</b> + + + + + + + + + + + + + + + + +>NAME +>VALUE +1 + + + + + + + + + + +<b>SMALL OUTLINE TRANSISTOR</b><p> +reflow soldering + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + +1 + + + + +>NAME + + +<b>PIN HEADER</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + +<b>Thin Quad Flat Pack</b><p> +package type TQ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME + + +<b>CRYSTAL</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + +<b>Resistors, Capacitors, Inductors</b><p> +Based on the previous libraries: +<ul> +<li>r.lbr +<li>cap.lbr +<li>cap-fe.lbr +<li>captant.lbr +<li>polcap.lbr +<li>ipc-smd.lbr +</ul> +All SMD packages are defined according to the IPC specifications and CECC<p> +<author>Created by librarian@cadsoft.de</author><p> +<p> +for Electrolyt Capacitors see also :<p> +www.bccomponents.com <p> +www.panasonic.com<p> +www.kemet.com<p> +http://www.secc.co.jp/pdf/os_e/2004/e_os_all.pdf <b>(SANYO)</b> +<p> +for trimmer refence see : <u>www.electrospec-inc.com/cross_references/trimpotcrossref.asp</u><p> + +<table border=0 cellspacing=0 cellpadding=0 width="100%" cellpaddding=0> +<tr valign="top"> + +<! <td width="10">&nbsp;</td> +<td width="90%"> + +<b><font color="#0000FF" size="4">TRIM-POT CROSS REFERENCE</font></b> +<P> +<TABLE BORDER=0 CELLSPACING=1 CELLPADDING=2> + <TR> + <TD COLSPAN=8> + <FONT SIZE=3 FACE=ARIAL><B>RECTANGULAR MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">BOURNS</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">BI&nbsp;TECH</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">DALE-VISHAY</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">PHILIPS/MEPCO</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">MURATA</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">PANASONIC</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">SPECTROL</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">MILSPEC</FONT> + </B> + </TD><TD>&nbsp;</TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3 > + 3005P<BR> + 3006P<BR> + 3006W<BR> + 3006Y<BR> + 3009P<BR> + 3009W<BR> + 3009Y<BR> + 3057J<BR> + 3057L<BR> + 3057P<BR> + 3057Y<BR> + 3059J<BR> + 3059L<BR> + 3059P<BR> + 3059Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 89P<BR> + 89W<BR> + 89X<BR> + 89PH<BR> + 76P<BR> + 89XH<BR> + 78SLT<BR> + 78L&nbsp;ALT<BR> + 56P&nbsp;ALT<BR> + 78P&nbsp;ALT<BR> + T8S<BR> + 78L<BR> + 56P<BR> + 78P<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + T18/784<BR> + 783<BR> + 781<BR> + -<BR> + -<BR> + -<BR> + 2199<BR> + 1697/1897<BR> + 1680/1880<BR> + 2187<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 8035EKP/CT20/RJ-20P<BR> + -<BR> + RJ-20X<BR> + -<BR> + -<BR> + -<BR> + 1211L<BR> + 8012EKQ&nbsp;ALT<BR> + 8012EKR&nbsp;ALT<BR> + 1211P<BR> + 8012EKJ<BR> + 8012EKL<BR> + 8012EKQ<BR> + 8012EKR<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 2101P<BR> + 2101W<BR> + 2101Y<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 2102L<BR> + 2102S<BR> + 2102Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + EVMCOG<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 43P<BR> + 43W<BR> + 43Y<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 40L<BR> + 40P<BR> + 40Y<BR> + 70Y-T602<BR> + 70L<BR> + 70P<BR> + 70Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + RT/RTR12<BR> + RT/RTR12<BR> + RT/RTR12<BR> + -<BR> + RJ/RJR12<BR> + RJ/RJR12<BR> + RJ/RJR12<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=8>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=8> + <FONT SIZE=4 FACE=ARIAL><B>SQUARE MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BOURN</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MURATA</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>SPECTROL</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MILSPEC</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3250L<BR> + 3250P<BR> + 3250W<BR> + 3250X<BR> + 3252P<BR> + 3252W<BR> + 3252X<BR> + 3260P<BR> + 3260W<BR> + 3260X<BR> + 3262P<BR> + 3262W<BR> + 3262X<BR> + 3266P<BR> + 3266W<BR> + 3266X<BR> + 3290H<BR> + 3290P<BR> + 3290W<BR> + 3292P<BR> + 3292W<BR> + 3292X<BR> + 3296P<BR> + 3296W<BR> + 3296X<BR> + 3296Y<BR> + 3296Z<BR> + 3299P<BR> + 3299W<BR> + 3299X<BR> + 3299Y<BR> + 3299Z<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66X&nbsp;ALT<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66X&nbsp;ALT<BR> + -<BR> + 64W&nbsp;ALT<BR> + -<BR> + 64P&nbsp;ALT<BR> + 64W&nbsp;ALT<BR> + 64X&nbsp;ALT<BR> + 64P<BR> + 64W<BR> + 64X<BR> + 66X&nbsp;ALT<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66P<BR> + 66W<BR> + 66X<BR> + 67P<BR> + 67W<BR> + 67X<BR> + 67Y<BR> + 67Z<BR> + 68P<BR> + 68W<BR> + 68X<BR> + 67Y&nbsp;ALT<BR> + 67Z&nbsp;ALT<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 5050<BR> + 5091<BR> + 5080<BR> + 5087<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + T63YB<BR> + T63XB<BR> + -<BR> + -<BR> + -<BR> + 5887<BR> + 5891<BR> + 5880<BR> + -<BR> + -<BR> + -<BR> + T93Z<BR> + T93YA<BR> + T93XA<BR> + T93YB<BR> + T93XB<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 8026EKP<BR> + 8026EKW<BR> + 8026EKM<BR> + 8026EKP<BR> + 8026EKB<BR> + 8026EKM<BR> + 1309X<BR> + 1309P<BR> + 1309W<BR> + 8024EKP<BR> + 8024EKW<BR> + 8024EKN<BR> + RJ-9P/CT9P<BR> + RJ-9W<BR> + RJ-9X<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3103P<BR> + 3103Y<BR> + 3103Z<BR> + 3103P<BR> + 3103Y<BR> + 3103Z<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3105P/3106P<BR> + 3105W/3106W<BR> + 3105X/3106X<BR> + 3105Y/3106Y<BR> + 3105Z/3105Z<BR> + 3102P<BR> + 3102W<BR> + 3102X<BR> + 3102Y<BR> + 3102Z<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + EVMCBG<BR> + EVMCCG<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 55-1-X<BR> + 55-4-X<BR> + 55-3-X<BR> + 55-2-X<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 50-2-X<BR> + 50-4-X<BR> + 50-3-X<BR> + -<BR> + -<BR> + -<BR> + 64P<BR> + 64W<BR> + 64X<BR> + 64Y<BR> + 64Z<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + RT/RTR22<BR> + RT/RTR22<BR> + RT/RTR22<BR> + RT/RTR22<BR> + RJ/RJR22<BR> + RJ/RJR22<BR> + RJ/RJR22<BR> + RT/RTR26<BR> + RT/RTR26<BR> + RT/RTR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RT/RTR24<BR> + RT/RTR24<BR> + RT/RTR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=8>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=8> + <FONT SIZE=4 FACE=ARIAL><B>SINGLE TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BOURN</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MURATA</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>SPECTROL</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MILSPEC</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3323P<BR> + 3323S<BR> + 3323W<BR> + 3329H<BR> + 3329P<BR> + 3329W<BR> + 3339H<BR> + 3339P<BR> + 3339W<BR> + 3352E<BR> + 3352H<BR> + 3352K<BR> + 3352P<BR> + 3352T<BR> + 3352V<BR> + 3352W<BR> + 3362H<BR> + 3362M<BR> + 3362P<BR> + 3362R<BR> + 3362S<BR> + 3362U<BR> + 3362W<BR> + 3362X<BR> + 3386B<BR> + 3386C<BR> + 3386F<BR> + 3386H<BR> + 3386K<BR> + 3386M<BR> + 3386P<BR> + 3386S<BR> + 3386W<BR> + 3386X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 25P<BR> + 25S<BR> + 25RX<BR> + 82P<BR> + 82M<BR> + 82PA<BR> + -<BR> + -<BR> + -<BR> + 91E<BR> + 91X<BR> + 91T<BR> + 91B<BR> + 91A<BR> + 91V<BR> + 91W<BR> + 25W<BR> + 25V<BR> + 25P<BR> + -<BR> + 25S<BR> + 25U<BR> + 25RX<BR> + 25X<BR> + 72XW<BR> + 72XL<BR> + 72PM<BR> + 72RX<BR> + -<BR> + 72PX<BR> + 72P<BR> + 72RXW<BR> + 72RXL<BR> + 72X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + T7YB<BR> + T7YA<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + TXD<BR> + TYA<BR> + TYP<BR> + -<BR> + TYD<BR> + TX<BR> + -<BR> + 150SX<BR> + 100SX<BR> + 102T<BR> + 101S<BR> + 190T<BR> + 150TX<BR> + 101<BR> + -<BR> + -<BR> + 101SX<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ET6P<BR> + ET6S<BR> + ET6X<BR> + RJ-6W/8014EMW<BR> + RJ-6P/8014EMP<BR> + RJ-6X/8014EMX<BR> + TM7W<BR> + TM7P<BR> + TM7X<BR> + -<BR> + 8017SMS<BR> + -<BR> + 8017SMB<BR> + 8017SMA<BR> + -<BR> + -<BR> + CT-6W<BR> + CT-6H<BR> + CT-6P<BR> + CT-6R<BR> + -<BR> + CT-6V<BR> + CT-6X<BR> + -<BR> + -<BR> + 8038EKV<BR> + -<BR> + 8038EKX<BR> + -<BR> + -<BR> + 8038EKP<BR> + 8038EKZ<BR> + 8038EKW<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + 3321H<BR> + 3321P<BR> + 3321N<BR> + 1102H<BR> + 1102P<BR> + 1102T<BR> + RVA0911V304A<BR> + -<BR> + RVA0911H413A<BR> + RVG0707V100A<BR> + RVA0607V(H)306A<BR> + RVA1214H213A<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3104B<BR> + 3104C<BR> + 3104F<BR> + 3104H<BR> + -<BR> + 3104M<BR> + 3104P<BR> + 3104S<BR> + 3104W<BR> + 3104X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + EVMQ0G<BR> + EVMQIG<BR> + EVMQ3G<BR> + EVMS0G<BR> + EVMQ0G<BR> + EVMG0G<BR> + -<BR> + -<BR> + -<BR> + EVMK4GA00B<BR> + EVM30GA00B<BR> + EVMK0GA00B<BR> + EVM38GA00B<BR> + EVMB6<BR> + EVLQ0<BR> + -<BR> + EVMMSG<BR> + EVMMBG<BR> + EVMMAG<BR> + -<BR> + -<BR> + EVMMCS<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + EVMM1<BR> + -<BR> + -<BR> + EVMM0<BR> + -<BR> + -<BR> + EVMM3<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + 62-3-1<BR> + 62-1-2<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 67R<BR> + -<BR> + 67P<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 67X<BR> + 63V<BR> + 63S<BR> + 63M<BR> + -<BR> + -<BR> + 63H<BR> + 63P<BR> + -<BR> + -<BR> + 63X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + RJ/RJR50<BR> + RJ/RJR50<BR> + RJ/RJR50<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> +</TABLE> +<P>&nbsp;<P> +<TABLE BORDER=0 CELLSPACING=1 CELLPADDING=3> + <TR> + <TD COLSPAN=7> + <FONT color="#0000FF" SIZE=4 FACE=ARIAL><B>SMD TRIM-POT CROSS REFERENCE</B></FONT> + <P> + <FONT SIZE=4 FACE=ARIAL><B>MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BOURNS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>TOCOS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>AUX/KYOCERA</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3224G<BR> + 3224J<BR> + 3224W<BR> + 3269P<BR> + 3269W<BR> + 3269X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 44G<BR> + 44J<BR> + 44W<BR> + 84P<BR> + 84W<BR> + 84X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + ST63Z<BR> + ST63Y<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + ST5P<BR> + ST5W<BR> + ST5X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=7>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=7> + <FONT SIZE=4 FACE=ARIAL><B>SINGLE TURN</B></FONT> + </TD> + </TR> + <TR> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BOURNS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>TOCOS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>AUX/KYOCERA</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3314G<BR> + 3314J<BR> + 3364A/B<BR> + 3364C/D<BR> + 3364W/X<BR> + 3313G<BR> + 3313J<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 23B<BR> + 23A<BR> + 21X<BR> + 21W<BR> + -<BR> + 22B<BR> + 22A<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ST5YL/ST53YL<BR> + ST5YJ/5T53YJ<BR> + ST-23A<BR> + ST-22B<BR> + ST-22<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ST-4B<BR> + ST-4A<BR> + -<BR> + -<BR> + -<BR> + ST-3B<BR> + ST-3A<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + EVM-6YS<BR> + EVM-1E<BR> + EVM-1G<BR> + EVM-1D<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + G4B<BR> + G4A<BR> + TR04-3S1<BR> + TRG04-2S1<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + DVR-43A<BR> + CVR-42C<BR> + CVR-42A/C<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> +</TABLE> +<P> +<FONT SIZE=4 FACE=ARIAL><B>ALT =&nbsp;ALTERNATE</B></FONT> +<P> + +&nbsp; +<P> +</td> +</tr> +</table> + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>Ceramic Chip Capacitor KEMET 0805 reflow solder</b><p> +Metric Code Size 2012 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 0204 reflow solder</b><p> +Metric Code Size 1005 + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + + + +<b>Pin Header Connectors</b><p> +<author>Created by librarian@cadsoft.de</author> + + +<b>PIN HEADER</b> + + + + + + + + + +>NAME +>VALUE + + + + + +<b>Diodes</b><p> +Based on the following sources: +<ul> +<li>Motorola : www.onsemi.com +<li>Fairchild : www.fairchildsemi.com +<li>Philips : www.semiconductors.com +<li>Vishay : www.vishay.de +</ul> +<author>Created by librarian@cadsoft.de</author> + + +<b>Diode Package</b> Reflow soldering<p> +INFINEON, www.infineon.com/cmc_upload/0/000/010/257/eh_db_5b.pdf + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME + + +RN4020 Bluetooth 4.0 module, surface mount castellation form-factor 19.5mm x 11.5mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Antenna + + + + +>NAME + + +SOT-23 package, 3 leads + + + + + + + +>NAME + + + + + + + + + + +<b>Small Outline Transistor</b><p> +package type OT + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + +<b>PNP Transistors</b><p> +<author>Created by librarian@cadsoft.de</author> + + +<b>SOT-23</b> + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + +<b>EAGLE Design Rules</b> +<p> +Die Standard-Design-Rules sind so gewählt, dass sie für +die meisten Anwendungen passen. Sollte ihre Platine +besondere Anforderungen haben, treffen Sie die erforderlichen +Einstellungen hier und speichern die Design Rules unter +einem neuen Namen ab. +<b>EAGLE Design Rules</b> +<p> +The default Design Rules have been set to cover +a wide range of applications. Your particular design +may have different requirements, so please make the +necessary adjustments and save your customized +design rules under a new name. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Since Version 6.2.2 text objects can contain more than one line, +which will not be processed correctly with this version. + + + diff --git a/test/test_pcb/eagle/controller_missing_attrs.brd b/test/test_pcb/eagle/controller_missing_attrs.brd new file mode 100644 index 0000000..c1639c9 --- /dev/null +++ b/test/test_pcb/eagle/controller_missing_attrs.brd @@ -0,0 +1,3161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +BATT +3.6V ++ + + + + + + + + + + + + +WellDone +Controller v4.1 +11/20/2014 + +Solar ++ +- + + + + +Microchip PIC24ka101 +CAD and Schematic files for the PIC24Fka101 series of microcontrollers. + + +<b>SMALL OUTLINE PACKAGE</b> + + + + + + + + + + + + + + + + +>NAME +>VALUE +1 + + + + + + + + + + +<b>SMALL OUTLINE TRANSISTOR</b><p> +reflow soldering + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + +1 + + + + +>NAME + + +<b>PIN HEADER</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + +<b>Thin Quad Flat Pack</b><p> +package type TQ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME + + +<b>CRYSTAL</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + +<b>Resistors, Capacitors, Inductors</b><p> +Based on the previous libraries: +<ul> +<li>r.lbr +<li>cap.lbr +<li>cap-fe.lbr +<li>captant.lbr +<li>polcap.lbr +<li>ipc-smd.lbr +</ul> +All SMD packages are defined according to the IPC specifications and CECC<p> +<author>Created by librarian@cadsoft.de</author><p> +<p> +for Electrolyt Capacitors see also :<p> +www.bccomponents.com <p> +www.panasonic.com<p> +www.kemet.com<p> +http://www.secc.co.jp/pdf/os_e/2004/e_os_all.pdf <b>(SANYO)</b> +<p> +for trimmer refence see : <u>www.electrospec-inc.com/cross_references/trimpotcrossref.asp</u><p> + +<table border=0 cellspacing=0 cellpadding=0 width="100%" cellpaddding=0> +<tr valign="top"> + +<! <td width="10">&nbsp;</td> +<td width="90%"> + +<b><font color="#0000FF" size="4">TRIM-POT CROSS REFERENCE</font></b> +<P> +<TABLE BORDER=0 CELLSPACING=1 CELLPADDING=2> + <TR> + <TD COLSPAN=8> + <FONT SIZE=3 FACE=ARIAL><B>RECTANGULAR MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">BOURNS</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">BI&nbsp;TECH</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">DALE-VISHAY</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">PHILIPS/MEPCO</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">MURATA</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">PANASONIC</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">SPECTROL</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">MILSPEC</FONT> + </B> + </TD><TD>&nbsp;</TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3 > + 3005P<BR> + 3006P<BR> + 3006W<BR> + 3006Y<BR> + 3009P<BR> + 3009W<BR> + 3009Y<BR> + 3057J<BR> + 3057L<BR> + 3057P<BR> + 3057Y<BR> + 3059J<BR> + 3059L<BR> + 3059P<BR> + 3059Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 89P<BR> + 89W<BR> + 89X<BR> + 89PH<BR> + 76P<BR> + 89XH<BR> + 78SLT<BR> + 78L&nbsp;ALT<BR> + 56P&nbsp;ALT<BR> + 78P&nbsp;ALT<BR> + T8S<BR> + 78L<BR> + 56P<BR> + 78P<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + T18/784<BR> + 783<BR> + 781<BR> + -<BR> + -<BR> + -<BR> + 2199<BR> + 1697/1897<BR> + 1680/1880<BR> + 2187<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 8035EKP/CT20/RJ-20P<BR> + -<BR> + RJ-20X<BR> + -<BR> + -<BR> + -<BR> + 1211L<BR> + 8012EKQ&nbsp;ALT<BR> + 8012EKR&nbsp;ALT<BR> + 1211P<BR> + 8012EKJ<BR> + 8012EKL<BR> + 8012EKQ<BR> + 8012EKR<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 2101P<BR> + 2101W<BR> + 2101Y<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 2102L<BR> + 2102S<BR> + 2102Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + EVMCOG<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 43P<BR> + 43W<BR> + 43Y<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 40L<BR> + 40P<BR> + 40Y<BR> + 70Y-T602<BR> + 70L<BR> + 70P<BR> + 70Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + RT/RTR12<BR> + RT/RTR12<BR> + RT/RTR12<BR> + -<BR> + RJ/RJR12<BR> + RJ/RJR12<BR> + RJ/RJR12<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=8>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=8> + <FONT SIZE=4 FACE=ARIAL><B>SQUARE MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BOURN</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MURATA</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>SPECTROL</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MILSPEC</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3250L<BR> + 3250P<BR> + 3250W<BR> + 3250X<BR> + 3252P<BR> + 3252W<BR> + 3252X<BR> + 3260P<BR> + 3260W<BR> + 3260X<BR> + 3262P<BR> + 3262W<BR> + 3262X<BR> + 3266P<BR> + 3266W<BR> + 3266X<BR> + 3290H<BR> + 3290P<BR> + 3290W<BR> + 3292P<BR> + 3292W<BR> + 3292X<BR> + 3296P<BR> + 3296W<BR> + 3296X<BR> + 3296Y<BR> + 3296Z<BR> + 3299P<BR> + 3299W<BR> + 3299X<BR> + 3299Y<BR> + 3299Z<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66X&nbsp;ALT<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66X&nbsp;ALT<BR> + -<BR> + 64W&nbsp;ALT<BR> + -<BR> + 64P&nbsp;ALT<BR> + 64W&nbsp;ALT<BR> + 64X&nbsp;ALT<BR> + 64P<BR> + 64W<BR> + 64X<BR> + 66X&nbsp;ALT<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66P<BR> + 66W<BR> + 66X<BR> + 67P<BR> + 67W<BR> + 67X<BR> + 67Y<BR> + 67Z<BR> + 68P<BR> + 68W<BR> + 68X<BR> + 67Y&nbsp;ALT<BR> + 67Z&nbsp;ALT<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 5050<BR> + 5091<BR> + 5080<BR> + 5087<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + T63YB<BR> + T63XB<BR> + -<BR> + -<BR> + -<BR> + 5887<BR> + 5891<BR> + 5880<BR> + -<BR> + -<BR> + -<BR> + T93Z<BR> + T93YA<BR> + T93XA<BR> + T93YB<BR> + T93XB<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 8026EKP<BR> + 8026EKW<BR> + 8026EKM<BR> + 8026EKP<BR> + 8026EKB<BR> + 8026EKM<BR> + 1309X<BR> + 1309P<BR> + 1309W<BR> + 8024EKP<BR> + 8024EKW<BR> + 8024EKN<BR> + RJ-9P/CT9P<BR> + RJ-9W<BR> + RJ-9X<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3103P<BR> + 3103Y<BR> + 3103Z<BR> + 3103P<BR> + 3103Y<BR> + 3103Z<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3105P/3106P<BR> + 3105W/3106W<BR> + 3105X/3106X<BR> + 3105Y/3106Y<BR> + 3105Z/3105Z<BR> + 3102P<BR> + 3102W<BR> + 3102X<BR> + 3102Y<BR> + 3102Z<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + EVMCBG<BR> + EVMCCG<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 55-1-X<BR> + 55-4-X<BR> + 55-3-X<BR> + 55-2-X<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 50-2-X<BR> + 50-4-X<BR> + 50-3-X<BR> + -<BR> + -<BR> + -<BR> + 64P<BR> + 64W<BR> + 64X<BR> + 64Y<BR> + 64Z<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + RT/RTR22<BR> + RT/RTR22<BR> + RT/RTR22<BR> + RT/RTR22<BR> + RJ/RJR22<BR> + RJ/RJR22<BR> + RJ/RJR22<BR> + RT/RTR26<BR> + RT/RTR26<BR> + RT/RTR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RT/RTR24<BR> + RT/RTR24<BR> + RT/RTR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=8>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=8> + <FONT SIZE=4 FACE=ARIAL><B>SINGLE TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BOURN</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MURATA</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>SPECTROL</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MILSPEC</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3323P<BR> + 3323S<BR> + 3323W<BR> + 3329H<BR> + 3329P<BR> + 3329W<BR> + 3339H<BR> + 3339P<BR> + 3339W<BR> + 3352E<BR> + 3352H<BR> + 3352K<BR> + 3352P<BR> + 3352T<BR> + 3352V<BR> + 3352W<BR> + 3362H<BR> + 3362M<BR> + 3362P<BR> + 3362R<BR> + 3362S<BR> + 3362U<BR> + 3362W<BR> + 3362X<BR> + 3386B<BR> + 3386C<BR> + 3386F<BR> + 3386H<BR> + 3386K<BR> + 3386M<BR> + 3386P<BR> + 3386S<BR> + 3386W<BR> + 3386X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 25P<BR> + 25S<BR> + 25RX<BR> + 82P<BR> + 82M<BR> + 82PA<BR> + -<BR> + -<BR> + -<BR> + 91E<BR> + 91X<BR> + 91T<BR> + 91B<BR> + 91A<BR> + 91V<BR> + 91W<BR> + 25W<BR> + 25V<BR> + 25P<BR> + -<BR> + 25S<BR> + 25U<BR> + 25RX<BR> + 25X<BR> + 72XW<BR> + 72XL<BR> + 72PM<BR> + 72RX<BR> + -<BR> + 72PX<BR> + 72P<BR> + 72RXW<BR> + 72RXL<BR> + 72X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + T7YB<BR> + T7YA<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + TXD<BR> + TYA<BR> + TYP<BR> + -<BR> + TYD<BR> + TX<BR> + -<BR> + 150SX<BR> + 100SX<BR> + 102T<BR> + 101S<BR> + 190T<BR> + 150TX<BR> + 101<BR> + -<BR> + -<BR> + 101SX<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ET6P<BR> + ET6S<BR> + ET6X<BR> + RJ-6W/8014EMW<BR> + RJ-6P/8014EMP<BR> + RJ-6X/8014EMX<BR> + TM7W<BR> + TM7P<BR> + TM7X<BR> + -<BR> + 8017SMS<BR> + -<BR> + 8017SMB<BR> + 8017SMA<BR> + -<BR> + -<BR> + CT-6W<BR> + CT-6H<BR> + CT-6P<BR> + CT-6R<BR> + -<BR> + CT-6V<BR> + CT-6X<BR> + -<BR> + -<BR> + 8038EKV<BR> + -<BR> + 8038EKX<BR> + -<BR> + -<BR> + 8038EKP<BR> + 8038EKZ<BR> + 8038EKW<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + 3321H<BR> + 3321P<BR> + 3321N<BR> + 1102H<BR> + 1102P<BR> + 1102T<BR> + RVA0911V304A<BR> + -<BR> + RVA0911H413A<BR> + RVG0707V100A<BR> + RVA0607V(H)306A<BR> + RVA1214H213A<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3104B<BR> + 3104C<BR> + 3104F<BR> + 3104H<BR> + -<BR> + 3104M<BR> + 3104P<BR> + 3104S<BR> + 3104W<BR> + 3104X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + EVMQ0G<BR> + EVMQIG<BR> + EVMQ3G<BR> + EVMS0G<BR> + EVMQ0G<BR> + EVMG0G<BR> + -<BR> + -<BR> + -<BR> + EVMK4GA00B<BR> + EVM30GA00B<BR> + EVMK0GA00B<BR> + EVM38GA00B<BR> + EVMB6<BR> + EVLQ0<BR> + -<BR> + EVMMSG<BR> + EVMMBG<BR> + EVMMAG<BR> + -<BR> + -<BR> + EVMMCS<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + EVMM1<BR> + -<BR> + -<BR> + EVMM0<BR> + -<BR> + -<BR> + EVMM3<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + 62-3-1<BR> + 62-1-2<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 67R<BR> + -<BR> + 67P<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 67X<BR> + 63V<BR> + 63S<BR> + 63M<BR> + -<BR> + -<BR> + 63H<BR> + 63P<BR> + -<BR> + -<BR> + 63X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + RJ/RJR50<BR> + RJ/RJR50<BR> + RJ/RJR50<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> +</TABLE> +<P>&nbsp;<P> +<TABLE BORDER=0 CELLSPACING=1 CELLPADDING=3> + <TR> + <TD COLSPAN=7> + <FONT color="#0000FF" SIZE=4 FACE=ARIAL><B>SMD TRIM-POT CROSS REFERENCE</B></FONT> + <P> + <FONT SIZE=4 FACE=ARIAL><B>MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BOURNS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>TOCOS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>AUX/KYOCERA</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3224G<BR> + 3224J<BR> + 3224W<BR> + 3269P<BR> + 3269W<BR> + 3269X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 44G<BR> + 44J<BR> + 44W<BR> + 84P<BR> + 84W<BR> + 84X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + ST63Z<BR> + ST63Y<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + ST5P<BR> + ST5W<BR> + ST5X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=7>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=7> + <FONT SIZE=4 FACE=ARIAL><B>SINGLE TURN</B></FONT> + </TD> + </TR> + <TR> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BOURNS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>TOCOS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>AUX/KYOCERA</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3314G<BR> + 3314J<BR> + 3364A/B<BR> + 3364C/D<BR> + 3364W/X<BR> + 3313G<BR> + 3313J<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 23B<BR> + 23A<BR> + 21X<BR> + 21W<BR> + -<BR> + 22B<BR> + 22A<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ST5YL/ST53YL<BR> + ST5YJ/5T53YJ<BR> + ST-23A<BR> + ST-22B<BR> + ST-22<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ST-4B<BR> + ST-4A<BR> + -<BR> + -<BR> + -<BR> + ST-3B<BR> + ST-3A<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + EVM-6YS<BR> + EVM-1E<BR> + EVM-1G<BR> + EVM-1D<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + G4B<BR> + G4A<BR> + TR04-3S1<BR> + TRG04-2S1<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + DVR-43A<BR> + CVR-42C<BR> + CVR-42A/C<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> +</TABLE> +<P> +<FONT SIZE=4 FACE=ARIAL><B>ALT =&nbsp;ALTERNATE</B></FONT> +<P> + +&nbsp; +<P> +</td> +</tr> +</table> + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>Ceramic Chip Capacitor KEMET 0805 reflow solder</b><p> +Metric Code Size 2012 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 0204 reflow solder</b><p> +Metric Code Size 1005 + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + + + +<b>Pin Header Connectors</b><p> +<author>Created by librarian@cadsoft.de</author> + + +<b>PIN HEADER</b> + + + + + + + + + +>NAME +>VALUE + + + + + +<b>Diodes</b><p> +Based on the following sources: +<ul> +<li>Motorola : www.onsemi.com +<li>Fairchild : www.fairchildsemi.com +<li>Philips : www.semiconductors.com +<li>Vishay : www.vishay.de +</ul> +<author>Created by librarian@cadsoft.de</author> + + +<b>Diode Package</b> Reflow soldering<p> +INFINEON, www.infineon.com/cmc_upload/0/000/010/257/eh_db_5b.pdf + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME + + +RN4020 Bluetooth 4.0 module, surface mount castellation form-factor 19.5mm x 11.5mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Antenna + + + + +>NAME + + +SOT-23 package, 3 leads + + + + + + + +>NAME + + + + + + + + + + +<b>Small Outline Transistor</b><p> +package type OT + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + +<b>PNP Transistors</b><p> +<author>Created by librarian@cadsoft.de</author> + + +<b>SOT-23</b> + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + +<b>EAGLE Design Rules</b> +<p> +Die Standard-Design-Rules sind so gewählt, dass sie für +die meisten Anwendungen passen. Sollte ihre Platine +besondere Anforderungen haben, treffen Sie die erforderlichen +Einstellungen hier und speichern die Design Rules unter +einem neuen Namen ab. +<b>EAGLE Design Rules</b> +<p> +The default Design Rules have been set to cover +a wide range of applications. Your particular design +may have different requirements, so please make the +necessary adjustments and save your customized +design rules under a new name. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Since Version 6.2.2 text objects can contain more than one line, +which will not be processed correctly with this version. + + + diff --git a/test/test_pcb/eagle/controller_nodist.brd b/test/test_pcb/eagle/controller_nodist.brd new file mode 100644 index 0000000..2c35334 --- /dev/null +++ b/test/test_pcb/eagle/controller_nodist.brd @@ -0,0 +1,3161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +BATT +3.6V ++ + + + + + + + + + + + + +WellDone +Controller v4.1 +11/20/2014 + +Solar ++ +- + + + + +Microchip PIC24ka101 +CAD and Schematic files for the PIC24Fka101 series of microcontrollers. + + +<b>SMALL OUTLINE PACKAGE</b> + + + + + + + + + + + + + + + + +>NAME +>VALUE +1 + + + + + + + + + + +<b>SMALL OUTLINE TRANSISTOR</b><p> +reflow soldering + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + +1 + + + + +>NAME + + +<b>PIN HEADER</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + +<b>Thin Quad Flat Pack</b><p> +package type TQ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME + + +<b>CRYSTAL</b> + + + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + +<b>Resistors, Capacitors, Inductors</b><p> +Based on the previous libraries: +<ul> +<li>r.lbr +<li>cap.lbr +<li>cap-fe.lbr +<li>captant.lbr +<li>polcap.lbr +<li>ipc-smd.lbr +</ul> +All SMD packages are defined according to the IPC specifications and CECC<p> +<author>Created by librarian@cadsoft.de</author><p> +<p> +for Electrolyt Capacitors see also :<p> +www.bccomponents.com <p> +www.panasonic.com<p> +www.kemet.com<p> +http://www.secc.co.jp/pdf/os_e/2004/e_os_all.pdf <b>(SANYO)</b> +<p> +for trimmer refence see : <u>www.electrospec-inc.com/cross_references/trimpotcrossref.asp</u><p> + +<table border=0 cellspacing=0 cellpadding=0 width="100%" cellpaddding=0> +<tr valign="top"> + +<! <td width="10">&nbsp;</td> +<td width="90%"> + +<b><font color="#0000FF" size="4">TRIM-POT CROSS REFERENCE</font></b> +<P> +<TABLE BORDER=0 CELLSPACING=1 CELLPADDING=2> + <TR> + <TD COLSPAN=8> + <FONT SIZE=3 FACE=ARIAL><B>RECTANGULAR MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">BOURNS</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">BI&nbsp;TECH</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">DALE-VISHAY</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">PHILIPS/MEPCO</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">MURATA</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">PANASONIC</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">SPECTROL</FONT> + </B> + </TD> + <TD ALIGN=CENTER> + <B> + <FONT SIZE=3 FACE=ARIAL color="#FF0000">MILSPEC</FONT> + </B> + </TD><TD>&nbsp;</TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3 > + 3005P<BR> + 3006P<BR> + 3006W<BR> + 3006Y<BR> + 3009P<BR> + 3009W<BR> + 3009Y<BR> + 3057J<BR> + 3057L<BR> + 3057P<BR> + 3057Y<BR> + 3059J<BR> + 3059L<BR> + 3059P<BR> + 3059Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 89P<BR> + 89W<BR> + 89X<BR> + 89PH<BR> + 76P<BR> + 89XH<BR> + 78SLT<BR> + 78L&nbsp;ALT<BR> + 56P&nbsp;ALT<BR> + 78P&nbsp;ALT<BR> + T8S<BR> + 78L<BR> + 56P<BR> + 78P<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + T18/784<BR> + 783<BR> + 781<BR> + -<BR> + -<BR> + -<BR> + 2199<BR> + 1697/1897<BR> + 1680/1880<BR> + 2187<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 8035EKP/CT20/RJ-20P<BR> + -<BR> + RJ-20X<BR> + -<BR> + -<BR> + -<BR> + 1211L<BR> + 8012EKQ&nbsp;ALT<BR> + 8012EKR&nbsp;ALT<BR> + 1211P<BR> + 8012EKJ<BR> + 8012EKL<BR> + 8012EKQ<BR> + 8012EKR<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 2101P<BR> + 2101W<BR> + 2101Y<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 2102L<BR> + 2102S<BR> + 2102Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + EVMCOG<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 43P<BR> + 43W<BR> + 43Y<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 40L<BR> + 40P<BR> + 40Y<BR> + 70Y-T602<BR> + 70L<BR> + 70P<BR> + 70Y<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + RT/RTR12<BR> + RT/RTR12<BR> + RT/RTR12<BR> + -<BR> + RJ/RJR12<BR> + RJ/RJR12<BR> + RJ/RJR12<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=8>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=8> + <FONT SIZE=4 FACE=ARIAL><B>SQUARE MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BOURN</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MURATA</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>SPECTROL</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MILSPEC</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3250L<BR> + 3250P<BR> + 3250W<BR> + 3250X<BR> + 3252P<BR> + 3252W<BR> + 3252X<BR> + 3260P<BR> + 3260W<BR> + 3260X<BR> + 3262P<BR> + 3262W<BR> + 3262X<BR> + 3266P<BR> + 3266W<BR> + 3266X<BR> + 3290H<BR> + 3290P<BR> + 3290W<BR> + 3292P<BR> + 3292W<BR> + 3292X<BR> + 3296P<BR> + 3296W<BR> + 3296X<BR> + 3296Y<BR> + 3296Z<BR> + 3299P<BR> + 3299W<BR> + 3299X<BR> + 3299Y<BR> + 3299Z<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66X&nbsp;ALT<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66X&nbsp;ALT<BR> + -<BR> + 64W&nbsp;ALT<BR> + -<BR> + 64P&nbsp;ALT<BR> + 64W&nbsp;ALT<BR> + 64X&nbsp;ALT<BR> + 64P<BR> + 64W<BR> + 64X<BR> + 66X&nbsp;ALT<BR> + 66P&nbsp;ALT<BR> + 66W&nbsp;ALT<BR> + 66P<BR> + 66W<BR> + 66X<BR> + 67P<BR> + 67W<BR> + 67X<BR> + 67Y<BR> + 67Z<BR> + 68P<BR> + 68W<BR> + 68X<BR> + 67Y&nbsp;ALT<BR> + 67Z&nbsp;ALT<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 5050<BR> + 5091<BR> + 5080<BR> + 5087<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + T63YB<BR> + T63XB<BR> + -<BR> + -<BR> + -<BR> + 5887<BR> + 5891<BR> + 5880<BR> + -<BR> + -<BR> + -<BR> + T93Z<BR> + T93YA<BR> + T93XA<BR> + T93YB<BR> + T93XB<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 8026EKP<BR> + 8026EKW<BR> + 8026EKM<BR> + 8026EKP<BR> + 8026EKB<BR> + 8026EKM<BR> + 1309X<BR> + 1309P<BR> + 1309W<BR> + 8024EKP<BR> + 8024EKW<BR> + 8024EKN<BR> + RJ-9P/CT9P<BR> + RJ-9W<BR> + RJ-9X<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3103P<BR> + 3103Y<BR> + 3103Z<BR> + 3103P<BR> + 3103Y<BR> + 3103Z<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3105P/3106P<BR> + 3105W/3106W<BR> + 3105X/3106X<BR> + 3105Y/3106Y<BR> + 3105Z/3105Z<BR> + 3102P<BR> + 3102W<BR> + 3102X<BR> + 3102Y<BR> + 3102Z<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + EVMCBG<BR> + EVMCCG<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 55-1-X<BR> + 55-4-X<BR> + 55-3-X<BR> + 55-2-X<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 50-2-X<BR> + 50-4-X<BR> + 50-3-X<BR> + -<BR> + -<BR> + -<BR> + 64P<BR> + 64W<BR> + 64X<BR> + 64Y<BR> + 64Z<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + RT/RTR22<BR> + RT/RTR22<BR> + RT/RTR22<BR> + RT/RTR22<BR> + RJ/RJR22<BR> + RJ/RJR22<BR> + RJ/RJR22<BR> + RT/RTR26<BR> + RT/RTR26<BR> + RT/RTR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RJ/RJR26<BR> + RT/RTR24<BR> + RT/RTR24<BR> + RT/RTR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + RJ/RJR24<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=8>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=8> + <FONT SIZE=4 FACE=ARIAL><B>SINGLE TURN</B></FONT> + </TD> + </TR> + <TR> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BOURN</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MURATA</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>SPECTROL</B></FONT> + </TD> + <TD ALIGN=CENTER> + <FONT SIZE=3 FACE=ARIAL><B>MILSPEC</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3323P<BR> + 3323S<BR> + 3323W<BR> + 3329H<BR> + 3329P<BR> + 3329W<BR> + 3339H<BR> + 3339P<BR> + 3339W<BR> + 3352E<BR> + 3352H<BR> + 3352K<BR> + 3352P<BR> + 3352T<BR> + 3352V<BR> + 3352W<BR> + 3362H<BR> + 3362M<BR> + 3362P<BR> + 3362R<BR> + 3362S<BR> + 3362U<BR> + 3362W<BR> + 3362X<BR> + 3386B<BR> + 3386C<BR> + 3386F<BR> + 3386H<BR> + 3386K<BR> + 3386M<BR> + 3386P<BR> + 3386S<BR> + 3386W<BR> + 3386X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 25P<BR> + 25S<BR> + 25RX<BR> + 82P<BR> + 82M<BR> + 82PA<BR> + -<BR> + -<BR> + -<BR> + 91E<BR> + 91X<BR> + 91T<BR> + 91B<BR> + 91A<BR> + 91V<BR> + 91W<BR> + 25W<BR> + 25V<BR> + 25P<BR> + -<BR> + 25S<BR> + 25U<BR> + 25RX<BR> + 25X<BR> + 72XW<BR> + 72XL<BR> + 72PM<BR> + 72RX<BR> + -<BR> + 72PX<BR> + 72P<BR> + 72RXW<BR> + 72RXL<BR> + 72X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + T7YB<BR> + T7YA<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + TXD<BR> + TYA<BR> + TYP<BR> + -<BR> + TYD<BR> + TX<BR> + -<BR> + 150SX<BR> + 100SX<BR> + 102T<BR> + 101S<BR> + 190T<BR> + 150TX<BR> + 101<BR> + -<BR> + -<BR> + 101SX<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ET6P<BR> + ET6S<BR> + ET6X<BR> + RJ-6W/8014EMW<BR> + RJ-6P/8014EMP<BR> + RJ-6X/8014EMX<BR> + TM7W<BR> + TM7P<BR> + TM7X<BR> + -<BR> + 8017SMS<BR> + -<BR> + 8017SMB<BR> + 8017SMA<BR> + -<BR> + -<BR> + CT-6W<BR> + CT-6H<BR> + CT-6P<BR> + CT-6R<BR> + -<BR> + CT-6V<BR> + CT-6X<BR> + -<BR> + -<BR> + 8038EKV<BR> + -<BR> + 8038EKX<BR> + -<BR> + -<BR> + 8038EKP<BR> + 8038EKZ<BR> + 8038EKW<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + 3321H<BR> + 3321P<BR> + 3321N<BR> + 1102H<BR> + 1102P<BR> + 1102T<BR> + RVA0911V304A<BR> + -<BR> + RVA0911H413A<BR> + RVG0707V100A<BR> + RVA0607V(H)306A<BR> + RVA1214H213A<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 3104B<BR> + 3104C<BR> + 3104F<BR> + 3104H<BR> + -<BR> + 3104M<BR> + 3104P<BR> + 3104S<BR> + 3104W<BR> + 3104X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + EVMQ0G<BR> + EVMQIG<BR> + EVMQ3G<BR> + EVMS0G<BR> + EVMQ0G<BR> + EVMG0G<BR> + -<BR> + -<BR> + -<BR> + EVMK4GA00B<BR> + EVM30GA00B<BR> + EVMK0GA00B<BR> + EVM38GA00B<BR> + EVMB6<BR> + EVLQ0<BR> + -<BR> + EVMMSG<BR> + EVMMBG<BR> + EVMMAG<BR> + -<BR> + -<BR> + EVMMCS<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + EVMM1<BR> + -<BR> + -<BR> + EVMM0<BR> + -<BR> + -<BR> + EVMM3<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + 62-3-1<BR> + 62-1-2<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 67R<BR> + -<BR> + 67P<BR> + -<BR> + -<BR> + -<BR> + -<BR> + 67X<BR> + 63V<BR> + 63S<BR> + 63M<BR> + -<BR> + -<BR> + 63H<BR> + 63P<BR> + -<BR> + -<BR> + 63X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + RJ/RJR50<BR> + RJ/RJR50<BR> + RJ/RJR50<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> +</TABLE> +<P>&nbsp;<P> +<TABLE BORDER=0 CELLSPACING=1 CELLPADDING=3> + <TR> + <TD COLSPAN=7> + <FONT color="#0000FF" SIZE=4 FACE=ARIAL><B>SMD TRIM-POT CROSS REFERENCE</B></FONT> + <P> + <FONT SIZE=4 FACE=ARIAL><B>MULTI-TURN</B></FONT> + </TD> + </TR> + <TR> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BOURNS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>TOCOS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>AUX/KYOCERA</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3224G<BR> + 3224J<BR> + 3224W<BR> + 3269P<BR> + 3269W<BR> + 3269X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 44G<BR> + 44J<BR> + 44W<BR> + 84P<BR> + 84W<BR> + 84X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + ST63Z<BR> + ST63Y<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + ST5P<BR> + ST5W<BR> + ST5X<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> + <TR> + <TD COLSPAN=7>&nbsp; + </TD> + </TR> + <TR> + <TD COLSPAN=7> + <FONT SIZE=4 FACE=ARIAL><B>SINGLE TURN</B></FONT> + </TD> + </TR> + <TR> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BOURNS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>BI&nbsp;TECH</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>DALE-VISHAY</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PHILIPS/MEPCO</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>PANASONIC</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>TOCOS</B></FONT> + </TD> + <TD> + <FONT SIZE=3 FACE=ARIAL><B>AUX/KYOCERA</B></FONT> + </TD> + </TR> + <TR> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 3314G<BR> + 3314J<BR> + 3364A/B<BR> + 3364C/D<BR> + 3364W/X<BR> + 3313G<BR> + 3313J<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + 23B<BR> + 23A<BR> + 21X<BR> + 21W<BR> + -<BR> + 22B<BR> + 22A<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ST5YL/ST53YL<BR> + ST5YJ/5T53YJ<BR> + ST-23A<BR> + ST-22B<BR> + ST-22<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + ST-4B<BR> + ST-4A<BR> + -<BR> + -<BR> + -<BR> + ST-3B<BR> + ST-3A<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + EVM-6YS<BR> + EVM-1E<BR> + EVM-1G<BR> + EVM-1D<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + G4B<BR> + G4A<BR> + TR04-3S1<BR> + TRG04-2S1<BR> + -<BR> + -<BR> + -<BR></FONT> + </TD> + <TD BGCOLOR="#cccccc" ALIGN=CENTER><FONT FACE=ARIAL SIZE=3> + -<BR> + -<BR> + DVR-43A<BR> + CVR-42C<BR> + CVR-42A/C<BR> + -<BR> + -<BR></FONT> + </TD> + </TR> +</TABLE> +<P> +<FONT SIZE=4 FACE=ARIAL><B>ALT =&nbsp;ALTERNATE</B></FONT> +<P> + +&nbsp; +<P> +</td> +</tr> +</table> + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + +<b>Ceramic Chip Capacitor KEMET 0805 reflow solder</b><p> +Metric Code Size 2012 + + + + +>NAME +>VALUE + + + + +<b>Ceramic Chip Capacitor KEMET 0204 reflow solder</b><p> +Metric Code Size 1005 + + + + +>NAME +>VALUE + + + + +<b>RESISTOR</b> + + + + + + + + +>NAME +>VALUE + + + + + + + +<b>Pin Header Connectors</b><p> +<author>Created by librarian@cadsoft.de</author> + + +<b>PIN HEADER</b> + + + + + + + + + +>NAME +>VALUE + + + + + +<b>Diodes</b><p> +Based on the following sources: +<ul> +<li>Motorola : www.onsemi.com +<li>Fairchild : www.fairchildsemi.com +<li>Philips : www.semiconductors.com +<li>Vishay : www.vishay.de +</ul> +<author>Created by librarian@cadsoft.de</author> + + +<b>Diode Package</b> Reflow soldering<p> +INFINEON, www.infineon.com/cmc_upload/0/000/010/257/eh_db_5b.pdf + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + + + + + +>NAME + + +RN4020 Bluetooth 4.0 module, surface mount castellation form-factor 19.5mm x 11.5mm + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Antenna + + + + +>NAME + + +SOT-23 package, 3 leads + + + + + + + +>NAME + + + + + + + + + + +<b>Small Outline Transistor</b><p> +package type OT + + + + + + + + + + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + +<b>PNP Transistors</b><p> +<author>Created by librarian@cadsoft.de</author> + + +<b>SOT-23</b> + + + + + + + +>NAME +>VALUE + + + + + + + + + + + + + + + + + + + + + +<b>EAGLE Design Rules</b> +<p> +Die Standard-Design-Rules sind so gewählt, dass sie für +die meisten Anwendungen passen. Sollte ihre Platine +besondere Anforderungen haben, treffen Sie die erforderlichen +Einstellungen hier und speichern die Design Rules unter +einem neuen Namen ab. +<b>EAGLE Design Rules</b> +<p> +The default Design Rules have been set to cover +a wide range of applications. Your particular design +may have different requirements, so please make the +necessary adjustments and save your customized +design rules under a new name. + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +Since Version 6.2.2 text objects can contain more than one line, +which will not be processed correctly with this version. + + + diff --git a/test/test_pcb/eagle/pcb_part_cache.db b/test/test_pcb/eagle/pcb_part_cache.db new file mode 100644 index 0000000..db2f6b7 Binary files /dev/null and b/test/test_pcb/eagle/pcb_part_cache.db differ diff --git a/test/test_pcb/test_bommatch.py b/test/test_pcb/test_bommatch.py new file mode 100644 index 0000000..a24a5d5 --- /dev/null +++ b/test/test_pcb/test_bommatch.py @@ -0,0 +1,105 @@ +import unittest +import os.path +import os +from nose.tools import * +from pymomo.exceptions import * +from pymomo.pcb import CircuitBoard +import pymomo.pcb.partcache +import tempfile +import shutil +import atexit +import hashlib + +class TestBOMCreation(unittest.TestCase): + """ + Test to make sure that BOM matching and BOM generation work. + """ + + def _create_tempfile(self, src=None): + """ + Create a named temporary file that is a copy of the src file + and register for it to be deleted at program exit. + """ + dst = tempfile.NamedTemporaryFile(delete=False) + + if src is not None: + with open(src, "r+b") as f: + shutil.copyfileobj(f, dst) + + dst.close() + + atexit.register(os.remove, dst.name) + return dst.name + + def _hash_file(self, path): + hasher = hashlib.sha256() + + with open(path,'r') as f: + block = 2**16 - 1 + buf = f.read(block) + while len(buf) > 0: + hasher.update(buf) + buf = f.read(block) + + return hasher.hexdigest() + + def setUp(self): + self.srcbrd = self._create_tempfile(os.path.join(os.path.dirname(__file__), 'eagle', 'controller_missing_attrs.brd')) + cachefile = self._create_tempfile(os.path.join(os.path.dirname(__file__), 'eagle', 'pcb_part_cache.db')) + + #Make sure we use our cache file and don't let it expire so we don't delete all of the entries + pymomo.pcb.partcache.default_cachefile(cachefile) + pymomo.pcb.partcache.default_noexpire(True) + self.board = CircuitBoard(self.srcbrd) + self.board.set_match_engine("CacheOnlyMatcher") + + def tearDown(self): + pass + + def test_cached_matching(self): + """ + Make sure that the cache is working + """ + + self.board.match_status() + print self.board.match_details() + + assert self.board.match_status() == True + + def test_lookup(self): + part = self.board.lookup('U5') + assert part is not None + + def test_update_metadata(self): + self.board.update_metadata('U5') + + board1 = CircuitBoard(self.srcbrd) + part = board1.find('U5') + + print part.mpn + print part.manu + print part.desc + + assert part.mpn == 'M25PX80-VMN6TP' + assert part.manu == 'MICRON' + + def test_update_all_metadata(self): + self.board.update_all_metadata() + + board1 = CircuitBoard(self.srcbrd) + + for part in board1._iterate_parts(): + assert part.mpn is not None + assert part.manu is not None + + def test_export_excel(self): + """ + Make sure that the BOM exporter works for excel files without error + """ + + output = self._create_tempfile() + + self.board.export_bom(output, format='excel') + #We can't verify the output with a hash since there's an embedded timestamp + #in the file format so just make sure it runs without error. + diff --git a/test/test_pcb/test_cache.py b/test/test_pcb/test_cache.py new file mode 100644 index 0000000..611a86e --- /dev/null +++ b/test/test_pcb/test_cache.py @@ -0,0 +1,56 @@ +import unittest +import os.path +from nose.tools import * +from pymomo.exceptions import * +from pymomo.pcb.partcache import PartCache +import tempfile +import shutil +import atexit + +class TestCache(unittest.TestCase): + """ + Test to make sure that caching of part request responses is working. + """ + + def _create_tempfile(self, src=None): + """ + Create a named temporary file that is a copy of the src file + and register for it to be deleted at program exit. + """ + dst = tempfile.NamedTemporaryFile(delete=False) + + if src is not None: + with open(src, "r+b") as f: + shutil.copyfileobj(f, dst) + + dst.close() + + atexit.register(os.remove, dst.name) + return dst.name + + def setUp(self): + self.filled_cachefile = self._create_tempfile(os.path.join(os.path.dirname(__file__), 'eagle', 'pcb_part_cache.db')) + self.empty_cachefile = self._create_tempfile() + + def test_create_cache(self): + pc = PartCache(cache=self.empty_cachefile) + + assert pc.size() == 0 + + val = {'test':'test', 'hello': 2} + pc.set('hello', val) + assert pc.size() == 1 + + stored = pc.get('hello') + assert val == stored + + def test_expire_parts(self): + """ + Make sure parts are expired like they should be + """ + + pc1 = PartCache(cache=self.filled_cachefile, no_expire=True) + assert pc1.size() > 0 + + pc2 = PartCache(cache=self.filled_cachefile, no_expire=False, expiration=1) + assert pc2.size() == 0 diff --git a/test/test_pcb/test_circuitboard.py b/test/test_pcb/test_circuitboard.py new file mode 100644 index 0000000..861ddec --- /dev/null +++ b/test/test_pcb/test_circuitboard.py @@ -0,0 +1,23 @@ +import unittest +import os.path +import os +from nose.tools import * +from pymomo.exceptions import * +from pymomo.pcb import CircuitBoard +import tempfile +import shutil + +def test_check_attributes(): + #Board is missing Company and Dimension unit attributes + board = os.path.join(os.path.dirname(__file__), 'eagle', 'controller_missing_attrs.brd') + b = CircuitBoard(board) + + assert b.is_clean() == False + errors = b.get_errors() + warnings = b.get_warnings() + + print errors + print warnings + + assert len(errors) == 3 + assert len(warnings) == 0 diff --git a/test/test_pcb/test_eagleparsing.py b/test/test_pcb/test_eagleparsing.py new file mode 100644 index 0000000..053c8e4 --- /dev/null +++ b/test/test_pcb/test_eagleparsing.py @@ -0,0 +1,103 @@ +import unittest +import os.path +from nose.tools import * +from pymomo.exceptions import * +from pymomo.pcb.eagle import part +from pymomo.pcb import CircuitBoard +import tempfile +import shutil +import os + +def test_parse_distpn(): + #Board is missing Company and Dimension unit attributes + board1 = os.path.join(os.path.dirname(__file__), 'eagle', 'controller_missing_attrs.brd') + board2 = os.path.join(os.path.dirname(__file__), 'eagle', 'controller_dist_distpn.brd') + b1 = CircuitBoard(board1) + b2 = CircuitBoard(board2) + + p1 = b1.find('U5') + p2 = b2.find('U5') + + print str(p1) + print str(p2) + + assert p1 == p2 + +def test_assy_vars(): + board = os.path.join(os.path.dirname(__file__), 'eagle', 'assyvars.brd') + b2 = CircuitBoard(board) + variants = b2.get_variants() + print variants + + assert len(variants) == 3 + assert 'VAR1' in variants + assert 'VAR2' in variants + assert 'VAR3' in variants + + p1 = b2.find('R1', 'VAR1') + p2 = b2.find('R1', 'VAR2') + + print p1 + print p2 + + assert p1.value == '1K' + assert p2.value == '25k' + + assert p1.dist == 'Digikey' + assert p2.dist == 'Digikey' + + assert p1.distpn == '1' + assert p2.distpn == '2' + +@raises(ArgumentError) +def test_assy_nopop(): + board = os.path.join(os.path.dirname(__file__), 'eagle', 'assyvars.brd') + b2 = CircuitBoard(board) + + b2.find('R1', 'VAR3') + +def test_nopop_list(): + board = os.path.join(os.path.dirname(__file__), 'eagle', 'assyvars.brd') + b2 = CircuitBoard(board) + + p1 = b2.nonpopulated_parts('VAR1') + p2 = b2.nonpopulated_parts('VAR2') + p3 = b2.nonpopulated_parts('VAR3') + + assert len(p1) == 0 + assert len(p2) == 0 + assert len(p3) == 1 + + assert p3[0] == 'R1' + +def test_update_attribute(): + board = os.path.join(os.path.dirname(__file__), 'eagle', 'controller_missing_attrs.brd') + brdcopy = _copy_to_temp(board) + + b = CircuitBoard(brdcopy) + + u5 = b.find('U5') + assert u5.mpn == None + assert u5.manu == None + + b.board.set_metadata(u5, None, 'mpn', "test_mpn") + b.board.set_metadata(u5, None, 'manu', "test_manu") + b.board.set_metadata(u5, None, 'digikey-pn', "test_digipn") + bn = CircuitBoard(brdcopy) + + u5 = bn.find('U5') + assert u5.mpn == 'TEST_MPN' + assert u5.manu == 'TEST_MANU' + assert u5.distpn == 'TEST_DIGIPN' + + os.remove(brdcopy) + +def _copy_to_temp(src): + dst = tempfile.NamedTemporaryFile(delete=False) + + with open(src, "r+b") as f: + shutil.copyfileobj(f, dst) + + dst.close() + + return dst.name \ No newline at end of file diff --git a/test/test_pcb/test_production.py b/test/test_pcb/test_production.py new file mode 100644 index 0000000..d809890 --- /dev/null +++ b/test/test_pcb/test_production.py @@ -0,0 +1,76 @@ +import unittest +import os.path +import os +from nose.tools import * +from nose.plugins.skip import SkipTest +from pymomo.exceptions import * +from pymomo.pcb import CircuitBoard +from pymomo.pcb.production import ProductionFileGenerator +from distutils.spawn import find_executable +import pymomo +import tempfile +import shutil +import atexit + +class TestProductionFileGeneration(unittest.TestCase): + """ + Test to make sure that BOM matching and BOM generation work. + """ + + def _create_tempfile(self, src=None, ext=None): + """ + Create a named temporary file that is a copy of the src file + and register for it to be deleted at program exit. + """ + dst = tempfile.NamedTemporaryFile(delete=False) + + if src is not None: + with open(src, "r+b") as f: + shutil.copyfileobj(f, dst) + + dst.close() + + name = dst.name + if ext is not None: + name = dst.name+"." + ext + os.rename(dst.name, name) + + atexit.register(os.remove, name) + return name + + def _create_tempdir(self): + path = tempfile.mkdtemp() + atexit.register(shutil.rmtree, path) + return path + + def setUp(self): + self.srcbrd = self._create_tempfile(os.path.join(os.path.dirname(__file__), 'eagle', 'controller_complete.brd'), ext='brd') + cachefile = self._create_tempfile(os.path.join(os.path.dirname(__file__), 'eagle', 'pcb_part_cache.db')) + + #Make sure we use our cache file and don't let it expire so we don't delete all of the entries + pymomo.pcb.partcache.default_cachefile(cachefile) + pymomo.pcb.partcache.default_noexpire(True) + self.board = CircuitBoard(self.srcbrd) + self.board.set_match_engine("CacheOnlyMatcher") + + def tearDown(self): + pass + + def test_basic(self): + """ + Basic tests of ProductionFileGenerator + """ + + prod = ProductionFileGenerator(self.board) + prod.save_readme(self._create_tempfile()) + + def test_prod(self): + try: + eagle = self.board.board._find_eagle() + except EnvironmentError: + raise SkipTest("Eagle could not be found") + + print eagle + + out = self._create_tempdir() + self.board.generate_production(out) diff --git a/test/test_typedargs/extra_type_package/__init__.py b/test/test_typedargs/extra_type_package/__init__.py index 6e3ac21..00939db 100644 --- a/test/test_typedargs/extra_type_package/__init__.py +++ b/test/test_typedargs/extra_type_package/__init__.py @@ -1,2 +1,2 @@ -new_pkg_type = {} \ No newline at end of file +import extra_type as new_pkg_type \ No newline at end of file diff --git a/test/test_typedargs/extra_type_package/extra_type.py b/test/test_typedargs/extra_type_package/extra_type.py new file mode 100644 index 0000000..17b0fc1 --- /dev/null +++ b/test/test_typedargs/extra_type_package/extra_type.py @@ -0,0 +1,7 @@ +#extra_type.py + +def convert(arg, **kwargs): + return None + +def default_formatter(arg, **kwargs): + return "" \ No newline at end of file diff --git a/test/test_typedargs/extra_types.py b/test/test_typedargs/extra_types.py index 10fd277..ead2dbc 100644 --- a/test/test_typedargs/extra_types.py +++ b/test/test_typedargs/extra_types.py @@ -1,2 +1,8 @@ -new_type = {} \ No newline at end of file +class new_type: + def convert(cls, value): + pass + + def default_formatter(cls, value): + pass + diff --git a/test/test_typedargs/test_builtin_types.py b/test/test_typedargs/test_builtin_types.py new file mode 100644 index 0000000..303a3bf --- /dev/null +++ b/test/test_typedargs/test_builtin_types.py @@ -0,0 +1,72 @@ +from pymomo.utilities.typedargs import type_system +from pymomo.utilities import typedargs +from nose.tools import * +from pymomo.exceptions import * + +def test_builtins_exist(): + builtin = ['integer', 'path', 'string'] + + for b in builtin: + type_system.get_type(b) + +def test_builtin_conversions(): + val = type_system.convert_to_type('42', 'integer') + assert val == 42 + + val = type_system.convert_to_type('/users/timburke', 'path') + assert val == '/users/timburke' + + val = type_system.convert_to_type('hello', 'string') + assert val == 'hello' + +def test_annotation_correct(): + @typedargs.param("string_param", "string", desc='Hello') + def function_test(string_param): + pass + + function_test("hello") + +@raises(ArgumentError) +def test_annotation_unknown_type(): + @typedargs.param("string_param", "unknown_type", desc='Hello') + def function_test(string_param): + pass + + function_test("hello") + +@raises(ValidationError) +def test_annotation_validation(): + @typedargs.param("int_param", "integer", "nonnegative", desc="No desc") + def function_test(int_param): + pass + + function_test(-1) + +def test_bool_valid(): + val = type_system.convert_to_type('True', 'bool') + assert val == True + + val = type_system.convert_to_type('false', 'bool') + assert val == False + + val = type_system.convert_to_type(True, 'bool') + assert val == True + + val = type_system.convert_to_type(False, 'bool') + assert val == False + + val = type_system.convert_to_type(None, 'bool') + assert val is None + + val = type_system.convert_to_type(0, 'bool') + assert val == False + + val = type_system.convert_to_type(1, 'bool') + assert val == True + +def test_format_bool(): + val = type_system.format_value(True, 'bool') + assert val == 'True' + + val = type_system.format_value(False, 'bool') + assert val == 'False' diff --git a/test/test_typedargs/test_complex_types.py b/test/test_typedargs/test_complex_types.py new file mode 100644 index 0000000..3a61d40 --- /dev/null +++ b/test/test_typedargs/test_complex_types.py @@ -0,0 +1,32 @@ +from pymomo.utilities import typedargs +from pymomo.utilities.typedargs import type_system +import unittest +from nose.tools import * +import os.path +from pymomo.exceptions import * + +def test_splitting(): + base, is_complex, subs = type_system.split_type('map(string, integer)') + assert base == 'map' + assert is_complex == True + assert len(subs) == 2 + assert subs[0] == 'string' + assert subs[1] == 'integer' + +def test_map_type(): + mapper = type_system.get_type('map(string, string)') + +def test_map_formatting(): + val = {'hello': 5} + + formatted = type_system.format_value(val, 'map(string, integer)') + assert formatted == 'hello: 5' + +def test_list_type(): + mapper = type_system.get_type('list(integer)') + +def test_list_formatting(): + val = [10, 15] + + formatted = type_system.format_value(val, 'list(integer)') + assert formatted == "10\n15" diff --git a/test/test_typedargs/test_injection.py b/test/test_typedargs/test_injection.py index b603aad..435de69 100644 --- a/test/test_typedargs/test_injection.py +++ b/test/test_typedargs/test_injection.py @@ -1,16 +1,17 @@ from pymomo.utilities import typedargs +from pymomo.utilities.typedargs import type_system import unittest from nose.tools import * import os.path from pymomo.exceptions import * def test_type_injection(): - typeobj = {} + import extra_type_package.extra_type as typeobj - assert not typedargs.is_known_type("test_injected_type1") + assert not type_system.is_known_type("test_injected_type1") - typedargs.inject_type("test_injected_type1", typeobj) - assert typedargs.is_known_type("test_injected_type1") + type_system.inject_type("test_injected_type1", typeobj) + assert type_system.is_known_type("test_injected_type1") def test_external_module_injection(): """ @@ -19,9 +20,9 @@ def test_external_module_injection(): path = os.path.join(os.path.dirname(__file__), 'extra_types') - assert not typedargs.is_known_type('new_type') - typedargs.load_external_types(path) - assert typedargs.is_known_type('new_type') + assert not type_system.is_known_type('new_type') + type_system.load_external_types(path) + assert type_system.is_known_type('new_type') def test_external_package_injection(): """ @@ -30,9 +31,9 @@ def test_external_package_injection(): path = os.path.join(os.path.dirname(__file__), 'extra_type_package') - assert not typedargs.is_known_type('new_pkg_type') - typedargs.load_external_types(path) - assert typedargs.is_known_type('new_pkg_type') + assert not type_system.is_known_type('new_pkg_type') + type_system.load_external_types(path) + assert type_system.is_known_type('new_pkg_type') @raises(ArgumentError) def test_external_package_injection_failure(): @@ -41,4 +42,4 @@ def test_external_package_injection_failure(): """ path = os.path.join(os.path.dirname(__file__), 'extra_type_package_nonexistant') - typedargs.load_external_types(path) + type_system.load_external_types(path) diff --git a/test/test_typedargs/test_returntypes.py b/test/test_typedargs/test_returntypes.py new file mode 100644 index 0000000..eb2f86d --- /dev/null +++ b/test/test_typedargs/test_returntypes.py @@ -0,0 +1,24 @@ +from pymomo.utilities.typedargs import type_system +from pymomo.utilities import typedargs +from nose.tools import * +from pymomo.exceptions import * + +def test_simplereturntype(): + @typedargs.return_type("string") + def returns_string(): + return "hello" + + val = returns_string() + formed = type_system.format_return_value(returns_string, val) + + assert formed == "hello" + +def test_complexreturntype(): + @typedargs.return_type("map(string, integer)") + def returns_map(): + return {"hello": 5} + + val = returns_map() + formed = type_system.format_return_value(returns_map, val) + + assert formed == "hello: 5" diff --git a/test/test_utilities/test_settings_dir.py b/test/test_utilities/test_settings_dir.py index 9f90145..ca4abf0 100644 --- a/test/test_utilities/test_settings_dir.py +++ b/test/test_utilities/test_settings_dir.py @@ -29,7 +29,6 @@ def test_settings_windows(self): base = self.confdir settings_dir = os.path.abspath(os.path.join(base, 'WellDone-MoMo')) - assert settings_dir == self.paths.settings @unittest.skipIf(platform.system() != 'Darwin', 'Mac OS X specific test')