px4_log_reader 0.0.5
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.
- checksums.yaml +7 -0
- data/.gitignore +4 -0
- data/README.md +43 -0
- data/Rakefile +124 -0
- data/lib/px4_log_reader/invalid_descriptor_error.rb +25 -0
- data/lib/px4_log_reader/log_buffer.rb +153 -0
- data/lib/px4_log_reader/log_file.rb +125 -0
- data/lib/px4_log_reader/log_message.rb +30 -0
- data/lib/px4_log_reader/message_descriptor.rb +185 -0
- data/lib/px4_log_reader/message_descriptor_cache.rb +56 -0
- data/lib/px4_log_reader/reader.rb +160 -0
- data/lib/px4_log_reader/version.rb +3 -0
- data/lib/px4_log_reader.rb +8 -0
- data/lib/px4log_parser.rb +228 -0
- data/px4_log_reader.gemspec +17 -0
- data/test/test.rb +33 -0
- data/test/test_files/pixhawk_descriptor_cache.px4mc +0 -0
- data/test/test_files/test_log.px4log +0 -0
- data/test/test_log_buffer.rb +173 -0
- data/test/test_log_file.rb +44 -0
- data/test/test_message_descriptor.rb +56 -0
- data/test/test_message_descriptor_cache.rb +70 -0
- data/test/test_reader.rb +127 -0
- metadata +81 -0
@@ -0,0 +1,56 @@
|
|
1
|
+
module Px4LogReader
|
2
|
+
|
3
|
+
class MessageDescriptorCache
|
4
|
+
|
5
|
+
attr_reader :cache_filename
|
6
|
+
|
7
|
+
def initialize( filename )
|
8
|
+
@cache_filename = filename
|
9
|
+
end
|
10
|
+
|
11
|
+
def exist?
|
12
|
+
return File.exist?( @cache_filename )
|
13
|
+
end
|
14
|
+
|
15
|
+
def read_descriptors
|
16
|
+
|
17
|
+
message_descriptors = {}
|
18
|
+
|
19
|
+
if File.exist?( cache_filename )
|
20
|
+
File.open( cache_filename, 'r' ) do |input|
|
21
|
+
begin
|
22
|
+
while ( ( data = input.read(4) ) && ( data.length == 4 ) ) do
|
23
|
+
descriptor_size = data.unpack('L').first
|
24
|
+
descriptor = Marshal.load( input.read( descriptor_size ) )
|
25
|
+
|
26
|
+
message_descriptors[ descriptor.type ] = descriptor
|
27
|
+
end
|
28
|
+
rescue EOFError => error
|
29
|
+
puts "Parsed #{@message_descriptions.size} cached message descriptions"
|
30
|
+
rescue StandardError => error
|
31
|
+
puts "#{error.class}: #{error.message}"
|
32
|
+
puts error.backtrace.join("\n")
|
33
|
+
end
|
34
|
+
end
|
35
|
+
else
|
36
|
+
puts "Cache file '#{cache_filename}' not found"
|
37
|
+
end
|
38
|
+
|
39
|
+
return message_descriptors
|
40
|
+
end
|
41
|
+
|
42
|
+
def write_descriptors( message_descriptors )
|
43
|
+
if !@cache_filename.empty? && File.exist?( File.dirname( @cache_filename ) )
|
44
|
+
File.open( @cache_filename, 'w+' ) do |output|
|
45
|
+
message_descriptors.each do |message_type,descriptor|
|
46
|
+
descriptor_data = Marshal.dump( descriptor )
|
47
|
+
output.write( [ descriptor_data.size ].pack('L') )
|
48
|
+
output.write( descriptor_data )
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
@@ -0,0 +1,160 @@
|
|
1
|
+
module Px4LogReader
|
2
|
+
|
3
|
+
def self.open_common( file, options, &block )
|
4
|
+
|
5
|
+
reader = Reader.new( file, options )
|
6
|
+
|
7
|
+
yield reader if block_given?
|
8
|
+
|
9
|
+
return reader
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.open( filename, options = {}, &block )
|
13
|
+
|
14
|
+
reader = nil
|
15
|
+
|
16
|
+
if File.exist?( filename )
|
17
|
+
reader = self.open_common( File.open( filename, 'r' ), options, &block )
|
18
|
+
end
|
19
|
+
|
20
|
+
return reader
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.open!( filename, options = {}, &block )
|
25
|
+
reader = nil
|
26
|
+
|
27
|
+
if File.exist?( filename )
|
28
|
+
reader = self.open_common( File.open( filename, 'r' ), options, &block )
|
29
|
+
else
|
30
|
+
raise FileNotFoundError.new( filename )
|
31
|
+
end
|
32
|
+
|
33
|
+
return reader
|
34
|
+
end
|
35
|
+
|
36
|
+
class Context
|
37
|
+
attr_reader :messages
|
38
|
+
def initialize
|
39
|
+
messages = {}
|
40
|
+
end
|
41
|
+
def find_by_name( name )
|
42
|
+
named_message = nil
|
43
|
+
@messages.values.each do |message|
|
44
|
+
if message.descriptor.name == name
|
45
|
+
named_message = message
|
46
|
+
end
|
47
|
+
end
|
48
|
+
return named_message
|
49
|
+
end
|
50
|
+
def find_by_type( type )
|
51
|
+
return @messages[ type ]
|
52
|
+
end
|
53
|
+
def set( message )
|
54
|
+
@messages[ message.descriptor.type ] = message.dup
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class Reader
|
59
|
+
|
60
|
+
def initialize( file, options )
|
61
|
+
|
62
|
+
opts = {
|
63
|
+
cache_filename: '',
|
64
|
+
buffer_size_kb: 10 * 1024
|
65
|
+
}.merge( options )
|
66
|
+
|
67
|
+
@message_descriptors = {}
|
68
|
+
@buffers = LogBufferArray.new
|
69
|
+
@descriptor_cache = nil
|
70
|
+
@context = Context.new
|
71
|
+
|
72
|
+
@log_file = file
|
73
|
+
# @buffers.set_file( @log_file, load_buffers: true )
|
74
|
+
|
75
|
+
@descriptor_cache = MessageDescriptorCache.new( opts[:cache_filename] )
|
76
|
+
end
|
77
|
+
|
78
|
+
def descriptors
|
79
|
+
if @log_file && @message_descriptors.empty?
|
80
|
+
if @descriptor_cache && @descriptor_cache.exist?
|
81
|
+
@message_descriptors = @descriptor_cache.read_descriptors
|
82
|
+
else
|
83
|
+
@message_descriptors = LogFile::read_descriptors( @log_file, @descriptor_cache )
|
84
|
+
end
|
85
|
+
|
86
|
+
@message_descriptors[ FORMAT_MESSAGE.type ] = FORMAT_MESSAGE
|
87
|
+
end
|
88
|
+
|
89
|
+
return @message_descriptors
|
90
|
+
end
|
91
|
+
|
92
|
+
def each_message( options = {}, &block )
|
93
|
+
|
94
|
+
opts ={
|
95
|
+
with: [], # white list - empty means all minus those in without list
|
96
|
+
without: ['FMT'] # black list - includes types or names
|
97
|
+
}.merge( options || {} )
|
98
|
+
|
99
|
+
opts[:with].map! do |val|
|
100
|
+
if val.class == String
|
101
|
+
descriptor = descriptors.values.find { |desc| desc.name == val }
|
102
|
+
val = descriptor.type
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
opts[:without].map! do |val|
|
107
|
+
if val.class == String
|
108
|
+
descriptor = descriptors.values.find { |desc| desc.name == val }
|
109
|
+
|
110
|
+
if descriptor
|
111
|
+
val = descriptor.type
|
112
|
+
else
|
113
|
+
raise "Failed to find descriptor with name '#{val}'"
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
if block_given?
|
119
|
+
|
120
|
+
loop do
|
121
|
+
|
122
|
+
message = LogFile::read_message( @log_file, @message_descriptors )
|
123
|
+
break if message.nil?
|
124
|
+
|
125
|
+
# Added message to the set of latest messages.
|
126
|
+
@context.set( message )
|
127
|
+
|
128
|
+
if opts[:with].empty?
|
129
|
+
if !opts[:without].include?( message.descriptor.name )
|
130
|
+
yield message, @context
|
131
|
+
end
|
132
|
+
else
|
133
|
+
if opts[:with].include?( message.descriptor.type )
|
134
|
+
yield message, @context
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
139
|
+
|
140
|
+
else
|
141
|
+
raise BlockRequiredError.new
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
145
|
+
|
146
|
+
# def rewind
|
147
|
+
# if @log_file
|
148
|
+
|
149
|
+
# @log_file.rewind
|
150
|
+
# @buffers.load_buffers
|
151
|
+
|
152
|
+
# end
|
153
|
+
# end
|
154
|
+
|
155
|
+
# def seek( offset )
|
156
|
+
# end
|
157
|
+
|
158
|
+
end
|
159
|
+
|
160
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
require 'px4_log_reader/version'
|
2
|
+
require 'px4_log_reader/invalid_descriptor_error'
|
3
|
+
require 'px4_log_reader/log_message'
|
4
|
+
require 'px4_log_reader/message_descriptor'
|
5
|
+
require 'px4_log_reader/log_buffer'
|
6
|
+
require 'px4_log_reader/log_file'
|
7
|
+
require 'px4_log_reader/message_descriptor_cache'
|
8
|
+
require 'px4_log_reader/reader'
|
@@ -0,0 +1,228 @@
|
|
1
|
+
|
2
|
+
|
3
|
+
|
4
|
+
|
5
|
+
# class Px4LogReader
|
6
|
+
|
7
|
+
# include PX4
|
8
|
+
|
9
|
+
# HEADER_MARKER = [0xA3,0x95]#.pack('CC').freeze
|
10
|
+
# HEADER_LENGTH = 3
|
11
|
+
|
12
|
+
# FORMAT_MESSAGE = Px4LogMessageDescription.new({
|
13
|
+
# name: 'FMT',
|
14
|
+
# type: 0x80,
|
15
|
+
# length: 89,
|
16
|
+
# format: 'BBnZ',
|
17
|
+
# fields: ["Type", "Length", "Name", "Format", "Labels"] }).freeze
|
18
|
+
|
19
|
+
# attr_reader :message_descriptions
|
20
|
+
# attr_reader :messages
|
21
|
+
# attr_reader :px4_log_format
|
22
|
+
|
23
|
+
# def initialize
|
24
|
+
# @message_descriptions = {}
|
25
|
+
# @messages = []
|
26
|
+
# @px4_log_format = false
|
27
|
+
# end
|
28
|
+
|
29
|
+
# def parse_log( filename, cache_filename=nil )
|
30
|
+
# @message_descriptions = {}
|
31
|
+
# @messages = []
|
32
|
+
|
33
|
+
# @file_size = File.size?(filename)
|
34
|
+
|
35
|
+
# if cache_filename && File.exist?( cache_filename )
|
36
|
+
# if File.exist?( cache_filename )
|
37
|
+
# File.open( cache_filename, 'r' ) do |io|
|
38
|
+
# begin
|
39
|
+
# loop do
|
40
|
+
# description_size = io.read_nonblock(4).unpack('L').first
|
41
|
+
# description = Marshal.load( io.read(description_size) )
|
42
|
+
|
43
|
+
# @message_descriptions[ description.type ] = description
|
44
|
+
# end
|
45
|
+
# rescue EOFError => error
|
46
|
+
# puts "Parsed #{@message_descriptions.size} cached message descriptions"
|
47
|
+
# # @message_descriptions.each do |message_type,description|
|
48
|
+
# # puts description
|
49
|
+
# # end
|
50
|
+
# rescue StandardError => error
|
51
|
+
# puts "#{error.class}: #{error.message}"
|
52
|
+
# puts error.backtrace.join("\n")
|
53
|
+
# end
|
54
|
+
# end
|
55
|
+
# else
|
56
|
+
# puts "Cache file '#{cache_filename}' not found"
|
57
|
+
# end
|
58
|
+
# else
|
59
|
+
# File.open( filename, 'r' ) do |io|
|
60
|
+
# read_formats( io, cache_filename )
|
61
|
+
# end
|
62
|
+
# end
|
63
|
+
|
64
|
+
# if @message_descriptions.size > 0
|
65
|
+
# File.open( filename, 'r' ) do |io|
|
66
|
+
# begin
|
67
|
+
# loop do
|
68
|
+
# message = read_message( io )
|
69
|
+
|
70
|
+
# if message.nil?
|
71
|
+
# puts "Failed to read message"
|
72
|
+
# break
|
73
|
+
# elsif message.description.name != "FMT"
|
74
|
+
# @messages << message
|
75
|
+
# end
|
76
|
+
|
77
|
+
# $stdout.printf "\rReading messages %d/%d", io.pos, @file_size
|
78
|
+
# end
|
79
|
+
# rescue StandardError => error
|
80
|
+
# puts "#{error.class}: #{error.message}"
|
81
|
+
# puts error.backtrace.join("\n")
|
82
|
+
# end
|
83
|
+
# end
|
84
|
+
# else
|
85
|
+
# raise "No message descriptions found"
|
86
|
+
# end
|
87
|
+
# puts
|
88
|
+
# end
|
89
|
+
|
90
|
+
# def read_message_header( io )
|
91
|
+
# byte = nil
|
92
|
+
|
93
|
+
# begin
|
94
|
+
|
95
|
+
# data = io.read(2)
|
96
|
+
|
97
|
+
# if data && data.length == 2
|
98
|
+
# loop do
|
99
|
+
|
100
|
+
# data << io.read_nonblock(1)
|
101
|
+
|
102
|
+
# # puts "#{data.unpack('CCC')[0,2]} == #{HEADER_MARKER}"
|
103
|
+
# if data.unpack('CCC')[0,2] == HEADER_MARKER
|
104
|
+
# byte = data.unpack('CCC').last & 0xFF
|
105
|
+
# # puts "Found message header #{data.unpack('CCC')}"
|
106
|
+
# # puts "message_type = #{'%02X'%byte}"
|
107
|
+
# break
|
108
|
+
# else
|
109
|
+
# data = data[1..-1]
|
110
|
+
# end
|
111
|
+
|
112
|
+
# end
|
113
|
+
# end
|
114
|
+
|
115
|
+
# rescue EOFError => error
|
116
|
+
# # Nothing to do.
|
117
|
+
# rescue StandardError => error
|
118
|
+
# puts error.message
|
119
|
+
# puts error.backtrace.join("\n")
|
120
|
+
# end
|
121
|
+
|
122
|
+
# return byte
|
123
|
+
# end
|
124
|
+
|
125
|
+
# def read_message( io )
|
126
|
+
|
127
|
+
# message = nil
|
128
|
+
# while message.nil? do
|
129
|
+
# message_type = read_message_header( io )
|
130
|
+
|
131
|
+
# if message_type && (message_type != FORMAT_MESSAGE.type)
|
132
|
+
# message_description = @message_descriptions[ message_type ]
|
133
|
+
|
134
|
+
# if message_description
|
135
|
+
# message_data = io.read( message_description.length - HEADER_LENGTH )
|
136
|
+
# message = message_description.parse_message( message_data )
|
137
|
+
# else
|
138
|
+
# puts "ERROR: Failed to get description for message of type '#{'0x%02X' % message_type}'"
|
139
|
+
# end
|
140
|
+
# elsif message_type.nil?
|
141
|
+
# break
|
142
|
+
# end
|
143
|
+
|
144
|
+
# # $stdout.printf "\rReading formats %d/%d", io.pos, @file_size
|
145
|
+
# end
|
146
|
+
|
147
|
+
# return message
|
148
|
+
# end
|
149
|
+
|
150
|
+
# def read_formats( io, cache_filename )
|
151
|
+
# loop do
|
152
|
+
# begin
|
153
|
+
# message_type = read_message_header( io )
|
154
|
+
|
155
|
+
# if message_type.nil?
|
156
|
+
# break
|
157
|
+
# elsif message_type == FORMAT_MESSAGE.type
|
158
|
+
# # puts "Found format message"
|
159
|
+
# message_description = Px4LogMessageDescription.new
|
160
|
+
# message_description.parse_from_io( io )
|
161
|
+
|
162
|
+
# unless @message_descriptions.keys.include? message_description.type
|
163
|
+
# @message_descriptions[message_description.type] = message_description
|
164
|
+
# end
|
165
|
+
|
166
|
+
# if message_description.name == "TIME"
|
167
|
+
# @px4_log_format = true
|
168
|
+
# end
|
169
|
+
# end
|
170
|
+
|
171
|
+
# $stdout.printf "\rReading formats %d/%d", io.pos, @file_size
|
172
|
+
|
173
|
+
# rescue StandardError => e
|
174
|
+
# puts "#{e.class}: #{e.message}"
|
175
|
+
# puts e.backtrace.join("\n")
|
176
|
+
# break
|
177
|
+
# end
|
178
|
+
# end
|
179
|
+
# $stdout.puts
|
180
|
+
|
181
|
+
# if cache_filename
|
182
|
+
# File.open( cache_filename, 'w+' ) do |io|
|
183
|
+
# @message_descriptions.each do |message_type,description|
|
184
|
+
# description_data = Marshal.dump( description )
|
185
|
+
# io.write( [ description_data.size ].pack('L') )
|
186
|
+
# io.write( description_data )
|
187
|
+
# end
|
188
|
+
# end
|
189
|
+
# end
|
190
|
+
# end
|
191
|
+
|
192
|
+
# end
|
193
|
+
|
194
|
+
|
195
|
+
# if ARGV.size == 2
|
196
|
+
# filename = ARGV[0]
|
197
|
+
# output_dir = ARGV[1]
|
198
|
+
# puts "Attempting to parse #{filename}"
|
199
|
+
|
200
|
+
# cache_filename = File.join( 'px4_csvs', "#{File.basename( filename, '.px4log' )}.px4log_description_cache" )
|
201
|
+
|
202
|
+
# log = Px4LogReader.new
|
203
|
+
# log.parse_log( filename, cache_filename )
|
204
|
+
|
205
|
+
# log.message_descriptions.each do |type,description|
|
206
|
+
# # puts description
|
207
|
+
# File.open(File.join(output_dir,"#{description.name.downcase}_log.csv"),"w+") do |io|
|
208
|
+
# io.puts description.to_csv_line
|
209
|
+
# end
|
210
|
+
# end
|
211
|
+
|
212
|
+
# last_timestamp = 0
|
213
|
+
# log.messages.each do |message|
|
214
|
+
# if message.description.name == "TIME"
|
215
|
+
# last_timestamp = message.get(0)
|
216
|
+
# end
|
217
|
+
|
218
|
+
# File.open(File.join(output_dir,"#{message.description.name.downcase}_log.csv"),"a+") do |io|
|
219
|
+
# io.puts message.to_csv_line(last_timestamp)
|
220
|
+
# end
|
221
|
+
# end
|
222
|
+
|
223
|
+
# else
|
224
|
+
|
225
|
+
# puts "Specify source and destination"
|
226
|
+
|
227
|
+
# end
|
228
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require_relative 'lib/px4_log_reader/version'
|
3
|
+
|
4
|
+
Gem::Specification.new do |s|
|
5
|
+
s.name = 'px4_log_reader'
|
6
|
+
s.version = Px4LogReader::VERSION
|
7
|
+
s.date = Time.now.to_date.strftime('%Y-%m-%d')
|
8
|
+
s.summary = "PX4 flight log reader"
|
9
|
+
s.description = "Px4LogReader is a gem for parsing PX4-format flight logs generated by the Pixhawk."
|
10
|
+
s.authors = [ "Robert Glissmann" ]
|
11
|
+
s.email = 'Robert.Glissmann@gmail.com'
|
12
|
+
s.files = `git ls-files`.split("\n")
|
13
|
+
s.licenses = ['BSD']
|
14
|
+
s.homepage = 'https://github.com/rgmann/px4_log_reader'
|
15
|
+
|
16
|
+
s.add_development_dependency 'rspec', '~> 3.1'
|
17
|
+
end
|
data/test/test.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
if ARGV.size == 2
|
2
|
+
filename = ARGV[0]
|
3
|
+
output_dir = ARGV[1]
|
4
|
+
puts "Attempting to parse #{filename}"
|
5
|
+
|
6
|
+
cache_filename = File.join( 'px4_csvs', "#{File.basename( filename, '.px4log' )}.px4log_description_cache" )
|
7
|
+
|
8
|
+
log = Px4LogReader.new
|
9
|
+
log.parse_log( filename, cache_filename )
|
10
|
+
|
11
|
+
log.message_descriptions.each do |type,description|
|
12
|
+
# puts description
|
13
|
+
File.open(File.join(output_dir,"#{description.name.downcase}_log.csv"),"w+") do |io|
|
14
|
+
io.puts description.to_csv_line
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
last_timestamp = 0
|
19
|
+
log.messages.each do |message|
|
20
|
+
if message.description.name == "TIME"
|
21
|
+
last_timestamp = message.get(0)
|
22
|
+
end
|
23
|
+
|
24
|
+
File.open(File.join(output_dir,"#{message.description.name.downcase}_log.csv"),"a+") do |io|
|
25
|
+
io.puts message.to_csv_line(last_timestamp)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
else
|
30
|
+
|
31
|
+
puts "Specify source and destination"
|
32
|
+
|
33
|
+
end
|
Binary file
|
Binary file
|
@@ -0,0 +1,173 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'px4_log_reader'
|
3
|
+
|
4
|
+
class TestLogBuffer < MiniTest::Test
|
5
|
+
|
6
|
+
def setup
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_buffer_create
|
10
|
+
|
11
|
+
buffer_size = 256
|
12
|
+
buffer = Px4LogReader::LogBuffer.new( buffer_size )
|
13
|
+
|
14
|
+
assert_equal buffer_size, buffer.data.size
|
15
|
+
assert_equal Array.new( buffer_size, 0x00 ), buffer.data
|
16
|
+
assert_equal 0, buffer.read_position
|
17
|
+
assert_equal 0, buffer.write_position
|
18
|
+
assert_equal true, buffer.empty?
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_buffer_read_write
|
23
|
+
|
24
|
+
buffer_size = 256
|
25
|
+
buffer = Px4LogReader::LogBuffer.new( buffer_size )
|
26
|
+
|
27
|
+
test_filename = './test_buffer_read_write.bin'
|
28
|
+
generate_test_file( test_filename, buffer_size )
|
29
|
+
|
30
|
+
test_file = File.open( test_filename,'rb+')
|
31
|
+
|
32
|
+
assert_equal true, buffer.empty?
|
33
|
+
|
34
|
+
buffer.write( test_file )
|
35
|
+
assert_equal 0, buffer.read_position
|
36
|
+
assert_equal 256, buffer.write_position
|
37
|
+
assert_equal false, buffer.empty?
|
38
|
+
|
39
|
+
test_read_1_size = 112
|
40
|
+
data = buffer.read( test_read_1_size )
|
41
|
+
assert_equal test_read_1_size, data.size
|
42
|
+
assert_equal test_read_1_size, buffer.read_position
|
43
|
+
assert_equal buffer_size, buffer.write_position
|
44
|
+
assert_equal false, buffer.empty?
|
45
|
+
|
46
|
+
test_read_2_size = 256
|
47
|
+
data = buffer.read( test_read_2_size )
|
48
|
+
assert_equal (buffer_size - test_read_1_size), data.size
|
49
|
+
assert_equal buffer_size, buffer.read_position
|
50
|
+
assert_equal buffer_size, buffer.write_position
|
51
|
+
assert_equal true, buffer.empty?
|
52
|
+
|
53
|
+
test_file.close
|
54
|
+
|
55
|
+
FileUtils.rm test_filename
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
def test_buffer_array
|
60
|
+
|
61
|
+
buffer_array = Px4LogReader::LogBufferArray.new
|
62
|
+
|
63
|
+
file_size = 256
|
64
|
+
test_filename = './test_buffer_array.bin'
|
65
|
+
generate_test_file( test_filename, file_size )
|
66
|
+
|
67
|
+
buffer_size = file_size
|
68
|
+
buffer_count = 4
|
69
|
+
buffer_array.set_file( File.open( test_filename, 'rb' ), buffer_size: buffer_size, buffer_count: buffer_count )
|
70
|
+
|
71
|
+
assert_equal buffer_count, buffer_array.buffers.count
|
72
|
+
assert_equal 0, buffer_array.current_buffer_index
|
73
|
+
|
74
|
+
buffer_array.buffers.each do |buffer|
|
75
|
+
assert_equal false, buffer.empty?
|
76
|
+
assert_equal 0, buffer.read_position
|
77
|
+
assert_equal (buffer_size / buffer_count), buffer.write_position
|
78
|
+
assert_equal (buffer_size / buffer_count), buffer.data.size
|
79
|
+
end
|
80
|
+
|
81
|
+
file_size.times do |index|
|
82
|
+
assert_equal index, buffer_array.read(1).unpack('C').first
|
83
|
+
end
|
84
|
+
|
85
|
+
FileUtils.rm test_filename
|
86
|
+
|
87
|
+
end
|
88
|
+
|
89
|
+
|
90
|
+
def test_buffer_array_refill
|
91
|
+
buffer_array = Px4LogReader::LogBufferArray.new
|
92
|
+
|
93
|
+
file_size = 256
|
94
|
+
test_filename = './test_buffer_array_refill.bin'
|
95
|
+
generate_test_file( test_filename, file_size )
|
96
|
+
|
97
|
+
buffer_size = file_size / 2
|
98
|
+
buffer_count = 4
|
99
|
+
single_buffer_size = buffer_size / buffer_count
|
100
|
+
buffer_array.set_file( File.open( test_filename, 'rb' ), buffer_size: buffer_size, buffer_count: buffer_count )
|
101
|
+
|
102
|
+
assert_equal buffer_count, buffer_array.buffers.count
|
103
|
+
assert_equal 0, buffer_array.current_buffer_index
|
104
|
+
|
105
|
+
buffer_array.buffers.each do |buffer|
|
106
|
+
assert_equal false, buffer.empty?
|
107
|
+
assert_equal (buffer_size / buffer_count), buffer.write_position
|
108
|
+
assert_equal (buffer_size / buffer_count), buffer.data.size
|
109
|
+
end
|
110
|
+
|
111
|
+
data = ''
|
112
|
+
|
113
|
+
# Read enough data to empty the first buffer.
|
114
|
+
data << buffer_array.read( single_buffer_size + single_buffer_size / 2 )
|
115
|
+
assert buffer_array.buffers[0].empty?
|
116
|
+
|
117
|
+
# Refill the empty buffer.
|
118
|
+
buffer_array.load_empty_buffers
|
119
|
+
assert_equal false, buffer_array.buffers[0].empty?
|
120
|
+
data << buffer_array.read( single_buffer_size / 2 + 3 * single_buffer_size )
|
121
|
+
assert_equal 5 * single_buffer_size, data.size
|
122
|
+
|
123
|
+
# Verify that we cannot read any more data.
|
124
|
+
assert_equal '', buffer_array.read( single_buffer_size )
|
125
|
+
|
126
|
+
assert_equal true, validate_data( test_filename, data )
|
127
|
+
|
128
|
+
|
129
|
+
FileUtils.rm test_filename
|
130
|
+
end
|
131
|
+
|
132
|
+
|
133
|
+
def generate_test_file( path, size )
|
134
|
+
test_filename = './test_buffer_read_write.bin'
|
135
|
+
File.open( path, 'wb+' ) do |io|
|
136
|
+
value = 0
|
137
|
+
size.times do
|
138
|
+
io.write( [ value ].pack('C') )
|
139
|
+
value = ( value + 1 ) % 256
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
def validate_data( path, data, max_bytes = nil )
|
145
|
+
equal = true
|
146
|
+
|
147
|
+
max_bytes = data.size if max_bytes.nil?
|
148
|
+
|
149
|
+
data = data.unpack('C*')
|
150
|
+
|
151
|
+
File.open( path, 'rb' ) do |io|
|
152
|
+
comp_index = 0
|
153
|
+
while ( comp_index < max_bytes ) && equal do
|
154
|
+
begin
|
155
|
+
byte = io.read(1).unpack('C').first
|
156
|
+
if data[comp_index] != byte
|
157
|
+
puts "mismatch at offset=#{comp_index}: expected '#{"0x%02X"%byte}; found '#{"0x%02X"%data[comp_index]}'"
|
158
|
+
equal = false
|
159
|
+
end
|
160
|
+
rescue EOFError => error
|
161
|
+
puts "reached EOF before end of data"
|
162
|
+
equal = false
|
163
|
+
end
|
164
|
+
comp_index += 1
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
return equal
|
169
|
+
end
|
170
|
+
|
171
|
+
end
|
172
|
+
|
173
|
+
|