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.
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)