openc3 5.1.1 → 5.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/openc3cli +48 -9
- data/data/config/interface_modifiers.yaml +14 -0
- data/data/config/parameter_modifiers.yaml +5 -3
- data/data/config/screen.yaml +12 -8
- data/data/config/target.yaml +33 -0
- data/ext/openc3/ext/config_parser/config_parser.c +66 -63
- data/ext/openc3/ext/packet/packet.c +1 -4
- data/lib/openc3/api/README.md +5 -0
- data/lib/openc3/api/api.rb +3 -1
- data/lib/openc3/api/cmd_api.rb +43 -112
- data/lib/openc3/api/interface_api.rb +3 -3
- data/lib/openc3/api/offline_access_api.rb +78 -0
- data/lib/openc3/api/settings_api.rb +3 -1
- data/lib/openc3/api/stash_api.rb +63 -0
- data/lib/openc3/api/target_api.rb +4 -5
- data/lib/openc3/config/config_parser.rb +47 -47
- data/lib/openc3/interfaces/interface.rb +11 -1
- data/lib/openc3/interfaces/protocols/burst_protocol.rb +30 -16
- data/lib/openc3/interfaces/protocols/fixed_protocol.rb +8 -2
- data/lib/openc3/interfaces/protocols/ignore_packet_protocol.rb +2 -2
- data/lib/openc3/interfaces/protocols/override_protocol.rb +2 -2
- data/lib/openc3/interfaces/tcpip_server_interface.rb +3 -1
- data/lib/openc3/io/json_api_object.rb +30 -9
- data/lib/openc3/io/json_drb.rb +6 -1
- data/lib/openc3/io/json_drb_object.rb +18 -9
- data/lib/openc3/io/json_rpc.rb +5 -3
- data/lib/openc3/logs/buffered_packet_log_writer.rb +1 -1
- data/lib/openc3/logs/log_writer.rb +8 -2
- data/lib/openc3/microservices/cleanup_microservice.rb +3 -3
- data/lib/openc3/microservices/decom_microservice.rb +8 -8
- data/lib/openc3/microservices/interface_microservice.rb +86 -71
- data/lib/openc3/microservices/log_microservice.rb +5 -3
- data/lib/openc3/microservices/microservice.rb +18 -14
- data/lib/openc3/microservices/multi_microservice.rb +62 -0
- data/lib/openc3/microservices/periodic_microservice.rb +58 -0
- data/lib/openc3/microservices/reaction_microservice.rb +61 -47
- data/lib/openc3/microservices/reducer_microservice.rb +64 -40
- data/lib/openc3/microservices/router_microservice.rb +4 -4
- data/lib/openc3/microservices/text_log_microservice.rb +2 -2
- data/lib/openc3/microservices/timeline_microservice.rb +44 -30
- data/lib/openc3/microservices/trigger_group_microservice.rb +39 -36
- data/lib/openc3/migrations/20221202214600_add_target_names.rb +30 -0
- data/lib/openc3/migrations/20221210174900_convert_to_multi.rb +65 -0
- data/lib/openc3/models/cvt_model.rb +1 -1
- data/lib/openc3/models/gem_model.rb +24 -20
- data/lib/openc3/models/interface_model.rb +69 -35
- data/lib/openc3/models/metadata_model.rb +1 -1
- data/lib/openc3/models/microservice_model.rb +7 -24
- data/lib/openc3/models/migration_model.rb +52 -0
- data/lib/openc3/models/model.rb +2 -7
- data/lib/openc3/models/note_model.rb +1 -1
- data/lib/openc3/models/offline_access_model.rb +55 -0
- data/lib/openc3/models/plugin_model.rb +12 -3
- data/lib/openc3/models/reaction_model.rb +6 -2
- data/lib/openc3/models/scope_model.rb +89 -13
- data/lib/openc3/models/settings_model.rb +1 -1
- data/lib/openc3/models/stash_model.rb +53 -0
- data/lib/openc3/models/target_model.rb +301 -130
- data/lib/openc3/models/tool_model.rb +1 -12
- data/lib/openc3/models/widget_model.rb +1 -6
- data/lib/openc3/operators/microservice_operator.rb +45 -6
- data/lib/openc3/operators/operator.rb +27 -5
- data/lib/openc3/packets/commands.rb +1 -25
- data/lib/openc3/packets/limits.rb +0 -75
- data/lib/openc3/packets/packet.rb +0 -28
- data/lib/openc3/packets/packet_item.rb +23 -0
- data/lib/openc3/packets/packet_item_limits.rb +2 -2
- data/lib/openc3/packets/parsers/state_parser.rb +10 -6
- data/lib/openc3/packets/telemetry.rb +1 -45
- data/lib/openc3/script/commands.rb +41 -71
- data/lib/openc3/script/extract.rb +15 -1
- data/lib/openc3/script/{calendar.rb → metadata.rb} +42 -17
- data/lib/openc3/script/script.rb +13 -5
- data/lib/openc3/script/storage.rb +3 -1
- data/lib/openc3/system/system.rb +19 -17
- data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +4 -4
- data/lib/openc3/top_level.rb +3 -3
- data/lib/openc3/topics/command_decom_topic.rb +2 -2
- data/lib/openc3/topics/command_topic.rb +7 -6
- data/lib/openc3/topics/interface_topic.rb +2 -2
- data/lib/openc3/topics/router_topic.rb +1 -1
- data/lib/openc3/topics/telemetry_topic.rb +2 -1
- data/lib/openc3/utilities/authentication.rb +35 -14
- data/lib/openc3/utilities/aws_bucket.rb +4 -3
- data/lib/openc3/utilities/bucket.rb +4 -2
- data/lib/openc3/utilities/bucket_file_cache.rb +3 -8
- data/lib/openc3/utilities/bucket_utilities.rb +77 -15
- data/lib/openc3/utilities/local_mode.rb +12 -9
- data/lib/openc3/utilities/logger.rb +17 -9
- data/lib/openc3/utilities/message_log.rb +6 -5
- data/lib/openc3/utilities/migration.rb +22 -0
- data/lib/openc3/utilities/store_autoload.rb +7 -5
- data/lib/openc3/utilities/target_file.rb +9 -7
- data/lib/openc3/version.rb +6 -6
- data/lib/openc3.rb +2 -1
- metadata +14 -3
@@ -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/utilities/authentication'
|
@@ -35,31 +35,38 @@ module OpenC3
|
|
35
35
|
# these workers will run the CMD (command) or SCRIPT (script)
|
36
36
|
# or anything that could be expanded in the future.
|
37
37
|
class TimelineWorker
|
38
|
-
def initialize(name:, scope:, queue:)
|
38
|
+
def initialize(name:, logger:, scope:, queue:)
|
39
39
|
@timeline_name = name
|
40
|
+
@logger = logger
|
40
41
|
@scope = scope
|
41
42
|
@queue = queue
|
42
|
-
@authentication = generate_auth()
|
43
43
|
end
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
return OpenC3Authentication.new()
|
45
|
+
def get_token(username)
|
46
|
+
if ENV['OPENC3_API_CLIENT'].nil?
|
47
|
+
return OpenC3Authentication.new().token
|
49
48
|
else
|
50
|
-
|
49
|
+
# Check for offline access token
|
50
|
+
model = nil
|
51
|
+
model = OpenC3::OfflineAccessModel.get_model(name: username, scope: @scope) if username and username != ''
|
52
|
+
if model and model.offline_access_token
|
53
|
+
auth = OpenC3KeycloakAuthentication.new(ENV['OPENC3_KEYCLOAK_URL'])
|
54
|
+
return auth.get_token_from_refresh_token(model.offline_access_token)
|
55
|
+
else
|
56
|
+
return nil
|
57
|
+
end
|
51
58
|
end
|
52
59
|
end
|
53
60
|
|
54
61
|
def run
|
55
|
-
|
62
|
+
@logger.info "#{@timeline_name} timeline worker running"
|
56
63
|
loop do
|
57
64
|
activity = @queue.pop
|
58
65
|
break if activity.nil?
|
59
66
|
|
60
67
|
run_activity(activity)
|
61
68
|
end
|
62
|
-
|
69
|
+
@logger.info "#{@timeline_name} timeine worker exiting"
|
63
70
|
end
|
64
71
|
|
65
72
|
def run_activity(activity)
|
@@ -71,28 +78,34 @@ module OpenC3
|
|
71
78
|
when 'EXPIRE'
|
72
79
|
clear_expired(activity)
|
73
80
|
else
|
74
|
-
|
81
|
+
@logger.error "Unknown kind passed to microservice #{@timeline_name}: #{activity.as_json(:allow_nan => true)}"
|
75
82
|
end
|
76
83
|
end
|
77
84
|
|
78
85
|
def run_command(activity)
|
79
|
-
|
86
|
+
@logger.info "#{@timeline_name} run_command > #{activity.as_json(:allow_nan => true)}"
|
80
87
|
begin
|
81
|
-
|
88
|
+
username = activity.data['username']
|
89
|
+
token = get_token(username)
|
90
|
+
raise "No token available for username: #{username}" unless token
|
91
|
+
cmd_no_hazardous_check(activity.data['command'], scope: @scope, token: token)
|
82
92
|
activity.commit(status: 'completed', fulfillment: true)
|
83
93
|
rescue StandardError => e
|
84
94
|
activity.commit(status: 'failed', message: e.message)
|
85
|
-
|
95
|
+
@logger.error "#{@timeline_name} run_cmd failed > #{activity.as_json(:allow_nan => true)}, #{e.formatted}"
|
86
96
|
end
|
87
97
|
end
|
88
98
|
|
89
99
|
def run_script(activity)
|
90
|
-
|
100
|
+
@logger.info "#{@timeline_name} run_script > #{activity.as_json(:allow_nan => true)}"
|
91
101
|
begin
|
102
|
+
username = activity.data['username']
|
103
|
+
token = get_token(username)
|
104
|
+
raise "No token available for username: #{username}" unless token
|
92
105
|
request = Net::HTTP::Post.new(
|
93
106
|
"/script-api/scripts/#{activity.data['script']}/run?scope=#{@scope}",
|
94
107
|
'Content-Type' => 'application/json',
|
95
|
-
'Authorization' =>
|
108
|
+
'Authorization' => token
|
96
109
|
)
|
97
110
|
request.body = JSON.generate({
|
98
111
|
'scope' => @scope,
|
@@ -107,7 +120,7 @@ module OpenC3
|
|
107
120
|
activity.commit(status: 'completed', message: "#{activity.data['script']} => #{response.body}", fulfillment: true)
|
108
121
|
rescue StandardError => e
|
109
122
|
activity.commit(status: 'failed', message: e.message)
|
110
|
-
|
123
|
+
@logger.error "#{@timeline_name} run_script failed > #{activity.as_json(:allow_nan => true).to_s}, #{e.message}"
|
111
124
|
end
|
112
125
|
end
|
113
126
|
|
@@ -116,7 +129,7 @@ module OpenC3
|
|
116
129
|
ActivityModel.range_destroy(name: @timeline_name, scope: @scope, min: activity.start, max: activity.stop)
|
117
130
|
activity.add_event(status: 'completed')
|
118
131
|
rescue StandardError => e
|
119
|
-
|
132
|
+
@logger.error "#{@timeline_name} clear_expired failed > #{activity.as_json(:allow_nan => true)} #{e.message}"
|
120
133
|
end
|
121
134
|
end
|
122
135
|
end
|
@@ -174,8 +187,9 @@ module OpenC3
|
|
174
187
|
# adds the "activity" to the thread pool and the thread will
|
175
188
|
# execute the "activity".
|
176
189
|
class TimelineManager
|
177
|
-
def initialize(name:, scope:, schedule:)
|
190
|
+
def initialize(name:, logger:, scope:, schedule:)
|
178
191
|
@timeline_name = name
|
192
|
+
@logger = logger
|
179
193
|
@scope = scope
|
180
194
|
@schedule = schedule
|
181
195
|
@worker_count = 3
|
@@ -188,20 +202,20 @@ module OpenC3
|
|
188
202
|
def generate_thread_pool
|
189
203
|
thread_pool = []
|
190
204
|
@worker_count.times {
|
191
|
-
worker = TimelineWorker.new(name: @timeline_name, scope: @scope, queue: @queue)
|
205
|
+
worker = TimelineWorker.new(name: @timeline_name, logger: @logger, scope: @scope, queue: @queue)
|
192
206
|
thread_pool << Thread.new { worker.run }
|
193
207
|
}
|
194
208
|
return thread_pool
|
195
209
|
end
|
196
210
|
|
197
211
|
def run
|
198
|
-
|
212
|
+
@logger.info "#{@timeline_name} timeline manager running"
|
199
213
|
loop do
|
200
214
|
start = Time.now.to_i
|
201
215
|
@schedule.activities.each do |activity|
|
202
216
|
start_difference = activity.start - start
|
203
217
|
if start_difference <= 0 && @schedule.not_queued?(activity.start)
|
204
|
-
|
218
|
+
@logger.debug "#{@timeline_name} #{@scope} current start: #{start}, vs #{activity.start}, #{start_difference}"
|
205
219
|
activity.add_event(status: 'queued')
|
206
220
|
@queue << activity
|
207
221
|
end
|
@@ -215,7 +229,7 @@ module OpenC3
|
|
215
229
|
sleep(1)
|
216
230
|
break if @cancel_thread
|
217
231
|
end
|
218
|
-
|
232
|
+
@logger.info "#{@timeline_name} timeine manager exiting"
|
219
233
|
end
|
220
234
|
|
221
235
|
# Add task to remove events older than 7 time
|
@@ -247,7 +261,7 @@ module OpenC3
|
|
247
261
|
begin
|
248
262
|
TimelineTopic.write_activity(notification, scope: @scope)
|
249
263
|
rescue StandardError
|
250
|
-
|
264
|
+
@logger.error "#{@name} manager failed to request update"
|
251
265
|
end
|
252
266
|
end
|
253
267
|
|
@@ -270,13 +284,13 @@ module OpenC3
|
|
270
284
|
super(name)
|
271
285
|
@timeline_name = name.split('__')[2]
|
272
286
|
@schedule = Schedule.new(@timeline_name)
|
273
|
-
@manager = TimelineManager.new(name: @timeline_name, scope: scope, schedule: @schedule)
|
287
|
+
@manager = TimelineManager.new(name: @timeline_name, logger: @logger, scope: scope, schedule: @schedule)
|
274
288
|
@manager_thread = nil
|
275
289
|
@read_topic = true
|
276
290
|
end
|
277
291
|
|
278
292
|
def run
|
279
|
-
|
293
|
+
@logger.info "#{@name} timeine running"
|
280
294
|
@manager_thread = Thread.new { @manager.run }
|
281
295
|
loop do
|
282
296
|
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
@@ -290,7 +304,7 @@ module OpenC3
|
|
290
304
|
block_for_updates()
|
291
305
|
break if @cancel_thread
|
292
306
|
end
|
293
|
-
|
307
|
+
@logger.info "#{@name} timeine exitting"
|
294
308
|
end
|
295
309
|
|
296
310
|
def topic_lookup_functions
|
@@ -321,17 +335,17 @@ module OpenC3
|
|
321
335
|
end
|
322
336
|
end
|
323
337
|
rescue StandardError => e
|
324
|
-
|
338
|
+
@logger.error "#{@timeline_name} failed to read topics #{@topics}\n#{e.formatted}"
|
325
339
|
end
|
326
340
|
end
|
327
341
|
end
|
328
342
|
|
329
343
|
def timeline_nop(data)
|
330
|
-
|
344
|
+
@logger.debug "#{@name} timeline web socket event: #{data}"
|
331
345
|
end
|
332
346
|
|
333
347
|
def schedule_refresh(data)
|
334
|
-
|
348
|
+
@logger.debug "#{@name} timeline web socket schedule refresh: #{data}"
|
335
349
|
@read_topic = false
|
336
350
|
end
|
337
351
|
|
@@ -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/microservices/microservice'
|
@@ -194,7 +194,7 @@ module OpenC3
|
|
194
194
|
@triggers[trigger['name']] = Marshal.load( Marshal.dump(trigger) )
|
195
195
|
end
|
196
196
|
t = TriggerModel.from_json(trigger, name: trigger['name'], scope: trigger['scope'])
|
197
|
-
@lookup_mutex.synchronize do
|
197
|
+
@lookup_mutex.synchronize do
|
198
198
|
t.generate_topics.each do | topic |
|
199
199
|
if @lookup[topic].nil?
|
200
200
|
@lookup[topic] = { t.name => 1 }
|
@@ -211,7 +211,7 @@ module OpenC3
|
|
211
211
|
@triggers.delete(trigger['name'])
|
212
212
|
end
|
213
213
|
t = TriggerModel.from_json(trigger, name: trigger['name'], scope: trigger['scope'])
|
214
|
-
@lookup_mutex.synchronize do
|
214
|
+
@lookup_mutex.synchronize do
|
215
215
|
t.generate_topics.each do | topic |
|
216
216
|
unless @lookup[topic].nil?
|
217
217
|
@lookup[topic].delete(t.name)
|
@@ -258,8 +258,9 @@ module OpenC3
|
|
258
258
|
|
259
259
|
attr_reader :name, :scope, :target, :packet, :group
|
260
260
|
|
261
|
-
def initialize(name:, scope:, group:, queue:, share:, ident:)
|
261
|
+
def initialize(name:, logger:, scope:, group:, queue:, share:, ident:)
|
262
262
|
@name = name
|
263
|
+
@logger = logger
|
263
264
|
@scope = scope
|
264
265
|
@group = group
|
265
266
|
@queue = queue
|
@@ -270,7 +271,7 @@ module OpenC3
|
|
270
271
|
end
|
271
272
|
|
272
273
|
def run
|
273
|
-
|
274
|
+
@logger.info "TriggerGroupWorker-#{@ident} running"
|
274
275
|
loop do
|
275
276
|
topic = @queue.pop
|
276
277
|
break if topic.nil?
|
@@ -282,10 +283,10 @@ module OpenC3
|
|
282
283
|
@metric_output_time = current_time + 120
|
283
284
|
end
|
284
285
|
rescue StandardError => e
|
285
|
-
|
286
|
+
@logger.error "TriggerGroupWorker-#{@ident} failed to evaluate data packet from topic: #{topic}\n#{e.formatted}"
|
286
287
|
end
|
287
288
|
end
|
288
|
-
|
289
|
+
@logger.info "TriggerGroupWorker-#{@ident} exiting"
|
289
290
|
end
|
290
291
|
|
291
292
|
# time how long each packet takes to eval and produce a metric to public
|
@@ -294,25 +295,25 @@ module OpenC3
|
|
294
295
|
evaluate_data_packet(topic: topic, triggers: @share.trigger_base.triggers)
|
295
296
|
diff = Process.clock_gettime(Process::CLOCK_MONOTONIC) - start # seconds as a float
|
296
297
|
metric_labels = { 'trigger_group' => @group, 'thread' => "worker-#{@ident}" }
|
297
|
-
@metric.add_sample(name: TRIGGER_METRIC_NAME, value: diff, labels: metric_labels)
|
298
|
+
@metric.add_sample(name: TRIGGER_METRIC_NAME, value: diff, labels: metric_labels)
|
298
299
|
end
|
299
300
|
|
300
301
|
# Each packet will be evaluated to all triggers and use the result to send
|
301
302
|
# the results back to the topic to be used by the reaction microservice.
|
302
303
|
def evaluate_data_packet(topic:, triggers:)
|
303
304
|
visited = Hash.new
|
304
|
-
|
305
|
+
@logger.debug "TriggerGroupWorker-#{@ident} topic: #{topic}"
|
305
306
|
triggers_to_eval = @share.trigger_base.get_triggers(topic: topic)
|
306
|
-
|
307
|
+
@logger.debug "TriggerGroupWorker-#{@ident} triggers_to_eval: #{triggers_to_eval}"
|
307
308
|
triggers_to_eval.each do | trigger |
|
308
|
-
|
309
|
+
@logger.debug "TriggerGroupWorker-#{@ident} eval head: #{trigger}"
|
309
310
|
value = evaluate_trigger(
|
310
311
|
head: trigger,
|
311
312
|
trigger: trigger,
|
312
313
|
visited: visited,
|
313
314
|
triggers: triggers
|
314
315
|
)
|
315
|
-
|
316
|
+
@logger.debug "TriggerGroupWorker-#{@ident} trigger: #{trigger} value: #{value}"
|
316
317
|
# value MUST be -1, 0, or 1
|
317
318
|
@share.trigger_base.update_state(name: trigger.name, value: value)
|
318
319
|
end
|
@@ -367,7 +368,7 @@ module OpenC3
|
|
367
368
|
# 1 (the value is considered as a true value)
|
368
369
|
#
|
369
370
|
def evaluate(left:, operator:, right:)
|
370
|
-
|
371
|
+
@logger.debug "TriggerGroupWorker-#{@ident} evaluate: (#{left} #{operator} #{right})"
|
371
372
|
begin
|
372
373
|
case operator
|
373
374
|
when '>'
|
@@ -388,7 +389,7 @@ module OpenC3
|
|
388
389
|
return left || right ? 1 : 0
|
389
390
|
end
|
390
391
|
rescue ArgumentError
|
391
|
-
|
392
|
+
@logger.error "invalid evaluate: (#{left} #{operator} #{right})"
|
392
393
|
return -1
|
393
394
|
end
|
394
395
|
end
|
@@ -407,21 +408,21 @@ module OpenC3
|
|
407
408
|
# IF a loop is detected it will log an error and return -1
|
408
409
|
def evaluate_trigger(head:, trigger:, visited:, triggers:)
|
409
410
|
if visited["#{trigger.name}__R"]
|
410
|
-
return visited["#{trigger.name}__R"]
|
411
|
+
return visited["#{trigger.name}__R"]
|
411
412
|
end
|
412
413
|
if visited["#{trigger.name}__P"].nil?
|
413
414
|
visited["#{trigger.name}__P"] = Hash.new
|
414
415
|
end
|
415
416
|
if visited["#{head.name}__P"][trigger.name]
|
416
417
|
# Not sure if this is posible as on create it validates that the dependents are already created
|
417
|
-
|
418
|
+
@logger.error "loop detected from #{head} -> #{trigger} path: #{visited["#{head.name}__P"]}"
|
418
419
|
return visited["#{trigger.name}__R"] = -1
|
419
420
|
end
|
420
421
|
trigger.roots.each do | root_trigger_name |
|
421
422
|
next if visited["#{root_trigger_name}__R"]
|
422
423
|
root_trigger = triggers[root_trigger_name]
|
423
424
|
if head.name == root_trigger.name
|
424
|
-
|
425
|
+
@logger.error "loop detected from #{head} -> #{root_trigger} path: #{visited["#{head.name}__P"]}"
|
425
426
|
return visited["#{trigger.name}__R"] = -1
|
426
427
|
end
|
427
428
|
result = evaluate_trigger(
|
@@ -430,7 +431,7 @@ module OpenC3
|
|
430
431
|
visited: visited,
|
431
432
|
triggers: triggers
|
432
433
|
)
|
433
|
-
|
434
|
+
@logger.debug "TriggerGroupWorker-#{@ident} #{root_trigger.name} result: #{result}"
|
434
435
|
visited["#{root_trigger.name}__R"] = visited["#{head.name}__P"][root_trigger.name] = result
|
435
436
|
end
|
436
437
|
left = operand_value(operand: trigger.left, other: trigger.right, visited: visited)
|
@@ -452,8 +453,9 @@ module OpenC3
|
|
452
453
|
|
453
454
|
attr_reader :name, :scope, :share, :group, :topics, :thread_pool
|
454
455
|
|
455
|
-
def initialize(name:, scope:, group:, share:)
|
456
|
+
def initialize(name:, logger:, scope:, group:, share:)
|
456
457
|
@name = name
|
458
|
+
@logger = logger
|
457
459
|
@scope = scope
|
458
460
|
@group = group
|
459
461
|
@share = share
|
@@ -470,6 +472,7 @@ module OpenC3
|
|
470
472
|
@worker_count.times do | i |
|
471
473
|
worker = TriggerGroupWorker.new(
|
472
474
|
name: @name,
|
475
|
+
logger: @logger,
|
473
476
|
scope: @scope,
|
474
477
|
group: @group,
|
475
478
|
queue: @queue,
|
@@ -482,26 +485,26 @@ module OpenC3
|
|
482
485
|
end
|
483
486
|
|
484
487
|
def run
|
485
|
-
|
488
|
+
@logger.info "TriggerGroupManager running"
|
486
489
|
@thread_pool = generate_thread_pool()
|
487
490
|
loop do
|
488
491
|
begin
|
489
492
|
update_topics()
|
490
493
|
rescue StandardError => e
|
491
|
-
|
494
|
+
@logger.error "TriggerGroupManager failed to update topics.\n#{e.formatted}"
|
492
495
|
end
|
493
496
|
break if @cancel_thread
|
494
497
|
|
495
498
|
block_for_updates()
|
496
499
|
break if @cancel_thread
|
497
500
|
end
|
498
|
-
|
501
|
+
@logger.info "TriggerGroupManager exiting"
|
499
502
|
end
|
500
503
|
|
501
504
|
def update_topics
|
502
505
|
past_topics = @topics
|
503
506
|
@topics = @share.trigger_base.topics()
|
504
|
-
|
507
|
+
@logger.debug "TriggerGroupManager past_topics: #{past_topics} topics: #{@topics}"
|
505
508
|
(past_topics - @topics).each do | removed_topic |
|
506
509
|
@share.packet_base.remove(topic: removed_topic)
|
507
510
|
end
|
@@ -512,7 +515,7 @@ module OpenC3
|
|
512
515
|
while @read_topic
|
513
516
|
begin
|
514
517
|
Topic.read_topics(@topics) do |topic, _msg_id, msg_hash, _redis|
|
515
|
-
|
518
|
+
@logger.debug "TriggerGroupManager block_for_updates: #{topic} #{msg_hash.to_s}"
|
516
519
|
if topic != @share.trigger_base.autonomic_topic
|
517
520
|
packet = JSON.parse(msg_hash['json_data'], :allow_nan => true, :create_additions => true)
|
518
521
|
@share.packet_base.add(topic: topic, packet: packet)
|
@@ -520,11 +523,11 @@ module OpenC3
|
|
520
523
|
@queue << "#{topic}"
|
521
524
|
end
|
522
525
|
rescue StandardError => e
|
523
|
-
|
526
|
+
@logger.error "TriggerGroupManager failed to read topics #{@topics}\n#{e.formatted}"
|
524
527
|
end
|
525
528
|
end
|
526
529
|
end
|
527
|
-
|
530
|
+
|
528
531
|
def refresh
|
529
532
|
@read_topic = false
|
530
533
|
end
|
@@ -551,13 +554,13 @@ module OpenC3
|
|
551
554
|
super(*args)
|
552
555
|
@group = TriggerGroupShare.get_group(name: @name)
|
553
556
|
@share = TriggerGroupShare.new(scope: @scope)
|
554
|
-
@manager = TriggerGroupManager.new(name: @name, scope: @scope, group: @group, share: @share)
|
557
|
+
@manager = TriggerGroupManager.new(name: @name, logger: @logger, scope: @scope, group: @group, share: @share)
|
555
558
|
@manager_thread = nil
|
556
559
|
@read_topic = true
|
557
560
|
end
|
558
561
|
|
559
562
|
def run
|
560
|
-
|
563
|
+
@logger.info "TriggerGroupMicroservice running"
|
561
564
|
@manager_thread = Thread.new { @manager.run }
|
562
565
|
loop do
|
563
566
|
start = Process.clock_gettime(Process::CLOCK_MONOTONIC)
|
@@ -571,7 +574,7 @@ module OpenC3
|
|
571
574
|
block_for_updates()
|
572
575
|
break if @cancel_thread
|
573
576
|
end
|
574
|
-
|
577
|
+
@logger.info "TriggerGroupMicroservice exiting"
|
575
578
|
end
|
576
579
|
|
577
580
|
def topic_lookup_functions
|
@@ -591,30 +594,30 @@ module OpenC3
|
|
591
594
|
while @read_topic
|
592
595
|
begin
|
593
596
|
AutonomicTopic.read_topics(@topics) do |_topic, _msg_id, msg_hash, _redis|
|
594
|
-
|
597
|
+
@logger.debug "TriggerGroupMicroservice block_for_updates: #{msg_hash.to_s}"
|
595
598
|
if msg_hash['type'] == 'trigger'
|
596
599
|
data = JSON.parse(msg_hash['data'], :allow_nan => true, :create_additions => true)
|
597
600
|
public_send(topic_lookup_functions[msg_hash['kind']], data)
|
598
601
|
end
|
599
602
|
end
|
600
603
|
rescue StandardError => e
|
601
|
-
|
604
|
+
@logger.error "TriggerGroupMicroservice failed to read topics #{@topics}\n#{e.formatted}"
|
602
605
|
end
|
603
606
|
end
|
604
607
|
end
|
605
608
|
|
606
609
|
def no_op(data)
|
607
|
-
|
610
|
+
@logger.debug "TriggerGroupMicroservice web socket event: #{data}"
|
608
611
|
end
|
609
612
|
|
610
613
|
def refresh_event(data)
|
611
|
-
|
614
|
+
@logger.debug "TriggerGroupMicroservice web socket schedule refresh: #{data}"
|
612
615
|
@read_topic = false
|
613
616
|
end
|
614
617
|
|
615
|
-
# Add the trigger to the share.
|
618
|
+
# Add the trigger to the share.
|
616
619
|
def created_trigger_event(data)
|
617
|
-
|
620
|
+
@logger.debug "TriggerGroupMicroservice created_trigger_event #{data}"
|
618
621
|
if data['group'] == @group
|
619
622
|
@share.trigger_base.add(trigger: data)
|
620
623
|
@manager.refresh()
|
@@ -623,7 +626,7 @@ module OpenC3
|
|
623
626
|
|
624
627
|
# Remove the trigger from the share.
|
625
628
|
def deleted_trigger_event(data)
|
626
|
-
|
629
|
+
@logger.debug "TriggerGroupMicroservice deleted_trigger_event #{data}"
|
627
630
|
if data['group'] == @group
|
628
631
|
@share.trigger_base.remove(trigger: data)
|
629
632
|
@manager.refresh()
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'openc3/utilities/migration'
|
2
|
+
require 'openc3/models/scope_model'
|
3
|
+
|
4
|
+
module OpenC3
|
5
|
+
class AddTargetNames < Migration
|
6
|
+
def self.run
|
7
|
+
ScopeModel.names.each do |scope|
|
8
|
+
# Get all existing InterfaceModels and add cmd_target_names / tlm_target_names if necessary
|
9
|
+
interface_models = InterfaceModel.all(scope: scope)
|
10
|
+
interface_models.each do |key, model_hash|
|
11
|
+
target_names = model_hash['target_names']
|
12
|
+
model_hash['cmd_target_names'] = target_names unless model_hash['cmd_target_names']
|
13
|
+
model_hash['tlm_target_names'] = target_names unless model_hash['tlm_target_names']
|
14
|
+
InterfaceModel.from_json(model_hash, scope: scope).update
|
15
|
+
end
|
16
|
+
router_models = RouterModel.all(scope: scope)
|
17
|
+
router_models.each do |key, model_hash|
|
18
|
+
target_names = model_hash['target_names']
|
19
|
+
model_hash['cmd_target_names'] = target_names unless model_hash['cmd_target_names']
|
20
|
+
model_hash['tlm_target_names'] = target_names unless model_hash['tlm_target_names']
|
21
|
+
RouterModel.from_json(model_hash, scope: scope).update
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
unless ENV['OPENC3_NO_MIGRATE']
|
29
|
+
OpenC3::AddTargetNames.run
|
30
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'openc3/utilities/migration'
|
2
|
+
require 'openc3/models/scope_model'
|
3
|
+
require 'openc3/models/target_model'
|
4
|
+
|
5
|
+
module OpenC3
|
6
|
+
class ConvertToMulti < Migration
|
7
|
+
def self.run
|
8
|
+
# Add parent to preexisting scope microservies and deploy new periodic and multi
|
9
|
+
ScopeModel.get_all_models(scope: nil).each do |scope, scope_model|
|
10
|
+
parent = "#{scope}__SCOPEMULTI__#{scope}"
|
11
|
+
model = MicroserviceModel.get_model(name: "#{scope}__OPENC3__LOG", scope: scope)
|
12
|
+
if model
|
13
|
+
model.parent = parent
|
14
|
+
model.update
|
15
|
+
scope_model.children << "#{scope}__OPENC3__LOG"
|
16
|
+
end
|
17
|
+
model = MicroserviceModel.get_model(name: "#{scope}__NOTIFICATION__LOG", scope: scope)
|
18
|
+
if model
|
19
|
+
model.parent = parent
|
20
|
+
model.update
|
21
|
+
scope_model.children << "#{scope}__NOTIFICATION__LOG"
|
22
|
+
end
|
23
|
+
model = MicroserviceModel.get_model(name: "#{scope}__COMMANDLOG__UNKNOWN", scope: scope)
|
24
|
+
if model
|
25
|
+
model.parent = parent
|
26
|
+
model.update
|
27
|
+
scope_model.children << "#{scope}__COMMANDLOG__UNKNOWN"
|
28
|
+
end
|
29
|
+
model = MicroserviceModel.get_model(name: "#{scope}__PACKETLOG__UNKNOWN", scope: scope)
|
30
|
+
if model
|
31
|
+
model.parent = parent
|
32
|
+
model.update
|
33
|
+
scope_model.children << "#{scope}__PACKETLOG__UNKNOWN"
|
34
|
+
end
|
35
|
+
scope_model.deploy_periodic_microservice("", {}, parent)
|
36
|
+
scope_model.deploy_scopemulti_microservice("", {})
|
37
|
+
|
38
|
+
# Add parent to preexisting target microservices and deploy new multi
|
39
|
+
TargetModel.get_all_models(scope: scope).each do |target_name, target_model|
|
40
|
+
next if target_name == 'UNKNOWN'
|
41
|
+
parent = "#{scope}__MULTI__#{target_name}"
|
42
|
+
%w(DECOM COMMANDLOG DECOMCMDLOG PACKETLOG DECOMLOG REDUCER CLEANUP).each do |type|
|
43
|
+
model = MicroserviceModel.get_model(name: "#{scope}__#{type}__#{target_name}", scope: scope)
|
44
|
+
if model
|
45
|
+
model.parent = parent
|
46
|
+
if %w(COMMANDLOG DECOMCMDLOG).include?(type)
|
47
|
+
model.options << ["BUFFER_DEPTH", 5]
|
48
|
+
end
|
49
|
+
if %w(PACKETLOG DECOMLOG).include?(type)
|
50
|
+
model.options << ["BUFFER_DEPTH", 60]
|
51
|
+
end
|
52
|
+
model.update
|
53
|
+
target_model.children << "#{scope}__#{type}__#{target_name}"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
target_model.deploy_multi_microservice("", {}, nil)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
unless ENV['OPENC3_NO_MIGRATE']
|
64
|
+
OpenC3::ConvertToMulti.run
|
65
|
+
end
|
@@ -93,7 +93,7 @@ module OpenC3
|
|
93
93
|
# @param stale_time [Integer] Time in seconds from Time.now that value will be marked stale
|
94
94
|
# @return [Array] Array of values
|
95
95
|
def self.get_tlm_values(items, stale_time: 30, scope: $openc3_scope)
|
96
|
-
now = Time.now.to_f
|
96
|
+
now = Time.now.sys.to_f
|
97
97
|
results = []
|
98
98
|
lookups = []
|
99
99
|
packet_lookup = {}
|
@@ -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 'fileutils'
|
@@ -66,27 +66,31 @@ module OpenC3
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def self.install(name_or_path, scope:)
|
69
|
+
if File.exist?(name_or_path)
|
70
|
+
gem_file_path = name_or_path
|
71
|
+
else
|
72
|
+
gem_file_path = get(name_or_path)
|
73
|
+
end
|
69
74
|
begin
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
Gem.
|
84
|
-
Gem.install(gem_file_path, "> 0.pre", :build_args => ['--no-document'], :prerelease => true)
|
85
|
-
rescue => err
|
86
|
-
message = "Gem file #{gem_file_path} error installing to #{ENV['GEM_HOME']}\n#{err.formatted}"
|
87
|
-
Logger.error message
|
88
|
-
raise err
|
75
|
+
rubygems_url = get_setting('rubygems_url', scope: scope)
|
76
|
+
rescue
|
77
|
+
# If Redis isn't running try the ENV, then simply rubygems.org
|
78
|
+
rubygems_url = ENV['RUBYGEMS_URL']
|
79
|
+
rubygems_url ||= 'https://rubygems.org'
|
80
|
+
end
|
81
|
+
Gem.sources = [rubygems_url] if rubygems_url
|
82
|
+
Gem.done_installing_hooks.clear
|
83
|
+
begin
|
84
|
+
# Look for local gems only first, this avoids lengthly timeouts when checking rubygems in airgap env
|
85
|
+
Gem.install(gem_file_path, "> 0.pre", build_args: ['--no-document'], prerelease: true, domain: :local)
|
86
|
+
rescue Gem::Exception => err
|
87
|
+
# If there is a failure look for both local and remote gems
|
88
|
+
Gem.install(gem_file_path, "> 0.pre", build_args: ['--no-document'], prerelease: true, domain: :both)
|
89
89
|
end
|
90
|
+
rescue => err
|
91
|
+
message = "Gem file #{gem_file_path} error installing to #{ENV['GEM_HOME']}\n#{err.formatted}"
|
92
|
+
Logger.error message
|
93
|
+
raise err
|
90
94
|
end
|
91
95
|
|
92
96
|
def self.destroy(name)
|