Class: OpenC3::PacketLogReader

Inherits:
Object
  • Object
show all
Includes:
PacketLogConstants
Defined in:
lib/openc3/logs/packet_log_reader.rb

Overview

Reads a packet log of either commands or telemetry.

Direct Known Subclasses

BufferedPacketLogReader

Constant Summary collapse

MAX_READ_SIZE =
1000000000

Constants included from PacketLogConstants

OpenC3::PacketLogConstants::COSMOS2_FILE_HEADER, OpenC3::PacketLogConstants::COSMOS4_FILE_HEADER, OpenC3::PacketLogConstants::OPENC3_CBOR_FLAG_MASK, OpenC3::PacketLogConstants::OPENC3_CMD_FLAG_MASK, OpenC3::PacketLogConstants::OPENC3_ENTRY_TYPE_MASK, OpenC3::PacketLogConstants::OPENC3_EXTRA_FLAG_MASK, OpenC3::PacketLogConstants::OPENC3_EXTRA_LENGTH_FIXED_SIZE, OpenC3::PacketLogConstants::OPENC3_EXTRA_LENGTH_PACK_DIRECTIVE, OpenC3::PacketLogConstants::OPENC3_EXTRA_LENGTH_PACK_ITEMS, OpenC3::PacketLogConstants::OPENC3_FILE_HEADER, OpenC3::PacketLogConstants::OPENC3_HEADER_LENGTH, OpenC3::PacketLogConstants::OPENC3_ID_FIXED_SIZE, OpenC3::PacketLogConstants::OPENC3_ID_FLAG_MASK, OpenC3::PacketLogConstants::OPENC3_INDEX_HEADER, OpenC3::PacketLogConstants::OPENC3_JSON_PACKET_ENTRY_TYPE_MASK, OpenC3::PacketLogConstants::OPENC3_KEY_MAP_ENTRY_TYPE_MASK, OpenC3::PacketLogConstants::OPENC3_KEY_MAP_PACK_DIRECTIVE, OpenC3::PacketLogConstants::OPENC3_KEY_MAP_PACK_ITEMS, OpenC3::PacketLogConstants::OPENC3_KEY_MAP_SECONDARY_FIXED_SIZE, OpenC3::PacketLogConstants::OPENC3_MAX_PACKET_INDEX, OpenC3::PacketLogConstants::OPENC3_MAX_TARGET_INDEX, OpenC3::PacketLogConstants::OPENC3_OFFSET_MARKER_ENTRY_TYPE_MASK, OpenC3::PacketLogConstants::OPENC3_OFFSET_MARKER_PACK_DIRECTIVE, OpenC3::PacketLogConstants::OPENC3_OFFSET_MARKER_PACK_ITEMS, OpenC3::PacketLogConstants::OPENC3_OFFSET_MARKER_SECONDARY_FIXED_SIZE, OpenC3::PacketLogConstants::OPENC3_PACKET_DECLARATION_ENTRY_TYPE_MASK, OpenC3::PacketLogConstants::OPENC3_PACKET_DECLARATION_PACK_DIRECTIVE, OpenC3::PacketLogConstants::OPENC3_PACKET_DECLARATION_PACK_ITEMS, OpenC3::PacketLogConstants::OPENC3_PACKET_DECLARATION_SECONDARY_FIXED_SIZE, OpenC3::PacketLogConstants::OPENC3_PACKET_PACK_DIRECTIVE, OpenC3::PacketLogConstants::OPENC3_PACKET_PACK_ITEMS, OpenC3::PacketLogConstants::OPENC3_PACKET_SECONDARY_FIXED_SIZE, OpenC3::PacketLogConstants::OPENC3_PRIMARY_FIXED_SIZE, OpenC3::PacketLogConstants::OPENC3_RAW_PACKET_ENTRY_TYPE_MASK, OpenC3::PacketLogConstants::OPENC3_RECEIVED_TIME_FIXED_SIZE, OpenC3::PacketLogConstants::OPENC3_RECEIVED_TIME_FLAG_MASK, OpenC3::PacketLogConstants::OPENC3_RECEIVED_TIME_PACK_DIRECTIVE, OpenC3::PacketLogConstants::OPENC3_RECEIVED_TIME_PACK_ITEMS, OpenC3::PacketLogConstants::OPENC3_STORED_FLAG_MASK, OpenC3::PacketLogConstants::OPENC3_TARGET_DECLARATION_ENTRY_TYPE_MASK, OpenC3::PacketLogConstants::OPENC3_TARGET_DECLARATION_PACK_DIRECTIVE, OpenC3::PacketLogConstants::OPENC3_TARGET_DECLARATION_PACK_ITEMS, OpenC3::PacketLogConstants::OPENC3_TARGET_DECLARATION_SECONDARY_FIXED_SIZE

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializePacketLogReader

Create a new log file reader



42
43
44
# File 'lib/openc3/logs/packet_log_reader.rb', line 42

def initialize
  reset()
end

Instance Attribute Details

#filenameObject (readonly)

Returns the value of attribute filename.



37
38
39
# File 'lib/openc3/logs/packet_log_reader.rb', line 37

def filename
  @filename
end

#last_offsetsObject (readonly)

Returns the value of attribute last_offsets.



36
37
38
# File 'lib/openc3/logs/packet_log_reader.rb', line 36

def last_offsets
  @last_offsets
end

#redis_offsetObject (readonly)

Returns the value of attribute redis_offset.



35
36
37
# File 'lib/openc3/logs/packet_log_reader.rb', line 35

def redis_offset
  @redis_offset
end

Instance Method Details

#bytes_readInteger

Returns The current file position in the log file.

Returns:

  • (Integer)

    The current file position in the log file



233
234
235
# File 'lib/openc3/logs/packet_log_reader.rb', line 233

def bytes_read
  @file.pos
end

#closeObject

Closes the current log file



105
106
107
# File 'lib/openc3/logs/packet_log_reader.rb', line 105

def close
  @file.close if @file and !@file.closed?
end

#each(filename, identify_and_define = true, start_time = nil, end_time = nil) {|packet| ... } ⇒ Boolean

Yields back each packet as it is found in the log file.

Parameters:

  • filename (String)

    The log file to read

  • identify_and_define (Boolean) (defaults to: true)

    Once the packet has been read from the log file, whether to both identify the packet by setting the target and packet name, and define the packet by populating all the items.

  • start_time (Time|nil) (defaults to: nil)

    Time at which to start returning packets. Packets found with a timestamp before this time are ignored. Pass nil to return all packets.

  • end_time (Time|nil) (defaults to: nil)

    Time at which to stop returning packets. Packets found with a timestamp after this time are ignored. Pass nil to return all packets.

Yield Parameters:

Returns:

  • (Boolean)

    Whether we reached the end_time while reading



60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/openc3/logs/packet_log_reader.rb', line 60

def each(filename, identify_and_define = true, start_time = nil, end_time = nil)
  reached_end_time = false
  open(filename)

  # seek_to_time(start_time) if start_time

  while true
    packet = read(identify_and_define)
    break unless packet

    time = packet.packet_time
    if time
      next if start_time and time < start_time
      # If we reach the end_time that means we found all the packets we asked for
      # This can be used by callers to know they are done reading
      if end_time and time > end_time
        reached_end_time = true
        break
      end
    end
    yield packet
  end
  reached_end_time
ensure # No implicit return value in the ensure block
  close()
end

#open(filename) ⇒ Boolean, Exception

Returns true if successfully changed to configuration specified in log, otherwise returns false and potentially an Exception class if an error occurred. If no error occurred false indicates that the requested configuration was simply not found.

Parameters:

  • filename (String)

    The log filename to open

Returns:

  • (Boolean, Exception)

    Returns true if successfully changed to configuration specified in log, otherwise returns false and potentially an Exception class if an error occurred. If no error occurred false indicates that the requested configuration was simply not found.



91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/openc3/logs/packet_log_reader.rb', line 91

def open(filename)
  close()
  reset()
  @filename = filename
  @file = BufferedFile.open(@filename, 'rb')
  @max_read_size = @file.size
  @max_read_size = MAX_READ_SIZE if @max_read_size > MAX_READ_SIZE
  return read_file_header()
rescue => e
  close()
  raise e
end

#read(identify_and_define = true) ⇒ Packet

Read a packet from the log file

Parameters:

  • identify_and_define (Boolean) (defaults to: true)

    Once the packet has been read from the log file, whether to both identify the packet by setting the target and packet name, and define the packet by populating all the items.

Returns:



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/openc3/logs/packet_log_reader.rb', line 113

def read(identify_and_define = true)
  # Read entry length
  length = @file.read(4)
  return nil if !length or length.length <= 0

  length = length.unpack('N')[0]
  entry = @file.read(length)
  flags = entry[0..1].unpack('n')[0]

  cmd_or_tlm = :TLM
  cmd_or_tlm = :CMD if flags & OPENC3_CMD_FLAG_MASK == OPENC3_CMD_FLAG_MASK
  stored = false
  stored = true if flags & OPENC3_STORED_FLAG_MASK == OPENC3_STORED_FLAG_MASK
  id = false
  id = true if flags & OPENC3_ID_FLAG_MASK == OPENC3_ID_FLAG_MASK
  cbor = false
  cbor = true if flags & OPENC3_CBOR_FLAG_MASK == OPENC3_CBOR_FLAG_MASK
  includes_received_time = false
  includes_received_time = true if flags & OPENC3_RECEIVED_TIME_FLAG_MASK == OPENC3_RECEIVED_TIME_FLAG_MASK
  includes_extra = false
  includes_extra = true if flags & OPENC3_EXTRA_FLAG_MASK == OPENC3_EXTRA_FLAG_MASK

  if flags & OPENC3_ENTRY_TYPE_MASK == OPENC3_JSON_PACKET_ENTRY_TYPE_MASK
    packet_index, time_nsec_since_epoch = entry[2..11].unpack('nQ>')
    received_time_nsec_since_epoch, extra, json_data = handle_received_time_extra_and_data(entry, time_nsec_since_epoch, includes_received_time, includes_extra, cbor)
    lookup_cmd_or_tlm, target_name, packet_name, _id, key_map = @packets[packet_index]
    if cmd_or_tlm != lookup_cmd_or_tlm
      raise "Packet type mismatch, packet:#{cmd_or_tlm}, lookup:#{lookup_cmd_or_tlm}"
    end

    if cbor
      return JsonPacket.new(cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, CBOR.decode(json_data), key_map, received_time_nsec_since_epoch: received_time_nsec_since_epoch, extra: extra)
    else
      return JsonPacket.new(cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, json_data, key_map, received_time_nsec_since_epoch: received_time_nsec_since_epoch, extra: extra)
    end
  elsif flags & OPENC3_ENTRY_TYPE_MASK == OPENC3_RAW_PACKET_ENTRY_TYPE_MASK
    packet_index, time_nsec_since_epoch = entry[2..11].unpack('nQ>')
    received_time_nsec_since_epoch, _extra, packet_data = handle_received_time_extra_and_data(entry, time_nsec_since_epoch, includes_received_time, includes_extra, cbor)
    lookup_cmd_or_tlm, target_name, packet_name, _id = @packets[packet_index]
    if cmd_or_tlm != lookup_cmd_or_tlm
      raise "Packet type mismatch, packet:#{cmd_or_tlm}, lookup:#{lookup_cmd_or_tlm}"
    end

    if identify_and_define
      packet = identify_and_define_packet_data(cmd_or_tlm, target_name, packet_name, packet_data)
    else
      # Build Packet
      packet = Packet.new(target_name, packet_name, :BIG_ENDIAN, nil, packet_data)
    end
    packet.packet_time = Time.from_nsec_from_epoch(time_nsec_since_epoch)
    received_time = Time.from_nsec_from_epoch(received_time_nsec_since_epoch)
    packet.set_received_time_fast(received_time)
    packet.cmd_or_tlm = cmd_or_tlm
    packet.stored = stored
    packet.received_count += 1
    return packet
  elsif flags & OPENC3_ENTRY_TYPE_MASK == OPENC3_TARGET_DECLARATION_ENTRY_TYPE_MASK
    target_name_length = length - OPENC3_PRIMARY_FIXED_SIZE - OPENC3_TARGET_DECLARATION_SECONDARY_FIXED_SIZE
    target_name_length -= OPENC3_ID_FIXED_SIZE if id
    target_name = entry[2..(target_name_length + 1)]
    if id
      id = entry[(target_name_length + 3)..(target_name_length + 34)]
      @target_ids << id
    end
    @target_names << target_name
    return read(identify_and_define)
  elsif flags & OPENC3_ENTRY_TYPE_MASK == OPENC3_PACKET_DECLARATION_ENTRY_TYPE_MASK
    target_index = entry[2..3].unpack('n')[0]
    target_name = @target_names[target_index]
    unless target_name
      # There was a bug in the PacketLogWriter before version 5.6.0 that stored an invalid target_index
      # Attempt to work around by reading the target_name from the filename
      filename_split = @filename.to_s.split('__')
      target_name = filename_split[3]
      target_name = 'UNKNOWN' unless target_name
    end
    packet_name_length = length - OPENC3_PRIMARY_FIXED_SIZE - OPENC3_PACKET_DECLARATION_SECONDARY_FIXED_SIZE
    packet_name_length -= OPENC3_ID_FIXED_SIZE if id
    packet_name = entry[4..(packet_name_length + 3)]
    if id
      id = entry[(packet_name_length + 4)..-1]
      @packet_ids << id
    end
    @packets << [cmd_or_tlm, target_name, packet_name, id]
    return read(identify_and_define)
  elsif flags & OPENC3_ENTRY_TYPE_MASK == OPENC3_KEY_MAP_ENTRY_TYPE_MASK
    packet_index = entry[2..3].unpack('n')[0]
    key_map_length = length - OPENC3_PRIMARY_FIXED_SIZE - OPENC3_KEY_MAP_SECONDARY_FIXED_SIZE
    if cbor
      key_map = CBOR.decode(entry[4..(key_map_length + 3)])
    else
      key_map = JSON.parse(entry[4..(key_map_length + 3)], :allow_nan => true, :create_additions => true)
    end
    @packets[packet_index] << key_map
    return read(identify_and_define)
  elsif flags & OPENC3_ENTRY_TYPE_MASK == OPENC3_OFFSET_MARKER_ENTRY_TYPE_MASK
    data = entry[2..-1]
    split_data = data.split(',')
    redis_offset = split_data[0]
    redis_topic = split_data[1]
    if redis_topic
      @last_offsets[redis_topic] = redis_offset
    else
      @redis_offset = redis_offset
    end
    return read(identify_and_define)
  else
    raise "Invalid Entry Flags: #{flags}"
  end
rescue => e
  close()
  raise e
end

#sizeInteger

Returns The size of the log file being processed.

Returns:

  • (Integer)

    The size of the log file being processed



228
229
230
# File 'lib/openc3/logs/packet_log_reader.rb', line 228

def size
  @file.stat.size
end