openplacos 0.0.8 → 0.0.9

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.
@@ -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
+