artoo 0.1.0
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/.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
|