openc3 5.17.0 → 5.18.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.
Potentially problematic release.
This version of openc3 might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/bin/openc3cli +1 -1
- data/data/config/_interfaces.yaml +4 -4
- data/data/config/command_modifiers.yaml +4 -0
- data/data/config/interface_modifiers.yaml +18 -8
- data/data/config/item_modifiers.yaml +34 -26
- data/data/config/microservice.yaml +4 -1
- data/data/config/param_item_modifiers.yaml +16 -0
- data/data/config/parameter_modifiers.yaml +29 -12
- data/data/config/plugins.yaml +3 -3
- data/data/config/screen.yaml +7 -7
- data/data/config/telemetry_modifiers.yaml +9 -4
- data/data/config/widgets.yaml +41 -14
- data/ext/openc3/ext/packet/packet.c +6 -0
- data/lib/openc3/accessors/accessor.rb +1 -0
- data/lib/openc3/accessors/binary_accessor.rb +170 -11
- data/lib/openc3/api/cmd_api.rb +39 -35
- data/lib/openc3/api/config_api.rb +10 -10
- data/lib/openc3/api/interface_api.rb +28 -21
- data/lib/openc3/api/limits_api.rb +29 -29
- data/lib/openc3/api/metrics_api.rb +3 -3
- data/lib/openc3/api/offline_access_api.rb +5 -5
- data/lib/openc3/api/router_api.rb +25 -19
- data/lib/openc3/api/settings_api.rb +10 -10
- data/lib/openc3/api/stash_api.rb +10 -10
- data/lib/openc3/api/target_api.rb +10 -10
- data/lib/openc3/api/tlm_api.rb +44 -44
- data/lib/openc3/conversions/bit_reverse_conversion.rb +60 -0
- data/lib/openc3/conversions/ip_read_conversion.rb +59 -0
- data/lib/openc3/conversions/ip_write_conversion.rb +61 -0
- data/lib/openc3/conversions/object_read_conversion.rb +88 -0
- data/lib/openc3/conversions/object_write_conversion.rb +38 -0
- data/lib/openc3/conversions.rb +6 -1
- data/lib/openc3/io/json_drb.rb +19 -21
- data/lib/openc3/io/json_rpc.rb +14 -13
- data/lib/openc3/microservices/microservice.rb +11 -11
- data/lib/openc3/microservices/scope_cleanup_microservice.rb +1 -1
- data/lib/openc3/microservices/timeline_microservice.rb +76 -51
- data/lib/openc3/models/activity_model.rb +25 -21
- data/lib/openc3/models/scope_model.rb +44 -13
- data/lib/openc3/models/sorted_model.rb +1 -1
- data/lib/openc3/models/target_model.rb +4 -1
- data/lib/openc3/operators/microservice_operator.rb +2 -2
- data/lib/openc3/operators/operator.rb +9 -9
- data/lib/openc3/packets/packet.rb +18 -1
- data/lib/openc3/packets/packet_config.rb +37 -16
- data/lib/openc3/packets/packet_item.rb +5 -0
- data/lib/openc3/packets/structure.rb +67 -3
- data/lib/openc3/packets/structure_item.rb +49 -12
- data/lib/openc3/script/calendar.rb +2 -2
- data/lib/openc3/script/extract.rb +5 -3
- data/lib/openc3/script/web_socket_api.rb +11 -0
- data/lib/openc3/topics/decom_interface_topic.rb +2 -1
- data/lib/openc3/topics/system_events_topic.rb +40 -0
- data/lib/openc3/utilities/authentication.rb +2 -1
- data/lib/openc3/utilities/authorization.rb +2 -2
- data/lib/openc3/version.rb +5 -5
- data/templates/tool_angular/package.json +5 -5
- data/templates/tool_react/package.json +8 -8
- data/templates/tool_svelte/package.json +10 -10
- data/templates/tool_vue/package.json +10 -10
- data/templates/widget/package.json +10 -10
- data/templates/widget/src/Widget.vue +0 -1
- metadata +22 -2
@@ -14,7 +14,7 @@
|
|
14
14
|
# GNU Affero General Public License for more details.
|
15
15
|
|
16
16
|
# Modified by OpenC3, Inc.
|
17
|
-
# All changes Copyright
|
17
|
+
# All changes Copyright 2024, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
20
|
# This file may also be used under the terms of a commercial license
|
@@ -26,6 +26,18 @@ require 'openc3/models/plugin_model'
|
|
26
26
|
require 'openc3/models/microservice_model'
|
27
27
|
require 'openc3/models/setting_model'
|
28
28
|
require 'openc3/models/trigger_group_model'
|
29
|
+
require 'openc3/topics/system_events_topic'
|
30
|
+
|
31
|
+
begin
|
32
|
+
require 'openc3-enterprise/models/cmd_authority_model'
|
33
|
+
rescue LoadError
|
34
|
+
# Stub out the Enterprise CmdAuthorityModel to do nothing
|
35
|
+
class CmdAuthorityModel
|
36
|
+
def self.names(scope:)
|
37
|
+
[]
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
29
41
|
|
30
42
|
module OpenC3
|
31
43
|
class ScopeModel < Model
|
@@ -37,9 +49,13 @@ module OpenC3
|
|
37
49
|
attr_accessor :text_log_retain_time
|
38
50
|
attr_accessor :tool_log_retain_time
|
39
51
|
attr_accessor :cleanup_poll_time
|
52
|
+
attr_accessor :command_authority
|
40
53
|
|
41
54
|
# NOTE: The following three class methods are used by the ModelController
|
42
55
|
# and are reimplemented to enable various Model class methods to work
|
56
|
+
#
|
57
|
+
# The scope keyword is given to support the ModelController method signature
|
58
|
+
# even though it is not used
|
43
59
|
def self.get(name:, scope: nil)
|
44
60
|
super(PRIMARY_KEY, name: name)
|
45
61
|
end
|
@@ -52,13 +68,13 @@ module OpenC3
|
|
52
68
|
super(PRIMARY_KEY)
|
53
69
|
end
|
54
70
|
|
55
|
-
def self.from_json(json
|
71
|
+
def self.from_json(json)
|
56
72
|
json = JSON.parse(json, :allow_nan => true, :create_additions => true) if String === json
|
57
73
|
raise "json data is nil" if json.nil?
|
58
|
-
self.new(**json.transform_keys(&:to_sym)
|
74
|
+
self.new(**json.transform_keys(&:to_sym))
|
59
75
|
end
|
60
76
|
|
61
|
-
def self.get_model(name
|
77
|
+
def self.get_model(name:)
|
62
78
|
json = get(name: name)
|
63
79
|
if json
|
64
80
|
return from_json(json)
|
@@ -73,18 +89,15 @@ module OpenC3
|
|
73
89
|
text_log_retain_time: nil,
|
74
90
|
tool_log_retain_time: nil,
|
75
91
|
cleanup_poll_time: 900,
|
76
|
-
|
77
|
-
|
92
|
+
command_authority: false,
|
93
|
+
updated_at: nil
|
78
94
|
)
|
79
95
|
super(
|
80
96
|
PRIMARY_KEY,
|
81
97
|
name: name,
|
82
|
-
text_log_cycle_time: text_log_cycle_time,
|
83
|
-
text_log_cycle_size: text_log_cycle_size,
|
84
|
-
text_log_retain_time: text_log_retain_time,
|
85
|
-
tool_log_retain_time: tool_log_retain_time,
|
86
|
-
cleanup_poll_time: cleanup_poll_time,
|
87
98
|
updated_at: updated_at,
|
99
|
+
# This sets the @scope variable which is sort of redundant for the ScopeModel
|
100
|
+
# (since its the same as @name) but every model has a @scope
|
88
101
|
scope: name
|
89
102
|
)
|
90
103
|
@text_log_cycle_time = text_log_cycle_time
|
@@ -92,6 +105,7 @@ module OpenC3
|
|
92
105
|
@text_log_retain_time = text_log_retain_time
|
93
106
|
@tool_log_retain_time = tool_log_retain_time
|
94
107
|
@cleanup_poll_time = cleanup_poll_time
|
108
|
+
@command_authority = command_authority
|
95
109
|
@children = []
|
96
110
|
end
|
97
111
|
|
@@ -99,14 +113,26 @@ module OpenC3
|
|
99
113
|
# Ensure there are no "." in the scope name - prevents gems accidently becoming scope names
|
100
114
|
raise "Invalid scope name: #{@name}" if @name !~ /^[a-zA-Z0-9_-]+$/
|
101
115
|
@name = @name.upcase
|
116
|
+
@scope = @name # Ensure @scope matches @name
|
102
117
|
super(update: update, force: force, queued: queued)
|
118
|
+
|
119
|
+
# If we're updating the scope and disabling command_authority
|
120
|
+
# then we clear out all the existing values so it comes up fresh
|
121
|
+
if update and @command_authority == false
|
122
|
+
CmdAuthorityModel.names(scope: @name).each do |auth_name|
|
123
|
+
model = CmdAuthorityModel.get_model(name: auth_name, scope: @name)
|
124
|
+
model.destroy if model
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
SystemEventsTopic.write(:scope, as_json())
|
103
129
|
end
|
104
130
|
|
105
131
|
def destroy
|
106
132
|
if @name != 'DEFAULT'
|
107
133
|
# Remove all the plugins for this scope
|
108
134
|
plugins = PluginModel.get_all_models(scope: @name)
|
109
|
-
plugins.each do |
|
135
|
+
plugins.each do |_plugin_name, plugin|
|
110
136
|
plugin.destroy
|
111
137
|
end
|
112
138
|
super()
|
@@ -115,7 +141,7 @@ module OpenC3
|
|
115
141
|
end
|
116
142
|
end
|
117
143
|
|
118
|
-
def as_json(*
|
144
|
+
def as_json(*_a)
|
119
145
|
{ 'name' => @name,
|
120
146
|
'updated_at' => @updated_at,
|
121
147
|
'text_log_cycle_time' => @text_log_cycle_time,
|
@@ -123,6 +149,7 @@ module OpenC3
|
|
123
149
|
'text_log_retain_time' => @text_log_retain_time,
|
124
150
|
'tool_log_retain_time' => @tool_log_retain_time,
|
125
151
|
'cleanup_poll_time' => @cleanup_poll_time,
|
152
|
+
'command_authority' => @command_authority,
|
126
153
|
}
|
127
154
|
end
|
128
155
|
|
@@ -276,6 +303,10 @@ module OpenC3
|
|
276
303
|
end
|
277
304
|
|
278
305
|
def undeploy
|
306
|
+
# Delete UNKNOWN target
|
307
|
+
target = TargetModel.get_model(name: "UNKNOWN", scope: @scope)
|
308
|
+
target.destroy
|
309
|
+
|
279
310
|
model = MicroserviceModel.get_model(name: "#{@scope}__SCOPEMULTI__#{@scope}", scope: @scope)
|
280
311
|
model.destroy if model
|
281
312
|
model = MicroserviceModel.get_model(name: "#{@scope}__SCOPECLEANUP__#{@scope}", scope: @scope)
|
@@ -14,7 +14,7 @@
|
|
14
14
|
# GNU Affero General Public License for more details.
|
15
15
|
|
16
16
|
# Modified by OpenC3, Inc.
|
17
|
-
# All changes Copyright
|
17
|
+
# All changes Copyright 2024, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
20
|
# This file may also be used under the terms of a commercial license
|
@@ -660,7 +660,10 @@ module OpenC3
|
|
660
660
|
Store.del(item_map_key)
|
661
661
|
@@item_map_cache[@name] = nil
|
662
662
|
|
663
|
-
|
663
|
+
topic = { kind: 'deleted', type: 'target', name: @name }
|
664
|
+
# The UNKNOWN target doesn't have an associated plugin
|
665
|
+
topic[:plugin] = @plugin if @plugin
|
666
|
+
ConfigTopic.write(topic, scope: @scope)
|
664
667
|
rescue Exception => e
|
665
668
|
Logger.error("Error undeploying target model #{@name} in scope #{@scope} due to #{e}")
|
666
669
|
end
|
@@ -14,7 +14,7 @@
|
|
14
14
|
# GNU Affero General Public License for more details.
|
15
15
|
|
16
16
|
# Modified by OpenC3, Inc.
|
17
|
-
# All changes Copyright
|
17
|
+
# All changes Copyright 2024, OpenC3, Inc.
|
18
18
|
# All Rights Reserved
|
19
19
|
#
|
20
20
|
# This file may also be used under the terms of a commercial license
|
@@ -186,7 +186,7 @@ module OpenC3
|
|
186
186
|
end
|
187
187
|
end
|
188
188
|
|
189
|
-
@removed_microservices.each do |microservice_name,
|
189
|
+
@removed_microservices.each do |microservice_name, _microservice_config|
|
190
190
|
process = @processes[microservice_name]
|
191
191
|
@processes.delete(microservice_name)
|
192
192
|
@removed_processes[microservice_name] = process
|
@@ -127,7 +127,7 @@ module OpenC3
|
|
127
127
|
# @process.io.inherit!
|
128
128
|
@process.cwd = @work_dir
|
129
129
|
# Spawned process should not be controlled by same Bundler constraints as spawning process
|
130
|
-
ENV.each do |key,
|
130
|
+
ENV.each do |key, _value|
|
131
131
|
if key =~ /^BUNDLER/
|
132
132
|
@process.environment[key] = nil
|
133
133
|
end
|
@@ -265,7 +265,7 @@ module OpenC3
|
|
265
265
|
if @new_processes.length > 0
|
266
266
|
# Start all the processes
|
267
267
|
Logger.info("#{self.class} starting each new process...")
|
268
|
-
@new_processes.each { |
|
268
|
+
@new_processes.each { |_name, p| p.start }
|
269
269
|
@new_processes = {}
|
270
270
|
end
|
271
271
|
end
|
@@ -278,7 +278,7 @@ module OpenC3
|
|
278
278
|
shutdown_processes(@changed_processes)
|
279
279
|
break if @shutdown
|
280
280
|
|
281
|
-
@changed_processes.each { |
|
281
|
+
@changed_processes.each { |_name, p| p.start }
|
282
282
|
@changed_processes = {}
|
283
283
|
end
|
284
284
|
end
|
@@ -296,7 +296,7 @@ module OpenC3
|
|
296
296
|
|
297
297
|
def respawn_dead
|
298
298
|
@mutex.synchronize do
|
299
|
-
@processes.each do |
|
299
|
+
@processes.each do |_name, p|
|
300
300
|
break if @shutdown
|
301
301
|
p.output_increment
|
302
302
|
unless p.alive?
|
@@ -314,7 +314,7 @@ module OpenC3
|
|
314
314
|
processes = processes.dup
|
315
315
|
|
316
316
|
Logger.info("Commanding soft stops...")
|
317
|
-
processes.each { |
|
317
|
+
processes.each { |_name, p| p.soft_stop }
|
318
318
|
start_time = Time.now
|
319
319
|
# Allow sufficient time for processes to shutdown cleanly
|
320
320
|
while (Time.now - start_time) < PROCESS_SHUTDOWN_SECONDS
|
@@ -322,21 +322,21 @@ module OpenC3
|
|
322
322
|
processes.each do |name, p|
|
323
323
|
unless p.alive?
|
324
324
|
processes_to_remove << name
|
325
|
-
Logger.
|
325
|
+
Logger.debug("Soft stop process successful: #{p.cmd_line}", scope: p.scope)
|
326
326
|
end
|
327
327
|
end
|
328
328
|
processes_to_remove.each do |name|
|
329
329
|
processes.delete(name)
|
330
330
|
end
|
331
331
|
if processes.length <= 0
|
332
|
-
Logger.
|
332
|
+
Logger.debug("Soft stop all successful")
|
333
333
|
break
|
334
334
|
end
|
335
335
|
sleep(0.1)
|
336
336
|
end
|
337
337
|
if processes.length > 0
|
338
|
-
Logger.
|
339
|
-
processes.each { |
|
338
|
+
Logger.debug("Commanding hard stops...")
|
339
|
+
processes.each { |_name, p| p.hard_stop }
|
340
340
|
end
|
341
341
|
end
|
342
342
|
|
@@ -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 [Boolean] If this packet should be used for identification
|
108
|
+
attr_reader :virtual
|
109
|
+
|
107
110
|
# Valid format types
|
108
111
|
VALUE_TYPES = [:RAW, :CONVERTED, :FORMATTED, :WITH_UNITS]
|
109
112
|
|
@@ -144,6 +147,7 @@ module OpenC3
|
|
144
147
|
@template = nil
|
145
148
|
@packet_time = nil
|
146
149
|
@ignore_overlap = false
|
150
|
+
@virtual = false
|
147
151
|
end
|
148
152
|
|
149
153
|
# Sets the target name this packet is associated with. Unidentified packets
|
@@ -233,6 +237,7 @@ module OpenC3
|
|
233
237
|
# @return [Boolean] Whether or not the buffer of data is this packet
|
234
238
|
def identify?(buffer)
|
235
239
|
return false unless buffer
|
240
|
+
return false if @virtual
|
236
241
|
return true unless @id_items
|
237
242
|
|
238
243
|
@id_items.each do |item|
|
@@ -286,6 +291,14 @@ module OpenC3
|
|
286
291
|
@packet_time = time
|
287
292
|
end
|
288
293
|
|
294
|
+
def virtual=(v)
|
295
|
+
@virtual = v
|
296
|
+
if @virtual
|
297
|
+
@hidden = true
|
298
|
+
@disabled = true
|
299
|
+
end
|
300
|
+
end
|
301
|
+
|
289
302
|
# Calculates a unique hashing sum that changes if the parts of the packet configuration change that could affect
|
290
303
|
# the "shape" of the packet. This value is cached and that packet should not be changed if this method is being used
|
291
304
|
def config_name
|
@@ -1058,7 +1071,9 @@ module OpenC3
|
|
1058
1071
|
config << " ALLOW_SHORT\n" if @short_buffer_allowed
|
1059
1072
|
config << " HAZARDOUS #{@hazardous_description.to_s.quote_if_necessary}\n" if @hazardous
|
1060
1073
|
config << " DISABLE_MESSAGES\n" if @messages_disabled
|
1061
|
-
if @
|
1074
|
+
if @virtual
|
1075
|
+
config << " VIRTUAL\n"
|
1076
|
+
elsif @disabled
|
1062
1077
|
config << " DISABLED\n"
|
1063
1078
|
elsif @hidden
|
1064
1079
|
config << " HIDDEN\n"
|
@@ -1122,6 +1137,7 @@ module OpenC3
|
|
1122
1137
|
config['messages_disabled'] = true if @messages_disabled
|
1123
1138
|
config['disabled'] = true if @disabled
|
1124
1139
|
config['hidden'] = true if @hidden
|
1140
|
+
config['virtual'] = true if @virtual
|
1125
1141
|
config['accessor'] = @accessor.class.to_s
|
1126
1142
|
config['accessor_args'] = @accessor.args
|
1127
1143
|
config['template'] = Base64.encode64(@template) if @template
|
@@ -1176,6 +1192,7 @@ module OpenC3
|
|
1176
1192
|
packet.messages_disabled = hash['messages_disabled']
|
1177
1193
|
packet.disabled = hash['disabled']
|
1178
1194
|
packet.hidden = hash['hidden']
|
1195
|
+
packet.virtual = hash['virtual']
|
1179
1196
|
if hash['accessor']
|
1180
1197
|
begin
|
1181
1198
|
accessor = OpenC3::const_get(hash['accessor'])
|
@@ -74,13 +74,13 @@ module OpenC3
|
|
74
74
|
attr_reader :latest_data
|
75
75
|
|
76
76
|
# @return [Hash<String>=>Hash<Array>=>Packet] Hash keyed by target name
|
77
|
-
# that returns a hash keyed by an array of id values.
|
78
|
-
# defined by that identification.
|
77
|
+
# that returns a hash keyed by an array of id values. The id values resolve to the packet
|
78
|
+
# defined by that identification. Command version
|
79
79
|
attr_reader :cmd_id_value_hash
|
80
80
|
|
81
81
|
# @return [Hash<String>=>Hash<Array>=>Packet] Hash keyed by target name
|
82
|
-
# that returns a hash keyed by an array of id values.
|
83
|
-
# defined by that identification.
|
82
|
+
# that returns a hash keyed by an array of id values. The id values resolve to the packet
|
83
|
+
# defined by that identification. Telemetry version
|
84
84
|
attr_reader :tlm_id_value_hash
|
85
85
|
|
86
86
|
# @return [String] Language of current target (ruby or python)
|
@@ -219,7 +219,7 @@ module OpenC3
|
|
219
219
|
'PARAMETER', 'ID_ITEM', 'ID_PARAMETER', 'ARRAY_ITEM', 'ARRAY_PARAMETER', 'APPEND_ITEM',\
|
220
220
|
'APPEND_PARAMETER', 'APPEND_ID_ITEM', 'APPEND_ID_PARAMETER', 'APPEND_ARRAY_ITEM',\
|
221
221
|
'APPEND_ARRAY_PARAMETER', 'ALLOW_SHORT', 'HAZARDOUS', 'PROCESSOR', 'META',\
|
222
|
-
'DISABLE_MESSAGES', 'HIDDEN', 'DISABLED', 'ACCESSOR', 'TEMPLATE', 'TEMPLATE_FILE',\
|
222
|
+
'DISABLE_MESSAGES', 'HIDDEN', 'DISABLED', 'VIRTUAL', 'ACCESSOR', 'TEMPLATE', 'TEMPLATE_FILE',\
|
223
223
|
'RESPONSE', 'ERROR_RESPONSE', 'SCREEN', 'RELATED_ITEM', 'IGNORE_OVERLAP'
|
224
224
|
raise parser.error("No current packet for #{keyword}") unless @current_packet
|
225
225
|
|
@@ -232,7 +232,7 @@ module OpenC3
|
|
232
232
|
'POLY_WRITE_CONVERSION', 'SEG_POLY_READ_CONVERSION', 'SEG_POLY_WRITE_CONVERSION',\
|
233
233
|
'GENERIC_READ_CONVERSION_START', 'GENERIC_WRITE_CONVERSION_START', 'REQUIRED',\
|
234
234
|
'LIMITS', 'LIMITS_RESPONSE', 'UNITS', 'FORMAT_STRING', 'DESCRIPTION',\
|
235
|
-
'MINIMUM_VALUE', 'MAXIMUM_VALUE', 'DEFAULT_VALUE', 'OVERFLOW', 'OVERLAP', 'KEY'
|
235
|
+
'MINIMUM_VALUE', 'MAXIMUM_VALUE', 'DEFAULT_VALUE', 'OVERFLOW', 'OVERLAP', 'KEY', 'VARIABLE_BIT_SIZE'
|
236
236
|
raise parser.error("No current item for #{keyword}") unless @current_item
|
237
237
|
|
238
238
|
process_current_item(parser, keyword, params)
|
@@ -317,16 +317,20 @@ module OpenC3
|
|
317
317
|
if @current_cmd_or_tlm == COMMAND
|
318
318
|
PacketParser.check_item_data_types(@current_packet)
|
319
319
|
@commands[@current_packet.target_name][@current_packet.packet_name] = @current_packet
|
320
|
-
|
321
|
-
|
322
|
-
|
323
|
-
|
320
|
+
unless @current_packet.virtual
|
321
|
+
hash = @cmd_id_value_hash[@current_packet.target_name]
|
322
|
+
hash = {} unless hash
|
323
|
+
@cmd_id_value_hash[@current_packet.target_name] = hash
|
324
|
+
update_id_value_hash(@current_packet, hash)
|
325
|
+
end
|
324
326
|
else
|
325
327
|
@telemetry[@current_packet.target_name][@current_packet.packet_name] = @current_packet
|
326
|
-
|
327
|
-
|
328
|
-
|
329
|
-
|
328
|
+
unless @current_packet.virtual
|
329
|
+
hash = @tlm_id_value_hash[@current_packet.target_name]
|
330
|
+
hash = {} unless hash
|
331
|
+
@tlm_id_value_hash[@current_packet.target_name] = hash
|
332
|
+
update_id_value_hash(@current_packet, hash)
|
333
|
+
end
|
330
334
|
end
|
331
335
|
@current_packet = nil
|
332
336
|
@current_item = nil
|
@@ -337,7 +341,7 @@ module OpenC3
|
|
337
341
|
if cmd_or_tlm == :COMMAND
|
338
342
|
@commands[packet.target_name][packet.packet_name] = packet
|
339
343
|
|
340
|
-
if affect_ids
|
344
|
+
if affect_ids and not packet.virtual
|
341
345
|
hash = @cmd_id_value_hash[packet.target_name]
|
342
346
|
hash = {} unless hash
|
343
347
|
@cmd_id_value_hash[packet.target_name] = hash
|
@@ -354,7 +358,7 @@ module OpenC3
|
|
354
358
|
latest_data_packets << packet unless latest_data_packets.include?(packet)
|
355
359
|
end
|
356
360
|
|
357
|
-
if affect_ids
|
361
|
+
if affect_ids and not packet.virtual
|
358
362
|
hash = @tlm_id_value_hash[packet.target_name]
|
359
363
|
hash = {} unless hash
|
360
364
|
@tlm_id_value_hash[packet.target_name] = hash
|
@@ -469,6 +473,13 @@ module OpenC3
|
|
469
473
|
@current_packet.hidden = true
|
470
474
|
@current_packet.disabled = true
|
471
475
|
|
476
|
+
when 'VIRTUAL'
|
477
|
+
usage = "#{keyword}"
|
478
|
+
parser.verify_num_parameters(0, 0, usage)
|
479
|
+
@current_packet.hidden = true
|
480
|
+
@current_packet.disabled = true
|
481
|
+
@current_packet.virtual = true
|
482
|
+
|
472
483
|
when 'ACCESSOR'
|
473
484
|
usage = "#{keyword} <Accessor class name>"
|
474
485
|
parser.verify_num_parameters(1, nil, usage)
|
@@ -720,6 +731,16 @@ module OpenC3
|
|
720
731
|
when 'KEY'
|
721
732
|
parser.verify_num_parameters(1, 1, 'KEY <key or path into data>')
|
722
733
|
@current_item.key = params[0]
|
734
|
+
|
735
|
+
when 'VARIABLE_BIT_SIZE'
|
736
|
+
parser.verify_num_parameters(1, 3, 'VARIABLE_BIT_SIZE <length_item_name> <length_bits_per_count = 8> <length_value_bit_offset = 0>')
|
737
|
+
|
738
|
+
variable_bit_size = {'length_bits_per_count' => 8, 'length_value_bit_offset' => 0}
|
739
|
+
variable_bit_size['length_item_name'] = params[0].upcase
|
740
|
+
variable_bit_size['length_bits_per_count'] = Integer(params[1]) if params[1]
|
741
|
+
variable_bit_size['length_value_bit_offset'] = Integer(params[2]) if params[2]
|
742
|
+
|
743
|
+
@current_item.variable_bit_size = variable_bit_size
|
723
744
|
end
|
724
745
|
end
|
725
746
|
|
@@ -401,6 +401,7 @@ module OpenC3
|
|
401
401
|
config << " #{self.endianness}" if self.endianness != default_endianness && self.data_type != :STRING && self.data_type != :BLOCK
|
402
402
|
config << "\n"
|
403
403
|
|
404
|
+
config << " VARIABLE_BIT_SIZE '#{self.variable_bit_size['length_item_name']}' #{self.variable_bit_size['length_bits_per_count']} #{self.variable_bit_size['length_value_bit_offset']}\n" if self.variable_bit_size
|
404
405
|
config << " REQUIRED\n" if self.required
|
405
406
|
config << " FORMAT_STRING #{self.format_string.to_s.quote_if_necessary}\n" if self.format_string
|
406
407
|
config << " UNITS #{self.units_full.to_s.quote_if_necessary} #{self.units.to_s.quote_if_necessary}\n" if self.units
|
@@ -510,6 +511,9 @@ module OpenC3
|
|
510
511
|
end
|
511
512
|
|
512
513
|
config['meta'] = @meta if @meta
|
514
|
+
if @variable_bit_size
|
515
|
+
config['variable_bit_size'] = @variable_bit_size
|
516
|
+
end
|
513
517
|
config
|
514
518
|
end
|
515
519
|
|
@@ -571,6 +575,7 @@ module OpenC3
|
|
571
575
|
item.limits.values = values if values.length > 0
|
572
576
|
end
|
573
577
|
item.meta = hash['meta']
|
578
|
+
item.variable_bit_size = hash['variable_bit_size']
|
574
579
|
item
|
575
580
|
end
|
576
581
|
|
@@ -299,7 +299,6 @@ module OpenC3
|
|
299
299
|
# @param overflow (see #define_item)
|
300
300
|
# @return (see #define_item)
|
301
301
|
def append_item(name, bit_size, data_type, array_size = nil, endianness = @default_endianness, overflow = :ERROR)
|
302
|
-
raise ArgumentError, "Can't append an item after a variably sized item" if !@fixed_size
|
303
302
|
if data_type == :DERIVED
|
304
303
|
return define_item(name, 0, bit_size, data_type, array_size, endianness, overflow)
|
305
304
|
else
|
@@ -313,12 +312,15 @@ module OpenC3
|
|
313
312
|
# @param item (see #define)
|
314
313
|
# @return (see #define)
|
315
314
|
def append(item)
|
316
|
-
raise ArgumentError, "Can't append an item after a variably sized item" if !@fixed_size
|
317
|
-
|
318
315
|
if item.data_type == :DERIVED
|
319
316
|
item.bit_offset = 0
|
320
317
|
else
|
318
|
+
# We're appending a new item so set the bit_offset
|
321
319
|
item.bit_offset = @defined_length_bits
|
320
|
+
# Also set original_bit_offset because it's currently 0
|
321
|
+
# due to PacketItemParser::create_packet_item
|
322
|
+
# get_bit_offset() returning 0 if append
|
323
|
+
item.original_bit_offset = @defined_length_bits
|
322
324
|
end
|
323
325
|
return define(item)
|
324
326
|
end
|
@@ -337,6 +339,20 @@ module OpenC3
|
|
337
339
|
def set_item(item)
|
338
340
|
if @items[item.name]
|
339
341
|
@items[item.name] = item
|
342
|
+
# Need to allocate space for the variable length item if its minimum size is greater than zero
|
343
|
+
if item.variable_bit_size
|
344
|
+
minimum_data_bits = 0
|
345
|
+
if (item.data_type == :INT or item.data_type == :UINT) and not item.original_array_size
|
346
|
+
# Minimum QUIC encoded integer, see https://datatracker.ietf.org/doc/html/rfc9000#name-variable-length-integer-enc
|
347
|
+
minimum_data_bits = 6
|
348
|
+
# :STRING, :BLOCK, or array item
|
349
|
+
elsif item.variable_bit_size['length_value_bit_offset'] > 0
|
350
|
+
minimum_data_bits = item.variable_bit_size['length_value_bit_offset'] * item.variable_bit_size['length_bits_per_count']
|
351
|
+
end
|
352
|
+
if minimum_data_bits > 0 and item.bit_offset >= 0 and @defined_length_bits == item.bit_offset
|
353
|
+
@defined_length_bits += minimum_data_bits
|
354
|
+
end
|
355
|
+
end
|
340
356
|
else
|
341
357
|
raise ArgumentError, "Unknown item: #{item.name} - Ensure item name is uppercase"
|
342
358
|
end
|
@@ -566,6 +582,51 @@ module OpenC3
|
|
566
582
|
end
|
567
583
|
end
|
568
584
|
|
585
|
+
def calculate_total_bit_size(item)
|
586
|
+
if item.variable_bit_size
|
587
|
+
# Bit size is determined by length field
|
588
|
+
length_value = self.read(item.variable_bit_size['length_item_name'], :CONVERTED)
|
589
|
+
if item.data_type == :INT or item.data_type == :UINT and not item.original_array_size
|
590
|
+
case length_value
|
591
|
+
when 0
|
592
|
+
return 6
|
593
|
+
when 1
|
594
|
+
return 14
|
595
|
+
when 2
|
596
|
+
return 30
|
597
|
+
else
|
598
|
+
return 62
|
599
|
+
end
|
600
|
+
else
|
601
|
+
return (length_value * item.variable_bit_size['length_bits_per_count']) + item.variable_bit_size['length_value_bit_offset']
|
602
|
+
end
|
603
|
+
elsif item.original_bit_size <= 0
|
604
|
+
# Bit size is full packet length - bits before item + negative bits saved at end
|
605
|
+
return (@buffer.length * 8) - item.bit_offset + item.original_bit_size
|
606
|
+
elsif item.original_array_size and item.original_array_size <= 0
|
607
|
+
# Bit size is full packet length - bits before item + negative bits saved at end
|
608
|
+
return (@buffer.length * 8) - item.bit_offset + item.original_array_size
|
609
|
+
else
|
610
|
+
raise "Unexpected use of calculate_total_bit_size for non-variable-sized item"
|
611
|
+
end
|
612
|
+
end
|
613
|
+
|
614
|
+
def recalculate_bit_offsets
|
615
|
+
adjustment = 0
|
616
|
+
@sorted_items.each do |item|
|
617
|
+
# Anything with a negative bit offset should be left alone
|
618
|
+
if item.original_bit_offset >= 0
|
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))
|
621
|
+
new_bit_size = calculate_total_bit_size(item)
|
622
|
+
if item.original_bit_size != new_bit_size
|
623
|
+
adjustment += (new_bit_size - item.original_bit_size)
|
624
|
+
end
|
625
|
+
end
|
626
|
+
end
|
627
|
+
end
|
628
|
+
end
|
629
|
+
|
569
630
|
def internal_buffer_equals(buffer)
|
570
631
|
raise ArgumentError, "Buffer class is #{buffer.class} but must be String" unless String === buffer
|
571
632
|
|
@@ -573,6 +634,9 @@ module OpenC3
|
|
573
634
|
if @accessor.enforce_encoding
|
574
635
|
@buffer.force_encoding(@accessor.enforce_encoding)
|
575
636
|
end
|
637
|
+
if not @fixed_size
|
638
|
+
recalculate_bit_offsets()
|
639
|
+
end
|
576
640
|
if @accessor.enforce_length
|
577
641
|
if @buffer.length != @defined_length
|
578
642
|
if @buffer.length < @defined_length
|