├── .gitignore ├── .rspec ├── .travis.yml ├── Gemfile ├── Gemfile.lock ├── README.md ├── lib ├── board.rb ├── exporter │ └── json.rb ├── feature_symbol_name.rb ├── line.rb ├── net.rb ├── net_point.rb ├── odb_symbol.rb ├── pad.rb ├── parseable.rb └── parsers │ ├── components_parser.rb │ ├── features_parser.rb │ └── netlist_parser.rb ├── main.rb ├── sample ├── components ├── features └── netlist └── spec ├── lib ├── exporter │ └── json_spec.rb └── parsers │ └── components_parser_spec.rb └── spec_helper.rb /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --color 2 | --require spec_helper 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.2 4 | script: bundle exec rspec -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | ruby "2.2.4" 2 | 3 | source 'https://rubygems.org' 4 | 5 | gem 'require_all' 6 | gem 'rspec', '~> 3.0' 7 | gem 'thor', '~> 0.19' 8 | gem 'rubocop', require: false -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | ast (2.3.0) 5 | diff-lcs (1.2.5) 6 | parser (2.3.1.4) 7 | ast (~> 2.2) 8 | powerpack (0.1.1) 9 | rainbow (2.1.0) 10 | require_all (1.3.3) 11 | rspec (3.5.0) 12 | rspec-core (~> 3.5.0) 13 | rspec-expectations (~> 3.5.0) 14 | rspec-mocks (~> 3.5.0) 15 | rspec-core (3.5.4) 16 | rspec-support (~> 3.5.0) 17 | rspec-expectations (3.5.0) 18 | diff-lcs (>= 1.2.0, < 2.0) 19 | rspec-support (~> 3.5.0) 20 | rspec-mocks (3.5.0) 21 | diff-lcs (>= 1.2.0, < 2.0) 22 | rspec-support (~> 3.5.0) 23 | rspec-support (3.5.0) 24 | rubocop (0.44.1) 25 | parser (>= 2.3.1.1, < 3.0) 26 | powerpack (~> 0.1) 27 | rainbow (>= 1.99.1, < 3.0) 28 | ruby-progressbar (~> 1.7) 29 | unicode-display_width (~> 1.0, >= 1.0.1) 30 | ruby-progressbar (1.8.1) 31 | thor (0.19.1) 32 | unicode-display_width (1.1.1) 33 | 34 | PLATFORMS 35 | ruby 36 | 37 | DEPENDENCIES 38 | require_all 39 | rspec (~> 3.0) 40 | rubocop 41 | thor (~> 0.19) 42 | 43 | RUBY VERSION 44 | ruby 2.2.4p230 45 | 46 | BUNDLED WITH 47 | 1.14.4 48 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # odb++ parser 2 | 3 | [![Build Status](https://travis-ci.org/capablemonkey/odb-pp-parser.svg?branch=master)](https://travis-ci.org/capablemonkey/odb-pp-parser) 4 | 5 | Parses odb++ files to give a human readable JSON representation of a board layer. Currently only parses **features** and **netlist** files. 6 | 7 | Built against the ODB++ Format Specification Version 8.1 (Sept. 2015), available here: http://www.odb-sa.com/resources/ 8 | 9 | ## Getting started 10 | 11 | You'll need Ruby 2.0+. Install dependencies: 12 | 13 | ``` 14 | bundle install 15 | ``` 16 | 17 | Output will be sent to `stdout` 18 | 19 | ### Parsing the board 20 | 21 | To parse the board: 22 | 23 | ``` 24 | ruby main.rb layer sample/features sample/netlist sample/components 25 | ``` 26 | 27 | ```json 28 | { 29 | "nets": [], 30 | "components": [], 31 | "pads": [] 32 | } 33 | ``` 34 | 35 | ### Parsing `features` 36 | 37 | To parse the `sample/features` file: 38 | 39 | ``` 40 | ruby main.rb features sample/features 41 | ``` 42 | 43 | ```json 44 | { 45 | "pads": [ 46 | { 47 | "id": "209", 48 | "symbol": { 49 | "type": "round", 50 | "diameter": "55" 51 | }, 52 | "x": "1.59", 53 | "y": "0.1" 54 | }, 55 | { 56 | "id": "312", 57 | "symbol": { 58 | "type": "oval", 59 | "width": "24", 60 | "height": "74" 61 | }, 62 | "x": "1.64", 63 | "y": "0.66" 64 | }, 65 | { 66 | "id": "346", 67 | "symbol": { 68 | "type": "square", 69 | "side": "70" 70 | }, 71 | "x": "0.25425295", 72 | "y": "0.69755837" 73 | } 74 | ] 75 | } 76 | ``` 77 | 78 | ### Parsing `netlist` 79 | 80 | To parse the `sample/netlist` file: 81 | 82 | ``` 83 | ruby main.rb netlist sample/netlist 84 | ``` 85 | 86 | ```json 87 | { 88 | "nets": [ 89 | { 90 | "name": "GND", 91 | "points": [ 92 | { 93 | "x": "1.72", 94 | "y": "0.56" 95 | }, 96 | { 97 | "x": "1.14", 98 | "y": "0.41" 99 | }, 100 | { 101 | "x": "1.59", 102 | "y": "0.1" 103 | }, 104 | { 105 | "x": "1.64", 106 | "y": "0.56" 107 | }, 108 | { 109 | "x": "1.64", 110 | "y": "0.51" 111 | }, 112 | { 113 | "x": "0.2552531", 114 | "y": "0.2975584" 115 | }, 116 | { 117 | "x": "1.2197234", 118 | "y": "0.4059187" 119 | }, 120 | { 121 | "x": "1.5294807", 122 | "y": "0.1026173" 123 | } 124 | ] 125 | } 126 | ] 127 | } 128 | ``` 129 | 130 | ### Parsing layer 131 | 132 | This includes net information for each pad: 133 | 134 | To parse the layer: 135 | 136 | ``` 137 | ruby main.rb layer sample/features sample/netlist 138 | ``` 139 | 140 | ```json 141 | { 142 | "pads": [ 143 | { 144 | "id": "90", 145 | "symbol": { 146 | "type": "round", 147 | "diameter": 55.0 148 | }, 149 | "x": 1.94, 150 | "y": 0.36, 151 | "net": "+5V" 152 | }, 153 | { 154 | "id": "96", 155 | "symbol": { 156 | "type": "round", 157 | "diameter": 55.0 158 | }, 159 | "x": 1.52, 160 | "y": 0.56, 161 | "net": "+5V" 162 | }, 163 | { 164 | "id": "100", 165 | "symbol": { 166 | "type": "round", 167 | "diameter": 55.0 168 | }, 169 | "x": 1.41, 170 | "y": 0.1, 171 | "net": "+5V" 172 | } 173 | ] 174 | } 175 | ``` 176 | 177 | ### Running tests 178 | 179 | To run tests: 180 | 181 | ``` 182 | rspec 183 | ``` 184 | 185 | ### Abilities 186 | 187 | Currently it can identify these entities: 188 | 189 | #### Standard symbols 190 | 191 | - round 192 | - square 193 | - rectangle 194 | - oval 195 | 196 | #### Features 197 | 198 | - pads 199 | - lines 200 | 201 | #### Nets 202 | 203 | - net_name 204 | - serial_num 205 | 206 | #### Net points 207 | 208 | - net_num 209 | - radius 210 | - x 211 | - y 212 | - side 213 | - w (optional) 214 | - h (optional) 215 | - epoint 216 | - exp 217 | 218 | #### Components 219 | 220 | - CMP records (components) 221 | - PRP records (properties) 222 | - TOP records (toeprints) 223 | -------------------------------------------------------------------------------- /lib/board.rb: -------------------------------------------------------------------------------- 1 | class Board 2 | def initialize(files) 3 | @netlist = NetlistParser.new(files[:netlist]) 4 | @features = FeaturesParser.new(files[:features]) 5 | @components = ComponentsParser.new(files[:components]).components 6 | end 7 | 8 | def describe_components 9 | @components.map do |component| 10 | { 11 | :id => component['id'], 12 | :x => component['x'], 13 | :y => component['y'], 14 | :comp_name => component['comp_name'], 15 | :part_name => component['part_name'], 16 | :rotation => component['rot'], 17 | :mirror => component['mirror'], 18 | :properties => component['properties'], 19 | :toeprints => component['toeprints'].map do |toeprint| 20 | underlying_pad = @features.get_pad_at_point(toeprint['x'], toeprint['y']) 21 | { 22 | :x => toeprint['x'], 23 | :y => toeprint['y'], 24 | :net => @netlist.get_net_by_index(toeprint['net_num']).name, 25 | :pad_id => underlying_pad ? underlying_pad.id : nil 26 | } 27 | end 28 | } 29 | end 30 | end 31 | 32 | def describe_pads 33 | @features.describe_pads do |pad| 34 | net = @netlist.get_net_at_point(pad.x, pad.y) 35 | {:net => (net ? net.name : nil)} 36 | end 37 | end 38 | 39 | def describe_board 40 | { 41 | :nets => @netlist.nets.map(&:name), 42 | :components => describe_components, 43 | :pads => describe_pads, 44 | :lines => @features.describe_lines 45 | } 46 | end 47 | end -------------------------------------------------------------------------------- /lib/exporter/json.rb: -------------------------------------------------------------------------------- 1 | require 'json' 2 | 3 | class JSONExporter 4 | def self.dump_features(file_lines) 5 | features = FeaturesParser.new(file_lines) 6 | 7 | output = { 8 | :pads => features.describe_pads {{}} 9 | } 10 | 11 | JSON.dump(output) 12 | end 13 | 14 | def self.dump_netlist(file_lines) 15 | netlist = NetlistParser.new(file_lines) 16 | 17 | output = { 18 | :nets => netlist.describe_nets 19 | } 20 | 21 | JSON.dump(output) 22 | end 23 | 24 | def self.dump_features_with_net(feature_lines, netlist_lines) 25 | features = FeaturesParser.new(feature_lines) 26 | netlist = NetlistParser.new(netlist_lines) 27 | 28 | output = { 29 | :pads => features.describe_pads do |pad| 30 | net = netlist.get_net_at_point(pad.x, pad.y) 31 | {:net => (net ? net.name : nil)} 32 | end 33 | } 34 | 35 | JSON.dump(output) 36 | end 37 | 38 | def self.dump_board(feature_lines, netlist_lines, components_lines) 39 | board = Board.new( 40 | :features => feature_lines, 41 | :netlist => netlist_lines, 42 | :components => components_lines 43 | ) 44 | 45 | output = board.describe_board 46 | JSON.dump(output) 47 | end 48 | end 49 | 50 | -------------------------------------------------------------------------------- /lib/feature_symbol_name.rb: -------------------------------------------------------------------------------- 1 | class FeatureSymbolName 2 | extend Parseable 3 | 4 | REGEX = /^\$(?\d*) (?[[:alnum:]]*)( (?I|M))?$/ 5 | 6 | def self.from_lines(file_lines) 7 | parse(file_lines).map do |match_data_hash| 8 | [ 9 | match_data_hash['serial_num'], 10 | OdbSymbol::from_symbol_name(match_data_hash['symbol_name']) 11 | ] 12 | end.to_h 13 | end 14 | end -------------------------------------------------------------------------------- /lib/line.rb: -------------------------------------------------------------------------------- 1 | class Line 2 | extend Parseable 3 | 4 | REGEX = /^L (?\d*(.\d*)?) (?\d*(.\d*)?) (?\d*(.\d*)?) (?\d*(.\d*)?) (?\d*) (?P|N) (?\d);(?(?\d*=.*)*|,|(?\d*))*;ID=(?\d*)$/ 5 | 6 | attr_accessor :id, :start, :end, :symbol_index 7 | 8 | def initialize(match_data_hash) 9 | @match_data_hash = match_data_hash 10 | @id = @match_data_hash['id'] 11 | @start = { 12 | :x => @match_data_hash['xs'], 13 | :y => @match_data_hash['ys'] 14 | } 15 | 16 | @end = { 17 | :x => @match_data_hash['xe'], 18 | :y => @match_data_hash['ye'] 19 | } 20 | 21 | @symbol_index = @match_data_hash['sym_num'] 22 | end 23 | 24 | def describe 25 | { 26 | :id => @match_data_hash['id'], 27 | :start => @start, 28 | :end => @end 29 | } 30 | end 31 | 32 | def self.from_lines(file_lines) 33 | parse(file_lines).map do |match_data_hash| 34 | new(match_data_hash) 35 | end 36 | end 37 | end -------------------------------------------------------------------------------- /lib/net.rb: -------------------------------------------------------------------------------- 1 | class OdbNet 2 | extend Parseable 3 | 4 | REGEX = /^\$(?\d*) (?.*)$/ 5 | 6 | attr_accessor :name, :serial_num, :points 7 | 8 | def initialize(match_data_hash) 9 | @name = match_data_hash['net_name'] 10 | @serial_num = match_data_hash['serial_num'] 11 | @points = [] 12 | end 13 | 14 | def describe 15 | { 16 | :name => @name, 17 | :points => @points.map { |point| point.describe } 18 | } 19 | end 20 | 21 | def self.from_lines(file_lines) 22 | parse(file_lines).map do |match_data_hash| 23 | new(match_data_hash) 24 | end 25 | end 26 | 27 | def add_point(netpoint) 28 | @points.push(netpoint) 29 | netpoint.net = self 30 | end 31 | end -------------------------------------------------------------------------------- /lib/net_point.rb: -------------------------------------------------------------------------------- 1 | class NetPoint 2 | extend Parseable 3 | 4 | REGEX = /^(?\d*) (?\d*(.\d*)?) (?\d*(.\d*)?) (?\d*(.\d*)?) (?T|D|B|I) ((?\d*(.\d*)?) (?\d*(.\d*)?) )?(?e|m) (?e|c|p|s)/ 5 | 6 | attr_accessor :net_num, :x, :y, :net 7 | 8 | def initialize(match_data_hash) 9 | @match_data_hash = match_data_hash 10 | 11 | @net_num = @match_data_hash['net_num'] 12 | @x = @match_data_hash['x'] 13 | @y = @match_data_hash['y'] 14 | end 15 | 16 | def describe 17 | { 18 | :x => @x, 19 | :y => @y 20 | } 21 | end 22 | 23 | def self.from_lines(file_lines) 24 | parse(file_lines).map do |match_data_hash| 25 | new(match_data_hash) 26 | end 27 | end 28 | end -------------------------------------------------------------------------------- /lib/odb_symbol.rb: -------------------------------------------------------------------------------- 1 | class OdbSymbol 2 | extend Parseable 3 | 4 | STANDARD_SYMBOLS = { 5 | /^r(?\d+(\.?\d*))$/ => :round, 6 | /^s(?\d+\.?\d*)$/ => :square, 7 | /^rect(?\d+\.?\d*)x(?\d+\.?\d*)$/ => :rect, 8 | /^oval(?\d+\.?\d*)x(?\d+\.?\d*)$/ => :oval 9 | } 10 | 11 | def initialize(type, params) 12 | @type = type 13 | @params = params 14 | end 15 | 16 | def self.from_symbol_name(symbol_name) 17 | matching_regex = STANDARD_SYMBOLS. 18 | keys. 19 | map { |regex, _| regex if regex =~ symbol_name }. 20 | reject(&:nil?). 21 | first 22 | 23 | raise "Symbol not supported: #{symbol_name}" unless matching_regex 24 | 25 | type = STANDARD_SYMBOLS[matching_regex] 26 | params = match_data_to_hash(matching_regex.match(symbol_name)) 27 | 28 | new(type, params) 29 | end 30 | 31 | def to_h 32 | { 33 | :type => @type 34 | }.merge(@params) 35 | end 36 | end -------------------------------------------------------------------------------- /lib/pad.rb: -------------------------------------------------------------------------------- 1 | class Pad 2 | extend Parseable 3 | 4 | REGEX = /^P (?\d(.\d*)?) (?\d(.\d*)?) (?\d) (?P|N) (?\d) (?\d)( (?\d{0,3}))?;(?(?\d*=.*)*|,|(?\d*))*;ID=(?\d*)$/ 5 | 6 | attr_accessor :x, :y, :symbol_index, :id 7 | 8 | def initialize(match_data_hash) 9 | @match_data_hash = match_data_hash 10 | @id = @match_data_hash['id'] 11 | @x = match_data_hash['x'] 12 | @y = match_data_hash['y'] 13 | @symbol_index = match_data_hash['apt_def'] 14 | end 15 | 16 | def self.from_lines(file_lines) 17 | parse(file_lines).map do |match_data_hash| 18 | new(match_data_hash) 19 | end 20 | end 21 | 22 | def describe 23 | { 24 | :id => @id, 25 | :x => @x, 26 | :y => @y, 27 | :rotation => @match_data_hash['rotation'] 28 | } 29 | end 30 | end -------------------------------------------------------------------------------- /lib/parseable.rb: -------------------------------------------------------------------------------- 1 | module Parseable 2 | module_function 3 | 4 | REGEX = nil 5 | NUMBER_FIELDS = ['width', 'height', 'x', 'y', 'diameter', 'rotation'] 6 | 7 | def parse(file_lines) 8 | raise "No regex defined" if self::REGEX.nil? 9 | 10 | matches = file_lines.map { |line| parse_line(line, self::REGEX) } 11 | matches.reject &:nil? 12 | end 13 | 14 | def parse_line(line, regex) 15 | match_data = regex.match(line) 16 | match_data_to_hash(match_data) if match_data 17 | end 18 | 19 | def match_data_to_hash(match_data) 20 | hash = create_hash(match_data.names, match_data.captures) 21 | parse_number_fields(hash) 22 | end 23 | 24 | def parse_number_fields(match_hash) 25 | match_hash.map do |key, value| 26 | [key, NUMBER_FIELDS.include?(key) ? Float(value) : value] 27 | end.to_h 28 | end 29 | 30 | def create_hash(keys, values) 31 | keys.zip(values).to_h 32 | end 33 | end -------------------------------------------------------------------------------- /lib/parsers/components_parser.rb: -------------------------------------------------------------------------------- 1 | class ComponentsParser 2 | COMPONENT_REGEX = /^(?CMP) (?\d+) (?\d*(.\d*)?) (?\d*(.\d*)?) (?\d*(.\d*)?) (?N|M) (?.*?) (?.*?);(?(?\d*=.*)*|,|(?\d*))*;ID=(?\d*)$/ 3 | PROPERTY_REGEX = /^(?PRP) (?.*?) '(?.*?)'/ 4 | TOEPRINT_REGEX = /^(?TOP) (?\d*) (?\d*(.\d*)?) (?\d*(.\d*)?) (?\d*(.\d*)?) (?N|M) (?\d*) (?\d*) (?.*)/ 5 | 6 | attr_reader :components 7 | 8 | def initialize(file_lines) 9 | # algorithm: 10 | # - go line by line, keeping track of the last known # CMP 11 | # - if line is a PRP record, add it to the current CMP 12 | # - if line is TOP record, add it to the current CMP 13 | # - if new CMP is encountered, set it as the current CMP. 14 | 15 | # TODO: TOEPRINT net_num tells you which net it's on. Does that mean we can just draw components on the board and then use this ID to determine its net? 16 | # TODO: create a Component class which we can add properties and toeprints to? 17 | @components = [] 18 | @last_component = nil # hash describing a Component 19 | 20 | file_lines.each do |line| 21 | match = attempt_match(line) 22 | 23 | next if match.nil? 24 | 25 | if match['record_type'] == 'CMP' 26 | @components.push(@last_component) unless @last_component.nil? 27 | @last_component = match 28 | elsif match['record_type'] == 'PRP' 29 | @last_component['properties'] ||= {} 30 | name = match['name'] 31 | value = match['value'] 32 | @last_component['properties'][name] = value 33 | elsif match['record_type'] == 'TOP' 34 | @last_component['toeprints'] ||= [] 35 | @last_component['toeprints'].push(match) 36 | end 37 | end 38 | 39 | @components.push(@last_component) 40 | end 41 | 42 | private 43 | 44 | def attempt_match(line) 45 | match = Parseable.parse_line(line, COMPONENT_REGEX) 46 | return match unless match.nil? 47 | 48 | match = Parseable.parse_line(line, PROPERTY_REGEX) 49 | return match unless match.nil? 50 | 51 | match = Parseable.parse_line(line, TOEPRINT_REGEX) 52 | return match unless match.nil? 53 | end 54 | end -------------------------------------------------------------------------------- /lib/parsers/features_parser.rb: -------------------------------------------------------------------------------- 1 | class FeaturesParser 2 | attr_accessor :pads, :symbols 3 | 4 | POINT_MATCH_PRECISION = 5 5 | 6 | def initialize(file_lines) 7 | @pads = Pad::from_lines(file_lines) 8 | @symbols = FeatureSymbolName::from_lines(file_lines) 9 | @lines = Line::from_lines(file_lines) 10 | end 11 | 12 | def describe_pads 13 | @pads.map do |pad| 14 | pad.describe. 15 | merge(:symbol => @symbols[pad.symbol_index].to_h). 16 | merge(yield pad) 17 | end 18 | end 19 | 20 | def describe_lines 21 | @lines.map do |line| 22 | line.describe. 23 | merge(:symbol => @symbols[line.symbol_index].to_h) 24 | end 25 | end 26 | 27 | def get_pad_at_point(x, y) 28 | @pads. 29 | select { |pad| approximate_match?(pad.x, x) && approximate_match?(pad.y, y) }. 30 | first 31 | end 32 | 33 | def approximate_match?(a, b) 34 | a.round(POINT_MATCH_PRECISION) == b.round(POINT_MATCH_PRECISION) 35 | end 36 | end -------------------------------------------------------------------------------- /lib/parsers/netlist_parser.rb: -------------------------------------------------------------------------------- 1 | class NetlistParser 2 | attr_accessor :nets, :netpoints 3 | 4 | # decimal places to match: 5 | POINT_MATCH_PRECISION = 5 6 | 7 | def initialize(file_lines) 8 | @nets = OdbNet::from_lines(file_lines) 9 | @netpoints = NetPoint::from_lines(file_lines) 10 | 11 | # associate net points with net: 12 | nets_by_id = @nets.map { |net| [net.serial_num, net] }.to_h 13 | @netpoints.each do |point| 14 | net = nets_by_id[point.net_num] 15 | net.add_point(point) 16 | end 17 | end 18 | 19 | def describe_nets 20 | @nets.map { |net| net.describe } 21 | end 22 | 23 | def get_net_at_point(x, y) 24 | @points_to_net ||= @netpoints.map { |point| [point_key(point.x, point.y), point.net] }.to_h 25 | @points_to_net[point_key(x, y)] 26 | end 27 | 28 | def get_net_by_index(index) 29 | @nets. 30 | select { |net| net.serial_num == index }. 31 | first 32 | end 33 | 34 | private 35 | 36 | def point_key(x, y) 37 | "#{x.round(POINT_MATCH_PRECISION)},#{y.round(POINT_MATCH_PRECISION)}" 38 | end 39 | end -------------------------------------------------------------------------------- /main.rb: -------------------------------------------------------------------------------- 1 | require 'thor' 2 | require 'require_all' 3 | require_rel './lib' 4 | 5 | class CLI < Thor 6 | desc "features ", "export features file" 7 | 8 | def features(feature_filename) 9 | file_lines = File.read(feature_filename).lines 10 | puts JSONExporter::dump_features(file_lines) 11 | end 12 | 13 | desc "netlist ", "export netlist file" 14 | 15 | def netlist(netlist_filename) 16 | file_lines = File.read(netlist_filename).lines 17 | puts JSONExporter::dump_netlist(file_lines) 18 | end 19 | 20 | desc "layer ", "export pads with nets" 21 | 22 | def layer(features_filename, netlist_filename) 23 | feature_lines = File.read(features_filename).lines 24 | netlist_lines = File.read(netlist_filename).lines 25 | puts JSONExporter::dump_features_with_net(feature_lines, netlist_lines) 26 | end 27 | 28 | desc "board ", "parse board with components list, features, netlist" 29 | 30 | def board(features_filename, netlist_filename, components_filename) 31 | feature_lines = File.read(features_filename).lines 32 | netlist_lines = File.read(netlist_filename).lines 33 | components_lines = File.read(components_filename).lines 34 | puts JSONExporter::dump_board(feature_lines, netlist_lines, components_lines) 35 | end 36 | end 37 | 38 | CLI.start -------------------------------------------------------------------------------- /sample/components: -------------------------------------------------------------------------------- 1 | UNITS=INCH 2 | ID=299 3 | # 4 | #Component attribute names 5 | # 6 | 7 | 8 | # CMP 0 9 | CMP 0 1.75 0.51 90.0 N U1 LM161H ;;ID=306 10 | PRP Cost '' 11 | PRP Description 'HIGH SPEED DIFFERENTIAL COMPARATOR' 12 | PRP Manufacturer_#1 ' NATIONAL SEMICONDUCTOR' 13 | PRP Manufacturer_#2 ' ' 14 | PRP Part_Number ' LM161H' 15 | TOP 0 1.64 0.66 90.0 N 0 0 1 16 | TOP 1 1.64 0.61 90.0 N 5 1 2 17 | TOP 2 1.64 0.56 90.0 N 4 9 3 18 | TOP 3 1.64 0.51 90.0 N 4 10 4 19 | TOP 4 1.64 0.46 90.0 N 0 1 5 20 | TOP 5 1.64 0.41 90.0 N 2 5 6 21 | TOP 6 1.64 0.36 90.0 N 3 5 7 22 | TOP 7 1.86 0.36 90.0 N 1 7 8 23 | TOP 8 1.86 0.41 90.0 N 0 2 9 24 | TOP 9 1.86 0.46 90.0 N 0 3 10 25 | TOP 10 1.86 0.51 90.0 N 0 4 11 26 | TOP 11 1.86 0.56 90.0 N 0 5 12 27 | TOP 12 1.86 0.61 90.0 N 0 6 13 28 | TOP 13 1.86 0.66 90.0 N 0 7 14 29 | # 30 | # CMP 1 31 | CMP 1 0.2542531 0.6975584 90.0 N J1 CONRA-11-100 ;;ID=339 32 | TOP 0 0.2542531 0.6975584 90.0 N 5 2 1 33 | TOP 1 0.2552531 0.5975584 90.0 N 3 6 2 34 | TOP 2 0.2552531 0.4975584 90.0 N 2 6 3 35 | TOP 3 0.2552531 0.3975584 90.0 N 1 8 4 36 | TOP 4 0.2552531 0.2975584 90.0 N 4 11 5 37 | TOP 5 0.1542531 0.6475584 90.0 N 0 8 6 38 | TOP 6 0.1542531 0.5475584 90.0 N 0 9 7 39 | TOP 7 0.1542531 0.4475584 90.0 N 0 10 8 40 | TOP 8 0.1542531 0.3475584 90.0 N 0 11 9 41 | TOP 9 0.2542531 0.8975584 90.0 N 0 12 10 42 | TOP 10 0.2542531 0.0975584 90.0 N 0 13 11 43 | # 44 | # CMP 2 45 | CMP 0 1.3297234 0.5059187 90.0 N U2 LM160J-14 ;;ID=385 46 | PRP Cost '' 47 | PRP Description 'HIGH SPEED DIFFERENTIAL COMPARATOR' 48 | PRP Manufacturer_#1 ' NATIONAL SEMICONDUCTOR' 49 | PRP Manufacturer_#2 ' ' 50 | PRP Part_Number ' LM160J-14' 51 | TOP 0 1.2197234 0.6559187 90.0 N 0 14 1 52 | TOP 1 1.2197234 0.6059187 90.0 N 0 15 2 53 | TOP 2 1.2197234 0.5559187 90.0 N 0 16 3 54 | TOP 3 1.2197234 0.5059187 90.0 N 3 7 4 55 | TOP 4 1.2197234 0.4559187 90.0 N 2 7 5 56 | TOP 5 1.2197234 0.4059187 90.0 N 4 12 6 57 | TOP 6 1.2197234 0.3559187 90.0 N 0 17 7 58 | TOP 7 1.4397234 0.3559187 90.0 N 0 18 8 59 | TOP 8 1.4397234 0.4059187 90.0 N 0 19 9 60 | TOP 9 1.4397234 0.4559187 90.0 N 2 8 10 61 | TOP 10 1.4397234 0.5059187 90.0 N 3 8 11 62 | TOP 11 1.4397234 0.5559187 90.0 N 1 9 12 63 | TOP 12 1.4397234 0.6059187 90.0 N 0 20 13 64 | TOP 13 1.4397234 0.6559187 90.0 N 0 21 14 65 | # 66 | # CMP 3 67 | CMP 2 1.4984808 0.1026173 0.0 N C1 CAP0603 ;;ID=404 68 | PRP Description 'SURFACE MOUNT CAPACITOR 0.031 X 0.061 INCHES' 69 | PRP Manufacturer_#1 ' IPC SM-782 STD.' 70 | PRP Part_Number ' ' 71 | PRP Sim.Analog.Model '' 72 | PRP Sim.Analog.Order 'Model$' 73 | PRP Sim.Analog.Prefix '' 74 | PRP Tolerance '' 75 | PRP Value '.01uf' 76 | PRP Voltage_Rating ' ' 77 | TOP 0 1.4674808 0.1026173 0.0 N 1 10 1 78 | TOP 1 1.5294808 0.1026173 0.0 N 4 13 2 79 | # 80 | -------------------------------------------------------------------------------- /sample/features: -------------------------------------------------------------------------------- 1 | UNITS=INCH 2 | ID=41 3 | # 4 | #Num Features 5 | # 6 | F 120 7 | 8 | # 9 | #Feature symbol names 10 | # 11 | $0 r6 12 | $1 r55 13 | $2 r70 14 | $3 r177 15 | $4 s70 16 | $5 oval24x74 I 17 | $6 rect38x30 I 18 | 19 | # 20 | #Feature attribute names 21 | # 22 | @0 .smd 23 | @1 .geometry 24 | @2 .pad_usage 25 | 26 | # 27 | #Feature attribute text strings 28 | # 29 | &0 STANDARDVIA 30 | &1 SO14 31 | &2 DB9-HM_SML 32 | &3 0603 33 | 34 | # 35 | #Layer features 36 | # 37 | L 1.86 0.36 1.94 0.36 0 P 0;;ID=82 38 | P 1.94 0.36 1 P 0 8 0;1=0,2=1;ID=90 39 | L 1.43972343 0.5559187 1.46472343 0.5559187 0 P 0;;ID=93 40 | L 1.46472343 0.5559187 1.52 0.56 0 P 0;;ID=94 41 | P 1.52 0.56 1 P 0 8 0;1=0,2=1;ID=96 42 | P 1.41 0.1 1 P 0 8 0;1=0,2=1;ID=100 43 | L 1.41 0.1 1.46748081 0.1 0 P 0;;ID=103 44 | L 1.46748081 0.1 1.46748081 0.10261732 0 P 0;;ID=104 45 | L 1.43972343 0.4559187 1.41472343 0.4559187 0 P 0;;ID=107 46 | L 1.41472343 0.4559187 1.40238632 0.47327982 0 P 0;;ID=108 47 | L 1.40238632 0.47327982 1.37916634 0.4964998 0 P 0;;ID=109 48 | L 1.37916634 0.4964998 1.32049823 0.4964998 0 P 0;;ID=110 49 | L 1.32049823 0.4964998 1.29691722 0.4729188 0 P 0;;ID=111 50 | L 1.29691722 0.4729188 1.26172352 0.4729188 0 P 0;;ID=112 51 | L 1.26172352 0.4729188 1.24472343 0.4559187 0 P 0;;ID=113 52 | L 1.24472343 0.4559187 1.21972343 0.4559187 0 P 0;;ID=114 53 | L 0.25525305 0.49755837 0.29725315 0.53955846 0 P 0;;ID=115 54 | L 0.29725315 0.53955846 0.66997362 0.53955846 0 P 0;;ID=116 55 | L 0.66997362 0.53955846 0.67241516 0.5420001 0 P 0;;ID=117 56 | L 0.67241516 0.5420001 1.04759902 0.5420001 0 P 0;;ID=118 57 | L 1.04759902 0.5420001 1.05424035 0.53535886 0 P 0;;ID=119 58 | L 1.05424035 0.53535886 1.09 0.5195 0 P 0;;ID=120 59 | P 1.09 0.5195 1 P 0 8 0;1=0,2=1;ID=122 60 | P 1.36 0.46 1 P 0 8 0;1=0,2=1;ID=133 61 | L 1.36 0.46 1.41472343 0.4559187 0 P 0;;ID=135 62 | L 1.21972343 0.4559187 1.19472343 0.4559187 0 P 0;;ID=137 63 | L 1.19472343 0.4559187 1.17772333 0.4729188 0 P 0;;ID=138 64 | L 1.17772333 0.4729188 1.1513003 0.4729188 0 P 0;;ID=139 65 | L 1.1513003 0.4729188 1.1035002 0.4251187 0 P 0;;ID=140 66 | L 1.1035002 0.4251187 1.1035002 0.3948813 0 P 0;;ID=141 67 | L 1.1035002 0.3948813 1.1634625 0.334919 0 P 0;;ID=142 68 | L 1.1634625 0.334919 1.50126506 0.334919 0 P 0;;ID=143 69 | L 1.50126506 0.334919 1.55934596 0.3929999 0 P 0;;ID=144 70 | L 1.55934596 0.3929999 1.5979999 0.3929999 0 P 0;;ID=145 71 | L 1.5979999 0.3929999 1.615 0.41 0 P 0;;ID=146 72 | L 1.615 0.41 1.64 0.41 0 P 0;;ID=147 73 | L 1.21972343 0.5059187 1.24472343 0.5059187 0 P 0;;ID=150 74 | L 1.24472343 0.5059187 1.26172352 0.4889186 0 P 0;;ID=151 75 | L 1.26172352 0.4889186 1.29028986 0.4889186 0 P 0;;ID=152 76 | L 1.29028986 0.4889186 1.31387077 0.51249961 0 P 0;;ID=153 77 | L 1.31387077 0.51249961 1.38714282 0.51249961 0 P 0;;ID=154 78 | L 1.38714282 0.51249961 1.39372362 0.5059187 0 P 0;;ID=155 79 | L 1.39372362 0.5059187 1.43972343 0.5059187 0 P 0;;ID=156 80 | L 1.64 0.36 1.615 0.36 0 P 0;;ID=157 81 | L 1.615 0.36 1.5979999 0.3770001 0 P 0;;ID=158 82 | L 1.5979999 0.3770001 1.56597343 0.3770001 0 P 0;;ID=159 83 | L 1.56597343 0.3770001 1.50789242 0.31891919 0 P 0;;ID=160 84 | L 1.50789242 0.31891919 1.15683524 0.31891919 0 P 0;;ID=161 85 | L 1.15683524 0.31891919 1.08750039 0.38825394 0 P 0;;ID=162 86 | L 1.08750039 0.38825394 1.08750039 0.43174606 0 P 0;;ID=163 87 | L 1.08750039 0.43174606 1.14467303 0.4889186 0 P 0;;ID=164 88 | L 1.14467303 0.4889186 1.17772333 0.4889186 0 P 0;;ID=165 89 | L 1.17772333 0.4889186 1.19472343 0.5059187 0 P 0;;ID=166 90 | L 1.19472343 0.5059187 1.21972343 0.5059187 0 P 0;;ID=167 91 | L 1.43972343 0.5059187 1.41472343 0.5059187 0 P 0;;ID=168 92 | L 1.41472343 0.5059187 1.39955325 0.52044675 0 P 0;;ID=169 93 | L 1.39955325 0.52044675 1.34 0.58 0 P 0;;ID=170 94 | P 1.34 0.58 1 P 0 8 0;1=0,2=1;ID=172 95 | P 1.09 0.5805 1 P 0 8 0;1=0,2=1;ID=182 96 | L 1.09 0.5805 1.06419075 0.55469075 0 P 0;;ID=184 97 | L 1.06419075 0.55469075 1.06088159 0.5579999 0 P 0;;ID=185 98 | L 1.06088159 0.5579999 0.6657878 0.5579999 0 P 0;;ID=186 99 | L 0.6657878 0.5579999 0.66334616 0.55555827 0 P 0;;ID=187 100 | L 0.66334616 0.55555827 0.29725315 0.55555827 0 P 0;;ID=188 101 | L 0.29725315 0.55555827 0.25525305 0.59755837 0 P 0;;ID=189 102 | L 1.64 0.56 1.72 0.56 0 P 0;;ID=192 103 | P 1.72 0.56 1 P 0 8 0;1=0,2=1;ID=194 104 | L 1.21972343 0.4059187 1.19472343 0.4059187 0 P 0;;ID=198 105 | L 1.19472343 0.4059187 1.14 0.41 0 P 0;;ID=199 106 | P 1.14 0.41 1 P 0 8 0;1=0,2=1;ID=201 107 | L 1.52948081 0.10261732 1.52948081 0.1 0 P 0;;ID=206 108 | L 1.52948081 0.1 1.59 0.1 0 P 0;;ID=207 109 | P 1.59 0.1 1 P 0 8 0;1=0,2=1;ID=209 110 | L 1.64 0.51 1.64 0.56 0 P 0;;ID=214 111 | L 1.64 0.61 1.54217037 0.61 0 P 0;;ID=217 112 | L 1.54217037 0.61 1.52217037 0.63 0 P 0;;ID=218 113 | L 1.52217037 0.63 0.77837756 0.63 0 P 0;;ID=219 114 | L 0.77837756 0.63 0.71081919 0.69755837 0 P 0;;ID=220 115 | L 0.71081919 0.69755837 0.25425305 0.69755837 0 P 0;;ID=221 116 | P 1.64 0.66 5 P 0 8 90;0,1=1,2=0;ID=312 117 | P 1.64 0.61 5 P 0 8 90;0,1=1,2=0;ID=313 118 | P 1.64 0.56 5 P 0 8 90;0,1=1,2=0;ID=314 119 | P 1.64 0.51 5 P 0 8 90;0,1=1,2=0;ID=315 120 | P 1.64 0.46 5 P 0 8 90;0,1=1,2=0;ID=316 121 | P 1.64 0.41 5 P 0 8 90;0,1=1,2=0;ID=317 122 | P 1.64 0.36 5 P 0 8 90;0,1=1,2=0;ID=318 123 | P 1.86 0.36 5 P 0 8 90;0,1=1,2=0;ID=319 124 | P 1.86 0.41 5 P 0 8 90;0,1=1,2=0;ID=320 125 | P 1.86 0.46 5 P 0 8 90;0,1=1,2=0;ID=321 126 | P 1.86 0.51 5 P 0 8 90;0,1=1,2=0;ID=322 127 | P 1.86 0.56 5 P 0 8 90;0,1=1,2=0;ID=323 128 | P 1.86 0.61 5 P 0 8 90;0,1=1,2=0;ID=324 129 | P 1.86 0.66 5 P 0 8 90;0,1=1,2=0;ID=325 130 | P 0.25425295 0.69755837 4 P 0 8 90;1=2,2=0;ID=346 131 | P 0.25525295 0.59755837 2 P 0 8 90;1=2,2=0;ID=349 132 | P 0.25525295 0.49755837 2 P 0 8 90;1=2,2=0;ID=352 133 | P 0.25525295 0.39755837 2 P 0 8 90;1=2,2=0;ID=355 134 | P 0.25525295 0.29755837 2 P 0 8 90;1=2,2=0;ID=359 135 | P 0.15425295 0.64755837 2 P 0 8 90;1=2,2=0;ID=363 136 | P 0.15425295 0.54755837 2 P 0 8 90;1=2,2=0;ID=366 137 | P 0.15425295 0.44755837 2 P 0 8 90;1=2,2=0;ID=369 138 | P 0.15425295 0.34755837 2 P 0 8 90;1=2,2=0;ID=372 139 | P 0.25425295 0.89755837 3 P 0 8 90;1=2,2=0;ID=380 140 | P 0.25425295 0.09755837 3 P 0 8 90;1=2,2=0;ID=383 141 | P 1.21972343 0.6559187 5 P 0 8 90;0,1=1,2=0;ID=386 142 | P 1.21972343 0.6059187 5 P 0 8 90;0,1=1,2=0;ID=387 143 | P 1.21972343 0.5559187 5 P 0 8 90;0,1=1,2=0;ID=388 144 | P 1.21972343 0.5059187 5 P 0 8 90;0,1=1,2=0;ID=389 145 | P 1.21972343 0.4559187 5 P 0 8 90;0,1=1,2=0;ID=390 146 | P 1.21972343 0.4059187 5 P 0 8 90;0,1=1,2=0;ID=391 147 | P 1.21972343 0.3559187 5 P 0 8 90;0,1=1,2=0;ID=392 148 | P 1.43972343 0.3559187 5 P 0 8 90;0,1=1,2=0;ID=393 149 | P 1.43972343 0.4059187 5 P 0 8 90;0,1=1,2=0;ID=394 150 | P 1.43972343 0.4559187 5 P 0 8 90;0,1=1,2=0;ID=395 151 | P 1.43972343 0.5059187 5 P 0 8 90;0,1=1,2=0;ID=396 152 | P 1.43972343 0.5559187 5 P 0 8 90;0,1=1,2=0;ID=397 153 | P 1.43972343 0.6059187 5 P 0 8 90;0,1=1,2=0;ID=398 154 | P 1.43972343 0.6559187 5 P 0 8 90;0,1=1,2=0;ID=399 155 | P 1.46748071 0.10261732 6 P 0 8 0;0,1=3,2=0;ID=410 156 | P 1.52948071 0.10261732 6 P 0 8 0;0,1=3,2=0;ID=411 157 | -------------------------------------------------------------------------------- /sample/netlist: -------------------------------------------------------------------------------- 1 | H optimize N staggered N 2 | UNITS=INCH 3 | $0 $NONE$ 4 | $1 +5V 5 | $2 OUT_DATA_POS 6 | $3 OUT_DATA_NEG 7 | $4 GND 8 | $5 $$$16436 9 | # 10 | #Netlist points 11 | # 12 | 0 0.012 1.64 0.66 T e e staggered 0 0 0 13 | 0 0.012 1.64 0.46 T e e staggered 0 0 0 14 | 0 0.012 1.86 0.41 T e e staggered 0 0 0 15 | 0 0.012 1.86 0.46 T e e staggered 0 0 0 16 | 0 0.012 1.86 0.51 T e e staggered 0 0 0 17 | 0 0.012 1.86 0.56 T e e staggered 0 0 0 18 | 0 0.012 1.86 0.61 T e e staggered 0 0 0 19 | 0 0.012 1.86 0.66 T e e staggered 0 0 0 20 | 0 0.0185 0.1542531 0.6475584 B e e staggered 0 0 0 21 | 0 0.0185 0.1542531 0.5475584 B e e staggered 0 0 0 22 | 0 0.0185 0.1542531 0.4475584 B e e staggered 0 0 0 23 | 0 0.0185 0.1542531 0.3475584 B e e staggered 0 0 0 24 | 0 0.06 0.2542531 0.8975584 B e e staggered 0 0 0 25 | 0 0.06 0.2542531 0.0975584 B e e staggered 0 0 0 26 | 0 0.012 1.2197234 0.6559187 T e e staggered 0 0 0 27 | 0 0.012 1.2197234 0.6059187 T e e staggered 0 0 0 28 | 0 0.012 1.2197234 0.5559187 T e e staggered 0 0 0 29 | 0 0.012 1.2197234 0.3559187 T e e staggered 0 0 0 30 | 0 0.012 1.4397234 0.3559187 T e e staggered 0 0 0 31 | 0 0.012 1.4397234 0.4059187 T e e staggered 0 0 0 32 | 0 0.012 1.4397234 0.6059187 T e e staggered 0 0 0 33 | 0 0.012 1.4397234 0.6559187 T e e staggered 0 0 0 34 | 1 0.0185 1.94 0.36 B m e staggered 0 0 0 v 35 | 1 0.0185 1.52 0.56 B m e staggered 0 0 0 v 36 | 1 0.0185 1.41 0.1 B m e staggered 0 0 0 v 37 | 1 0.012 1.86 0.36 T e e staggered 0 0 0 38 | 1 0.0185 0.2552531 0.3975584 B e e staggered 0 0 0 39 | 1 0.012 1.4397234 0.5559187 T e e staggered 0 0 0 40 | 1 0 1.4674807 0.1026173 T 0.038 0.03 e e staggered 0 0 0 41 | 2 0.0185 1.09 0.5195 B m e staggered 0 0 0 v 42 | 2 0.0185 1.36 0.46 B m e staggered 0 0 0 v 43 | 2 0.012 1.64 0.41 T e e staggered 0 0 0 44 | 2 0.0185 0.2552531 0.4975584 B e e staggered 0 0 0 45 | 2 0.012 1.2197234 0.4559187 T e e staggered 0 0 0 46 | 2 0.012 1.4397234 0.4559187 T e e staggered 0 0 0 47 | 3 0.0185 1.34 0.58 B m e staggered 0 0 0 v 48 | 3 0.0185 1.09 0.5805 B m e staggered 0 0 0 v 49 | 3 0.012 1.64 0.36 T e e staggered 0 0 0 50 | 3 0.0185 0.2552531 0.5975584 B e e staggered 0 0 0 51 | 3 0.012 1.2197234 0.5059187 T e e staggered 0 0 0 52 | 3 0.012 1.4397234 0.5059187 T e e staggered 0 0 0 53 | 4 0.0185 1.72 0.56 B m e staggered 0 0 0 v 54 | 4 0.0185 1.14 0.41 B m e staggered 0 0 0 v 55 | 4 0.0185 1.59 0.1 B m e staggered 0 0 0 v 56 | 4 0.012 1.64 0.56 T e e staggered 0 0 0 57 | 4 0.012 1.64 0.51 T e e staggered 0 0 0 58 | 4 0.0185 0.2552531 0.2975584 B e e staggered 0 0 0 59 | 4 0.012 1.2197234 0.4059187 T e e staggered 0 0 0 60 | 4 0 1.5294807 0.1026173 T 0.038 0.03 e e staggered 0 0 0 61 | 5 0.012 1.64 0.61 T e e staggered 0 0 0 62 | 5 0.0185 0.2542531 0.6975584 B e e staggered 0 0 0 63 | -------------------------------------------------------------------------------- /spec/lib/exporter/json_spec.rb: -------------------------------------------------------------------------------- 1 | require 'require_all' 2 | require_rel '../../../lib' 3 | 4 | describe JSONExporter do 5 | describe '#dump_features' do 6 | context 'with pads' do 7 | let(:file_lines) do 8 | [ 9 | "$1 r55", 10 | "$5 oval24x74 I", 11 | "P 1.94 0.36 1 P 0 8 0;1=0,2=1;ID=90", 12 | "P 1.21972343 0.6559187 5 P 0 8 90;0,1=1,2=0;ID=386" 13 | ] 14 | end 15 | 16 | it 'should represent the round in JSON as expected' do 17 | output = JSONExporter::dump_features(file_lines) 18 | parsed_output = JSON.load(output) 19 | expect(parsed_output).to eq( 20 | { 21 | "pads" => [ 22 | {"id"=>"90", "symbol"=>{"type"=>"round", "diameter"=>55.0}, "x"=>1.94, "y"=>0.36}, 23 | {"id"=>"386", "symbol"=>{"type"=>"oval", "width"=>24.0, "height"=>74.0}, "x"=>1.21972343, "y"=>0.6559187} 24 | ] 25 | } 26 | ) 27 | end 28 | end 29 | end 30 | 31 | describe '#dump_netlist' do 32 | context 'with nets' do 33 | let(:file_lines) do 34 | [ 35 | "$0 $NONE$", 36 | "$2 OUT_DATA_POS", 37 | "0 0.012 1.4397234 0.3559187 T e e staggered 0 0 0", 38 | "2 0.0185 1.36 0.46 B m e staggered 0 0 0 v", 39 | "2 0.0185 0.2552531 0.4975584 B e e staggered 0 0 0" 40 | ] 41 | end 42 | 43 | it 'should represent the net in JSON as expected' do 44 | output = JSONExporter::dump_netlist(file_lines) 45 | parsed_output = JSON.load(output) 46 | expect(parsed_output).to eq( 47 | { 48 | "nets" => 49 | [ 50 | {"name"=>"$NONE$", "points"=>[{"x"=>1.4397234, "y"=>0.3559187}]}, 51 | {"name"=>"OUT_DATA_POS", "points"=>[{"x"=>1.36, "y"=>0.46}, {"x"=>0.2552531, "y"=>0.4975584}]} 52 | ] 53 | } 54 | ) 55 | end 56 | end 57 | end 58 | 59 | describe "#dump_features_with_net" do 60 | context 'with a netlist and features' do 61 | let(:feature_lines) do 62 | [ 63 | "$1 r55", 64 | "$5 oval24x74 I", 65 | "P 1.4397234 0.3559187 1 P 0 8 0;1=0,2=1;ID=90", 66 | "P 1.36 0.46 1 P 0 8 0;1=0,2=1;ID=92", 67 | "P 0.2552531 0.4975584 5 P 0 8 90;0,1=1,2=0;ID=386" 68 | ] 69 | end 70 | 71 | let(:netlist_lines) do 72 | [ 73 | "$0 $NONE$", 74 | "$2 OUT_DATA_POS", 75 | "0 0.012 1.4397234 0.3559187 T e e staggered 0 0 0", 76 | "2 0.0185 1.36 0.46 B m e staggered 0 0 0 v", 77 | "2 0.0185 0.2552531 0.4975584 B e e staggered 0 0 0" 78 | ] 79 | end 80 | 81 | it 'should return pads with their nets' do 82 | output = JSONExporter::dump_features_with_net(feature_lines, netlist_lines) 83 | parsed_output = JSON.load(output) 84 | expect(parsed_output).to eq( 85 | { 86 | "pads" => [ 87 | {"id"=>"90", "symbol"=>{"type"=>"round", "diameter"=>55.0}, "x"=>1.4397234, "y"=>0.3559187, "net"=>"$NONE$"}, 88 | {"id"=>"92", "symbol"=>{"type"=>"round", "diameter"=>55.0}, "x"=>1.36, "y"=>0.46, "net"=>"OUT_DATA_POS"}, 89 | {"id"=>"386", "symbol"=>{"type"=>"oval", "width"=>24.0, "height"=>74.0}, "x"=>0.2552531, "y"=>0.4975584, "net"=>"OUT_DATA_POS"} 90 | ] 91 | } 92 | ) 93 | end 94 | end 95 | end 96 | end -------------------------------------------------------------------------------- /spec/lib/parsers/components_parser_spec.rb: -------------------------------------------------------------------------------- 1 | require 'require_all' 2 | require_rel '../../../lib' 3 | 4 | describe ComponentsParser do 5 | describe "#components" do 6 | let(:file_lines) { File.read('sample/components').lines } 7 | 8 | it 'should parse components as expected' do 9 | parser = ComponentsParser.new(file_lines) 10 | components = parser.components 11 | 12 | expect(components.size).to eq(4) 13 | # puts components 14 | end 15 | end 16 | end -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # This file was generated by the `rspec --init` command. Conventionally, all 2 | # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`. 3 | # The generated `.rspec` file contains `--require spec_helper` which will cause 4 | # this file to always be loaded, without a need to explicitly require it in any 5 | # files. 6 | # 7 | # Given that it is always loaded, you are encouraged to keep this file as 8 | # light-weight as possible. Requiring heavyweight dependencies from this file 9 | # will add to the boot time of your test suite on EVERY test run, even for an 10 | # individual file that may not need all of that loaded. Instead, consider making 11 | # a separate helper file that requires the additional dependencies and performs 12 | # the additional setup, and require it from the spec files that actually need 13 | # it. 14 | # 15 | # The `.rspec` file also contains a few flags that are not defaults but that 16 | # users commonly want. 17 | # 18 | # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration 19 | RSpec.configure do |config| 20 | # rspec-expectations config goes here. You can use an alternate 21 | # assertion/expectation library such as wrong or the stdlib/minitest 22 | # assertions if you prefer. 23 | config.expect_with :rspec do |expectations| 24 | # This option will default to `true` in RSpec 4. It makes the `description` 25 | # and `failure_message` of custom matchers include text for helper methods 26 | # defined using `chain`, e.g.: 27 | # be_bigger_than(2).and_smaller_than(4).description 28 | # # => "be bigger than 2 and smaller than 4" 29 | # ...rather than: 30 | # # => "be bigger than 2" 31 | expectations.include_chain_clauses_in_custom_matcher_descriptions = true 32 | end 33 | 34 | # rspec-mocks config goes here. You can use an alternate test double 35 | # library (such as bogus or mocha) by changing the `mock_with` option here. 36 | config.mock_with :rspec do |mocks| 37 | # Prevents you from mocking or stubbing a method that does not exist on 38 | # a real object. This is generally recommended, and will default to 39 | # `true` in RSpec 4. 40 | mocks.verify_partial_doubles = true 41 | end 42 | 43 | # This option will default to `:apply_to_host_groups` in RSpec 4 (and will 44 | # have no way to turn it off -- the option exists only for backwards 45 | # compatibility in RSpec 3). It causes shared context metadata to be 46 | # inherited by the metadata hash of host groups and examples, rather than 47 | # triggering implicit auto-inclusion in groups with matching metadata. 48 | config.shared_context_metadata_behavior = :apply_to_host_groups 49 | 50 | # The settings below are suggested to provide a good initial experience 51 | # with RSpec, but feel free to customize to your heart's content. 52 | =begin 53 | # This allows you to limit a spec run to individual examples or groups 54 | # you care about by tagging them with `:focus` metadata. When nothing 55 | # is tagged with `:focus`, all examples get run. RSpec also provides 56 | # aliases for `it`, `describe`, and `context` that include `:focus` 57 | # metadata: `fit`, `fdescribe` and `fcontext`, respectively. 58 | config.filter_run_when_matching :focus 59 | 60 | # Allows RSpec to persist some state between runs in order to support 61 | # the `--only-failures` and `--next-failure` CLI options. We recommend 62 | # you configure your source control system to ignore this file. 63 | config.example_status_persistence_file_path = "spec/examples.txt" 64 | 65 | # Limits the available syntax to the non-monkey patched syntax that is 66 | # recommended. For more details, see: 67 | # - http://rspec.info/blog/2012/06/rspecs-new-expectation-syntax/ 68 | # - http://www.teaisaweso.me/blog/2013/05/27/rspecs-new-message-expectation-syntax/ 69 | # - http://rspec.info/blog/2014/05/notable-changes-in-rspec-3/#zero-monkey-patching-mode 70 | config.disable_monkey_patching! 71 | 72 | # This setting enables warnings. It's recommended, but in some cases may 73 | # be too noisy due to issues in dependencies. 74 | config.warnings = true 75 | 76 | # Many RSpec users commonly either run the entire suite or an individual 77 | # file, and it's useful to allow more verbose output when running an 78 | # individual spec file. 79 | if config.files_to_run.one? 80 | # Use the documentation formatter for detailed output, 81 | # unless a formatter has already been configured 82 | # (e.g. via a command-line flag). 83 | config.default_formatter = 'doc' 84 | end 85 | 86 | # Print the 10 slowest examples and example groups at the 87 | # end of the spec run, to help surface which specs are running 88 | # particularly slow. 89 | config.profile_examples = 10 90 | 91 | # Run specs in random order to surface order dependencies. If you find an 92 | # order dependency and want to debug it, you can fix the order by providing 93 | # the seed, which is printed after each run. 94 | # --seed 1234 95 | config.order = :random 96 | 97 | # Seed global randomization in this process using the `--seed` CLI option. 98 | # Setting this allows you to use `--seed` to deterministically reproduce 99 | # test failures related to randomization by passing the same `--seed` value 100 | # as the one that triggered the failure. 101 | Kernel.srand config.seed 102 | =end 103 | end 104 | --------------------------------------------------------------------------------