openc3 5.10.0 → 5.11.0

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of openc3 might be problematic. Click here for more details.

Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +1 -1
  3. data/Rakefile +3 -2
  4. data/data/config/target.yaml +9 -0
  5. data/ext/openc3/ext/packet/packet.c +3 -0
  6. data/ext/openc3/ext/reducer_microservice/extconf.rb +13 -0
  7. data/ext/openc3/ext/reducer_microservice/reducer_microservice.c +165 -0
  8. data/ext/openc3/ext/structure/structure.c +7 -9
  9. data/lib/openc3/accessors/accessor.rb +53 -3
  10. data/lib/openc3/accessors/binary_accessor.rb +16 -0
  11. data/lib/openc3/accessors/cbor_accessor.rb +3 -3
  12. data/lib/openc3/accessors/form_accessor.rb +78 -0
  13. data/lib/openc3/accessors/http_accessor.rb +145 -0
  14. data/lib/openc3/accessors/json_accessor.rb +19 -3
  15. data/lib/openc3/accessors/xml_accessor.rb +18 -1
  16. data/lib/openc3/accessors.rb +3 -1
  17. data/lib/openc3/config/config_parser.rb +7 -5
  18. data/lib/openc3/config/meta_config_parser.rb +1 -1
  19. data/lib/openc3/core_ext/string.rb +16 -1
  20. data/lib/openc3/interfaces/http_client_interface.rb +202 -0
  21. data/lib/openc3/interfaces/http_server_interface.rb +183 -0
  22. data/lib/openc3/interfaces/interface.rb +86 -16
  23. data/lib/openc3/interfaces/mqtt_interface.rb +6 -5
  24. data/lib/openc3/interfaces/protocols/burst_protocol.rb +11 -11
  25. data/lib/openc3/interfaces/protocols/cobs_protocol.rb +7 -7
  26. data/lib/openc3/interfaces/protocols/crc_protocol.rb +7 -7
  27. data/lib/openc3/interfaces/protocols/length_protocol.rb +6 -6
  28. data/lib/openc3/interfaces/protocols/preidentified_protocol.rb +9 -5
  29. data/lib/openc3/interfaces/protocols/protocol.rb +8 -6
  30. data/lib/openc3/interfaces/protocols/slip_protocol.rb +8 -8
  31. data/lib/openc3/interfaces/protocols/template_protocol.rb +6 -7
  32. data/lib/openc3/interfaces/protocols/terminated_protocol.rb +4 -4
  33. data/lib/openc3/interfaces/simulated_target_interface.rb +2 -0
  34. data/lib/openc3/interfaces/stream_interface.rb +6 -4
  35. data/lib/openc3/interfaces/tcpip_server_interface.rb +2 -0
  36. data/lib/openc3/interfaces/udp_interface.rb +8 -5
  37. data/lib/openc3/interfaces.rb +2 -0
  38. data/lib/openc3/logs/buffered_packet_log_writer.rb +6 -7
  39. data/lib/openc3/logs/log_writer.rb +2 -10
  40. data/lib/openc3/logs/packet_log_constants.rb +13 -3
  41. data/lib/openc3/logs/packet_log_reader.rb +35 -98
  42. data/lib/openc3/logs/packet_log_writer.rb +24 -62
  43. data/lib/openc3/logs/text_log_writer.rb +32 -6
  44. data/lib/openc3/microservices/cleanup_microservice.rb +23 -16
  45. data/lib/openc3/microservices/decom_microservice.rb +8 -20
  46. data/lib/openc3/microservices/log_microservice.rb +3 -1
  47. data/lib/openc3/microservices/reaction_microservice.rb +22 -11
  48. data/lib/openc3/microservices/reducer_microservice.rb +174 -130
  49. data/lib/openc3/{models/notification_model.rb → microservices/scope_cleanup_microservice.rb} +20 -21
  50. data/lib/openc3/microservices/text_log_microservice.rb +2 -5
  51. data/lib/openc3/microservices/timeline_microservice.rb +0 -1
  52. data/lib/openc3/microservices/trigger_group_microservice.rb +6 -3
  53. data/lib/openc3/migrations/20230915000002_no_scope_log_messages.rb +44 -0
  54. data/lib/openc3/models/microservice_model.rb +1 -1
  55. data/lib/openc3/models/scope_model.rb +65 -34
  56. data/lib/openc3/models/target_model.rb +25 -5
  57. data/lib/openc3/models/tool_model.rb +14 -2
  58. data/lib/openc3/models/widget_model.rb +1 -1
  59. data/lib/openc3/packets/json_packet.rb +10 -2
  60. data/lib/openc3/packets/packet.rb +37 -22
  61. data/lib/openc3/packets/packet_config.rb +6 -2
  62. data/lib/openc3/packets/parsers/packet_item_parser.rb +11 -6
  63. data/lib/openc3/packets/structure.rb +19 -12
  64. data/lib/openc3/packets/structure_item.rb +0 -3
  65. data/lib/openc3/script/storage.rb +1 -1
  66. data/lib/openc3/script/web_socket_api.rb +17 -14
  67. data/lib/openc3/topics/command_decom_topic.rb +0 -4
  68. data/lib/openc3/topics/telemetry_decom_topic.rb +0 -4
  69. data/lib/openc3/topics/telemetry_reduced_topics.rb +1 -13
  70. data/lib/openc3/topics/telemetry_topic.rb +2 -1
  71. data/lib/openc3/topics/topic.rb +0 -8
  72. data/lib/openc3/utilities/aws_bucket.rb +40 -38
  73. data/lib/openc3/utilities/bucket_utilities.rb +2 -0
  74. data/lib/openc3/utilities/cli_generator.rb +1 -1
  75. data/lib/openc3/utilities/logger.rb +62 -47
  76. data/lib/openc3/utilities/metric.rb +19 -1
  77. data/lib/openc3/utilities/sleeper.rb +3 -1
  78. data/lib/openc3/utilities/throttle.rb +76 -0
  79. data/lib/openc3/version.rb +5 -5
  80. data/templates/tool_angular/package.json +20 -20
  81. data/templates/tool_angular/yarn.lock +1815 -1009
  82. data/templates/tool_react/package.json +24 -26
  83. data/templates/tool_react/yarn.lock +1269 -902
  84. data/templates/tool_svelte/.prettierrc.js +5 -0
  85. data/templates/tool_svelte/package.json +26 -26
  86. data/templates/tool_svelte/src/services/cable.js +1 -1
  87. data/templates/tool_svelte/src/services/openc3-api.js +173 -173
  88. data/templates/tool_svelte/yarn.lock +981 -735
  89. data/templates/tool_vue/package.json +13 -13
  90. data/templates/tool_vue/yarn.lock +363 -147
  91. data/templates/widget/package.json +14 -14
  92. data/templates/widget/yarn.lock +367 -99
  93. metadata +41 -4
  94. data/lib/openc3/topics/notifications_topic.rb +0 -31
@@ -57,14 +57,14 @@ module OpenC3
57
57
  super(discard_leading_bytes, sync_pattern, fill_fields, allow_empty_data)
58
58
  end
59
59
 
60
- def write_data(data)
60
+ def write_data(data, extra = nil)
61
61
  raise "Packet contains termination characters!" if data.index(@write_termination_characters)
62
62
 
63
- data = super(data)
63
+ data, extra = super(data, extra)
64
64
  @write_termination_characters.each_byte do |byte|
65
65
  data << byte
66
66
  end
67
- return data
67
+ return data, extra
68
68
  end
69
69
 
70
70
  protected
@@ -88,7 +88,7 @@ module OpenC3
88
88
  end
89
89
  end
90
90
  @data.replace(@data[(index + @read_termination_characters.length)..-1])
91
- return packet_data
91
+ return packet_data, @extra
92
92
  else
93
93
  return :STOP
94
94
  end
@@ -54,6 +54,7 @@ module OpenC3
54
54
  # Save the current time + delta as the next expected tick time
55
55
  @next_tick_time = Time.now.sys + @sim_target.tick_period_seconds
56
56
 
57
+ super()
57
58
  @connected = true
58
59
  end
59
60
 
@@ -150,6 +151,7 @@ module OpenC3
150
151
  # Disconnect from the simulator
151
152
  def disconnect
152
153
  @connected = false
154
+ super()
153
155
  end
154
156
 
155
157
  protected
@@ -72,13 +72,15 @@ module OpenC3
72
72
  return nil
73
73
  end
74
74
 
75
- read_interface_base(data)
76
- data
75
+ extra = nil
76
+ read_interface_base(data, extra)
77
+ return data, extra
77
78
  end
78
79
 
79
- def write_interface(data)
80
- write_interface_base(data)
80
+ def write_interface(data, extra = nil)
81
+ write_interface_base(data, extra)
81
82
  @stream.write(data)
83
+ return data, extra
82
84
  end
83
85
  end
84
86
  end
@@ -156,6 +156,7 @@ module OpenC3
156
156
  @write_thread = nil
157
157
  @write_raw_thread = nil
158
158
  end
159
+ super()
159
160
  @connected = true
160
161
  end
161
162
 
@@ -204,6 +205,7 @@ module OpenC3
204
205
 
205
206
  shutdown_interfaces(@write_interface_infos)
206
207
  @connected = false
208
+ super()
207
209
  end
208
210
 
209
211
  # Gracefully kill all the threads
@@ -121,6 +121,7 @@ module OpenC3
121
121
  ) if @write_dest_port
122
122
  end
123
123
  @thread_sleeper = nil
124
+ super()
124
125
  end
125
126
 
126
127
  # @return [Boolean] Whether the active ports (read and/or write) have
@@ -146,6 +147,7 @@ module OpenC3
146
147
  @read_socket = nil
147
148
  @thread_sleeper.cancel if @thread_sleeper
148
149
  @thread_sleeper = nil
150
+ super()
149
151
  end
150
152
 
151
153
  def read
@@ -161,18 +163,19 @@ module OpenC3
161
163
  def read_interface
162
164
  data = @read_socket.read(@read_timeout)
163
165
  Logger.info "#{@name}: Udp read returned 0 bytes (stream closed)" if data.length <= 0
164
- read_interface_base(data)
165
- return data
166
+ extra = nil
167
+ read_interface_base(data, extra)
168
+ return data, extra
166
169
  rescue IOError # Disconnected
167
170
  return nil
168
171
  end
169
172
 
170
173
  # Writes to the socket
171
174
  # @param data [String] Raw packet data
172
- def write_interface(data)
173
- write_interface_base(data)
175
+ def write_interface(data, extra = nil)
176
+ write_interface_base(data, extra)
174
177
  @write_socket.write(data, @write_timeout)
175
- data
178
+ return data, extra
176
179
  end
177
180
  end
178
181
  end
@@ -22,6 +22,8 @@
22
22
 
23
23
  module OpenC3
24
24
  autoload(:Interface, 'openc3/interfaces/interface.rb')
25
+ autoload(:HttpClientInterface, 'openc3/interfaces/http_client_interface.rb')
26
+ autoload(:HttpServerInterface, 'openc3/interfaces/http_server_interface.rb')
25
27
  autoload(:MqttInterface, 'openc3/interfaces/mqtt_interface.rb')
26
28
  autoload(:StreamInterface, 'openc3/interfaces/stream_interface.rb')
27
29
  autoload(:SerialInterface, 'openc3/interfaces/serial_interface.rb')
@@ -75,23 +75,22 @@ module OpenC3
75
75
  # @param data [String] Binary string of data
76
76
  # @param id [Integer] Target ID
77
77
  # @param redis_offset [Integer] The offset of this packet in its Redis stream
78
- def buffered_write(entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id = nil, redis_topic = nil, redis_offset = '0-0')
78
+ def buffered_write(entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id = nil, redis_topic = nil, redis_offset = '0-0', received_time_nsec_since_epoch: nil, extra: nil)
79
79
  case entry_type
80
80
  when :RAW_PACKET, :JSON_PACKET
81
- @buffer << [entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id, redis_topic, redis_offset]
81
+ @buffer << [entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id, redis_topic, redis_offset, received_time_nsec_since_epoch, extra]
82
82
  @buffer.sort! {|entry1, entry2| entry1[4] <=> entry2[4] }
83
83
  if @buffer.length >= @buffer_depth
84
84
  entry = @buffer.shift
85
- write(*entry)
85
+ write(*entry[0..-3], received_time_nsec_since_epoch: entry[-2], extra: entry[-1])
86
86
  end
87
87
  else
88
- write(entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id, redis_topic, redis_offset)
88
+ write(entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id, redis_topic, redis_offset, received_time_nsec_since_epoch: received_time_nsec_since_epoch, extra: extra)
89
89
  end
90
90
  end
91
91
 
92
92
  def buffered_first_time_nsec
93
- time = first_time()
94
- return time.to_nsec_from_epoch if time
93
+ return @first_time if @first_time
95
94
  return @buffer[0][4] if @buffer[0]
96
95
  return nil
97
96
  end
@@ -115,7 +114,7 @@ module OpenC3
115
114
  def write_buffer
116
115
  begin
117
116
  @buffer.each do |entry|
118
- write(*entry, allow_new_file: false, take_mutex: false)
117
+ write(*entry[0..-3], allow_new_file: false, take_mutex: false, received_time_nsec_since_epoch: entry[-2], extra: entry[-1])
119
118
  end
120
119
  rescue => err
121
120
  Logger.instance.error "Error writing out buffer : #{err.formatted}"
@@ -350,20 +350,12 @@ module OpenC3
350
350
  '.log'.freeze
351
351
  end
352
352
 
353
- def first_time
354
- Time.from_nsec_from_epoch(@first_time)
355
- end
356
-
357
- def last_time
358
- Time.from_nsec_from_epoch(@last_time)
359
- end
360
-
361
353
  def first_timestamp
362
- first_time().to_timestamp # "YYYYMMDDHHmmSSNNNNNNNNN"
354
+ Time.from_nsec_from_epoch(@first_time).to_timestamp # "YYYYMMDDHHmmSSNNNNNNNNN"
363
355
  end
364
356
 
365
357
  def last_timestamp
366
- last_time().to_timestamp # "YYYYMMDDHHmmSSNNNNNNNNN"
358
+ Time.from_nsec_from_epoch(@last_time).to_timestamp # "YYYYMMDDHHmmSSNNNNNNNNN"
367
359
  end
368
360
  end
369
361
  end
@@ -17,7 +17,7 @@
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
- # This file may also be used under the terms of a commercial license
20
+ # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
23
  module OpenC3
@@ -38,6 +38,8 @@ module OpenC3
38
38
  OPENC3_JSON_PACKET_ENTRY_TYPE_MASK = 0x4000
39
39
  OPENC3_OFFSET_MARKER_ENTRY_TYPE_MASK = 0x5000
40
40
  OPENC3_KEY_MAP_ENTRY_TYPE_MASK = 0x6000
41
+ OPENC3_RECEIVED_TIME_FLAG_MASK = 0x0040
42
+ OPENC3_EXTRA_FLAG_MASK = 0x0080
41
43
  OPENC3_CBOR_FLAG_MASK = 0x0100
42
44
  OPENC3_ID_FLAG_MASK = 0x0200
43
45
  OPENC3_STORED_FLAG_MASK = 0x0400
@@ -47,8 +49,8 @@ module OpenC3
47
49
  OPENC3_MAX_PACKET_INDEX = 65535
48
50
  OPENC3_MAX_TARGET_INDEX = 65535
49
51
 
50
- OPENC3_PRIMARY_FIXED_SIZE = 2
51
- OPENC3_TARGET_DECLARATION_SECONDARY_FIXED_SIZE = 0
52
+ OPENC3_PRIMARY_FIXED_SIZE = 2 # 2 bytes for flags - Size of length field is not included in length value
53
+ OPENC3_TARGET_DECLARATION_SECONDARY_FIXED_SIZE = 0 # No additional data beyond 'Nn' (Length, Flags)
52
54
  OPENC3_TARGET_DECLARATION_PACK_DIRECTIVE = 'Nn'.freeze
53
55
  OPENC3_TARGET_DECLARATION_PACK_ITEMS = 2 # Useful for testing
54
56
 
@@ -67,5 +69,13 @@ module OpenC3
67
69
  OPENC3_PACKET_SECONDARY_FIXED_SIZE = 10
68
70
  OPENC3_PACKET_PACK_DIRECTIVE = 'NnnQ>'.freeze
69
71
  OPENC3_PACKET_PACK_ITEMS = 4 # Useful for testing
72
+
73
+ OPENC3_RECEIVED_TIME_FIXED_SIZE = 8
74
+ OPENC3_RECEIVED_TIME_PACK_DIRECTIVE = 'Q>'.freeze
75
+ OPENC3_RECEIVED_TIME_PACK_ITEMS = 1 # Useful for testing
76
+
77
+ OPENC3_EXTRA_LENGTH_FIXED_SIZE = 4
78
+ OPENC3_EXTRA_LENGTH_PACK_DIRECTIVE = 'N'.freeze
79
+ OPENC3_EXTRA_LENGTH_PACK_ITEMS = 1 # Useful for testing
70
80
  end
71
81
  end
@@ -127,35 +127,41 @@ module OpenC3
127
127
  id = true if flags & OPENC3_ID_FLAG_MASK == OPENC3_ID_FLAG_MASK
128
128
  cbor = false
129
129
  cbor = true if flags & OPENC3_CBOR_FLAG_MASK == OPENC3_CBOR_FLAG_MASK
130
+ includes_received_time = false
131
+ includes_received_time = true if flags & OPENC3_RECEIVED_TIME_FLAG_MASK == OPENC3_RECEIVED_TIME_FLAG_MASK
132
+ includes_extra = false
133
+ includes_extra = true if flags & OPENC3_EXTRA_FLAG_MASK == OPENC3_EXTRA_FLAG_MASK
130
134
 
131
135
  if flags & OPENC3_ENTRY_TYPE_MASK == OPENC3_JSON_PACKET_ENTRY_TYPE_MASK
132
136
  packet_index, time_nsec_since_epoch = entry[2..11].unpack('nQ>')
133
- json_data = entry[12..-1]
137
+ next_offset = 12
138
+ 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)
134
139
  lookup_cmd_or_tlm, target_name, packet_name, id, key_map = @packets[packet_index]
135
140
  if cmd_or_tlm != lookup_cmd_or_tlm
136
141
  raise "Packet type mismatch, packet:#{cmd_or_tlm}, lookup:#{lookup_cmd_or_tlm}"
137
142
  end
138
143
 
139
144
  if cbor
140
- return JsonPacket.new(cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, CBOR.decode(json_data), key_map)
145
+ 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)
141
146
  else
142
- return JsonPacket.new(cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, json_data, key_map)
147
+ 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)
143
148
  end
144
149
  elsif flags & OPENC3_ENTRY_TYPE_MASK == OPENC3_RAW_PACKET_ENTRY_TYPE_MASK
145
150
  packet_index, time_nsec_since_epoch = entry[2..11].unpack('nQ>')
146
- packet_data = entry[12..-1]
151
+ 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)
147
152
  lookup_cmd_or_tlm, target_name, packet_name, id = @packets[packet_index]
148
153
  if cmd_or_tlm != lookup_cmd_or_tlm
149
154
  raise "Packet type mismatch, packet:#{cmd_or_tlm}, lookup:#{lookup_cmd_or_tlm}"
150
155
  end
151
156
 
152
- received_time = Time.from_nsec_from_epoch(time_nsec_since_epoch)
153
157
  if identify_and_define
154
- packet = identify_and_define_packet_data(cmd_or_tlm, target_name, packet_name, received_time, packet_data)
158
+ packet = identify_and_define_packet_data(cmd_or_tlm, target_name, packet_name, packet_data)
155
159
  else
156
160
  # Build Packet
157
161
  packet = Packet.new(target_name, packet_name, :BIG_ENDIAN, nil, packet_data)
158
162
  end
163
+ packet.packet_time = Time.from_nsec_from_epoch(time_nsec_since_epoch)
164
+ received_time = Time.from_nsec_from_epoch(received_time_nsec_since_epoch)
159
165
  packet.set_received_time_fast(received_time)
160
166
  packet.cmd_or_tlm = cmd_or_tlm
161
167
  packet.stored = stored
@@ -219,95 +225,6 @@ module OpenC3
219
225
  raise err
220
226
  end
221
227
 
222
- # TODO: Currently not used
223
- # Returns an analysis of the log file by reading all the packets and
224
- # returning information about each packet. This information maps directly
225
- # to the parameters need by the {#read_at_offset} method and thus should be
226
- # called before using {#read_at_offset}.
227
- #
228
- # @param filename [String] The filename to analyze
229
- # @param progress_callback [Proc] Callback that should receive a single
230
- # floating point parameter which is the percentage done
231
- # @return [Array<Array<Integer, Integer, String, String, Time, Time>] Array
232
- # of arrays for each packet found in the log file consisting of:
233
- # [File position, length, target name, packet name, time formatted,
234
- # received time].
235
- # def packet_offsets(filename, progress_callback = nil)
236
- # open(filename)
237
- # offsets = []
238
- # filesize = size().to_f
239
-
240
- # while true
241
- # current_pos = @file.pos
242
- # packet = read(false)
243
- # break unless packet
244
- # offsets << current_pos
245
- # if progress_callback
246
- # break if progress_callback.call(current_pos / filesize)
247
- # end
248
- # end
249
-
250
- # return offsets
251
- # ensure
252
- # close()
253
- # end
254
-
255
- # TODO: Currently not used
256
- # Reads a packet from the opened log file. Should only be used in
257
- # conjunction with {#packet_offsets}.
258
- #
259
- # @param file_offset [Integer] Byte offset into the log file to start
260
- # reading
261
- # @param identify_and_define (see #each)
262
- # @return [Packet]
263
- # def read_at_offset(file_offset, identify_and_define = true)
264
- # @file.seek(file_offset, IO::SEEK_SET)
265
- # return read(identify_and_define)
266
- # rescue => err
267
- # close()
268
- # raise err
269
- # end
270
-
271
- # TODO: Currently not used
272
- # Read the first packet from the log file and reset the file position back
273
- # to the current position. This allows the client to call read multiple
274
- # times to return packets, call first, and continue calling read which will
275
- # return the next packet in the file.
276
- #
277
- # @return [Packet]
278
- # def first
279
- # original_position = @file.pos
280
- # @file.seek(0, IO::SEEK_SET)
281
- # read_file_header()
282
- # packet = read()
283
- # raise "No first packet found" unless packet
284
- # @file.seek(original_position, IO::SEEK_SET)
285
- # packet.clone
286
- # rescue => err
287
- # close()
288
- # raise err
289
- # end
290
-
291
- # TODO: Currently not used
292
- # Read the last packet from the log file and reset the file position back
293
- # to the current position. This allows the client to call read multiple
294
- # times to return packets, call last, and continue calling read which will
295
- # return the next packet in the file.
296
- #
297
- # @return [Packet]
298
- # def last
299
- # raise "TODO: Implement me - Need to add end of file entry to support"
300
- # original_position = @file.pos
301
- # @file.seek(-1, IO::SEEK_END)
302
- # packet = search(-1)
303
- # raise "No last packet found" unless packet
304
- # @file.seek(original_position, IO::SEEK_SET)
305
- # packet.clone
306
- # rescue => err
307
- # close()
308
- # raise err
309
- # end
310
-
311
228
  # @return [Integer] The size of the log file being processed
312
229
  def size
313
230
  @file.stat.size
@@ -333,7 +250,7 @@ module OpenC3
333
250
  end
334
251
 
335
252
  # This is best effort. May return unidentified/undefined packets
336
- def identify_and_define_packet_data(cmd_or_tlm, target_name, packet_name, received_time, packet_data)
253
+ def identify_and_define_packet_data(cmd_or_tlm, target_name, packet_name, packet_data)
337
254
  packet = nil
338
255
  unless target_name and packet_name
339
256
  if cmd_or_tlm == :CMD
@@ -376,8 +293,28 @@ module OpenC3
376
293
  end
377
294
  end
378
295
 
379
- def seek_to_time(time)
380
- raise "TODO: Implement me - Use index file or offsets"
296
+ # Handle common optional fields in raw and JSON packets
297
+ def handle_received_time_extra_and_data(entry, time_nsec_since_epoch, includes_received_time, includes_extra, cbor)
298
+ next_offset = 12
299
+ received_time_nsec_since_epoch = time_nsec_since_epoch
300
+ if includes_received_time
301
+ received_time_nsec_since_epoch = entry[next_offset..(next_offset + OPENC3_RECEIVED_TIME_FIXED_SIZE - 1)].unpack(OPENC3_RECEIVED_TIME_PACK_DIRECTIVE)
302
+ next_offset += OPENC3_RECEIVED_TIME_FIXED_SIZE
303
+ end
304
+ extra = nil
305
+ if includes_extra
306
+ extra_length = entry[next_offset..(next_offset + OPENC3_EXTRA_LENGTH_FIXED_SIZE - 1)].unpack(OPENC3_EXTRA_LENGTH_PACK_DIRECTIVE)
307
+ next_offset += OPENC3_EXTRA_LENGTH_FIXED_SIZE
308
+ extra_encoded = entry[next_offset..(next_offset + extra_length - 1)]
309
+ next_offset += extra_length
310
+ if cbor
311
+ extra = CBOR.decode(extra_encoded)
312
+ else
313
+ extra = JSON.parse(extra_encode, allow_nan: true, create_additions: true)
314
+ end
315
+ end
316
+ data = entry[next_offset..-1]
317
+ return received_time_nsec_since_epoch, extra, data
381
318
  end
382
319
  end
383
320
  end
@@ -64,22 +64,15 @@ module OpenC3
64
64
  enforce_time_order
65
65
  )
66
66
  @label = label
67
- @index_file = nil
68
- @index_filename = nil
69
67
  @cmd_packet_table = {}
70
68
  @tlm_packet_table = {}
71
69
  @key_map_table = {}
72
70
  @target_dec_entries = []
73
71
  @packet_dec_entries = []
74
- @key_map_entries = []
75
72
  @next_packet_index = 0
76
73
  @target_indexes = {}
77
74
  @next_target_index = 0
78
75
  @data_format = :CBOR # Default to CBOR for improved compression
79
-
80
- # This is an optimization to avoid creating a new entry object
81
- # each time we create an entry which we do a LOT!
82
- @index_entry = String.new
83
76
  end
84
77
 
85
78
  # Write a packet to the log file.
@@ -97,13 +90,13 @@ module OpenC3
97
90
  # @param data [String] Binary string of data
98
91
  # @param id [Integer] Target ID
99
92
  # @param redis_offset [Integer] The offset of this packet in its Redis stream
100
- def write(entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id = nil, redis_topic = nil, redis_offset = '0-0', take_mutex: true, allow_new_file: true)
93
+ def write(entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id = nil, redis_topic = nil, redis_offset = '0-0', take_mutex: true, allow_new_file: true, received_time_nsec_since_epoch: nil, extra: nil)
101
94
  return if !@logging_enabled
102
95
 
103
96
  @mutex.lock if take_mutex
104
97
  begin
105
98
  prepare_write(time_nsec_since_epoch, data.length, redis_topic, redis_offset, allow_new_file: allow_new_file)
106
- write_entry(entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id) if @file
99
+ write_entry(entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id, received_time_nsec_since_epoch: received_time_nsec_since_epoch, extra: extra) if @file
107
100
  ensure
108
101
  @mutex.unlock if take_mutex
109
102
  end
@@ -112,7 +105,7 @@ module OpenC3
112
105
  OpenC3.handle_critical_exception(err)
113
106
  end
114
107
 
115
- # Starting a new index file is a critical operation so the entire method is
108
+ # Starting a new file is a critical operation so the entire method is
116
109
  # wrapped with a rescue and handled with handle_critical_exception
117
110
  # Assumes mutex has already been taken
118
111
  def start_new_file
@@ -120,10 +113,6 @@ module OpenC3
120
113
  @file.write(OPENC3_FILE_HEADER)
121
114
  @file_size += OPENC3_FILE_HEADER.length
122
115
 
123
- # Start index log file
124
- @index_filename = create_unique_filename('.idx'.freeze)
125
- @index_file = File.new(@index_filename, 'wb')
126
- @index_file.write(OPENC3_INDEX_HEADER)
127
116
  @cmd_packet_table = {}
128
117
  @tlm_packet_table = {}
129
118
  @key_map_table = {}
@@ -132,8 +121,6 @@ module OpenC3
132
121
  @next_target_index = 0
133
122
  @target_dec_entries = []
134
123
  @packet_dec_entries = []
135
- @key_map_entries = []
136
- Logger.debug "Index Log File Opened : #{@index_filename}"
137
124
  rescue => err
138
125
  Logger.error "Error starting new log file: #{err.formatted}"
139
126
  @logging_enabled = false
@@ -151,22 +138,6 @@ module OpenC3
151
138
  write_entry(:OFFSET_MARKER, nil, nil, nil, nil, nil, last_offset + ',' + redis_topic, nil) if @file
152
139
  end
153
140
 
154
- if @index_file
155
- begin
156
- write_index_file_footer()
157
- @index_file.close unless @index_file.closed?
158
- Logger.debug "Index Log File Closed : #{@index_filename}"
159
- date = first_timestamp[0..7] # YYYYMMDD
160
- bucket_key = File.join(@remote_log_directory, date, "#{first_timestamp}__#{last_timestamp}__#{@label}.idx")
161
- threads << BucketUtilities.move_log_file_to_bucket(@index_filename, bucket_key)
162
- rescue Exception => err
163
- Logger.instance.error "Error closing #{@index_filename} : #{err.formatted}"
164
- end
165
-
166
- @index_file = nil
167
- @index_filename = nil
168
- end
169
-
170
141
  threads.concat(super(false))
171
142
 
172
143
  ensure
@@ -241,7 +212,7 @@ module OpenC3
241
212
  return packet_index
242
213
  end
243
214
 
244
- def write_entry(entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id)
215
+ def write_entry(entry_type, cmd_or_tlm, target_name, packet_name, time_nsec_since_epoch, stored, data, id, received_time_nsec_since_epoch: nil, extra: nil)
245
216
  raise ArgumentError.new("Length of id must be 64, got #{id.length}") if id and id.length != 64 # 64 hex digits, gets packed to 32 bytes with .pack('H*')
246
217
 
247
218
  length = OPENC3_PRIMARY_FIXED_SIZE
@@ -283,7 +254,6 @@ module OpenC3
283
254
  packet_index = get_packet_index(cmd_or_tlm, target_name, packet_name, entry_type, data)
284
255
  @entry.clear
285
256
  @entry << [length, flags, packet_index].pack(OPENC3_KEY_MAP_PACK_DIRECTIVE) << data
286
- @key_map_entries << @entry.dup
287
257
  when :OFFSET_MARKER
288
258
  flags |= OPENC3_OFFSET_MARKER_ENTRY_TYPE_MASK
289
259
  length += OPENC3_OFFSET_MARKER_SECONDARY_FIXED_SIZE + data.length
@@ -318,13 +288,28 @@ module OpenC3
318
288
  if cmd_or_tlm == :CMD
319
289
  flags |= OPENC3_CMD_FLAG_MASK
320
290
  end
291
+ if received_time_nsec_since_epoch
292
+ flags |= OPENC3_RECEIVED_TIME_FLAG_MASK
293
+ length += OPENC3_RECEIVED_TIME_FIXED_SIZE
294
+ end
295
+ extra_encoded = nil
296
+ if extra
297
+ flags |= OPENC3_EXTRA_FLAG_MASK
298
+ extra = JSON.parse(extra, :allow_nan => true, :create_additions => true) if String === extra
299
+ length += OPENC3_EXTRA_LENGTH_FIXED_SIZE
300
+ if @data_format == :CBOR
301
+ extra_encoded = extra.as_json.to_cbor
302
+ else
303
+ extra_encoded = JSON.generate(extra.as_json, :allow_nan => true)
304
+ end
305
+ length += extra_encoded.length
306
+ end
321
307
  length += OPENC3_PACKET_SECONDARY_FIXED_SIZE + data.length
322
308
  @entry.clear
323
- @index_entry.clear
324
- @index_entry << [length, flags, packet_index, time_nsec_since_epoch].pack(OPENC3_PACKET_PACK_DIRECTIVE)
325
- @entry << @index_entry << data
326
- @index_entry << [@file_size].pack('Q>')
327
- @index_file.write(@index_entry)
309
+ @entry << [length, flags, packet_index, time_nsec_since_epoch].pack(OPENC3_PACKET_PACK_DIRECTIVE)
310
+ @entry << [received_time_nsec_since_epoch].pack(OPENC3_RECEIVED_TIME_PACK_DIRECTIVE) if received_time_nsec_since_epoch
311
+ @entry << [extra_encoded.length].pack(OPENC3_EXTRA_LENGTH_PACK_DIRECTIVE) << extra_encoded if extra_encoded
312
+ @entry << data
328
313
  @first_time = time_nsec_since_epoch if !@first_time or time_nsec_since_epoch < @first_time
329
314
  @last_time = time_nsec_since_epoch if !@last_time or time_nsec_since_epoch > @last_time
330
315
  else
@@ -334,29 +319,6 @@ module OpenC3
334
319
  @file_size += @entry.length
335
320
  end
336
321
 
337
- def write_index_file_footer
338
- footer_length = 4 # Includes length of length field at end
339
- @index_file.write([@target_dec_entries.length].pack('n'))
340
- footer_length += 2
341
- @target_dec_entries.each do |target_dec_entry|
342
- @index_file.write(target_dec_entry)
343
- footer_length += target_dec_entry.length
344
- end
345
- @index_file.write([@packet_dec_entries.length].pack('n'))
346
- footer_length += 2
347
- @packet_dec_entries.each do |packet_dec_entry|
348
- @index_file.write(packet_dec_entry)
349
- footer_length += packet_dec_entry.length
350
- end
351
- @index_file.write([@key_map_entries.length].pack('n'))
352
- footer_length += 2
353
- @key_map_entries.each do |key_map_entry|
354
- @index_file.write(key_map_entry)
355
- footer_length += key_map_entry.length
356
- end
357
- @index_file.write([footer_length].pack('N'))
358
- end
359
-
360
322
  def bucket_filename
361
323
  "#{first_timestamp}__#{last_timestamp}__#{@label}" + extension
362
324
  end
@@ -17,15 +17,22 @@
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
- # This file may also be used under the terms of a commercial license
20
+ # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
23
  require 'openc3/logs/log_writer'
24
+ require 'socket'
24
25
 
25
26
  module OpenC3
26
27
  # Creates a text log. Can automatically cycle the log based on an elasped
27
28
  # time period or when the log file reaches a predefined size.
28
29
  class TextLogWriter < LogWriter
30
+ NEWLINE = "\n".freeze
31
+ def initialize(*args)
32
+ super(*args)
33
+ @container_name = Socket.gethostname
34
+ end
35
+
29
36
  # Write to the log file.
30
37
  #
31
38
  # If no log file currently exists in the filesystem, a new file will be
@@ -47,11 +54,9 @@ module OpenC3
47
54
  end
48
55
 
49
56
  def write_entry(time_nsec_since_epoch, data)
50
- @entry.clear
51
- @entry << "#{time_nsec_since_epoch}\t"
52
- @entry << "#{data}\n"
53
- @file.write(@entry)
54
- @file_size += @entry.length
57
+ @file.write(data)
58
+ @file.write(NEWLINE)
59
+ @file_size += (data.length + NEWLINE.length)
55
60
  @first_time = time_nsec_since_epoch if !@first_time or time_nsec_since_epoch < @first_time
56
61
  @last_time = time_nsec_since_epoch if !@last_time or time_nsec_since_epoch > @last_time
57
62
  end
@@ -65,6 +70,27 @@ module OpenC3
65
70
  "#{first_timestamp}__#{last_timestamp}__#{topic_name}" + extension
66
71
  end
67
72
 
73
+ # Closing a log file isn't critical so we just log an error
74
+ # Returns threads that moves log to bucket
75
+ def close_file(take_mutex = true)
76
+ threads = []
77
+ @mutex.lock if take_mutex
78
+ begin
79
+ # Need to write the OFFSET_MARKER for each packet
80
+ @last_offsets.each do |redis_topic, last_offset|
81
+ time = Time.now
82
+ data = { time: time.to_nsec_from_epoch, '@timestamp' => time.xmlschema(3), severity: 'INFO', "microservice_name" => Logger.microservice_name, "container_name" => @container_name, "last_offset" => last_offset, "redis_topic" => redis_topic, "type" => "offset" }
83
+ write_entry(time.to_nsec_from_epoch, data.as_json(allow_nan: true).to_json(allow_nan: true)) if @file
84
+ end
85
+
86
+ threads.concat(super(false))
87
+
88
+ ensure
89
+ @mutex.unlock if take_mutex
90
+ end
91
+ return threads
92
+ end
93
+
68
94
  def extension
69
95
  '.txt'.freeze
70
96
  end