class Crubyflie::Log
The logging facility class
This class is used to read packages received in the Logging
port. It maintains a list of log blocks, which are conveniently added, or removed and for which logging is started or stopped. When a packet with new information for log block comes in, the block in question unpacks the data and triggers a callback.
In Crubyflie
, the Log
class includes all the functionality which is to be found in the Python library LogEntry class (start logging, add block etc) and the Crazyflie
class (callbacks for intialization), so interfacing with Log
should be done through this class primarily.
Unlike the original Pyhton library, there are no callbacks registered somewhere else or anything and functions being called from them. In turn, the Crazyflie
class will queue all the logging requests in the @in_queue while a thread in the Logging
class takes care of processing them and doing the appropiate. This saves us from registering callbacks in other places and from selecting which data we are to use here.
Attributes
Public Class Methods
Store the crazyflie, find the incoming packet queue for this facility and initialize a new TOC
@param crazyflie [Crazyflie]
# File lib/crubyflie/crazyflie/log.rb, line 175 def initialize(crazyflie) @log_blocks = {} @crazyflie = crazyflie @in_queue = crazyflie.crtp_queues[:logging] @toc = TOC.new(@crazyflie.cache_folder, LogTOCElement) @packet_reader_thread = nil end
Public Instance Methods
Finds a log block by id @param block_id [Integer] the block ID @return p
# File lib/crubyflie/crazyflie/log.rb, line 302 def [](block_id) @log_blocks[block_id] end
Creates a log block with the information from a configuration object. @param log_conf [LogConf] Configuration for this block @return [Integer] block ID if things went well,
# File lib/crubyflie/crazyflie/log.rb, line 198 def create_log_block(log_conf) start_packet_reader_thread() if !@packet_reader_thread block = LogBlock.new(log_conf.variables, {:period => log_conf.period}) block_id = block.ident @log_blocks[block_id] = block packet = packet_factory() packet.data = [CMD_CREATE_BLOCK, block_id] log_conf.variables.each do |var| if var.is_toc_variable? packet.data << var.stored_fetch_as packet.data << @toc[var.name].ident else bin_stored_fetch_as = [var.stored_fetch_as].pack('C') bin_address = [var.address].pack('L<') packet.data += bin_stored_fetch_as.unpack('C*') packet.data += bin_address.unpack('C*') end end logger.debug "Adding block #{block_id}" @crazyflie.send_packet(packet) return block_id end
Sends the DELETE_BLOCK command to the Crazyflie
for a given block. It fails silently if the block does not exist. To be called after stop_logging
.
# File lib/crubyflie/crazyflie/log.rb, line 259 def delete_block(block_id) block = @log_blocks.delete(block_id) return if !block packet = packet_factory() packet.data = [CMD_DELETE_BLOCK, block_id] @crazyflie.send_packet(packet) end
Refreshes the TOC
. TOC
class implement this step synchronously so there is no need to provide callbacks or anything
# File lib/crubyflie/crazyflie/log.rb, line 185 def refresh_toc reset_packet = packet_factory() reset_packet.data = [CMD_RESET_LOGGING] port = Crazyflie::CRTP_PORTS[:logging] channel = TOC_CHANNEL @crazyflie.send_packet(reset_packet) @toc.fetch_from_crazyflie(@crazyflie, port, @in_queue) end
Sends the START_LOGGING command for a given block. It should be called after create_toc_log_block. This call will return immediately, but the provided block will be called regularly as logging data is received, until stop_logging
is issued for the same log Crazyflie
. It fails silently if the block does not exist.
@param block_id [Integer] @param data_callback [Proc] a block to be called everytime
the log data is received.
# File lib/crubyflie/crazyflie/log.rb, line 232 def start_logging(block_id, &data_callback) block = @log_blocks[block_id] block.data_callback = data_callback return if !block start_packet_reader_thread() if !@packet_reader_thread packet = packet_factory() period = block.period packet.data = [CMD_START_LOGGING, block_id, period] logger.debug("Start logging on #{block_id} every #{period*10} ms") @crazyflie.send_packet(packet) end
A thread that processes the queue of packets intended for this facility. Recommended to start it after TOC
has been refreshed.
# File lib/crubyflie/crazyflie/log.rb, line 269 def start_packet_reader_thread stop_packet_reader_thread() @packet_reader_thread = Thread.new do Thread.current.priority = -4 loop do packet = @in_queue.pop() # block here if nothing is up # @todo align these two case packet.channel() when LOG_SETTINGS_CHANNEL handle_settings_packet(packet) when LOG_DATA_CHANNEL handle_logdata_packet(packet) when TOC_CHANNEL # We are refreshing TOC probably @in_queue << packet sleep 0.2 else logger.debug("Log on #{packet.channel}. Cannot handle") ## in_queue << packet end end end end
Sends the STOP_LOGGING command to the crazyflie for a given block. It fails silently if the block does not exist. @param block_id [Integer]
# File lib/crubyflie/crazyflie/log.rb, line 247 def stop_logging(block_id) block = @log_blocks[block_id] return if !block packet = packet_factory() packet.data = [CMD_STOP_LOGGING, block_id] logger.debug("Stop logging on #{block_id}") @crazyflie.send_packet(packet) end
Stop the facility’s packet processing
# File lib/crubyflie/crazyflie/log.rb, line 294 def stop_packet_reader_thread @packet_reader_thread.kill() if @packet_reader_thread @packet_reader_thread = nil end
Private Instance Methods
# File lib/crubyflie/crazyflie/log.rb, line 358 def handle_logdata_packet(packet) block_id = packet.data[0] #logger.debug("Handling log data for block #{block_id}") #timestamp = packet.data[1..3] . pack and re unpack as 4 bytes logdata = packet.data_repack()[4..-1] block = @log_blocks[block_id] if block block.unpack_log_data(logdata) else logger.error "No entry for logdata for block #{block_id}" end end
Processes an incoming settings packet. Sort of a callback @param packet [CRTPPacket] the packet on the settings channel
# File lib/crubyflie/crazyflie/log.rb, line 308 def handle_settings_packet(packet) cmd = packet.data[0] # byte 0 of data payload = packet.data_repack()[1..-1] # the rest of data block_id = payload[0].ord() #See projects:crazyflie:firmware:comm_protocol#list_of_return_codes # @todo write down error codes in some constant error_st = payload[1].ord() # 0 is no error case cmd when CMD_CREATE_BLOCK if !@log_blocks[block_id] logger.error "No log entry for #{block_id}" return end if error_st != 0 hex_error = error_st.to_s(16) mesg = "Error creating block #{block_id}: #{hex_error}" logger.error(mesg) return end # Block was created, let's start logging logger.debug "Log block #{block_id} created" # We do not start logging right away do we? when CMD_APPEND_BLOCK logger.debug "Received log settings with APPEND_LOG" when CMD_DELETE_BLOCK logger.debug "Received log settings with DELETE_LOG" when CMD_START_LOGGING if error_st != 0 hex_error = error_st.to_s(16) mesg = "Error starting to log #{block_id}: #{hex_error}" logger.error(mesg) else logger.debug "Logging started for #{block_id}" end when CMD_STOP_LOGGING # @todo logger.debug "Received log settings with STOP_LOGGING" when CMD_RESET_LOGGING # @todo logger.debug "Received log settings with RESET_LOGGING" else mesg = "Received log settings with #{cmd}. Dont now what to do" logger.warn(mesg) end end
# File lib/crubyflie/crazyflie/log.rb, line 372 def packet_factory packet = CRTPPacket.new() packet.modify_header(nil, CRTP_PORTS[:logging], LOG_SETTINGS_CHANNEL) return packet end