eaglecad 0 → 1
Sign up to get free protection for your applications and to get access to all the features.
- data/Gemfile +5 -1
- data/README.markdown +29 -0
- data/Rakefile +9 -0
- data/eaglecad.gemspec +4 -2
- data/lib/eaglecad.rb +15 -1
- data/lib/eaglecad/attribute.rb +47 -0
- data/lib/eaglecad/board.rb +184 -0
- data/lib/eaglecad/clearance.rb +31 -0
- data/lib/eaglecad/design_rules.rb +38 -0
- data/lib/eaglecad/deviceset.rb +113 -0
- data/lib/eaglecad/drawing.rb +124 -0
- data/lib/eaglecad/geometry.rb +259 -0
- data/lib/eaglecad/layer.rb +40 -0
- data/lib/eaglecad/library.rb +73 -0
- data/lib/eaglecad/package.rb +74 -0
- data/lib/eaglecad/part.rb +29 -0
- data/lib/eaglecad/schematic.rb +83 -0
- data/lib/eaglecad/sheet.rb +256 -0
- data/lib/eaglecad/symbol.rb +73 -0
- data/test/eaglecad.rb +12 -0
- data/test/eaglecad/board.rb +68 -0
- data/test/eaglecad/design_rules.rb +38 -0
- data/test/eaglecad/deviceset.rb +52 -0
- data/test/eaglecad/drawing.rb +94 -0
- data/test/eaglecad/library.rb +47 -0
- data/test/eaglecad/package.rb +60 -0
- data/test/eaglecad/schematic.rb +50 -0
- data/test/eaglecad/sheet.rb +24 -0
- data/test/eaglecad/symbol.rb +41 -0
- data/test/fixtures/board.xml +859 -0
- data/test/fixtures/demo1.sch +10932 -0
- data/test/fixtures/design_rules.xml +70 -0
- data/test/fixtures/deviceset.xml +683 -0
- data/test/fixtures/drawing_board.xml +910 -0
- data/test/fixtures/drawing_schematic.xml +10928 -0
- data/test/fixtures/hexapodu.brd +1863 -0
- data/test/fixtures/library.xml +175 -0
- data/test/fixtures/package.xml +28 -0
- data/test/fixtures/schematic.xml +10861 -0
- data/test/fixtures/sheet.xml +318 -0
- data/test/fixtures/symbol.xml +11 -0
- metadata +79 -5
- data/README.md +0 -29
data/Gemfile
CHANGED
data/README.markdown
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# EagleCAD
|
2
|
+
|
3
|
+
[![Build Status](https://travis-ci.org/bfoz/eaglecad-ruby.png)](https://travis-ci.org/bfoz/eaglecad-ruby)
|
4
|
+
|
5
|
+
The EagleCAD gem provides tools for working with the files generated by the popular [EagleCAD](http://www.cadsoftusa.com/) PCB design software.
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'eaglecad'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install eaglecad
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
require 'eaglecad'
|
25
|
+
|
26
|
+
drawing = EagleCAD.read('my_schematic.sch')
|
27
|
+
|
28
|
+
drawing.write('cloned_schematic.sch')
|
29
|
+
```
|
data/Rakefile
CHANGED
data/eaglecad.gemspec
CHANGED
@@ -4,12 +4,12 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "eaglecad"
|
7
|
-
spec.version = '
|
7
|
+
spec.version = '1'
|
8
8
|
spec.authors = ["Brandon Fosdick"]
|
9
9
|
spec.email = ["bfoz@bfoz.net"]
|
10
10
|
spec.description = %q{Everything you need to bend Eagle CAD projects to your will}
|
11
11
|
spec.summary = %q{Read and write Eagle CAD files}
|
12
|
-
spec.homepage = "http://github.com/bfoz/ruby
|
12
|
+
spec.homepage = "http://github.com/bfoz/eaglecad-ruby"
|
13
13
|
spec.license = "BSD"
|
14
14
|
|
15
15
|
spec.files = `git ls-files`.split($/)
|
@@ -19,4 +19,6 @@ Gem::Specification.new do |spec|
|
|
19
19
|
|
20
20
|
spec.add_development_dependency "bundler", "~> 1.3"
|
21
21
|
spec.add_development_dependency "rake"
|
22
|
+
|
23
|
+
spec.add_dependency 'geometry', '~> 6'
|
22
24
|
end
|
data/lib/eaglecad.rb
CHANGED
@@ -1,3 +1,17 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
require_relative 'eaglecad/drawing'
|
4
|
+
|
1
5
|
module EagleCAD
|
2
|
-
|
6
|
+
# Load and parse the given {Schematic} or {Board} file
|
7
|
+
# @return [Drawing] A new {EagleCAD::Drawing}
|
8
|
+
def self.read(filename)
|
9
|
+
parse(REXML::Document.new File.open(filename))
|
10
|
+
end
|
11
|
+
|
12
|
+
# @param [REXML::Document] An XML document to parse
|
13
|
+
# @return [Drawing] A new {EagleCAD::Drawing}, or nil if there was an error
|
14
|
+
def self.parse(document)
|
15
|
+
Drawing.from_xml(document.root.elements['drawing'])
|
16
|
+
end
|
3
17
|
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require_relative 'geometry'
|
2
|
+
|
3
|
+
module EagleCAD
|
4
|
+
Attribute = Struct.new :name, :value, :origin, :size, :layer_number, :font, :ratio, :rotation, :display, :constant do
|
5
|
+
def self.from_xml(element)
|
6
|
+
Attribute.new.tap do |attribute|
|
7
|
+
attribute.name = element.attributes['name']
|
8
|
+
attribute.value = element.attributes['value']
|
9
|
+
attribute.origin = Geometry.point_from(element)
|
10
|
+
attribute.size = element.attributes['size']
|
11
|
+
attribute.layer_number = element.attributes['layer']
|
12
|
+
attribute.ratio = element.attributes['ratio'].to_i
|
13
|
+
attribute.rotation = element.attributes['rot']
|
14
|
+
|
15
|
+
element.attributes.each do |name, value|
|
16
|
+
case name
|
17
|
+
when 'constant' then attribute.constant = ('yes' == element.attribute['constant'])
|
18
|
+
when 'display' then attribute.display = value
|
19
|
+
when 'font' then attribute.font = value
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize
|
26
|
+
super
|
27
|
+
@display = 'value'
|
28
|
+
end
|
29
|
+
|
30
|
+
# @return [REXML::Element]
|
31
|
+
def to_xml
|
32
|
+
REXML::Element.new('attribute').tap do |element|
|
33
|
+
element.add_attribute('name', name)
|
34
|
+
element.add_attribute('value', value)
|
35
|
+
element.add_attribute('x', origin.x)
|
36
|
+
element.add_attribute('y', origin.y)
|
37
|
+
element.add_attribute('size', size)
|
38
|
+
element.add_attribute('layer', layer_number)
|
39
|
+
element.add_attribute('ratio', ratio) unless 0 == ratio
|
40
|
+
element.add_attribute('rot', rotation)
|
41
|
+
element.add_attribute('constant', 'yes') if constant
|
42
|
+
element.add_attribute('display', display)
|
43
|
+
element.add_attribute('font', font)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,184 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
require_relative 'attribute'
|
4
|
+
require_relative 'clearance'
|
5
|
+
require_relative 'design_rules'
|
6
|
+
require_relative 'library'
|
7
|
+
|
8
|
+
module EagleCAD
|
9
|
+
class Board
|
10
|
+
attr_accessor :design_rules, :description
|
11
|
+
attr_reader :attributes, :classes, :elements, :libraries, :passes, :plain
|
12
|
+
|
13
|
+
ContactReference = Struct.new :element, :pad, :route, :route_tag
|
14
|
+
Via = Struct.new :origin, :extent, :drill
|
15
|
+
|
16
|
+
class Element
|
17
|
+
attr_accessor :name, :library, :package, :value, :origin, :locked, :smashed, :rotation
|
18
|
+
attr_reader :attributes, :variants
|
19
|
+
|
20
|
+
def self.from_xml(element)
|
21
|
+
self.new(element.attributes['name'], element.attributes['library'], element.attributes['package'], element.attributes['value'], Geometry.point_from(element)).tap do |object|
|
22
|
+
element.elements.each do |element|
|
23
|
+
case element.name
|
24
|
+
when 'attribute'
|
25
|
+
object.attributes.push Attribute.from_xml(element)
|
26
|
+
when 'variant'
|
27
|
+
variants = {}
|
28
|
+
element.attributes.each {|name, value| variants[name] = value }
|
29
|
+
object.variants.push variants
|
30
|
+
else
|
31
|
+
raise StandardError, "Unrecognized Element element '#{element.name}'"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def initialize(name, library, package, value, origin)
|
38
|
+
@name = name
|
39
|
+
@library = library
|
40
|
+
@package = package
|
41
|
+
@value = value
|
42
|
+
@origin = origin
|
43
|
+
@locked = false
|
44
|
+
@smashed = false
|
45
|
+
@rotation = 0
|
46
|
+
|
47
|
+
@attributes = []
|
48
|
+
@variants = []
|
49
|
+
end
|
50
|
+
|
51
|
+
# @return [REXML::Element]
|
52
|
+
def to_xml
|
53
|
+
REXML::Element.new('element').tap do |element|
|
54
|
+
element.add_attributes({'name' => name, 'library' => library, 'package' => package, 'value' => value, 'x' => origin.x, 'y' => origin.y})
|
55
|
+
element.add_attribute('rot', "R#{rotation}") if 0 != rotation
|
56
|
+
element.add_attribute('locked', 'yes') if locked
|
57
|
+
element.add_attribute('smashed', 'yes') if smashed
|
58
|
+
|
59
|
+
attributes.each {|attribute| element.add_element attribute.to_xml }
|
60
|
+
variants.each {|variant| element.add_element('variant', variant)}
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
class Signal
|
66
|
+
attr_accessor :air_wires_hidden, :clearance_class, :name
|
67
|
+
attr_reader :contact_references, :vias
|
68
|
+
|
69
|
+
def self.from_xml(element)
|
70
|
+
self.new(element.attributes['name']).tap do |signal|
|
71
|
+
element.attributes.each do |name, value|
|
72
|
+
case name
|
73
|
+
when 'airwireshidden'
|
74
|
+
signal.air_wires_hidden = ('no' != value)
|
75
|
+
when 'class'
|
76
|
+
signal.clearance_class = value.to_i
|
77
|
+
when 'name' # Ignore; already handled
|
78
|
+
else
|
79
|
+
raise StandardError, "Unrecognized Signal element '#{element.name}'"
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
element.elements.each do |element|
|
84
|
+
case element.name
|
85
|
+
when 'contactref'
|
86
|
+
signal.contact_references.push ContactReference.new element.attributes['element'], element.attributes['pad'], element.attributes['route'], element.attributes['routetag']
|
87
|
+
when 'polygon', 'wire'
|
88
|
+
signal.push Geometry.from_xml(element)
|
89
|
+
when 'via'
|
90
|
+
signal.vias.push Via.new Geometry.from_point(element), element.attributes['extent'], element.attributes['drill']
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def initialize(name)
|
97
|
+
@contact_references = []
|
98
|
+
@name = name
|
99
|
+
@vias = []
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def self.from_xml(element)
|
104
|
+
Board.new.tap do |board|
|
105
|
+
element.elements.each do |element|
|
106
|
+
case element.name
|
107
|
+
when 'attributes'
|
108
|
+
element.elements.each {|attribute| board.attributes.push Attribute.from_xml(attribute) }
|
109
|
+
when 'autorouter'
|
110
|
+
element.elements.each do |pass_element|
|
111
|
+
pass = board.passes[pass_element.attributes['name']]
|
112
|
+
pass_element.elements.each {|parameter| pass[parameter.attributes['name']] = parameter.attributes['value'] }
|
113
|
+
end
|
114
|
+
when 'classes'
|
115
|
+
element.elements.each {|clearance| board.classes.push Clearance.from_xml(clearance) }
|
116
|
+
when 'description'
|
117
|
+
board.description = element.text
|
118
|
+
when 'designrules'
|
119
|
+
board.design_rules = DesignRules.from_xml(element)
|
120
|
+
when 'elements'
|
121
|
+
element.elements.each {|object| board.elements.push Element.from_xml(object) }
|
122
|
+
when 'libraries'
|
123
|
+
element.elements.each {|library| board.libraries[library.attributes['name']] = Library.from_xml(library) }
|
124
|
+
when 'plain'
|
125
|
+
element.elements.each {|object| board.plain.push Geometry.from_xml(object) }
|
126
|
+
when 'signals'
|
127
|
+
when 'variantdefs'
|
128
|
+
else
|
129
|
+
raise StandardError, "Unrecognized Board element '#{element.name}'"
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def initialize
|
136
|
+
@attributes = []
|
137
|
+
@classes = []
|
138
|
+
@contact_references = []
|
139
|
+
@elements = []
|
140
|
+
@libraries = {}
|
141
|
+
@passes = Hash.new {|hash, key| hash[key] = Hash.new }
|
142
|
+
@plain = []
|
143
|
+
end
|
144
|
+
|
145
|
+
# @return [REXML::Element]
|
146
|
+
def to_xml
|
147
|
+
REXML::Element.new('board').tap do |element|
|
148
|
+
element.add_element('attributes').tap do |attribute_element|
|
149
|
+
attributes.each {|attribute| attribute_element.add_element attribute.to_xml }
|
150
|
+
end
|
151
|
+
|
152
|
+
element.add_element('description').text = description
|
153
|
+
|
154
|
+
element.add_element('autorouter').tap do |autorouter_element|
|
155
|
+
passes.each do |name, pass|
|
156
|
+
autorouter_element.add_element('pass', {'name' => name}).tap do |pass_element|
|
157
|
+
pass.each {|name, value| pass_element.add_element('param', {'name' => name, 'value' => value})}
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
element.add_element('classes').tap do |classes_element|
|
163
|
+
classes.each do |clearance|
|
164
|
+
classes_element.add_element clearance.to_xml
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
element.add_element(design_rules.to_xml) if design_rules
|
169
|
+
|
170
|
+
element.add_element('elements').tap do |element_element|
|
171
|
+
elements.each {|object| element_element.add_element object.to_xml }
|
172
|
+
end
|
173
|
+
|
174
|
+
element.add_element('libraries').tap do |libraries_element|
|
175
|
+
libraries.each {|name, library| libraries_element.add_element library.to_xml }
|
176
|
+
end
|
177
|
+
|
178
|
+
element.add_element('plain').tap do |plain_element|
|
179
|
+
plain.each {|object| plain_element.add_element object.to_xml }
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
module EagleCAD
|
4
|
+
class Clearance
|
5
|
+
attr_accessor :name, :number, :width, :drill
|
6
|
+
attr_reader :values
|
7
|
+
|
8
|
+
def self.from_xml(element)
|
9
|
+
Clearance.new(element.attributes['name'], element.attributes['number'].to_i).tap do |clearance|
|
10
|
+
clearance.width = (element.attributes['width'] || 0).to_f
|
11
|
+
clearance.drill = (element.attributes['drill'] || 0).to_f
|
12
|
+
|
13
|
+
element.elements.each {|element| clearance.values.push (element.text || 0).to_f }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def initialize(name, number)
|
18
|
+
@name = name
|
19
|
+
@number = number
|
20
|
+
@values = []
|
21
|
+
end
|
22
|
+
|
23
|
+
# @param [REXML::Element]
|
24
|
+
def to_xml
|
25
|
+
REXML::Element.new('class').tap do |element|
|
26
|
+
element.add_attributes({'name' => name, 'number' => number, 'width' => width, 'drill' => drill})
|
27
|
+
values.each {|value| element.add_element('clearance', {'class' => number, 'value' => value})}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
module EagleCAD
|
4
|
+
class DesignRules
|
5
|
+
attr_accessor :description, :name
|
6
|
+
attr_reader :parameters
|
7
|
+
|
8
|
+
def self.from_xml(element)
|
9
|
+
self.new(element.attributes['name']).tap do |rule|
|
10
|
+
element.elements.each do |element|
|
11
|
+
case element.name
|
12
|
+
when 'description'
|
13
|
+
rule.description = element.text
|
14
|
+
when 'param'
|
15
|
+
rule.parameters[element.attributes['name']] = element.attributes['value']
|
16
|
+
else
|
17
|
+
raise StandardError, "Unrecognized Design Rule element '#{element.name}'"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(name)
|
24
|
+
@name = name
|
25
|
+
@parameters = {}
|
26
|
+
end
|
27
|
+
|
28
|
+
# @return [REXML::Element]
|
29
|
+
def to_xml
|
30
|
+
REXML::Element.new('designrules').tap do |element|
|
31
|
+
element.add_attribute 'name', name
|
32
|
+
element.add_element('description').text = description if description
|
33
|
+
|
34
|
+
parameters.each {|key, value| element.add_element('param', {'name' => key, 'value' => value})}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'rexml/document'
|
2
|
+
|
3
|
+
require_relative 'geometry'
|
4
|
+
|
5
|
+
module EagleCAD
|
6
|
+
class DeviceSet
|
7
|
+
attr_accessor :description, :name, :prefix, :uservalue
|
8
|
+
attr_reader :devices, :gates
|
9
|
+
|
10
|
+
Connect = Struct.new :gate, :pin, :pad, :route do
|
11
|
+
def self.from_xml(element)
|
12
|
+
Connect.new element.attributes['gate'], element.attributes['pin'], element.attributes['pad'], element.attributes['route']
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [REXML::Element]
|
16
|
+
def to_xml
|
17
|
+
REXML::Element.new('connect').tap {|element| element.add_attributes({'gate' => gate, 'pad' => pad, 'pin' => pin}) }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class Device
|
22
|
+
attr_accessor :name, :package
|
23
|
+
attr_reader :connects, :technologies
|
24
|
+
|
25
|
+
def self.from_xml(element)
|
26
|
+
Device.new(element.attributes['name']).tap do |device|
|
27
|
+
device.package = element.attributes['package']
|
28
|
+
|
29
|
+
element.elements.each do |element|
|
30
|
+
case element.name
|
31
|
+
when 'connects'
|
32
|
+
element.elements.each {|connect| device.connects.push Connect.from_xml(connect) }
|
33
|
+
when 'technologies'
|
34
|
+
element.elements.each {|technology| device.technologies.push technology.attributes['name'] }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize(name)
|
41
|
+
@connects = []
|
42
|
+
@name = name
|
43
|
+
@technologies = []
|
44
|
+
end
|
45
|
+
|
46
|
+
# Generate XML for the {DeviceSet} element
|
47
|
+
# @return [REXML::Element]
|
48
|
+
def to_xml
|
49
|
+
REXML::Element.new('device').tap do |element|
|
50
|
+
element.add_attributes({'name' => name, 'package' => package})
|
51
|
+
|
52
|
+
connects_element = element.add_element('connects')
|
53
|
+
connects.each {|connect| connects_element.add_element connect.to_xml }
|
54
|
+
|
55
|
+
technologies_element = element.add_element('technologies')
|
56
|
+
technologies.each {|technology| technologies_element.add_element('technology', {'name' => technology}) }
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
Gate = Struct.new :name, :symbol, :origin, :addlevel, :swaplevel do
|
62
|
+
def self.from_xml(element)
|
63
|
+
Gate.new element.attributes['name'], element.attributes['symbol'], Geometry.point_from(element, 'x', 'y'), element.attributes['addlevel'], element.attributes['swaplevel']
|
64
|
+
end
|
65
|
+
|
66
|
+
# @return [REXML::Element]
|
67
|
+
def to_xml
|
68
|
+
REXML::Element.new('gate').tap {|element| element.add_attributes({'name' => name, 'symbol' => symbol, 'x' => origin.x, 'y' => origin.y, 'addlevel' => addlevel, 'swaplevel' => swaplevel}) }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Create a new {DeviceSet} from an {REXML::Element}
|
73
|
+
# @param [Element] element The {REXML::Element} to parse
|
74
|
+
def self.from_xml(element)
|
75
|
+
DeviceSet.new(element.attributes['name']).tap do |deviceset|
|
76
|
+
deviceset.prefix = element.attributes['prefix']
|
77
|
+
deviceset.uservalue = ('yes' == element.attributes['uservalue'])
|
78
|
+
|
79
|
+
element.elements.each do |element|
|
80
|
+
case element.name
|
81
|
+
when 'devices'
|
82
|
+
element.elements.each {|device| deviceset.devices.push Device.from_xml(device) }
|
83
|
+
when 'description'
|
84
|
+
deviceset.description = element.text
|
85
|
+
when 'gates'
|
86
|
+
element.elements.each {|gate| deviceset.gates.push Gate.from_xml(gate) }
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def initialize(name)
|
93
|
+
@devices = []
|
94
|
+
@gates = []
|
95
|
+
@name = name
|
96
|
+
end
|
97
|
+
|
98
|
+
# Generate XML for the {DeviceSet} element
|
99
|
+
# @return [REXML::Element]
|
100
|
+
def to_xml
|
101
|
+
REXML::Element.new('deviceset').tap do |element|
|
102
|
+
element.add_attributes({'name' => name, 'prefix' => prefix, 'uservalue' => (uservalue ? 'yes' : 'no')})
|
103
|
+
element.add_element('description').text = description
|
104
|
+
|
105
|
+
gates_element = element.add_element('gates')
|
106
|
+
gates.each {|gate| gates_element.add_element gate.to_xml }
|
107
|
+
|
108
|
+
devices_element = element.add_element('devices')
|
109
|
+
devices.each {|device| devices_element.add_element device.to_xml }
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|