openc3 5.18.0 → 5.19.0

Sign up to get free protection for your applications and to get access to all the features.
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