artoo 0.4.0 → 0.4.1
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.
- checksums.yaml +4 -4
- data/.travis.yml +1 -1
- data/.yardopts +10 -0
- data/Gemfile +9 -0
- data/Gemfile.lock +10 -7
- data/README.md +2 -2
- data/api/assets/javascripts/artoo/controllers/robot.js.coffee +2 -1
- data/api/public/core.js +3 -1
- data/artoo.gemspec +0 -1
- data/lib/artoo/adaptors/adaptor.rb +27 -6
- data/lib/artoo/adaptors/ardrone.rb +10 -2
- data/lib/artoo/adaptors/ardrone_navigation.rb +9 -2
- data/lib/artoo/adaptors/ardrone_video.rb +13 -5
- data/lib/artoo/adaptors/firmata.rb +10 -2
- data/lib/artoo/adaptors/loopback.rb +2 -1
- data/lib/artoo/adaptors/roomba.rb +12 -4
- data/lib/artoo/adaptors/sphero.rb +15 -3
- data/lib/artoo/api.rb +48 -5
- data/lib/artoo/api_route_helpers.rb +22 -3
- data/lib/artoo/basic.rb +3 -7
- data/lib/artoo/connection.rb +25 -7
- data/lib/artoo/delegator.rb +6 -16
- data/lib/artoo/device.rb +39 -10
- data/lib/artoo/device_event_client.rb +6 -0
- data/lib/artoo/drivers/ardrone.rb +5 -1
- data/lib/artoo/drivers/ardrone_navigation.rb +5 -1
- data/lib/artoo/drivers/ardrone_video.rb +9 -4
- data/lib/artoo/drivers/button.rb +7 -1
- data/lib/artoo/drivers/driver.rb +44 -4
- data/lib/artoo/drivers/led.rb +12 -1
- data/lib/artoo/drivers/motor.rb +12 -1
- data/lib/artoo/drivers/pinger.rb +10 -1
- data/lib/artoo/drivers/pinger2.rb +10 -1
- data/lib/artoo/drivers/roomba.rb +65 -17
- data/lib/artoo/drivers/servo.rb +12 -2
- data/lib/artoo/drivers/sphero.rb +19 -5
- data/lib/artoo/drivers/wiichuck.rb +7 -1
- data/lib/artoo/drivers/wiiclassic.rb +14 -5
- data/lib/artoo/drivers/wiidriver.rb +4 -1
- data/lib/artoo/events.rb +11 -4
- data/lib/artoo/ext/actor.rb +1 -1
- data/lib/artoo/ext/timers.rb +1 -1
- data/lib/artoo/main.rb +2 -13
- data/lib/artoo/master.rb +20 -2
- data/lib/artoo/port.rb +8 -3
- data/lib/artoo/robot.rb +45 -17
- data/lib/artoo/utility.rb +53 -9
- data/lib/artoo/version.rb +1 -1
- data/test/drivers/driver_test.rb +15 -0
- data/test/drivers/led_test.rb +4 -0
- data/test/utility_test.rb +35 -18
- data/test/utility_test_cases.rb +56 -0
- metadata +5 -16
data/lib/artoo/api.rb
CHANGED
@@ -3,46 +3,80 @@ require 'artoo/api_route_helpers'
|
|
3
3
|
require 'artoo/device_event_client'
|
4
4
|
|
5
5
|
module Artoo
|
6
|
+
# Artoo API Server provides an interface to communicate with
|
7
|
+
# master class and retrieve information about robots being
|
8
|
+
# controlled
|
6
9
|
class Api < Reel::Server
|
7
10
|
include Artoo::ApiRouteHelpers
|
8
11
|
|
12
|
+
# Create new API server
|
13
|
+
# @param [String] host
|
14
|
+
# @param [Int] port
|
9
15
|
def initialize(host = "127.0.0.1", port = 3000)
|
10
16
|
super(host, port, &method(:on_connection))
|
11
17
|
end
|
12
18
|
|
19
|
+
# Dispatches connection requests
|
13
20
|
def on_connection(connection)
|
14
21
|
while request = connection.request
|
15
22
|
dispatch!(connection, request)
|
16
23
|
end
|
17
24
|
end
|
18
25
|
|
26
|
+
# Retrieve list of robots
|
27
|
+
# @return [JSON] robots
|
19
28
|
get '/robots' do
|
20
29
|
MultiJson.dump(master.robots.collect {|r|r.to_hash})
|
21
30
|
end
|
22
31
|
|
32
|
+
# Retrieve robot by id
|
33
|
+
# @return [JSON] robot
|
23
34
|
get '/robots/:robotid' do
|
24
|
-
master.
|
35
|
+
master.robot(@params['robotid']).as_json
|
25
36
|
end
|
26
37
|
|
38
|
+
# Retrieve robot devices
|
39
|
+
# @return [JSON] devices
|
27
40
|
get '/robots/:robotid/devices' do
|
28
|
-
MultiJson.dump(master.
|
41
|
+
MultiJson.dump(master.robot_devices(@params['robotid']).each_value.collect {|d| d.to_hash})
|
29
42
|
end
|
30
43
|
|
44
|
+
# Retrieve robot device
|
45
|
+
# @return [JSON] device
|
31
46
|
get '/robots/:robotid/devices/:deviceid' do
|
32
47
|
device(@params['robotid'], @params['deviceid']).as_json
|
33
48
|
end
|
34
49
|
|
50
|
+
# Retrieve robot commands
|
51
|
+
# @return [JSON] commands
|
52
|
+
get '/robots/:robotid/devices/:deviceid/commands' do
|
53
|
+
MultiJson.dump(device(@params['robotid'], @params['deviceid']).commands)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Retrieve robot command
|
57
|
+
# @return [JSON] command
|
58
|
+
post '/robots/:robotid/devices/:deviceid/commands/:commandid' do
|
59
|
+
result = device(@params['robotid'], @params['deviceid']).command(@params['commandid'], command_params)
|
60
|
+
return MultiJson.dump({'result' => result})
|
61
|
+
end
|
62
|
+
|
63
|
+
# Subscribte to robot device events
|
64
|
+
# @return [nil]
|
35
65
|
get_ws '/robots/:robotid/devices/:deviceid/events' do
|
36
66
|
DeviceEventClient.new(@req, device(@params['robotid'], @params['deviceid']).event_topic_name('update'))
|
37
67
|
return nil
|
38
68
|
end
|
39
69
|
|
70
|
+
# Retrieve robot connections
|
71
|
+
# @return [JSON] connections
|
40
72
|
get '/robots/:robotid/connections' do
|
41
|
-
MultiJson.dump(master.
|
73
|
+
MultiJson.dump(master.robot_connections(@params['robotid']).each_value.collect {|c| c.to_hash})
|
42
74
|
end
|
43
75
|
|
76
|
+
# Retrieve robot connection
|
77
|
+
# @return [JSON] connection
|
44
78
|
get '/robots/:robotid/connections/:connectionid' do
|
45
|
-
master.
|
79
|
+
master.robot_connection(@params['robotid'], @params['connectionid']).as_json
|
46
80
|
end
|
47
81
|
|
48
82
|
protected
|
@@ -52,7 +86,16 @@ module Artoo
|
|
52
86
|
end
|
53
87
|
|
54
88
|
def device(robot_id, device_id)
|
55
|
-
master.
|
89
|
+
master.robot_device(robot_id, device_id)
|
90
|
+
end
|
91
|
+
|
92
|
+
def command_params
|
93
|
+
data = MultiJson.load(@req.body, :symbolize_keys => true)
|
94
|
+
if data
|
95
|
+
data[:params]
|
96
|
+
else
|
97
|
+
nil
|
98
|
+
end
|
56
99
|
end
|
57
100
|
end
|
58
101
|
end
|
@@ -1,23 +1,30 @@
|
|
1
|
-
# route helpers used within the Artoo::Api class
|
2
1
|
module Artoo
|
2
|
+
# Route helpers used within the Artoo::Api class
|
3
3
|
module ApiRouteHelpers
|
4
4
|
class ResponseHandled < StandardError; end
|
5
5
|
module ClassMethods
|
6
6
|
|
7
|
+
# Path to api/public directory
|
8
|
+
# @return [String] static path
|
7
9
|
def static_path(default=File.join(File.dirname(__FILE__), "..", "..", "api/public"))
|
8
10
|
@static_path ||= default
|
9
11
|
end
|
10
12
|
|
13
|
+
# @return [Hash] routes
|
11
14
|
def routes
|
12
15
|
@routes ||= {}
|
13
16
|
end
|
14
17
|
|
18
|
+
# Adds compiled signature to routes hash
|
19
|
+
# @return [Array] signature
|
15
20
|
def route(verb, path, &block)
|
16
21
|
signature = compile!(verb, path, block, {})
|
17
22
|
(routes[verb] ||= []) << signature
|
18
23
|
end
|
19
24
|
|
20
|
-
|
25
|
+
# Creates method from block, ripped from Sinatra
|
26
|
+
# 'cause it's so sexy in there
|
27
|
+
# @return [Method] generated method
|
21
28
|
def generate_method(method_name, &block)
|
22
29
|
define_method(method_name, &block)
|
23
30
|
method = instance_method method_name
|
@@ -25,6 +32,7 @@ module Artoo
|
|
25
32
|
method
|
26
33
|
end
|
27
34
|
|
35
|
+
#@todo Add documentation
|
28
36
|
def compile!(verb, path, block, options = {})
|
29
37
|
options.each_pair { |option, args| send(option, *args) }
|
30
38
|
method_name = "#{verb} #{path}"
|
@@ -37,6 +45,7 @@ module Artoo
|
|
37
45
|
proc { |a,p| unbound_method.bind(a).call } ]
|
38
46
|
end
|
39
47
|
|
48
|
+
#@todo Add documentation
|
40
49
|
def compile(path)
|
41
50
|
keys = []
|
42
51
|
if path.respond_to? :to_str
|
@@ -71,6 +80,7 @@ module Artoo
|
|
71
80
|
end
|
72
81
|
end
|
73
82
|
|
83
|
+
#@todo Add documentation
|
74
84
|
def safe_ignore(ignore)
|
75
85
|
unsafe_ignore = []
|
76
86
|
ignore = ignore.gsub(/%[\da-fA-F]{2}/) do |hex|
|
@@ -94,13 +104,22 @@ module Artoo
|
|
94
104
|
end
|
95
105
|
end
|
96
106
|
|
97
|
-
#
|
107
|
+
# Route function for get
|
98
108
|
def get(path, &block)
|
99
109
|
route 'GET', path, &block
|
100
110
|
end
|
111
|
+
|
112
|
+
# Route function for get_ws
|
101
113
|
def get_ws(path, &block)
|
102
114
|
route 'GET', path, &block
|
103
115
|
end
|
116
|
+
|
117
|
+
# Route function for post
|
118
|
+
def post(path, &block)
|
119
|
+
route 'POST', path, &block
|
120
|
+
end
|
121
|
+
|
122
|
+
# Route function for put
|
104
123
|
def put(path, &block)
|
105
124
|
route 'PUT', path, &block
|
106
125
|
end
|
data/lib/artoo/basic.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
|
2
1
|
module Artoo
|
2
|
+
# Methods taken from Sinatra codebase
|
3
3
|
module Basic
|
4
|
-
# Taken from Sinatra codebase
|
5
4
|
# Sets an option to the given value. If the value is a proc,
|
6
5
|
# the proc will be called every time the option is accessed.
|
7
6
|
def set(option, value = (not_set = true), ignore_setter = false, &block)
|
@@ -39,7 +38,7 @@ module Artoo
|
|
39
38
|
self
|
40
39
|
end
|
41
40
|
|
42
|
-
#
|
41
|
+
# Callers to ignore
|
43
42
|
CALLERS_TO_IGNORE = [ # :nodoc:
|
44
43
|
/lib\/artoo.*\.rb$/, # artoo code
|
45
44
|
/^\(.*\)$/, # generated code
|
@@ -50,7 +49,6 @@ module Artoo
|
|
50
49
|
/src\/kernel\/bootstrap\/[A-Z]/ # maglev kernel files
|
51
50
|
]
|
52
51
|
|
53
|
-
# Taken from Sinatra codebase
|
54
52
|
# Like Kernel#caller but excluding certain magic entries and without
|
55
53
|
# line / method information; the resulting array contains filenames only.
|
56
54
|
def caller_files
|
@@ -59,16 +57,14 @@ module Artoo
|
|
59
57
|
|
60
58
|
private
|
61
59
|
|
62
|
-
#
|
60
|
+
# Replace with call to singleton_class once we're 1.9 only
|
63
61
|
def define_singleton_method(name, content = Proc.new)
|
64
|
-
# replace with call to singleton_class once we're 1.9 only
|
65
62
|
(class << self; self; end).class_eval do
|
66
63
|
undef_method(name) if method_defined? name
|
67
64
|
String === content ? class_eval("def #{name}() #{content}; end") : define_method(name, &content)
|
68
65
|
end
|
69
66
|
end
|
70
67
|
|
71
|
-
# Taken from Sinatra codebase
|
72
68
|
# Like Kernel#caller but excluding certain magic entries
|
73
69
|
def cleaned_caller(keep = 3)
|
74
70
|
caller(1).
|
data/lib/artoo/connection.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
require 'artoo/utility'
|
2
2
|
|
3
3
|
module Artoo
|
4
|
-
# The Connection class represents the interface to
|
4
|
+
# The Connection class represents the interface to
|
5
5
|
# a specific group of hardware devices. Examples would be an
|
6
6
|
# Arduino, a Sphero, or an ARDrone.
|
7
7
|
class Connection
|
@@ -11,6 +11,12 @@ module Artoo
|
|
11
11
|
|
12
12
|
attr_reader :parent, :name, :port, :adaptor, :connection_id
|
13
13
|
|
14
|
+
# Create new connection
|
15
|
+
# @param [Hash] params
|
16
|
+
# @option params :name [String]
|
17
|
+
# @option params :parent [String]
|
18
|
+
# @option params :adaptor [String]
|
19
|
+
# @option params :port [Integer]
|
14
20
|
def initialize(params={})
|
15
21
|
@connection_id = rand(10000)
|
16
22
|
@name = params[:name].to_s
|
@@ -20,6 +26,8 @@ module Artoo
|
|
20
26
|
require_adaptor(params[:adaptor] || :loopback)
|
21
27
|
end
|
22
28
|
|
29
|
+
# Creates adaptor connection
|
30
|
+
# @return [Boolean]
|
23
31
|
def connect
|
24
32
|
Logger.info "Connecting to '#{name}' on port '#{port}'..."
|
25
33
|
adaptor.connect
|
@@ -28,36 +36,46 @@ module Artoo
|
|
28
36
|
Logger.error e.backtrace.inspect
|
29
37
|
end
|
30
38
|
|
39
|
+
# Closes adaptor connection
|
40
|
+
# @return [Boolean]
|
31
41
|
def disconnect
|
32
42
|
Logger.info "Disconnecting from '#{name}' on port '#{port}'..."
|
33
43
|
adaptor.disconnect
|
34
44
|
end
|
35
45
|
|
46
|
+
# @return [Boolean] Connection status
|
36
47
|
def connected?
|
37
48
|
adaptor.connected?
|
38
49
|
end
|
39
50
|
|
51
|
+
# @return [String] Adaptor class name
|
40
52
|
def adaptor_name
|
41
53
|
adaptor.class.name
|
42
54
|
end
|
43
55
|
|
56
|
+
# @return [Hash] connection
|
44
57
|
def to_hash
|
45
|
-
{
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
58
|
+
{
|
59
|
+
:name => name,
|
60
|
+
:connection_id => connection_id,
|
61
|
+
:port => port.to_s,
|
62
|
+
:adaptor => adaptor_name.to_s.gsub(/^.*::/, ''),
|
63
|
+
:connected => connected?
|
50
64
|
}
|
51
65
|
end
|
52
66
|
|
67
|
+
# @return [JSON] connection
|
53
68
|
def as_json
|
54
69
|
MultiJson.dump(to_hash)
|
55
70
|
end
|
56
71
|
|
72
|
+
# @return [String] Formated connection
|
57
73
|
def inspect
|
58
74
|
"#<Connection @id=#{object_id}, @name='#{name}', @adaptor=#{adaptor_name}>"
|
59
75
|
end
|
60
76
|
|
77
|
+
# Redirects missing methods to adaptor,
|
78
|
+
# attemps reconnection if adaptor not connected
|
61
79
|
def method_missing(method_name, *arguments, &block)
|
62
80
|
unless adaptor.connected?
|
63
81
|
Logger.warn "Cannot call unconnected adaptor '#{name}', attempting to reconnect..."
|
@@ -78,4 +96,4 @@ module Artoo
|
|
78
96
|
@adaptor = constantize("Artoo::Adaptors::#{classify(type.to_s)}").new(:port => port, :parent => current_instance)
|
79
97
|
end
|
80
98
|
end
|
81
|
-
end
|
99
|
+
end
|
data/lib/artoo/delegator.rb
CHANGED
@@ -1,25 +1,15 @@
|
|
1
|
-
|
2
1
|
module Artoo
|
3
2
|
# Execution context for top-level robots
|
4
3
|
# DSL methods executed on main are delegated to this class like Sinatra
|
5
4
|
class MainRobot < Robot
|
6
|
-
|
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
|
5
|
+
set :start_work, false
|
16
6
|
end
|
17
|
-
|
18
|
-
# Artoo delegation mixin that acts like Sinatra.
|
7
|
+
|
8
|
+
# Artoo delegation mixin that acts like Sinatra.
|
19
9
|
# Mixing this module into an object causes all
|
20
|
-
# methods to be delegated to the Artoo::MainRobot class.
|
10
|
+
# methods to be delegated to the Artoo::MainRobot class.
|
21
11
|
# Used primarily at the top-level.
|
22
|
-
module Delegator
|
12
|
+
module Delegator
|
23
13
|
def self.delegate(*methods)
|
24
14
|
methods.each do |method_name|
|
25
15
|
define_method(method_name) do |*args, &block|
|
@@ -46,4 +36,4 @@ module Artoo
|
|
46
36
|
robot.class_eval(&block) if block_given?
|
47
37
|
robot
|
48
38
|
end
|
49
|
-
end
|
39
|
+
end
|
data/lib/artoo/device.rb
CHANGED
@@ -1,13 +1,21 @@
|
|
1
1
|
module Artoo
|
2
|
-
# The Artoo::Device class represents the interface to
|
2
|
+
# The Artoo::Device class represents the interface to
|
3
3
|
# a specific individual hardware devices. Examples would be a digital
|
4
4
|
# thermometer connected to an Arduino, or a Sphero's accelerometer.
|
5
5
|
class Device
|
6
6
|
include Celluloid
|
7
7
|
include Artoo::Utility
|
8
|
-
|
8
|
+
|
9
9
|
attr_reader :parent, :name, :driver, :pin, :connection, :interval
|
10
10
|
|
11
|
+
# Create new device
|
12
|
+
# @param [Hash] params
|
13
|
+
# @option params :name [String]
|
14
|
+
# @option params :pin [String]
|
15
|
+
# @option params :parent [String]
|
16
|
+
# @option params :connection [String]
|
17
|
+
# @option params :interval [String]
|
18
|
+
# @option params :driver [String]
|
11
19
|
def initialize(params={})
|
12
20
|
@name = params[:name].to_s
|
13
21
|
@pin = params[:pin]
|
@@ -18,39 +26,60 @@ module Artoo
|
|
18
26
|
require_driver(params[:driver] || :passthru)
|
19
27
|
end
|
20
28
|
|
29
|
+
# Retrieve connections from parent
|
30
|
+
# @param [String] c connection
|
21
31
|
def determine_connection(c)
|
22
32
|
parent.connections[c] unless c.nil?
|
23
33
|
end
|
24
34
|
|
35
|
+
# @return [Connection] default connection
|
25
36
|
def default_connection
|
26
37
|
parent.default_connection
|
27
38
|
end
|
28
39
|
|
40
|
+
# Starts device driver
|
29
41
|
def start_device
|
30
42
|
driver.start_driver
|
31
43
|
end
|
32
44
|
|
45
|
+
# @return [String] event topic name
|
33
46
|
def event_topic_name(event)
|
34
|
-
"#{parent.safe_name}_#{name}_#{event}"
|
47
|
+
"#{parent.safe_name}_#{name}_#{event}"
|
35
48
|
end
|
36
49
|
|
50
|
+
# @return [Hash] device
|
37
51
|
def to_hash
|
38
|
-
{
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
52
|
+
{
|
53
|
+
:name => name,
|
54
|
+
:driver => driver.class.name.to_s.gsub(/^.*::/, ''),
|
55
|
+
:pin => pin.to_s,
|
56
|
+
:connection => connection.to_hash,
|
57
|
+
:interval => interval,
|
58
|
+
:commands => driver.commands
|
43
59
|
}
|
44
60
|
end
|
45
61
|
|
62
|
+
# @return [JSON] device
|
46
63
|
def as_json
|
47
64
|
MultiJson.dump(to_hash)
|
48
65
|
end
|
49
66
|
|
67
|
+
# @return [Collection] commands
|
68
|
+
def commands
|
69
|
+
driver.commands
|
70
|
+
end
|
71
|
+
|
72
|
+
# Execute driver command
|
73
|
+
def command(method_name, *arguments, &block)
|
74
|
+
driver.command(method_name, *arguments)
|
75
|
+
end
|
76
|
+
|
77
|
+
# Sends missing methods to command
|
50
78
|
def method_missing(method_name, *arguments, &block)
|
51
|
-
|
79
|
+
command(method_name, *arguments, &block)
|
52
80
|
end
|
53
81
|
|
82
|
+
# @return [String] pretty inspect
|
54
83
|
def inspect
|
55
84
|
"#<Device @id=#{object_id}, @name='name', @driver='driver'>"
|
56
85
|
end
|
@@ -62,4 +91,4 @@ module Artoo
|
|
62
91
|
@driver = constantize("Artoo::Drivers::#{classify(d.to_s)}").new(:parent => current_instance)
|
63
92
|
end
|
64
93
|
end
|
65
|
-
end
|
94
|
+
end
|
@@ -9,6 +9,9 @@ module Artoo
|
|
9
9
|
|
10
10
|
attr_reader :topic
|
11
11
|
|
12
|
+
# Create new event client
|
13
|
+
# @param [Socket] websocket
|
14
|
+
# @param [String] topic
|
12
15
|
def initialize(websocket, topic)
|
13
16
|
@topic = topic
|
14
17
|
info "Streaming #{@topic} to websocket..."
|
@@ -16,6 +19,9 @@ module Artoo
|
|
16
19
|
subscribe(@topic, :notify_event)
|
17
20
|
end
|
18
21
|
|
22
|
+
# Event notification
|
23
|
+
# @param [String] topic
|
24
|
+
# @param [Object] data
|
19
25
|
def notify_event(topic, *data)
|
20
26
|
# TODO: send which topic sent the notification
|
21
27
|
@socket << data.last.to_s
|