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/robot.rb
CHANGED
@@ -26,50 +26,58 @@ module Artoo
|
|
26
26
|
|
27
27
|
attr_reader :connections, :devices, :name
|
28
28
|
|
29
|
+
# Create new robot
|
30
|
+
# @param [Hash] params
|
31
|
+
# @option params [String] :name
|
32
|
+
# @option params [Collection] :connections
|
33
|
+
# @option params [Collection] :devices
|
29
34
|
def initialize(params={})
|
30
35
|
@name = params[:name] || "Robot #{random_string}"
|
31
36
|
initialize_connections(params[:connections] || {})
|
32
37
|
initialize_devices(params[:devices] || {})
|
33
38
|
end
|
34
|
-
|
39
|
+
|
35
40
|
class << self
|
36
41
|
attr_accessor :device_types, :working_code, :running,
|
37
42
|
:use_api, :api_host, :api_port
|
38
|
-
|
43
|
+
|
39
44
|
def connection_types
|
40
45
|
@@connection_types ||= []
|
41
46
|
end
|
42
47
|
|
43
|
-
#
|
44
|
-
#
|
45
|
-
#
|
48
|
+
# Connection to some hardware that has one or more devices via some specific protocol
|
49
|
+
# @example connection :arduino, :adaptor => :firmata, :port => '/dev/tty.usbmodemxxxxx'
|
50
|
+
# @param [String] name
|
51
|
+
# @param [Hash] params
|
46
52
|
def connection(name, params = {})
|
47
53
|
Celluloid::Logger.info "Registering connection '#{name}'..."
|
48
54
|
self.connection_types << {:name => name}.merge(params)
|
49
55
|
end
|
50
56
|
|
51
|
-
#
|
52
|
-
#
|
53
|
-
#
|
57
|
+
# Device that uses a connection to communicate
|
58
|
+
# @example device :collision_detect, :driver => :switch, :pin => 3
|
59
|
+
# @param [String] name
|
60
|
+
# @param [Hash] params
|
54
61
|
def device(name, params = {})
|
55
62
|
Celluloid::Logger.info "Registering device '#{name}'..."
|
56
63
|
self.device_types ||= []
|
57
64
|
self.device_types << {:name => name}.merge(params)
|
58
65
|
end
|
59
66
|
|
60
|
-
#
|
61
|
-
#
|
62
|
-
# work do
|
67
|
+
# The work that needs to be performed
|
68
|
+
# @example
|
69
|
+
# work do
|
63
70
|
# every(10.seconds) do
|
64
71
|
# puts "hello, world"
|
65
72
|
# end
|
66
73
|
# end
|
74
|
+
# @param [block] work
|
67
75
|
def work(&block)
|
68
76
|
Celluloid::Logger.info "Preparing work..."
|
69
77
|
self.working_code = block if block_given?
|
70
78
|
end
|
71
79
|
|
72
|
-
#
|
80
|
+
# Activate RESTful api
|
73
81
|
# Example:
|
74
82
|
# api :host => '127.0.0.1', :port => '1234'
|
75
83
|
def api(params = {})
|
@@ -79,10 +87,11 @@ module Artoo
|
|
79
87
|
self.api_port = params[:port] || '4321'
|
80
88
|
end
|
81
89
|
|
82
|
-
#
|
90
|
+
# Work can be performed by either:
|
83
91
|
# an existing instance
|
84
92
|
# an array of existing instances
|
85
93
|
# or, a new instance can be created
|
94
|
+
# @param [Robot] robot
|
86
95
|
def work!(robot=nil)
|
87
96
|
return if !test? && is_running?
|
88
97
|
prepare_robots(robot)
|
@@ -95,6 +104,7 @@ module Artoo
|
|
95
104
|
end
|
96
105
|
end
|
97
106
|
|
107
|
+
# Prepare master robots for work
|
98
108
|
def prepare_robots(robot=nil)
|
99
109
|
if robot.respond_to?(:work)
|
100
110
|
robots = [robot]
|
@@ -107,32 +117,39 @@ module Artoo
|
|
107
117
|
Celluloid::Actor[:master] = Master.new(robots)
|
108
118
|
end
|
109
119
|
|
120
|
+
# Master actor
|
110
121
|
def master
|
111
122
|
Celluloid::Actor[:master]
|
112
123
|
end
|
113
124
|
|
125
|
+
# @return [Boolean] True if test env
|
114
126
|
def test?
|
115
127
|
ENV["ARTOO_TEST"] == 'true'
|
116
128
|
end
|
117
129
|
|
130
|
+
# @return [Boolean] True if cli env
|
118
131
|
def cli?
|
119
132
|
ENV["ARTOO_CLI"] == 'true'
|
120
133
|
end
|
121
134
|
|
135
|
+
# @return [Boolean] True if it's running
|
122
136
|
def is_running?
|
123
137
|
self.running ||= false
|
124
138
|
self.running == true
|
125
139
|
end
|
126
140
|
end
|
127
141
|
|
142
|
+
# @return [String] Name without spaces and downcased
|
128
143
|
def safe_name
|
129
144
|
name.gsub(' ', '_').downcase
|
130
145
|
end
|
131
146
|
|
147
|
+
# @return [String] Api Host
|
132
148
|
def api_host
|
133
149
|
self.class.api_host
|
134
150
|
end
|
135
151
|
|
152
|
+
# @return [String] Api Port
|
136
153
|
def api_port
|
137
154
|
self.class.api_port
|
138
155
|
end
|
@@ -145,48 +162,59 @@ module Artoo
|
|
145
162
|
execute_working_code
|
146
163
|
end
|
147
164
|
|
165
|
+
# pause the work
|
148
166
|
def pause_work
|
149
167
|
Logger.info "Pausing work..."
|
150
168
|
current_instance.timers.pause
|
151
169
|
end
|
152
170
|
|
171
|
+
# continue with the work
|
153
172
|
def continue_work
|
154
173
|
Logger.info "Continuing work..."
|
155
174
|
current_instance.timers.continue
|
156
175
|
end
|
157
176
|
|
177
|
+
# Terminate all connections
|
158
178
|
def disconnect
|
159
179
|
connections.each {|k, c| c.async.disconnect}
|
160
180
|
end
|
161
181
|
|
182
|
+
# @return [Connection] default connection
|
162
183
|
def default_connection
|
163
184
|
connections.values.first
|
164
185
|
end
|
165
186
|
|
187
|
+
# @return [Collection] connection types
|
166
188
|
def connection_types
|
167
189
|
current_class.connection_types
|
168
190
|
end
|
169
191
|
|
192
|
+
# @return [Collection] device types
|
170
193
|
def device_types
|
171
194
|
current_class.device_types ||= []
|
172
195
|
current_class.device_types
|
173
196
|
end
|
174
197
|
|
198
|
+
# @return [Proc] current working code
|
175
199
|
def working_code
|
176
200
|
current_class.working_code ||= proc {puts "No work defined."}
|
177
201
|
end
|
178
|
-
|
202
|
+
|
203
|
+
# @return [Hash] robot
|
179
204
|
def to_hash
|
180
|
-
{
|
181
|
-
|
182
|
-
|
205
|
+
{
|
206
|
+
:name => name,
|
207
|
+
:connections => connections.each_value.collect {|c|c.to_hash},
|
208
|
+
:devices => devices.each_value.collect {|d|d.to_hash}
|
183
209
|
}
|
184
210
|
end
|
185
211
|
|
212
|
+
# @return [JSON] robot
|
186
213
|
def as_json
|
187
214
|
MultiJson.dump(to_hash)
|
188
215
|
end
|
189
216
|
|
217
|
+
# @return [String] robot
|
190
218
|
def inspect
|
191
219
|
"#<Robot #{object_id}>"
|
192
220
|
end
|
data/lib/artoo/utility.rb
CHANGED
@@ -1,38 +1,82 @@
|
|
1
|
-
require 'active_support/inflector'
|
2
|
-
|
3
1
|
module Artoo
|
2
|
+
# Utility methods used for convertions
|
4
3
|
module Utility
|
4
|
+
|
5
|
+
# Converts camel_cased_word to constant
|
6
|
+
# @example "CamelCasedWord" > CamelCasedWord
|
7
|
+
# @return [Constant] Converted constant
|
5
8
|
def constantize(camel_cased_word)
|
6
|
-
|
9
|
+
names = camel_cased_word.split('::')
|
10
|
+
names.shift if names.empty? || names.first.empty?
|
11
|
+
|
12
|
+
names.inject(Object) do |constant, name|
|
13
|
+
if constant == Object
|
14
|
+
constant.const_get(name)
|
15
|
+
else
|
16
|
+
candidate = constant.const_get(name)
|
17
|
+
next candidate if constant.const_defined?(name, false)
|
18
|
+
next candidate unless Object.const_defined?(name)
|
19
|
+
|
20
|
+
# Go down the ancestors to check it it's owned
|
21
|
+
# directly before we reach Object or the end of ancestors.
|
22
|
+
constant = constant.ancestors.inject do |const, ancestor|
|
23
|
+
break const if ancestor == Object
|
24
|
+
break ancestor if ancestor.const_defined?(name, false)
|
25
|
+
const
|
26
|
+
end
|
27
|
+
|
28
|
+
# owner is in Object, so raise
|
29
|
+
constant.const_get(name, false)
|
30
|
+
end
|
31
|
+
end
|
7
32
|
end
|
8
33
|
|
9
|
-
|
10
|
-
|
34
|
+
# Convert a underscore string to a class
|
35
|
+
# @example "underscore_word" > UnderscoreWord
|
36
|
+
# @return [Class] converted class
|
37
|
+
def classify(word)
|
38
|
+
underscore(word).gsub(/\/(.?)/) { "::#{$1.upcase}" }.gsub(/(?:^|_)(.)/) { $1.upcase }
|
11
39
|
end
|
12
40
|
|
41
|
+
# @return [String] random string
|
13
42
|
def random_string
|
14
43
|
(0...8).map{65.+(rand(26)).chr}.join
|
15
44
|
end
|
16
45
|
|
46
|
+
# Converts camel cased word to downcased with undercores
|
47
|
+
# @example CamelCase > camel_case
|
48
|
+
def underscore(camel_cased_word)
|
49
|
+
word = camel_cased_word.to_s.dup
|
50
|
+
word.gsub!(/::/, '/')
|
51
|
+
word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
52
|
+
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
53
|
+
word.tr!("-", "_")
|
54
|
+
word.downcase!
|
55
|
+
word
|
56
|
+
end
|
57
|
+
|
58
|
+
# @return [Celluloid::Actor] current instance
|
17
59
|
def current_instance
|
18
60
|
Celluloid::Actor.current
|
19
61
|
end
|
20
62
|
|
63
|
+
# @return [Class] current actor class
|
21
64
|
def current_class
|
22
65
|
Celluloid::Actor.current.class
|
23
66
|
end
|
24
67
|
end
|
25
68
|
end
|
26
69
|
|
27
|
-
#
|
28
|
-
# Example:
|
29
|
-
# 20.seconds => 20
|
30
|
-
# 1.second => 1
|
70
|
+
# Adds syntactic suger for seconds
|
31
71
|
class Integer < Numeric
|
72
|
+
# @return [Integer] Plain number, actually does nothing
|
73
|
+
# @example 20.seconds => 20
|
32
74
|
def seconds
|
33
75
|
return self
|
34
76
|
end
|
35
77
|
|
78
|
+
# @return [Integer] Plain number, actually does nothing
|
79
|
+
# @example 1.second => 1
|
36
80
|
def second
|
37
81
|
return self
|
38
82
|
end
|
data/lib/artoo/version.rb
CHANGED
data/test/drivers/driver_test.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + "/../test_helper")
|
2
2
|
require 'artoo/drivers/driver'
|
3
3
|
|
4
|
+
class Awesome < Artoo::Drivers::Driver
|
5
|
+
COMMANDS = [:awesome].freeze
|
6
|
+
end
|
7
|
+
|
4
8
|
describe Artoo::Drivers::Driver do
|
5
9
|
before do
|
6
10
|
@device = mock('device')
|
@@ -18,4 +22,15 @@ describe Artoo::Drivers::Driver do
|
|
18
22
|
@device.expects(:pin).returns(@pin)
|
19
23
|
@driver.pin.must_equal @pin
|
20
24
|
end
|
25
|
+
|
26
|
+
it 'Driver#commands' do
|
27
|
+
@awesome_driver = Awesome.new(:parent => @device)
|
28
|
+
@awesome_driver.commands.first.must_equal :awesome
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'Driver#known_command?' do
|
32
|
+
@awesome_driver = Awesome.new(:parent => @device)
|
33
|
+
@awesome_driver.known_command?(:awesome).must_equal true
|
34
|
+
@awesome_driver.known_command?(:crazy).must_equal false
|
35
|
+
end
|
21
36
|
end
|
data/test/drivers/led_test.rb
CHANGED
data/test/utility_test.rb
CHANGED
@@ -1,27 +1,44 @@
|
|
1
1
|
require File.expand_path(File.dirname(__FILE__) + "/test_helper")
|
2
|
-
|
3
|
-
class UtilityTestRobot < Artoo::Robot
|
4
|
-
connection :test_connection
|
5
|
-
device :test_device_1
|
6
|
-
device :test_device_2
|
7
|
-
end
|
2
|
+
require File.expand_path(File.dirname(__FILE__) + "/utility_test_cases")
|
8
3
|
|
9
4
|
describe Artoo::Utility do
|
10
|
-
|
11
|
-
|
5
|
+
include UtilityTestCases
|
6
|
+
include Artoo::Utility
|
7
|
+
|
8
|
+
describe "#random_string" do
|
9
|
+
it "returns an 8-character String" do
|
10
|
+
random_string.must_be_kind_of String
|
11
|
+
random_string.size.must_equal 8
|
12
|
+
end
|
12
13
|
end
|
13
14
|
|
14
|
-
|
15
|
-
string
|
16
|
-
|
17
|
-
|
15
|
+
describe "#classify" do
|
16
|
+
it "converts a snake_case string to CamelCase" do
|
17
|
+
classify("firmata").must_equal "Firmata"
|
18
|
+
classify("ardrone_awesome").must_equal "ArdroneAwesome"
|
19
|
+
|
20
|
+
CamelToUnderscore.each do |camel, underscore|
|
21
|
+
classify(underscore).must_equal camel
|
22
|
+
end
|
23
|
+
end
|
18
24
|
end
|
19
25
|
|
20
|
-
|
21
|
-
|
22
|
-
|
26
|
+
describe "#underscore" do
|
27
|
+
it "converts CamelCase strings to snake_case" do
|
28
|
+
CamelToUnderscore.each do |camel, underscore|
|
29
|
+
underscore(camel).must_equal underscore
|
30
|
+
end
|
23
31
|
|
24
|
-
|
25
|
-
|
32
|
+
underscore("HTMLTidy").must_equal "html_tidy"
|
33
|
+
underscore("HTMLTidyGenerator").must_equal "html_tidy_generator"
|
34
|
+
end
|
26
35
|
end
|
27
|
-
|
36
|
+
|
37
|
+
describe "#constantize" do
|
38
|
+
it "converts strings to constants" do
|
39
|
+
run_constantize_tests_on do |string|
|
40
|
+
constantize(string)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Ace
|
2
|
+
module Base
|
3
|
+
class Case
|
4
|
+
class Dice
|
5
|
+
end
|
6
|
+
end
|
7
|
+
class Fase < Case
|
8
|
+
end
|
9
|
+
end
|
10
|
+
class Gas
|
11
|
+
include Base
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Object
|
16
|
+
module AddtlGlobalConstants
|
17
|
+
class Case
|
18
|
+
class Dice
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
include AddtlGlobalConstants
|
23
|
+
end
|
24
|
+
|
25
|
+
module UtilityTestCases
|
26
|
+
def run_constantize_tests_on
|
27
|
+
assert_equal Ace::Base::Case, yield("Ace::Base::Case")
|
28
|
+
assert_equal Ace::Base::Case, yield("::Ace::Base::Case")
|
29
|
+
assert_equal Ace::Base::Case::Dice, yield("Ace::Base::Case::Dice")
|
30
|
+
assert_equal Ace::Base::Fase::Dice, yield("Ace::Base::Fase::Dice")
|
31
|
+
assert_equal Ace::Gas::Case, yield("Ace::Gas::Case")
|
32
|
+
assert_equal Ace::Gas::Case::Dice, yield("Ace::Gas::Case::Dice")
|
33
|
+
assert_equal Case::Dice, yield("Case::Dice")
|
34
|
+
assert_equal Case::Dice, yield("Object::Case::Dice")
|
35
|
+
assert_equal UtilityTestCases, yield("UtilityTestCases")
|
36
|
+
assert_equal UtilityTestCases, yield("::UtilityTestCases")
|
37
|
+
assert_equal Object, yield("")
|
38
|
+
assert_equal Object, yield("::")
|
39
|
+
assert_raises(NameError) { yield("UnknownClass") }
|
40
|
+
assert_raises(NameError) { yield("UnknownClass::Ace") }
|
41
|
+
assert_raises(NameError) { yield("UnknownClass::Ace::Base") }
|
42
|
+
assert_raises(NameError) { yield("An invalid string") }
|
43
|
+
assert_raises(NameError) { yield("InvalidClass\n") }
|
44
|
+
assert_raises(NameError) { yield("Ace::UtilityTestCases") }
|
45
|
+
assert_raises(NameError) { yield("Ace::Base::UtilityTestCases") }
|
46
|
+
assert_raises(NameError) { yield("Ace::Gas::Base") }
|
47
|
+
assert_raises(NameError) { yield("Ace::Gas::UtilityTestCases") }
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
CamelToUnderscore = {
|
52
|
+
"Product" => "product",
|
53
|
+
"SpecialGuest" => "special_guest",
|
54
|
+
"ApplicationController" => "application_controller",
|
55
|
+
"Area51Controller" => "area51_controller"
|
56
|
+
}
|