bcl 0.2.3 → 0.2.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,94 @@
1
+ ######################################################################
2
+ # Copyright (c) 2008-2013, 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
+ require 'rubygems'
21
+ require 'pathname'
22
+ require 'fileutils'
23
+ require 'enumerator'
24
+
25
+ require 'bcl/TarBall'
26
+
27
+ module BCL
28
+ module_function
29
+
30
+ def gather_components(component_dir)
31
+ #store the starting directory
32
+ current_dir = Dir.pwd
33
+
34
+ #an array to hold reporting info about the batches
35
+ gather_components_report = []
36
+
37
+ #go to the directory containing the components
38
+ Dir.chdir(component_dir)
39
+
40
+ #delete old gather files first
41
+ gather_dest_base = "components.tar.gz"
42
+ #File.delete("./gather/" + gather_dest_base) if File.exists?("./0_Package/" + gather_dest_base)
43
+
44
+ #copy all the components' tar.gz files into a single directory
45
+ targzs = Pathname.glob("./**/*.tar.gz")
46
+ targzs.each do |targz|
47
+ destination = "./0_Package/#{File.basename(targz.to_s)}"
48
+ #puts "copying #{targz.to_s} to #{destination}"
49
+ Dir.mkdir("./0_Package") unless File.directory?("./0_Package") #named so it will be at top of directory list
50
+ File.delete(destination) if File.exists?(destination)
51
+ FileUtils.cp(targz.to_s, destination)
52
+ end
53
+
54
+ #go into that directory
55
+ Dir.chdir("./0_Package")
56
+
57
+ #get a list of all the tar.gz files in the new directory
58
+ targzs = Pathname.glob("*.tar.gz")
59
+
60
+ #report the total number of components in the directory
61
+ gather_components_report << "Total components = #{targzs.length}"
62
+
63
+ #define an iterator to keep track of the number of batches
64
+ batch_num = 1
65
+
66
+ #package all the tar.gzs in the directory into a few master tar.gz files of 1000 components or less
67
+ targzs.each_slice(100) do |batch|
68
+
69
+ gather_components_report << " batch #{batch_num} contains #{batch.length} components"
70
+
71
+ #put all the paths in the batch into an array
72
+ paths = []
73
+ batch.each do |targz|
74
+ paths << File.basename(targz.to_s)
75
+ end
76
+
77
+ #path where the batch tarball is going
78
+ gather_dest = "0_Package_#{batch_num}_#{gather_dest_base}" #prefix to move to top of directory
79
+
80
+ #tar up the batch
81
+ tarball(gather_dest, paths)
82
+
83
+ batch_num += 1
84
+ end
85
+
86
+ #report out
87
+ puts gather_components_report
88
+
89
+ #change back to the directory where we started
90
+ Dir.chdir(current_dir)
91
+
92
+ end
93
+
94
+ end # module BCL
@@ -0,0 +1,461 @@
1
+ ######################################################################
2
+ # Copyright (c) 2008-2013, 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(/\W/,'_').gsub(/___/,'_').gsub(/__/,'_').chomp('_').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, datatype = nil)
173
+ attr = AttrStruct.new
174
+ attr.name = name
175
+ attr.value = value
176
+
177
+ if !datatype.nil?
178
+ attribute.datatype = datatype
179
+ else
180
+ isint = true if Integer(value) rescue false
181
+ isfloat = true if Float(value) rescue false
182
+
183
+ if value.is_a? Fixnum
184
+ attr.datatype = "int"
185
+ elsif value.is_a? Float
186
+ attr.datatype = "float"
187
+ elsif isfloat
188
+ attr.datatype = "float"
189
+ elsif isint
190
+ attr.datatype = "int"
191
+ else
192
+ attr.datatype = "string"
193
+ end
194
+ end
195
+ attr.units = units
196
+
197
+ @attributes << attr
198
+ end
199
+
200
+ def add_file(version_sp, version_id, fqp_file, filename, filetype)
201
+ fs = FileStruct.new
202
+ fs.version_software_program = version_sp
203
+ fs.version_id = version_id
204
+ fs.fqp_file = fqp_file
205
+ fs.filename = filename
206
+ fs.filetype = filetype
207
+
208
+ @files << fs
209
+ end
210
+
211
+
212
+ def add_cost(cost_name, cost_type, category, value, units, interval, interval_units, year, location, currency,
213
+ source, reference_component_name, reference_component_id)
214
+ cs = CostStruct.new
215
+ cs.cost_name = cost_name
216
+ cs.cost_type = cost_type
217
+ cs.category = category
218
+ cs.value = value
219
+ cs.interval = interval
220
+ cs.interval_units = interval_units
221
+ cs.year = year
222
+ cs.location = location
223
+ cs.units = units
224
+ cs.currency = currency
225
+ cs.source = source
226
+ cs.reference_component_name = reference_component_name
227
+ cs.reference_component_id = reference_component_id
228
+
229
+ @costs << cs
230
+ end
231
+
232
+ def add_object(object_type, object_instance)
233
+ ob = ObjectStruct.new
234
+ ob.obj_type = object_type
235
+ ob.obj_instance = object_instance
236
+
237
+ @objects << ob
238
+ end
239
+
240
+ def resolve_path
241
+ FileUtils.mkdir_p(@path) unless File.directory?(@path)
242
+ new_path = "#{@path}/#{name.gsub(/\W/,'_').gsub(/___/,'_').gsub(/__/,'_').chomp('_').strip}"
243
+ FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
244
+ result = new_path
245
+ end
246
+
247
+ def osm_resolve_path
248
+ FileUtils.mkdir_p(@path) unless File.directory?(@path)
249
+ new_path = "#{@path}/osm_#{name.gsub(/\W/,'_').gsub(/___/,'_').gsub(/__/,'_').chomp('_').strip}"
250
+ FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
251
+ result = new_path
252
+ end
253
+
254
+ def osc_resolve_path
255
+ FileUtils.mkdir_p(@path) unless File.directory?(@path)
256
+ new_path = "#{@path}/osc_#{name.gsub(/\W/,'_').gsub(/___/,'_').gsub(/__/,'_').chomp('_').strip}"
257
+ FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
258
+ result = new_path
259
+ end
260
+
261
+ def resolve_component_path(component_type)
262
+ FileUtils.mkdir_p(@path) unless File.directory?(@path)
263
+ new_path = @path + '/OpenStudio'
264
+ FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
265
+ new_path = new_path + "/#{component_type}"
266
+ FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
267
+ return new_path
268
+ end
269
+
270
+ def tmp_resolve_path
271
+ FileUtils.mkdir_p(@path) unless File.directory?(@path)
272
+ new_path = "#{@path}/tmp_#{name.gsub(/\W/,'_').gsub(/___/,'_').gsub(/__/,'_').chomp('_').strip}"
273
+ FileUtils.mkdir_p(new_path) unless File.directory?(new_path)
274
+ result = new_path
275
+ end
276
+
277
+
278
+ def create_os_component(osobj)
279
+ osobj.getCostLineItems.each do |os|
280
+ @costs.each do |cost|
281
+ #figure out costs for constructions
282
+ os.setMaterialCost(cost.value.to_f) if cost.category == "material"
283
+ if cost.category == "installation"
284
+ os.setInstallationCost(cost.value.to_f)
285
+ os.setExpectedLife(cost.interval.to_i)
286
+ end
287
+ os.setFixedOM(cost.value.to_f) if cost.category == "operations and maintenance"
288
+ os.setVariableOM(cost.value.to_f) if cost.category == "variable operations and maintenance"
289
+ os.setSalvageCost(cost.value.to_f) if cost.category == "salvage"
290
+ end
291
+ end
292
+ newcomp = osobj.createComponent
293
+
294
+ cd = newcomp.componentData
295
+ cd.setDescription(@description)
296
+ cd.setFidelityLevel(@fidelity_level)
297
+
298
+ at = newcomp.componentData.componentDataAttributes
299
+ @attributes.each do |attrib|
300
+ if (attrib.value.to_s != "") and (attrib.name.to_s != "")
301
+ if attrib.units != ""
302
+ at.addAttribute(tc(attrib.name), attrib.value, attrib.units)
303
+ else
304
+ at.addAttribute(tc(attrib.name), attrib.value)
305
+ end
306
+ end
307
+ end
308
+
309
+ tg = newcomp.componentData.componentDataTags
310
+ comp_tag = ""
311
+ @tags.each do |tag|
312
+ tg.addTag(tc(tag.descriptor))
313
+ if (tag.descriptor != "energyplus") and (tag.descriptor != "construction")
314
+ #create a map of component tags to directories
315
+ comp_tag = tag.descriptor
316
+ if comp_tag == "interior wall"
317
+ comp_tag = "interiorwalls"
318
+ elsif comp_tag == "exterior wall"
319
+ comp_tag = "exteriorwalls"
320
+ elsif comp_tag == "exterior slab"
321
+ comp_tag = "exteriorslabs"
322
+ elsif comp_tag == "exposed floor"
323
+ comp_tag = "exposedfloors"
324
+ elsif comp_tag == "attic floor"
325
+ comp_tag = "atticfloors"
326
+ elsif comp_tag == "roof"
327
+ comp_tag = "roofs"
328
+ elsif comp_tag == "door"
329
+ comp_tag = "doors"
330
+ elsif comp_tag == "skylight"
331
+ comp_tag = "skylights"
332
+ elsif comp_tag == "window"
333
+ comp_tag = "windows"
334
+ end
335
+ puts comp_tag
336
+ end
337
+ end
338
+
339
+ return newcomp
340
+ end
341
+
342
+ def save_component_xml(dir_path = resolve_path)
343
+ xmlfile = File.new(dir_path + '/component.xml', 'w')
344
+ comp_xml = Builder::XmlMarkup.new(:target => xmlfile, :indent=>2)
345
+
346
+ #setup the xml file
347
+ comp_xml.instruct! :xml, :version=>"1.0", :encoding=>"UTF-8"
348
+ comp_xml.component("xmlns:xsi"=>"http://www.w3.org/2001/XMLSchema-instance",
349
+ "xsi:noNamespaceSchemaLocation"=>"#{SCHEMA_LOCATION}") {
350
+ comp_xml.name @name
351
+ comp_xml.uid @uid
352
+ comp_xml.version_id @comp_version_id
353
+ comp_xml.description @description if @description != ""
354
+ comp_xml.comment @comment if @comment != ""
355
+ comp_xml.fidelity_level @fidelity_level
356
+
357
+ comp_xml.provenances {
358
+ @provenance.each do |prov|
359
+ comp_xml.provenance {
360
+ comp_xml.author prov.author
361
+ comp_xml.datetime prov.datetime
362
+ comp_xml.comment prov.comment
363
+ }
364
+ end
365
+ }
366
+
367
+ comp_xml.tags {
368
+ @tags.each do |tag|
369
+ comp_xml.tag tag.descriptor
370
+ end
371
+ }
372
+
373
+ comp_xml.attributes {
374
+ @attributes.each do |attrib|
375
+ if (attrib.value.to_s != "") and (attrib.name.to_s != "") then
376
+ comp_xml.attribute {
377
+ comp_xml.name attrib.name
378
+ comp_xml.value attrib.value
379
+ comp_xml.datatype attrib.datatype
380
+ comp_xml.units attrib.units if attrib.units != ""
381
+ }
382
+ end
383
+ end
384
+ }
385
+
386
+ comp_xml.source {
387
+ comp_xml.manufacturer @source_manufacturer if @source_manufacturer != ""
388
+ comp_xml.model @source_model if @source_model != ""
389
+ comp_xml.serial_no @source_serial_no if @source_serial_no != ""
390
+ comp_xml.year @source_year if @source_year != ""
391
+ comp_xml.url @source_url if @source_url != ""
392
+ }
393
+
394
+ if not @files.nil?
395
+ comp_xml.files {
396
+ @files.each do |file|
397
+ comp_xml.file {
398
+ comp_xml.version {
399
+ comp_xml.software_program file.version_software_program
400
+ comp_xml.identifier file.version_id
401
+ }
402
+
403
+ comp_xml.filename file.filename
404
+ comp_xml.filetype file.filetype
405
+ }
406
+ end
407
+ }
408
+ end
409
+
410
+ #check if we should write out costs, don't write if all values are 0 or nil
411
+ #DLM: schema always expects costs
412
+ write_costs = true
413
+ #if not @costs.nil?
414
+ # @costs.each do |cost|
415
+ # if (cost.value.nil?) && (not cost.value == 0)
416
+ # write_costs = true
417
+ # break
418
+ # end
419
+ # end
420
+ #end
421
+
422
+ if write_costs
423
+ comp_xml.costs {
424
+ @costs.each do |cost|
425
+ comp_xml.cost {
426
+ comp_xml.instance_name cost.cost_name
427
+ comp_xml.cost_type cost.cost_type
428
+ comp_xml.category cost.category
429
+ comp_xml.value cost.value
430
+ comp_xml.units cost.units if cost.units != ""
431
+ comp_xml.interval cost.interval if cost.interval != ""
432
+ comp_xml.interval_units cost.interval_units if cost.interval_units != ""
433
+ comp_xml.year cost.year if cost.year != ""
434
+ comp_xml.currency cost.currency if cost.currency != ""
435
+ comp_xml.source cost.source if cost.source != ""
436
+ comp_xml.reference_component_name cost.reference_component_name if cost.reference_component_name != ""
437
+ comp_xml.reference_component_id cost.reference_component_id if cost.reference_component_id != ""
438
+ }
439
+ end
440
+ }
441
+ end
442
+
443
+ }
444
+
445
+ xmlfile.close
446
+ end
447
+
448
+ private
449
+
450
+ #return the title case of the string
451
+ def tc(input)
452
+ val = input.gsub(/\b\w/){$&.upcase}
453
+ if val.downcase == "energyplus"
454
+ val = "EnergyPlus"
455
+ end
456
+ return val
457
+ end
458
+
459
+ end
460
+
461
+ end # module BCL