px4_log_reader 0.0.7 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0490cff29bd96afe03a73177375e181a7c5ee9f1
4
- data.tar.gz: 98611a918006a2d12bbaedf6656db34b39f2ed9f
3
+ metadata.gz: 4c75862bd918e0d1d080d9b7a4cb52d342ddd3b8
4
+ data.tar.gz: 2002ad3763f5a4cd976264036b7a7b7733aebbd7
5
5
  SHA512:
6
- metadata.gz: cf1783f773a16c17214a83167cf16ae520c6b3c59e2521068f54fd3620d6f3d244d1ca01b6546f3a85935a849279bd9cfc0b28c14004f247a5436f5102131ef4
7
- data.tar.gz: 8f653267c2e89885394f3a2a990e28b9af69cda03eef319d65c90cc6b7431a8a75fe206d2e3d971c2024051b5324e5f01feade8ea784b447cb300d261fba4ba4
6
+ metadata.gz: c95423a6772f8ce0c6243bdb2b528d21b47f86784d91de05a154877db24a8a08cae4ddbff196b691fe84bc253785c5e3dec356a24ed1a4d7c377ad4ef75a38a1
7
+ data.tar.gz: b87364ae76b58ae13fa860358c8973fd68351fd328a1b99f117773ab53f5964f8be3851400a820ed382ea2e1da2d7f38a0aca37d624d482b6510110c7e325ebf
data/.gitignore CHANGED
@@ -2,3 +2,4 @@
2
2
  .DS_Store
3
3
  *.gem
4
4
  default_log_path.txt
5
+ descriptors.txt
data/Rakefile CHANGED
@@ -131,7 +131,7 @@ task :cut_log, :count, :skip, :filename do |t,args|
131
131
 
132
132
  read_count = args[:count].to_i
133
133
  read_count.times do
134
- message = Px4LogReader::LogFile.read_message( input, message_descriptors )
134
+ message, offset = Px4LogReader::LogFile.read_message( input, message_descriptors )
135
135
  Px4LogReader::LogFile.write_message( output, message )
136
136
  end
137
137
  end
@@ -148,7 +148,9 @@ task :dump, :filename do |t,args|
148
148
 
149
149
  File.open( args[:filename], 'r' ) do |input|
150
150
  count = 1
151
- while ( message = Px4LogReader::LogFile.read_message( input, message_descriptors ) ) do
151
+ loop do
152
+ message, offset = Px4LogReader::LogFile.read_message( input, message_descriptors )
153
+ break if message.nil?
152
154
  puts "#{count}) #{message.descriptor.name}, #{'%02X'%message.descriptor.type}"
153
155
  count += 1
154
156
  end
@@ -35,17 +35,32 @@ module Px4LogReader
35
35
  module LogFile
36
36
 
37
37
  HEADER_MARKER = [0xA3,0x95]
38
- HEADER_LENGTH = 3
38
+ HEADER_LENGTH = HEADER_MARKER.length + 1
39
39
  FORMAT_DESCRIPTOR_TABLE = { FORMAT_MESSAGE.type => FORMAT_MESSAGE }.freeze
40
40
 
41
- def self.read_descriptors( buffered_io, descriptor_cache=nil )
41
+ @@debug = false
42
+
43
+ def self.enable_debug( enable )
44
+ @@debug = enable
45
+ end
46
+
47
+ def self.read_descriptors( file, descriptor_cache=nil, &block )
42
48
 
43
49
  message_descriptors = {}
44
50
 
45
- while ( message_descriptor = read_descriptor( buffered_io ) ) do
46
- if !message_descriptors.keys.include? message_descriptor.type
47
- message_descriptors[ message_descriptor.type ] = message_descriptor
51
+ loop do
52
+
53
+ message_descriptor, file_offset = read_descriptor( file )
54
+
55
+ if message_descriptor
56
+ if !message_descriptors.keys.include? message_descriptor.type
57
+ message_descriptors[ message_descriptor.type ] = message_descriptor
58
+ yield message_descriptor if block_given?
59
+ end
60
+ else
61
+ break
48
62
  end
63
+
49
64
  end
50
65
 
51
66
  # If a cache filename was supplied, dump the descriptors to the cache
@@ -56,80 +71,108 @@ module Px4LogReader
56
71
  return message_descriptors
57
72
  end
58
73
 
59
- def self.read_descriptor( buffered_io, skip_corrupt=true )
74
+ def self.read_descriptor( file, skip_corrupt=true, &block )
60
75
 
61
76
  message_descriptor = nil
77
+ offset = nil
62
78
 
63
79
  begin
64
80
 
65
- descriptor_message = read_message( buffered_io, FORMAT_DESCRIPTOR_TABLE )
81
+ descriptor_message, offset = read_message( file, FORMAT_DESCRIPTOR_TABLE, true, &block )
66
82
 
67
83
  if descriptor_message
68
-
69
84
  message_descriptor = Px4LogReader::MessageDescriptor.new
70
85
  message_descriptor.from_message( descriptor_message )
71
-
72
86
  end
73
87
 
74
88
  rescue Px4LogReader::InvalidDescriptorError => error
75
89
 
90
+ puts "#{error.class}: #{error.message}"
91
+ puts error.backtrace.join("\n")
92
+
76
93
  if skip_corrupt
77
94
  retry
78
95
  else
79
96
  raise error
80
97
  end
81
98
 
82
- rescue StandardError => e
83
- puts "#{e.class}: #{e.message}"
84
- puts e.backtrace.join("\n")
99
+ rescue StandardError => error
100
+ puts "#{error.class}: #{error.message}"
101
+ puts error.backtrace.join("\n")
85
102
  end
86
103
 
87
- return message_descriptor
104
+ return message_descriptor, offset
88
105
  end
89
106
 
90
- def self.read_message( buffered_io, message_descriptors )
107
+ def self.read_message( file, message_descriptors, stop_on_no_match = false, &block )
91
108
 
92
109
  message = nil
93
- while message.nil? do
94
- message_type = read_message_header( buffered_io )
110
+ offset = nil
111
+ eof = false
112
+
113
+ while message.nil? && !eof do
114
+ message_type, offset = read_message_header( file )
115
+ eof = message_type.nil?
95
116
 
96
117
  if message_type
97
118
 
98
119
  message_descriptor = message_descriptors[ message_type ]
99
120
 
121
+ yield offset if block_given?
122
+
100
123
  if message_descriptor
101
- message_data = buffered_io.read( message_descriptor.length - HEADER_LENGTH )
124
+
125
+ message_data = file.read( message_descriptor.length - HEADER_LENGTH )
126
+ print_data( message_data ) if @@debug
102
127
  message = message_descriptor.unpack_message( message_data )
103
- end
104
128
 
105
- elsif message_type.nil?
106
- break
129
+ elsif stop_on_no_match
130
+
131
+ # Seek back to the beginning of the header for the non-
132
+ # matching message.
133
+ file.seek( -1 * HEADER_LENGTH, IO::SEEK_CUR )
134
+ break
135
+
136
+ end
107
137
  end
108
138
  end
109
139
 
110
- return message
140
+ return message, offset
111
141
  end
112
142
 
113
- def self.read_message_header( buffered_io )
143
+ #
144
+ # Read the next message header from the input stream. Returns the
145
+ # message type and current file offset. If no header is found, the
146
+ # returned message type is nil and the offset is the end of the file.
147
+ #
148
+ def self.read_message_header( file )
114
149
  message_type = nil
150
+ offset = nil
151
+ drop_count = 0
115
152
 
116
153
  begin
117
154
 
118
- data = buffered_io.read_nonblock( HEADER_MARKER.length )
155
+ data = file.read_nonblock( HEADER_MARKER.length )
156
+ offset = file.pos
119
157
 
120
158
  if data && ( data.length == HEADER_MARKER.length )
121
159
 
122
160
  while !data.empty? && message_type.nil? do
123
161
 
124
- if ( byte = buffered_io.read_nonblock( 1 ) )
162
+ if ( byte = file.read_nonblock( 1 ) )
125
163
  data << byte
126
164
  end
165
+ offset = file.pos
127
166
 
128
167
  if data.length >= ( HEADER_MARKER.length + 1 )
129
- if data.unpack('CCC')[0,2] == HEADER_MARKER
130
- message_type = data.unpack('CCC').last & 0xFF
168
+
169
+ unpacked_data = data.unpack('C*')
170
+
171
+ if unpacked_data[ 0, HEADER_MARKER.length ] == HEADER_MARKER
172
+ message_type = unpacked_data.last & 0xFF
131
173
  else
132
174
  data = data[1..-1]
175
+ drop_count += 1
133
176
  end
134
177
  else
135
178
  data = []
@@ -145,7 +188,7 @@ module Px4LogReader
145
188
  puts error.backtrace.join("\n")
146
189
  end
147
190
 
148
- return message_type
191
+ return message_type, offset
149
192
  end
150
193
 
151
194
 
@@ -155,6 +198,21 @@ module Px4LogReader
155
198
  io.write( message.pack )
156
199
  end
157
200
 
201
+
202
+ def self.print_data( buffer )
203
+ text = ''
204
+ buffer.unpack( 'C*' ).each_with_index do |byte,count|
205
+ if (count % 16) == 0
206
+ text << "#{count/16}: "
207
+ end
208
+ text << ('%02X'%byte) << ' '
209
+ if (((count+1) % 16) == 0)
210
+ text << "\n"
211
+ end
212
+ end
213
+ puts text
214
+ end
215
+
158
216
  end
159
217
 
160
218
  end
@@ -34,6 +34,8 @@ module Px4LogReader
34
34
 
35
35
  class MessageDescriptor
36
36
 
37
+ @@validation_level = :strict
38
+
37
39
  attr_reader :name
38
40
  attr_reader :type
39
41
  attr_reader :length
@@ -85,9 +87,16 @@ module Px4LogReader
85
87
 
86
88
  fields = fields_string.split(',')
87
89
 
88
- if fields.length != @format.length
90
+ if (@@validation_level == :strict) && (fields.length != @format.length)
91
+ puts "fields = #{fields_string}"
92
+ puts "format = '#{@format}'"
93
+ puts "length = #{@length}"
94
+ puts "type = 0x#{'%02X'% @type}"
89
95
  raise InvalidDescriptorError.new(
90
96
  "Field count must match format length: expected #{@format.length}; found #{fields.length}")
97
+ elsif (@@validation_level == :lenient) && (fields.length > @format.length)
98
+ raise InvalidDescriptorError.new(
99
+ "Field count is greater than format length: expected #{@format.length}; found #{fields.length}")
91
100
  else
92
101
  @field_list = MessageDescriptor.build_field_list( fields )
93
102
  end
@@ -209,7 +218,7 @@ module Px4LogReader
209
218
  FORMAT_MESSAGE = Px4LogReader::MessageDescriptor.new({
210
219
  name: 'FMT',
211
220
  type: 0x80,
212
- length: 86,
221
+ length: 89, # header.size + B(1) + B(1) + n(4) + N(16) + Z(64)
213
222
  format: 'BBnNZ',
214
223
  fields: [ "Type", "Length", "Name", "Format", "Labels" ] }).freeze
215
224
 
@@ -0,0 +1,56 @@
1
+ #
2
+ # Copyright (c) 2016, Robert Glissmann
3
+ # All rights reserved.
4
+ #
5
+ # Redistribution and use in source and binary forms, with or without
6
+ # modification, are permitted provided that the following conditions are met:
7
+ #
8
+ # * Redistributions of source code must retain the above copyright notice, this
9
+ # list of conditions and the following disclaimer.
10
+ #
11
+ # * Redistributions in binary form must reproduce the above copyright notice,
12
+ # this list of conditions and the following disclaimer in the documentation
13
+ # and/or other materials provided with the distribution.
14
+ #
15
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
16
+ # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17
+ # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18
+ # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
19
+ # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20
+ # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
22
+ # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23
+ # OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24
+ # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25
+ #
26
+
27
+ # %% license-end-token %%
28
+ #
29
+ # Author: Robert.Glissmann@gmail.com (Robert Glissmann)
30
+ #
31
+ #
32
+
33
+ module Px4LogReader
34
+
35
+ class Progress
36
+
37
+ attr_accessor :file_size
38
+
39
+ def initialize( active_file )
40
+ @active_file = active_file
41
+
42
+ active_file.seek( 0, IO::SEEK_END )
43
+ @file_size = active_file.pos
44
+ active_file.seek( 0 )
45
+ end
46
+
47
+ def file_offset
48
+ @active_file.pos
49
+ end
50
+
51
+ def percentage
52
+ return ( file_offset.to_f / @file_size.to_f )
53
+ end
54
+ end
55
+
56
+ end
@@ -32,13 +32,20 @@
32
32
 
33
33
  module Px4LogReader
34
34
 
35
- def self.open_common( file, options, &block )
35
+ # Attach a reader to an existing input stream.
36
+ #
37
+ # @param input_stream [IO] Valid input stream
38
+ # @param options [Hash] Reader options hash
39
+ # @param block Optional block
40
+ #
41
+ def self.attach( input_stream, options, &block )
36
42
 
37
- reader = Reader.new( file, options )
43
+ reader = Reader.new( input_stream, options )
38
44
 
39
45
  yield reader if block_given?
40
46
 
41
47
  return reader
48
+
42
49
  end
43
50
 
44
51
  def self.open( filename, options = {}, &block )
@@ -46,7 +53,7 @@ module Px4LogReader
46
53
  reader = nil
47
54
 
48
55
  if File.exist?( filename )
49
- reader = self.open_common( File.open( filename, 'r' ), options, &block )
56
+ reader = self.attach( File.open( filename, 'rb' ), options, &block )
50
57
  end
51
58
 
52
59
  return reader
@@ -57,7 +64,7 @@ module Px4LogReader
57
64
  reader = nil
58
65
 
59
66
  if File.exist?( filename )
60
- reader = self.open_common( File.open( filename, 'r' ), options, &block )
67
+ reader = self.attach( File.open( filename, 'rb' ), options, &block )
61
68
  else
62
69
  raise FileNotFoundError.new( filename )
63
70
  end
@@ -65,11 +72,19 @@ module Px4LogReader
65
72
  return reader
66
73
  end
67
74
 
75
+ # Container to hold the most recent copy of each message type
68
76
  class Context
77
+
69
78
  attr_reader :messages
79
+
70
80
  def initialize
71
- messages = {}
81
+ @messages = {}
72
82
  end
83
+
84
+ # Query the context for the most recent copy of a message by name
85
+ #
86
+ # @param name [String] Message name
87
+ #
73
88
  def find_by_name( name )
74
89
  named_message = nil
75
90
  @messages.values.each do |message|
@@ -79,40 +94,62 @@ module Px4LogReader
79
94
  end
80
95
  return named_message
81
96
  end
97
+
98
+ # Query the context for the most recent copy of a message by type
99
+ #
100
+ # @param type [Fixnum] Message type
101
+ #
82
102
  def find_by_type( type )
83
103
  return @messages[ type ]
84
104
  end
105
+
106
+ # Set the most recent copy of a message by type. Any existing message
107
+ # is overwritten.
108
+ #
109
+ # @param message [LogMessage] Message instance
110
+ #
85
111
  def set( message )
86
112
  @messages[ message.descriptor.type ] = message.dup
87
113
  end
114
+
88
115
  end
89
116
 
90
- class Reader
117
+ class Reader
118
+
119
+ attr_reader :progress
120
+ attr_reader :context
91
121
 
92
122
  def initialize( file, options )
93
123
 
94
124
  opts = {
95
125
  cache_filename: '',
96
- buffer_size_kb: 10 * 1024
97
126
  }.merge( options )
98
127
 
99
128
  @message_descriptors = {}
100
- @buffers = LogBufferArray.new
101
129
  @descriptor_cache = nil
102
130
  @context = Context.new
103
131
 
104
132
  @log_file = file
105
- # @buffers.set_file( @log_file, load_buffers: true )
133
+ @progress = Progress.new( @log_file )
106
134
 
107
135
  @descriptor_cache = MessageDescriptorCache.new( opts[:cache_filename] )
108
136
  end
109
137
 
110
- def descriptors
138
+ #
139
+ # Get the list of descriptors associated with the open PX4 log file.
140
+ # If a valid descriptor cache was specified at startup, the descriptors
141
+ # are loaded from the cache. Otherwise, the descriptors are parsed from
142
+ # the open log.
143
+ #
144
+ # @param [block] optional block is passed each descriptor as it is read
145
+ # @return descriptors [Array] Array of descriptors
146
+ #
147
+ def descriptors( &block )
111
148
  if @log_file && @message_descriptors.empty?
112
149
  if @descriptor_cache && @descriptor_cache.exist?
113
150
  @message_descriptors = @descriptor_cache.read_descriptors
114
151
  else
115
- @message_descriptors = LogFile::read_descriptors( @log_file, @descriptor_cache )
152
+ @message_descriptors = LogFile::read_descriptors( @log_file, @descriptor_cache, &block )
116
153
  end
117
154
 
118
155
  @message_descriptors[ FORMAT_MESSAGE.type ] = FORMAT_MESSAGE
@@ -121,6 +158,16 @@ module Px4LogReader
121
158
  return @message_descriptors
122
159
  end
123
160
 
161
+ #
162
+ # Iterate over all log messages. Embedded message descriptors are skipped.
163
+ # If a "with" list is supplied, only messages in the list are passed to
164
+ # the caller-supplied block. If a "without" list supplied, all messages
165
+ # except those in the list are passed to the caller-supplied block. The
166
+ # caller must supply a block.
167
+ #
168
+ # @param options [Hash] options
169
+ # @param block [Block] block takes message as argument
170
+ #
124
171
  def each_message( options = {}, &block )
125
172
 
126
173
  opts ={
@@ -131,7 +178,11 @@ module Px4LogReader
131
178
  opts[:with].map! do |val|
132
179
  if val.class == String
133
180
  descriptor = descriptors.values.find { |desc| desc.name == val }
134
- val = descriptor.type
181
+ if descriptor
182
+ val = descriptor.type
183
+ else
184
+ puts "Failed to find descriptor with name '#{val}'"
185
+ end
135
186
  end
136
187
  end
137
188
 
@@ -151,19 +202,19 @@ module Px4LogReader
151
202
 
152
203
  loop do
153
204
 
154
- message = LogFile::read_message( @log_file, @message_descriptors )
205
+ message, offset = LogFile::read_message( @log_file, @message_descriptors )
155
206
  break if message.nil?
156
207
 
157
- # Added message to the set of latest messages.
208
+ # Add message to the set of latest messages.
158
209
  @context.set( message )
159
210
 
160
211
  if opts[:with].empty?
161
- if !opts[:without].include?( message.descriptor.name )
162
- yield message, @context
212
+ if !opts[:without].include?( message.descriptor.type )
213
+ yield message
163
214
  end
164
215
  else
165
216
  if opts[:with].include?( message.descriptor.type )
166
- yield message, @context
217
+ yield message
167
218
  end
168
219
  end
169
220
 
@@ -175,17 +226,19 @@ module Px4LogReader
175
226
 
176
227
  end
177
228
 
178
- # def rewind
179
- # if @log_file
180
-
181
- # @log_file.rewind
182
- # @buffers.load_buffers
183
-
184
- # end
185
- # end
186
-
187
- # def seek( offset )
188
- # end
229
+ #
230
+ # Seek to the specified file offset. If the offset is greater than the
231
+ # file size, seeks to the end of the file.
232
+ #
233
+ # @param offset [Fixnum] File offset in bytes
234
+ #
235
+ def seek( offset )
236
+ if @log_file
237
+ seek_offset = offset
238
+ seek_offset = @progress.file_size if offset > @progress.file_size
239
+ @log_file.seek( seek_offset, IO::SEEK_SET )
240
+ end
241
+ end
189
242
 
190
243
  end
191
244
 
@@ -1,3 +1,3 @@
1
1
  module Px4LogReader
2
- VERSION = '0.0.7'
2
+ VERSION = '1.0.2'
3
3
  end
@@ -37,6 +37,7 @@ require 'px4_log_reader/invalid_descriptor_error'
37
37
  require 'px4_log_reader/log_message'
38
38
  require 'px4_log_reader/message_descriptor'
39
39
  require 'px4_log_reader/log_buffer'
40
+ require 'px4_log_reader/progress'
40
41
  require 'px4_log_reader/log_file'
41
42
  require 'px4_log_reader/message_descriptor_cache'
42
43
  require 'px4_log_reader/reader'
Binary file
@@ -41,96 +41,67 @@ class TestLogFile < MiniTest::Test
41
41
  def teardown
42
42
  end
43
43
 
44
- class MockFileIO
45
-
46
- attr_reader :buffer
47
-
48
- def initialize
49
- @buffer = ''
50
- end
51
-
52
- def reset
53
- @buffer = ''
54
- end
55
-
56
- def read( byte_count = nil )
57
- data = nil
58
-
59
- if byte_count >= @buffer.size
60
- data = @buffer.dup
61
- @buffer = ''
62
- else
63
- data = @buffer[ 0, byte_count ].dup
64
- @buffer = @buffer[ byte_count .. -1 ]
65
- end
66
-
67
- data
68
- end
69
-
70
- def read_nonblock( byte_count = nil )
71
- return read( byte_count )
72
- end
73
-
74
- def write( data )
75
- @buffer << data
76
- end
77
-
78
- end
79
-
80
44
  def test_read_header
81
45
 
82
46
  # First, test that read_message_header returns null if the file is exhausted
83
47
  # without finding a message header.
84
- mock_io = MockFileIO.new
85
- mock_io.write( rand_data( 256 ).pack('C*') )
86
-
87
- assert_nil Px4LogReader::LogFile.read_message_header( mock_io )
88
- assert_equal true, mock_io.buffer.empty?
48
+ mock_io( rand_data( 256 ).pack('C*') ) do |writer,reader|
49
+ assert_equal [ nil, 256 ], Px4LogReader::LogFile.read_message_header( reader )
50
+ assert_equal 256, reader.pos
51
+ end
89
52
 
90
53
 
91
54
  # Insert a message header with type = 0x80 into the middle of the data.
92
- mock_io = MockFileIO.new
93
-
94
55
  test_data = rand_data( 256 )
95
56
  test_data.concat( Px4LogReader::LogFile::HEADER_MARKER )
96
57
  test_data.concat( [ Px4LogReader::FORMAT_MESSAGE.type ] )
97
- test_data.concat( rand_data( 256 ) )
98
- mock_io.write( test_data.pack('C*') )
58
+ test_data.concat( rand_data( 256 ) )
59
+ mock_io( test_data.pack('C*') ) do |writer,reader|
99
60
 
100
- assert_equal Px4LogReader::FORMAT_MESSAGE.type, Px4LogReader::LogFile.read_message_header( mock_io )
101
- assert_equal 256, mock_io.buffer.size
61
+ message_type, offset = Px4LogReader::LogFile.read_message_header( reader )
102
62
 
103
- assert_nil Px4LogReader::LogFile.read_message_header( mock_io )
104
- assert_equal true, mock_io.buffer.empty?
63
+ assert_equal Px4LogReader::FORMAT_MESSAGE.type, message_type
64
+ assert_equal 256 + Px4LogReader::LogFile::HEADER_LENGTH, offset
105
65
 
66
+ message_type, offset = Px4LogReader::LogFile.read_message_header( reader )
67
+ assert_nil message_type
68
+ assert_equal true, reader.eof?
69
+ assert_equal test_data.size, offset
70
+
71
+ end
106
72
 
107
- # Test with a buffer that ends in a message header sync pattern.
108
- mock_io = MockFileIO.new
109
73
 
74
+ # Test with a buffer that ends in a message header sync pattern.
110
75
  test_data = rand_data( 64 )
111
76
  test_data.concat( Px4LogReader::LogFile::HEADER_MARKER )
112
- mock_io.write( test_data.pack('C*') )
77
+ mock_io( test_data.pack('C*') ) do |writer,reader|
113
78
 
114
- assert_nil Px4LogReader::LogFile.read_message_header( mock_io )
115
- assert_equal true, mock_io.buffer.empty?
79
+ message_type, offset = Px4LogReader::LogFile.read_message_header( reader )
80
+ assert_nil message_type
81
+ assert_equal true, reader.eof?
82
+ assert_equal test_data.size, offset
83
+
84
+ end
116
85
 
117
86
  end
118
87
 
119
88
  def test_write_message
120
89
 
121
- mock_io = MockFileIO.new
122
-
123
90
  fields = [ 0x24, 32, 'test', 'IIbMh', 'id,counts,flag,length,ord' ]
124
91
 
125
92
  message = Px4LogReader::LogMessage.new(
126
93
  Px4LogReader::FORMAT_MESSAGE,
127
94
  fields )
128
95
 
129
- Px4LogReader::LogFile.write_message( mock_io, message )
96
+ mock_io do |writer,reader|
97
+ Px4LogReader::LogFile.write_message( writer, message )
130
98
 
131
- expected_length = Px4LogReader::FORMAT_MESSAGE.length + Px4LogReader::LogFile::HEADER_MARKER.length + 1
132
- assert_equal expected_length, mock_io.buffer.length
133
- assert_equal [ 0xA3, 0x95, 0x80 ], mock_io.buffer[0,3].unpack('C*')
99
+ expected_length = Px4LogReader::FORMAT_MESSAGE.length
100
+ assert_equal expected_length, writer.pos
101
+
102
+ header = reader.read( 3 )
103
+ assert_equal [ 0xA3, 0x95, 0x80 ], header.unpack('C*')
104
+ end
134
105
 
135
106
  end
136
107
 
@@ -155,4 +126,34 @@ class TestLogFile < MiniTest::Test
155
126
  return test_data
156
127
  end
157
128
 
129
+ def mock_io( buffer = nil )
130
+
131
+ temp_filename = File.join( 'test', 'temp_test_data', 'mock_io.dat' )
132
+ unless Dir.exist?( File.dirname( temp_filename ) )
133
+ FileUtils.mkdir( File.dirname( temp_filename ) )
134
+ end
135
+
136
+ # Create the file.
137
+ FileUtils.touch( temp_filename )
138
+
139
+ # Open the file for writing and reading and writing.
140
+ writer = File.open( temp_filename, 'wb' )
141
+ if buffer
142
+ writer.write( buffer )
143
+ writer.flush
144
+ end
145
+
146
+ reader = File.open( temp_filename, 'rb' )
147
+
148
+ yield writer, reader if block_given?
149
+
150
+ reader.close
151
+ writer.close
152
+
153
+ if Dir.exist?( File.dirname( temp_filename ) )
154
+ FileUtils.rm_rf( File.dirname( temp_filename ) )
155
+ end
156
+
157
+ end
158
+
158
159
  end
data/test/test_reader.rb CHANGED
@@ -84,36 +84,56 @@ class TestReader < MiniTest::Test
84
84
  # The log file associated with this test case contains the following
85
85
  # messages in the specified order. Validate the order and context.
86
86
  #
87
- # 1) TIME
88
- # 2) STAT
89
- # 3) IMU
90
- # 4) SENS
91
- # 5) IMU1
92
- # 6) VTOL
93
- # 7) GPS
87
+ # 1: LPOS
88
+ # 2: GPOS
89
+ # 3: BATT
90
+ # 4: PWR
91
+ # 5: EST0
92
+ # 6: EST1
93
+ # 7: EST2
94
+ # 8: EST3
95
+ # 9: CTS
96
+ # 10: ATT
97
+ # 11: TIME
98
+ # 12: IMU
99
+ # 13: SENS
100
+ # 14: IMU1
101
+ # 15: ATSP
102
+ # 16: ARSP
103
+ # 17: OUT0
104
+ # 18: ATTC
94
105
  #
95
106
  index = 0
96
- expected_message_names = ['TIME','STAT','IMU','SENS','IMU1','VTOL','GPS']
97
- expected_message_types = [0x81,0x0A,0x04,0x05,0x16,0x2B,0x08]
107
+ expected_messages = [
108
+ ['LPOS',0x06],
109
+ ['GPOS',0x10],
110
+ ['BATT',0x14],
111
+ ['PWR',0x18],
112
+ ['EST0',0x20],
113
+ ['EST1',0x21],
114
+ ['EST2',0x22]]
98
115
 
99
- reader.each_message do |message,context|
116
+ reader.each_message do |message|
100
117
 
101
- expected_name = expected_message_names[ index ]
102
- expected_type = expected_message_types[ index ]
118
+ # puts "#{index+1}: #{message.descriptor.name}, #{'%02X'%message.descriptor.type}"
119
+ break if index >= expected_messages.size
120
+
121
+ expected_name = expected_messages[ index ][0]
122
+ expected_type = expected_messages[ index ][1]
103
123
 
104
124
  assert_equal expected_name, message.descriptor.name
105
125
  assert_equal expected_type, message.descriptor.type
106
126
 
107
- context_message = context.find_by_name( expected_name )
127
+ context_message = reader.context.find_by_name( expected_name )
108
128
  refute_nil context_message
109
129
  assert_equal expected_name, context_message.descriptor.name
110
130
 
111
- context_message = context.find_by_type( expected_type )
131
+ context_message = reader.context.find_by_type( expected_type )
112
132
  refute_nil context_message
113
133
  assert_equal expected_type, context_message.descriptor.type
114
134
 
115
135
  index += 1
116
- assert_equal index, context.messages.size
136
+ assert_equal index, reader.context.messages.size
117
137
 
118
138
  end
119
139
 
@@ -133,40 +153,36 @@ class TestReader < MiniTest::Test
133
153
 
134
154
  log_file_opened = true
135
155
 
136
- # The log file associated with this test case contains the following
137
- # messages in the specified order. Validate the order and context.
138
- #
139
- # 1) TIME
140
- # 2) STAT
141
- # 3) IMU
142
- # 4) SENS
143
- # 5) IMU1
144
- # 6) VTOL
145
- # 7) GPS
146
- #
156
+ # See test_reader for the un-filtered list of messages. This test
157
+ # expects the same list, minus the GPOS and EST1 messages.
147
158
 
148
159
  index = 0
149
- expected_message_names = ['TIME','IMU','SENS','VTOL','GPS']
150
- expected_message_types = [0x81,0x04,0x05,0x2B,0x08]
160
+ expected_messages = [
161
+ ['LPOS',0x06],
162
+ ['BATT',0x14],
163
+ ['PWR',0x18],
164
+ ['EST0',0x20],
165
+ ['EST2',0x22]]
166
+
167
+ reader.each_message( { without: ['GPOS','EST1'] } ) do |message|
151
168
 
152
- reader.each_message( { without: ['STAT','IMU1'] } ) do |message,context|
169
+ break if index >= expected_messages.size
153
170
 
154
- expected_name = expected_message_names[ index ]
155
- expected_type = expected_message_types[ index ]
171
+ expected_name = expected_messages[ index ][0]
172
+ expected_type = expected_messages[ index ][1]
156
173
 
157
174
  assert_equal expected_name, message.descriptor.name
158
175
  assert_equal expected_type, message.descriptor.type
159
176
 
160
- context_message = context.find_by_name( expected_name )
177
+ context_message = reader.context.find_by_name( expected_name )
161
178
  refute_nil context_message
162
179
  assert_equal expected_name, context_message.descriptor.name
163
180
 
164
- context_message = context.find_by_type( expected_type )
181
+ context_message = reader.context.find_by_type( expected_type )
165
182
  refute_nil context_message
166
183
  assert_equal expected_type, context_message.descriptor.type
167
184
 
168
185
  index += 1
169
- assert_equal index, context.messages.size
170
186
 
171
187
  end
172
188
 
@@ -184,40 +200,33 @@ class TestReader < MiniTest::Test
184
200
 
185
201
  log_file_opened = true
186
202
 
187
- # The log file associated with this test case contains the following
188
- # messages in the specified order. Validate the order and context.
189
- #
190
- # 1) TIME
191
- # 2) STAT
192
- # 3) IMU
193
- # 4) SENS
194
- # 5) IMU1
195
- # 6) VTOL
196
- # 7) GPS
197
- #
203
+ # See test_reader for the un-filtered list of messages. This test
204
+ # expects just the GPOS and PWR messages.
198
205
 
199
206
  index = 0
200
- expected_message_names = ['TIME','SENS']
201
- expected_message_types = [0x81,0x05]
207
+ expected_messages = [
208
+ ['GPOS',0x10],
209
+ ['PWR',0x18]]
210
+
211
+ reader.each_message( { with: ['GPOS','PWR'] } ) do |message|
202
212
 
203
- reader.each_message( { with: ['TIME','SENS'] } ) do |message,context|
213
+ break if index >= expected_messages.size
204
214
 
205
- expected_name = expected_message_names[ index ]
206
- expected_type = expected_message_types[ index ]
215
+ expected_name = expected_messages[ index ][0]
216
+ expected_type = expected_messages[ index ][1]
207
217
 
208
218
  assert_equal expected_name, message.descriptor.name
209
219
  assert_equal expected_type, message.descriptor.type
210
220
 
211
- context_message = context.find_by_name( expected_name )
221
+ context_message = reader.context.find_by_name( expected_name )
212
222
  refute_nil context_message
213
223
  assert_equal expected_name, context_message.descriptor.name
214
224
 
215
- context_message = context.find_by_type( expected_type )
225
+ context_message = reader.context.find_by_type( expected_type )
216
226
  refute_nil context_message
217
227
  assert_equal expected_type, context_message.descriptor.type
218
228
 
219
229
  index += 1
220
- assert_equal index, context.messages.size
221
230
 
222
231
  end
223
232
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: px4_log_reader
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.7
4
+ version: 1.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Robert Glissmann
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-09-11 00:00:00.000000000 Z
11
+ date: 2016-10-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rspec
@@ -46,6 +46,7 @@ files:
46
46
  - lib/px4_log_reader/log_message.rb
47
47
  - lib/px4_log_reader/message_descriptor.rb
48
48
  - lib/px4_log_reader/message_descriptor_cache.rb
49
+ - lib/px4_log_reader/progress.rb
49
50
  - lib/px4_log_reader/reader.rb
50
51
  - lib/px4_log_reader/version.rb
51
52
  - px4_log_reader.gemspec