hybridgroup-crubyflie 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +18 -0
  3. data/.travis.yml +8 -0
  4. data/Gemfile +5 -0
  5. data/LICENSE.txt +674 -0
  6. data/README.md +127 -0
  7. data/Rakefile +15 -0
  8. data/bin/crubyflie +94 -0
  9. data/configs/joystick_default.yaml +106 -0
  10. data/crubyflie.gemspec +50 -0
  11. data/examples/params_and_logging.rb +87 -0
  12. data/lib/crubyflie/crazyflie/commander.rb +54 -0
  13. data/lib/crubyflie/crazyflie/console.rb +67 -0
  14. data/lib/crubyflie/crazyflie/log.rb +383 -0
  15. data/lib/crubyflie/crazyflie/log_conf.rb +57 -0
  16. data/lib/crubyflie/crazyflie/param.rb +220 -0
  17. data/lib/crubyflie/crazyflie/toc.rb +239 -0
  18. data/lib/crubyflie/crazyflie/toc_cache.rb +87 -0
  19. data/lib/crubyflie/crazyflie.rb +282 -0
  20. data/lib/crubyflie/crazyradio/crazyradio.rb +301 -0
  21. data/lib/crubyflie/crazyradio/radio_ack.rb +48 -0
  22. data/lib/crubyflie/crubyflie_logger.rb +74 -0
  23. data/lib/crubyflie/driver/crtp_packet.rb +146 -0
  24. data/lib/crubyflie/driver/radio_driver.rb +363 -0
  25. data/lib/crubyflie/exceptions.rb +36 -0
  26. data/lib/crubyflie/input/input_reader.rb +190 -0
  27. data/lib/crubyflie/input/joystick_input_reader.rb +328 -0
  28. data/lib/crubyflie/version.rb +22 -0
  29. data/lib/crubyflie.rb +36 -0
  30. data/spec/commander_spec.rb +67 -0
  31. data/spec/console_spec.rb +76 -0
  32. data/spec/crazyflie_spec.rb +176 -0
  33. data/spec/crazyradio_spec.rb +228 -0
  34. data/spec/crtp_packet_spec.rb +79 -0
  35. data/spec/crubyflie_logger_spec.rb +39 -0
  36. data/spec/crubyflie_spec.rb +21 -0
  37. data/spec/input_reader_spec.rb +136 -0
  38. data/spec/joystick_cfg.yaml +44 -0
  39. data/spec/joystick_input_reader_spec.rb +323 -0
  40. data/spec/log_spec.rb +266 -0
  41. data/spec/param_spec.rb +166 -0
  42. data/spec/radio_ack_spec.rb +43 -0
  43. data/spec/radio_driver_spec.rb +227 -0
  44. data/spec/spec_helper.rb +53 -0
  45. data/spec/toc_cache_spec.rb +87 -0
  46. data/spec/toc_spec.rb +187 -0
  47. data/tools/sdl-joystick-axis.rb +69 -0
  48. metadata +225 -0
@@ -0,0 +1,383 @@
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
+
20
+ require 'crazyflie/log_conf.rb'
21
+
22
+ module Crubyflie
23
+
24
+ # An element in the Logging Table of Contents
25
+ # A LogTOCElement knows what the type of data that comes in a #LogBlock
26
+ # and is able to initialize the #TOCElement from a TOC Logging packet
27
+ class LogTOCElement < TOCElement
28
+
29
+ # A map between crazyflie C types and ruby directives to
30
+ # interpret them. This will help parsing the logging data
31
+ C_RUBY_TYPE_MAP = {
32
+ 1 => {
33
+ :ctype => "uint8_t",
34
+ :directive => 'C',
35
+ :size => 1
36
+ },
37
+ 2 => {
38
+ :ctype => "uint16_t",
39
+ :directive => 'S<',
40
+ :size => 2
41
+ },
42
+ 3 => {
43
+ :ctype => "uint32_t",
44
+ :directive => 'L<',
45
+ :size => 4
46
+ },
47
+ 4 => {
48
+ :ctype => "int8_t",
49
+ :directive => 'c',
50
+ :size => '1'
51
+ },
52
+ 5 => {
53
+ :ctype => "int16_t",
54
+ :directive => 's<',
55
+ :size => 2
56
+ },
57
+ 6 => {
58
+ :ctype => "int32_t",
59
+ :directive => 'l<',
60
+ :size => 4
61
+ },
62
+ 7 => {
63
+ :ctype => "float",
64
+ :directive => 'e',
65
+ :size => 4
66
+ },
67
+ # Unsupported
68
+ # 8 => {
69
+ # :ctype => "FP16",
70
+ # :directive => '',
71
+ # :size => 2
72
+ # }
73
+ }
74
+
75
+ # Initializes a Log TOC element, which means interpreting the
76
+ # data in the packet and calling the parent class
77
+ # @param data [String] a binary payload
78
+ def initialize(data)
79
+ # unpack two null padded strings
80
+ group, name = data[2..-1].unpack('Z*Z*')
81
+ ident = data[0].ord()
82
+ ctype_id = data[1].ord() & 0b1111 # go from 0 to 15
83
+ ctype = C_RUBY_TYPE_MAP[ctype_id][:ctype]
84
+ directive = C_RUBY_TYPE_MAP[ctype_id][:directive]
85
+ access = data[1].ord & 0b00010000 # 0x10, the 5th bit
86
+
87
+ super({
88
+ :ident => ident,
89
+ :group => group,
90
+ :name => name,
91
+ :ctype => ctype,
92
+ :type_id => ctype_id,
93
+ :directive => directive,
94
+ :access => access
95
+ })
96
+ end
97
+
98
+
99
+ end
100
+
101
+ # A LogBlock represents a piece of logging information that is
102
+ # received periodically from the Crazyflie after having set the
103
+ # START_LOGGING command. Each LogBlock will trigger a callback when
104
+ # a piece of data is received for it.
105
+ #
106
+ # Note log blocks are added/removed by the Logging class through the
107
+ # interface provided. So you should not need to use them directly
108
+ class LogBlock
109
+
110
+ @@block_id_counter = 0
111
+ attr_reader :ident, :period
112
+ attr_writer :data_callback
113
+
114
+ # Initialize a LogBlock
115
+ # @param variables [Array] a set of LogConfVariables
116
+ # @param opts [Hash] current options:
117
+ # :period, in centiseconds (100 = 1s)
118
+ def initialize(variables, opts={})
119
+ @variables = variables || []
120
+ @ident = @@block_id_counter
121
+ @@block_id_counter += 1
122
+
123
+ @period = opts.delete(:period) || 10
124
+
125
+ @data_callback = nil
126
+ end
127
+
128
+ # Finds out the binary data by unpacking each of the variables
129
+ # depending on the number of bites for the declared size
130
+ # @param data [String] Binary data string
131
+ def unpack_log_data(data)
132
+ unpacked_data = {}
133
+ position = 0
134
+ @variables.each do |var|
135
+ fetch_as = var.fetch_as
136
+ map = LogTOCElement::C_RUBY_TYPE_MAP
137
+ size = map[fetch_as][:size]
138
+ directive = map[fetch_as][:directive]
139
+ name = var.name
140
+ data_to_unpack = data[position..position + size - 1]
141
+ value = data_to_unpack.unpack(directive).first
142
+ unpacked_data[name] = value
143
+ position += size
144
+ end
145
+ @data_callback.call(unpacked_data) if @data_callback
146
+ end
147
+ end
148
+
149
+ # The logging facility class
150
+ #
151
+ # This class is used to read packages received in the Logging port.
152
+ # It maintains a list of log blocks, which are conveniently added,
153
+ # or removed and for which logging is started or stopped.
154
+ # When a packet with new information for log block comes in,
155
+ # the block in question unpacks the data and triggers a callback.
156
+ #
157
+ # In Crubyflie, the Log class includes all the functionality
158
+ # which is to be found in the Python library LogEntry class (start logging,
159
+ # add block etc) and the Crazyflie class (callbacks for intialization), so
160
+ # interfacing with Log should be done through this class primarily.
161
+ #
162
+ # Unlike the original Pyhton library, there are no callbacks registered
163
+ # somewhere else or anything and functions being called from them. In turn,
164
+ # the Crazyflie class will queue all the logging requests in the @in_queue
165
+ # while a thread in the Logging class takes care of processing them
166
+ # and doing the appropiate. This saves us from registering callbacks
167
+ # in other places and from selecting which data we are to use here.
168
+ class Log
169
+ include Logging
170
+ include CRTPConstants
171
+
172
+ attr_reader :log_blocks, :toc
173
+ # Store the crazyflie, find the incoming packet queue for this
174
+ # facility and initialize a new TOC
175
+ # @param crazyflie [Crazyflie]
176
+ def initialize(crazyflie)
177
+ @log_blocks = {}
178
+ @crazyflie = crazyflie
179
+ @in_queue = crazyflie.crtp_queues[:logging]
180
+ @toc = TOC.new(@crazyflie.cache_folder, LogTOCElement)
181
+ @packet_reader_thread = nil
182
+ end
183
+
184
+ # Refreshes the TOC. TOC class implement this step synchronously
185
+ # so there is no need to provide callbacks or anything
186
+ def refresh_toc
187
+ reset_packet = packet_factory()
188
+ reset_packet.data = [CMD_RESET_LOGGING]
189
+ port = Crazyflie::CRTP_PORTS[:logging]
190
+ channel = TOC_CHANNEL
191
+ @crazyflie.send_packet(reset_packet)
192
+ @toc.fetch_from_crazyflie(@crazyflie, port, @in_queue)
193
+ end
194
+
195
+ # Creates a log block with the information from a configuration
196
+ # object.
197
+ # @param log_conf [LogConf] Configuration for this block
198
+ # @return [Integer] block ID if things went well,
199
+ def create_log_block(log_conf)
200
+ start_packet_reader_thread() if !@packet_reader_thread
201
+ block = LogBlock.new(log_conf.variables,
202
+ {:period => log_conf.period})
203
+ block_id = block.ident
204
+ @log_blocks[block_id] = block
205
+ packet = packet_factory()
206
+ packet.data = [CMD_CREATE_BLOCK, block_id]
207
+ log_conf.variables.each do |var|
208
+ if var.is_toc_variable?
209
+ packet.data << var.stored_fetch_as
210
+ packet.data << @toc[var.name].ident
211
+ else
212
+ bin_stored_fetch_as = [var.stored_fetch_as].pack('C')
213
+ bin_address = [var.address].pack('L<')
214
+ packet.data += bin_stored_fetch_as.unpack('C*')
215
+ packet.data += bin_address.unpack('C*')
216
+ end
217
+ end
218
+ logger.debug "Adding block #{block_id}"
219
+ @crazyflie.send_packet(packet)
220
+ return block_id
221
+ end
222
+
223
+ # Sends the START_LOGGING command for a given block.
224
+ # It should be called after #create_toc_log_block. This call
225
+ # will return immediately, but the provided block will be called
226
+ # regularly as logging data is received, until #stop_logging is
227
+ # issued for the same log
228
+ # Crazyflie. It fails silently if the block does not exist.
229
+ #
230
+ # @param block_id [Integer]
231
+ # @param data_callback [Proc] a block to be called everytime
232
+ # the log data is received.
233
+ def start_logging(block_id, &data_callback)
234
+ block = @log_blocks[block_id]
235
+ block.data_callback = data_callback
236
+ return if !block
237
+ start_packet_reader_thread() if !@packet_reader_thread
238
+ packet = packet_factory()
239
+ period = block.period
240
+ packet.data = [CMD_START_LOGGING, block_id, period]
241
+ logger.debug("Start logging on #{block_id} every #{period*10} ms")
242
+ @crazyflie.send_packet(packet)
243
+ end
244
+
245
+ # Sends the STOP_LOGGING command to the crazyflie for a given block.
246
+ # It fails silently if the block does not exist.
247
+ # @param block_id [Integer]
248
+ def stop_logging(block_id)
249
+ block = @log_blocks[block_id]
250
+ return if !block
251
+ packet = packet_factory()
252
+ packet.data = [CMD_STOP_LOGGING, block_id]
253
+ logger.debug("Stop logging on #{block_id}")
254
+ @crazyflie.send_packet(packet)
255
+ end
256
+
257
+ # Sends the DELETE_BLOCK command to the Crazyflie for a given block.
258
+ # It fails silently if the block does not exist.
259
+ # To be called after #stop_logging.
260
+ def delete_block(block_id)
261
+ block = @log_blocks.delete(block_id)
262
+ return if !block
263
+ packet = packet_factory()
264
+ packet.data = [CMD_DELETE_BLOCK, block_id]
265
+ @crazyflie.send_packet(packet)
266
+ end
267
+
268
+ # A thread that processes the queue of packets intended for this
269
+ # facility. Recommended to start it after TOC has been refreshed.
270
+ def start_packet_reader_thread
271
+ stop_packet_reader_thread()
272
+ @packet_reader_thread = Thread.new do
273
+ Thread.current.priority = -4
274
+ loop do
275
+ packet = @in_queue.pop() # block here if nothing is up
276
+ # @todo align these two
277
+ case packet.channel()
278
+ when LOG_SETTINGS_CHANNEL
279
+ handle_settings_packet(packet)
280
+ when LOG_DATA_CHANNEL
281
+ handle_logdata_packet(packet)
282
+ when TOC_CHANNEL
283
+ # We are refreshing TOC probably
284
+ @in_queue << packet
285
+ sleep 0.2
286
+ else
287
+ logger.debug("Log on #{packet.channel}. Cannot handle")
288
+ ## in_queue << packet
289
+ end
290
+ end
291
+ end
292
+ end
293
+
294
+ # Stop the facility's packet processing
295
+ def stop_packet_reader_thread
296
+ @packet_reader_thread.kill() if @packet_reader_thread
297
+ @packet_reader_thread = nil
298
+ end
299
+
300
+ # Finds a log block by id
301
+ # @param block_id [Integer] the block ID
302
+ # @return p
303
+ def [](block_id)
304
+ @log_blocks[block_id]
305
+ end
306
+
307
+ # Processes an incoming settings packet. Sort of a callback
308
+ # @param packet [CRTPPacket] the packet on the settings channel
309
+ def handle_settings_packet(packet)
310
+ cmd = packet.data[0] # byte 0 of data
311
+ payload = packet.data_repack()[1..-1] # the rest of data
312
+
313
+ block_id = payload[0].ord()
314
+ #See projects:crazyflie:firmware:comm_protocol#list_of_return_codes
315
+ # @todo write down error codes in some constant
316
+ error_st = payload[1].ord() # 0 is no error
317
+
318
+ case cmd
319
+ when CMD_CREATE_BLOCK
320
+ if !@log_blocks[block_id]
321
+ logger.error "No log entry for #{block_id}"
322
+ return
323
+ end
324
+ if error_st != 0
325
+ hex_error = error_st.to_s(16)
326
+ mesg = "Error creating block #{block_id}: #{hex_error}"
327
+ logger.error(mesg)
328
+ return
329
+ end
330
+ # Block was created, let's start logging
331
+ logger.debug "Log block #{block_id} created"
332
+ # We do not start logging right away do we?
333
+ when CMD_APPEND_BLOCK
334
+ logger.debug "Received log settings with APPEND_LOG"
335
+ when CMD_DELETE_BLOCK
336
+ logger.debug "Received log settings with DELETE_LOG"
337
+ when CMD_START_LOGGING
338
+ if error_st != 0
339
+ hex_error = error_st.to_s(16)
340
+ mesg = "Error starting to log #{block_id}: #{hex_error}"
341
+ logger.error(mesg)
342
+ else
343
+ logger.debug "Logging started for #{block_id}"
344
+ end
345
+ when CMD_STOP_LOGGING
346
+ # @todo
347
+ logger.debug "Received log settings with STOP_LOGGING"
348
+ when CMD_RESET_LOGGING
349
+ # @todo
350
+ logger.debug "Received log settings with RESET_LOGGING"
351
+ else
352
+ mesg = "Received log settings with #{cmd}. Dont now what to do"
353
+ logger.warn(mesg)
354
+ end
355
+
356
+ end
357
+ private :handle_settings_packet
358
+
359
+ def handle_logdata_packet(packet)
360
+ block_id = packet.data[0]
361
+ #logger.debug("Handling log data for block #{block_id}")
362
+ #timestamp = packet.data[1..3] . pack and re unpack as 4 bytes
363
+ logdata = packet.data_repack()[4..-1]
364
+ block = @log_blocks[block_id]
365
+ if block
366
+ block.unpack_log_data(logdata)
367
+ else
368
+ logger.error "No entry for logdata for block #{block_id}"
369
+ end
370
+ end
371
+ private :handle_logdata_packet
372
+
373
+ def packet_factory
374
+ packet = CRTPPacket.new()
375
+ packet.modify_header(nil,
376
+ CRTP_PORTS[:logging],
377
+ LOG_SETTINGS_CHANNEL)
378
+ return packet
379
+ end
380
+ private :packet_factory
381
+
382
+ end
383
+ end
@@ -0,0 +1,57 @@
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
+ module Crubyflie
20
+
21
+ # Interface for Logging configuration objects
22
+ class LogConf
23
+ attr_reader :variables, :data_callback, :period
24
+ def initialize(variables, data_callback, opts={})
25
+ @variables = variables
26
+ @data_callback = data_callback
27
+ @period = opts[:period] || 20
28
+ end
29
+ end
30
+
31
+ # Interface for Logging variable configuration objects
32
+ # this class lists methods to be implemented
33
+ # Python implementation is in cfclient/utils/logconfigreader.py
34
+ class LogConfVariable
35
+
36
+ attr_reader :name, :stored_as, :fetch_as, :address
37
+
38
+ def initialize(name, is_toc, stored_as, fetch_as, address=0)
39
+ @name = name
40
+ @is_toc = is_toc
41
+ @stored_as = stored_as
42
+ @fetch_as = fetch_as
43
+ @address = address
44
+ end
45
+ # @return [Integer] a byte where the upper 4 bits are the
46
+ # type indentifier of how the variable is stored and the lower
47
+ # 4 bits are the type the variable should be fetched as
48
+ def stored_fetch_as
49
+ return @stored_as << 4 | (0x0F & @fetch_as)
50
+ end
51
+
52
+ # @return [TrueClass,FalseClass] true if it is stored in the TOC
53
+ def is_toc_variable?
54
+ return @is_toc == true
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,220 @@
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 'timeout'
20
+
21
+ module Crubyflie
22
+ # An element in the Parameters Table of Contents
23
+ class ParamTOCElement < TOCElement
24
+ # A map between crazyflie C types and ruby directives to
25
+ # interpret them. This will help parsing the parameter data
26
+ C_RUBY_TYPE_MAP = {
27
+ 0 => {
28
+ :ctype => "int8_t",
29
+ :directive => 'c',
30
+ :size => 1
31
+ },
32
+ 1 => {
33
+ :ctype => "int16_t",
34
+ :directive => 's<',
35
+ :size => 2
36
+ },
37
+ 2 => {
38
+ :ctype => "int32_t",
39
+ :directive => 'l<',
40
+ :size => 4
41
+ },
42
+ 3 => {
43
+ :ctype => "int64_t",
44
+ :directive => 'q<',
45
+ :size => 8
46
+ },
47
+ 5 => {
48
+ :ctype => "FP16",
49
+ :directive => 'e',
50
+ :size => 2
51
+ },
52
+ 6 => {
53
+ :ctype => "float",
54
+ :directive => 'e',
55
+ :size => 4
56
+ },
57
+ 7 => {
58
+ :ctype => "double",
59
+ :directive => 'E',
60
+ :size => 8
61
+ },
62
+ 8 => {
63
+ :ctype => "uint8_t",
64
+ :directive => 'C',
65
+ :size => 1
66
+ },
67
+ 9 => {
68
+ :ctype => "uint16_t",
69
+ :directive => 'S<',
70
+ :size => 2
71
+ },
72
+ 10 => {
73
+ :ctype => "uint32_t",
74
+ :directive => 'L<',
75
+ :size => 4
76
+ },
77
+ 11 => {
78
+ :ctype => "int64_t",
79
+ :directive => 'Q<',
80
+ :size => '8'
81
+ }
82
+ }
83
+
84
+ # Initializes a Param TOC element, which means interpreting the
85
+ # data in the packet and calling the parent class
86
+ # @param data [String] a binary payload
87
+ # @todo It turns out this is the same as Log. Only the type conversion
88
+ # changes
89
+ def initialize(data)
90
+ # The group and name are zero-terminated strings from the 3rd byte
91
+ group, name = data[2..-1].unpack('Z*Z*')
92
+ ident = data[0].ord()
93
+ ctype_id = data[1].ord() & 0b1111 #from 0 to 15
94
+ ctype = C_RUBY_TYPE_MAP[ctype_id][:ctype]
95
+ directive = C_RUBY_TYPE_MAP[ctype_id][:directive]
96
+ access = data[1].ord() & 0b00010000 # 5th bit
97
+
98
+ super({
99
+ :ident => ident,
100
+ :group => group,
101
+ :name => name,
102
+ :ctype => ctype,
103
+ :type_id => ctype_id,
104
+ :directive => directive,
105
+ :access => access
106
+ })
107
+ end
108
+ end
109
+
110
+ # The parameter facility. Used to retrieve the table of contents,
111
+ # set the value of a parameter and read the value of a parameter
112
+ class Param
113
+ include Logging
114
+ include CRTPConstants
115
+
116
+ attr_reader :toc
117
+ # Initialize the parameter facility
118
+ # @param crazyflie [Crazyflie]
119
+ def initialize(crazyflie)
120
+ @crazyflie = crazyflie
121
+ @in_queue = crazyflie.crtp_queues[:param]
122
+ @toc = TOC.new(@crazyflie.cache_folder, ParamTOCElement)
123
+ end
124
+
125
+ # Refreshes the TOC. It only returns when it is finished
126
+ def refresh_toc
127
+ channel = TOC_CHANNEL
128
+ port = Crazyflie::CRTP_PORTS[:param]
129
+ @toc.fetch_from_crazyflie(@crazyflie, port, @in_queue)
130
+ end
131
+
132
+ # Set the value of a paremeter. This call will only return after
133
+ # a ack response has been received, or will timeout if
134
+ # #CRTPConstants::WAIT_PACKET_TIMEOUT is reached.
135
+ # @param name [String] parameter group.name
136
+ # @param value [Numeric] a value. It must be packable as binary data,
137
+ # the type being set in the params TOC
138
+ # @param block [Proc] an optional block that will be called with the
139
+ # response CRTPPacket received. Otherwise will
140
+ # log to debug
141
+ def set_value(name, value, &block)
142
+ element = @toc[name]
143
+ if element.nil?
144
+ logger.error "Param #{name} not in TOC!"
145
+ return
146
+ end
147
+
148
+ ident = element.ident
149
+ packet = CRTPPacket.new()
150
+ packet.modify_header(nil, CRTP_PORTS[:param],
151
+ PARAM_WRITE_CHANNEL)
152
+ packet.data = [ident]
153
+ packet.data += [value].pack(element.directive()).unpack('C*')
154
+ @in_queue.clear()
155
+ @crazyflie.send_packet(packet, true) # expect answer
156
+
157
+ response = wait_for_response()
158
+ return if response.nil?
159
+
160
+ if block_given?
161
+ yield response
162
+ else
163
+ mesg = "Got answer to setting param '#{name}' with '#{value}'"
164
+ logger.debug(mesg)
165
+ end
166
+ end
167
+
168
+ # Request an update for a parameter and call the provided block with
169
+ # the value of the parameter. This call will block until the response
170
+ # has been received or the #CRTPConstants::WAIT_PACKET_TIMEOUT is
171
+ # reached.
172
+ # @param name [String] a name in the form group.name
173
+ # @param block [Proc] a block to be called with the value as argument
174
+ def get_value(name, &block)
175
+ element = @toc[name]
176
+ if element.nil?
177
+ logger.error "Cannot update #{name}, not in TOC"
178
+ return
179
+ end
180
+ packet = CRTPPacket.new()
181
+ packet.modify_header(nil, CRTP_PORTS[:param],
182
+ PARAM_READ_CHANNEL)
183
+ packet.data = [element.ident]
184
+ @in_queue.clear()
185
+ @crazyflie.send_packet(packet, true)
186
+
187
+ # Pop packages until mine comes up
188
+ begin
189
+ response = wait_for_response()
190
+ return if response.nil?
191
+
192
+ ident = response.data()[0]
193
+ if ident != element.ident()
194
+ mesg = "Value expected for element with ID #{element.ident}"
195
+ mesg << " but got for element with ID #{ident}. Requeing"
196
+ logger.debug(mesg)
197
+ end
198
+ end until ident == element.ident()
199
+
200
+ value = response.data_repack()[1..-1]
201
+ value = value.unpack(element.directive).first
202
+ yield(value)
203
+ end
204
+ alias_method :request_param_update, :get_value
205
+
206
+ def wait_for_response
207
+ begin
208
+ wait = WAIT_PACKET_TIMEOUT
209
+ response = timeout(wait, WaitTimeoutException) do
210
+ @in_queue.pop()
211
+ end
212
+ return response
213
+ rescue WaitTimeoutException
214
+ logger.error("Param: waited too long to get response")
215
+ return nil
216
+ end
217
+ end
218
+ private :wait_for_response
219
+ end
220
+ end