openc3 6.9.2 → 6.10.0

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.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/data/config/command_modifiers.yaml +79 -0
  3. data/data/config/item_modifiers.yaml +5 -0
  4. data/data/config/parameter_modifiers.yaml +5 -0
  5. data/data/config/telemetry_modifiers.yaml +79 -0
  6. data/ext/openc3/ext/packet/packet.c +9 -0
  7. data/lib/openc3/accessors/accessor.rb +27 -3
  8. data/lib/openc3/accessors/binary_accessor.rb +21 -4
  9. data/lib/openc3/accessors/template_accessor.rb +3 -2
  10. data/lib/openc3/api/cmd_api.rb +7 -3
  11. data/lib/openc3/api/tlm_api.rb +17 -7
  12. data/lib/openc3/interfaces/protocols/fixed_protocol.rb +19 -13
  13. data/lib/openc3/io/json_rpc.rb +6 -0
  14. data/lib/openc3/microservices/decom_microservice.rb +97 -17
  15. data/lib/openc3/microservices/interface_decom_common.rb +32 -0
  16. data/lib/openc3/microservices/interface_microservice.rb +3 -75
  17. data/lib/openc3/microservices/queue_microservice.rb +16 -1
  18. data/lib/openc3/migrations/20251022000000_remove_unique_id.rb +23 -0
  19. data/lib/openc3/models/plugin_model.rb +7 -1
  20. data/lib/openc3/models/queue_model.rb +32 -5
  21. data/lib/openc3/models/reaction_model.rb +25 -9
  22. data/lib/openc3/models/target_model.rb +78 -10
  23. data/lib/openc3/packets/commands.rb +33 -7
  24. data/lib/openc3/packets/packet.rb +75 -71
  25. data/lib/openc3/packets/packet_config.rb +78 -29
  26. data/lib/openc3/packets/packet_item.rb +11 -103
  27. data/lib/openc3/packets/parsers/packet_item_parser.rb +177 -34
  28. data/lib/openc3/packets/parsers/xtce_converter.rb +2 -2
  29. data/lib/openc3/packets/structure.rb +29 -21
  30. data/lib/openc3/packets/structure_item.rb +31 -19
  31. data/lib/openc3/packets/telemetry.rb +37 -11
  32. data/lib/openc3/script/suite_results.rb +2 -2
  33. data/lib/openc3/subpacketizers/subpacketizer.rb +18 -0
  34. data/lib/openc3/system/target.rb +3 -32
  35. data/lib/openc3/tools/table_manager/table_config.rb +9 -1
  36. data/lib/openc3/tools/table_manager/table_item_parser.rb +2 -2
  37. data/lib/openc3/top_level.rb +45 -19
  38. data/lib/openc3/topics/decom_interface_topic.rb +31 -0
  39. data/lib/openc3/utilities/env_helper.rb +10 -0
  40. data/lib/openc3/utilities/logger.rb +7 -11
  41. data/lib/openc3/version.rb +6 -6
  42. data/tasks/spec.rake +2 -1
  43. data/templates/tool_angular/package.json +2 -2
  44. data/templates/tool_react/package.json +1 -1
  45. data/templates/tool_svelte/package.json +1 -1
  46. data/templates/tool_vue/package.json +3 -3
  47. data/templates/widget/package.json +2 -2
  48. metadata +4 -1
@@ -33,16 +33,17 @@ module OpenC3
33
33
  # @param packet [Packet] The packet the item should be added to
34
34
  # @param cmd_or_tlm [String] Whether this is a command or telemetry packet
35
35
  # @param warnings [Array<String>] Array of warning strings from PacketConfig
36
- def self.parse(parser, packet, cmd_or_tlm, warnings)
37
- parser = PacketItemParser.new(parser, warnings)
36
+ def self.parse(parser, packet_config, packet, cmd_or_tlm, warnings)
37
+ parser = PacketItemParser.new(parser, packet_config, warnings)
38
38
  parser.verify_parameters(cmd_or_tlm)
39
39
  parser.create_packet_item(packet, cmd_or_tlm)
40
40
  end
41
41
 
42
42
  # @param parser [ConfigParser] Configuration parser
43
43
  # @param warnings [Array<String>] Array of warning strings from PacketConfig
44
- def initialize(parser, warnings)
44
+ def initialize(parser, packet_config, warnings)
45
45
  @parser = parser
46
+ @packet_config = packet_config
46
47
  @warnings = warnings
47
48
  @usage = get_usage()
48
49
  end
@@ -57,8 +58,12 @@ module OpenC3
57
58
  # The usage is formatted with brackets <XXX> around each option so
58
59
  # count the number of open brackets to determine the number of options
59
60
  max_options = @usage.count("<")
60
- # The last two options (description and endianness) are optional
61
- @parser.verify_num_parameters(max_options - 2, max_options, @usage)
61
+ if @parser.keyword.include?('STRUCTURE')
62
+ @parser.verify_num_parameters(max_options, max_options, @usage)
63
+ else
64
+ # The last two options (description and endianness) are optional
65
+ @parser.verify_num_parameters(max_options - 2, max_options, @usage)
66
+ end
62
67
  @parser.verify_parameter_naming(1) # Item name is the 1st parameter
63
68
  end
64
69
 
@@ -69,24 +74,38 @@ module OpenC3
69
74
  Logger.instance.warn msg
70
75
  @warnings << msg
71
76
  end
72
- item = PacketItem.new(item_name,
73
- get_bit_offset(),
74
- get_bit_size(),
75
- get_data_type(),
76
- get_endianness(packet),
77
- get_array_size(),
78
- :ERROR) # overflow
79
- if cmd_or_tlm == PacketConfig::COMMAND
80
- item.range = get_range()
81
- item.default = get_default()
77
+ if @parser.keyword.include?("STRUCTURE")
78
+ item = PacketItem.new(item_name,
79
+ get_bit_offset(),
80
+ get_bit_size(true),
81
+ :BLOCK,
82
+ :BIG_ENDIAN,
83
+ nil,
84
+ :ERROR) # overflow
85
+ else
86
+ item = PacketItem.new(item_name,
87
+ get_bit_offset(),
88
+ get_bit_size(),
89
+ get_data_type(),
90
+ get_endianness(packet),
91
+ get_array_size(),
92
+ :ERROR) # overflow
93
+ if cmd_or_tlm == PacketConfig::COMMAND
94
+ item.range = get_range()
95
+ item.default = get_default()
96
+ end
97
+ item.id_value = get_id_value(item)
98
+ item.description = get_description()
82
99
  end
83
- item.id_value = get_id_value(item)
84
- item.description = get_description()
85
100
  if append?
86
101
  item = packet.append(item)
87
102
  else
88
103
  item = packet.define(item)
89
104
  end
105
+ if @parser.keyword.include?("STRUCTURE")
106
+ structure = lookup_packet(get_cmd_or_tlm(), get_target_name(), get_packet_name())
107
+ packet.structurize_item(item, structure)
108
+ end
90
109
  item
91
110
  end
92
111
 
@@ -108,9 +127,15 @@ module OpenC3
108
127
  raise @parser.error(e, @usage)
109
128
  end
110
129
 
111
- def get_bit_size
130
+ def get_bit_size(check_structure = false)
112
131
  index = append? ? 1 : 2
113
- Integer(@parser.parameters[index])
132
+ bit_size = @parser.parameters[index]
133
+ if not check_structure or bit_size.to_s.upcase != 'DEFINED'
134
+ return Integer(bit_size)
135
+ else
136
+ structure = lookup_packet(get_cmd_or_tlm(), get_target_name(), get_packet_name())
137
+ return structure.defined_length_bits
138
+ end
114
139
  rescue => e
115
140
  raise @parser.error(e, @usage)
116
141
  end
@@ -152,7 +177,7 @@ module OpenC3
152
177
  return nil if @parser.keyword.include?('ARRAY')
153
178
 
154
179
  data_type = get_data_type()
155
- return nil if data_type == :STRING or data_type == :BLOCK
180
+ return nil unless data_type == :INT or data_type == :UINT or data_type == :FLOAT
156
181
 
157
182
  index = append? ? 3 : 4
158
183
  return nil if @parser.parameters[index] == 'nil'
@@ -165,6 +190,31 @@ module OpenC3
165
190
  min..max
166
191
  end
167
192
 
193
+ def get_cmd_or_tlm
194
+ index = append? ? 2 : 3
195
+ cmd_or_tlm = @parser.parameters[index].to_s.upcase.intern
196
+ raise ArgumentError, "Unknown type: #{cmd_or_tlm}" unless %i(CMD TLM COMMAND TELEMETRY).include?(cmd_or_tlm)
197
+ cmd_or_tlm
198
+ end
199
+
200
+ def get_target_name
201
+ index = append? ? 3 : 4
202
+ @parser.parameters[index].to_s.upcase
203
+ end
204
+
205
+ def get_packet_name
206
+ index = append? ? 4 : 5
207
+ @parser.parameters[index].to_s.upcase
208
+ end
209
+
210
+ def lookup_packet(cmd_or_tlm, target_name, packet_name)
211
+ if cmd_or_tlm == :CMD or cmd_or_tlm == :COMMAND
212
+ return @packet_config.commands[target_name][packet_name]
213
+ else
214
+ return @packet_config.telemetry[target_name][packet_name]
215
+ end
216
+ end
217
+
168
218
  def convert_string_value(index)
169
219
  # If the default value is 0x<data> (no quotes), it is treated as
170
220
  # binary data. Otherwise, the default value is considered to be a string.
@@ -182,8 +232,52 @@ module OpenC3
182
232
 
183
233
  index = append? ? 3 : 4
184
234
  data_type = get_data_type()
185
- return [] if data_type == :ARRAY
186
- return {} if data_type == :OBJECT
235
+ if data_type == :BOOL
236
+ value = @parser.parameters[index].to_s.upcase
237
+ if value == "TRUE" or value == "FALSE"
238
+ return ConfigParser.handle_true_false(@parser.parameters[index])
239
+ else
240
+ raise @parser.error("Default for BOOL data type must be TRUE or FALSE")
241
+ end
242
+ end
243
+ if data_type == :ARRAY
244
+ value = @parser.parameters[index].to_s
245
+ begin
246
+ value = JSON.parse(value, allow_nan: true)
247
+ rescue Exception
248
+ raise @parser.error("Unparsable value for ARRAY: #{value}")
249
+ end
250
+ if Array === value
251
+ return value
252
+ else
253
+ raise @parser.error("Default for ARRAY data type must be an Array")
254
+ end
255
+ end
256
+ if data_type == :OBJECT
257
+ value = @parser.parameters[index].to_s
258
+ begin
259
+ value = JSON.parse(value, allow_nan: true)
260
+ rescue Exception
261
+ raise @parser.error("Unparsable value for OBJECT: #{value}")
262
+ end
263
+ if Hash === value
264
+ return value
265
+ else
266
+ raise @parser.error("Default for OBJECT data type must be a Hash")
267
+ end
268
+ end
269
+ if data_type == :ANY
270
+ value = @parser.parameters[index].to_s
271
+ if value.length > 0
272
+ begin
273
+ return JSON.parse(value, allow_nan: true)
274
+ rescue Exception
275
+ return value
276
+ end
277
+ else
278
+ return ""
279
+ end
280
+ end
187
281
  if data_type == :STRING or data_type == :BLOCK
188
282
  return convert_string_value(index)
189
283
  else
@@ -209,6 +303,52 @@ module OpenC3
209
303
  end
210
304
 
211
305
  index = append? ? 3 : 4
306
+ if data_type == :BOOL
307
+ value = @parser.parameters[index].to_s.upcase
308
+ if value == "TRUE" or value == "FALSE"
309
+ return ConfigParser.handle_true_false(@parser.parameters[index])
310
+ else
311
+ raise @parser.error("ID Value for BOOL data type must be TRUE or FALSE")
312
+ end
313
+ end
314
+ if data_type == :ARRAY
315
+ value = @parser.parameters[index].to_s
316
+ begin
317
+ value = JSON.parse(value, allow_nan: true)
318
+ rescue Exception
319
+ raise @parser.error("Unparsable value for ARRAY: #{value}")
320
+ end
321
+ if Array === value
322
+ return value
323
+ else
324
+ raise @parser.error("ID Value for ARRAY data type must be an Array")
325
+ end
326
+ end
327
+ if data_type == :OBJECT
328
+ value = @parser.parameters[index].to_s
329
+ begin
330
+ value = JSON.parse(value, allow_nan: true)
331
+ rescue Exception
332
+ raise @parser.error("Unparsable value for OBJECT: #{value}")
333
+ end
334
+ if Hash === value
335
+ return value
336
+ else
337
+ raise @parser.error("ID Value for OBJECT data type must be a Hash")
338
+ end
339
+ end
340
+ if data_type == :ANY
341
+ value = @parser.parameters[index].to_s
342
+ if value.length > 0
343
+ begin
344
+ return JSON.parse(value, allow_nan: true)
345
+ rescue Exception
346
+ return value
347
+ end
348
+ else
349
+ return ""
350
+ end
351
+ end
212
352
  if data_type == :STRING or data_type == :BLOCK
213
353
  return convert_string_value(index)
214
354
  else
@@ -229,10 +369,14 @@ module OpenC3
229
369
  usage = "#{@parser.keyword} <ITEM NAME> "
230
370
  usage << "<BIT OFFSET> " unless @parser.keyword.include?("APPEND")
231
371
  usage << bit_size_usage()
232
- usage << type_usage()
233
- usage << "<TOTAL ARRAY BIT SIZE> " if @parser.keyword.include?("ARRAY")
234
- usage << id_usage()
235
- usage << "<DESCRIPTION (Optional)> <ENDIANNESS (Optional)>"
372
+ if not @parser.keyword.include?("STRUCTURE")
373
+ usage << type_usage()
374
+ usage << "<TOTAL ARRAY BIT SIZE> " if @parser.keyword.include?("ARRAY")
375
+ usage << id_usage()
376
+ usage << "<DESCRIPTION (Optional)> <ENDIANNESS (Optional)>"
377
+ else
378
+ usage << "<CMD or TLM> <Target Name> <Packet Name>"
379
+ end
236
380
  usage
237
381
  end
238
382
 
@@ -247,13 +391,13 @@ module OpenC3
247
391
  def type_usage
248
392
  keyword = @parser.keyword
249
393
  # Item type usage is simple so just return it
250
- return "<TYPE: INT/UINT/FLOAT/STRING/BLOCK/DERIVED/ARRAY/OBJECT> " if keyword.include?("ITEM")
394
+ return "<TYPE: INT/UINT/FLOAT/STRING/BLOCK/DERIVED/BOOL/ARRAY/OBJECT/ANY> " if keyword.include?("ITEM")
251
395
 
252
396
  # Build up the parameter type usage based on the keyword
253
397
  usage = "<TYPE: "
254
398
  # ARRAY types don't have min or max or default values
255
399
  if keyword.include?("ARRAY")
256
- usage << "INT/UINT/FLOAT/STRING/BLOCK/OBJECT> "
400
+ usage << "INT/UINT/FLOAT/STRING/BLOCK/BOOL/OBJECT/ANY> "
257
401
  else
258
402
  begin
259
403
  data_type = get_data_type()
@@ -261,14 +405,13 @@ module OpenC3
261
405
  # If the data type could not be determined set something
262
406
  data_type = :INT
263
407
  end
264
- # STRING, BLOCK, ARRAY, OBJECT types do not have min or max values
265
- if data_type == :STRING || data_type == :BLOCK
266
- usage << "STRING/BLOCK/ARRAY/OBJECT> "
408
+ if data_type == :INT || data_type == :UINT || data_type == :FLOAT || data_type == :DERIVED
409
+ usage << "INT/UINT/FLOAT/DERIVED> <MIN VALUE> <MAX VALUE> "
267
410
  else
268
- usage << "INT/UINT/FLOAT> <MIN VALUE> <MAX VALUE> "
411
+ usage << "STRING/BLOCK/BOOL/ARRAY/OBJECT/ANY> "
269
412
  end
270
- # ID Values do not have default values (or ARRAY/OBJECT)
271
- unless keyword.include?("ID") or data_type == :ARRAY or data_type == :OBJECT
413
+ # ID Values do not have default values
414
+ unless keyword.include?("ID")
272
415
  usage << "<DEFAULT_VALUE> "
273
416
  end
274
417
  end
@@ -261,8 +261,8 @@ module OpenC3
261
261
  to_xtce_string(item, param_or_arg, xml, 'String')
262
262
  when :BLOCK
263
263
  to_xtce_string(item, param_or_arg, xml, 'Binary')
264
- when :DERIVED
265
- raise "DERIVED data type not supported in XTCE"
264
+ else
265
+ raise "#{item.data_type} data type not supported in XTCE"
266
266
  end
267
267
 
268
268
  # Handle arrays
@@ -248,32 +248,34 @@ module OpenC3
248
248
 
249
249
  # Recalculate the overall defined length of the structure
250
250
  update_needed = false
251
- if item.bit_offset >= 0
252
- if item.bit_size > 0
253
- if item.array_size
254
- if item.array_size >= 0
255
- item_defined_length_bits = item.bit_offset + item.array_size
251
+ if not item.parent_item
252
+ if item.bit_offset >= 0
253
+ if item.bit_size > 0
254
+ if item.array_size
255
+ if item.array_size >= 0
256
+ item_defined_length_bits = item.bit_offset + item.array_size
257
+ else
258
+ item_defined_length_bits = item.bit_offset
259
+ end
256
260
  else
257
- item_defined_length_bits = item.bit_offset
261
+ item_defined_length_bits = item.bit_offset + item.bit_size
262
+ end
263
+ if item_defined_length_bits > @pos_bit_size
264
+ @pos_bit_size = item_defined_length_bits
265
+ update_needed = true
258
266
  end
259
267
  else
260
- item_defined_length_bits = item.bit_offset + item.bit_size
261
- end
262
- if item_defined_length_bits > @pos_bit_size
263
- @pos_bit_size = item_defined_length_bits
264
- update_needed = true
268
+ if item.bit_offset > @pos_bit_size
269
+ @pos_bit_size = item.bit_offset
270
+ update_needed = true
271
+ end
265
272
  end
266
273
  else
267
- if item.bit_offset > @pos_bit_size
268
- @pos_bit_size = item.bit_offset
274
+ if item.bit_offset.abs > @neg_bit_size
275
+ @neg_bit_size = item.bit_offset.abs
269
276
  update_needed = true
270
277
  end
271
278
  end
272
- else
273
- if item.bit_offset.abs > @neg_bit_size
274
- @neg_bit_size = item.bit_offset.abs
275
- update_needed = true
276
- end
277
279
  end
278
280
  if update_needed
279
281
  @defined_length_bits = @pos_bit_size + @neg_bit_size
@@ -349,7 +351,7 @@ module OpenC3
349
351
  elsif item.variable_bit_size['length_value_bit_offset'] > 0
350
352
  minimum_data_bits = item.variable_bit_size['length_value_bit_offset'] * item.variable_bit_size['length_bits_per_count']
351
353
  end
352
- if minimum_data_bits > 0 and item.bit_offset >= 0 and @defined_length_bits == item.bit_offset
354
+ if minimum_data_bits > 0 and item.bit_offset >= 0 and @defined_length_bits == item.bit_offset and not item.parent_item
353
355
  @defined_length_bits += minimum_data_bits
354
356
  end
355
357
  end
@@ -436,7 +438,11 @@ module OpenC3
436
438
  def read_all(value_type = :RAW, buffer = @buffer, top = true)
437
439
  item_array = []
438
440
  synchronize_allow_reads(top) do
439
- @sorted_items.each { |item| item_array << [item.name, read_item(item, value_type, buffer)] }
441
+ @sorted_items.each do |item|
442
+ unless item.hidden
443
+ item_array << [item.name, read_item(item, value_type, buffer)]
444
+ end
445
+ end
440
446
  end
441
447
  return item_array
442
448
  end
@@ -454,7 +460,7 @@ module OpenC3
454
460
  string = ''
455
461
  synchronize_allow_reads(true) do
456
462
  @sorted_items.each do |item|
457
- next if ignored && ignored.include?(item.name)
463
+ next if item.hidden || (ignored && ignored.include?(item.name))
458
464
 
459
465
  if (item.data_type != :BLOCK) ||
460
466
  (item.data_type == :BLOCK and value_type != :RAW and
@@ -631,6 +637,8 @@ module OpenC3
631
637
  def recalculate_bit_offsets
632
638
  adjustment = 0
633
639
  @sorted_items.each do |item|
640
+ # Parented items rely on the parent
641
+ next if item.parent_item
634
642
  # Anything with a negative bit offset should be left alone
635
643
  if item.original_bit_offset >= 0
636
644
  item.bit_offset = item.original_bit_offset + adjustment
@@ -14,7 +14,7 @@
14
14
  # GNU Affero General Public License for more details.
15
15
 
16
16
  # Modified by OpenC3, Inc.
17
- # All changes Copyright 2024, OpenC3, Inc.
17
+ # All changes Copyright 2025, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
20
  # This file may also be used under the terms of a commercial license
@@ -31,7 +31,7 @@ module OpenC3
31
31
  @@create_index = 0
32
32
 
33
33
  # Valid data types adds :DERIVED, :ARRAY, :OBJECT to those defined by BinaryAccessor
34
- DATA_TYPES = BinaryAccessor::DATA_TYPES << :DERIVED << :ARRAY << :OBJECT
34
+ DATA_TYPES = [:INT, :UINT, :FLOAT, :STRING, :BLOCK, :BOOL, :OBJECT, :ARRAY, :ANY, :DERIVED]
35
35
 
36
36
  # Name is used by higher level classes to access the StructureItem.
37
37
  # @return [String] Name of the item
@@ -98,6 +98,15 @@ module OpenC3
98
98
  # @return [Integer] Incrementing value that shows relative order items are created
99
99
  attr_reader :create_index
100
100
 
101
+ # @return [Boolean] Indicates if item should be listed in items and in read all type methods
102
+ attr_accessor :hidden
103
+
104
+ # @return [StructureItem] Parent structure item
105
+ attr_accessor :parent_item
106
+
107
+ # @return [Structure] Structure associated with item
108
+ attr_accessor :structure
109
+
101
110
  # Create a StructureItem by setting all the attributes. It
102
111
  # calls all the setter routines to do the attribute verification and then
103
112
  # verifies the overall integrity.
@@ -126,6 +135,9 @@ module OpenC3
126
135
  self.overflow = overflow
127
136
  self.overlap = false
128
137
  self.variable_bit_size = nil
138
+ self.hidden = false
139
+ self.parent_item = nil
140
+ self.structure = nil
129
141
  @create_index = @@create_index
130
142
  @@create_index += 1
131
143
  @structure_item_constructed = true
@@ -198,7 +210,7 @@ module OpenC3
198
210
  when *DATA_TYPES
199
211
  # Valid data_type
200
212
  else
201
- raise ArgumentError, "#{@name}: unknown data_type: #{data_type} - Must be :INT, :UINT, :FLOAT, :STRING, :BLOCK, or :DERIVED"
213
+ raise ArgumentError, "#{@name}: unknown data_type: #{data_type} - Must be #{DATA_TYPES.join(", ")}"
202
214
  end
203
215
 
204
216
  @data_type = data_type
@@ -308,31 +320,31 @@ module OpenC3
308
320
  end
309
321
  alias dup clone
310
322
 
311
- def self.from_json(hash)
312
- # Convert strings to symbols
313
- endianness = hash['endianness'] ? hash['endianness'].intern : nil
314
- data_type = hash['data_type'] ? hash['data_type'].intern : nil
315
- overflow = hash['overflow'] ? hash['overflow'].intern : nil
316
- si = StructureItem.new(hash['name'], hash['bit_offset'], hash['bit_size'], data_type,
317
- endianness, hash['array_size'], overflow)
318
- si.key = hash['key'] || hash['name']
319
- si.variable_bit_size = hash['variable_bit_size']
320
- si
321
- end
322
-
323
323
  def as_json(*a)
324
324
  hash = {}
325
325
  hash['name'] = self.name
326
326
  hash['key'] = self.key
327
327
  hash['bit_offset'] = self.original_bit_offset
328
328
  hash['bit_size'] = self.original_bit_size
329
- hash['data_type'] = self.data_type
330
- hash['endianness'] = self.endianness
331
- hash['array_size'] = self.original_array_size
332
- hash['overflow'] = self.overflow
329
+ hash['data_type'] = self.data_type.to_s
330
+ hash['endianness'] = self.endianness.to_s
331
+ hash['overflow'] = self.overflow.to_s
332
+ hash['overlap'] = self.overlap
333
+ hash['create_index'] = self.create_index
334
+ hash['hidden'] = self.hidden
335
+ if self.original_array_size
336
+ hash['array_size'] = self.original_array_size
337
+ end
333
338
  if @variable_bit_size
334
339
  hash['variable_bit_size'] = @variable_bit_size
335
340
  end
341
+ if self.parent_item
342
+ hash['parent_item'] = self.parent_item.as_json
343
+ end
344
+ if self.structure
345
+ hash['structure'] = self.structure.as_json
346
+ end
347
+
336
348
  hash
337
349
  end
338
350
 
@@ -264,8 +264,8 @@ module OpenC3
264
264
  # default value of nil means to search all known targets.
265
265
  # @return [Packet] The identified packet with its data set to the given
266
266
  # packet_data buffer. Returns nil if no packet could be identified.
267
- def identify!(packet_data, target_names = nil)
268
- identified_packet = identify(packet_data, target_names)
267
+ def identify!(packet_data, target_names = nil, subpackets: false)
268
+ identified_packet = identify(packet_data, target_names, subpackets: subpackets)
269
269
  identified_packet.buffer = packet_data if identified_packet
270
270
  return identified_packet
271
271
  end
@@ -277,7 +277,7 @@ module OpenC3
277
277
  # @param target_names [Array<String>] List of target names to limit the search. The
278
278
  # default value of nil means to search all known targets.
279
279
  # @return [Packet] The identified packet, Returns nil if no packet could be identified.
280
- def identify(packet_data, target_names = nil)
280
+ def identify(packet_data, target_names = nil, subpackets: false)
281
281
  target_names = target_names() unless target_names
282
282
 
283
283
  target_names.each do |target_name|
@@ -292,18 +292,36 @@ module OpenC3
292
292
  next
293
293
  end
294
294
 
295
- target = System.targets[target_name]
296
- if target and target.tlm_unique_id_mode
295
+ if (not subpackets and System.telemetry.tlm_unique_id_mode(target_name)) or (subpackets and System.telemetry.tlm_subpacket_unique_id_mode(target_name))
297
296
  # Iterate through the packets and see if any represent the buffer
298
297
  target_packets.each do |_packet_name, packet|
299
- return packet if packet.identify?(packet_data)
298
+ if subpackets
299
+ next unless packet.subpacket
300
+ else
301
+ next if packet.subpacket
302
+ end
303
+ return packet if packet.identify?(packet_data) # Handles virtual
300
304
  end
301
305
  else
302
306
  # Do a hash lookup to quickly identify the packet
303
- if target_packets.length > 0
304
- packet = target_packets.first[1]
307
+ packet = nil
308
+ target_packets.each do |_packet_name, target_packet|
309
+ next if target_packet.virtual
310
+ if subpackets
311
+ next unless target_packet.subpacket
312
+ else
313
+ next if target_packet.subpacket
314
+ end
315
+ packet = target_packet
316
+ break
317
+ end
318
+ if packet
305
319
  key = packet.read_id_values(packet_data)
306
- hash = @config.tlm_id_value_hash[target_name]
320
+ if subpackets
321
+ hash = @config.tlm_subpacket_id_value_hash[target_name]
322
+ else
323
+ hash = @config.tlm_id_value_hash[target_name]
324
+ end
307
325
  identified_packet = hash[key]
308
326
  identified_packet = hash['CATCHALL'.freeze] unless identified_packet
309
327
  return identified_packet if identified_packet
@@ -314,9 +332,9 @@ module OpenC3
314
332
  return nil
315
333
  end
316
334
 
317
- def identify_and_define_packet(packet, target_names = nil)
335
+ def identify_and_define_packet(packet, target_names = nil, subpackets: false)
318
336
  if !packet.identified?
319
- identified_packet = identify(packet.buffer(false), target_names)
337
+ identified_packet = identify(packet.buffer(false), target_names, subpackets: subpackets)
320
338
  return nil unless identified_packet
321
339
 
322
340
  identified_packet = identified_packet.clone
@@ -425,5 +443,13 @@ module OpenC3
425
443
  def dynamic_add_packet(packet, affect_ids: false)
426
444
  @config.dynamic_add_packet(packet, :TELEMETRY, affect_ids: affect_ids)
427
445
  end
446
+
447
+ def tlm_unique_id_mode(target_name)
448
+ return @config.tlm_unique_id_mode[target_name.upcase]
449
+ end
450
+
451
+ def tlm_subpacket_unique_id_mode(target_name)
452
+ return @config.tlm_subpacket_unique_id_mode[target_name.upcase]
453
+ end
428
454
  end # class Telemetry
429
455
  end
@@ -172,11 +172,11 @@ module OpenC3
172
172
  end
173
173
 
174
174
  def write(string)
175
- @report << (Time.now.sys.formatted + ': ' + string)
175
+ @report << (Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%6NZ") + ': ' + string)
176
176
  end
177
177
 
178
178
  def puts(string)
179
- @report << (Time.now.sys.formatted + ': ' + string)
179
+ @report << (Time.now.utc.strftime("%Y-%m-%dT%H:%M:%S.%6NZ") + ': ' + string)
180
180
  end
181
181
 
182
182
  # def collect_metadata(parent = nil)
@@ -0,0 +1,18 @@
1
+ class Subpacketizer
2
+ attr_reader :args
3
+
4
+ def initialize(packet=nil)
5
+ @packet = packet
6
+ @args = []
7
+ end
8
+
9
+ # Subclass and implement this method to break packet into array of subpackets
10
+ # Subpackets should be fully identified and defined
11
+ def call(packet)
12
+ return [packet]
13
+ end
14
+
15
+ def as_json(*a)
16
+ { 'class' => self.class.name, 'args' => @args.as_json(*a) }
17
+ end
18
+ end