openplacos 0.0.8 → 0.0.9
Sign up to get free protection for your applications and to get access to all the features.
- data/VERSION +1 -1
- data/lib/openplacos.rb +1 -2
- data/lib/openplacos/libclient.rb +364 -127
- data/lib/openplacos/libcomponent.rb +550 -0
- data/lib/openplacos/template_client.rb +17 -0
- data/lib/openplacos/widget/Analog.rb +63 -0
- data/lib/openplacos/widget/Digital.rb +55 -0
- data/lib/openplacos/widget/modules.rb +19 -0
- data/openplacos.gemspec +5 -3
- metadata +75 -58
- data/lib/openplacos/libdriver.rb +0 -129
- data/lib/openplacos/libplugin.rb +0 -76
@@ -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
|
+
|