crubyflie 0.1.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.
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