openc3 5.1.1 → 5.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/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)
|