artoo 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
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