eaglecad 0 → 1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|
+
[](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
|