artoo 0.4.1 → 0.5.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/api/public/core.js CHANGED
@@ -234,6 +234,17 @@ ngChange:rd,required:dc,ngRequired:dc,ngValue:ud}).directive(lb).directive(ec);a
234
234
  return device.console();
235
235
  });
236
236
  };
237
+ $scope.executeCommand = function(deviceId, command) {
238
+ var params, post_params;
239
+ params = $("#appendedDropdownButton").val();
240
+ post_params = {};
241
+ if (params !== "") {
242
+ post_params = "{\"params\": [" + params + "]}";
243
+ }
244
+ return $http.post('/robots/' + $scope.robot.name + "/devices/" + deviceId + "/commands/" + command, post_params).success(function(data) {
245
+ return true;
246
+ });
247
+ };
237
248
  $scope.driverHasOutput = function(driverId) {
238
249
  if ($.inArray(driverId, window.driversWithOutput) !== -1) {
239
250
  return true;
@@ -25,7 +25,7 @@
25
25
  <div class="span2">
26
26
  <b><span class="name">{{deviceDetail.name}}</span></b>
27
27
  </div>
28
- <div class="span10">
28
+ <div class="span5 pull-right">
29
29
  <b>
30
30
  <dl class="controls">
31
31
  <span ng-show='deviceDetail.pin'>
@@ -42,9 +42,23 @@
42
42
  </span>
43
43
  <span ng-show='deviceDetail.connection'>
44
44
  <dt><i class="icon-circle" ng-class="isConnected(deviceDetail.connection)"></i> </dt>
45
- <dd ng-show="deviceDetail.connection.name">{{deviceDetail.connection.name}}</dd>
45
+ <dd ng-show="deviceDetail.connection.name">{{deviceDetail.connection.name}}</dd>
46
46
  </span>
47
47
  </dl>
48
+ <div class="input-prepend">
49
+ <div class="commands btn-group" >
50
+ <a class="btn dropdown-toggle btn-mini" data-toggle="dropdown" href="#">
51
+ Commands
52
+ <span class="caret"></span>
53
+ </a>
54
+ <ul class="dropdown-menu">
55
+ <div ng-repeat="command in deviceDetail.commands">
56
+ <li><a ng-click="executeCommand(deviceDetail.name, command)">{{command }}</a></li>
57
+ </div>
58
+ </ul>
59
+ </div>
60
+ <input id="appendedDropdownButton" type="text">
61
+ </div>
48
62
  </b>
49
63
  </div>
50
64
  </div>
data/artoo.gemspec CHANGED
@@ -25,6 +25,7 @@ Gem::Specification.new do |s|
25
25
  s.add_runtime_dependency 'multi_json', '~> 1.6'
26
26
  s.add_runtime_dependency 'rake', '~> 10.0'
27
27
  s.add_runtime_dependency 'pry', '~> 0.9'
28
- s.add_development_dependency 'minitest', '~> 4.6'
29
- s.add_development_dependency 'mocha', '~> 0.13'
28
+ s.add_development_dependency 'minitest', '~> 5.0'
29
+ s.add_development_dependency 'minitest-happy'
30
+ s.add_development_dependency 'mocha', '~> 0.14.0.alpha'
30
31
  end
@@ -0,0 +1,25 @@
1
+ #!/bin/bash
2
+ #
3
+ # This will create a sphero connection bound to /dev/Sphero-XXX
4
+ #
5
+ # Requires sudo or ran as root
6
+ #
7
+ # $1 = unbound /dev/rfcommXX
8
+ # $2 = sphero hardware address from hcitool scan
9
+ # $3 = sphero three letter color code
10
+ #
11
+ # Optional parameter
12
+ # $4 = bluetooth radio hcix
13
+ #
14
+ # Example
15
+ #
16
+ # sudo ./sphero_linux_bind.sh 1 00:06:66:4A:43:23 PYG hci1
17
+ #
18
+ #
19
+ if [ -z "$4" ]; then
20
+ addr="hci0"
21
+ else
22
+ addr=$4
23
+ fi
24
+ rfcomm -i $addr bind /dev/rfcomm$1 $2 1
25
+ ln -s /dev/rfcomm$1 /dev/Sphero-$3
@@ -6,14 +6,21 @@ device :drone, :driver => :ardrone, :connection => :ardrone
6
6
  connection :arduino, :adaptor => :firmata, :port => "8023"
7
7
  device :classic, :driver => :wiiclassic, :connection => :arduino, :interval => 0.1
8
8
 
9
- work do
10
- init_settings
9
+ OFFSETS = {
10
+ :ry => 12.0,
11
+ :ly => 27.0,
12
+ :lx => 27.0,
13
+ :rt => 27.0,
14
+ :lt => 12.0
15
+ }
16
+ @toggle_camera = 0
11
17
 
18
+ work do
12
19
  on classic, :a_button => proc { drone.take_off }
13
20
  on classic, :b_button => proc { drone.hover }
14
21
  on classic, :x_button => proc { drone.land }
15
22
  on classic, :y_button => proc {
16
- if @toggle_camera == 0
23
+ unless @toggle_camera
17
24
  drone.bottom_camera
18
25
  @toggle_camera = 1
19
26
  else
@@ -28,17 +35,17 @@ work do
28
35
  on classic, :left_joystick => proc { |*value|
29
36
  pair = value[1]
30
37
  if pair[:y] > 0
31
- drone.forward(validate_pitch(pair[:y], @offsets[:ly]))
38
+ drone.forward(validate_pitch(pair[:y], OFFSETS[:ly]))
32
39
  elsif pair[:y] < 0
33
- drone.backward(validate_pitch(pair[:y], @offsets[:ly]))
40
+ drone.backward(validate_pitch(pair[:y], OFFSETS[:ly]))
34
41
  else
35
42
  drone.forward(0.0)
36
43
  end
37
44
 
38
45
  if pair[:x] > 0
39
- drone.right(validate_pitch(pair[:x], @offsets[:lx]))
46
+ drone.right(validate_pitch(pair[:x], OFFSETS[:lx]))
40
47
  elsif pair[:x] < 0
41
- drone.left(validate_pitch(pair[:x], @offsets[:lx]))
48
+ drone.left(validate_pitch(pair[:x], OFFSETS[:lx]))
42
49
  else
43
50
  drone.right(0.0)
44
51
  end
@@ -47,9 +54,9 @@ work do
47
54
  on classic, :right_joystick => proc { |*value|
48
55
  pair = value[1]
49
56
  if pair[:y] > 0
50
- drone.up(validate_pitch(pair[:y], @offsets[:ry]))
57
+ drone.up(validate_pitch(pair[:y], OFFSETS[:ry]))
51
58
  elsif pair[:y] < 0
52
- drone.down(validate_pitch(pair[:y], @offsets[:ry]))
59
+ drone.down(validate_pitch(pair[:y], OFFSETS[:ry]))
53
60
  else
54
61
  drone.up(0.0)
55
62
  end
@@ -57,7 +64,7 @@ work do
57
64
 
58
65
  on classic, :right_trigger => proc { |*value|
59
66
  if value[1] > 0
60
- drone.turn_right(validate_pitch(value[1], @offsets[:rt]))
67
+ drone.turn_right(validate_pitch(value[1], OFFSETS[:rt]))
61
68
  else
62
69
  drone.turn_right(0.0)
63
70
  end
@@ -65,22 +72,11 @@ work do
65
72
 
66
73
  on classic, :left_trigger => proc { |*value|
67
74
  if value[1] > 0
68
- drone.turn_left(validate_pitch(value[1], @offsets[:lt]))
75
+ drone.turn_left(validate_pitch(value[1], OFFSETS[:lt]))
69
76
  end
70
77
  }
71
78
  end
72
79
 
73
- def init_settings
74
- @toggle_camera = 0
75
- @offsets = {
76
- :ry => 12.0,
77
- :ly => 27.0,
78
- :lx => 27.0,
79
- :rt => 27.0,
80
- :lt => 12.0
81
- }
82
- end
83
-
84
80
  def validate_pitch(data, offset)
85
81
  value = data.abs / offset
86
82
  value >= 0.1 ? (value <= 1.0 ? value.round(2) : 1.0) : 0.0
@@ -0,0 +1,7 @@
1
+ Sphero-BWY: bash ../bin/sphero_linux_socat.sh 4560 Sphero-BWY
2
+ Sphero-GOB: bash ../bin/sphero_linux_socat.sh 4561 Sphero-GOB
3
+ Sphero-PGB: bash ../bin/sphero_linux_socat.sh 4562 Sphero-PGB
4
+ Sphero-PYG: bash ../bin/sphero_linux_socat.sh 4563 Sphero-PYG
5
+ Sphero-WRW: bash ../bin/sphero_linux_socat.sh 4564 Sphero-WRW
6
+ Sphero-WWW: bash ../bin/sphero_linux_socat.sh 4565 Sphero-WWW
7
+ Sphero-YBW: bash ../bin/sphero_linux_socat.sh 4566 Sphero-YBW
@@ -9,14 +9,18 @@ class ConwaySpheroRobot < Artoo::Robot
9
9
  work do
10
10
  birth
11
11
 
12
+ on sphero, :collision => proc { contact }
13
+
12
14
  every(3.seconds) { movement if alive? }
13
15
  every(10.seconds) { birthday if alive? }
14
16
  end
15
17
 
16
18
  def alive?; (@alive == true); end
19
+ def reset_contacts; @contacts = 0; end
20
+ def contact; @contacts += 1; end
17
21
 
18
22
  def birth
19
- sphero.detect_collisions
23
+ reset_contacts
20
24
  @age = 0
21
25
  life
22
26
  movement
@@ -37,12 +41,11 @@ class ConwaySpheroRobot < Artoo::Robot
37
41
 
38
42
  def birthday
39
43
  @age += 1
40
- contacts = sphero.collisions.size
41
- sphero.clear_collisions
42
44
 
43
- puts "Happy birthday, #{name}, you are #{@age} and had #{contacts} contacts."
45
+ puts "Happy birthday, #{name}, you are #{@age} and had #{@contacts} contacts."
44
46
  #return if @age <= 3
45
- death unless contacts >= 3 && contacts < 5
47
+ death unless @contacts >= 3 && @contacts < 5
48
+ reset_contacts
46
49
  end
47
50
 
48
51
  def movement
@@ -4,19 +4,10 @@ connection :sphero, :adaptor => :sphero, :port => '127.0.0.1:4560'
4
4
  device :sphero, :driver => :sphero
5
5
 
6
6
  work do
7
- puts "Configuring..."
8
- sphero.detect_collisions
7
+ on sphero, :collision => proc { puts "Collision!" }
9
8
 
10
9
  every(3.seconds) do
11
10
  puts "Rolling..."
12
11
  sphero.roll 90, rand(360)
13
- unless sphero.collisions.empty?
14
- puts "----------"
15
- sphero.collisions.each do |c|
16
- puts c
17
- end
18
- puts "=========="
19
- sphero.async_messages.clear
20
- end
21
12
  end
22
13
  end
data/lib/artoo/api.rb CHANGED
@@ -53,7 +53,7 @@ module Artoo
53
53
  MultiJson.dump(device(@params['robotid'], @params['deviceid']).commands)
54
54
  end
55
55
 
56
- # Retrieve robot command
56
+ # Execute robot command
57
57
  # @return [JSON] command
58
58
  post '/robots/:robotid/devices/:deviceid/commands/:commandid' do
59
59
  result = device(@params['robotid'], @params['deviceid']).command(@params['commandid'], command_params)
@@ -91,8 +91,8 @@ module Artoo
91
91
 
92
92
  def command_params
93
93
  data = MultiJson.load(@req.body, :symbolize_keys => true)
94
- if data
95
- data[:params]
94
+ if data && params = data[:params]
95
+ params.size == 1 ? params.first : params
96
96
  else
97
97
  nil
98
98
  end
@@ -5,6 +5,8 @@ module Artoo
5
5
  # Ardrone driver behaviors
6
6
  # @see https://github.com/hybridgroup/argus/blob/master/lib/argus/drone.rb Argus::Drone docs for supported actions
7
7
  class Ardrone < Driver
8
+ COMMANDS = [:start, :stop, :hover, :land, :take_off, :emergency, :front_camera, :bottom_camera, :up, :down, :left, :right, :forward, :backward, :turn_left, :turn_right].freeze
9
+
8
10
  def start
9
11
  connection.start(false) # send false, so Argus does not use NavMonitor
10
12
  end
@@ -17,18 +17,31 @@ module Artoo
17
17
  # Sets values to read and write from button
18
18
  # and starts driver
19
19
  def start_driver
20
- listener = ->(value) { update(value) }
21
- connection.on("digital-read-#{pin}", listener)
22
20
  connection.set_pin_mode(pin, Firmata::Board::INPUT)
23
21
  connection.toggle_pin_reporting(pin)
24
22
 
25
23
  every(interval) do
26
24
  connection.read_and_process
25
+ handle_events
27
26
  end
28
27
 
29
28
  super
30
29
  end
31
30
 
31
+ def handle_events
32
+ while i = find_event("digital-read-#{pin}") do
33
+ update(events.slice!(i).data.first)
34
+ end
35
+ end
36
+
37
+ def find_event(name)
38
+ events.index {|e| e.name == name}
39
+ end
40
+
41
+ def events
42
+ connection.async_events
43
+ end
44
+
32
45
  # Publishes events according to the button feedback
33
46
  def update(value)
34
47
  if value == DOWN
@@ -5,7 +5,7 @@ module Artoo
5
5
  # The LED driver behaviors
6
6
  class Led < Driver
7
7
 
8
- COMMANDS = [:is_on?, :is_off?, :on, :off, :toggle, :brightness].freeze
8
+ COMMANDS = [:on, :off, :toggle, :brightness].freeze
9
9
 
10
10
  # @return [Boolean] True if on
11
11
  def is_on?
@@ -14,6 +14,34 @@ module Artoo
14
14
  COMMANDS = [:detect_collisions, :clear_collisions, :collisions,
15
15
  :power_notifications, :sensor_data, :set_color, :color].freeze
16
16
 
17
+ # Starts drives and required connections
18
+ def start_driver
19
+ begin
20
+ detect_collisions
21
+
22
+ every(interval) do
23
+ handle_collision_events
24
+ end
25
+
26
+ super
27
+ rescue Exception => e
28
+ Logger.error "Error starting Sphero driver!"
29
+ Logger.error e.message
30
+ Logger.error e.backtrace.inspect
31
+ end
32
+ end
33
+
34
+ def handle_collision_events
35
+ while i = find_event(::Sphero::Response::CollisionDetected) do
36
+ update_collision(messages.slice!(i))
37
+ end
38
+ end
39
+
40
+ # Publish collision events
41
+ def update_collision(data)
42
+ publish(event_topic_name("collision"), data)
43
+ end
44
+
17
45
  # Detects collisions
18
46
  # @param [Hash] params
19
47
  def detect_collisions(params={})
@@ -61,6 +89,10 @@ module Artoo
61
89
 
62
90
  private
63
91
 
92
+ def find_event(response_klass)
93
+ messages.index {|m| m.is_a? response_klass}
94
+ end
95
+
64
96
  def matching_response_types(responses, respone_klass)
65
97
  responses.select { |m| m.is_a? respone_klass } if responses
66
98
  end
@@ -18,25 +18,21 @@ module Artoo
18
18
  # Starts drives and required connections
19
19
  def start_driver
20
20
  begin
21
- listener = ->(value) { update(value) }
22
- connection.on("i2c_reply", listener)
23
-
24
21
  connection.i2c_config(0)
25
22
  every(interval) do
26
23
  connection.i2c_write_request(address, 0x40, 0x00)
27
- p
28
24
  connection.i2c_write_request(address, 0x00, 0x00)
29
- p
30
25
  connection.i2c_read_request(address, 6)
31
- p
26
+
32
27
  connection.read_and_process
28
+ handle_events
33
29
  end
34
30
 
35
31
  super
36
32
  rescue Exception => e
37
- p "start driver"
38
- p e.message
39
- p e.backtrace.inspect
33
+ Logger.error "Error starting wii driver!"
34
+ Logger.error e.message
35
+ Logger.error e.backtrace.inspect
40
36
  end
41
37
  end
42
38
 
@@ -50,8 +46,22 @@ module Artoo
50
46
  @data = parse(value)
51
47
  end
52
48
 
49
+ def handle_events
50
+ while i = find_event("i2c_reply") do
51
+ update(events.slice!(i).data.first)
52
+ end
53
+ end
54
+
53
55
  protected
54
56
 
57
+ def find_event(name)
58
+ events.index {|e| e.name == name}
59
+ end
60
+
61
+ def events
62
+ connection.async_events
63
+ end
64
+
55
65
  def get_defaults
56
66
  {}
57
67
  end
data/lib/artoo/version.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module Artoo
2
2
  unless const_defined?('VERSION')
3
- VERSION = "0.4.1"
3
+ VERSION = "0.5.0"
4
4
  end
5
5
  end
@@ -12,7 +12,7 @@ describe Artoo::Connection do
12
12
 
13
13
  it 'Artoo::Connection#connect' do
14
14
  @connection.connect
15
- @connection.adaptor.must_be_kind_of Artoo::Adaptors::Loopback
15
+ @connection.adaptor.class.must_equal Artoo::Adaptors::Loopback
16
16
  end
17
17
 
18
18
  it 'Artoo::Connection#disconnect' do
@@ -1,6 +1,6 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + "/test_helper")
2
2
 
3
- class DelegatorTest < MiniTest::Unit::TestCase
3
+ class DelegatorTest < Minitest::Test
4
4
  class Mirror
5
5
  attr_reader :last_call
6
6
  def method_missing(*a, &b)