openc3 5.1.1 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (97) hide show
  1. checksums.yaml +4 -4
  2. data/bin/openc3cli +48 -9
  3. data/data/config/interface_modifiers.yaml +14 -0
  4. data/data/config/parameter_modifiers.yaml +5 -3
  5. data/data/config/screen.yaml +12 -8
  6. data/data/config/target.yaml +33 -0
  7. data/ext/openc3/ext/config_parser/config_parser.c +66 -63
  8. data/ext/openc3/ext/packet/packet.c +1 -4
  9. data/lib/openc3/api/README.md +5 -0
  10. data/lib/openc3/api/api.rb +3 -1
  11. data/lib/openc3/api/cmd_api.rb +43 -112
  12. data/lib/openc3/api/interface_api.rb +3 -3
  13. data/lib/openc3/api/offline_access_api.rb +78 -0
  14. data/lib/openc3/api/settings_api.rb +3 -1
  15. data/lib/openc3/api/stash_api.rb +63 -0
  16. data/lib/openc3/api/target_api.rb +4 -5
  17. data/lib/openc3/config/config_parser.rb +47 -47
  18. data/lib/openc3/interfaces/interface.rb +11 -1
  19. data/lib/openc3/interfaces/protocols/burst_protocol.rb +30 -16
  20. data/lib/openc3/interfaces/protocols/fixed_protocol.rb +8 -2
  21. data/lib/openc3/interfaces/protocols/ignore_packet_protocol.rb +2 -2
  22. data/lib/openc3/interfaces/protocols/override_protocol.rb +2 -2
  23. data/lib/openc3/interfaces/tcpip_server_interface.rb +3 -1
  24. data/lib/openc3/io/json_api_object.rb +30 -9
  25. data/lib/openc3/io/json_drb.rb +6 -1
  26. data/lib/openc3/io/json_drb_object.rb +18 -9
  27. data/lib/openc3/io/json_rpc.rb +5 -3
  28. data/lib/openc3/logs/buffered_packet_log_writer.rb +1 -1
  29. data/lib/openc3/logs/log_writer.rb +8 -2
  30. data/lib/openc3/microservices/cleanup_microservice.rb +3 -3
  31. data/lib/openc3/microservices/decom_microservice.rb +8 -8
  32. data/lib/openc3/microservices/interface_microservice.rb +86 -71
  33. data/lib/openc3/microservices/log_microservice.rb +5 -3
  34. data/lib/openc3/microservices/microservice.rb +18 -14
  35. data/lib/openc3/microservices/multi_microservice.rb +62 -0
  36. data/lib/openc3/microservices/periodic_microservice.rb +58 -0
  37. data/lib/openc3/microservices/reaction_microservice.rb +61 -47
  38. data/lib/openc3/microservices/reducer_microservice.rb +64 -40
  39. data/lib/openc3/microservices/router_microservice.rb +4 -4
  40. data/lib/openc3/microservices/text_log_microservice.rb +2 -2
  41. data/lib/openc3/microservices/timeline_microservice.rb +44 -30
  42. data/lib/openc3/microservices/trigger_group_microservice.rb +39 -36
  43. data/lib/openc3/migrations/20221202214600_add_target_names.rb +30 -0
  44. data/lib/openc3/migrations/20221210174900_convert_to_multi.rb +65 -0
  45. data/lib/openc3/models/cvt_model.rb +1 -1
  46. data/lib/openc3/models/gem_model.rb +24 -20
  47. data/lib/openc3/models/interface_model.rb +69 -35
  48. data/lib/openc3/models/metadata_model.rb +1 -1
  49. data/lib/openc3/models/microservice_model.rb +7 -24
  50. data/lib/openc3/models/migration_model.rb +52 -0
  51. data/lib/openc3/models/model.rb +2 -7
  52. data/lib/openc3/models/note_model.rb +1 -1
  53. data/lib/openc3/models/offline_access_model.rb +55 -0
  54. data/lib/openc3/models/plugin_model.rb +12 -3
  55. data/lib/openc3/models/reaction_model.rb +6 -2
  56. data/lib/openc3/models/scope_model.rb +89 -13
  57. data/lib/openc3/models/settings_model.rb +1 -1
  58. data/lib/openc3/models/stash_model.rb +53 -0
  59. data/lib/openc3/models/target_model.rb +301 -130
  60. data/lib/openc3/models/tool_model.rb +1 -12
  61. data/lib/openc3/models/widget_model.rb +1 -6
  62. data/lib/openc3/operators/microservice_operator.rb +45 -6
  63. data/lib/openc3/operators/operator.rb +27 -5
  64. data/lib/openc3/packets/commands.rb +1 -25
  65. data/lib/openc3/packets/limits.rb +0 -75
  66. data/lib/openc3/packets/packet.rb +0 -28
  67. data/lib/openc3/packets/packet_item.rb +23 -0
  68. data/lib/openc3/packets/packet_item_limits.rb +2 -2
  69. data/lib/openc3/packets/parsers/state_parser.rb +10 -6
  70. data/lib/openc3/packets/telemetry.rb +1 -45
  71. data/lib/openc3/script/commands.rb +41 -71
  72. data/lib/openc3/script/extract.rb +15 -1
  73. data/lib/openc3/script/{calendar.rb → metadata.rb} +42 -17
  74. data/lib/openc3/script/script.rb +13 -5
  75. data/lib/openc3/script/storage.rb +3 -1
  76. data/lib/openc3/system/system.rb +19 -17
  77. data/lib/openc3/tools/cmd_tlm_server/interface_thread.rb +4 -4
  78. data/lib/openc3/top_level.rb +3 -3
  79. data/lib/openc3/topics/command_decom_topic.rb +2 -2
  80. data/lib/openc3/topics/command_topic.rb +7 -6
  81. data/lib/openc3/topics/interface_topic.rb +2 -2
  82. data/lib/openc3/topics/router_topic.rb +1 -1
  83. data/lib/openc3/topics/telemetry_topic.rb +2 -1
  84. data/lib/openc3/utilities/authentication.rb +35 -14
  85. data/lib/openc3/utilities/aws_bucket.rb +4 -3
  86. data/lib/openc3/utilities/bucket.rb +4 -2
  87. data/lib/openc3/utilities/bucket_file_cache.rb +3 -8
  88. data/lib/openc3/utilities/bucket_utilities.rb +77 -15
  89. data/lib/openc3/utilities/local_mode.rb +12 -9
  90. data/lib/openc3/utilities/logger.rb +17 -9
  91. data/lib/openc3/utilities/message_log.rb +6 -5
  92. data/lib/openc3/utilities/migration.rb +22 -0
  93. data/lib/openc3/utilities/store_autoload.rb +7 -5
  94. data/lib/openc3/utilities/target_file.rb +9 -7
  95. data/lib/openc3/version.rb +6 -6
  96. data/lib/openc3.rb +2 -1
  97. 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
- # generate the auth object
46
- def generate_auth
47
- if ENV['OPENC3_API_USER'].nil? || ENV['OPENC3_API_CLIENT'].nil?
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
- return OpenC3KeycloakAuthentication.new(ENV['OPENC3_KEYCLOAK_URL'])
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
- Logger.info "#{@timeline_name} timeline worker running"
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
- Logger.info "#{@timeline_name} timeine worker exiting"
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
- Logger.error "Unknown kind passed to microservice #{@timeline_name}: #{activity.as_json(:allow_nan => true)}"
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
- Logger.info "#{@timeline_name} run_command > #{activity.as_json(:allow_nan => true)}"
86
+ @logger.info "#{@timeline_name} run_command > #{activity.as_json(:allow_nan => true)}"
80
87
  begin
81
- cmd_no_hazardous_check(activity.data['command'], scope: @scope)
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
- Logger.error "#{@timeline_name} run_cmd failed > #{activity.as_json(:allow_nan => true)}, #{e.message}"
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
- Logger.info "#{@timeline_name} run_script > #{activity.as_json(:allow_nan => true)}"
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' => @authentication.token()
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
- Logger.error "#{@timeline_name} run_script failed > #{activity.as_json(:allow_nan => true).to_s}, #{e.message}"
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
- Logger.error "#{@timeline_name} clear_expired failed > #{activity.as_json(:allow_nan => true)} #{e.message}"
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
- Logger.info "#{@timeline_name} timeline manager running"
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
- Logger.debug "#{@timeline_name} #{@scope} current start: #{start}, vs #{activity.start}, #{start_difference}"
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
- Logger.info "#{@timeline_name} timeine manager exiting"
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
- Logger.error "#{@name} manager failed to request update"
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
- Logger.info "#{@name} timeine running"
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
- Logger.info "#{@name} timeine exitting"
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
- Logger.error "#{@timeline_name} failed to read topics #{@topics}\n#{e.formatted}"
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
- Logger.debug "#{@name} timeline web socket event: #{data}"
344
+ @logger.debug "#{@name} timeline web socket event: #{data}"
331
345
  end
332
346
 
333
347
  def schedule_refresh(data)
334
- Logger.debug "#{@name} timeline web socket schedule refresh: #{data}"
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
- Logger.info "TriggerGroupWorker-#{@ident} running"
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
- Logger.error "TriggerGroupWorker-#{@ident} failed to evaluate data packet from topic: #{topic}\n#{e.formatted}"
286
+ @logger.error "TriggerGroupWorker-#{@ident} failed to evaluate data packet from topic: #{topic}\n#{e.formatted}"
286
287
  end
287
288
  end
288
- Logger.info "TriggerGroupWorker-#{@ident} exiting"
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
- Logger.debug "TriggerGroupWorker-#{@ident} topic: #{topic}"
305
+ @logger.debug "TriggerGroupWorker-#{@ident} topic: #{topic}"
305
306
  triggers_to_eval = @share.trigger_base.get_triggers(topic: topic)
306
- Logger.debug "TriggerGroupWorker-#{@ident} triggers_to_eval: #{triggers_to_eval}"
307
+ @logger.debug "TriggerGroupWorker-#{@ident} triggers_to_eval: #{triggers_to_eval}"
307
308
  triggers_to_eval.each do | trigger |
308
- Logger.debug "TriggerGroupWorker-#{@ident} eval head: #{trigger}"
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
- Logger.debug "TriggerGroupWorker-#{@ident} trigger: #{trigger} value: #{value}"
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
- Logger.debug "TriggerGroupWorker-#{@ident} evaluate: (#{left} #{operator} #{right})"
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
- Logger.error "invalid evaluate: (#{left} #{operator} #{right})"
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
- Logger.error "loop detected from #{head} -> #{trigger} path: #{visited["#{head.name}__P"]}"
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
- Logger.error "loop detected from #{head} -> #{root_trigger} path: #{visited["#{head.name}__P"]}"
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
- Logger.debug "TriggerGroupWorker-#{@ident} #{root_trigger.name} result: #{result}"
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
- Logger.info "TriggerGroupManager running"
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
- Logger.error "TriggerGroupManager failed to update topics.\n#{e.formatted}"
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
- Logger.info "TriggerGroupManager exiting"
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
- Logger.debug "TriggerGroupManager past_topics: #{past_topics} topics: #{@topics}"
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
- Logger.debug "TriggerGroupManager block_for_updates: #{topic} #{msg_hash.to_s}"
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
- Logger.error "TriggerGroupManager failed to read topics #{@topics}\n#{e.formatted}"
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
- Logger.info "TriggerGroupMicroservice running"
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
- Logger.info "TriggerGroupMicroservice exiting"
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
- Logger.debug "TriggerGroupMicroservice block_for_updates: #{msg_hash.to_s}"
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
- Logger.error "TriggerGroupMicroservice failed to read topics #{@topics}\n#{e.formatted}"
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
- Logger.debug "TriggerGroupMicroservice web socket event: #{data}"
610
+ @logger.debug "TriggerGroupMicroservice web socket event: #{data}"
608
611
  end
609
612
 
610
613
  def refresh_event(data)
611
- Logger.debug "TriggerGroupMicroservice web socket schedule refresh: #{data}"
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
- Logger.debug "TriggerGroupMicroservice created_trigger_event #{data}"
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
- Logger.debug "TriggerGroupMicroservice deleted_trigger_event #{data}"
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
- if File.exist?(name_or_path)
71
- gem_file_path = name_or_path
72
- else
73
- gem_file_path = get(name_or_path)
74
- end
75
- begin
76
- rubygems_url = get_setting('rubygems_url', scope: scope)
77
- rescue
78
- # If Redis isn't running try the ENV, then simply rubygems.org
79
- rubygems_url = ENV['RUBYGEMS_URL']
80
- rubygems_url ||= 'https://rubygems.org'
81
- end
82
- Gem.sources = [rubygems_url] if rubygems_url
83
- Gem.done_installing_hooks.clear
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)