px4_log_reader 0.0.7 → 1.0.2

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 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