openc3 5.8.1 → 5.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (104) hide show
  1. checksums.yaml +4 -4
  2. data/ext/openc3/ext/crc/crc.c +1 -1
  3. data/lib/openc3/api/cmd_api.rb +1 -1
  4. data/lib/openc3/microservices/decom_microservice.rb +10 -2
  5. data/lib/openc3/microservices/reaction_microservice.rb +152 -81
  6. data/lib/openc3/microservices/timeline_microservice.rb +1 -1
  7. data/lib/openc3/microservices/trigger_group_microservice.rb +188 -118
  8. data/lib/openc3/migrations/20230615000000_autonomic.rb +86 -0
  9. data/lib/openc3/models/activity_model.rb +2 -4
  10. data/lib/openc3/models/microservice_model.rb +6 -2
  11. data/lib/openc3/models/model.rb +1 -3
  12. data/lib/openc3/models/reaction_model.rb +124 -119
  13. data/lib/openc3/models/scope_model.rb +15 -3
  14. data/lib/openc3/models/timeline_model.rb +1 -3
  15. data/lib/openc3/models/trigger_group_model.rb +16 -50
  16. data/lib/openc3/models/trigger_model.rb +86 -123
  17. data/lib/openc3/packets/json_packet.rb +2 -3
  18. data/lib/openc3/script/commands.rb +10 -0
  19. data/lib/openc3/script/script.rb +1 -0
  20. data/lib/openc3/top_level.rb +0 -12
  21. data/lib/openc3/utilities/authorization.rb +1 -1
  22. data/lib/openc3/utilities/bucket_require.rb +5 -1
  23. data/lib/openc3/utilities/bucket_utilities.rb +4 -1
  24. data/lib/openc3/utilities/cli_generator.rb +56 -4
  25. data/lib/openc3/utilities/ruby_lex_utils.rb +4 -0
  26. data/lib/openc3/version.rb +6 -6
  27. data/templates/plugin/README.md +54 -4
  28. data/templates/plugin/Rakefile +31 -3
  29. data/templates/tool_angular/.editorconfig +16 -0
  30. data/templates/tool_angular/.gitignore +44 -0
  31. data/templates/tool_angular/.vscode/extensions.json +4 -0
  32. data/templates/tool_angular/.vscode/launch.json +20 -0
  33. data/templates/tool_angular/.vscode/tasks.json +42 -0
  34. data/templates/tool_angular/angular.json +111 -0
  35. data/templates/tool_angular/extra-webpack.config.js +8 -0
  36. data/templates/tool_angular/package.json +47 -0
  37. data/templates/tool_angular/src/app/app-routing.module.ts +15 -0
  38. data/templates/tool_angular/src/app/app.component.html +31 -0
  39. data/templates/tool_angular/src/app/app.component.scss +26 -0
  40. data/templates/tool_angular/src/app/app.component.spec.ts +29 -0
  41. data/templates/tool_angular/src/app/app.component.ts +51 -0
  42. data/templates/tool_angular/src/app/app.module.ts +30 -0
  43. data/templates/tool_angular/src/app/custom-overlay-container.ts +17 -0
  44. data/templates/tool_angular/src/app/empty-route/empty-route.component.ts +7 -0
  45. data/templates/tool_angular/src/app/openc3-api.d.ts +1 -0
  46. data/templates/tool_angular/src/assets/.gitkeep +0 -0
  47. data/templates/tool_angular/src/environments/environment.prod.ts +3 -0
  48. data/templates/tool_angular/src/environments/environment.ts +16 -0
  49. data/templates/tool_angular/src/favicon.ico +0 -0
  50. data/templates/tool_angular/src/index.html +13 -0
  51. data/templates/tool_angular/src/main.single-spa.ts +40 -0
  52. data/templates/tool_angular/src/single-spa/asset-url.ts +12 -0
  53. data/templates/tool_angular/src/single-spa/single-spa-props.ts +8 -0
  54. data/templates/tool_angular/src/styles.scss +1 -0
  55. data/templates/tool_angular/tsconfig.app.json +13 -0
  56. data/templates/tool_angular/tsconfig.json +33 -0
  57. data/templates/tool_angular/tsconfig.spec.json +14 -0
  58. data/templates/tool_angular/yarn.lock +8080 -0
  59. data/templates/tool_react/.eslintrc +7 -0
  60. data/templates/tool_react/.gitignore +72 -0
  61. data/templates/tool_react/.prettierignore +8 -0
  62. data/templates/tool_react/babel.config.json +29 -0
  63. data/templates/tool_react/jest.config.js +12 -0
  64. data/templates/tool_react/package.json +53 -0
  65. data/templates/tool_react/src/openc3-tool_name.js +24 -0
  66. data/templates/tool_react/src/root.component.js +88 -0
  67. data/templates/tool_react/src/root.component.test.js +9 -0
  68. data/templates/tool_react/webpack.config.js +27 -0
  69. data/templates/tool_react/yarn.lock +6854 -0
  70. data/templates/tool_svelte/.gitignore +72 -0
  71. data/templates/tool_svelte/.prettierignore +8 -0
  72. data/templates/tool_svelte/babel.config.js +12 -0
  73. data/templates/tool_svelte/build/smui.css +5 -0
  74. data/templates/tool_svelte/jest.config.js +9 -0
  75. data/templates/tool_svelte/package.json +46 -0
  76. data/templates/tool_svelte/rollup.config.js +72 -0
  77. data/templates/tool_svelte/src/App.svelte +42 -0
  78. data/templates/tool_svelte/src/App.test.js +9 -0
  79. data/templates/tool_svelte/src/services/api.js +92 -0
  80. data/templates/tool_svelte/src/services/axios.js +85 -0
  81. data/templates/tool_svelte/src/services/cable.js +65 -0
  82. data/templates/tool_svelte/src/services/config-parser.js +199 -0
  83. data/templates/tool_svelte/src/services/openc3-api.js +647 -0
  84. data/templates/tool_svelte/src/theme/_smui-theme.scss +25 -0
  85. data/templates/tool_svelte/src/tool_name.js +17 -0
  86. data/templates/tool_svelte/yarn.lock +5052 -0
  87. data/templates/tool_vue/.browserslistrc +16 -0
  88. data/templates/tool_vue/.env.standalone +1 -0
  89. data/templates/tool_vue/.eslintrc.js +43 -0
  90. data/templates/tool_vue/.gitignore +2 -0
  91. data/templates/tool_vue/.nycrc +3 -0
  92. data/templates/tool_vue/.prettierrc.js +5 -0
  93. data/templates/tool_vue/babel.config.json +11 -0
  94. data/templates/tool_vue/jsconfig.json +6 -0
  95. data/templates/tool_vue/package.json +52 -0
  96. data/templates/tool_vue/src/App.vue +15 -0
  97. data/templates/tool_vue/src/main.js +38 -0
  98. data/templates/tool_vue/src/router.js +29 -0
  99. data/templates/tool_vue/src/tools/tool_name/tool_name.vue +63 -0
  100. data/templates/tool_vue/vue.config.js +30 -0
  101. data/templates/tool_vue/yarn.lock +9145 -0
  102. data/templates/widget/package.json +9 -9
  103. data/templates/widget/yarn.lock +77 -73
  104. metadata +76 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: f6639b1c8a8f8b4902a85d6bdf1f23800aefd25b61b333c661c25236058e9595
4
- data.tar.gz: e2b037827217d57a34a126189895afc3ff2c62e9758cc326f1cf24be08e1b791
3
+ metadata.gz: 35a757bea46ad5b21ac348aa90b6163595d04c702b752b728f628d7dd82f6132
4
+ data.tar.gz: 149be2f48cdb7864a3665bf46982a1e16fb37541f68203387297c14d0d55e7d8
5
5
  SHA512:
6
- metadata.gz: 6d2b3412ed3db8cdee1772416419737fc895d54145f58c087168c8b8c17649ab155cc36c4563a6bb826852c02ae0f89ac0972f101e28db04fa2cdc9163799732
7
- data.tar.gz: 39eacdb1118b721017fb84359426576ced5f08be342972a46429797b22e2f6ad46db4999171a03dc310dbb8c41e09d3bbbc8dc86145f807dfa50bfa0ffbacaf3
6
+ metadata.gz: 1595ea101a9473b1aa5a1894d08bcd5704943fae771f9e58c4506b905cae9cb5957ab0fc961349ec9ecc4466a3ae34b6987a2a9d21ed8b9d2b0d31592c16908b
7
+ data.tar.gz: afbf06dfc4d4dbbfd7916146b6ee2b3005959bae650735127710b257a675487591adb5739744e603ef5eb20cc545b4dc79962021aaef00127c9aa76017a4ba3e
@@ -410,7 +410,7 @@ static VALUE crc64_calculate(int argc, VALUE *argv, VALUE self)
410
410
  /*
411
411
  * Initialize methods for Crc
412
412
  */
413
- void Init_crc()
413
+ void Init_crc(void)
414
414
  {
415
415
  id_ivar_seed = rb_intern("@seed");
416
416
  id_ivar_xor = rb_intern("@xor");
@@ -393,7 +393,7 @@ module OpenC3
393
393
  # Check if any of the parameters have DISABLE_MESSAGES
394
394
  cmd_params.each do |key, value|
395
395
  item = packet['items'].find { |item| item['name'] == key.to_s }
396
- if item['states'] && item['states'][value] && item['states'][value]["messages_disabled"]
396
+ if item && item['states'] && item['states'][value] && item['states'][value]["messages_disabled"]
397
397
  log_message = false
398
398
  end
399
399
  end
@@ -124,14 +124,22 @@ module OpenC3
124
124
  when :BLUE, :GREEN, :GREEN_LOW, :GREEN_HIGH
125
125
  @logger.info message
126
126
  when :YELLOW, :YELLOW_LOW, :YELLOW_HIGH
127
+ notification = NotificationModel.new(
128
+ time: time_nsec,
129
+ severity: "caution",
130
+ url: "/tools/limitsmonitor",
131
+ title: "#{packet.target_name} #{packet.packet_name} #{item.name} #{item.limits.state}",
132
+ body: "#{item.name} is #{item.limits.state}"
133
+ )
134
+ NotificationsTopic.write_notification(notification.as_json(:allow_nan => true), scope: @scope)
127
135
  @logger.warn message
128
136
  when :RED, :RED_LOW, :RED_HIGH
129
137
  notification = NotificationModel.new(
130
138
  time: time_nsec,
131
139
  severity: "critical",
132
140
  url: "/tools/limitsmonitor",
133
- title: "#{packet.target_name} #{packet.packet_name} #{item.name} out of limits",
134
- body: "Item went into #{item.limits.state} limit status."
141
+ title: "#{packet.target_name} #{packet.packet_name} #{item.name} #{item.limits.state}",
142
+ body: "#{item.name} is #{item.limits.state}"
135
143
  )
136
144
  NotificationsTopic.write_notification(notification.as_json(:allow_nan => true), scope: @scope)
137
145
  @logger.error message
@@ -23,6 +23,7 @@
23
23
  require 'openc3/microservices/microservice'
24
24
  require 'openc3/models/reaction_model'
25
25
  require 'openc3/models/notification_model'
26
+ require 'openc3/topics/notifications_topic'
26
27
  require 'openc3/models/trigger_model'
27
28
  require 'openc3/topics/autonomic_topic'
28
29
  require 'openc3/utilities/authentication'
@@ -30,12 +31,12 @@ require 'openc3/utilities/authentication'
30
31
  require 'openc3/script'
31
32
 
32
33
  module OpenC3
33
-
34
- # This should remain a thread safe implamentation. This is the in memory
34
+ # This should remain a thread safe implementation. This is the in memory
35
35
  # cache that should mirror the database. This will update two hash
36
36
  # variables and will track triggers to lookup what triggers link to what
37
37
  # reactions.
38
38
  class ReactionBase
39
+ attr_reader :reactions
39
40
 
40
41
  def initialize(scope:)
41
42
  @scope = scope
@@ -45,7 +46,7 @@ module OpenC3
45
46
  @lookup = Hash.new
46
47
  end
47
48
 
48
- # RETURNS an Array of active and not snoozed reactions
49
+ # RETURNS an Array of actively snoozed reactions
49
50
  def get_snoozed
50
51
  data = nil
51
52
  @reactions_mutex.synchronize do
@@ -53,15 +54,15 @@ module OpenC3
53
54
  end
54
55
  ret = Array.new
55
56
  return ret unless data
56
- data.each do | _name, r_hash |
57
+ data.each do |_name, r_hash|
57
58
  data = Marshal.load( Marshal.dump(r_hash) )
58
59
  reaction = ReactionModel.from_json(data, name: data['name'], scope: data['scope'])
59
- ret << reaction if reaction.active && reaction.snoozed_until
60
+ ret << reaction if reaction.enabled && reaction.snoozed_until
60
61
  end
61
62
  return ret
62
63
  end
63
64
 
64
- # RETURNS an Array of active and not snoozed reactions
65
+ # RETURNS an Array of actively NOT snoozed reactions
65
66
  def get_reactions(trigger_name:)
66
67
  array_value = nil
67
68
  @lookup_mutex.synchronize do
@@ -69,26 +70,25 @@ module OpenC3
69
70
  end
70
71
  ret = Array.new
71
72
  return ret unless array_value
72
- array_value.each do | name |
73
+ array_value.each do |name|
73
74
  @reactions_mutex.synchronize do
74
75
  data = Marshal.load( Marshal.dump(@reactions[name]) )
75
76
  reaction = ReactionModel.from_json(data, name: data['name'], scope: data['scope'])
76
- ret << reaction if reaction.active && reaction.snoozed_until.nil?
77
+ ret << reaction if reaction.enabled && reaction.snoozed_until.nil?
77
78
  end
78
79
  end
79
80
  return ret
80
81
  end
81
82
 
82
- # Update the memeory database with a HASH of reactions from the external
83
- # database
83
+ # Update the memory database with a HASH of reactions from the external database
84
84
  def setup(reactions:)
85
85
  @reactions_mutex.synchronize do
86
86
  @reactions = Marshal.load( Marshal.dump(reactions) )
87
87
  end
88
88
  @lookup_mutex.synchronize do
89
89
  @lookup = Hash.new
90
- reactions.each do | reaction_name, reaction |
91
- reaction['triggers'].each do | trigger |
90
+ reactions.each do |reaction_name, reaction|
91
+ reaction['triggers'].each do |trigger|
92
92
  trigger_name = trigger['name']
93
93
  if @lookup[trigger_name].nil?
94
94
  @lookup[trigger_name] = [reaction_name]
@@ -132,7 +132,7 @@ module OpenC3
132
132
  @reactions_mutex.synchronize do
133
133
  @reactions[reaction_name] = reaction
134
134
  end
135
- reaction['triggers'].each do | trigger |
135
+ reaction['triggers'].each do |trigger|
136
136
  trigger_name = trigger['name']
137
137
  @lookup_mutex.synchronize do
138
138
  if @lookup[trigger_name].nil?
@@ -147,30 +147,30 @@ module OpenC3
147
147
  # Updates a reaction to the in memory database. This current does not
148
148
  # update the lookup Hash for the triggers.
149
149
  def update(reaction:)
150
- reaction_name = reaction['name']
151
150
  @reactions_mutex.synchronize do
152
- @reactions[reaction_name] = reaction
151
+ model = ReactionModel.from_json(reaction, name: reaction['name'], scope: reaction['scope'])
152
+ model.update()
153
+ @reactions[reaction['name']] = model.as_json(:allow_nan => true)
153
154
  end
154
155
  end
155
156
 
156
157
  # Removes a reaction to the in memory database.
157
158
  def remove(reaction:)
158
- reaction_name = reaction['name']
159
159
  @reactions_mutex.synchronize do
160
- @reactions.delete(reaction_name)
160
+ @reactions.delete(reaction['name'])
161
+ ReactionModel.delete(name: reaction['name'], scope: reaction['scope'])
161
162
  end
162
- reaction['triggers'].each do | trigger |
163
+ reaction['triggers'].each do |trigger|
163
164
  trigger_name = trigger['name']
164
165
  @lookup_mutex.synchronize do
165
- @lookup[trigger_name].delete(reaction_name)
166
+ @lookup[trigger_name].delete(reaction['name'])
166
167
  end
167
168
  end
168
169
  end
169
170
  end
170
171
 
171
- # This should remain a thread safe implamentation.
172
+ # This should remain a thread safe implementation.
172
173
  class QueueBase
173
-
174
174
  attr_reader :queue
175
175
 
176
176
  def initialize(scope:)
@@ -182,9 +182,8 @@ module OpenC3
182
182
  end
183
183
  end
184
184
 
185
- # This should remain a thread safe implamentation.
185
+ # This should remain a thread safe implementation.
186
186
  class SnoozeBase
187
-
188
187
  def initialize(scope:)
189
188
  # store the round robin watch
190
189
  @watch_mutex = Mutex.new
@@ -207,7 +206,6 @@ module OpenC3
207
206
  # Shared between the monitor thread and the manager thread to
208
207
  # share the resources.
209
208
  class ReactionShare
210
-
211
209
  attr_reader :reaction_base, :queue_base, :snooze_base
212
210
 
213
211
  def initialize(scope:)
@@ -215,7 +213,6 @@ module OpenC3
215
213
  @queue_base = QueueBase.new(scope: scope)
216
214
  @snooze_base = SnoozeBase.new(scope: scope)
217
215
  end
218
-
219
216
  end
220
217
 
221
218
  # The Reaction worker is a very simple thread pool worker. Once the manager
@@ -262,7 +259,7 @@ module OpenC3
262
259
  when 'reaction'
263
260
  run_reaction(reaction: reaction(data: data))
264
261
  when 'trigger'
265
- process_enabled_trigger(data: data)
262
+ process_true_trigger(data: data)
266
263
  end
267
264
  rescue StandardError => e
268
265
  @logger.error "ReactionWorker-#{@ident} failed to evaluate kind: #{kind} data: #{data}\n#{e.formatted}"
@@ -271,8 +268,8 @@ module OpenC3
271
268
  @logger.info "ReactionWorker-#{@ident} exiting"
272
269
  end
273
270
 
274
- def process_enabled_trigger(data:)
275
- @share.reaction_base.get_reactions(trigger_name: data['name']).each do | reaction |
271
+ def process_true_trigger(data:)
272
+ @share.reaction_base.get_reactions(trigger_name: data['name']).each do |reaction|
276
273
  run_reaction(reaction: reaction)
277
274
  end
278
275
  end
@@ -285,7 +282,21 @@ module OpenC3
285
282
  end
286
283
 
287
284
  def run_action(reaction:, action:)
285
+ reaction.updated_at = Time.now.to_nsec_from_epoch
286
+ reaction_json = reaction.as_json(:allow_nan => true)
287
+ # Let the frontend know which action is being run
288
+ # because we can combine commands and scripts with notifications
289
+ reaction_json['action'] = action['type']
290
+ notification = {
291
+ 'kind' => 'run',
292
+ 'type' => 'reaction',
293
+ 'data' => JSON.generate(reaction_json),
294
+ }
295
+ AutonomicTopic.write_notification(notification, scope: @scope)
296
+
288
297
  case action['type']
298
+ when 'notify'
299
+ run_notify(reaction: reaction, action: action)
289
300
  when 'command'
290
301
  run_command(reaction: reaction, action: action)
291
302
  when 'script'
@@ -293,21 +304,31 @@ module OpenC3
293
304
  end
294
305
  end
295
306
 
307
+ def run_notify(reaction:, action:)
308
+ notification = NotificationModel.new(
309
+ time: Time.now.to_nsec_from_epoch,
310
+ severity: action['severity'],
311
+ url: "/tools/autonomic/reactions",
312
+ title: "#{reaction.name} run",
313
+ body: action['value']
314
+ )
315
+ NotificationsTopic.write_notification(notification.as_json(:allow_nan => true), scope: @scope)
316
+ @logger.info "ReactionWorker-#{@ident} #{reaction.name} notify action complete, body: #{action['value']}, severity: #{action['severity']}"
317
+ end
318
+
296
319
  def run_command(reaction:, action:)
297
- @logger.debug "ReactionWorker-#{@ident} running reaction #{reaction.name}, command: '#{action['value']}' "
298
320
  begin
299
321
  username = reaction.username
300
322
  token = get_token(username)
301
323
  raise "No token available for username: #{username}" unless token
302
324
  cmd_no_hazardous_check(action['value'], scope: @scope, token: token)
303
- @logger.info "ReactionWorker-#{@ident} #{reaction.name} command action complete, #{action['value']}"
325
+ @logger.info "ReactionWorker-#{@ident} #{reaction.name} command action complete, command: #{action['value']}"
304
326
  rescue StandardError => e
305
327
  @logger.error "ReactionWorker-#{@ident} #{reaction.name} command action failed, #{action}\n#{e.message}"
306
328
  end
307
329
  end
308
330
 
309
331
  def run_script(reaction:, action:)
310
- @logger.debug "ReactionWorker-#{@ident} running reaction #{reaction.name}, script: '#{action['value']}'"
311
332
  begin
312
333
  username = reaction.username
313
334
  token = get_token(username)
@@ -351,7 +372,7 @@ module OpenC3
351
372
 
352
373
  def generate_thread_pool()
353
374
  thread_pool = []
354
- @worker_count.times do | i |
375
+ @worker_count.times do |i|
355
376
  worker = ReactionWorker.new(name: @name, logger: @logger, scope: @scope, share: @share, ident: i)
356
377
  thread_pool << Thread.new { worker.run }
357
378
  end
@@ -376,7 +397,7 @@ module OpenC3
376
397
  end
377
398
 
378
399
  def active_triggers(reaction:)
379
- reaction.triggers.each do | trigger |
400
+ reaction.triggers.each do |trigger|
380
401
  t = TriggerModel.get(name: trigger['name'], group: trigger['group'], scope: @scope)
381
402
  return true if t && t.state
382
403
  end
@@ -384,16 +405,11 @@ module OpenC3
384
405
  end
385
406
 
386
407
  def manage_snoozed_reactions(current_time:)
387
- @share.reaction_base.get_snoozed.each do | reaction |
408
+ @share.reaction_base.get_snoozed.each do |reaction|
388
409
  time_difference = reaction.snoozed_until - current_time
389
410
  if time_difference <= 0 && @share.snooze_base.not_queued?(reaction: reaction)
390
- @logger.info "#{reaction.name} current: #{current_time}, vs #{reaction.snoozed_until}, #{time_difference}"
391
- unless reaction.review
392
- @logger.debug "#{reaction.name} review set to false, setting snoozed_until back to nil"
393
- @share.reaction_base.wake(name: reaction.name)
394
- next
395
- end
396
- if active_triggers(reaction: reaction)
411
+ # LEVEL triggers mean we run if the trigger is active
412
+ if reaction.triggerLevel == 'LEVEL' and active_triggers(reaction: reaction)
397
413
  @share.queue_base.enqueue(kind: 'reaction', data: reaction.as_json(:allow_nan => true))
398
414
  else
399
415
  @share.reaction_base.wake(name: reaction.name)
@@ -404,7 +420,7 @@ module OpenC3
404
420
 
405
421
  def shutdown
406
422
  @cancel_thread = true
407
- @worker_count.times do | i |
423
+ @worker_count.times do |i|
408
424
  @share.queue_base.enqueue(kind: nil, data: nil)
409
425
  end
410
426
  end
@@ -415,8 +431,39 @@ module OpenC3
415
431
  # AutonomicTopic for changes.
416
432
  class ReactionMicroservice < Microservice
417
433
  attr_reader :name, :scope, :share, :manager, :manager_thread
434
+ TOPIC_LOOKUP = {
435
+ 'group' => {
436
+ 'created' => :no_op,
437
+ 'updated' => :no_op,
438
+ 'deleted' => :no_op,
439
+ },
440
+ 'trigger' => {
441
+ 'error' => :no_op,
442
+ 'created' => :no_op,
443
+ 'updated' => :no_op,
444
+ 'deleted' => :no_op,
445
+ 'enabled' => :no_op,
446
+ 'disabled' => :no_op,
447
+ 'true' => :trigger_true_event,
448
+ 'false' => :no_op,
449
+ },
450
+ 'reaction' => {
451
+ 'run' => :no_op,
452
+ 'deployed' => :no_op,
453
+ 'undeployed' => :no_op,
454
+ 'created' => :reaction_created_event,
455
+ 'updated' => :reaction_updated_event,
456
+ 'deleted' => :reaction_deleted_event,
457
+ 'enabled' => :reaction_enabled_event,
458
+ 'disabled' => :reaction_disabled_event,
459
+ 'snoozed' => :no_op,
460
+ 'awakened' => :no_op,
461
+ 'executed' => :reaction_execute_event,
462
+ }
463
+ }
418
464
 
419
465
  def initialize(*args)
466
+ # The name is passed in via the reaction_model as "#{scope}__OPENC3__REACTION"
420
467
  super(*args)
421
468
  @share = ReactionShare.new(scope: @scope)
422
469
  @manager = ReactionSnoozeManager.new(name: @name, logger: @logger, scope: @scope, share: @share)
@@ -426,53 +473,36 @@ module OpenC3
426
473
 
427
474
  def run
428
475
  @logger.info "ReactionMicroservice running"
476
+ # Let the frontend know that the microservice has been deployed and is running
477
+ notification = {
478
+ 'kind' => 'deployed',
479
+ 'type' => 'reaction',
480
+ # name and updated_at fields are required for Event formatting
481
+ 'data' => JSON.generate({
482
+ 'name' => @name,
483
+ 'updated_at' => Time.now.to_nsec_from_epoch,
484
+ }),
485
+ }
486
+ AutonomicTopic.write_notification(notification, scope: @scope)
487
+
429
488
  @manager_thread = Thread.new { @manager.run }
430
489
  loop do
431
490
  reactions = ReactionModel.all(scope: @scope)
432
491
  @share.reaction_base.setup(reactions: reactions)
433
492
  break if @cancel_thread
434
-
435
493
  block_for_updates()
436
494
  break if @cancel_thread
437
495
  end
438
496
  @logger.info "ReactionMicroservice exiting"
439
497
  end
440
498
 
441
- def topic_lookup_functions
442
- return {
443
- 'group' => {
444
- 'created' => :no_op,
445
- 'updated' => :no_op,
446
- 'deleted' => :no_op,
447
- },
448
- 'trigger' => {
449
- 'created' => :no_op,
450
- 'updated' => :no_op,
451
- 'deleted' => :no_op,
452
- 'enabled' => :trigger_enabled_event,
453
- 'disabled' => :no_op,
454
- 'activated' => :no_op,
455
- 'deactivated' => :no_op,
456
- },
457
- 'reaction' => {
458
- 'created' => :reaction_created_event,
459
- 'updated' => :refresh_event,
460
- 'deleted' => :reaction_deleted_event,
461
- 'sleep' => :no_op,
462
- 'awaken' => :no_op,
463
- 'activated' => :reaction_updated_event,
464
- 'deactivated' => :reaction_updated_event,
465
- }
466
- }
467
- end
468
-
469
499
  def block_for_updates
470
500
  @read_topic = true
471
- while @read_topic
501
+ while @read_topic && !@cancel_thread
472
502
  begin
473
503
  AutonomicTopic.read_topics(@topics) do |_topic, _msg_id, msg_hash, _redis|
474
504
  @logger.debug "ReactionMicroservice block_for_updates: #{msg_hash.to_s}"
475
- public_send(topic_lookup_functions[msg_hash['type']][msg_hash['kind']], msg_hash)
505
+ public_send(TOPIC_LOOKUP[msg_hash['type']][msg_hash['kind']], msg_hash)
476
506
  end
477
507
  rescue StandardError => e
478
508
  @logger.error "ReactionMicroservice failed to read topics #{@topics}\n#{e.formatted}"
@@ -484,29 +514,70 @@ module OpenC3
484
514
  @logger.debug "ReactionMicroservice web socket event: #{data}"
485
515
  end
486
516
 
487
- def refresh_event(data)
488
- @logger.debug "ReactionMicroservice web socket schedule refresh: #{data}"
517
+ def reaction_updated_event(msg_hash)
518
+ @logger.debug "ReactionMicroservice reaction updated msg_hash: #{msg_hash}"
519
+ reaction = JSON.parse(msg_hash['data'], :allow_nan => true, :create_additions => true)
520
+ @share.reaction_base.update(reaction: reaction)
489
521
  @read_topic = false
490
522
  end
491
523
 
492
- #
493
- def trigger_enabled_event(msg_hash)
494
- @logger.debug "ReactionMicroservice trigger event msg_hash: #{msg_hash}"
524
+ def trigger_true_event(msg_hash)
525
+ @logger.debug "ReactionMicroservice trigger true msg_hash: #{msg_hash}"
495
526
  @share.queue_base.enqueue(kind: 'trigger', data: JSON.parse(msg_hash['data'], :allow_nan => true, :create_additions => true))
496
527
  end
497
528
 
498
529
  # Add the reaction to the shared data.
499
530
  def reaction_created_event(msg_hash)
500
531
  @logger.debug "ReactionMicroservice reaction created msg_hash: #{msg_hash}"
501
- @share.reaction_base.add(reaction: JSON.parse(msg_hash['data'], :allow_nan => true, :create_additions => true))
532
+ reaction = JSON.parse(msg_hash['data'], :allow_nan => true, :create_additions => true)
533
+ @share.reaction_base.add(reaction: reaction)
534
+
535
+ # If the reaction triggerLevel is LEVEL we have to check its triggers
536
+ # on add because if the trigger is active it should run
537
+ if reaction['triggerLevel'] == 'LEVEL'
538
+ reaction['triggers'].each do |trigger_hash|
539
+ trigger = TriggerModel.get(name: trigger_hash['name'], group: trigger_hash['group'], scope: reaction['scope'])
540
+ if trigger && trigger.state
541
+ @logger.info "ReactionMicroservice reaction #{reaction['name']} created. Since triggerLevel is 'LEVEL' it was run due to #{trigger.name}."
542
+ @share.queue_base.enqueue(kind: 'reaction', data: reaction)
543
+ end
544
+ end
545
+ end
502
546
  end
503
547
 
504
548
  # Update the reaction to the shared data.
505
- def reaction_updated_event(msg_hash)
506
- @logger.debug "ReactionMicroservice reaction updated msg_hash: #{msg_hash}"
549
+ def reaction_enabled_event(msg_hash)
550
+ @logger.debug "ReactionMicroservice reaction enabled msg_hash: #{msg_hash}"
551
+ reaction = JSON.parse(msg_hash['data'], :allow_nan => true, :create_additions => true)
552
+ @share.reaction_base.update(reaction: reaction)
553
+
554
+ # If the reaction triggerLevel is LEVEL we have to check its triggers
555
+ # on add because if the trigger is active it should run
556
+ if reaction['triggerLevel'] == 'LEVEL'
557
+ reaction['triggers'].each do |trigger_hash|
558
+ trigger = TriggerModel.get(name: trigger_hash['name'], group: trigger_hash['group'], scope: reaction['scope'])
559
+ if trigger && trigger.state
560
+ @logger.info "ReactionMicroservice reaction #{reaction['name']} enabled. Since triggerLevel is 'LEVEL' it was run due to #{trigger.name}."
561
+ @share.queue_base.enqueue(kind: 'reaction', data: reaction)
562
+ end
563
+ end
564
+ end
565
+ end
566
+
567
+ # Update the reaction to the shared data.
568
+ def reaction_disabled_event(msg_hash)
569
+ @logger.debug "ReactionMicroservice reaction disabled msg_hash: #{msg_hash}"
507
570
  @share.reaction_base.update(reaction: JSON.parse(msg_hash['data'], :allow_nan => true, :create_additions => true))
508
571
  end
509
572
 
573
+ # Add the reaction to the shared data.
574
+ def reaction_execute_event(msg_hash)
575
+ @logger.debug "ReactionMicroservice reaction execute msg_hash: #{msg_hash}"
576
+ reaction = JSON.parse(msg_hash['data'], :allow_nan => true, :create_additions => true)
577
+ @share.reaction_base.update(reaction: reaction)
578
+ @share.queue_base.enqueue(kind: 'reaction', data: reaction)
579
+ end
580
+
510
581
  # Remove the reaction from the shared data
511
582
  def reaction_deleted_event(msg_hash)
512
583
  @logger.debug "ReactionMicroservice reaction deleted msg_hash: #{msg_hash}"
@@ -136,7 +136,7 @@ module OpenC3
136
136
 
137
137
  # Shared between the monitor thread and the manager thread to
138
138
  # share the planned activities. This should remain a thread
139
- # safe implamentation.
139
+ # safe implementation.
140
140
  class Schedule
141
141
  def initialize(name)
142
142
  @name = name