openc3 5.0.8 → 5.0.10

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 (50) hide show
  1. checksums.yaml +4 -4
  2. data/bin/openc3cli +128 -95
  3. data/data/config/_id_items.yaml +2 -1
  4. data/data/config/_id_params.yaml +2 -1
  5. data/data/config/_items.yaml +2 -1
  6. data/data/config/_params.yaml +2 -1
  7. data/data/config/command_modifiers.yaml +30 -0
  8. data/data/config/item_modifiers.yaml +10 -0
  9. data/data/config/microservice.yaml +12 -0
  10. data/data/config/param_item_modifiers.yaml +10 -0
  11. data/data/config/plugins.yaml +0 -9
  12. data/data/config/telemetry_modifiers.yaml +10 -0
  13. data/data/config/widgets.yaml +49 -73
  14. data/ext/openc3/ext/packet/packet.c +20 -2
  15. data/ext/openc3/ext/structure/structure.c +12 -17
  16. data/lib/openc3/accessors/accessor.rb +71 -0
  17. data/lib/openc3/accessors/binary_accessor.rb +1226 -0
  18. data/lib/openc3/accessors/cbor_accessor.rb +83 -0
  19. data/lib/openc3/accessors/html_accessor.rb +28 -0
  20. data/lib/openc3/accessors/json_accessor.rb +131 -0
  21. data/lib/openc3/accessors/xml_accessor.rb +67 -0
  22. data/lib/openc3/accessors.rb +23 -0
  23. data/lib/openc3/api/cmd_api.rb +5 -2
  24. data/lib/openc3/api/interface_api.rb +15 -0
  25. data/lib/openc3/api/limits_api.rb +3 -3
  26. data/lib/openc3/config/config_parser.rb +10 -4
  27. data/lib/openc3/core_ext/file.rb +5 -0
  28. data/lib/openc3/core_ext/tempfile.rb +20 -0
  29. data/lib/openc3/io/json_rpc.rb +3 -3
  30. data/lib/openc3/microservices/cleanup_microservice.rb +7 -5
  31. data/lib/openc3/models/cvt_model.rb +1 -10
  32. data/lib/openc3/models/interface_model.rb +41 -0
  33. data/lib/openc3/models/microservice_model.rb +33 -1
  34. data/lib/openc3/models/plugin_model.rb +1 -1
  35. data/lib/openc3/models/target_model.rb +85 -68
  36. data/lib/openc3/packets/binary_accessor.rb +2 -1207
  37. data/lib/openc3/packets/packet.rb +106 -6
  38. data/lib/openc3/packets/packet_config.rb +30 -7
  39. data/lib/openc3/packets/parsers/limits_response_parser.rb +1 -3
  40. data/lib/openc3/packets/parsers/processor_parser.rb +1 -2
  41. data/lib/openc3/packets/parsers/xtce_parser.rb +68 -3
  42. data/lib/openc3/packets/structure.rb +39 -14
  43. data/lib/openc3/packets/structure_item.rb +15 -1
  44. data/lib/openc3/script/storage.rb +19 -1
  45. data/lib/openc3/utilities/local_mode.rb +521 -0
  46. data/lib/openc3/utilities/simulated_target.rb +3 -2
  47. data/lib/openc3/utilities/target_file.rb +146 -0
  48. data/lib/openc3/version.rb +5 -5
  49. data/lib/openc3.rb +1 -0
  50. metadata +45 -7
@@ -21,6 +21,7 @@ require 'digest'
21
21
  require 'openc3/packets/structure'
22
22
  require 'openc3/packets/packet_item'
23
23
  require 'openc3/ext/packet' if RUBY_ENGINE == 'ruby' and !ENV['OPENC3_NO_EXT']
24
+ require 'base64'
24
25
 
25
26
  module OpenC3
26
27
  # Adds features common to all OpenC3 packets of data to the Structure class.
@@ -85,6 +86,9 @@ module OpenC3
85
86
  # @return [Symbol] :CMD or :TLM
86
87
  attr_accessor :cmd_or_tlm
87
88
 
89
+ # @return [String] Base data for building packet
90
+ attr_reader :template
91
+
88
92
  # Valid format types
89
93
  VALUE_TYPES = [:RAW, :CONVERTED, :FORMATTED, :WITH_UNITS]
90
94
 
@@ -98,7 +102,7 @@ module OpenC3
98
102
  # @param buffer [String] String buffer to hold the packet data
99
103
  # @param item_class [Class] Class used to instantiate items (Must be a
100
104
  # subclass of PacketItem)
101
- def initialize(target_name, packet_name, default_endianness = :BIG_ENDIAN, description = nil, buffer = nil, item_class = PacketItem)
105
+ def initialize(target_name = nil, packet_name = nil, default_endianness = :BIG_ENDIAN, description = nil, buffer = nil, item_class = PacketItem)
102
106
  super(default_endianness, buffer, item_class)
103
107
  # Explictly call the defined setter methods
104
108
  self.target_name = target_name
@@ -123,6 +127,7 @@ module OpenC3
123
127
  @stored = false
124
128
  @extra = nil
125
129
  @cmd_or_tlm = nil
130
+ @template = nil
126
131
  end
127
132
 
128
133
  # Sets the target name this packet is associated with. Unidentified packets
@@ -290,7 +295,9 @@ module OpenC3
290
295
  begin
291
296
  internal_buffer_equals(buffer)
292
297
  rescue RuntimeError
293
- Logger.instance.error "#{@target_name} #{@packet_name} received with actual packet length of #{buffer.length} but defined length of #{@defined_length}"
298
+ if BinaryAccessor == @accessor
299
+ Logger.instance.error "#{@target_name} #{@packet_name} received with actual packet length of #{buffer.length} but defined length of #{@defined_length}"
300
+ end
294
301
  end
295
302
  @read_conversion_cache.clear if @read_conversion_cache
296
303
  process()
@@ -353,6 +360,20 @@ module OpenC3
353
360
  end
354
361
  end
355
362
 
363
+ # Sets the template data for the packet
364
+ # Template data is used as the default buffer contents if provided
365
+ #
366
+ # @param hazardous_description [String] Hazardous description of the packet
367
+ def template=(template)
368
+ if template
369
+ raise ArgumentError, "template must be a String but is a #{template.class}" unless String === template
370
+
371
+ @template = template.freeze
372
+ else
373
+ @template = nil
374
+ end
375
+ end
376
+
356
377
  # Review bit offset to look for overlapping definitions. This will allow
357
378
  # gaps in the packet, but not allow the same bits to be used for multiple
358
379
  # variables.
@@ -531,11 +552,16 @@ module OpenC3
531
552
  # @param value_type [Symbol] How to convert the item before returning it.
532
553
  # Must be one of {VALUE_TYPES}
533
554
  # @param buffer (see Structure#read_item)
555
+ # @param given_raw Given raw value to optimize
534
556
  # @return The value. :FORMATTED and :WITH_UNITS values are always returned
535
557
  # as Strings. :RAW values will match their data_type. :CONVERTED values
536
558
  # can be any type.
537
- def read_item(item, value_type = :CONVERTED, buffer = @buffer)
538
- value = super(item, :RAW, buffer)
559
+ def read_item(item, value_type = :CONVERTED, buffer = @buffer, given_raw = nil)
560
+ if given_raw
561
+ value = given_raw
562
+ else
563
+ value = super(item, :RAW, buffer)
564
+ end
539
565
  derived_raw = false
540
566
  if item.data_type == :DERIVED && value_type == :RAW
541
567
  value_type = :CONVERTED
@@ -632,6 +658,31 @@ module OpenC3
632
658
  return value
633
659
  end
634
660
 
661
+ # Read a list of items in the structure
662
+ #
663
+ # @param items [StructureItem] Array of PacketItem or one of its subclasses
664
+ # @param value_type [Symbol] Value type to read for every item
665
+ # @param buffer [String] The binary buffer to read the items from
666
+ # @return Hash of read names and values
667
+ def read_items(items, value_type = :RAW, buffer = @buffer, raw_value = nil)
668
+ buffer = allocate_buffer_if_needed() unless buffer
669
+ if value_type == :RAW
670
+ result = super(items, value_type, buffer)
671
+ # Must handle DERIVED special
672
+ items.each do |item|
673
+ if item.data_type == :DERIVED
674
+ result[item.name] = read_item(item, value_type, buffer)
675
+ end
676
+ end
677
+ else
678
+ result = {}
679
+ items.each do |item|
680
+ result[item.name] = read_item(item, value_type, buffer)
681
+ end
682
+ end
683
+ return result
684
+ end
685
+
635
686
  # Write an item in the packet
636
687
  #
637
688
  # @param item [PacketItem] Instance of PacketItem or one of its subclasses
@@ -674,6 +725,24 @@ module OpenC3
674
725
  end
675
726
  end
676
727
 
728
+ # Write values to the buffer based on the item definitions
729
+ #
730
+ # @param items [StructureItem] Array of StructureItem or one of its subclasses
731
+ # @param value [Object] Array of values based on the item definitions.
732
+ # @param value_type [Symbol] Value type of each item to write
733
+ # @param buffer [String] The binary buffer to write the values to
734
+ def write_items(items, values, value_type = :RAW, buffer = @buffer)
735
+ buffer = allocate_buffer_if_needed() unless buffer
736
+ if value_type == :RAW
737
+ return super(items, values, value_type, buffer)
738
+ else
739
+ items.each_with_index do |item, index|
740
+ write_item(item, values[index], value_type, buffer)
741
+ end
742
+ end
743
+ return buffer
744
+ end
745
+
677
746
  # Read an item in the packet by name
678
747
  #
679
748
  # @param name [String] Name of the item to read
@@ -738,12 +807,17 @@ module OpenC3
738
807
  #
739
808
  # @param buffer [String] Raw buffer of binary data
740
809
  # @param skip_item_names [Array] Array of item names to skip
741
- def restore_defaults(buffer = @buffer, skip_item_names = nil)
810
+ # @param use_templase [Boolean] Apply template before setting defaults (or not)
811
+ def restore_defaults(buffer = @buffer, skip_item_names = nil, use_template = true)
812
+ buffer = allocate_buffer_if_needed() unless buffer
742
813
  upcase_skip_item_names = skip_item_names.map(&:upcase) if skip_item_names
814
+ buffer.replace(@template) if @template and use_template
743
815
  @sorted_items.each do |item|
744
816
  next if RESERVED_ITEM_NAMES.include?(item.name)
745
817
 
746
- write_item(item, item.default, :CONVERTED, buffer) unless skip_item_names and upcase_skip_item_names.include?(item.name)
818
+ unless item.default.nil?
819
+ write_item(item, item.default, :CONVERTED, buffer) unless skip_item_names and upcase_skip_item_names.include?(item.name)
820
+ end
747
821
  end
748
822
  end
749
823
 
@@ -986,6 +1060,8 @@ module OpenC3
986
1060
  config['disabled'] = true if @disabled
987
1061
  config['hidden'] = true if @hidden
988
1062
  config['stale'] = true if @stale
1063
+ config['accessor'] = @accessor.to_s
1064
+ config['template'] = Base64.encode64(@template) if @template
989
1065
 
990
1066
  if @processors
991
1067
  processors = []
@@ -1024,6 +1100,14 @@ module OpenC3
1024
1100
  packet.disabled = hash['disabled']
1025
1101
  packet.hidden = hash['hidden']
1026
1102
  # packet.stale is read only
1103
+ if hash['accessor']
1104
+ begin
1105
+ packet.accessor = OpenC3::const_get(hash['accessor'])
1106
+ rescue => error
1107
+ Logger.instance.error "#{packet.target_name} #{packet.packet_name} accessor of #{hash['accessor']} could not be found due to #{error}"
1108
+ end
1109
+ end
1110
+ packet.template = Base64.decode64(hash['template']) if hash['template']
1027
1111
  packet.meta = hash['meta']
1028
1112
  # Can't convert processors
1029
1113
  hash['items'].each do |item|
@@ -1032,6 +1116,22 @@ module OpenC3
1032
1116
  packet
1033
1117
  end
1034
1118
 
1119
+ def decom
1120
+ # Read all the RAW at once because this could be optimized by the accessor
1121
+ json_hash = read_items(@sorted_items)
1122
+
1123
+ # Now read all other value types - no accessor required
1124
+ @sorted_items.each do |item|
1125
+ given_raw = json_hash[item.name]
1126
+ json_hash["#{item.name}__C"] = read_item(item, :CONVERTED, @buffer, given_raw) if item.states or (item.read_conversion and item.data_type != :DERIVED)
1127
+ json_hash["#{item.name}__F"] = read_item(item, :FORMATTED, @buffer, given_raw) if item.format_string
1128
+ json_hash["#{item.name}__U"] = read_item(item, :WITH_UNITS, @buffer, given_raw) if item.units
1129
+ limits_state = item.limits.state
1130
+ json_hash["#{item.name}__L"] = limits_state if limits_state
1131
+ end
1132
+ json_hash
1133
+ end
1134
+
1035
1135
  protected
1036
1136
 
1037
1137
  # Performs packet specific processing on the packet.
@@ -209,7 +209,7 @@ module OpenC3
209
209
  'PARAMETER', 'ID_ITEM', 'ID_PARAMETER', 'ARRAY_ITEM', 'ARRAY_PARAMETER', 'APPEND_ITEM',\
210
210
  'APPEND_PARAMETER', 'APPEND_ID_ITEM', 'APPEND_ID_PARAMETER', 'APPEND_ARRAY_ITEM',\
211
211
  'APPEND_ARRAY_PARAMETER', 'ALLOW_SHORT', 'HAZARDOUS', 'PROCESSOR', 'META',\
212
- 'DISABLE_MESSAGES', 'HIDDEN', 'DISABLED'
212
+ 'DISABLE_MESSAGES', 'HIDDEN', 'DISABLED', 'ACCESSOR', 'TEMPLATE', 'TEMPLATE_FILE'
213
213
  raise parser.error("No current packet for #{keyword}") unless @current_packet
214
214
 
215
215
  process_current_packet(parser, keyword, params)
@@ -221,7 +221,7 @@ module OpenC3
221
221
  'POLY_WRITE_CONVERSION', 'SEG_POLY_READ_CONVERSION', 'SEG_POLY_WRITE_CONVERSION',\
222
222
  'GENERIC_READ_CONVERSION_START', 'GENERIC_WRITE_CONVERSION_START', 'REQUIRED',\
223
223
  'LIMITS', 'LIMITS_RESPONSE', 'UNITS', 'FORMAT_STRING', 'DESCRIPTION',\
224
- 'MINIMUM_VALUE', 'MAXIMUM_VALUE', 'DEFAULT_VALUE', 'OVERFLOW', 'OVERLAP'
224
+ 'MINIMUM_VALUE', 'MAXIMUM_VALUE', 'DEFAULT_VALUE', 'OVERFLOW', 'OVERLAP', 'KEY'
225
225
  raise parser.error("No current item for #{keyword}") unless @current_item
226
226
 
227
227
  process_current_item(parser, keyword, params)
@@ -427,7 +427,30 @@ module OpenC3
427
427
  parser.verify_num_parameters(0, 0, usage)
428
428
  @current_packet.hidden = true
429
429
  @current_packet.disabled = true
430
+ when 'ACCESSOR'
431
+ usage = "#{keyword} <Accessor class name>"
432
+ parser.verify_num_parameters(1, 1, usage)
433
+ begin
434
+ klass = OpenC3.require_class(params[0])
435
+ @current_packet.accessor = klass
436
+ rescue Exception => err
437
+ raise parser.error(err)
438
+ end
439
+
440
+ when 'TEMPLATE'
441
+ usage = "#{keyword} <Template string>"
442
+ parser.verify_num_parameters(1, 1, usage)
443
+ @current_packet.template = params[0]
430
444
 
445
+ when 'TEMPLATE_FILE'
446
+ usage = "#{keyword} <Template file path>"
447
+ parser.verify_num_parameters(1, 1, usage)
448
+
449
+ begin
450
+ @current_packet.template = parser.read_file(params[0])
451
+ rescue Exception => err
452
+ raise parser.error(err)
453
+ end
431
454
  end
432
455
  end
433
456
 
@@ -444,14 +467,11 @@ module OpenC3
444
467
  usage = "#{keyword} <conversion class filename> <custom parameters> ..."
445
468
  parser.verify_num_parameters(1, nil, usage)
446
469
  begin
447
- # require should be performed in target.txt
448
- klass = params[0].filename_to_class_name.to_class
449
- raise parser.error("#{params[0].filename_to_class_name} class not found. Did you require the file in target.txt?", usage) unless klass
450
-
470
+ klass = OpenC3.require_class(params[0])
451
471
  conversion = klass.new(*params[1..(params.length - 1)])
452
472
  @current_item.public_send("#{keyword.downcase}=".to_sym, conversion)
453
473
  if klass != ProcessorConversion and (conversion.converted_type.nil? or conversion.converted_bit_size.nil?)
454
- msg = "Read Conversion #{params[0].filename_to_class_name} on item #{@current_item.name} does not specify converted type or bit size"
474
+ msg = "Read Conversion #{params[0]} on item #{@current_item.name} does not specify converted type or bit size"
455
475
  @warnings << msg
456
476
  Logger.instance.warn @warnings[-1]
457
477
  end
@@ -600,6 +620,9 @@ module OpenC3
600
620
  parser.verify_num_parameters(0, 0, 'OVERLAP')
601
621
  @current_item.overlap = true
602
622
 
623
+ when 'KEY'
624
+ parser.verify_num_parameters(1, 1, 'KEY <key or path into data>')
625
+ @current_item.key = params[0]
603
626
  end
604
627
  end
605
628
 
@@ -45,9 +45,7 @@ module OpenC3
45
45
 
46
46
  # @param item [PacketItem] The item the limits response should be added to
47
47
  def create_limits_response(item)
48
- # require should be performed in target.txt
49
- klass = @parser.parameters[0].filename_to_class_name.to_class
50
- raise @parser.error("#{@parser.parameters[0].filename_to_class_name} class not found. Did you require the file in target.txt?", @usage) unless klass
48
+ klass = OpenC3.require_class(@parser.parameters[0])
51
49
 
52
50
  if @parser.parameters[1]
53
51
  item.limits.response = klass.new(*@parser.parameters[1..(@parser.parameters.length - 1)])
@@ -48,8 +48,7 @@ module OpenC3
48
48
  # @param packet [Packet] The packet the processor should be added to
49
49
  def create_processor(packet)
50
50
  # require should be performed in target.txt
51
- klass = @parser.parameters[1].filename_to_class_name.to_class
52
- raise @parser.error("#{@parser.parameters[1].filename_to_class_name} class not found. Did you require the file in target.txt?", @usage) unless klass
51
+ klass = OpenC3.require_class(@parser.parameters[1])
53
52
 
54
53
  if @parser.parameters[2]
55
54
  processor = klass.new(*@parser.parameters[2..(@parser.parameters.length - 1)])
@@ -178,14 +178,40 @@ module OpenC3
178
178
  when 'ParameterTypeSet', 'EnumerationList', 'ParameterSet', 'ContainerSet',
179
179
  'EntryList', 'DefaultCalibrator', 'DefaultAlarm', 'RestrictionCriteria',
180
180
  'ComparisonList', 'MetaCommandSet', 'ArgumentTypeSet', 'ArgumentList',
181
- 'ArgumentAssignmentList', 'LocationInContainerInBits'
181
+ 'ArgumentAssignmentList', 'LocationInContainerInBits', 'ReferenceTime'
182
182
  # Do Nothing
183
183
 
184
+ when 'ErrorDetectCorrect'
185
+ # TODO: Setup an algorithm to calculate the CRC
186
+ exponents = []
187
+ xtce_recurse_element(element) do |crc_element|
188
+ if crc_element.name == 'CRC'
189
+ xtce_recurse_element(crc_element) do |poly_element|
190
+ if poly_element['Polynomial']
191
+ xtce_recurse_element(poly_element) do |term_element|
192
+ if term_element['Term']
193
+ exponents << term_element['exponent'].to_i
194
+ end
195
+ true
196
+ end
197
+ end
198
+ true
199
+ end
200
+ end
201
+ true
202
+ end
203
+ @current_type.xtce_encoding = 'IntegerDataEncoding'
204
+ @current_type.signed = 'false'
205
+ return false # Already recursed
206
+
184
207
  when 'EnumeratedParameterType', 'EnumeratedArgumentType',
185
208
  'IntegerParameterType', 'IntegerArgumentType',
186
209
  'FloatParameterType', 'FloatArgumentType',
187
210
  'StringParameterType', 'StringArgumentType',
188
- 'BinaryParameterType', 'BinaryArgumentType'
211
+ 'BinaryParameterType', 'BinaryArgumentType',
212
+ 'BooleanParameterType', 'BooleanArgumentType',
213
+ 'AbsoluteTimeParameterType', 'AbsoluteTimeArgumentType',
214
+ 'RelativeTimeParameterType', 'RelativeTimeArgumentType'
189
215
  @current_type = create_new_type(element)
190
216
  @current_type.endianness = :BIG_ENDIAN
191
217
 
@@ -204,6 +230,20 @@ module OpenC3
204
230
  when 'BinaryParameterType', 'BinaryArgumentType'
205
231
  @current_type.xtce_encoding = 'BinaryDataEncoding'
206
232
  @current_type.sizeInBits = 8 # This is undocumented but appears to be the design
233
+ when 'BooleanParameterType', 'BooleanArgumentType'
234
+ @current_type.xtce_encoding = 'StringDataEncoding'
235
+ @current_type.sizeInBits = 8 # This is undocumented but appears to be the design
236
+ @current_type.states ||= {}
237
+ if element.attributes['zeroStringValue'] && element.attributes['oneStringValue']
238
+ @current_type.states[element.attributes['zeroStringValue'].value] = 0
239
+ @current_type.states[element.attributes['oneStringValue'].value] = 1
240
+ else
241
+ @current_type.states['FALSE'] = 0
242
+ @current_type.states['TRUE'] = 1
243
+ end
244
+ # No defaults for the time types
245
+ # when 'AbsoluteTimeParameterType', 'AbsoluteTimeArgumentType',
246
+ # when'RelativeTimeParameterType', 'RelativeTimeArgumentType'
207
247
  end
208
248
 
209
249
  when 'ArrayParameterType', 'ArrayArgumentType'
@@ -251,7 +291,7 @@ module OpenC3
251
291
 
252
292
  return false # Already recursed
253
293
 
254
- when "SizeInBits"
294
+ when 'SizeInBits'
255
295
  xtce_recurse_element(element) do |block_element|
256
296
  if block_element.name == 'FixedValue'
257
297
  @current_type.sizeInBits = Integer(block_element.text)
@@ -326,6 +366,26 @@ module OpenC3
326
366
  @current_type.states ||= {}
327
367
  @current_type.states[element['label']] = Integer(element['value'])
328
368
 
369
+ when 'Encoding'
370
+ if element.attributes['units']
371
+ @current_type.units_full = element.attributes['units'].value
372
+ # Could do a better job mapping units to abbreviations
373
+ @current_type.units = element.attributes['units'].value[0]
374
+ end
375
+ # TODO: Not sure if this is correct
376
+ # if @current_type.attributes['scale'] || @current_type.attributes['offset']
377
+ # @current_type.conversion ||= PolynomialConversion.new()
378
+ # if @current_type.attributes['offset']
379
+ # @current_type.conversion.coeffs[0] = @current_type.attributes['offset']
380
+ # end
381
+ # if @current_type.attributes['scale']
382
+ # @current_type.conversion.coeffs[1] = @current_type.attributes['scale']
383
+ # end
384
+ # end
385
+
386
+ when 'Epoch'
387
+ # TODO: How to handle this ... it affects the conversion applied
388
+
329
389
  when 'IntegerDataEncoding', 'FloatDataEncoding', 'StringDataEncoding', 'BinaryDataEncoding'
330
390
  @current_type.xtce_encoding = element.name
331
391
  element.attributes.each do |att_name, att|
@@ -340,6 +400,11 @@ module OpenC3
340
400
  @current_type.endianness = :LITTLE_ENDIAN
341
401
  end
342
402
 
403
+ # Ensure if the encoding is a string we convert state values to strings
404
+ if @current_type.xtce_encoding == 'StringDataEncoding' && @current_type.states
405
+ @current_type.states.transform_values!(&:to_s)
406
+ end
407
+
343
408
  when 'Parameter'
344
409
  @current_parameter = OpenStruct.new
345
410
  element.attributes.each do |att_name, att|
@@ -51,6 +51,9 @@ module OpenC3
51
51
  # required data size is allowed.
52
52
  attr_accessor :short_buffer_allowed
53
53
 
54
+ # @return [Accessor] Class used to access raw data of structure from buffer
55
+ attr_reader :accessor
56
+
54
57
  if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
55
58
  # Used to force encoding
56
59
  ASCII_8BIT_STRING = "ASCII-8BIT".freeze
@@ -86,6 +89,7 @@ module OpenC3
86
89
  @fixed_size = true
87
90
  @short_buffer_allowed = false
88
91
  @mutex = nil
92
+ @accessor = BinaryAccessor
89
93
  else
90
94
  raise(ArgumentError, "Unknown endianness '#{default_endianness}', must be :BIG_ENDIAN or :LITTLE_ENDIAN")
91
95
  end
@@ -103,11 +107,7 @@ module OpenC3
103
107
  return nil if item.data_type == :DERIVED
104
108
 
105
109
  buffer = allocate_buffer_if_needed() unless buffer
106
- if item.array_size
107
- return BinaryAccessor.read_array(item.bit_offset, item.bit_size, item.data_type, item.array_size, buffer, item.endianness)
108
- else
109
- return BinaryAccessor.read(item.bit_offset, item.bit_size, item.data_type, buffer, item.endianness)
110
- end
110
+ return @accessor.read_item(item, buffer)
111
111
  end
112
112
 
113
113
  # Get the length of the buffer used by the structure
@@ -133,6 +133,26 @@ module OpenC3
133
133
  end
134
134
  end
135
135
 
136
+ # Configure the accessor for this packet
137
+ #
138
+ # @param accessor [Accessor] The class to use as an accessor
139
+ def accessor=(accessor)
140
+ @accessor = accessor
141
+ @short_buffer_allowed = true if @accessor != BinaryAccessor
142
+ end
143
+
144
+ # Read a list of items in the structure
145
+ #
146
+ # @param items [StructureItem] Array of StructureItem or one of its subclasses
147
+ # @param value_type [Symbol] Not used. Subclasses should overload this
148
+ # parameter to check whether to perform conversions on the item.
149
+ # @param buffer [String] The binary buffer to read the item from
150
+ # @return Hash of read names and values
151
+ def read_items(items, value_type = :RAW, buffer = @buffer)
152
+ buffer = allocate_buffer_if_needed() unless buffer
153
+ return @accessor.read_items(items, buffer)
154
+ end
155
+
136
156
  # Allocate a buffer if not available
137
157
  def allocate_buffer_if_needed
138
158
  unless @buffer
@@ -180,11 +200,8 @@ module OpenC3
180
200
  # dependant on the item_class but by default see StructureItem.
181
201
  # @return [StrutureItem] The struture item defined
182
202
  def define_item(name, bit_offset, bit_size, data_type, array_size = nil, endianness = @default_endianness, overflow = :ERROR)
183
- # Handle case-insensitive naming
184
- name_upcase = name.upcase
185
-
186
203
  # Create the item
187
- item = @item_class.new(name_upcase, bit_offset, bit_size, data_type, endianness, array_size, overflow)
204
+ item = @item_class.new(name, bit_offset, bit_size, data_type, endianness, array_size, overflow)
188
205
  define(item)
189
206
  end
190
207
 
@@ -348,11 +365,19 @@ module OpenC3
348
365
  # @param buffer [String] The binary buffer to write the value to
349
366
  def write_item(item, value, value_type = :RAW, buffer = @buffer)
350
367
  buffer = allocate_buffer_if_needed() unless buffer
351
- if item.array_size
352
- BinaryAccessor.write_array(value, item.bit_offset, item.bit_size, item.data_type, item.array_size, buffer, item.endianness, item.overflow)
353
- else
354
- BinaryAccessor.write(value, item.bit_offset, item.bit_size, item.data_type, buffer, item.endianness, item.overflow)
355
- end
368
+ return @accessor.write_item(item, value, buffer)
369
+ end
370
+
371
+ # Write values to the buffer based on the item definitions
372
+ #
373
+ # @param items [StructureItem] Array of StructureItem or one of its subclasses
374
+ # @param value [Object] Array of values based on the item definitions.
375
+ # @param value_type [Symbol] Not used. Subclasses should overload this
376
+ # parameter to check whether to perform conversions on the item.
377
+ # @param buffer [String] The binary buffer to write the values to
378
+ def write_items(items, values, value_type = :RAW, buffer = @buffer)
379
+ buffer = allocate_buffer_if_needed() unless buffer
380
+ return @accessor.write_items(items, values, buffer)
356
381
  end
357
382
 
358
383
  # Read an item in the structure by name
@@ -34,6 +34,9 @@ module OpenC3
34
34
  # @return [String] Name of the item
35
35
  attr_reader :name
36
36
 
37
+ # Key is used to access into nested structures during decom if applicable
38
+ attr_reader :key
39
+
37
40
  # Indicates where in the binary buffer the StructureItem exists.
38
41
  # @return [Integer] 0 based bit offset
39
42
  attr_reader :bit_offset
@@ -89,6 +92,7 @@ module OpenC3
89
92
  @structure_item_constructed = false
90
93
  # Assignment order matters due to verifications!
91
94
  self.name = name
95
+ self.key = name # Key defaults to name as given (not upcased)
92
96
  self.endianness = endianness
93
97
  self.data_type = data_type
94
98
  self.bit_offset = bit_offset
@@ -110,6 +114,13 @@ module OpenC3
110
114
  verify_overall() if @structure_item_constructed
111
115
  end
112
116
 
117
+ def key=(key)
118
+ raise ArgumentError, "key must be a String but is a #{name.class}" unless String === key
119
+ raise ArgumentError, "key must contain at least one character" if key.empty?
120
+
121
+ @key = key
122
+ end
123
+
113
124
  def endianness=(endianness)
114
125
  raise ArgumentError, "#{@name}: endianness must be a Symbol" unless Symbol === endianness
115
126
  unless BinaryAccessor::ENDIANNESS.include? endianness
@@ -297,13 +308,16 @@ module OpenC3
297
308
  endianness = hash['endianness'] ? hash['endianness'].intern : nil
298
309
  data_type = hash['data_type'] ? hash['data_type'].intern : nil
299
310
  overflow = hash['overflow'] ? hash['overflow'].intern : nil
300
- StructureItem.new(hash['name'], hash['bit_offset'], hash['bit_size'], data_type,
311
+ si = StructureItem.new(hash['name'], hash['bit_offset'], hash['bit_size'], data_type,
301
312
  endianness, hash['array_size'], overflow)
313
+ si.key = hash['key'] || hash['name']
314
+ si
302
315
  end
303
316
 
304
317
  def as_json(*a)
305
318
  hash = {}
306
319
  hash['name'] = self.name
320
+ hash['key'] = self.key
307
321
  hash['bit_offset'] = self.bit_offset
308
322
  hash['bit_size'] = self.bit_size
309
323
  hash['data_type'] = self.data_type
@@ -17,6 +17,7 @@
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
 
20
+ require 'tempfile'
20
21
  require 'net/http'
21
22
 
22
23
  module OpenC3
@@ -34,7 +35,7 @@ module OpenC3
34
35
  OpenC3::Logger.info "Deleting #{delete_path}"
35
36
  response = $api_server.request('delete', endpoint, query: {bucket: 'config'}, scope: scope)
36
37
  if response.nil? || response.code != 200
37
- raise "Failed to delete #{delete_path}. Note: #{scope}/targets is read-only."
38
+ raise "Failed to delete #{delete_path}"
38
39
  end
39
40
  rescue => error
40
41
  raise "Failed deleting #{path} due to #{error.message}"
@@ -48,7 +49,13 @@ module OpenC3
48
49
  # @param io_or_string [Io or String] IO object
49
50
  def put_target_file(path, io_or_string, scope: $openc3_scope)
50
51
  raise "Disallowed path modifier '..' found in #{path}" if path.include?('..')
52
+
51
53
  upload_path = "#{scope}/targets_modified/#{path}"
54
+
55
+ if ENV['OPENC3_LOCAL_MODE'] and $openc3_in_cluster
56
+ OpenC3::LocalMode.put_target_file(upload_path, io_or_string, scope: scope)
57
+ end
58
+
52
59
  endpoint = "/openc3-api/storage/upload/#{upload_path}"
53
60
  OpenC3::Logger.info "Writing #{upload_path}"
54
61
  result = _get_presigned_request(endpoint, scope: scope)
@@ -86,6 +93,17 @@ module OpenC3
86
93
  # Loop to allow redo when switching from modified to original
87
94
  loop do
88
95
  begin
96
+ if part == "targets_modified" and ENV['OPENC3_LOCAL_MODE']
97
+ local_file = OpenC3::LocalMode.open_local_file(path, scope: scope)
98
+ if local_file
99
+ file = Tempfile.new('target', binmode: true)
100
+ file.write(local_file.read)
101
+ local_file.close
102
+ file.rewind
103
+ return file if local_file
104
+ end
105
+ end
106
+
89
107
  return _get_storage_file("#{part}/#{path}", scope: scope)
90
108
  rescue => error
91
109
  if part == "targets_modified"