openc3 7.1.1 → 7.2.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.
- checksums.yaml +4 -4
- data/data/config/command_modifiers.yaml +2 -2
- data/data/config/item_modifiers.yaml +10 -3
- data/lib/openc3/api/tlm_api.rb +6 -0
- data/lib/openc3/microservices/microservice.rb +20 -5
- data/lib/openc3/operators/operator.rb +34 -9
- data/lib/openc3/packets/packet_config.rb +17 -4
- data/lib/openc3/script/suite.rb +1 -1
- data/lib/openc3/script/web_socket_api.rb +5 -1
- data/lib/openc3/utilities/cli_generator.rb +427 -403
- data/lib/openc3/utilities/questdb_client.rb +51 -4
- data/lib/openc3/utilities/running_script.rb +41 -3
- data/lib/openc3/utilities/simulated_target.rb +4 -2
- data/lib/openc3/version.rb +6 -6
- data/templates/command_validator/command_validator.py +8 -10
- data/templates/command_validator/command_validator.rb +6 -9
- data/templates/plugin/LICENSE.md +16 -4
- data/templates/tool_angular/package.json +2 -2
- data/templates/tool_react/package.json +1 -1
- data/templates/tool_svelte/package.json +1 -1
- data/templates/tool_vue/package.json +3 -3
- data/templates/widget/package.json +2 -2
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 90e17f6bf28158ea786646171220e3c23aca07cce30559b3e64a29f83dabb50d
|
|
4
|
+
data.tar.gz: 9a13e73273ba67e7d6003a9578a34a8b1c0abadea3b8d4d369f1fd3803a4f0ec
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: eec5ccdb945747570717e5f60f9a89158d500d994f1fa5e6ebc81ed8bb263229950f10e1f277e0c68bf5d9fb531f3470354d45fda2164f7608c9ad22413b2022
|
|
7
|
+
data.tar.gz: a32f7675eacf6b41b345a4b5ae554292db011499d12a9e5e65173c514e5fac7e66da3bacea7ad5cf9a3738fcb96e3a82009997993fc70722d4a9ba4edd236261
|
|
@@ -417,12 +417,12 @@ VALIDATOR:
|
|
|
417
417
|
return [False, "TGT PKT ITEM is 0"]
|
|
418
418
|
self.cmd_acpt_cnt = tlm("INST HEALTH_STATUS CMD_ACPT_CNT")
|
|
419
419
|
# Return true to indicate Success, false to indicate Failure,
|
|
420
|
-
# and
|
|
420
|
+
# and None to indicate Unknown. The second value is the optional message.
|
|
421
421
|
return [True, None]
|
|
422
422
|
|
|
423
423
|
def post_check(self, command):
|
|
424
424
|
wait_check(f"INST HEALTH_STATUS CMD_ACPT_CNT > {self.cmd_acpt_cnt}", 10)
|
|
425
425
|
# Return true to indicate Success, false to indicate Failure,
|
|
426
|
-
# and
|
|
426
|
+
# and None to indicate Unknown. The second value is the optional message.
|
|
427
427
|
return [True, None]
|
|
428
428
|
since: 5.19.0
|
|
@@ -111,20 +111,27 @@ CONVERTED_DATA:
|
|
|
111
111
|
values: \d+
|
|
112
112
|
LIMITS:
|
|
113
113
|
summary: Defines a set of limits for a telemetry item
|
|
114
|
-
description:
|
|
114
|
+
description: |
|
|
115
|
+
If limits are violated a message is printed in the Command and Telemetry Server
|
|
115
116
|
to indicate an item went out of limits. Other tools also use this information
|
|
116
117
|
to update displays with different colored telemetry items or other useful information.
|
|
117
118
|
The concept of "limits sets" is defined to allow for different limits values
|
|
118
119
|
in different environments. For example, you might want tighter or looser limits
|
|
119
120
|
on telemetry if your environment changes such as during thermal vacuum testing.
|
|
121
|
+
|
|
122
|
+
A DEFAULT limits set is required for every telemetry item with limits. If you
|
|
123
|
+
define additional named sets (e.g. TVAC), the DEFAULT set must be defined first.
|
|
124
|
+
Attempting to define a named set before DEFAULT will raise an error of the form
|
|
125
|
+
"DEFAULT limits set must be defined for TARGET PACKET ITEM before setting limits set NAME".
|
|
120
126
|
example: |
|
|
121
127
|
LIMITS DEFAULT 3 ENABLED -80.0 -70.0 60.0 80.0 -20.0 20.0
|
|
122
128
|
LIMITS TVAC 3 ENABLED -80.0 -30.0 30.0 80.0
|
|
123
129
|
parameters:
|
|
124
130
|
- name: Limits Set
|
|
125
131
|
required: true
|
|
126
|
-
description: Name of the limits set.
|
|
127
|
-
|
|
132
|
+
description: Name of the limits set. A DEFAULT set is required and must be
|
|
133
|
+
defined before any other named sets for this item. If you have no unique
|
|
134
|
+
limits sets use the keyword DEFAULT.
|
|
128
135
|
values: .+
|
|
129
136
|
- name: Persistence
|
|
130
137
|
required: true
|
data/lib/openc3/api/tlm_api.rb
CHANGED
|
@@ -457,6 +457,12 @@ module OpenC3
|
|
|
457
457
|
alias subscribe_packet subscribe_packets
|
|
458
458
|
|
|
459
459
|
# Get packets based on ID returned from subscribe_packet.
|
|
460
|
+
# Packets are ordered within each subscribed packet stream (target/packet pair)
|
|
461
|
+
# but are NOT interleaved by time across streams. If chronological order across
|
|
462
|
+
# streams is required, sort the returned array by the 'time' field. Sorting only
|
|
463
|
+
# orders the current batch - packets across separate get_packets calls may still
|
|
464
|
+
# arrive out of order, so subscribers needing global ordering must buffer and merge
|
|
465
|
+
# across calls.
|
|
460
466
|
# @param id [String] ID returned from subscribe_packets or last call to get_packets
|
|
461
467
|
# @param block [Integer] Unused - Blocking must be implemented at the client
|
|
462
468
|
# @param count [Integer] Maximum number of packets to return from EACH packet stream
|
|
@@ -155,11 +155,26 @@ module OpenC3
|
|
|
155
155
|
|
|
156
156
|
prefix = "#{@scope}/microservices/#{@name}/"
|
|
157
157
|
file_count = 0
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
158
|
+
# Tolerate transient object store failures during startup. On a busy or
|
|
159
|
+
# underpowered cluster the bucket store (e.g. MinIO) can be briefly
|
|
160
|
+
# unreachable while many microservices start at once. Rather than crash
|
|
161
|
+
# and CrashLoopBackOff (which can outlast deploy timeouts), retry for a
|
|
162
|
+
# bounded time before giving up.
|
|
163
|
+
startup_timeout = (ENV['OPENC3_MICROSERVICE_STARTUP_BUCKET_TIMEOUT'] || 60).to_f
|
|
164
|
+
startup_deadline = Time.now + startup_timeout
|
|
165
|
+
begin
|
|
166
|
+
file_count = 0
|
|
167
|
+
client.list_objects(bucket: bucket, prefix: prefix).each do |object|
|
|
168
|
+
response_target = OpenC3.sanitize_path(File.join(@temp_dir, object.key.split(prefix)[-1]))
|
|
169
|
+
FileUtils.mkdir_p(File.dirname(response_target))
|
|
170
|
+
client.get_object(bucket: bucket, key: object.key, path: response_target)
|
|
171
|
+
file_count += 1
|
|
172
|
+
end
|
|
173
|
+
rescue => error
|
|
174
|
+
raise if Time.now >= startup_deadline
|
|
175
|
+
@logger.warn("Microservice #{@name} startup: bucket access failed (#{error.class}: #{error.message}); retrying for up to #{startup_timeout.to_i}s")
|
|
176
|
+
sleep(5)
|
|
177
|
+
retry
|
|
163
178
|
end
|
|
164
179
|
|
|
165
180
|
# Adjust @work_dir to microservice files downloaded if files and a relative path
|
|
@@ -238,6 +238,11 @@ module OpenC3
|
|
|
238
238
|
|
|
239
239
|
OperatorProcess.setup()
|
|
240
240
|
@cycle_time = (ENV['OPERATOR_CYCLE_TIME'] and ENV['OPERATOR_CYCLE_TIME'].to_f) || CYCLE_TIME # time in seconds
|
|
241
|
+
# Maximum number of new microservices to start per cycle. This spreads a
|
|
242
|
+
# large startup burst (e.g. installing a plugin with many targets) across
|
|
243
|
+
# multiple cycles so the shared services (object store, redis) aren't
|
|
244
|
+
# stampeded by every microservice connecting at once. 0 = no limit.
|
|
245
|
+
@max_start_per_cycle = (ENV['OPENC3_OPERATOR_MAX_START_PER_CYCLE'] || 5).to_i
|
|
241
246
|
|
|
242
247
|
@ruby_process_name = ENV['OPENC3_RUBY']
|
|
243
248
|
if RUBY_ENGINE != 'ruby'
|
|
@@ -262,10 +267,17 @@ module OpenC3
|
|
|
262
267
|
def start_new
|
|
263
268
|
@mutex.synchronize do
|
|
264
269
|
if @new_processes.length > 0
|
|
265
|
-
# Start
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
270
|
+
# Start at most @max_start_per_cycle processes this cycle; any
|
|
271
|
+
# remaining stay queued in @new_processes and start on later cycles.
|
|
272
|
+
# This avoids a startup stampede when many microservices appear at
|
|
273
|
+
# once (e.g. a plugin install) overwhelming the object store / redis.
|
|
274
|
+
start_names = @new_processes.keys
|
|
275
|
+
start_names = start_names[0...@max_start_per_cycle] if @max_start_per_cycle > 0
|
|
276
|
+
Logger.info("#{self.class} starting #{start_names.length} of #{@new_processes.length} new process(es)...")
|
|
277
|
+
start_names.each do |name|
|
|
278
|
+
@new_processes[name].start
|
|
279
|
+
@new_processes.delete(name)
|
|
280
|
+
end
|
|
269
281
|
end
|
|
270
282
|
end
|
|
271
283
|
end
|
|
@@ -273,12 +285,22 @@ module OpenC3
|
|
|
273
285
|
def respawn_changed
|
|
274
286
|
@mutex.synchronize do
|
|
275
287
|
if @changed_processes.length > 0
|
|
276
|
-
|
|
277
|
-
|
|
288
|
+
# Cycle at most @max_start_per_cycle changed microservices this cycle;
|
|
289
|
+
# any remaining stay queued in @changed_processes and are cycled on
|
|
290
|
+
# later cycles. This avoids a restart stampede when many microservices
|
|
291
|
+
# change at once (e.g. a configmap change) overwhelming shared
|
|
292
|
+
# services. Processes not yet cycled keep running until their turn.
|
|
293
|
+
cycle_names = @changed_processes.keys
|
|
294
|
+
cycle_names = cycle_names[0...@max_start_per_cycle] if @max_start_per_cycle > 0
|
|
295
|
+
cycle = @changed_processes.slice(*cycle_names)
|
|
296
|
+
Logger.info("Cycling #{cycle.length} of #{@changed_processes.length} changed microservices...")
|
|
297
|
+
shutdown_processes(cycle)
|
|
278
298
|
break if @shutdown
|
|
279
299
|
|
|
280
|
-
|
|
281
|
-
|
|
300
|
+
cycle_names.each do |name|
|
|
301
|
+
@changed_processes[name].start
|
|
302
|
+
@changed_processes.delete(name)
|
|
303
|
+
end
|
|
282
304
|
end
|
|
283
305
|
end
|
|
284
306
|
end
|
|
@@ -295,8 +317,11 @@ module OpenC3
|
|
|
295
317
|
|
|
296
318
|
def respawn_dead
|
|
297
319
|
@mutex.synchronize do
|
|
298
|
-
@processes.each do |
|
|
320
|
+
@processes.each do |name, p|
|
|
299
321
|
break if @shutdown
|
|
322
|
+
# Skip processes still queued by the per-cycle start limit; they
|
|
323
|
+
# haven't been started yet so they aren't "dead" to be respawned.
|
|
324
|
+
next if @new_processes[name]
|
|
300
325
|
p.output_increment
|
|
301
326
|
unless p.alive?
|
|
302
327
|
# Respawn process
|
|
@@ -437,12 +437,25 @@ module OpenC3
|
|
|
437
437
|
if packet.id_items.length > 0
|
|
438
438
|
key = []
|
|
439
439
|
id_signature = ""
|
|
440
|
-
# Accessor class
|
|
441
|
-
# with different accessors
|
|
442
|
-
#
|
|
440
|
+
# Accessor class AND args are part of the signature so packets in the
|
|
441
|
+
# same target with different accessors -- or the same accessor class
|
|
442
|
+
# configured with different args -- trigger unique_id_mode. Different
|
|
443
|
+
# accessors (or same accessor, different args) decode the buffer
|
|
444
|
+
# differently, so the shared hash-lookup path is unsafe.
|
|
443
445
|
packet.id_items.each do |item|
|
|
444
446
|
key << item.id_value
|
|
445
|
-
id_signature << "__#{item.key}__#{item.bit_offset}__#{item.bit_size}__#{item.data_type}__#{packet.accessor.class.to_s}"
|
|
447
|
+
id_signature << "__#{item.key}__#{item.bit_offset}__#{item.bit_size}__#{item.data_type}__#{packet.accessor.class.to_s}__#{packet.accessor.args.inspect}"
|
|
448
|
+
# STRUCTURE-derived id_items are decoded by the parent's structure
|
|
449
|
+
# accessor (see Accessor#read_item), so include that accessor's class
|
|
450
|
+
# and args in the signature too.
|
|
451
|
+
if item.parent_item
|
|
452
|
+
parent = packet.get_item(item.parent_item)
|
|
453
|
+
structure = parent.structure
|
|
454
|
+
if structure
|
|
455
|
+
structure_accessor = structure.accessor
|
|
456
|
+
id_signature << "__#{structure_accessor.class.to_s}__#{structure_accessor.args.inspect}"
|
|
457
|
+
end
|
|
458
|
+
end
|
|
446
459
|
end
|
|
447
460
|
target_id_value_hash[key] = packet
|
|
448
461
|
target_id_signature = id_signature_hash[packet.target_name]
|
data/lib/openc3/script/suite.rb
CHANGED
|
@@ -287,7 +287,7 @@ module OpenC3
|
|
|
287
287
|
# Find all the script methods
|
|
288
288
|
methods = []
|
|
289
289
|
self.instance_methods.each do |method_name|
|
|
290
|
-
if /^
|
|
290
|
+
if /^test_|^script_|^op_/.match?(method_name.to_s)
|
|
291
291
|
methods << method_name.to_s
|
|
292
292
|
end
|
|
293
293
|
end
|
|
@@ -91,6 +91,10 @@ module OpenC3
|
|
|
91
91
|
# Will subscribe to the channel based on @identifier
|
|
92
92
|
def subscribe
|
|
93
93
|
unless @subscribed
|
|
94
|
+
# Token is part of the identifier so it surfaces as params[:token] in
|
|
95
|
+
# ApplicationCable::Channel#authenticate_subscription! — ActionCable
|
|
96
|
+
# ignores `data` on `subscribe` commands.
|
|
97
|
+
@identifier['token'] = @authentication.token(include_bearer: false)
|
|
94
98
|
json_hash = {}
|
|
95
99
|
json_hash['command'] = 'subscribe'
|
|
96
100
|
json_hash['identifier'] = JSON.generate(@identifier, allow_nan: true)
|
|
@@ -128,7 +132,7 @@ module OpenC3
|
|
|
128
132
|
# Connect to the websocket with authorization in query params
|
|
129
133
|
def connect
|
|
130
134
|
disconnect()
|
|
131
|
-
final_url = @url + "?scope=#{@scope}
|
|
135
|
+
final_url = @url + "?scope=#{@scope}"
|
|
132
136
|
@stream = WebSocketClientStream.new(final_url, @write_timeout, @read_timeout, @connect_timeout)
|
|
133
137
|
@stream.headers = {
|
|
134
138
|
'Sec-WebSocket-Protocol' => 'actioncable-v1-json, actioncable-unsupported',
|