crubyflie 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +674 -0
  5. data/README.md +99 -0
  6. data/Rakefile +15 -0
  7. data/bin/crubyflie +85 -0
  8. data/configs/joystick_default.yaml +48 -0
  9. data/crubyflie.gemspec +50 -0
  10. data/examples/params_and_logging.rb +87 -0
  11. data/lib/crubyflie/crazyflie/commander.rb +54 -0
  12. data/lib/crubyflie/crazyflie/console.rb +67 -0
  13. data/lib/crubyflie/crazyflie/log.rb +383 -0
  14. data/lib/crubyflie/crazyflie/log_conf.rb +57 -0
  15. data/lib/crubyflie/crazyflie/param.rb +220 -0
  16. data/lib/crubyflie/crazyflie/toc.rb +239 -0
  17. data/lib/crubyflie/crazyflie/toc_cache.rb +87 -0
  18. data/lib/crubyflie/crazyflie.rb +282 -0
  19. data/lib/crubyflie/crazyradio/crazyradio.rb +301 -0
  20. data/lib/crubyflie/crazyradio/radio_ack.rb +48 -0
  21. data/lib/crubyflie/crubyflie_logger.rb +74 -0
  22. data/lib/crubyflie/driver/crtp_packet.rb +146 -0
  23. data/lib/crubyflie/driver/radio_driver.rb +333 -0
  24. data/lib/crubyflie/exceptions.rb +36 -0
  25. data/lib/crubyflie/input/input_reader.rb +168 -0
  26. data/lib/crubyflie/input/joystick_input_reader.rb +280 -0
  27. data/lib/crubyflie/version.rb +22 -0
  28. data/lib/crubyflie.rb +31 -0
  29. data/spec/commander_spec.rb +67 -0
  30. data/spec/console_spec.rb +76 -0
  31. data/spec/crazyflie_spec.rb +176 -0
  32. data/spec/crazyradio_spec.rb +226 -0
  33. data/spec/crtp_packet_spec.rb +79 -0
  34. data/spec/crubyflie_logger_spec.rb +39 -0
  35. data/spec/crubyflie_spec.rb +20 -0
  36. data/spec/input_reader_spec.rb +136 -0
  37. data/spec/joystick_cfg.yaml +48 -0
  38. data/spec/joystick_input_reader_spec.rb +238 -0
  39. data/spec/log_spec.rb +266 -0
  40. data/spec/param_spec.rb +166 -0
  41. data/spec/radio_ack_spec.rb +43 -0
  42. data/spec/radio_driver_spec.rb +227 -0
  43. data/spec/spec_helper.rb +51 -0
  44. data/spec/toc_cache_spec.rb +87 -0
  45. data/spec/toc_spec.rb +187 -0
  46. data/tools/sdl-joystick-axis.rb +69 -0
  47. metadata +222 -0
@@ -0,0 +1,238 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (C) 2013 Hector Sanjuan
3
+
4
+ # This file is part of Crubyflie.
5
+
6
+ # Crubyflie is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+
11
+ # Crubyflie is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Crubyflie. If not, see <http://www.gnu.org/licenses/>
18
+
19
+ require 'input/joystick_input_reader'
20
+
21
+ describe Joystick do
22
+
23
+ before :each do
24
+ @sdl_joystick = double("Joystick")
25
+ allow(SDL::Joystick).to receive(:update_all)
26
+ allow(SDL).to receive(:init).with(SDL::INIT_JOYSTICK)
27
+ allow(SDL::Joystick).to receive(:num).and_return(3)
28
+ allow(SDL::Joystick).to receive(:open).and_return(@sdl_joystick)
29
+ allow(SDL::Joystick).to receive(:index_name).and_return("My Joystick")
30
+ allow(SDL::Joystick).to receive(:poll).with(false)
31
+ expect(SDL::Joystick).not_to receive(:poll).with(true)
32
+ @path = File.join(File.dirname(__FILE__), 'joystick_cfg.yaml')
33
+
34
+ @joystick = Joystick.new(@path)
35
+
36
+ @logger = @joystick.logger
37
+ allow(@logger).to receive(:info)
38
+ @joystick.init()
39
+ end
40
+
41
+ describe "#initialize" do
42
+ it "should initialize a valid joystick supposing things go well" do
43
+ @joystick.axis.should == {
44
+ 0 => :roll,
45
+ 1 => :pitch,
46
+ 2 => :yaw,
47
+ 3 => :thrust
48
+ }
49
+
50
+ @joystick.buttons.should == {
51
+ 0 => :switch_xmode,
52
+ 1 => :close_link
53
+ }
54
+ end
55
+ end
56
+
57
+ describe "#read_configuration" do
58
+ it "should read a configuration correctly" do
59
+ cfg = {
60
+ :type => "Joystick",
61
+ :axis => {0 => {:action => :yaw}},
62
+ :buttons => {0 => {:action => :roll}}
63
+ }
64
+ expect(YAML).to receive(:load_file).and_return(cfg)
65
+
66
+ axis, buttons = @joystick.read_configuration('path')
67
+ @joystick.config[:type].should == "Joystick"
68
+ axis.should == { 0 => :yaw }
69
+ buttons.should == { 0 => :roll }
70
+ end
71
+
72
+ it "should raise exception if it cannot load the configuration" do
73
+ expect {
74
+ @joystick.read_configuration('baa')
75
+ }.to raise_exception(JoystickException)
76
+ end
77
+
78
+ it "should raise exception if the configuration type is bad" do
79
+ cfg = {
80
+ :type => "BBOBOBO",
81
+ :axis => {0 => {:action => :yaw}},
82
+ :buttons => {0 => {:action => :roll}}
83
+ }
84
+ expect(YAML).to receive(:load_file).and_return(cfg)
85
+ m = "Configuration is not of type Joystick"
86
+ expect {
87
+ @joystick.read_configuration('baa')
88
+ }.to raise_exception(JoystickException, m)
89
+ end
90
+
91
+ it "should raise exception if the axis or buttons are missing" do
92
+ cfg = {
93
+ :type => "Joystick",
94
+ :axis => {0 => {:action => :yaw}},
95
+ }
96
+ expect(YAML).to receive(:load_file).and_return(cfg)
97
+ expect {
98
+ @joystick.read_configuration('baa')
99
+ }.to raise_exception(JoystickException, "No buttons section")
100
+ end
101
+
102
+ it "should raise exception if an axis has no action" do
103
+ cfg = {
104
+ :type => "Joystick",
105
+ :axis => {0 => {:invert => true}},
106
+ :buttons => {0 => {:action => :roll}}
107
+ }
108
+ expect(YAML).to receive(:load_file).and_return(cfg)
109
+ expect {
110
+ @joystick.read_configuration('baa')
111
+ }.to raise_exception(JoystickException, "Axis 0 needs an action")
112
+ end
113
+ end
114
+
115
+ describe "#init_sdl" do
116
+ it "should raise an exception if the joystick index is invalid" do
117
+ expect {
118
+ js = Joystick.new(@path, 33)
119
+ allow(js.logger).to receive(:info)
120
+ js.init()
121
+ }.to raise_exception(JoystickException, "No valid joystick index")
122
+ end
123
+
124
+ it "should open the joystick" do
125
+ expect(SDL::Joystick).to receive(:open).with(2)
126
+ js = Joystick.new(@path, 2)
127
+ allow(js.logger).to receive(:info)
128
+ js.init()
129
+ end
130
+ end
131
+
132
+ describe "#read_axis" do
133
+ it "should read the axis value with all the defaults" do
134
+ @joystick.config[:axis][8] = {:action => :test}
135
+ expect(@sdl_joystick).to receive(:axis).with(8).and_return(-32768)
136
+ value = @joystick.read_axis(8)
137
+ value.should == -30
138
+ end
139
+
140
+ it "should put values out of range in range" do
141
+ @joystick.config[:axis][8] = {:action => :test}
142
+ expect(@sdl_joystick).to receive(:axis).with(8).and_return(-40000)
143
+ value = @joystick.read_axis(8)
144
+ value.should == -30
145
+ end
146
+
147
+ it "should invert values if requested" do
148
+ @joystick.config[:axis][8] = {:action => :test, :invert => true}
149
+ expect(@sdl_joystick).to receive(:axis).with(8).and_return(-32767)
150
+ value = @joystick.read_axis(8)
151
+ value.should == 30
152
+ end
153
+
154
+ it "should put values in the deadzone as 0" do
155
+ @joystick.config[:axis][8] = {
156
+ :action => :test,
157
+ :dead_zone => "-40:40"
158
+ }
159
+ expect(@sdl_joystick).to receive(:axis).with(8).and_return(23)
160
+ value = @joystick.read_axis(8)
161
+ value.should == 0
162
+ end
163
+
164
+ it "should normalize the thrust correctly" do
165
+ @joystick.config[:axis][8] = {
166
+ :action => :thrust,
167
+ :output_range => "0:100"
168
+ }
169
+ expect(@sdl_joystick).to receive(:axis).with(8).and_return(32767)
170
+ value = @joystick.read_axis(8)
171
+ value.should == 60000
172
+ end
173
+
174
+ it "should throotle the change rate correctly" do
175
+ @joystick.config[:axis][8] = {
176
+ :action => :test,
177
+ :output_range => "100:200",
178
+ :input_range => "0:100",
179
+ :max_change_rate => 1,
180
+ :last_poll => 0.500,
181
+ :last_value => 150
182
+ }
183
+ allow(Time).to receive(:now).and_return(1.0) # 0.5 secs after
184
+ expect(@sdl_joystick).to receive(:axis).with(8).and_return(60)
185
+ value = @joystick.read_axis(8)
186
+ value.should == 150.5
187
+ end
188
+
189
+ it "should not throotle the change rate when increasing thrust" do
190
+ @joystick.config[:axis][8] = {
191
+ :action => :thrust,
192
+ :output_range => "0:100",
193
+ :input_range => "0:100",
194
+ :max_change_rate => 1,
195
+ :last_poll => 0.500,
196
+ :last_value => 50
197
+ }
198
+ allow(Time).to receive(:now).and_return(1.0) # 0.5 secs after
199
+ expect(@sdl_joystick).to receive(:axis).with(8).and_return(100)
200
+ value = @joystick.read_axis(8)
201
+ value.should == 60000
202
+ end
203
+ end
204
+
205
+ describe "#pre_normalize_thrust" do
206
+ it "should discard negative axis values" do
207
+ v = @joystick.send(:pre_normalize_thrust, -12, (-20..20), (0..100))
208
+ v.should == 0
209
+ end
210
+
211
+ it "should pre-normalize to the expected" do
212
+ v = @joystick.send(:pre_normalize_thrust, 5, (-10..10), (0..50))
213
+ v.should == 50
214
+ end
215
+ end
216
+
217
+ describe "#normalize_thrust" do
218
+ it "should return values within the range expected by the CF" do
219
+ v = @joystick.send(:normalize_thrust, 50)
220
+ v.should == 34750
221
+ v.should be_an_instance_of Fixnum
222
+ end
223
+ end
224
+
225
+ describe "#read button" do
226
+ it "should return 1 when pressed" do
227
+ expect(@sdl_joystick).to receive(:button).and_return(true)
228
+ v = @joystick.send(:read_button, 1)
229
+ v.should == 1
230
+ end
231
+
232
+ it "should return -1 when not pressed" do
233
+ expect(@sdl_joystick).to receive(:button).and_return(false)
234
+ v = @joystick.send(:read_button, 0)
235
+ v.should == -1
236
+ end
237
+ end
238
+ end
data/spec/log_spec.rb ADDED
@@ -0,0 +1,266 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (C) 2013 Hector Sanjuan
3
+
4
+ # This file is part of Crubyflie.
5
+
6
+ # Crubyflie is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+
11
+ # Crubyflie is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Crubyflie. If not, see <http://www.gnu.org/licenses/>
18
+
19
+ require 'crazyflie/log'
20
+
21
+
22
+ describe LogTOCElement do
23
+ describe "#initialize" do
24
+ it "should initialize correctly" do
25
+ data = [5, 23].pack('C*') + ['hello', 'world'].pack('Z*Z*')
26
+ lte = LogTOCElement.new(data)
27
+ lte.ident.should == 5
28
+ lte.group.should == 'hello'
29
+ lte.name.should == 'world'
30
+ lte.ctype.should == 'float'
31
+ lte.directive.should == 'e'
32
+ lte.access.should == 0x10
33
+ end
34
+ end
35
+ end
36
+
37
+ describe LogBlock do
38
+ describe "#initialize" do
39
+ it "should count blocks correctly" do
40
+ b1 = LogBlock.new([]) # 0
41
+ b2 = LogBlock.new([]) # 1
42
+ b3 = LogBlock.new([]) # 2
43
+ b3.ident.should == 2
44
+ end
45
+ end
46
+
47
+ describe "#unpack_log_data" do
48
+ it "should unpack some binary data and call the callback" do
49
+ result = nil
50
+ cb = Proc.new do |r|
51
+ result = r
52
+ end
53
+
54
+ var1 = LogConfVariable.new("var1", true, 0, 1)
55
+ var2 = LogConfVariable.new("var2", true, 0, 2)
56
+ var3 = LogConfVariable.new("var3", true, 0, 3)
57
+
58
+ variables = [var1, var2, var3]
59
+
60
+ # 1 uint8, 1 uint16 and 1 uint32
61
+ data = [255].pack('C') + [65535].pack('S<') + [16777215].pack('L<')
62
+
63
+ b = LogBlock.new(variables) #3
64
+ b.data_callback = cb
65
+ b.unpack_log_data(data)
66
+ result.should == {
67
+ 'var1' => 255,
68
+ 'var2' => 65535,
69
+ 'var3' => 16777215
70
+ }
71
+ end
72
+ end
73
+ end
74
+
75
+ describe Log do
76
+ before :each do
77
+ @crazyflie = double("Crazyflie")
78
+ @queue = Queue.new
79
+ allow(@crazyflie).to receive(:crtp_queues).and_return({:logging =>
80
+ @queue})
81
+ allow(@crazyflie).to receive(:cache_folder).and_return(nil)
82
+
83
+ @log = Log.new(@crazyflie)
84
+ @logger = @log.logger
85
+ end
86
+
87
+ describe "#initialize" do
88
+ it "should intialize the Log facility" do
89
+ @log.toc.toc.should == {}
90
+ @log.log_blocks.should == {}
91
+ end
92
+ end
93
+
94
+ describe "#refresh_toc" do
95
+ it "send the packet and fetch the TOC from the crazyflie" do
96
+ port = Crazyflie::CRTP_PORTS[:logging]
97
+ channel = TOC_CHANNEL
98
+ expect(@crazyflie).to receive(:send_packet).once
99
+ expect(@log.toc).to receive(:fetch_from_crazyflie).with(
100
+ @crazyflie,
101
+ port,
102
+ @queue)
103
+ @log.refresh_toc()
104
+ end
105
+ end
106
+
107
+ describe "#create_log_block" do
108
+ it "should send the create block package for toc variables" do
109
+ log_conf = double("LogConf")
110
+
111
+ var1 = LogConfVariable.new("var1", true, 0, 1)
112
+
113
+ var2 = LogConfVariable.new("var2", false, 0, 2, 2)
114
+ expect(var2).not_to receive(:name)
115
+
116
+ var3 = LogConfVariable.new("var3", true, 0, 3)
117
+ expect(var3).not_to receive(:address)
118
+
119
+ variables = [var1, var2, var3]
120
+ log_conf = LogConf.new(variables, {:period => 200})
121
+
122
+ # It is the 4th call to logblock.new in these specs
123
+ expect(@logger).to receive(:debug).with("Adding block 4")
124
+
125
+ packet = CRTPPacket.new()
126
+ packet.modify_header(nil, CRTP_PORTS[:logging],
127
+ LOG_SETTINGS_CHANNEL)
128
+
129
+ packet.data = [Log::CMD_CREATE_BLOCK] + [4] +
130
+ [1] + [1] + [2] + [2,0,0,0] + [3] + [32]
131
+
132
+ toc1 = TOCElement.new({:ident => 1})
133
+ toc3 = TOCElement.new({:ident => 32})
134
+ expect(@log.toc).to receive(:[]).with('var1').and_return(toc1)
135
+ expect(@log.toc).to receive(:[]).with('var3').and_return(toc3)
136
+
137
+ expect(@crazyflie).to receive(:send_packet) do |pk|
138
+ packet.header.should == pk.header
139
+ packet.data.should == pk.data
140
+ end
141
+ @log.create_log_block(log_conf).should == 4
142
+ end
143
+ end
144
+
145
+ describe "#start_logging" do
146
+ it "should send the start logging packet" do
147
+ logblock = double("logblock")
148
+ expect(logblock).to receive(:period).and_return(30)
149
+ expect(logblock).to receive(:data_callback=).with(an_instance_of(Proc))
150
+ expect(@log.log_blocks).to receive(:[]).with(5).and_return(logblock)
151
+ expect(@logger).to receive(:debug)
152
+ expect(@crazyflie).to receive(:send_packet) do |pk|
153
+ pk.data.should == [CMD_START_LOGGING, 5, 30]
154
+ end
155
+ @log.start_logging(5) do |values|
156
+ end
157
+ end
158
+ end
159
+
160
+ describe "#stop_logging" do
161
+ it "should send the stop logging packet" do
162
+ block = double("block")
163
+ expect(@log.log_blocks).to receive(:[]).with(5).and_return(block)
164
+ expect(@logger).to receive(:debug)
165
+ expect(@crazyflie).to receive(:send_packet) do |pk|
166
+ pk.data.should == [CMD_STOP_LOGGING, 5]
167
+ end
168
+ @log.stop_logging(5)
169
+ end
170
+ end
171
+
172
+ describe "#delete_block" do
173
+ it "should delete the block" do
174
+ block = double("block")
175
+ expect(@crazyflie).to receive(:send_packet) do |pk|
176
+ pk.data.should == [CMD_DELETE_BLOCK, 5]
177
+ end
178
+ expect(@log.log_blocks).to receive(:delete).with(5).and_return({})
179
+
180
+ @log.delete_block(5)
181
+ end
182
+ end
183
+
184
+ describe "#[]" do
185
+ it "should return a block with the given ID" do
186
+ expect(@log.log_blocks).to receive(:[]).with(3)
187
+ @log[3]
188
+ end
189
+ end
190
+
191
+ describe "#start_packet_reader_thread" do
192
+ it "should start a thread and process a packet" do
193
+ packet = CRTPPacket.new()
194
+ packet.modify_header(nil, nil, 3)
195
+ packet.channel.should == 3 # We dont process this one
196
+ do_this = receive(:pop).and_return(packet).at_least(:once)
197
+ expect(@queue).to do_this
198
+ expect(@logger).to receive(:debug).at_least(:once)
199
+ @log.start_packet_reader_thread
200
+ sleep 0.1
201
+ @log.stop_packet_reader_thread
202
+ end
203
+ end
204
+
205
+ describe "#handle_settings_packet" do
206
+ # This has not many secret
207
+ it "should go to CMD_CREATE_BLOCK case without error" do
208
+ packet = CRTPPacket.new()
209
+ packet.data = [CMD_CREATE_BLOCK, 33, 0]
210
+ expect(@log).not_to receive(:puts)
211
+ expect(@logger).to receive(:error).with("No log entry for 33")
212
+ @log.send(:handle_settings_packet, packet)
213
+ end
214
+
215
+ it "should go to CMD_CREATE_BLOCK case with error" do
216
+ packet = CRTPPacket.new()
217
+ packet.data = [CMD_CREATE_BLOCK, 33, 3]
218
+ expect(@logger).to receive(:error).with("Error creating block 33: 3")
219
+ expect(@log.log_blocks).to receive(:[]).with(33).and_return({})
220
+ @log.send(:handle_settings_packet, packet)
221
+ end
222
+
223
+ it "should go to CMD_START_LOGGING case without error" do
224
+ packet = CRTPPacket.new()
225
+ packet.data = [CMD_START_LOGGING, 33, 0]
226
+ expect(@logger).to receive(:debug).with("Logging started for 33")
227
+ @log.send(:handle_settings_packet, packet)
228
+ end
229
+
230
+ it "should go to CMD_START_LOGGING case with error" do
231
+ packet = CRTPPacket.new()
232
+ packet.data = [CMD_START_LOGGING, 33, 3]
233
+ expect(@logger).to receive(:error).with("Error starting to log 33: 3")
234
+ @log.send(:handle_settings_packet, packet)
235
+ end
236
+ end
237
+
238
+ describe "#handle_logdata_packet" do
239
+ it "should warn if no block exist" do
240
+ expect(@log.log_blocks).to receive(:[]).with(33).and_return(nil)
241
+ m = "No entry for logdata for block 33"
242
+ expect(@logger).to receive(:error).with(m)
243
+ packet = CRTPPacket.new()
244
+ packet.data = [33, 44, 45, 46]
245
+ @log.send(:handle_logdata_packet, packet)
246
+ end
247
+
248
+ it "should call the unpack log data if the block exists" do
249
+ block = double("Block")
250
+ expect(block).to receive(:unpack_log_data).with([46,47].pack('C*'))
251
+ expect(@log.log_blocks).to receive(:[]).with(33).and_return(block)
252
+ expect(@logger).not_to receive(:error)
253
+ packet = CRTPPacket.new()
254
+ packet.data = [33, 44, 45, 46, 46, 47]
255
+ @log.send(:handle_logdata_packet, packet)
256
+ end
257
+ end
258
+
259
+ describe "#packet_factory" do
260
+ it "should return a new packet on logging port and settings channel" do
261
+ pk = @log.send(:packet_factory)
262
+ pk.channel.should == LOG_SETTINGS_CHANNEL
263
+ pk.port.should == CRTP_PORTS[:logging]
264
+ end
265
+ end
266
+ end
@@ -0,0 +1,166 @@
1
+ # -*- coding: utf-8 -*-
2
+ # Copyright (C) 2013 Hector Sanjuan
3
+
4
+ # This file is part of Crubyflie.
5
+
6
+ # Crubyflie is free software: you can redistribute it and/or modify
7
+ # it under the terms of the GNU General Public License as published by
8
+ # the Free Software Foundation, either version 3 of the License, or
9
+ # (at your option) any later version.
10
+
11
+ # Crubyflie is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU General Public License for more details.
15
+
16
+ # You should have received a copy of the GNU General Public License
17
+ # along with Crubyflie. If not, see <http://www.gnu.org/licenses/>
18
+
19
+ require 'crazyflie/param'
20
+
21
+
22
+
23
+ describe LogTOCElement do
24
+ describe "#initialize" do
25
+ it "should initialize correctly" do
26
+ data = [5, 23].pack('C*') + ['hello', 'world'].pack('Z*Z*')
27
+ lte = ParamTOCElement.new(data)
28
+ lte.ident.should == 5
29
+ lte.group.should == 'hello'
30
+ lte.name.should == 'world'
31
+ lte.ctype.should == 'double'
32
+ lte.directive.should == 'E'
33
+ lte.access.should == 0x10
34
+ end
35
+ end
36
+ end
37
+
38
+
39
+ describe Param do
40
+ before :each do
41
+ @crazyflie = double("Crazyflie")
42
+ @queue = Queue.new
43
+ allow(@crazyflie).to receive(:crtp_queues).and_return({:param =>
44
+ @queue})
45
+ allow(@crazyflie).to receive(:cache_folder).and_return(nil)
46
+
47
+ @param = Param.new(@crazyflie)
48
+ @logger = @param.logger
49
+ end
50
+
51
+ describe "#initialize" do
52
+ it "should initialize the Param instance" do
53
+ param = Param.new(@crazyflie)
54
+ @param.toc.toc.should == {}
55
+ end
56
+ end
57
+
58
+ describe "#refresh_toc" do
59
+ it "send the packet and fetch the TOC from the crazyflie" do
60
+ port = Crazyflie::CRTP_PORTS[:param]
61
+ channel = Param::TOC_CHANNEL
62
+ expect(@param.toc).to receive(:fetch_from_crazyflie).with(
63
+ @crazyflie,
64
+ port,
65
+ @queue)
66
+ @param.refresh_toc()
67
+ end
68
+ end
69
+
70
+ describe "#set_value" do
71
+ it "should not do anything if the element is not in the TOC" do
72
+ expect(@logger).to receive(:error).with("Param abc not in TOC!")
73
+ expect(@crazyflie).not_to receive(:send_packet)
74
+ @param.set_value('abc', 45)
75
+ end
76
+
77
+ it "should set the value of a parameter and yield the response" do
78
+ toc_elem = TOCElement.new({
79
+ :ident => 3,
80
+ :group => "gr",
81
+ :name => "name",
82
+ :ctype => "int32_t",
83
+ :directive => "l<"
84
+ })
85
+
86
+ name = 'gr.name'
87
+ expect(@param.toc).to receive(:[]).with(name).and_return(toc_elem)
88
+
89
+ expect(@crazyflie).to receive(:send_packet) { |packet, expect|
90
+ packet.port.should == CRTP_PORTS[:param]
91
+ packet.channel == PARAM_WRITE_CHANNEL
92
+ packet.data.should == [3, 1, 0, 0, 0]
93
+ expect.should == true
94
+ }
95
+
96
+ res = CRTPPacket.new()
97
+ expect(@queue).to receive(:pop).and_return(res)
98
+
99
+ @param.set_value('gr.name', 1) do |response|
100
+ response.should == res
101
+ end
102
+ end
103
+
104
+ it "should set the value of a parameter without a block_given" do
105
+ toc_elem = TOCElement.new({
106
+ :ident => 3,
107
+ :group => "gr",
108
+ :name => "name",
109
+ :ctype => "int32_t",
110
+ :directive => "l<"
111
+ })
112
+
113
+ name = 'gr.name'
114
+ expect(@param.toc).to receive(:[]).with(name).and_return(toc_elem)
115
+
116
+ expect(@crazyflie).to receive(:send_packet) { |packet, expect|
117
+ packet.port.should == CRTP_PORTS[:param]
118
+ packet.channel == PARAM_WRITE_CHANNEL
119
+ packet.data.should == [3, 1, 0, 0, 0]
120
+ expect.should == true
121
+ }
122
+
123
+ res = CRTPPacket.new()
124
+ expect(@queue).to receive(:pop).and_return(res)
125
+
126
+ m = "Got answer to setting param 'gr.name' with '1'"
127
+ expect(@logger).to receive(:debug).with(m)
128
+ @param.set_value('gr.name', 1)
129
+ end
130
+ end
131
+
132
+ describe "#get_value" do
133
+ it "should not do anything if the element is not in the TOC" do
134
+ m = "Cannot update gr.name, not in TOC"
135
+ expect(@logger).to receive(:error).with(m)
136
+
137
+ @param.get_value('gr.name') {}
138
+ end
139
+
140
+ it "should request an element and yield the value" do
141
+ toc_elem = TOCElement.new({
142
+ :ident => 3,
143
+ :group => "gr",
144
+ :name => "name",
145
+ :ctype => "int32_t",
146
+ :directive => "l<"
147
+ })
148
+
149
+ name = 'gr.name'
150
+ expect(@param.toc).to receive(:[]).with(name).and_return(toc_elem)
151
+
152
+ expect(@crazyflie).to receive(:send_packet) do |packet|
153
+ packet.port.should == CRTP_PORTS[:param]
154
+ packet.channel.should == PARAM_READ_CHANNEL
155
+ packet.data.should == [3]
156
+ end
157
+
158
+ res = CRTPPacket.new()
159
+ res.data = [3, 5, 0, 0, 0]
160
+ expect(@queue).to receive(:pop).and_return(res)
161
+ @param.get_value('gr.name') do |value|
162
+ value.should == 5
163
+ end
164
+ end
165
+ end
166
+ end