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.
Files changed (53) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/.yardopts +10 -0
  4. data/Gemfile +9 -0
  5. data/Gemfile.lock +10 -7
  6. data/README.md +2 -2
  7. data/api/assets/javascripts/artoo/controllers/robot.js.coffee +2 -1
  8. data/api/public/core.js +3 -1
  9. data/artoo.gemspec +0 -1
  10. data/lib/artoo/adaptors/adaptor.rb +27 -6
  11. data/lib/artoo/adaptors/ardrone.rb +10 -2
  12. data/lib/artoo/adaptors/ardrone_navigation.rb +9 -2
  13. data/lib/artoo/adaptors/ardrone_video.rb +13 -5
  14. data/lib/artoo/adaptors/firmata.rb +10 -2
  15. data/lib/artoo/adaptors/loopback.rb +2 -1
  16. data/lib/artoo/adaptors/roomba.rb +12 -4
  17. data/lib/artoo/adaptors/sphero.rb +15 -3
  18. data/lib/artoo/api.rb +48 -5
  19. data/lib/artoo/api_route_helpers.rb +22 -3
  20. data/lib/artoo/basic.rb +3 -7
  21. data/lib/artoo/connection.rb +25 -7
  22. data/lib/artoo/delegator.rb +6 -16
  23. data/lib/artoo/device.rb +39 -10
  24. data/lib/artoo/device_event_client.rb +6 -0
  25. data/lib/artoo/drivers/ardrone.rb +5 -1
  26. data/lib/artoo/drivers/ardrone_navigation.rb +5 -1
  27. data/lib/artoo/drivers/ardrone_video.rb +9 -4
  28. data/lib/artoo/drivers/button.rb +7 -1
  29. data/lib/artoo/drivers/driver.rb +44 -4
  30. data/lib/artoo/drivers/led.rb +12 -1
  31. data/lib/artoo/drivers/motor.rb +12 -1
  32. data/lib/artoo/drivers/pinger.rb +10 -1
  33. data/lib/artoo/drivers/pinger2.rb +10 -1
  34. data/lib/artoo/drivers/roomba.rb +65 -17
  35. data/lib/artoo/drivers/servo.rb +12 -2
  36. data/lib/artoo/drivers/sphero.rb +19 -5
  37. data/lib/artoo/drivers/wiichuck.rb +7 -1
  38. data/lib/artoo/drivers/wiiclassic.rb +14 -5
  39. data/lib/artoo/drivers/wiidriver.rb +4 -1
  40. data/lib/artoo/events.rb +11 -4
  41. data/lib/artoo/ext/actor.rb +1 -1
  42. data/lib/artoo/ext/timers.rb +1 -1
  43. data/lib/artoo/main.rb +2 -13
  44. data/lib/artoo/master.rb +20 -2
  45. data/lib/artoo/port.rb +8 -3
  46. data/lib/artoo/robot.rb +45 -17
  47. data/lib/artoo/utility.rb +53 -9
  48. data/lib/artoo/version.rb +1 -1
  49. data/test/drivers/driver_test.rb +15 -0
  50. data/test/drivers/led_test.rb +4 -0
  51. data/test/utility_test.rb +35 -18
  52. data/test/utility_test_cases.rb +56 -0
  53. metadata +5 -16
@@ -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.get_robot(@params['robotid']).as_json
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.get_robot_devices(@params['robotid']).each_value.collect {|d| d.to_hash})
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.get_robot_connections(@params['robotid']).each_value.collect {|c| c.to_hash})
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.get_robot_connection(@params['robotid'], @params['connectionid']).as_json
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.get_robot_device(robot_id, device_id)
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
- ## Ripped from Sinatra 'cause it's so sexy in there
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
- # Helper route functions
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
@@ -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
- # Taken from Sinatra codebase
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
- # Taken from Sinatra codebase
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).
@@ -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
- {:name => name,
46
- :connection_id => connection_id,
47
- :port => port.to_s,
48
- :adaptor => adaptor_name.demodulize,
49
- :connected => connected?
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
@@ -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
- #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
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 #:nodoc:
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
@@ -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
- {:name => name,
39
- :driver => driver.class.name.demodulize,
40
- :pin => pin.to_s,
41
- :connection => connection.to_hash,
42
- :interval => interval
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
- driver.send(method_name, *arguments, &block)
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