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