bcl 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+ require 'bcl/ComponentSpreadsheet'
2
+ require 'bcl/ComponentXml'
3
+ require 'bcl/GatherComponents'
4
+ require 'bcl/TarBall'
5
+ require 'bcl/MasterTaxonomy'
6
+ require 'bcl/MongoToComponent'
@@ -0,0 +1,292 @@
1
+ ######################################################################
2
+ # Copyright (c) 2008-2010, Alliance for Sustainable Energy.
3
+ # All rights reserved.
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ ######################################################################
19
+
20
+ # Converts a custom Excel spreadsheet format to BCL components for upload
21
+ # Format of the Excel spreadsheet is documented in /doc/ComponentSpreadsheet.docx
22
+
23
+ require 'rubygems'
24
+
25
+ require 'pathname'
26
+ require 'fileutils'
27
+ require 'uuid' # gem install uuid
28
+
29
+ require 'bcl/ComponentXml'
30
+ require 'bcl/GatherComponents'
31
+ require 'bcl/MasterTaxonomy'
32
+
33
+ $have_win32ole = false
34
+ begin
35
+ # apparently this is not a gem
36
+ require 'win32ole'
37
+ mod = WIN32OLE
38
+ $have_win32ole = true
39
+ rescue NameError
40
+ # do not have win32ole
41
+ end
42
+
43
+ module BCL
44
+
45
+ WorksheetStruct = Struct.new(:name, :components)
46
+ HeaderStruct = Struct.new(:name, :children)
47
+ ComponentStruct = Struct.new(:row, :name, :uid, :version_id, :headers, :values)
48
+
49
+ class ComponentSpreadsheet
50
+
51
+ public
52
+
53
+ # WINDOWS ONLY SECTION BECAUSE THIS USES WIN32OLE
54
+ if $have_win32ole
55
+
56
+ #initialize with Excel spreadsheet to read
57
+ def initialize(xlsx_path,worksheet_names = "all")
58
+
59
+ @xlsx_path = Pathname.new(xlsx_path).realpath.to_s
60
+ @worksheets = []
61
+
62
+ begin
63
+
64
+ excel = WIN32OLE::new('Excel.Application')
65
+
66
+ xlsx = excel.Workbooks.Open(@xlsx_path)
67
+
68
+ #by default, operate on all worksheets
69
+ if worksheet_names == "all"
70
+ xlsx.Worksheets.each do |xlsx_worksheet|
71
+ parse_xlsx_worksheet(xlsx_worksheet)
72
+ end
73
+ else #if specific worksheets are specified, operate on them
74
+ worksheet_names.each do |worksheet_name|
75
+ parse_xlsx_worksheet(xlsx.Worksheets(worksheet_name))
76
+ end
77
+ end
78
+
79
+ xlsx.saved = true
80
+ xlsx.Save
81
+
82
+ ensure
83
+
84
+ excel.Quit
85
+ WIN32OLE.ole_free(excel)
86
+ excel.ole_free
87
+ xlsx=nil
88
+ excel=nil
89
+ GC.start
90
+
91
+ end
92
+
93
+ end
94
+
95
+ else # if $have_win32ole
96
+
97
+ # parse the master taxonomy document
98
+ def initialize(xlsx_path)
99
+ puts "ComponentSpreadsheet class requires 'win32ole' to parse the component spreadsheet."
100
+ puts "ComponentSpreadsheet may also be stored and loaded from JSON if your platform does not support win32ole."
101
+ end
102
+
103
+ end # if $have_win32ole
104
+
105
+ def save(save_path)
106
+
107
+ # load master taxonomy to validate components
108
+ taxonomy = BCL::MasterTaxonomy.new
109
+
110
+ FileUtils.rm_rf(save_path) if File.exists?(save_path) and File.directory?(save_path)
111
+
112
+ @worksheets.each do |worksheet|
113
+ worksheet.components.each do |component|
114
+
115
+ component_xml = Component.new(save_path)
116
+ component_xml.name = component.name
117
+ component_xml.uid = component.uid
118
+ component_xml.comp_version_id = component.version_id
119
+
120
+ # this tag is how we know where this goes in the taxonomy
121
+ component_xml.add_tag(worksheet.name)
122
+
123
+ values = component.values[0]
124
+ component.headers.each do |header|
125
+
126
+ if /description/i.match(header.name)
127
+
128
+ name = values.delete_at(0)
129
+ uid = values.delete_at(0)
130
+ version_id = values.delete_at(0)
131
+ description = values.delete_at(0)
132
+ fidelity_level = values.delete_at(0).to_int
133
+ # name, uid, and version_id already processed
134
+ component_xml.description = description
135
+ component_xml.fidelity_level = fidelity_level
136
+
137
+ elsif /provenance/i.match(header.name)
138
+
139
+ author = values.delete_at(0)
140
+ datetime = values.delete_at(0)
141
+ comment = values.delete_at(0)
142
+ component_xml.add_provenance(author.to_s, datetime.to_s, comment.to_s)
143
+
144
+ elsif /tag/i.match(header.name)
145
+
146
+ value = values.delete_at(0)
147
+ component_xml.add_tag(value)
148
+
149
+ elsif /attribute/i.match(header.name)
150
+
151
+ value = values.delete_at(0)
152
+ name = header.children[0]
153
+ units = ""
154
+ if match_data = /(.*)\((.*)\)/.match(name)
155
+ name = match_data[1].strip
156
+ units = match_data[2].strip
157
+ end
158
+ component_xml.add_attribute(name, value, units)
159
+
160
+ elsif /source/i.match(header.name)
161
+
162
+ manufacturer = values.delete_at(0)
163
+ model = values.delete_at(0)
164
+ serial_no = values.delete_at(0)
165
+ year = values.delete_at(0)
166
+ url = values.delete_at(0)
167
+ component_xml.source_manufacturer = manufacturer
168
+ component_xml.source_model = model
169
+ component_xml.source_serial_no = serial_no
170
+ component_xml.source_year = year
171
+ component_xml.source_url = url
172
+
173
+ elsif /file/i.match(header.name)
174
+
175
+ software_program = values.delete_at(0)
176
+ version = values.delete_at(0)
177
+ filename = values.delete_at(0)
178
+ filetype = values.delete_at(0)
179
+ filepath = values.delete_at(0)
180
+ component_xml.add_file(software_program, version, filepath, filename, filetype)
181
+
182
+ else
183
+ raise "Unknown section #{header.name}"
184
+
185
+ end
186
+
187
+ end
188
+
189
+ taxonomy.check_component(component_xml)
190
+
191
+ component_xml.save_tar_gz(false)
192
+
193
+ end
194
+
195
+ end
196
+
197
+ BCL.gather_components(save_path)
198
+
199
+ end
200
+
201
+ private
202
+
203
+ def parse_xlsx_worksheet(xlsx_worksheet)
204
+
205
+ worksheet = WorksheetStruct.new
206
+ worksheet.name = xlsx_worksheet.Range("A1").Value
207
+ worksheet.components = []
208
+ puts "[ComponentSpreadsheet] Starting parsing components of type #{worksheet.name}"
209
+
210
+ # find number of rows, first column should be name, should not be empty
211
+ num_rows = 1
212
+ while true do
213
+ test = xlsx_worksheet.Range("A#{num_rows}").Value
214
+ if test.nil? or test.empty?
215
+ num_rows -= 1
216
+ break
217
+ end
218
+ num_rows += 1
219
+ end
220
+
221
+ # scan number of columns
222
+ headers = []
223
+ header = nil
224
+ max_col = nil
225
+ xlsx_worksheet.Columns.each do |col|
226
+ value1 = col.Rows("1").Value
227
+ value2 = col.Rows("2").Value
228
+
229
+ if not value1.nil? and not value1.empty?
230
+ if not header.nil?
231
+ headers << header
232
+ end
233
+ header = HeaderStruct.new
234
+ header.name = value1
235
+ header.children = []
236
+ end
237
+
238
+ if not value2.nil? and not value2.empty?
239
+ if not header.nil?
240
+ header.children << value2
241
+ end
242
+ end
243
+
244
+ if (value1.nil? or value1.empty?) and (value2.nil? or value2.empty?)
245
+ break
246
+ end
247
+
248
+ matchdata = /^\$(.+):/.match(col.Address)
249
+ max_col = matchdata[1]
250
+ end
251
+
252
+ if not header.nil?
253
+ headers << header
254
+ end
255
+
256
+ if not headers.empty?
257
+ headers[0].name = "description"
258
+ end
259
+
260
+ components = []
261
+ for i in 3..num_rows do
262
+ component = ComponentStruct.new
263
+ component.row = i
264
+
265
+ # get name
266
+ component.name = xlsx_worksheet.Range("A#{i}").value
267
+
268
+ # get uid, if empty set it
269
+ component.uid = xlsx_worksheet.Range("B#{i}").value
270
+ if component.uid.nil? or component.uid.empty?
271
+ component.uid = UUID.new.generate
272
+ xlsx_worksheet.Range("B#{i}").value = component.uid
273
+ end
274
+
275
+ # always write new version id
276
+ component.version_id = UUID.new.generate
277
+ xlsx_worksheet.Range("C#{i}").value = component.version_id
278
+
279
+ component.headers = headers
280
+ component.values = xlsx_worksheet.Range("A#{i}:#{max_col}#{i}").value
281
+ worksheet.components << component
282
+ end
283
+
284
+ @worksheets << worksheet
285
+
286
+ puts "[ComponentSpreadsheet] Finished parsing components of type #{worksheet.name}"
287
+
288
+ end
289
+
290
+ end
291
+
292
+ end # module BCL
@@ -0,0 +1,451 @@
1
+ ######################################################################
2
+ # Copyright (c) 2008-2010, Alliance for Sustainable Energy.
3
+ # All rights reserved.
4
+ #
5
+ # This library is free software; you can redistribute it and/or
6
+ # modify it under the terms of the GNU Lesser General Public
7
+ # License as published by the Free Software Foundation; either
8
+ # version 2.1 of the License, or (at your option) any later version.
9
+ #
10
+ # This library is distributed in the hope that it will be useful,
11
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
12
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13
+ # Lesser General Public License for more details.
14
+ #
15
+ # You should have received a copy of the GNU Lesser General Public
16
+ # License along with this library; if not, write to the Free Software
17
+ # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18
+ ######################################################################
19
+
20
+ # Provides programmatic access to the component.xsd schema needed for
21
+ # generating the component information that will be uploaded to
22
+ # the Building Component Library.
23
+
24
+ require 'rubygems'
25
+
26
+ require 'pathname'
27
+ require 'csv'
28
+ require 'builder' #gem install builder (creates xml files)
29
+ require 'uuid' # gem install uuid
30
+ require 'fileutils'
31
+
32
+ require 'bcl/TarBall'
33
+
34
+ module BCL
35
+
36
+ SCHEMA_LOCATION = "component.xsd"
37
+
38
+ ProvStruct = Struct.new(:author, :datetime, :comment)
39
+ TagsStruct = Struct.new(:descriptor)
40
+ AttrStruct = Struct.new(:name, :value, :datatype, :units)
41
+ FileStruct = Struct.new(:version_software_program, :version_id, :fqp_file, :filename, :filetype)
42
+ #cost_type is an enumeration (not enforced) of installation, material, operations and maintenance,
43
+ #variable operations and maintenance, salvage
44
+ CostStruct = Struct.new(:cost_name, :cost_type, :category, :value, :interval,
45
+ :interval_units, :year, :location, :units, :currency, :source,
46
+ :reference_component_name, :reference_component_id)
47
+ ObjectStruct = Struct.new(:obj_type, :obj_instance)
48
+
49
+ class Component
50
+ attr_accessor :name
51
+ attr_accessor :uid
52
+ attr_accessor :comp_version_id
53
+ attr_accessor :description
54
+ attr_accessor :comment
55
+ attr_accessor :fidelity_level
56
+ attr_accessor :source_manufacturer
57
+ attr_accessor :source_model
58
+ attr_accessor :source_serial_no
59
+ attr_accessor :source_year
60
+ attr_accessor :source_url
61
+ attr_accessor :tags
62
+ attr_accessor :provenance
63
+ attr_accessor :attributes
64
+ attr_accessor :files
65
+ attr_accessor :costs
66
+ attr_accessor :objects
67
+
68
+ public
69
+
70
+ #the save path is where the component will be saved
71
+ def initialize(save_path)
72
+ @name = "" #this is also a unique identifier to the component...
73
+ @uid = UUID.new.generate
74
+ @comp_version_id = UUID.new.generate
75
+ @description = ""
76
+ @comment = ""
77
+ @fidelity_level = 0 #restricted to level_1 to level_5
78
+ @source_manufacturer = ""
79
+ @source_model = ""
80
+ @source_serial_no = ""
81
+ @source_year = ""
82
+ @source_url = ""
83
+
84
+ #these items have multiple instances
85
+ @provenance = []
86
+ @tags = []
87
+ @attributes = []
88
+ @files = []
89
+ @costs = []
90
+ @objects = [] #container for saving the idf/osm snippets
91
+
92
+ @path = save_path
93
+
94
+ #puts "[ComponentXml] " + @path
95
+ #need to hit a webservice to validate which tags and attributes are
96
+ #available (including units?)
97
+
98
+ #todo: validate against master taxonomy
99
+ end
100
+
101
+ def open_component(filename)
102
+ read_component_xml(filename)
103
+ end
104
+
105
+ # savefile, save the component xml along with
106
+ # the files that have been added to the object
107
+ def save_tar_gz(delete_files = true)
108
+ current_d = Dir.pwd
109
+ paths = []
110
+
111
+ save_component_xml
112
+
113
+ paths << "./component.xml"
114
+
115
+ #copy over the files to the directory
116
+ @files.each do |file|
117
+ src_path = Pathname.new(file.fqp_file)
118
+ dest_path = Pathname.new("#{resolve_path}/#{file.filename}")
119
+ if File.exists?(src_path)
120
+ if src_path == dest_path
121
+ #do nothing, file is already where it needs to be
122
+ else
123
+ #move the file where it needs to go
124
+ FileUtils.cp(src_path, dest_path)
125
+ end
126
+ else
127
+ puts "#{src_path} -> File does not exist"
128
+ end
129
+ paths << "./#{file.filename}"
130
+ end
131
+
132
+ #take all the files and tar.gz them -- name the file the same as
133
+ #the directory
134
+
135
+ Dir.chdir("#{resolve_path}")
136
+ destination = "#{@name.gsub(" ","_").gsub("/","_").gsub("-","_").gsub("___","_").gsub("__","_").gsub("in.","in").gsub(",","").strip}.tar.gz"
137
+
138
+ File.delete(destination) if File.exists?(destination)
139
+
140
+ BCL.tarball(destination, paths)
141
+
142
+ Dir.chdir(current_d)
143
+
144
+ if (delete_files)
145
+ @files.each do |file|
146
+ if File.exists?(File.dirname(file.fqp_file))
147
+ puts "[ComponentXml] Deleting: #{File.dirname(file.fqp_file)}"
148
+ FileUtils.rm_rf(File.dirname(file.fqp_file))
149
+ end
150
+ end
151
+ end
152
+
153
+ #puts "[ComponentXml] " + Dir.pwd
154
+ end
155
+
156
+ def add_provenance(author, datetime, comment)
157
+ prov = ProvStruct.new
158
+ prov.author = author
159
+ prov.datetime = datetime
160
+ prov.comment = comment
161
+
162
+ @provenance << prov
163
+ end
164
+
165
+ def add_tag(tag_name)
166
+ tag = TagsStruct.new
167
+ tag.descriptor = tag_name
168
+
169
+ @tags << tag
170
+ end
171
+
172
+ def add_attribute(name, value, units)
173
+ attr = AttrStruct.new
174
+ attr.name = name
175
+ attr.value = value
176
+
177
+ if value.is_a? Fixnum
178
+ attr.datatype = "int"
179
+ elsif value.is_a? Float
180
+ attr.datatype = "float"
181
+ else
182
+ attr.datatype = "string"
183
+ end
184
+
185
+ attr.units = units
186
+
187
+ @attributes << attr
188
+ end
189
+
190
+ def add_file(version_sp, version_id, fqp_file, filename, filetype)
191
+ fs = FileStruct.new
192
+ fs.version_software_program = version_sp
193
+ fs.version_id = version_id
194
+ fs.fqp_file = fqp_file
195
+ fs.filename = filename
196
+ fs.filetype = filetype
197
+
198
+ @files << fs
199
+ end
200
+
201
+
202
+ def add_cost(cost_name, cost_type, category, value, units, interval, interval_units, year, location, currency,
203
+ source, reference_component_name, reference_component_id)
204
+ cs = CostStruct.new
205
+ cs.cost_name = cost_name
206
+ cs.cost_type = cost_type
207
+ cs.category = category
208
+ cs.value = value
209
+ cs.interval = interval
210
+ cs.interval_units = interval_units
211
+ cs.year = year
212
+ cs.location = location
213
+ cs.units = units
214
+ cs.currency = currency
215
+ cs.source = source
216
+ cs.reference_component_name = reference_component_name
217
+ cs.reference_component_id = reference_component_id
218
+
219
+ @costs << cs
220
+ end
221
+
222
+ def add_object(object_type, object_instance)
223
+ ob = ObjectStruct.new
224
+ ob.obj_type = object_type
225
+ ob.obj_instance = object_instance
226
+
227
+ @objects << ob
228
+ end
229
+
230
+ def resolve_path
231
+ FileUtils.mkdir_p(@path) unless File.directory?(@path)
232
+ new_path = "#{@path}/#{name.gsub(" ","_").gsub("/","_").gsub("-","_").gsub("___","_").gsub("__","_").gsub("in.","in").gsub(",","").strip}"
233
+ FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
234
+ result = new_path
235
+ end
236
+
237
+ def osm_resolve_path
238
+ FileUtils.mkdir_p(@path) unless File.directory?(@path)
239
+ new_path = "#{@path}/osm_#{name.gsub(" ","_").gsub("/","_").gsub("-","_").gsub("___","_").gsub("__","_").gsub("in.","in").gsub(",","").strip}"
240
+ FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
241
+ result = new_path
242
+ end
243
+
244
+ def osc_resolve_path
245
+ FileUtils.mkdir_p(@path) unless File.directory?(@path)
246
+ new_path = "#{@path}/osc_#{name.gsub(" ","_").gsub("/","_").gsub("-","_").gsub("___","_").gsub("__","_").gsub("in.","in").gsub(",","").strip}"
247
+ FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
248
+ result = new_path
249
+ end
250
+
251
+ def resolve_component_path(component_type)
252
+ FileUtils.mkdir_p(@path) unless File.directory?(@path)
253
+ new_path = @path + '/OpenStudio'
254
+ FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
255
+ new_path = new_path + "/#{component_type}"
256
+ FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
257
+ return new_path
258
+ end
259
+
260
+ def tmp_resolve_path
261
+ FileUtils.mkdir_p(@path) unless File.directory?(@path)
262
+ new_path = "#{@path}/tmp_#{name.gsub(" ","_").gsub("/","_").gsub("-","_").gsub("___","_").gsub("__","_").gsub("in.","in").gsub(",","").strip}"
263
+ FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
264
+ result = new_path
265
+ end
266
+
267
+
268
+ def create_os_component(osobj)
269
+ osobj.getCostLineItems.each do |os|
270
+ @costs.each do |cost|
271
+ #figure out costs for constructions
272
+ os.setMaterialCost(cost.value.to_f) if cost.category == "material"
273
+ if cost.category == "installation"
274
+ os.setInstallationCost(cost.value.to_f)
275
+ os.setExpectedLife(cost.interval.to_i)
276
+ end
277
+ os.setFixedOM(cost.value.to_f) if cost.category == "operations and maintenance"
278
+ os.setVariableOM(cost.value.to_f) if cost.category == "variable operations and maintenance"
279
+ os.setSalvageCost(cost.value.to_f) if cost.category == "salvage"
280
+ end
281
+ end
282
+ newcomp = osobj.createComponent
283
+
284
+ cd = newcomp.componentData
285
+ cd.setDescription(@description)
286
+ cd.setFidelityLevel(@fidelity_level)
287
+
288
+ at = newcomp.componentData.componentDataAttributes
289
+ @attributes.each do |attrib|
290
+ if (attrib.value.to_s != "") and (attrib.name.to_s != "")
291
+ if attrib.units != ""
292
+ at.addAttribute(tc(attrib.name), attrib.value, attrib.units)
293
+ else
294
+ at.addAttribute(tc(attrib.name), attrib.value)
295
+ end
296
+ end
297
+ end
298
+
299
+ tg = newcomp.componentData.componentDataTags
300
+ comp_tag = ""
301
+ @tags.each do |tag|
302
+ tg.addTag(tc(tag.descriptor))
303
+ if (tag.descriptor != "energyplus") and (tag.descriptor != "construction")
304
+ #create a map of component tags to directories
305
+ comp_tag = tag.descriptor
306
+ if comp_tag == "interior wall"
307
+ comp_tag = "interiorwalls"
308
+ elsif comp_tag == "exterior wall"
309
+ comp_tag = "exteriorwalls"
310
+ elsif comp_tag == "exterior slab"
311
+ comp_tag = "exteriorslabs"
312
+ elsif comp_tag == "exposed floor"
313
+ comp_tag = "exposedfloors"
314
+ elsif comp_tag == "attic floor"
315
+ comp_tag = "atticfloors"
316
+ elsif comp_tag == "roof"
317
+ comp_tag = "roofs"
318
+ elsif comp_tag == "door"
319
+ comp_tag = "doors"
320
+ elsif comp_tag == "skylight"
321
+ comp_tag = "skylights"
322
+ elsif comp_tag == "window"
323
+ comp_tag = "windows"
324
+ end
325
+ puts comp_tag
326
+ end
327
+ end
328
+
329
+ return newcomp
330
+ end
331
+
332
+ def save_component_xml(dir_path = resolve_path)
333
+ xmlfile = File.new(dir_path + '/component.xml', 'w')
334
+ comp_xml = Builder::XmlMarkup.new(:target => xmlfile, :indent=>2)
335
+
336
+ #setup the xml file
337
+ comp_xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
338
+ comp_xml.component("xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
339
+ "xsi:noNamespaceSchemaLocation"=>"#{SCHEMA_LOCATION}") {
340
+ comp_xml.name @name
341
+ comp_xml.uid @uid
342
+ comp_xml.version_id @comp_version_id
343
+ comp_xml.description @description if @description != ""
344
+ comp_xml.comment @comment if @comment != ""
345
+ comp_xml.fidelity_level @fidelity_level
346
+
347
+ comp_xml.provenances {
348
+ @provenance.each do |prov|
349
+ comp_xml.provenance {
350
+ comp_xml.author prov.author
351
+ comp_xml.datetime prov.datetime
352
+ comp_xml.comment prov.comment
353
+ }
354
+ end
355
+ }
356
+
357
+ comp_xml.tags {
358
+ @tags.each do |tag|
359
+ comp_xml.tag tag.descriptor
360
+ end
361
+ }
362
+
363
+ comp_xml.attributes {
364
+ @attributes.each do |attrib|
365
+ if (attrib.value.to_s != "") and (attrib.name.to_s != "") then
366
+ comp_xml.attribute {
367
+ comp_xml.name attrib.name
368
+ comp_xml.value attrib.value
369
+ comp_xml.datatype attrib.datatype
370
+ comp_xml.units attrib.units if attrib.units != ""
371
+ }
372
+ end
373
+ end
374
+ }
375
+
376
+ comp_xml.source {
377
+ comp_xml.manufacturer @source_manufacturer if @source_manufacturer != ""
378
+ comp_xml.model @source_model if @source_model != ""
379
+ comp_xml.serial_no @source_serial_no if @source_serial_no != ""
380
+ comp_xml.year @source_year if @source_year != ""
381
+ comp_xml.url @source_url if @source_url != ""
382
+ }
383
+
384
+ if not @files.nil?
385
+ comp_xml.files {
386
+ @files.each do |file|
387
+ comp_xml.file {
388
+ comp_xml.version {
389
+ comp_xml.software_program file.version_software_program
390
+ comp_xml.identifier file.version_id
391
+ }
392
+
393
+ comp_xml.filename file.filename
394
+ comp_xml.filetype file.filetype
395
+ }
396
+ end
397
+ }
398
+ end
399
+
400
+ #check if we should write out costs, don't write if all values are 0 or nil
401
+ #DLM: schema always expects costs
402
+ write_costs = true
403
+ #if not @costs.nil?
404
+ # @costs.each do |cost|
405
+ # if (cost.value.nil?) && (not cost.value == 0)
406
+ # write_costs = true
407
+ # break
408
+ # end
409
+ # end
410
+ #end
411
+
412
+ if write_costs
413
+ comp_xml.costs {
414
+ @costs.each do |cost|
415
+ comp_xml.cost {
416
+ comp_xml.instance_name cost.cost_name
417
+ comp_xml.cost_type cost.cost_type
418
+ comp_xml.category cost.category
419
+ comp_xml.value cost.value
420
+ comp_xml.units cost.units if cost.units != ""
421
+ comp_xml.interval cost.interval if cost.interval != ""
422
+ comp_xml.interval_units cost.interval_units if cost.interval_units != ""
423
+ comp_xml.year cost.year if cost.year != ""
424
+ comp_xml.currency cost.currency if cost.currency != ""
425
+ comp_xml.source cost.source if cost.source != ""
426
+ comp_xml.reference_component_name cost.reference_component_name if cost.reference_component_name != ""
427
+ comp_xml.reference_component_id cost.reference_component_id if cost.reference_component_id != ""
428
+ }
429
+ end
430
+ }
431
+ end
432
+
433
+ }
434
+
435
+ xmlfile.close
436
+ end
437
+
438
+ private
439
+
440
+ #return the title case of the string
441
+ def tc(input)
442
+ val = input.gsub(/\b\w/){$&.upcase}
443
+ if val.downcase == "energyplus"
444
+ val = "EnergyPlus"
445
+ end
446
+ return val
447
+ end
448
+
449
+ end
450
+
451
+ end # module BCL