lego_ev3 0.9.0 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -6,8 +6,15 @@ end
6
6
 
7
7
  require_relative 'utilities'
8
8
  require_relative 'exceptions'
9
+
10
+ require_relative 'commands/builder'
9
11
  require_all_relative 'commands'
12
+
13
+ require_relative 'connection/base'
10
14
  require_all_relative 'connection'
15
+
16
+ require_relative 'sensors/base'
11
17
  require_all_relative 'sensors'
18
+
12
19
  require_relative 'tacho_motor'
13
20
  require_relative 'brick'
@@ -1,30 +1,37 @@
1
1
  module LegoEv3
2
2
  # More info: http://www.ev3dev.org/docs/drivers/lego-sensor-class/
3
3
  class LegoSensor
4
+ attr_reader :value
5
+
4
6
  def initialize(connection, id, port, driver_name)
5
7
  @connection = connection
6
8
  @id = id
7
9
  @port = port
8
10
  @driver_name = driver_name
9
-
10
- LegoEv3::Commands::LegoSensor.get_decimals(@connection, @id) do |response|
11
- @decimals = response
12
- end
13
-
14
- LegoEv3::Commands::LegoSensor.get_num_values(@connection, @id) do |response|
15
- @value_parts_count = response
16
- end
17
-
18
- @connection.flush
19
11
  end
20
12
 
21
13
  def info
22
14
  {
23
15
  id: @id,
24
16
  port: @port,
25
- driver_name: @driver_name,
26
- decimals: @decimals
17
+ driver_name: @driver_name
27
18
  }
28
19
  end
20
+
21
+ protected
22
+
23
+ def poll_value(parts_count)
24
+ raw = []
25
+
26
+ parts_count.times do |i|
27
+ LegoEv3::Commands::LegoSensor.send("get_value#{i}", @connection, @id) do |value|
28
+ raw << value
29
+ end
30
+ end
31
+
32
+ @connection.flush
33
+
34
+ raw
35
+ end
29
36
  end
30
37
  end
@@ -0,0 +1,76 @@
1
+ module LegoEv3
2
+ class ColorSensor < LegoSensor
3
+ def initialize(connection, id, port)
4
+ super(connection, id, port, 'lego-ev3-color')
5
+
6
+ @supported_modes = {
7
+ reflect: 'COL-REFLECT',
8
+ ambient: 'COL-AMBIENT',
9
+ color: 'COL-COLOR',
10
+ reflect_raw: 'REF-RAW',
11
+ rgb: 'RGB-RAW'
12
+ }
13
+
14
+ @modes_value_parts = {
15
+ reflect: 1,
16
+ ambient: 1,
17
+ color: 1,
18
+ reflect_raw: 2,
19
+ rgb: 3
20
+ }
21
+
22
+ @value_to_color = [:none, :black, :blue, :green, :yellow, :red, :white, :brown]
23
+
24
+ self.mode = :reflect
25
+ end
26
+
27
+ def mode
28
+ @mode = parse_mode(LegoEv3::Commands::LegoSensor.get_mode!(@connection, @id))
29
+ end
30
+
31
+ def mode=(new_value)
32
+ to_set = @supported_modes[new_value.to_sym]
33
+ throw new InvalidModeException(:sensor, :color, new_value, @supported_modes.keys) unless to_set
34
+
35
+ LegoEv3::Commands::LegoSensor.set_mode(@connection, @id, to_set)
36
+ LegoEv3::Commands::LegoSensor.get_mode(@connection, @id) do |m|
37
+ @mode = parse_mode(m)
38
+ end
39
+
40
+ @connection.flush
41
+ @mode
42
+ end
43
+
44
+ def poll
45
+ @value = parse_value(poll_value(@modes_value_parts[@mode]))
46
+ end
47
+
48
+ def info
49
+ super.merge({
50
+ sub_type: :color,
51
+ mode: @mode,
52
+ value: @value
53
+ })
54
+ end
55
+
56
+ private
57
+
58
+ def parse_mode(raw)
59
+ @supported_modes.select do |key, value|
60
+ raw == value
61
+ end.first[0]
62
+ end
63
+
64
+ def parse_value(raw)
65
+ if @mode == :reflect || @mode == :ambient
66
+ raw.first.to_f / 100 # percent
67
+ elsif @mode == :color
68
+ @value_to_color[raw.first]
69
+ elsif @mode == :reflect_raw
70
+ raw[0..1]
71
+ elsif @mode == :rgb
72
+ raw[0..2]
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,142 @@
1
+ module LegoEv3
2
+ # More info: http://www.ev3dev.org/docs/sensors/lego-ev3-infrared-sensor/
3
+ # TODO: make sense of proximity
4
+ class InfraredSensor < LegoSensor
5
+ def initialize(connection, id, port)
6
+ super(connection, id, port, 'lego-ev3-ir')
7
+
8
+ @supported_modes = {
9
+ proximity: 'IR-PROX',
10
+ seek: 'IR-SEEK',
11
+ control: 'IR-REMOTE',
12
+ control_alt: 'IR-REM-A'
13
+ }
14
+
15
+ @modes_value_parts = {
16
+ proximity: 1,
17
+ seek: 8,
18
+ control: 4,
19
+ control_alt: 1
20
+ }
21
+
22
+ @control_pressed_buttons = [
23
+ [],
24
+ [:red_up],
25
+ [:red_down],
26
+ [:blue_up],
27
+ [:blue_down],
28
+ [:red_up, :blue_up],
29
+ [:red_up, :blue_down],
30
+ [:red_down, :blue_up],
31
+ [:red_down, :blue_down],
32
+ [:beacon_on],
33
+ [:red_up, :red_down],
34
+ [:blue_up, :blue_down]
35
+ ]
36
+
37
+ self.mode = :proximity
38
+
39
+ @channel = 1
40
+
41
+ init_values
42
+ end
43
+
44
+ def channel
45
+ @channel
46
+ end
47
+
48
+ def channel=(new_value)
49
+ @channel = [1, new_value.to_i, 4].sort[1]
50
+
51
+ init_values
52
+ end
53
+
54
+ def mode
55
+ @mode = parse_mode(LegoEv3::Commands::LegoSensor.get_mode!(@connection, @id))
56
+ end
57
+
58
+ def mode=(new_value)
59
+ to_set = @supported_modes[new_value.to_sym]
60
+ throw new InvalidModeException(:sensor, :infrared, new_value, @supported_modes.keys) unless to_set
61
+
62
+ LegoEv3::Commands::LegoSensor.set_mode(@connection, @id, to_set)
63
+ LegoEv3::Commands::LegoSensor.get_mode(@connection, @id) do |m|
64
+ @mode = parse_mode(m)
65
+ end
66
+
67
+ @connection.flush
68
+
69
+ init_values
70
+
71
+ @mode
72
+ end
73
+
74
+ def poll
75
+ throw new InvalidChannelException(@channel) if @mode == :control_alt && @channel != 1
76
+ @last_value_raw = @value_raw
77
+ @value_raw = poll_value(@modes_value_parts[@mode])
78
+ @value = parse_value(@value_raw)
79
+ end
80
+
81
+ def info
82
+ super.merge({
83
+ sub_type: :infrared,
84
+ mode: @mode,
85
+ value: @value,
86
+ channel: @channel
87
+ })
88
+ end
89
+
90
+ private
91
+
92
+ # TODO Eventually use this to solve the "value 262" problem.
93
+ def init_values
94
+ @value = nil
95
+ @value_raw = nil
96
+ @last_value_raw = nil
97
+ end
98
+
99
+ def parse_mode(raw)
100
+ @supported_modes.select do |key, value|
101
+ raw == value
102
+ end.first[0]
103
+ end
104
+
105
+ def parse_value(raw)
106
+ if @mode == :proximity
107
+ raw.first.to_f / 100 # percent, [0, 1]. 100% is approximately 70cm.
108
+ elsif @mode == :seek
109
+ bit_position = (@channel - 1) * 4
110
+ values_at_position = raw[bit_position, bit_position + 1]
111
+
112
+ # When looking in the same direction as the sensor,
113
+ # -25 is far left and +25 is far right.
114
+ {
115
+ heading: values_at_position[0].to_f / 25, # percent, [-1, 1]
116
+ distance: values_at_position[1].to_f / 100, # percent, [0, 1]
117
+ activated: values_at_position[1] != -128
118
+ }
119
+ elsif @mode == :control
120
+ bit_position = @channel - 1
121
+ value_at_position = raw[bit_position]
122
+ @control_pressed_buttons[value_at_position]
123
+ elsif @mode == :control_alt
124
+ value = []
125
+
126
+ # TODO: Pressing an up/down button while beacon mode
127
+ # TODO: is activated with turn off beacon mode.
128
+
129
+ # TODO: Also, when the beacon mode is active or for
130
+ # TODO: about 1 second after any button is released the value is 262.
131
+ if raw[0] & 0x0F > 0
132
+ value << :blue_down if raw[0][7] == 1
133
+ value << :blue_up if raw[0][6] == 1
134
+ value << :red_down if raw[0][5] == 1
135
+ value << :red_up if raw[0][4] == 1
136
+ end
137
+
138
+ value
139
+ end
140
+ end
141
+ end
142
+ end
@@ -1,5 +1,4 @@
1
1
  module LegoEv3
2
- # More info: http://www.ev3dev.org/docs/drivers/lego-sensor-class/
3
2
  class TouchSensor < LegoSensor
4
3
  def initialize(connection, id, port)
5
4
  super(connection, id, port, 'lego-ev3-touch')
@@ -10,7 +9,7 @@ module LegoEv3
10
9
  end
11
10
 
12
11
  def poll
13
- @value = LegoEv3::Commands::LegoSensor.get_value0!(@connection, @id)
12
+ @value = poll_value(1)[0]
14
13
  end
15
14
 
16
15
  def info
@@ -60,7 +60,6 @@ module LegoEv3
60
60
  LegoEv3::Commands::TachoMotor.run_to_abs_pos!(@connection, @id)
61
61
 
62
62
  loop do
63
- ap info
64
63
  break if operation_completed?(old_position)
65
64
  end
66
65
 
@@ -98,12 +97,12 @@ module LegoEv3
98
97
  sync!
99
98
  end
100
99
 
101
- def ticks_per_rotation
102
- @ticks_per_rotation = LegoEv3::Commands::TachoMotor.get_count_per_rot!(@connection, @id)
100
+ def ticks_per_rotation(sync = true)
101
+ get_value('@ticks_per_rotation', 'get_count_per_rot', sync)
103
102
  end
104
103
 
105
- def speed
106
- @speed = LegoEv3::Commands::TachoMotor.get_duty_cycle!(@connection, @id)
104
+ def speed(sync = true)
105
+ get_value('@speed', 'get_duty_cycle', sync)
107
106
  end
108
107
 
109
108
  # This is actually the desired speed but feels more natural.
@@ -118,12 +117,12 @@ module LegoEv3
118
117
  desired_speed
119
118
  end
120
119
 
121
- def desired_speed
122
- @desired_speed = LegoEv3::Commands::TachoMotor.get_duty_cycle_sp!(@connection, @id)
120
+ def desired_speed(sync = true)
121
+ get_value('@desired_speed', 'get_duty_cycle_sp', sync)
123
122
  end
124
123
 
125
- def polarity
126
- @polarity = LegoEv3::Commands::TachoMotor.get_polarity!(@connection, @id)
124
+ def polarity(sync = true)
125
+ get_value('@polarity', 'get_polarity', sync)
127
126
  end
128
127
 
129
128
  def polarity=(new_value)
@@ -135,8 +134,8 @@ module LegoEv3
135
134
  polarity
136
135
  end
137
136
 
138
- def position
139
- @position = LegoEv3::Commands::TachoMotor.get_position!(@connection, @id)
137
+ def position(sync = true)
138
+ get_value('@position', 'get_position', sync)
140
139
  end
141
140
 
142
141
  def position=(new_value)
@@ -144,16 +143,16 @@ module LegoEv3
144
143
  position
145
144
  end
146
145
 
147
- def desired_position
148
- @desired_position = LegoEv3::Commands::TachoMotor.get_position_sp!(@connection, @id)
146
+ def desired_position(sync = true)
147
+ get_value('@desired_position', 'get_position_sp', sync)
149
148
  end
150
149
 
151
- def desired_time
152
- @desired_time = LegoEv3::Commands::TachoMotor.get_time_sp!(@connection, @id)
150
+ def desired_time(sync = true)
151
+ get_value('@desired_time', 'get_time_sp', sync)
153
152
  end
154
153
 
155
- def stop_mode
156
- @stop_mode = LegoEv3::Commands::TachoMotor.get_stop_command!(@connection, @id)
154
+ def stop_mode(sync = true)
155
+ get_value('@stop_mode', 'get_stop_command', sync)
157
156
  end
158
157
 
159
158
  def stop_mode=(new_value)
@@ -199,15 +198,17 @@ module LegoEv3
199
198
  end
200
199
 
201
200
  def sync!
202
- ticks_per_rotation
203
- speed
204
- desired_speed
205
- polarity
206
- position
207
- desired_position
208
- desired_time
209
- stop_mode
210
- update_states
201
+ ticks_per_rotation(false)
202
+ speed(false)
203
+ desired_speed(false)
204
+ polarity(false)
205
+ position(false)
206
+ desired_position(false)
207
+ desired_time(false)
208
+ stop_mode(false)
209
+ update_states(false)
210
+
211
+ @connection.flush("Updated motor state")
211
212
 
212
213
  info
213
214
  end
@@ -237,9 +238,17 @@ module LegoEv3
237
238
  throw Exception.new('Speed is set to 0.') if desired_speed == 0
238
239
  end
239
240
 
240
- def update_states
241
- states = LegoEv3::Commands::TachoMotor.get_states!(@connection, @id)
241
+ def update_states(sync = true)
242
+ if sync
243
+ update_states_part_2(LegoEv3::Commands::TachoMotor.get_states!(@connection, @id))
244
+ else
245
+ LegoEv3::Commands::TachoMotor.get_states(@connection, @id) do |states|
246
+ update_states_part_2(states)
247
+ end
248
+ end
249
+ end
242
250
 
251
+ def update_states_part_2(states)
243
252
  @running = states.include?(:running)
244
253
  @ramping = states.include?(:ramping)
245
254
  @holding = states.include?(:holding)
@@ -251,5 +260,17 @@ module LegoEv3
251
260
 
252
261
  !@running || @holding
253
262
  end
263
+
264
+ def get_value(variable_name, command, sync = true)
265
+ if sync
266
+ instance_variable_set(
267
+ variable_name,
268
+ LegoEv3::Commands::TachoMotor.send("#{command}!", @connection, @id))
269
+ else
270
+ LegoEv3::Commands::TachoMotor.send(command, @connection, @id) do |value|
271
+ instance_variable_set(variable_name, value)
272
+ end
273
+ end
274
+ end
254
275
  end
255
276
  end
@@ -1,4 +1,5 @@
1
1
  require 'yaml'
2
+ require 'optparse'
2
3
 
3
4
  module LegoEv3
4
5
  # bar, time = with_timer do
@@ -16,13 +17,44 @@ module LegoEv3
16
17
  [return_value, (diff * 1000).to_i] # in ms.
17
18
  end
18
19
 
20
+ def self.user_config_overrides
21
+ @@user_config_overrides
22
+ end
23
+
24
+ def self.user_config_overrides=(new_value)
25
+ @@user_config_overrides = new_value
26
+ end
27
+
28
+ # 1. Start from a default config + programatic config
29
+ # 2. If a config is found in the command parameters, apply overrides.
30
+ # 3. If a config is found in LegoEv3::resolved_config, apply overrides.
31
+ def self.resolve_user_config(user_config = nil)
32
+ config = default_user_config.merge(user_config || {})
33
+
34
+ OptionParser.new do |opt|
35
+ opt.on('-c, --config PATH', 'Use the provided configuration at PATH.') do |path|
36
+ config.merge!(LegoEv3::load_config(path))
37
+ end
38
+ end.parse!
39
+
40
+ config.merge!(user_config_overrides || {})
41
+
42
+ user_config_overrides = config
43
+
44
+ config
45
+ end
46
+
19
47
  def self.default_user_config
20
48
  {
21
- 'ssh' => {
49
+ 'entry_point' => 'script.rb',
50
+ 'remote' => {
22
51
  'host' => '192.168.2.3', # I'm working on Mac OS X, eh.
23
52
  'hostname' => 'ev3dev',
24
53
  'username' => 'root',
25
- 'password' => 'r00tme'
54
+ 'password' => 'r00tme',
55
+ 'ssh' => 22,
56
+ 'tcp' => 13603,
57
+ 'service' => 'ssh'
26
58
  }
27
59
  }
28
60
  end
@@ -30,4 +62,8 @@ module LegoEv3
30
62
  def self.load_config(path)
31
63
  YAML::load(File.open(path))
32
64
  end
65
+
66
+ private
67
+
68
+ @@user_config_overrides = nil
33
69
  end