openc3 5.18.0 → 5.19.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 (98) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +7 -4
  3. data/bin/cstol_converter +14 -14
  4. data/bin/openc3cli +189 -7
  5. data/data/config/_interfaces.yaml +1 -1
  6. data/data/config/command_modifiers.yaml +55 -0
  7. data/data/config/interface_modifiers.yaml +1 -1
  8. data/data/config/param_item_modifiers.yaml +1 -1
  9. data/data/config/parameter_modifiers.yaml +1 -1
  10. data/data/config/plugins.yaml +6 -2
  11. data/data/config/screen.yaml +2 -2
  12. data/data/config/table_manager.yaml +2 -2
  13. data/data/config/tool.yaml +4 -1
  14. data/data/config/widgets.yaml +3 -3
  15. data/ext/openc3/ext/config_parser/config_parser.c +1 -1
  16. data/ext/openc3/ext/packet/packet.c +1 -1
  17. data/ext/openc3/ext/platform/platform.c +3 -3
  18. data/ext/openc3/ext/structure/structure.c +56 -76
  19. data/lib/openc3/accessors/binary_accessor.rb +4 -4
  20. data/lib/openc3/accessors/form_accessor.rb +2 -2
  21. data/lib/openc3/accessors/http_accessor.rb +1 -1
  22. data/lib/openc3/accessors/json_accessor.rb +6 -4
  23. data/lib/openc3/accessors/template_accessor.rb +6 -9
  24. data/lib/openc3/accessors/xml_accessor.rb +1 -1
  25. data/lib/openc3/api/cmd_api.rb +35 -11
  26. data/lib/openc3/api/limits_api.rb +1 -1
  27. data/lib/openc3/config/config_parser.rb +1 -1
  28. data/lib/openc3/conversions/segmented_polynomial_conversion.rb +7 -7
  29. data/lib/openc3/core_ext/array.rb +5 -5
  30. data/lib/openc3/core_ext/exception.rb +9 -2
  31. data/lib/openc3/core_ext/string.rb +2 -2
  32. data/lib/openc3/interfaces/http_server_interface.rb +1 -0
  33. data/lib/openc3/interfaces/interface.rb +1 -1
  34. data/lib/openc3/interfaces/linc_interface.rb +3 -3
  35. data/lib/openc3/io/json_api.rb +11 -6
  36. data/lib/openc3/io/json_rpc.rb +1 -1
  37. data/lib/openc3/logs/buffered_packet_log_writer.rb +3 -3
  38. data/lib/openc3/logs/log_writer.rb +7 -8
  39. data/lib/openc3/logs/packet_log_writer.rb +7 -7
  40. data/lib/openc3/logs/text_log_writer.rb +4 -4
  41. data/lib/openc3/microservices/decom_microservice.rb +19 -4
  42. data/lib/openc3/microservices/interface_microservice.rb +41 -3
  43. data/lib/openc3/microservices/reaction_microservice.rb +2 -2
  44. data/lib/openc3/microservices/trigger_group_microservice.rb +3 -3
  45. data/lib/openc3/migrations/20240915000000_activity_uuid.rb +28 -0
  46. data/lib/openc3/models/activity_model.rb +109 -80
  47. data/lib/openc3/models/auth_model.rb +31 -2
  48. data/lib/openc3/models/cvt_model.rb +11 -5
  49. data/lib/openc3/models/gem_model.rb +8 -8
  50. data/lib/openc3/models/plugin_model.rb +3 -3
  51. data/lib/openc3/models/reducer_model.rb +2 -2
  52. data/lib/openc3/models/scope_model.rb +1 -1
  53. data/lib/openc3/models/sorted_model.rb +4 -4
  54. data/lib/openc3/models/target_model.rb +3 -3
  55. data/lib/openc3/models/tool_config_model.rb +1 -1
  56. data/lib/openc3/models/tool_model.rb +4 -4
  57. data/lib/openc3/models/widget_model.rb +11 -5
  58. data/lib/openc3/operators/operator.rb +5 -3
  59. data/lib/openc3/packets/command_validator.rb +48 -0
  60. data/lib/openc3/packets/commands.rb +6 -14
  61. data/lib/openc3/packets/packet.rb +31 -15
  62. data/lib/openc3/packets/packet_config.rb +10 -9
  63. data/lib/openc3/packets/parsers/packet_parser.rb +3 -3
  64. data/lib/openc3/packets/structure.rb +21 -13
  65. data/lib/openc3/packets/structure_item.rb +33 -47
  66. data/lib/openc3/packets/telemetry.rb +6 -27
  67. data/lib/openc3/script/api_shared.rb +7 -5
  68. data/lib/openc3/script/calendar.rb +2 -2
  69. data/lib/openc3/script/commands.rb +6 -4
  70. data/lib/openc3/script/metadata.rb +2 -2
  71. data/lib/openc3/script/suite.rb +17 -17
  72. data/lib/openc3/streams/serial_stream.rb +2 -3
  73. data/lib/openc3/streams/stream.rb +2 -2
  74. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +10 -10
  75. data/lib/openc3/tools/table_manager/table_manager_core.rb +11 -11
  76. data/lib/openc3/tools/table_manager/table_parser.rb +2 -3
  77. data/lib/openc3/topics/command_decom_topic.rb +2 -1
  78. data/lib/openc3/topics/command_topic.rb +3 -3
  79. data/lib/openc3/topics/decom_interface_topic.rb +2 -2
  80. data/lib/openc3/topics/telemetry_decom_topic.rb +1 -1
  81. data/lib/openc3/utilities/authorization.rb +2 -1
  82. data/lib/openc3/utilities/cli_generator.rb +15 -8
  83. data/lib/openc3/utilities/cosmos_rails_formatter.rb +60 -0
  84. data/lib/openc3/utilities/crc.rb +6 -6
  85. data/lib/openc3/utilities/local_mode.rb +2 -1
  86. data/lib/openc3/utilities/logger.rb +44 -34
  87. data/lib/openc3/utilities/metric.rb +1 -2
  88. data/lib/openc3/utilities/quaternion.rb +18 -18
  89. data/lib/openc3/utilities/target_file.rb +4 -4
  90. data/lib/openc3/version.rb +5 -5
  91. data/lib/openc3/win32/win32_main.rb +2 -2
  92. data/templates/tool_angular/package.json +21 -21
  93. data/templates/tool_react/package.json +10 -10
  94. data/templates/tool_svelte/package.json +11 -11
  95. data/templates/tool_svelte/src/services/openc3-api.js +17 -17
  96. data/templates/tool_vue/package.json +9 -9
  97. data/templates/widget/package.json +6 -7
  98. metadata +5 -2
@@ -73,9 +73,11 @@ module OpenC3
73
73
  def self.handle_config(parser, keyword, parameters, plugin: nil, needs_dependencies: false, scope:)
74
74
  case keyword
75
75
  when 'WIDGET'
76
- parser.verify_num_parameters(1, 2, "WIDGET <Name> <Label>")
76
+ parser.verify_num_parameters(1, 3, "WIDGET <Name> <Label> <Select Items (true/false)>")
77
77
  # Label is optional and if it doesn't exist nil is fine
78
- return self.new(name: parameters[0], plugin: plugin, label: parameters[1], needs_dependencies: needs_dependencies, scope: scope)
78
+ # Select Items is optional and if it doesn't exist nil is fine
79
+ items = ConfigParser.handle_true_false_nil(parameters[2])
80
+ return self.new(name: parameters[0], plugin: plugin, label: parameters[1], items: items, needs_dependencies: needs_dependencies, scope: scope)
79
81
  else
80
82
  raise ConfigParser::Error.new(parser, "Unknown keyword and parameters for Widget: #{keyword} #{parameters.join(" ")}")
81
83
  end
@@ -87,6 +89,7 @@ module OpenC3
87
89
  updated_at: nil,
88
90
  plugin: nil,
89
91
  label: nil,
92
+ items: false,
90
93
  needs_dependencies: false,
91
94
  disable_erb: nil,
92
95
  scope:
@@ -96,6 +99,8 @@ module OpenC3
96
99
  @filename = @full_name + '.umd.min.js'
97
100
  @bucket_key = 'widgets/' + @full_name + '/' + @filename
98
101
  @label = label
102
+ # Ensure items is a boolean because it could be nil
103
+ @items = items ? true : false
99
104
  @needs_dependencies = needs_dependencies
100
105
  @disable_erb = disable_erb
101
106
  end
@@ -106,8 +111,9 @@ module OpenC3
106
111
  'updated_at' => @updated_at,
107
112
  'plugin' => @plugin,
108
113
  'label' => @label,
114
+ 'items' => @items,
109
115
  'needs_dependencies' => @needs_dependencies,
110
- 'disable_erb' => @disable_erb
116
+ 'disable_erb' => @disable_erb,
111
117
  }
112
118
  end
113
119
 
@@ -155,8 +161,8 @@ module OpenC3
155
161
  bucket = Bucket.getClient()
156
162
  bucket.delete_object(bucket: ENV['OPENC3_TOOLS_BUCKET'], key: @bucket_key)
157
163
  bucket.delete_object(bucket: ENV['OPENC3_TOOLS_BUCKET'], key: @bucket_key + '.map')
158
- rescue Exception => error
159
- Logger.error("Error undeploying widget model #{@name} in scope #{@scope} due to #{error}")
164
+ rescue Exception => e
165
+ Logger.error("Error undeploying widget model #{@name} in scope #{@scope} due to #{e}")
160
166
  end
161
167
  end
162
168
  end
@@ -105,7 +105,7 @@ module OpenC3
105
105
  def cmd_line
106
106
  # In ProcessManager processes, the process_definition is the actual thing run
107
107
  # e.g. OpenC3::ProcessManager.instance.spawn(["ruby", "/openc3/bin/openc3cli", "load", ...])
108
- # However, if the MicroserviceOperator is spawning the proceses it sets
108
+ # However, if the MicroserviceOperator is spawning the processes it sets
109
109
  # process_definition = ["ruby", "plugin_microservice.rb"]
110
110
  # which then calls exec(*@config["cmd"]) to actually run
111
111
  # So check if the @config['cmd'] is defined to give the user more info in the log
@@ -195,12 +195,14 @@ module OpenC3
195
195
  if @process
196
196
  stdout = @process.io.stdout.extract
197
197
  if stdout.length > 0
198
- STDOUT.puts "STDOUT #{stdout.length} bytes from #{cmd_line()}:"
198
+ message = "STDOUT #{stdout.length} bytes from #{cmd_line()}:"
199
+ STDOUT.puts Logger.build_log_data(Logger::INFO_LEVEL, message, user: nil, type: OpenC3::Logger::LOG, url: nil).as_json(:allow_nan => true).to_json(:allow_nan => true)
199
200
  STDOUT.puts stdout
200
201
  end
201
202
  stderr = @process.io.stderr.extract
202
203
  if stderr.length > 0
203
- STDERR.puts "STDERR #{stderr.length} bytes from #{cmd_line()}:"
204
+ message = "STDERR #{stderr.length} bytes from #{cmd_line()}:"
205
+ STDERR.puts Logger.build_log_data(Logger::ERROR_LEVEL, message, user: nil, type: OpenC3::Logger::LOG, url: nil).as_json(:allow_nan => true).to_json(:allow_nan => true)
204
206
  STDERR.puts stderr
205
207
  end
206
208
  end
@@ -0,0 +1,48 @@
1
+ # encoding: ascii-8bit
2
+
3
+ # Copyright 2024 OpenC3, Inc.
4
+ # All Rights Reserved.
5
+ #
6
+ # This program is free software; you can modify and/or redistribute it
7
+ # under the terms of the GNU Affero General Public License
8
+ # as published by the Free Software Foundation; version 3 with
9
+ # attribution addendums as found in the LICENSE.txt
10
+ #
11
+ # This program is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
+ # GNU Affero General Public License for more details.
15
+
16
+ # This file may also be used under the terms of a commercial license
17
+ # if purchased from OpenC3, Inc.
18
+
19
+ # This file implements a class to handle command validation
20
+
21
+ require 'openc3/api/api'
22
+
23
+ module OpenC3
24
+ # This class defines methods which are called when a command is sent.
25
+ # This class must be subclassed and the pre_check or
26
+ # post_check methods implemented. Do NOT use this class directly.
27
+ class CommandValidator
28
+ attr_reader :args
29
+ include Api
30
+
31
+ def initialize(command = nil)
32
+ @command = command
33
+ @args = []
34
+ end
35
+
36
+ def pre_check(command)
37
+ # Return true to indicate Success, false to indicate Failure,
38
+ # and nil to indicate Unknown. The second value is the optional message.
39
+ return [true, nil]
40
+ end
41
+
42
+ def post_check(command)
43
+ # Return true to indicate Success, false to indicate Failure,
44
+ # and nil to indicate Unknown. The second value is the optional message.
45
+ return [true, nil]
46
+ end
47
+ end
48
+ end
@@ -30,7 +30,7 @@ module OpenC3
30
30
  #
31
31
  # This should not be confused with the Api module which implements the JSON
32
32
  # API that is used by tools when accessing the Server. The Api module always
33
- # provides Ruby primatives where the PacketConfig class can return actual
33
+ # provides Ruby primitives where the PacketConfig class can return actual
34
34
  # Packet or PacketItem objects. While there are some overlapping methods between
35
35
  # the two, these are separate interfaces into the system.
36
36
  class Commands
@@ -115,7 +115,7 @@ module OpenC3
115
115
  target = System.targets[target_name]
116
116
  if target and target.cmd_unique_id_mode
117
117
  # Iterate through the packets and see if any represent the buffer
118
- target_packets.each do |packet_name, packet|
118
+ target_packets.each do |_packet_name, packet|
119
119
  if packet.identify?(packet_data)
120
120
  identified_packet = packet
121
121
  break
@@ -147,7 +147,7 @@ module OpenC3
147
147
  end
148
148
 
149
149
  # Returns a copy of the specified command packet with the parameters
150
- # initialzed to the given params values.
150
+ # initialized to the given params values.
151
151
  #
152
152
  # @param target_name (see #packet)
153
153
  # @param packet_name (see #packet)
@@ -195,7 +195,7 @@ module OpenC3
195
195
  items = packet.read_all(:FORMATTED)
196
196
  raw = false
197
197
  end
198
- items.delete_if { |item_name, item_value| ignored_parameters.include?(item_name) }
198
+ items.delete_if { |item_name, _item_value| ignored_parameters.include?(item_name) }
199
199
  return build_cmd_output_string(packet.target_name, packet.packet_name, items, raw)
200
200
  end
201
201
 
@@ -207,7 +207,7 @@ module OpenC3
207
207
  end
208
208
  target_name = 'UNKNOWN' unless target_name
209
209
  cmd_name = 'UNKNOWN' unless cmd_name
210
- output_string << target_name + ' ' + cmd_name
210
+ output_string << (target_name + ' ' + cmd_name)
211
211
  if cmd_params.nil? or cmd_params.empty?
212
212
  output_string << '")'
213
213
  else
@@ -247,7 +247,7 @@ module OpenC3
247
247
  params << "#{key} #{value}"
248
248
  end
249
249
  params = params.join(", ")
250
- output_string << ' with ' + params + '")'
250
+ output_string << (' with ' + params + '")')
251
251
  end
252
252
  return output_string
253
253
  end
@@ -291,14 +291,6 @@ module OpenC3
291
291
  cmd_pkt_hazardous?(build_cmd(target_name, packet_name, params, false, false, false))
292
292
  end
293
293
 
294
- def clear_counters
295
- @config.commands.each do |target_name, target_packets|
296
- target_packets.each do |packet_name, packet|
297
- packet.received_count = 0
298
- end
299
- end
300
- end
301
-
302
294
  def all
303
295
  @config.commands
304
296
  end
@@ -104,6 +104,9 @@ module OpenC3
104
104
  # @return [Boolean] Whether to ignore overlapping items
105
105
  attr_accessor :ignore_overlap
106
106
 
107
+ # @return [Validator] Instance of class used to validate commands
108
+ attr_accessor :validator
109
+
107
110
  # @return [Boolean] If this packet should be used for identification
108
111
  attr_reader :virtual
109
112
 
@@ -111,7 +114,7 @@ module OpenC3
111
114
  VALUE_TYPES = [:RAW, :CONVERTED, :FORMATTED, :WITH_UNITS]
112
115
 
113
116
  if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
114
- # Creates a new packet by initalizing the attributes.
117
+ # Creates a new packet by initializing the attributes.
115
118
  #
116
119
  # @param target_name [String] Name of the target this packet is associated with
117
120
  # @param packet_name [String] Name of the packet
@@ -122,7 +125,7 @@ module OpenC3
122
125
  # subclass of PacketItem)
123
126
  def initialize(target_name = nil, packet_name = nil, default_endianness = :BIG_ENDIAN, description = nil, buffer = nil, item_class = PacketItem)
124
127
  super(default_endianness, buffer, item_class)
125
- # Explictly call the defined setter methods
128
+ # Explicitly call the defined setter methods
126
129
  self.target_name = target_name
127
130
  self.packet_name = packet_name
128
131
  self.description = description
@@ -148,6 +151,7 @@ module OpenC3
148
151
  @packet_time = nil
149
152
  @ignore_overlap = false
150
153
  @virtual = false
154
+ @validator = nil
151
155
  end
152
156
 
153
157
  # Sets the target name this packet is associated with. Unidentified packets
@@ -325,7 +329,7 @@ module OpenC3
325
329
  synchronize() do
326
330
  begin
327
331
  internal_buffer_equals(buffer)
328
- rescue RuntimeError
332
+ rescue RuntimeError => e
329
333
  Logger.instance.error "#{@target_name} #{@packet_name} received with actual packet length of #{buffer.length} but defined length of #{@defined_length}"
330
334
  end
331
335
  @read_conversion_cache.clear if @read_conversion_cache
@@ -511,20 +515,20 @@ module OpenC3
511
515
 
512
516
  # Define an item in the packet. This creates a new instance of the
513
517
  # item_class as given in the constructor and adds it to the items hash. It
514
- # also resizes the buffer to accomodate the new item.
518
+ # also resizes the buffer to accommodate the new item.
515
519
  #
516
520
  # @param name [String] Name of the item. Used by the items hash to retrieve
517
521
  # the item.
518
522
  # @param bit_offset [Integer] Bit offset of the item in the raw buffer
519
523
  # @param bit_size [Integer] Bit size of the item in the raw buffer
520
524
  # @param data_type [Symbol] Type of data contained by the item. This is
521
- # dependant on the item_class but by default see StructureItem.
525
+ # dependent on the item_class but by default see StructureItem.
522
526
  # @param array_size [Integer] Set to a non nil value if the item is to
523
527
  # represented as an array.
524
528
  # @param endianness [Symbol] Endianness of this item. By default the
525
- # endianness as set in the constructure is used.
529
+ # endianness as set in the constructor is used.
526
530
  # @param overflow [Symbol] How to handle value overflows. This is
527
- # dependant on the item_class but by default see StructureItem.
531
+ # dependent on the item_class but by default see StructureItem.
528
532
  # @param format_string [String] String to pass to Kernel#sprintf
529
533
  # @param read_conversion [Conversion] Conversion to apply when reading the
530
534
  # item from the packet buffer
@@ -540,7 +544,7 @@ module OpenC3
540
544
  end
541
545
 
542
546
  # Add an item to the packet by adding it to the items hash. It also
543
- # resizes the buffer to accomodate the new item.
547
+ # resizes the buffer to accommodate the new item.
544
548
  #
545
549
  # @param item [PacketItem] Item to add to the packet
546
550
  # @return [PacketItem] The same packet item
@@ -553,7 +557,7 @@ module OpenC3
553
557
 
554
558
  # Define an item at the end of the packet. This creates a new instance of the
555
559
  # item_class as given in the constructor and adds it to the items hash. It
556
- # also resizes the buffer to accomodate the new item.
560
+ # also resizes the buffer to accommodate the new item.
557
561
  #
558
562
  # @param name (see #define_item)
559
563
  # @param bit_size (see #define_item)
@@ -1064,9 +1068,12 @@ module OpenC3
1064
1068
  if @accessor.class.to_s != 'OpenC3::BinaryAccessor'
1065
1069
  config << " ACCESSOR #{@accessor.class} #{@accessor.args.map { |a| a.to_s.quote_if_necessary }.join(" ")}\n"
1066
1070
  end
1071
+ if @validator
1072
+ config << " VALIDATOR #{@validator.class} #{@validator.args.map { |a| a.to_s.quote_if_necessary }.join(" ")}\n"
1073
+ end
1067
1074
  # TODO: Add TEMPLATE_ENCODED so this can always be done inline regardless of content
1068
1075
  if @template
1069
- config << " TEMPLATE '#{@template}'"
1076
+ config << " TEMPLATE '#{@template}'\n"
1070
1077
  end
1071
1078
  config << " ALLOW_SHORT\n" if @short_buffer_allowed
1072
1079
  config << " HAZARDOUS #{@hazardous_description.to_s.quote_if_necessary}\n" if @hazardous
@@ -1106,21 +1113,21 @@ module OpenC3
1106
1113
  end
1107
1114
 
1108
1115
  if @response
1109
- config << " RESPONSE #{@response[0].to_s.quote_if_necessary} #{@response[1].to_s.quote_if_necessary}"
1116
+ config << " RESPONSE #{@response[0].to_s.quote_if_necessary} #{@response[1].to_s.quote_if_necessary}\n"
1110
1117
  end
1111
1118
  if @error_response
1112
- config << " ERROR_RESPONSE #{@error_response[0].to_s.quote_if_necessary} #{@error_response[1].to_s.quote_if_necessary}"
1119
+ config << " ERROR_RESPONSE #{@error_response[0].to_s.quote_if_necessary} #{@error_response[1].to_s.quote_if_necessary}\n"
1113
1120
  end
1114
1121
  if @screen
1115
- config << " SCREEN #{@screen[0].to_s.quote_if_necessary} #{@screen[1].to_s.quote_if_necessary}"
1122
+ config << " SCREEN #{@screen[0].to_s.quote_if_necessary} #{@screen[1].to_s.quote_if_necessary}\n"
1116
1123
  end
1117
1124
  if @related_items
1118
1125
  @related_items.each do |target_name, packet_name, item_name|
1119
- config << " RELATED_ITEM #{target_name.to_s.quote_if_necessary} #{packet_name.to_s.quote_if_necessary} #{item_name.to_s.quote_if_necessary}"
1126
+ config << " RELATED_ITEM #{target_name.to_s.quote_if_necessary} #{packet_name.to_s.quote_if_necessary} #{item_name.to_s.quote_if_necessary}\n"
1120
1127
  end
1121
1128
  end
1122
1129
  if @ignore_overlap
1123
- config << " IGNORE_OVERLAP"
1130
+ config << " IGNORE_OVERLAP\n"
1124
1131
  end
1125
1132
  config
1126
1133
  end
@@ -1140,6 +1147,7 @@ module OpenC3
1140
1147
  config['virtual'] = true if @virtual
1141
1148
  config['accessor'] = @accessor.class.to_s
1142
1149
  config['accessor_args'] = @accessor.args
1150
+ config['validator'] = @validator.class.to_s if @validator
1143
1151
  config['template'] = Base64.encode64(@template) if @template
1144
1152
  config['config_name'] = self.config_name
1145
1153
 
@@ -1205,6 +1213,14 @@ module OpenC3
1205
1213
  Logger.instance.error "#{packet.target_name} #{packet.packet_name} accessor of #{hash['accessor']} could not be found due to #{e}"
1206
1214
  end
1207
1215
  end
1216
+ if hash['validator']
1217
+ begin
1218
+ validator = OpenC3::const_get(hash['validator'])
1219
+ packet.validator = validator.new(packet)
1220
+ rescue => e
1221
+ Logger.instance.error "#{packet.target_name} #{packet.packet_name} validator of #{hash['validator']} could not be found due to #{e}"
1222
+ end
1223
+ end
1208
1224
  packet.template = Base64.decode64(hash['template']) if hash['template']
1209
1225
  packet.meta = hash['meta']
1210
1226
  # Can't convert processors
@@ -220,7 +220,7 @@ module OpenC3
220
220
  'APPEND_PARAMETER', 'APPEND_ID_ITEM', 'APPEND_ID_PARAMETER', 'APPEND_ARRAY_ITEM',\
221
221
  'APPEND_ARRAY_PARAMETER', 'ALLOW_SHORT', 'HAZARDOUS', 'PROCESSOR', 'META',\
222
222
  'DISABLE_MESSAGES', 'HIDDEN', 'DISABLED', 'VIRTUAL', 'ACCESSOR', 'TEMPLATE', 'TEMPLATE_FILE',\
223
- 'RESPONSE', 'ERROR_RESPONSE', 'SCREEN', 'RELATED_ITEM', 'IGNORE_OVERLAP'
223
+ 'RESPONSE', 'ERROR_RESPONSE', 'SCREEN', 'RELATED_ITEM', 'IGNORE_OVERLAP', 'VALIDATOR'
224
224
  raise parser.error("No current packet for #{keyword}") unless @current_packet
225
225
 
226
226
  process_current_packet(parser, keyword, params)
@@ -409,7 +409,7 @@ module OpenC3
409
409
  else # DELETE
410
410
  @current_packet.delete_item(params[0])
411
411
  end
412
- rescue # Rescue the default execption to provide a nicer error message
412
+ rescue # Rescue the default exception to provide a nicer error message
413
413
  raise parser.error("#{params[0]} not found in #{@current_cmd_or_tlm.downcase} packet #{@current_packet.target_name} #{@current_packet.packet_name}", usage)
414
414
  end
415
415
 
@@ -480,22 +480,23 @@ module OpenC3
480
480
  @current_packet.disabled = true
481
481
  @current_packet.virtual = true
482
482
 
483
- when 'ACCESSOR'
484
- usage = "#{keyword} <Accessor class name>"
483
+ when 'ACCESSOR', 'VALIDATOR'
484
+ usage = "#{keyword} <Class name> <Optional parameters> ..."
485
485
  parser.verify_num_parameters(1, nil, usage)
486
486
  begin
487
+ keyword_equals = "#{keyword.downcase}=".to_sym
487
488
  if @language == 'ruby'
488
489
  klass = OpenC3.require_class(params[0])
489
490
  if params.length > 1
490
- @current_packet.accessor = klass.new(@current_packet, *params[1..-1])
491
+ @current_packet.public_send(keyword_equals, klass.new(@current_packet, *params[1..-1]))
491
492
  else
492
- @current_packet.accessor = klass.new(@current_packet)
493
+ @current_packet.public_send(keyword_equals, klass.new(@current_packet))
493
494
  end
494
495
  else
495
496
  if params.length > 1
496
- @current_packet.accessor = PythonProxy.new('Accessor', params[0], @current_packet, *params[1..-1])
497
+ @current_packet.public_send(keyword_equals, PythonProxy.new(keyword.capitalize, params[0], @current_packet, *params[1..-1]))
497
498
  else
498
- @current_packet.accessor = PythonProxy.new('Accessor', params[0], @current_packet)
499
+ @current_packet.public_send(keyword_equals, PythonProxy.new(keyword.capitalize, params[0], @current_packet))
499
500
  end
500
501
  end
501
502
  rescue Exception => e
@@ -675,7 +676,7 @@ module OpenC3
675
676
  raise parser.error("#{keyword} only applies to command parameters")
676
677
  end
677
678
 
678
- # Update the mimimum value for the current command parameter
679
+ # Update the minimum value for the current command parameter
679
680
  when 'MINIMUM_VALUE'
680
681
  if @current_cmd_or_tlm == TELEMETRY
681
682
  raise parser.error("#{keyword} only applies to command parameters")
@@ -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
  require 'openc3/packets/packet'
@@ -30,7 +30,7 @@ module OpenC3
30
30
  # used instead of this parameter.
31
31
  # @param commands [Hash] Hash of the currently defined commands
32
32
  # @param warnings [Array<String>] Any warning strings generated while
33
- # parsing this command will be appened to this array
33
+ # parsing this command will be appended to this array
34
34
  def self.parse_command(parser, target_name, commands, warnings)
35
35
  parser = PacketParser.new(parser)
36
36
  parser.verify_parameters()
@@ -48,7 +48,7 @@ module OpenC3
48
48
  # used to perform lookups when the packet and item are known but the
49
49
  # packet is not.
50
50
  # @param warnings [Array<String>] Any warning strings generated while
51
- # parsing this command will be appened to this array
51
+ # parsing this command will be appended to this array
52
52
  def self.parse_telemetry(parser, target_name, telemetry, latest_data, warnings)
53
53
  parser = PacketParser.new(parser)
54
54
  parser.verify_parameters()
@@ -188,21 +188,21 @@ module OpenC3
188
188
 
189
189
  # Define an item in the structure. This creates a new instance of the
190
190
  # item_class as given in the constructor and adds it to the items hash. It
191
- # also resizes the buffer to accomodate the new item.
191
+ # also resizes the buffer to accommodate the new item.
192
192
  #
193
193
  # @param name [String] Name of the item. Used by the items hash to retrieve
194
194
  # the item.
195
195
  # @param bit_offset [Integer] Bit offset of the item in the raw buffer
196
196
  # @param bit_size [Integer] Bit size of the item in the raw buffer
197
197
  # @param data_type [Symbol] Type of data contained by the item. This is
198
- # dependant on the item_class but by default see StructureItem.
198
+ # dependent on the item_class but by default see StructureItem.
199
199
  # @param array_size [Integer] Set to a non nil value if the item is to
200
200
  # represented as an array.
201
201
  # @param endianness [Symbol] Endianness of this item. By default the
202
- # endianness as set in the constructure is used.
202
+ # endianness as set in the constructor is used.
203
203
  # @param overflow [Symbol] How to handle value overflows. This is
204
- # dependant on the item_class but by default see StructureItem.
205
- # @return [StrutureItem] The struture item defined
204
+ # dependent on the item_class but by default see StructureItem.
205
+ # @return [StrutureItem] The structure item defined
206
206
  def define_item(name, bit_offset, bit_size, data_type, array_size = nil, endianness = @default_endianness, overflow = :ERROR)
207
207
  # Create the item
208
208
  item = @item_class.new(name, bit_offset, bit_size, data_type, endianness, array_size, overflow)
@@ -210,10 +210,10 @@ module OpenC3
210
210
  end
211
211
 
212
212
  # Adds the given item to the items hash. It also resizes the buffer to
213
- # accomodate the new item.
213
+ # accommodate the new item.
214
214
  #
215
215
  # @param item [StructureItem] The structure item to add
216
- # @return [StrutureItem] The struture item defined
216
+ # @return [StrutureItem] The structure item defined
217
217
  def define(item)
218
218
  # Handle Overwriting Existing Item
219
219
  if @items[item.name]
@@ -289,7 +289,7 @@ module OpenC3
289
289
 
290
290
  # Define an item at the end of the structure. This creates a new instance of the
291
291
  # item_class as given in the constructor and adds it to the items hash. It
292
- # also resizes the buffer to accomodate the new item.
292
+ # also resizes the buffer to accommodate the new item.
293
293
  #
294
294
  # @param name (see #define_item)
295
295
  # @param bit_size (see #define_item)
@@ -307,7 +307,7 @@ module OpenC3
307
307
  end
308
308
 
309
309
  # Adds an item at the end of the structure. It adds the item to the items
310
- # hash and resizes the buffer to accomodate the new item.
310
+ # hash and resizes the buffer to accommodate the new item.
311
311
  #
312
312
  # @param item (see #define)
313
313
  # @return (see #define)
@@ -493,7 +493,7 @@ module OpenC3
493
493
  # further modifications to the buffer have no effect on the structure
494
494
  # items.
495
495
  #
496
- # @param buffer [String] Buffer of data to back the stucture items
496
+ # @param buffer [String] Buffer of data to back the structure items
497
497
  def buffer=(buffer)
498
498
  synchronize() do
499
499
  internal_buffer_equals(buffer)
@@ -507,8 +507,8 @@ module OpenC3
507
507
  # buffer of data
508
508
  def clone
509
509
  structure = super()
510
- # Use instance_variable_set since we have overriden buffer= to do
511
- # additional work that isn't neccessary here
510
+ # Use instance_variable_set since we have overridden buffer= to do
511
+ # additional work that isn't necessary here
512
512
  structure.instance_variable_set("@buffer".freeze, @buffer.clone) if @buffer
513
513
  # Need to update reference packet in the Accessor
514
514
  structure.accessor.packet = structure
@@ -617,9 +617,17 @@ module OpenC3
617
617
  # Anything with a negative bit offset should be left alone
618
618
  if item.original_bit_offset >= 0
619
619
  item.bit_offset = item.original_bit_offset + adjustment
620
- if item.data_type != :DERIVED and (item.variable_bit_size or item.original_bit_size <= 0 or (item.original_array_size and item.original_array_size <= 0))
620
+
621
+ # May need to update adjustment with variable length items
622
+ # Note legacy variable length does not push anything
623
+ if item.data_type != :DERIVED and item.variable_bit_size # Not DERIVED and New Variable Length
624
+ # Calculate the actual current size of this variable length item
621
625
  new_bit_size = calculate_total_bit_size(item)
626
+
622
627
  if item.original_bit_size != new_bit_size
628
+ # Bit size has changed from original - so we need to adjust everything after this item
629
+ # This includes items that may have the same bit_offset as the variable length item because it
630
+ # started out at zero bit_size
623
631
  adjustment += (new_bit_size - item.original_bit_size)
624
632
  end
625
633
  end
@@ -95,6 +95,9 @@ module OpenC3
95
95
  # @return [Hash] Variable bit size information
96
96
  attr_reader :variable_bit_size
97
97
 
98
+ # @return [Integer] Incrementing value that shows relative order items are created
99
+ attr_reader :create_index
100
+
98
101
  # Create a StructureItem by setting all the attributes. It
99
102
  # calls all the setter routines to do the attribute verification and then
100
103
  # verifies the overall integrity.
@@ -238,73 +241,56 @@ module OpenC3
238
241
  verify_overall() if @structure_item_constructed
239
242
  end
240
243
 
241
- def create_index
242
- @create_index.to_i
243
- end
244
-
245
244
  if RUBY_ENGINE != 'ruby' or ENV['OPENC3_NO_EXT']
246
- # Comparison Operator based on bit_offset. This means that StructureItems
247
- # with different names or bit sizes are equal if they have the same bit
248
- # offset.
245
+ # Comparison Operator primarily based on bit_offset
249
246
  def <=>(other)
250
247
  return nil unless other.kind_of?(StructureItem)
251
248
 
252
- other_bit_offset = other.bit_offset
253
- other_bit_size = other.bit_size
254
-
255
- # Handle same bit offset case
256
- if (@bit_offset == 0) && (other_bit_offset == 0)
257
- # Both bit_offsets are 0 so sort by bit_size
258
- # This allows derived items with bit_size of 0 to be listed first
259
- # Compare based on bit size then create index
260
- if @bit_size == other_bit_size
261
- if @create_index
262
- if @create_index <= other.create_index
263
- return -1
264
- else
265
- return 1
266
- end
267
- else
268
- return 0
269
- end
270
- elsif @bit_size < other_bit_size
249
+ other_original_bit_offset = other.original_bit_offset
250
+
251
+ # Derived items should be first in the list with multiple derived sorted
252
+ # by create_index
253
+ if @data_type == :DERIVED
254
+ if other.data_type != :DERIVED
271
255
  return -1
272
256
  else
273
- return 1
257
+ if @create_index <= other.create_index
258
+ return -1
259
+ else
260
+ return 1
261
+ end
274
262
  end
263
+ elsif other.data_type == :DERIVED
264
+ return 1
275
265
  end
276
266
 
277
- # Handle different bit offsets
278
- if ((@bit_offset >= 0) && (other_bit_offset >= 0)) || ((@bit_offset < 0) && (other_bit_offset < 0))
267
+ # Handle non-derived items
268
+ if ((@original_bit_offset >= 0) && (other_original_bit_offset >= 0)) || ((@original_bit_offset < 0) && (other_original_bit_offset < 0))
279
269
  # Both Have Same Sign
280
- if @bit_offset == other_bit_offset
281
- if @create_index
282
- if @create_index <= other.create_index
270
+ if @original_bit_offset == other_original_bit_offset
271
+ # New Variable Bit Size items are before regular items
272
+ if @variable_bit_size
273
+ if not other.variable_bit_size
283
274
  return -1
284
- else
285
- return 1
286
275
  end
276
+ # If both variable_bit_size use create index
277
+ elsif other.variable_bit_size
278
+ return 1
279
+ end
280
+
281
+ if @create_index <= other.create_index
282
+ return -1
287
283
  else
288
- return 0
284
+ return 1
289
285
  end
290
- elsif @bit_offset <= other_bit_offset
286
+ elsif @original_bit_offset <= other_original_bit_offset
291
287
  return -1
292
288
  else
293
289
  return 1
294
290
  end
295
291
  else
296
292
  # Different Signs
297
- if @bit_offset == other_bit_offset
298
- if @create_index
299
- if @create_index < other.create_index
300
- return -1
301
- else
302
- return 1
303
- end
304
- else
305
- return 0
306
- end
307
- elsif @bit_offset < other_bit_offset
293
+ if @original_bit_offset < other_original_bit_offset
308
294
  return 1
309
295
  else
310
296
  return -1