openplacos 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,550 @@
1
+ ENV["DBUS_THREADED_ACCESS"] = "1" #activate threaded dbus
2
+
3
+ require "rubygems"
4
+ require 'dbus-openplacos'
5
+ require 'yaml'
6
+ require "micro-optparse"
7
+
8
+ module LibComponent
9
+
10
+ ACK = 0
11
+ Error = 1
12
+ # Common module to Input and Output
13
+ module Pin
14
+
15
+ def set_component(component_)
16
+ @component=component_
17
+ end
18
+
19
+ # Return introspect object that can be delivered to openplacos server
20
+ def introspect
21
+ iface = Hash.new
22
+ pin = Hash.new
23
+ meth = Array.new
24
+
25
+ if self.respond_to?(:read)
26
+ meth << "read"
27
+ end
28
+ if self.respond_to?(:write)
29
+ meth << "write"
30
+ end
31
+ iface[@interface] = meth
32
+ pin[@name] = iface
33
+ return pin
34
+ end
35
+
36
+ def buffer=(time_)
37
+ self.extend(Buffer)
38
+ @buffer_time = time_
39
+ @buffer_last_value = Time.new(0)
40
+ end
41
+
42
+ end
43
+
44
+ module Buffer
45
+
46
+ #get the value from the buffer
47
+ def get_buffer_value
48
+ if Time.now - @buffer_last_value < @buffer_time
49
+ return @buffer_value
50
+ else
51
+ return nil
52
+ end
53
+ end
54
+
55
+ def set_buffer_value(value_)
56
+ @buffer_value = value_
57
+ @buffer_last_value = Time.now
58
+ end
59
+ end
60
+
61
+ # Instanciate an input pin to your component
62
+ class Input
63
+ include Pin
64
+ attr_reader :name, :interface
65
+ attr_accessor :input, :last_iface_init
66
+
67
+ # instaciate an input pin with its name and the interface this pin will support
68
+ def initialize(pin_name_,iface_name_)
69
+ @name = pin_name_
70
+ @interface = iface_name_
71
+ @input = nil
72
+ @last_iface_init = ""
73
+ end
74
+
75
+ # read method called by dbus
76
+ # this method will
77
+ # * turning off previous interface
78
+ # * turn pin into input mode
79
+ # * initialize current interface
80
+ # * and then made a read access
81
+ # All these steps are optionnal and are in charge of component developper.
82
+ def read_lib(*args)
83
+ set_off # set_off last iface
84
+ set_input_lib
85
+ init_iface
86
+ return read(*args)
87
+ end
88
+
89
+ # write method called by dbus
90
+ # this method will
91
+ # * turning off previous interface
92
+ # * turn pin into input mode
93
+ # * initialize current interface
94
+ # * and then made a read access
95
+ # All these steps are optionnal and are in charge of component developper.
96
+ def write_lib(*args)
97
+ set_off # set_off last iface
98
+ set_output_lib
99
+ init_iface
100
+ return write(*args)
101
+ end
102
+
103
+ # Event style for read definition
104
+ # Can also be overloaded by developper
105
+ def on_read(&block)
106
+ self.singleton_class.instance_eval {
107
+ define_method(:read , &block)
108
+ }
109
+ end
110
+
111
+ # Event style for write definition
112
+ # Can also be overloaded by developper
113
+ def on_write(&block)
114
+ self.singleton_class.instance_eval {
115
+ define_method(:write , &block)
116
+ }
117
+ end
118
+
119
+ private
120
+
121
+ # Iface changed since last call, turn off previous one
122
+ # Please implement exit method at your end
123
+ def set_off()
124
+ if (@last_iface_init != @interface)
125
+ prev_iface = @component.get_input_iface(@name, @last_iface_init)
126
+ if(prev_iface.respond_to?(:exit))
127
+ prev_iface.exit()
128
+ end
129
+ end
130
+ end
131
+
132
+ # If pin has to be set to input mode
133
+ # Please implement set_input method at your end
134
+ def set_input_lib
135
+ if (@input != 1) # 1 means input / 0 output
136
+ if(self.respond_to?(:set_input))
137
+ self.set_input()
138
+ end
139
+ @input = 1
140
+ end
141
+ @last_iface_init = @interface
142
+ end
143
+
144
+ # If pin has to be set to output mode
145
+ # Please implement set_output method at your end
146
+ def set_output_lib
147
+ if (@input != 0) # 1 means input / 0 output
148
+ if(self.respond_to?(:set_output))
149
+ self.set_output()
150
+ end
151
+ @input = 0
152
+ end
153
+ end
154
+
155
+ # Initialize this interface
156
+ # Please implement init method at your end
157
+ def init_iface
158
+ if(@last_iface_init != @interface)
159
+ if(self.respond_to?(:init))
160
+ self.init
161
+ end
162
+ end
163
+ @component.set_last_iface_init(@name, @interface) # set last_iface_name
164
+ end
165
+
166
+ end
167
+
168
+ class Output
169
+ include Pin
170
+ attr_reader :name, :interface
171
+ def initialize(pin_name_,iface_name_,meth_ = "rw")
172
+ @name = pin_name_
173
+ @interface = iface_name_
174
+ @meth = meth_
175
+ @proxy = nil
176
+
177
+ # introspect is defined according to read and write methods
178
+ if @meth.include?("r")
179
+ instance_eval { self.extend(Read) }
180
+ end
181
+ if @meth.include?("w")
182
+ instance_eval { self.extend(Write) }
183
+ end
184
+ end
185
+
186
+ module Read
187
+ # Make a read access on this pin
188
+ # Please provide arguments needed according to interface definition
189
+ def read(*args)
190
+ if self.respond_to?(:get_buffer_value)
191
+ buf = get_buffer_value()
192
+ ret = buf || read_on_proxy(*args)
193
+ else
194
+ ret = read_on_proxy(*args)
195
+ end
196
+ return ret
197
+ end
198
+
199
+ private
200
+
201
+ def read_on_proxy(*args)
202
+ val = @proxy.read(*args)[0]
203
+ set_buffer_value(val) if self.respond_to?(:set_buffer_value)
204
+ return val
205
+ end
206
+
207
+ end
208
+
209
+ module Write
210
+ # Make a write access on this pin
211
+ # Please provide arguments needed according to interface definition
212
+ def write(*args)
213
+ return @proxy.write(*args)[0]
214
+ end
215
+ end
216
+
217
+ def connect(proxy_)
218
+ @proxy = proxy_["org.openplacos.#{@interface}"]
219
+ if @proxy.nil?
220
+ LibError.quit_server(255, "The interface org.openplacos.#{@interface} is not available for pin #{self.name}")
221
+ end
222
+ end
223
+
224
+
225
+ end
226
+
227
+
228
+ # Entry point of LibComponent
229
+ class Component
230
+ attr_reader :options, :bus ,:name, :main
231
+
232
+ # Please provide ARGV in argument
233
+ def initialize(argv_ = ARGV)
234
+ @argv = argv_
235
+ @description = ""
236
+ @bus = nil
237
+ @main = nil
238
+ @inputs = Array.new
239
+ @outputs = Array.new
240
+ @parser = Parser.new
241
+ @parser.option(:introspect, "Return introspection of the component",{})
242
+ @parser.option(:debug, "debug flag")
243
+ yield self if block_given?
244
+ @options = @parser.process!(@argv)
245
+ @name = @options[:name].downcase
246
+ end
247
+
248
+ # provide a string describing your component
249
+ def description(desc_)
250
+ @description = desc_
251
+ @parser.banner = desc_
252
+ end
253
+
254
+ # Set version of your component
255
+ def version(version_)
256
+ @parser.version = version_
257
+ end
258
+
259
+ # Default name for identiying your component
260
+ def default_name(name_)
261
+ @parser.option(:name,"Dbus name of the composant", :default => name_)
262
+ end
263
+
264
+ # define an option in command line (micro-optparse syntaxe)
265
+ def option(*args_)
266
+ @parser.option(*args_)
267
+ end
268
+
269
+ # Push pin to component
270
+ def <<(pin_)
271
+ if pin_.kind_of?(Input)
272
+ @inputs << pin_
273
+ elsif pin_.kind_of?(Output)
274
+ @outputs << pin_
275
+ elsif pin_.kind_of?(Array)
276
+ # push an array of pin
277
+ pin_.each { |p|
278
+ self << p
279
+ }
280
+ end
281
+ pin_.set_component(self)
282
+ end
283
+
284
+ # print function
285
+ # please use this method rather than puts
286
+ def print_debug(arg_)
287
+ if !@options[:introspect]
288
+ puts arg_
289
+ end
290
+ end
291
+
292
+ # Let's rock! Run the component
293
+ def run
294
+ intro = introspect
295
+ if @options[:introspect]
296
+ print intro.to_yaml
297
+ else
298
+ @bus = create_bus
299
+
300
+ #create dbus input pins
301
+ dbusinputs = LibComponent::DbusInput.create_dbusinputs_from_introspect(intro["input"]["pin"],self)
302
+ name = "org.openplacos.components.#{@name.downcase}"
303
+ if (@bus.proxy.ListNames[0].member?(name))
304
+ LibError.quit_server(255, "#{name} already exists")
305
+ end
306
+ @service = @bus.request_service(name)
307
+ dbusinputs.each { |pin|
308
+ @service.export(pin)
309
+ }
310
+
311
+ #create and connect output pins
312
+ if options[:debug]
313
+ @dbusoutputs = LibComponent::DebugOutput.create_dbusoutputs_from_introspect(intro["output"]["pin"],self)
314
+ else
315
+ @dbusoutputs = LibComponent::DbusOutput.create_dbusoutputs_from_introspect(intro["output"]["pin"],self)
316
+ @servicesignal = Servicesignal.new(@bus, self) # listen for service signal from server
317
+ end
318
+
319
+ Signal.trap('INT') do
320
+ self.quit_callback
321
+ end
322
+
323
+ @main = DBus::Main.new
324
+ @main << @bus
325
+ @main.run
326
+ end
327
+ end
328
+
329
+ # Event style for quit method
330
+ def on_quit(&block)
331
+ self.singleton_class.instance_eval {
332
+ define_method(:quit , &block)
333
+ }
334
+ end
335
+
336
+ # Parse inputs and outputs to communicate with server
337
+ def introspect
338
+ inputs_h = Hash.new
339
+ outputs_h = Hash.new
340
+
341
+ #call all inputs introspect and merge values
342
+ @inputs.each { |input|
343
+ inputs_h.merge!(input.introspect) { |key, old, new| old.merge(new) }
344
+ }
345
+ #call all outputs introspect and merge values
346
+ @outputs.each { |output|
347
+ outputs_h.merge!(output.introspect) { |key, old, new| old.merge(new) }
348
+ }
349
+
350
+ res = Hash.new
351
+ res["input"] = {"pin" => inputs_h}
352
+ res["output"] = {"pin" => outputs_h}
353
+ return res
354
+ end
355
+
356
+ def get_input_iface(object_name_,iface_name_)
357
+ @inputs.each { |input|
358
+ return input if (input.name==object_name_) and (input.interface == iface_name_)
359
+ }
360
+ return nil
361
+ end
362
+
363
+ def set_input(object_name_, input_)
364
+ @inputs.each { |input|
365
+ input.input= input_ if (input.name==object_name_)
366
+ }
367
+ return nil
368
+ end
369
+
370
+ def set_last_iface_init(object_name_, iface_name_)
371
+ @inputs.each { |input|
372
+ input.last_iface_init = iface_name_ if (input.name==object_name_)
373
+ }
374
+ return nil
375
+ end
376
+
377
+ def get_output_iface(object_name_,iface_name_)
378
+ @outputs.each { |output|
379
+ return output if (output.name==object_name_) and (output.interface == iface_name_)
380
+ }
381
+ return nil
382
+ end
383
+
384
+ # Method called when signal "quit" from server is raised
385
+ def quit_callback
386
+ self.quit if self.respond_to?(:quit)
387
+ @main.quit if !@main.nil?
388
+ end
389
+
390
+ private
391
+
392
+ def create_bus
393
+ return DBus::ASessionBus.new
394
+ end
395
+
396
+ end
397
+
398
+ class DbusInput < DBus::Object
399
+
400
+ def initialize(name)
401
+ super(name)
402
+ end
403
+
404
+ # Create all dbus I/O for this pin.
405
+ # Called internally by component
406
+ def self.create_dbusinputs_from_introspect(intro_,component_)
407
+ pin = Array.new
408
+ intro_.each { |name, definition|
409
+ p = self.new(name)
410
+ definition.each { |iface, meths|
411
+ component_input = component_.get_input_iface(name,iface)
412
+ p.singleton_class.instance_eval do
413
+ dbus_interface "org.openplacos.#{iface}" do
414
+ meths.each { |m|
415
+ add_dbusmethod m.to_sym do |*args|
416
+ return component_input.send(m+"_lib",*args)
417
+ end
418
+ }
419
+ end
420
+ end
421
+ }
422
+ pin << p
423
+ }
424
+ return pin
425
+ end
426
+
427
+ private
428
+
429
+ def self.add_dbusmethod(sym_,&block)
430
+ case sym_
431
+ when :read
432
+ prototype = "out return:v, in option:a{sv}"
433
+ when :write
434
+ prototype = "out return:v, in value:v, in option:a{sv}"
435
+ end
436
+ dbus_method(sym_,prototype,&block)
437
+ end
438
+
439
+ end
440
+
441
+ class DbusOutput < DBus::ProxyObject
442
+
443
+ def initialize(bus_,name_)
444
+ @name = name_
445
+ @bus = bus_
446
+ super(@bus,"org.openplacos.server.internal",@name)
447
+ end
448
+
449
+ # Create all dbus I/O for this pin.
450
+ # Called internally by component
451
+ def self.create_dbusoutputs_from_introspect(intro_,component_)
452
+ pin = Array.new
453
+ intro_.each { |name, definition|
454
+ p = self.new(component_.bus,"/#{component_.name}#{name}")
455
+ begin
456
+ p.introspect
457
+ rescue DBus::Error
458
+ LibError.quit(255, "From #{component_.name}: Introspect of pin /#{component_.name}#{name} failed \nOpenplacos server is probably unreachable")
459
+ rescue
460
+ LibError.quit_server(255, "From #{component_.name}: Introspect of pin /#{component_.name}#{name} failed \n")
461
+ end
462
+ definition.each_key { |iface|
463
+ component_output = component_.get_output_iface(name,iface)
464
+ component_output.connect(p)
465
+ }
466
+ pin << p
467
+ }
468
+ return pin
469
+ end
470
+
471
+ end
472
+
473
+ # a class for debugging which print the output
474
+ class DebugOutput
475
+ attr_reader :name
476
+
477
+ def initialize(name_)
478
+ @name = name_
479
+ end
480
+
481
+ def [](propname)
482
+ return self
483
+ end
484
+
485
+ def self.create_dbusoutputs_from_introspect(intro_,component_)
486
+ pin = Array.new
487
+ intro_.each { |name, definition|
488
+ p = self.new("/#{component_.name}#{name}")
489
+
490
+ definition.each_key { |iface|
491
+ component_output = component_.get_output_iface(name,iface)
492
+ component_output.connect(p)
493
+ }
494
+ pin << p
495
+ }
496
+ return pin
497
+ end
498
+
499
+ def read(*args)
500
+ puts "Read on #{self.name} : #{args.inspect}"
501
+ return [0]
502
+ end
503
+
504
+ def write(*args)
505
+ puts "Write on #{self.name} : #{args.inspect}"
506
+ return [0]
507
+ end
508
+
509
+ end
510
+
511
+ class LibError
512
+
513
+ # Print an error message and make the server quit
514
+ def self.quit_server(status_, str_)
515
+ bus = DBus::ASessionBus.new
516
+ server = bus.service("org.openplacos.server.internal")
517
+ opos = server.object("/plugins")
518
+ opos.introspect
519
+ opos.default_iface = "org.openplacos.plugins"
520
+
521
+ $stderr.puts str_
522
+ opos.exit(status_, str_)
523
+ end
524
+
525
+ # Only quit component
526
+ # Only use this method if component cannot connect to server
527
+ def self.quit(status_,str_)
528
+ $stderr.puts str_
529
+ exit(status_)
530
+ end
531
+ end
532
+
533
+ class Servicesignal
534
+
535
+ def initialize(bus_, component_)
536
+ @bus = bus_
537
+ @component = component_
538
+ @server = @bus.service("org.openplacos.server.internal")
539
+ @opos = @server.object("/plugins")
540
+ @opos.introspect
541
+ @opos.default_iface = "org.openplacos.plugins"
542
+
543
+ @opos.on_signal("quit") do
544
+ @component.quit_callback
545
+ end
546
+ end
547
+
548
+ end
549
+ end
550
+