artoo 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +25 -0
- data/Gemfile +26 -0
- data/Gemfile.lock +144 -0
- data/Guardfile +15 -0
- data/LICENSE +13 -0
- data/README.md +97 -0
- data/Rakefile +11 -0
- data/api/assets/compass.rb +25 -0
- data/api/assets/javascripts/artoo/controllers/robot.js.coffee +27 -0
- data/api/assets/javascripts/artoo/routes.js.coffee +23 -0
- data/api/assets/javascripts/core.js.coffee +6 -0
- data/api/assets/javascripts/vendor/angular.min.js +161 -0
- data/api/assets/javascripts/vendor/bootstrap.min.js +6 -0
- data/api/assets/javascripts/vendor/jquery.min.js +5 -0
- data/api/assets/stylesheets/artoo/_core.css.scss +3 -0
- data/api/assets/stylesheets/artoo/_font-awesome.scss +534 -0
- data/api/assets/stylesheets/artoo/_variables.scss +8 -0
- data/api/assets/stylesheets/core.scss +70 -0
- data/api/public/core.css +9848 -0
- data/api/public/core.js +259 -0
- data/api/public/favicon.ico +0 -0
- data/api/public/font/FontAwesome.otf +0 -0
- data/api/public/font/fontawesome-webfont.eot +0 -0
- data/api/public/font/fontawesome-webfont.svg +284 -0
- data/api/public/font/fontawesome-webfont.ttf +0 -0
- data/api/public/font/fontawesome-webfont.woff +0 -0
- data/api/public/html5shiv.js +8 -0
- data/api/public/images/devices/ardrone.jpg +0 -0
- data/api/public/images/devices/arduino.jpg +0 -0
- data/api/public/images/devices/sphero.png +0 -0
- data/api/public/images/glyphicons-halflings-white.png +0 -0
- data/api/public/images/glyphicons-halflings.png +0 -0
- data/api/public/index.html +36 -0
- data/api/public/partials/robot-detail.html +111 -0
- data/api/public/partials/robot-device-detail.html +0 -0
- data/api/public/partials/robot-index.html +26 -0
- data/artoo.gemspec +21 -0
- data/bin/retry.sh +8 -0
- data/bin/sphero.sh +8 -0
- data/examples/ardrone.rb +12 -0
- data/examples/ardrone_nav.rb +22 -0
- data/examples/ardrone_nav_video.rb +33 -0
- data/examples/ardrone_video.rb +22 -0
- data/examples/conway_sphero.rb +65 -0
- data/examples/firmata.rb +13 -0
- data/examples/firmata_button.rb +9 -0
- data/examples/hello.rb +12 -0
- data/examples/hello_api.rb +9 -0
- data/examples/hello_api_multiple.rb +25 -0
- data/examples/hello_modular.rb +16 -0
- data/examples/hello_multiple.rb +22 -0
- data/examples/notifications.rb +9 -0
- data/examples/sphero.rb +11 -0
- data/examples/sphero_color.rb +13 -0
- data/examples/sphero_firmata.rb +17 -0
- data/examples/sphero_messages.rb +22 -0
- data/examples/sphero_multiple.rb +33 -0
- data/examples/wiiclassic.rb +94 -0
- data/lib/artoo.rb +3 -0
- data/lib/artoo/adaptors/adaptor.rb +54 -0
- data/lib/artoo/adaptors/ardrone.rb +32 -0
- data/lib/artoo/adaptors/ardrone_navigation.rb +26 -0
- data/lib/artoo/adaptors/ardrone_video.rb +27 -0
- data/lib/artoo/adaptors/firmata.rb +25 -0
- data/lib/artoo/adaptors/loopback.rb +8 -0
- data/lib/artoo/adaptors/sphero.rb +46 -0
- data/lib/artoo/api.rb +48 -0
- data/lib/artoo/api_route_helpers.rb +197 -0
- data/lib/artoo/connection.rb +70 -0
- data/lib/artoo/delegator.rb +49 -0
- data/lib/artoo/device.rb +61 -0
- data/lib/artoo/device_event_client.rb +27 -0
- data/lib/artoo/drivers/ardrone.rb +9 -0
- data/lib/artoo/drivers/ardrone_navigation.rb +21 -0
- data/lib/artoo/drivers/ardrone_video.rb +22 -0
- data/lib/artoo/drivers/button.rb +40 -0
- data/lib/artoo/drivers/driver.rb +48 -0
- data/lib/artoo/drivers/led.rb +37 -0
- data/lib/artoo/drivers/passthru.rb +9 -0
- data/lib/artoo/drivers/pinger.rb +19 -0
- data/lib/artoo/drivers/pinger2.rb +18 -0
- data/lib/artoo/drivers/sphero.rb +57 -0
- data/lib/artoo/drivers/wiichuck.rb +29 -0
- data/lib/artoo/drivers/wiiclassic.rb +137 -0
- data/lib/artoo/main.rb +32 -0
- data/lib/artoo/master.rb +16 -0
- data/lib/artoo/port.rb +51 -0
- data/lib/artoo/robot.rb +299 -0
- data/lib/artoo/utility.rb +39 -0
- data/lib/artoo/version.rb +5 -0
- data/test/adaptors/adaptor_test.rb +18 -0
- data/test/adaptors/ardrone_test.rb +24 -0
- data/test/adaptors/firmata_test.rb +25 -0
- data/test/adaptors/loopback_test.rb +18 -0
- data/test/adaptors/sphero_test.rb +24 -0
- data/test/api_test.rb +61 -0
- data/test/artoo_test.rb +12 -0
- data/test/connection_test.rb +28 -0
- data/test/delegator_test.rb +71 -0
- data/test/device_test.rb +41 -0
- data/test/drivers/ardrone_navigation_test.rb +11 -0
- data/test/drivers/ardrone_test.rb +11 -0
- data/test/drivers/ardrone_video_test.rb +11 -0
- data/test/drivers/driver_test.rb +21 -0
- data/test/drivers/led_test.rb +52 -0
- data/test/drivers/sphero_test.rb +54 -0
- data/test/drivers/wiichuck_test.rb +11 -0
- data/test/port_test.rb +33 -0
- data/test/robot_test.rb +96 -0
- data/test/test_helper.rb +8 -0
- data/test/utility_test.rb +27 -0
- metadata +185 -0
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'artoo/adaptors/adaptor'
|
2
|
+
|
3
|
+
module Artoo
|
4
|
+
module Adaptors
|
5
|
+
# Connect to a Sphero (http://gosphero.com)
|
6
|
+
class Sphero < Adaptor
|
7
|
+
RETRY_COUNT = 5
|
8
|
+
attr_reader :sphero
|
9
|
+
|
10
|
+
def finalize
|
11
|
+
if connected?
|
12
|
+
#sphero.stop
|
13
|
+
sphero.close
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def connect
|
18
|
+
@retries_left = RETRY_COUNT
|
19
|
+
require 'sphero' unless defined?(::Sphero)
|
20
|
+
begin
|
21
|
+
@sphero = ::Sphero.new(connect_to)
|
22
|
+
super
|
23
|
+
return true
|
24
|
+
rescue Errno::EBUSY => e
|
25
|
+
@retries_left -= 1
|
26
|
+
if @retries_left > 0
|
27
|
+
retry
|
28
|
+
else
|
29
|
+
Logger.error e.message
|
30
|
+
Logger.error e.backtrace.inspect
|
31
|
+
return false
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def disconnect
|
37
|
+
sphero.close
|
38
|
+
super
|
39
|
+
end
|
40
|
+
|
41
|
+
def method_missing(method_name, *arguments, &block)
|
42
|
+
sphero.send(method_name, *arguments, &block)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
data/lib/artoo/api.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'reel'
|
2
|
+
require 'artoo/api_route_helpers'
|
3
|
+
require 'artoo/device_event_client'
|
4
|
+
|
5
|
+
module Artoo
|
6
|
+
class Api < Reel::Server
|
7
|
+
include Artoo::ApiRouteHelpers
|
8
|
+
|
9
|
+
def initialize(host = "127.0.0.1", port = 3000)
|
10
|
+
super(host, port, &method(:on_connection))
|
11
|
+
end
|
12
|
+
|
13
|
+
def on_connection(connection)
|
14
|
+
while request = connection.request
|
15
|
+
dispatch!(connection, request)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
get '/robots' do
|
20
|
+
MultiJson.dump(Actor[:master].robots.collect {|r|r.to_hash})
|
21
|
+
end
|
22
|
+
|
23
|
+
get '/robots/:robotid' do
|
24
|
+
Actor[:master].get_robot_by_name(@params['robotid']).as_json
|
25
|
+
end
|
26
|
+
|
27
|
+
get '/robots/:robotid/devices' do
|
28
|
+
MultiJson.dump(Actor[:master].get_robot_by_name(@params['robotid']).devices.each_value.collect {|d| d.to_hash})
|
29
|
+
end
|
30
|
+
|
31
|
+
get '/robots/:robotid/devices/:deviceid' do
|
32
|
+
Actor[:master].get_robot_by_name(@params['robotid']).devices[@params['deviceid'].intern].as_json
|
33
|
+
end
|
34
|
+
|
35
|
+
get_ws '/robots/:robotid/devices/:deviceid/events' do
|
36
|
+
DeviceEventClient.new(@req, Actor[:master].get_robot_by_name(@params['robotid']).devices[@params['deviceid'].intern].event_topic_name('update'))
|
37
|
+
return nil
|
38
|
+
end
|
39
|
+
|
40
|
+
get '/robots/:robotid/connections' do
|
41
|
+
MultiJson.dump(Actor[:master].get_robot_by_name(@params['robotid']).connections.each_value.collect {|c| c.to_hash})
|
42
|
+
end
|
43
|
+
|
44
|
+
get '/robots/:robotid/connections/:connectionid' do
|
45
|
+
Actor[:master].get_robot_by_name(@params['robotid']).connections[@params['connectionid'].intern].as_json
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,197 @@
|
|
1
|
+
# route helpers used within the Artoo::Api class
|
2
|
+
module Artoo
|
3
|
+
module ApiRouteHelpers
|
4
|
+
class ResponseHandled < StandardError; end
|
5
|
+
module ClassMethods
|
6
|
+
|
7
|
+
def static_path(default=File.join(File.dirname(__FILE__), "..", "..", "api/public"))
|
8
|
+
@static_path ||= default
|
9
|
+
end
|
10
|
+
|
11
|
+
def routes
|
12
|
+
@routes ||= {}
|
13
|
+
end
|
14
|
+
|
15
|
+
def route(verb, path, &block)
|
16
|
+
signature = compile!(verb, path, block, {})
|
17
|
+
(routes[verb] ||= []) << signature
|
18
|
+
end
|
19
|
+
|
20
|
+
## Ripped from Sinatra 'cause it's so sexy in there
|
21
|
+
def generate_method(method_name, &block)
|
22
|
+
define_method(method_name, &block)
|
23
|
+
method = instance_method method_name
|
24
|
+
remove_method method_name
|
25
|
+
method
|
26
|
+
end
|
27
|
+
|
28
|
+
def compile!(verb, path, block, options = {})
|
29
|
+
options.each_pair { |option, args| send(option, *args) }
|
30
|
+
method_name = "#{verb} #{path}"
|
31
|
+
unbound_method = generate_method(method_name, &block)
|
32
|
+
pattern, keys = compile path
|
33
|
+
conditions, @conditions = @conditions, []
|
34
|
+
|
35
|
+
[ pattern, keys, conditions, block.arity != 0 ?
|
36
|
+
proc { |a,p| unbound_method.bind(a).call(*p) } :
|
37
|
+
proc { |a,p| unbound_method.bind(a).call } ]
|
38
|
+
end
|
39
|
+
|
40
|
+
def compile(path)
|
41
|
+
keys = []
|
42
|
+
if path.respond_to? :to_str
|
43
|
+
ignore = ""
|
44
|
+
pattern = path.to_str.gsub(/[^\?\%\\\/\:\*\w]/) do |c|
|
45
|
+
ignore << escaped(c).join if c.match(/[\.@]/)
|
46
|
+
patt = encoded(c)
|
47
|
+
patt.gsub(/%[\da-fA-F]{2}/) do |match|
|
48
|
+
match.split(//).map {|char| char =~ /[A-Z]/ ? "[#{char}#{char.tr('A-Z', 'a-z')}]" : char}.join
|
49
|
+
end
|
50
|
+
end
|
51
|
+
pattern.gsub!(/((:\w+)|\*)/) do |match|
|
52
|
+
if match == "*"
|
53
|
+
keys << 'splat'
|
54
|
+
"(.*?)"
|
55
|
+
else
|
56
|
+
keys << $2[1..-1]
|
57
|
+
ignore_pattern = safe_ignore(ignore)
|
58
|
+
|
59
|
+
ignore_pattern
|
60
|
+
end
|
61
|
+
end
|
62
|
+
[/\A#{pattern}\z/, keys]
|
63
|
+
elsif path.respond_to?(:keys) && path.respond_to?(:match)
|
64
|
+
[path, path.keys]
|
65
|
+
elsif path.respond_to?(:names) && path.respond_to?(:match)
|
66
|
+
[path, path.names]
|
67
|
+
elsif path.respond_to? :match
|
68
|
+
[path, keys]
|
69
|
+
else
|
70
|
+
raise TypeError, path
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def safe_ignore(ignore)
|
75
|
+
unsafe_ignore = []
|
76
|
+
ignore = ignore.gsub(/%[\da-fA-F]{2}/) do |hex|
|
77
|
+
unsafe_ignore << hex[1..2]
|
78
|
+
''
|
79
|
+
end
|
80
|
+
unsafe_patterns = unsafe_ignore.map do |unsafe|
|
81
|
+
chars = unsafe.split(//).map do |char|
|
82
|
+
if char =~ /[A-Z]/
|
83
|
+
char <<= char.tr('A-Z', 'a-z')
|
84
|
+
end
|
85
|
+
char
|
86
|
+
end
|
87
|
+
|
88
|
+
"|(?:%[^#{chars[0]}].|%[#{chars[0]}][^#{chars[1]}])"
|
89
|
+
end
|
90
|
+
if unsafe_patterns.length > 0
|
91
|
+
"((?:[^#{ignore}/?#%]#{unsafe_patterns.join()})+)"
|
92
|
+
else
|
93
|
+
"([^#{ignore}/?#]+)"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
# Helper route functions
|
98
|
+
def get(path, &block)
|
99
|
+
route 'GET', path, &block
|
100
|
+
end
|
101
|
+
def get_ws(path, &block)
|
102
|
+
route 'GET', path, &block
|
103
|
+
end
|
104
|
+
def put(path, &block)
|
105
|
+
route 'PUT', path, &block
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
module InstanceMethods
|
110
|
+
## Handle the request
|
111
|
+
def dispatch!(connection, req)
|
112
|
+
resp = catch(:halt) do
|
113
|
+
try_static! connection, req
|
114
|
+
route! connection, req
|
115
|
+
end
|
116
|
+
if resp && !resp.nil?
|
117
|
+
return if req.is_a?(Reel::WebSocket)
|
118
|
+
status, body = resp
|
119
|
+
req.respond status, body
|
120
|
+
else
|
121
|
+
req.respond :not_found, "NOT FOUND"
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Exit the current block, halts any further processing
|
126
|
+
# of the request, and returns the specified response.
|
127
|
+
def halt(*response)
|
128
|
+
response = response.first if response.length == 1
|
129
|
+
throw :halt, response
|
130
|
+
end
|
131
|
+
|
132
|
+
def try_static!(connection, req)
|
133
|
+
fpath = req.url == '/' ? 'index.html' : req.url[1..-1]
|
134
|
+
filepath = File.expand_path(fpath, self.class.static_path)
|
135
|
+
if File.file?(filepath)
|
136
|
+
# TODO: stream this?
|
137
|
+
data = open(filepath).read
|
138
|
+
halt :ok, data
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def route!(connection, req)
|
143
|
+
if routes = self.class.routes[req.method]
|
144
|
+
routes.each do |pattern, keys, conditions, block|
|
145
|
+
route = req.url
|
146
|
+
next unless match = pattern.match(route)
|
147
|
+
values = match.captures.to_a.map { |v| URI.decode_www_form_component(v) if v }
|
148
|
+
if values.any?
|
149
|
+
params = {}
|
150
|
+
keys.zip(values) { |k,v| Array === params[k] ? params[k] << v : params[k] = v if v }
|
151
|
+
@params = params
|
152
|
+
end
|
153
|
+
|
154
|
+
@connection = connection
|
155
|
+
@req = req
|
156
|
+
|
157
|
+
begin
|
158
|
+
body = block ? block[self, values] : yield(self, values)
|
159
|
+
halt [:ok, body]
|
160
|
+
rescue Exception => e
|
161
|
+
p [:e, e]
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
nil
|
166
|
+
end
|
167
|
+
|
168
|
+
# Fixes encoding issues by
|
169
|
+
# * defaulting to UTF-8
|
170
|
+
# * casting params to Encoding.default_external
|
171
|
+
#
|
172
|
+
# The latter might not be necessary if Rack handles it one day.
|
173
|
+
# Keep an eye on Rack's LH #100.
|
174
|
+
def force_encoding(*args) settings.force_encoding(*args) end
|
175
|
+
if defined? Encoding
|
176
|
+
def self.force_encoding(data, encoding = default_encoding)
|
177
|
+
return if data == settings || data.is_a?(Tempfile)
|
178
|
+
if data.respond_to? :force_encoding
|
179
|
+
data.force_encoding(encoding).encode!
|
180
|
+
elsif data.respond_to? :each_value
|
181
|
+
data.each_value { |v| force_encoding(v, encoding) }
|
182
|
+
elsif data.respond_to? :each
|
183
|
+
data.each { |v| force_encoding(v, encoding) }
|
184
|
+
end
|
185
|
+
data
|
186
|
+
end
|
187
|
+
else
|
188
|
+
def self.force_encoding(data, *) data end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def self.included(receiver)
|
193
|
+
receiver.extend ClassMethods
|
194
|
+
receiver.send :include, InstanceMethods
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
require 'artoo/utility'
|
2
|
+
|
3
|
+
module Artoo
|
4
|
+
# The Connection class represents the interface to
|
5
|
+
# a specific group of hardware devices. Examples would be an
|
6
|
+
# Arduino, a Sphero, or an ARDrone.
|
7
|
+
class Connection
|
8
|
+
include Celluloid
|
9
|
+
include Artoo::Utility
|
10
|
+
|
11
|
+
attr_reader :parent, :name, :port, :adaptor
|
12
|
+
|
13
|
+
def initialize(params={})
|
14
|
+
@name = params[:name].to_s
|
15
|
+
@port = Port.new(params[:port])
|
16
|
+
@parent = params[:parent]
|
17
|
+
|
18
|
+
require_adaptor(params[:adaptor] || :loopback)
|
19
|
+
end
|
20
|
+
|
21
|
+
def connect
|
22
|
+
Logger.info "Connecting to '#{name}' on port '#{port}'..."
|
23
|
+
adaptor.connect
|
24
|
+
rescue Exception => e
|
25
|
+
Logger.error e.message
|
26
|
+
Logger.error e.backtrace.inspect
|
27
|
+
end
|
28
|
+
|
29
|
+
def disconnect
|
30
|
+
Logger.info "Disconnecting from '#{name}' on port '#{port}'..."
|
31
|
+
adaptor.disconnect
|
32
|
+
end
|
33
|
+
|
34
|
+
def connected?
|
35
|
+
adaptor.connected?
|
36
|
+
end
|
37
|
+
|
38
|
+
def to_hash
|
39
|
+
{:name => name,
|
40
|
+
:port => port.to_s,
|
41
|
+
:adaptor => adaptor.class.name.demodulize,
|
42
|
+
:connected => connected?
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def as_json
|
47
|
+
MultiJson.dump(to_hash)
|
48
|
+
end
|
49
|
+
|
50
|
+
def method_missing(method_name, *arguments, &block)
|
51
|
+
unless adaptor.connected?
|
52
|
+
Logger.warn "Cannot call unconnected adaptor '#{name}', attempting to reconnect..."
|
53
|
+
adaptor.reconnect
|
54
|
+
return nil
|
55
|
+
end
|
56
|
+
adaptor.send(method_name, *arguments, &block)
|
57
|
+
rescue Exception => e
|
58
|
+
Logger.error e.message
|
59
|
+
Logger.error e.backtrace.inspect
|
60
|
+
return nil
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def require_adaptor(type)
|
66
|
+
require "artoo/adaptors/#{type.to_s}"
|
67
|
+
@adaptor = constantize("Artoo::Adaptors::#{classify(type.to_s)}").new(:port => port, :parent => current_instance)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
|
2
|
+
module Artoo
|
3
|
+
# Execution context for top-level robots
|
4
|
+
# DSL methods executed on main are delegated to this class like Sinatra
|
5
|
+
class MainRobot < Robot
|
6
|
+
#set :logging, Proc.new { ! test? }
|
7
|
+
#set :method_override, true
|
8
|
+
set :start_work, false #Proc.new { ! test? }
|
9
|
+
#set :app_file, nil
|
10
|
+
|
11
|
+
# def self.register(*extensions, &block) #:nodoc:
|
12
|
+
# added_methods = extensions.map {|m| m.public_instance_methods }.flatten
|
13
|
+
# Delegator.delegate(*added_methods)
|
14
|
+
# super(*extensions, &block)
|
15
|
+
# end
|
16
|
+
end
|
17
|
+
|
18
|
+
# Artoo delegation mixin that acts like Sinatra.
|
19
|
+
# Mixing this module into an object causes all
|
20
|
+
# methods to be delegated to the Artoo::MainRobot class.
|
21
|
+
# Used primarily at the top-level.
|
22
|
+
module Delegator #:nodoc:
|
23
|
+
def self.delegate(*methods)
|
24
|
+
methods.each do |method_name|
|
25
|
+
define_method(method_name) do |*args, &block|
|
26
|
+
return super(*args, &block) if respond_to? method_name
|
27
|
+
Delegator.target.send(method_name, *args, &block)
|
28
|
+
end
|
29
|
+
private method_name
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
delegate :connection, :device, :work, :api, :set, :test?
|
34
|
+
|
35
|
+
class << self
|
36
|
+
attr_accessor :target
|
37
|
+
end
|
38
|
+
|
39
|
+
self.target = Artoo::MainRobot
|
40
|
+
end
|
41
|
+
|
42
|
+
# Create a new Artoo robot. The block is evaluated
|
43
|
+
# in the new robot's class scope.
|
44
|
+
def self.new(robot=Robot, options={}, &block)
|
45
|
+
robot = Class.new(robot)
|
46
|
+
robot.class_eval(&block) if block_given?
|
47
|
+
robot
|
48
|
+
end
|
49
|
+
end
|
data/lib/artoo/device.rb
ADDED
@@ -0,0 +1,61 @@
|
|
1
|
+
module Artoo
|
2
|
+
# The Artoo::Device class represents the interface to
|
3
|
+
# a specific individual hardware devices. Examples would be a digital
|
4
|
+
# thermometer connected to an Arduino, or a Sphero's accelerometer.
|
5
|
+
class Device
|
6
|
+
include Celluloid
|
7
|
+
include Artoo::Utility
|
8
|
+
|
9
|
+
attr_reader :parent, :name, :driver, :pin, :connection, :interval
|
10
|
+
|
11
|
+
def initialize(params={})
|
12
|
+
@name = params[:name].to_s
|
13
|
+
@pin = params[:pin]
|
14
|
+
@parent = params[:parent]
|
15
|
+
@connection = determine_connection(params[:connection]) || default_connection
|
16
|
+
@interval = params[:interval] || 0.5
|
17
|
+
|
18
|
+
require_driver(params[:driver] || :passthru)
|
19
|
+
end
|
20
|
+
|
21
|
+
def determine_connection(c)
|
22
|
+
parent.connections[c] unless c.nil?
|
23
|
+
end
|
24
|
+
|
25
|
+
def default_connection
|
26
|
+
parent.default_connection
|
27
|
+
end
|
28
|
+
|
29
|
+
def start_device
|
30
|
+
driver.start_driver
|
31
|
+
end
|
32
|
+
|
33
|
+
def event_topic_name(event)
|
34
|
+
"#{parent.safe_name}_#{name}_#{event}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def to_hash
|
38
|
+
{:name => name,
|
39
|
+
:driver => driver.class.name.demodulize,
|
40
|
+
:pin => pin.to_s,
|
41
|
+
:connection => connection.to_hash,
|
42
|
+
:interval => interval
|
43
|
+
}
|
44
|
+
end
|
45
|
+
|
46
|
+
def as_json
|
47
|
+
MultiJson.dump(to_hash)
|
48
|
+
end
|
49
|
+
|
50
|
+
def method_missing(method_name, *arguments, &block)
|
51
|
+
driver.send(method_name, *arguments, &block)
|
52
|
+
end
|
53
|
+
|
54
|
+
private
|
55
|
+
|
56
|
+
def require_driver(d)
|
57
|
+
require "artoo/drivers/#{d.to_s}"
|
58
|
+
@driver = constantize("Artoo::Drivers::#{classify(d.to_s)}").new(:parent => current_instance)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|