oml4r 2.8.pre0 → 2.8.pre1

Sign up to get free protection for your applications and to get access to all the features.
data/.yardopts ADDED
@@ -0,0 +1,2 @@
1
+ --title OML4R
2
+ lib/*.rb
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in oml4r.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright 2009-2012 National ICT Australia (NICTA), Australia
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # OML4R
2
+
3
+ This is a simple client library for OML which does not use liboml2 and its
4
+ filters, but connects directly to the server using the text protocol.
5
+ User can use this library to create ruby applications which can send
6
+ measurement to the OML collection server. A simple example on how to use
7
+ this library is attached at the end of this file. Another example can be
8
+ found in the file oml4r-example.rb
9
+
10
+ ## Installation
11
+
12
+ $ gem install oml4r
13
+
14
+ ## Usage
15
+
16
+ ### Definition of a Measurement Point
17
+
18
+ class MyMP < OML4R::MPBase
19
+ name :mymp
20
+
21
+ param :mystring
22
+ param :myint, :type => :int32
23
+ param :mydouble, :type => :double
24
+ end
25
+
26
+ ### Initialisation, Injection and Tear-down
27
+
28
+ OML4R::init(ARGV, {
29
+ :appName => 'oml4rSimpleExample',
30
+ :expID => 'foo',
31
+ :nodeId => 'n1',
32
+ :omlServer => 'file:-'}
33
+ )
34
+ MyMP.inject("hello", 13, 37.)
35
+ OML4R::close()
36
+
37
+ ### Real example
38
+
39
+ See examples files oml4r-simple-example.rb and oml4r-wlanconfig.rb.
40
+
41
+ ## License
42
+
43
+ Copyright 2009-2012 National ICT Australia (NICTA), Australia
44
+
45
+ Permission is hereby granted, free of charge, to any person obtaining a copy
46
+ of this software and associated documentation files (the "Software"), to deal
47
+ in the Software without restriction, including without limitation the rights
48
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
49
+ copies of the Software, and to permit persons to whom the Software is
50
+ furnished to do so, subject to the following conditions:
51
+
52
+ The above copyright notice and this permission notice shall be included in
53
+ all copies or substantial portions of the Software.
54
+
55
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
56
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
57
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
58
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
59
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
60
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
61
+ THE SOFTWARE.
62
+
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,69 @@
1
+ #
2
+ # Copyright (c) 2012 National ICT Australia (NICTA), Australia
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+ #
23
+ # = oml4r-simple-example.rb
24
+ #
25
+ # == Description
26
+ #
27
+ # A very simple straightforward example of OML4R.
28
+ #
29
+ require 'oml4r'
30
+
31
+ # Define your own Measurement Points
32
+ class SinMP < OML4R::MPBase
33
+ name :sin
34
+ #channel :default
35
+
36
+ param :label
37
+ param :angle, :type => :int32
38
+ param :value, :type => :double
39
+ end
40
+
41
+ class CosMP < OML4R::MPBase
42
+ name :cos
43
+ # channel :ch1
44
+ # channel :default
45
+
46
+ param :label
47
+ param :value, :type => :double
48
+ end
49
+
50
+ # Initialise the OML4R module for your application
51
+ opts = {:appName => 'oml4rSimpleExample',
52
+ :expID => 'foo', :nodeId => 'n1',
53
+ :omlServer => 'file:-'} # Server could also be tcp:host:port
54
+ # OML4R::create_channel(:default, 'file:-')
55
+
56
+ OML4R::init(ARGV, opts)
57
+
58
+ # Now collect and inject some measurements
59
+ 5.times do |i|
60
+ sleep 0.5
61
+ angle = 15 * i
62
+ SinMP.inject("label_#{angle}", angle, Math.sin(angle))
63
+ CosMP.inject("label_#{angle}", Math.cos(angle))
64
+ end
65
+
66
+ # Don't forget to close when you are finished
67
+ OML4R::close()
68
+
69
+ # vim: sw=2
@@ -0,0 +1,149 @@
1
+ #
2
+ # Copyright (c) 2010-2012 National ICT Australia (NICTA), Australia
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+ #
23
+ # = oml4r-wlanconfig.rb
24
+ #
25
+ # == Description
26
+ #
27
+ # This is a simple example on how to use the OML4R module to create a simple
28
+ # ruby application that "wraps" around the existing command "wlanconfig".
29
+ # This wrapper invokes "wlanconfig" at regular interval, captures and formats
30
+ # its output (= measurements), and finally pass them to OML4R, which will in
31
+ # turn either store them in a local file or forward them to the OML Server.
32
+ #
33
+ require "oml4r"
34
+
35
+ APPNAME = "wlanconfig"
36
+ APPPATH = "/sbin/wlanconfig"
37
+
38
+
39
+ #
40
+ # This class defines the Measurement Point for our application and the
41
+ # corresponding metrics we would like to capture
42
+ #
43
+ class WlanConfigMP < OML4R::MPBase
44
+ name :wlanstat
45
+ param :addr
46
+ param :aid, :type => :int32
47
+ param :channel, :type => :int32
48
+ param :rate
49
+ param :rssi, :type => :int32
50
+ param :dbm, :type => :int32
51
+ param :idle, :type => :int32
52
+ param :txseq, :type => :int32
53
+ param :rxseq, :type => :int32
54
+ # Note: other metrics potentially returned by wlanconfig are
55
+ # not supported here, as they are seldom set by wlanconfig.
56
+ # These are: caps, acaps, erp, mode
57
+ end
58
+
59
+ #
60
+ # This class is the Wrapper around the existing "wlanconfig" application
61
+ #
62
+ class Wrapper
63
+
64
+ #
65
+ # Initialise a new Wrapper object
66
+ # - args = the command line argument which was given to this wrapper
67
+ # application
68
+ #
69
+ def initialize(args)
70
+
71
+ # Initialise some variable specific to this wrapper
72
+ @interface = nil
73
+ @interval = 1
74
+
75
+ # Now call the Init of OML4R with the command line arguments (args)
76
+ # and a block defining the arguments specific to this wrapper
77
+ OML4R::init(args, :appName => "#{APPNAME}_wrapper") { |argParser|
78
+ argParser.banner = "\nExecute a wrapper around #{APPNAME}\n" +
79
+ "Use -h or --help for a list of options\n\n"
80
+ argParser.on("-i", "--interface IFNAME", "Name of Interface to monitor") { |name| @interface = name }
81
+ argParser.on("-s", "--sampling DURATION", "Interval in second between sample collection for OML") { |time| @interval = time }
82
+ }
83
+
84
+ # Finally do some checking specific to this wrapper
85
+ # e.g. here we do not proceed if the user did not give us a
86
+ # valid interface to monitor
87
+ unless @interface != nil
88
+ raise "You did not specify an interface to monitor! (-i option)"
89
+ end
90
+ end
91
+
92
+ #
93
+ # Start the wrapped "wlaconfig" application, capture and process its output
94
+ #
95
+ def start()
96
+ # Loop until the user interrupts us
97
+ while true
98
+ # Run the wlanconfig command
99
+ cmd = "#{APPPATH} #{@interface} list"
100
+ output = `#{cmd}`
101
+ # Process its output
102
+ processOutput(output)
103
+ # Wait for a given duration and loop again
104
+ sleep @interval.to_i
105
+ end
106
+ end
107
+
108
+ #
109
+ # Process each output coming from an executing of the "wlaconfig" application
110
+ # - output = a String holding the output to process
111
+ #
112
+ def processOutput(output)
113
+ # wlanconfig returns a sequence of lines
114
+ # The 1st line is a list of labels for the fields of the remaining lines
115
+ # Each remaining line is for a given station, and follow the format:
116
+ # ADDR AID CHAN RATE RSSI DBM IDLE TXSEQ RXSEQ CAPS ACAPS ERP STATE MODE
117
+ lines = output.split("\n")
118
+ labels = lines.delete_at(0)
119
+ lines.each { |row|
120
+ column = row.split(" ")
121
+ # Inject the measurements into OML
122
+ WlanConfigMP.inject("#{column[0]}", column[1], column[2],
123
+ "#{column[3]}", column[4], column[5],
124
+ column[6], column[7], column[8])
125
+ }
126
+ end
127
+
128
+
129
+ end
130
+
131
+ #
132
+ # Entry point to this Ruby application
133
+ #
134
+ begin
135
+ app = Wrapper.new(ARGV)
136
+ app.start()
137
+ rescue SystemExit
138
+ rescue SignalException
139
+ puts "Wrapper stopped."
140
+ rescue Exception => ex
141
+ puts "Error - When executing the wrapper application!"
142
+ puts "Error - Type: #{ex.class}"
143
+ puts "Error - Message: #{ex}\n\n"
144
+ # Uncomment the next line to get more info on errors
145
+ # puts "Trace - #{ex.backtrace.join("\n\t")}"
146
+ end
147
+ OML4R::close()
148
+
149
+ # vim: sw=2
data/lib/oml4r.rb ADDED
@@ -0,0 +1,445 @@
1
+ #
2
+ # Copyright 2009-2012 National ICT Australia (NICTA), Australia
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ # of this software and associated documentation files (the "Software"), to deal
6
+ # in the Software without restriction, including without limitation the rights
7
+ # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ # copies of the Software, and to permit persons to whom the Software is
9
+ # furnished to do so, subject to the following conditions:
10
+ #
11
+ # The above copyright notice and this permission notice shall be included in
12
+ # all copies or substantial portions of the Software.
13
+ #
14
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
20
+ # THE SOFTWARE.
21
+ #
22
+ #
23
+ # = oml4r.rb
24
+ #
25
+ # == Description
26
+ #
27
+ # This is a simple client library for OML which does not use liboml2 and its
28
+ # filters, but connects directly to the server using the +text+ protocol.
29
+ # User can use this library to create ruby applications which can send
30
+ # measurement to the OML collection server.
31
+ #
32
+ require 'socket'
33
+ require 'monitor'
34
+ require 'thread'
35
+
36
+ #
37
+ # This is the OML4R module, which should be required by ruby applications
38
+ # that want to collect measurements via OML
39
+ #
40
+ module OML4R
41
+
42
+ VERSION = "2.8.pre1"
43
+ REVISION = "$Revision: 1 $".split(":")[1].chomp("$").strip
44
+ VERSION_STRING = "OML4R Client Library - Version #{VERSION} (#{REVISION})"
45
+ DEF_SERVER_PORT = 3003
46
+
47
+ #
48
+ # Measurement Point Class
49
+ # Ruby applications using this module should sub-class this MPBase class
50
+ # to define their own Measurement Point (see the example at the end of
51
+ # this file)
52
+ #
53
+ class MPBase
54
+
55
+ # Some Class variables
56
+ @@defs = {}
57
+ @@channels = {}
58
+ @@frozen = false
59
+ @@useOML = false
60
+ @@start_time = nil
61
+
62
+ # Execute a block for each defined MP
63
+ def self.each_mp(&block)
64
+ @@defs.each(&block)
65
+ end
66
+
67
+ # Set the useOML flag. If set to false, make 'inject' a NOOP
68
+ def self.__useOML__()
69
+ @@useOML = true
70
+ end
71
+
72
+ # Returns the definition of this MP
73
+ def self.__def__()
74
+ unless (defs = @@defs[self])
75
+ defs = @@defs[self] = {}
76
+ defs[:p_def] = []
77
+ defs[:seq_no] = 0
78
+ end
79
+ defs
80
+ end
81
+
82
+ # Set a name for this MP
83
+ def self.name(name)
84
+ __def__()[:name] = name
85
+ end
86
+
87
+ # Set the channel these measurements should be sent out on.
88
+ # Multiple declarations are allowed, and ':default' identifies
89
+ # the channel defined by the command line arguments or environment variables.
90
+ #
91
+ def self.channel(channel, domain = :default)
92
+ (@@channels[self] ||= []) << [channel, domain]
93
+ end
94
+
95
+ # Set a metric for this MP
96
+ # - name = name of the metric to set
97
+ # - opts = a Hash with the options for this metric
98
+ # Only supported option is :type = { :string | :int32 | :double }
99
+ def self.param(name, opts = {})
100
+ o = opts.dup
101
+ o[:name] = name
102
+ o[:type] ||= :string
103
+ if o[:type] == :long
104
+ $stderr.puts "OML4R: WARN: :long is deprecated use, :int32 instead"
105
+ o[:type] = :int32
106
+ end
107
+ __def__()[:p_def] << o
108
+ nil
109
+ end
110
+
111
+ # Inject a measurement from this Measurement Point to the OML Server
112
+ # However, if useOML flag is false, then only prints the measurement on stdout
113
+ # - args = a list of arguments (comma separated) which correspond to the
114
+ # different values of the metrics for the measurement to inject
115
+ def self.inject(*args)
116
+ return unless @@useOML
117
+
118
+ # Check that the list of values passed as argument matches the
119
+ # definition of this Measurement Point
120
+ defs = __def__()
121
+ pdef = defs[:p_def]
122
+ if args.size != pdef.size
123
+ raise "OML4R: Size mismatch between the measurement (#{args.size}) and the MP definition (#{pdef.size})!"
124
+ end
125
+
126
+ # Now prepare the measurement...
127
+ t = Time.now - @@start_time
128
+ a = []
129
+ a << (defs[:seq_no] += 1)
130
+ args.each do |arg|
131
+ a << arg
132
+ end
133
+ # ...and inject it!
134
+ msg = a.join("\t")
135
+ @@channels[self].each do |ca|
136
+ channel = ca[0]
137
+ index = ca[1]
138
+ channel.send "#{t}\t#{index}\t#{msg}"
139
+ end
140
+ end
141
+
142
+ def self.start_time()
143
+ @@start_time
144
+ end
145
+
146
+ # Freeze the definition of further MPs
147
+ #
148
+ def self.__freeze__(appName, start_time)
149
+ return if @@frozen
150
+ @@frozen = true
151
+ # replace channel names with channel object
152
+ self.each_mp do |klass, defs|
153
+ cna = @@channels[klass] || []
154
+ #$stderr.puts "OML4R: '#{cna.inspect}', '#{klass}'"
155
+ ca = cna.collect do |cname, domain|
156
+ # return it in an array as we need to add the channel specific index
157
+ [Channel[cname.to_sym, domain.to_sym]]
158
+ end
159
+ #$stderr.puts "OML4R: Using channels '#{ca.inspect}"
160
+ @@channels[klass] = ca.empty? ? [[Channel[]]] : ca
161
+ end
162
+ @@start_time = start_time
163
+
164
+ end
165
+
166
+ # Build the table schema for this MP and send it to the OML collection server
167
+ # - name_prefix = the name for this MP to use as a prefix for its table
168
+ def self.__print_meta__(name_prefix = nil)
169
+ return unless @@frozen
170
+ defs = __def__()
171
+
172
+ # Do some sanity checks...
173
+ unless (mp_name = defs[:name])
174
+ raise "Missing 'name' declaration for '#{self}'"
175
+ end
176
+ unless (name_prefix.nil?)
177
+ mp_name = "#{name_prefix}_#{mp_name}"
178
+ end
179
+
180
+ @@channels[self].each do |ca|
181
+ #$stderr.puts "OML4R: Setting up channel '#{ca.inspect}"
182
+ index = ca[0].send_schema(mp_name, defs[:p_def])
183
+ ca << index
184
+ end
185
+ end
186
+ end # class MPBase
187
+
188
+
189
+
190
+ #
191
+ # The Init method of OML4R
192
+ # Ruby applications should call this method to initialise the OML4R module
193
+ # This method will parse the command line argument of the calling application
194
+ # to extract the OML specific parameters, it will also do the parsing for the
195
+ # remaining application-specific parameters.
196
+ # It will then connect to the OML server (if requested on the command line), and
197
+ # send the initial instruction to setup the database and the tables for each MPs.
198
+ #
199
+ # - argv = the Array of command line arguments from the calling Ruby application
200
+ # - & block = a block which defines the additional application-specific arguments
201
+ #
202
+ def self.init(argv, opts = {}, &block)
203
+
204
+ if d = (ENV['OML_EXP_ID'] || opts[:expID])
205
+ # XXX: It is still too early to complain about that. We need to be sure
206
+ # of the nomenclature before making user-visible changes.
207
+ #$stderr.puts "OML4R: WARN: ENV['OML_EXP_ID'] and opts[:expID] are deprecated, use #DOMAIN or opts[:domain] instead"
208
+ opts[:domain] ||= d
209
+ end
210
+ domain ||= ENV['OML_DOMAIN'] || opts[:domain]
211
+
212
+ # XXX: Same as above; here, though, :id might actually be the way to go; or
213
+ # perhaps instId?
214
+ #if opts[:id]
215
+ # raise 'OML4R: :id is not a valid option. Do you mean :nodeID?'
216
+ #end
217
+ nodeID = ENV['OML_NAME'] || opts[:nodeID] || opts[:id] || ENV['OML_ID']
218
+ #
219
+ # XXX: Same again; also, this is the responsibility of the developer, not the user
220
+ #if opts[:app]
221
+ # raise 'OML4R: :app is not a valid option. Do you mean :appName?'
222
+ #end
223
+ appName = opts[:appName] || opts[:app]
224
+
225
+ if ENV['OML_URL'] || opts[:omlURL] || opts[:url]
226
+ raise 'OML4R: neither OML_URL, :omlURL nor :url are valid. Do you mean OML_SERVER or :omlServer?'
227
+ end
228
+ omlCollectUri = ENV['OML_SERVER'] || opts[:omlServer]
229
+ noop = opts[:noop] || false
230
+
231
+
232
+ # Create a new Parser for the command line
233
+ require 'optparse'
234
+ op = OptionParser.new
235
+ # Include the definition of application's specific arguments
236
+ yield(op) if block
237
+ # Include the definition of OML specific arguments
238
+ op.on("--oml-file file", "Writes measurements to 'file'") { |name| omlCollectUri = "file:#{name}" }
239
+ op.on("--oml-id id", "Name to identify this app instance [#{nodeID || 'undefined'}]") { |name| nodeID = name }
240
+ op.on("--oml-exp-id expId", "Name to experiment DB [#{domain || 'undefined'}]") { |name| domain = name }
241
+ op.on("--oml-domain expId", "Name to experiment DB [#{domain || 'undefined'}] *EXPERIMENTAL*") { |name| domain = name }
242
+ op.on("--oml-server uri", "URI of server to send measurements to (tcp:host:port)") { |u| omlCollectUri = u }
243
+ op.on("--oml-noop", "Do not collect measurements") { noop = true }
244
+ op.on_tail("--oml-help", "Show this message") { $stderr.puts op; exit }
245
+ # XXX: This should be set by the application writer, not the command line
246
+ #op.on("--oml-appid APPID", "Application ID for OML [#{appName || 'undefined'}] *EXPERIMENTAL*") { |name| appID = name }
247
+
248
+ # Set a default collection URI if nothing has been specified
249
+ omlCollectUri ||= "file:#{appName}_#{nodeID}_#{domain}_#{Time.now.strftime("%Y-%m-%dt%H.%M.%S%z")}"
250
+
251
+ # Now parse the command line
252
+ #$stderr.puts "OML4R: ARGV:>>> #{argv.inspect}"
253
+ rest = op.parse(argv)
254
+ return if noop
255
+
256
+
257
+ Channel.create(:default, omlCollectUri) if omlCollectUri
258
+
259
+ unless domain && nodeID && appName
260
+ raise 'OML4R: Missing values for parameters :expID (--oml-exp-id), :nodeID (--oml-id), or :appName (in code)!'
261
+ end
262
+
263
+ # Handle the defined Measurement Points
264
+ startTime = Time.now
265
+ Channel.init_all(domain, nodeID, appName, startTime)
266
+ msg = "OML4R enabled."
267
+ Object.const_defined?(:MObject) ? MObject.debug(:oml4r, msg) : $stderr.puts("OML4R: #{msg}")
268
+
269
+ rest || []
270
+ end
271
+
272
+ #
273
+ # Create a new channel over which to send measurement channels.
274
+ #
275
+ def self.create_channel(name, url)
276
+ Channel.create(name.to_s, url)
277
+ end
278
+
279
+ #
280
+ # Close the OML collection. This will block until all outstanding data have been sent out.
281
+ #
282
+ def self.close()
283
+ Channel.close_all
284
+ end
285
+
286
+
287
+
288
+ #
289
+ # Measurement Point Class
290
+ # Ruby applications using this module should sub-class this MPBase class
291
+ # to define their own Measurement Point (see the example at the end of
292
+ # this file)
293
+ #
294
+ class Channel
295
+ @@channels = {}
296
+ @@default_domain = nil
297
+
298
+ def self.create(name, url, domain = :default)
299
+ key = "#{name}:#{domain}"
300
+ if channel = @@channels[key]
301
+ if url != channel.url
302
+ raise "OML4R: Channel '#{name}' already defined with different url"
303
+ end
304
+ return channel
305
+ end
306
+ return self._create(key, domain, url)
307
+ end
308
+
309
+ def self._create(key, domain, url)
310
+ #oml_opts = {:exp_id => 'image_load', :node_id => 'n1', :app_name => 'img_load'}
311
+ if url.start_with? 'file:'
312
+ proto, fname = url.split(':')
313
+ out = (fname == '-' ? $stdout : File.open(fname, "w+"))
314
+ elsif url.start_with? 'tcp:'
315
+ #tcp:norbit.npc.nicta.com.au:3003
316
+ proto, host, port = url.split(':')
317
+ port ||= DEF_SERVER_PORT
318
+ out = TCPSocket.new(host, port)
319
+ else
320
+ raise "OML4R: Unknown transport in server url '#{url}'"
321
+ end
322
+ #$stderr.puts "Created channel for '#{key}'"
323
+ @@channels[key] = self.new(url, domain, out)
324
+ end
325
+
326
+ def self.[](name = :default, domain = :default)
327
+ key = "#{name}:#{domain}"
328
+ unless (@@channels.key?(key))
329
+ # If domain != :default and we have one for :default, create a new one
330
+ if (domain != :default)
331
+ if (dc = @@channels["#{name}:default"])
332
+ return self._create(key, domain, dc.url)
333
+ end
334
+ end
335
+ raise "OML4R: Unknown channel '#{name}'"
336
+ end
337
+ @@channels[key]
338
+ end
339
+
340
+ def self.init_all(domain, nodeID, appName, startTime)
341
+ @@default_domain = domain
342
+
343
+ MPBase.__freeze__(appName, startTime)
344
+
345
+ # send channel header
346
+ @@channels.values.each { |c| c.init(nodeID, appName, startTime) }
347
+
348
+ # send schema definitions
349
+ MPBase.each_mp do |klass, defs|
350
+ klass.__print_meta__(appName)
351
+ end
352
+
353
+ # add empty line to separate header form MP channel
354
+ @@channels.values.each { |c| c.send "\n" }
355
+
356
+ MPBase.__useOML__()
357
+ end
358
+
359
+ def self.close_all()
360
+ @@channels.values.each { |c| c.close }
361
+ end
362
+
363
+ attr_reader :url
364
+
365
+ def send_schema(mp_name, pdefs) # defs[:p_def]
366
+ # Build the schema and send it
367
+ @index += 1
368
+ line = ['schema:', @index, mp_name]
369
+ pdefs.each do |d|
370
+ line << "#{d[:name]}:#{d[:type]}"
371
+ end
372
+ msg = line.join(' ')
373
+ send msg
374
+ @index
375
+ end
376
+
377
+ def send(msg)
378
+ @queue.push msg
379
+ end
380
+
381
+ def init(nodeID, appName, startTime)
382
+ send_protocol_header(nodeID, appName, startTime)
383
+ end
384
+
385
+ def close()
386
+ @queue.push nil # indicate end of work
387
+ @runner.join()
388
+ end
389
+
390
+ protected
391
+ def initialize(url, domain, out_channel)
392
+ @domain = domain
393
+ @url = url
394
+ @out = out_channel
395
+ @index = 0
396
+ @queue = Queue.new
397
+ start_runner
398
+ end
399
+
400
+
401
+ def send_protocol_header(nodeID, appName, startTime)
402
+ @queue.push "protocol: 1"
403
+ d = (@domain == :default) ? @@default_domain : @domain
404
+ raise "Missing domain name" unless d
405
+ @queue.push "experiment-id: #{d}"
406
+ @queue.push "start_time: #{startTime.tv_sec}"
407
+ @queue.push "sender-id: #{nodeID}"
408
+ @queue.push "app-name: #{appName}"
409
+ @queue.push "content: text"
410
+ end
411
+
412
+ def start_runner
413
+ @runner = Thread.new do
414
+ active = true
415
+ begin
416
+ while (active)
417
+ msg = @queue.pop
418
+ active = !msg.nil?
419
+ if !@queue.empty?
420
+ ma = [msg]
421
+ while !@queue.empty?
422
+ msg = @queue.pop
423
+ if (active = !msg.nil?)
424
+ ma << msg
425
+ end
426
+ end
427
+ msg = ma.join("\n")
428
+ end
429
+ #$stderr.puts ">>>>>>#{@domain}: <#{msg}>"
430
+ @out.puts msg unless msg.nil?
431
+ @out.flush
432
+ end
433
+ @out.close
434
+ rescue Exception => ex
435
+ msg = "Exception while sending message to channel '#{@url}' (#{ex})"
436
+ Object.const_defined?(:MObject) ? MObject.warn(:oml4r, msg) : $stderr.puts("OML4R: #{msg}")
437
+ end
438
+ end
439
+ end
440
+
441
+ end # Channel
442
+
443
+ end # module OML4R
444
+
445
+ # vim: sw=2
data/oml4r.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/oml4r.rb', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["NICTA"]
6
+ gem.email = ["oml-user@lists.nicta.com.au"]
7
+ gem.description = ["Simple OML client library for Ruby"]
8
+ gem.summary = ["This is a simple client library for OML which does not use liboml2 and its filters, but connects directly to the server using the +text+ protocol. User can use this library to create ruby applications which can send measurement to the OML collection server."]
9
+ gem.homepage = "http://oml.mytestbed.net"
10
+
11
+ # ls-files won't work in VPATH builds;
12
+ # plus, we want lib/oml4r.rb rather than lib/oml4r.rb.in
13
+ #gem.files = `git ls-files`.split($\)
14
+ gem.files = [
15
+ ".yardopts",
16
+ "Gemfile",
17
+ "LICENSE",
18
+ "README.md",
19
+ "Rakefile",
20
+ "lib/oml4r.rb",
21
+ "lib/oml4r/oml4r-simple-example.rb",
22
+ "lib/oml4r/oml4r-wlanconfig.rb",
23
+ "oml4r.gemspec",
24
+ ]
25
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
26
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
27
+ gem.name = "oml4r"
28
+ gem.require_paths = ["lib"]
29
+ gem.version = OML4R::VERSION
30
+
31
+ end
32
+
33
+ # vim: sw=2
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: oml4r
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.8.pre0
4
+ version: 2.8.pre1
5
5
  prerelease: 4
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-15 00:00:00.000000000 Z
12
+ date: 2012-06-19 00:00:00.000000000 Z
13
13
  dependencies: []
14
14
  description: ! '["Simple OML client library for Ruby"]'
15
15
  email:
@@ -17,7 +17,16 @@ email:
17
17
  executables: []
18
18
  extensions: []
19
19
  extra_rdoc_files: []
20
- files: []
20
+ files:
21
+ - .yardopts
22
+ - Gemfile
23
+ - LICENSE
24
+ - README.md
25
+ - Rakefile
26
+ - lib/oml4r.rb
27
+ - lib/oml4r/oml4r-simple-example.rb
28
+ - lib/oml4r/oml4r-wlanconfig.rb
29
+ - oml4r.gemspec
21
30
  homepage: http://oml.mytestbed.net
22
31
  licenses: []
23
32
  post_install_message: