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
@@ -0,0 +1,86 @@
1
+ require 'openc3/utilities/migration'
2
+ require 'openc3/models/trigger_group_model'
3
+ require 'openc3/models/trigger_model'
4
+ require 'openc3/models/reaction_model'
5
+
6
+ module OpenC3
7
+ class Autonomic < Migration
8
+ def self.run
9
+ ScopeModel.names.each do |scope|
10
+ puts "Processing scope #{scope}"
11
+
12
+ # Update all old TriggerModels just so they work when we delete the ReactionModel
13
+ # Because the ReactionModel verifies the triggers
14
+ delete_all_triggers = false
15
+ groups = TriggerGroupModel.all(scope: scope)
16
+ groups.each do |key, group_hash|
17
+ puts "Processing group #{group_hash['name']}"
18
+ if group_hash.has_key?('color')
19
+ group_hash.delete('color')
20
+ group = TriggerGroupModel.from_json(group_hash, name: group_hash['name'], scope: scope)
21
+ group.update()
22
+ end
23
+ TriggerModel.all(group: group_hash['name'], scope: scope).each do |key, model_hash|
24
+ if model_hash.has_key?('description') or model_hash.has_key?('active')
25
+ puts "Updating TriggerModel: #{model_hash['name']}"
26
+ model_hash.delete('description')
27
+ model_hash.delete('active')
28
+ model_hash['left'] = {'type' => 'item', 'target' => 'TGT', 'packet' => 'PKT', 'item' => 'ITEM', 'valueType' => 'CONVERTED'}
29
+ model_hash['operator'] = 'CHANGES'
30
+ model_hash['right'] = nil
31
+ TriggerModel.from_json(model_hash, name: model_hash['name'], scope: scope).update()
32
+ delete_all_triggers = true
33
+ end
34
+ end
35
+ end
36
+
37
+ # Remove all old ReactionModels
38
+ ReactionModel.all(scope: scope).each do |key, model_hash|
39
+ if model_hash.has_key?('description') or model_hash.has_key?('review') or model_hash.has_key?('active')
40
+ # Can't delete directly because delete calls get which calls from_json which calls new
41
+ # and at that point we get missing keyword: :triggerLevel (ArgumentError)
42
+ # So update to add triggerLevel
43
+ model_hash['triggerLevel'] = 'EDGE'
44
+ model_hash.delete('description')
45
+ model_hash.delete('review')
46
+ model_hash.delete('active')
47
+ ReactionModel.from_json(model_hash, name: model_hash['name'], scope: scope).update()
48
+ puts "Deleting ReactionModel: #{model_hash['name']}"
49
+ ReactionModel.delete(name: model_hash['name'], scope: scope)
50
+ end
51
+ end
52
+
53
+ # Remove all old TriggerModels and TriggerGroupModels
54
+ if delete_all_triggers
55
+ groups = TriggerGroupModel.all(scope: scope)
56
+ groups.each do |key, group_hash|
57
+ TriggerModel.all(group: group_hash['name'], scope: scope).each do |key, trigger_hash|
58
+ puts "Deleting TriggerModel: #{trigger_hash['name']}"
59
+ TriggerModel.delete(name: trigger_hash['name'], group: group_hash['name'], scope: scope)
60
+ end
61
+ group = TriggerGroupModel.from_json(group_hash, name: group_hash['name'], scope: scope)
62
+ group.undeploy()
63
+ puts "Deleting TriggerGroupModel: #{group_hash['name']}"
64
+ TriggerGroupModel.delete(name: group_hash['name'], scope: scope)
65
+ end
66
+ end
67
+
68
+ # Create DEFAULT trigger group model
69
+ model = TriggerGroupModel.get(name: 'DEFAULT', scope: scope)
70
+ unless model
71
+ puts "Creating TriggerGroupModel: DEFAULT"
72
+ model = TriggerGroupModel.new(name: 'DEFAULT', scope: scope)
73
+ model.create()
74
+ model.deploy()
75
+ end
76
+ end
77
+ rescue => error
78
+ puts error.message
79
+ puts error.backtrace
80
+ end
81
+ end
82
+ end
83
+
84
+ unless ENV['OPENC3_NO_MIGRATE']
85
+ OpenC3::Autonomic.run
86
+ end
@@ -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
  # https://www.rubydoc.info/gems/redis/Redis/Commands/SortedSets
@@ -107,9 +107,7 @@ module OpenC3
107
107
  def self.from_json(json, name:, scope:)
108
108
  json = JSON.parse(json, :allow_nan => true, :create_additions => true) if String === json
109
109
  raise "json data is nil" if json.nil?
110
-
111
- json.transform_keys!(&:to_sym)
112
- self.new(**json, name: name, scope: scope)
110
+ self.new(**json.transform_keys(&:to_sym), name: name, scope: scope)
113
111
  end
114
112
 
115
113
  attr_reader :duration, :start, :stop, :kind, :data, :events, :fulfillment
@@ -228,7 +228,9 @@ module OpenC3
228
228
  end
229
229
  end
230
230
  unless validate_only
231
- ConfigTopic.write({ kind: 'created', type: 'microservice', name: @name, plugin: @plugin }, scope: @scope)
231
+ config = { kind: 'created', type: 'microservice', name: @name }
232
+ config[:plugin] = @plugin if @plugin
233
+ ConfigTopic.write(config, scope: @scope)
232
234
  end
233
235
  end
234
236
 
@@ -237,7 +239,9 @@ module OpenC3
237
239
  @bucket.list_objects(bucket: ENV['OPENC3_CONFIG_BUCKET'], prefix: prefix).each do |object|
238
240
  @bucket.delete_object(bucket: ENV['OPENC3_CONFIG_BUCKET'], key: object.key)
239
241
  end
240
- ConfigTopic.write({ kind: 'deleted', type: 'microservice', name: @name, plugin: @plugin }, scope: @scope)
242
+ config = { kind: 'deleted', type: 'microservice', name: @name }
243
+ config[:plugin] = @plugin if @plugin
244
+ ConfigTopic.write(config, scope: @scope)
241
245
  rescue Exception => error
242
246
  Logger.error("Error undeploying microservice model #{@name} in scope #{@scope} due to #{error}")
243
247
  end
@@ -85,10 +85,8 @@ module OpenC3
85
85
  def self.from_json(json, scope:)
86
86
  json = JSON.parse(json, :allow_nan => true, :create_additions => true) if String === json
87
87
  raise "json data is nil" if json.nil?
88
-
89
88
  json[:scope] = scope
90
- json.transform_keys!(&:to_sym)
91
- self.new(**json, scope: scope)
89
+ self.new(**json.transform_keys(&:to_sym), scope: scope)
92
90
  end
93
91
 
94
92
  # Calls self.get and then from_json to turn the Hash configuration into a Ruby Model object.
@@ -27,47 +27,22 @@ require 'openc3/topics/autonomic_topic'
27
27
 
28
28
  module OpenC3
29
29
  class ReactionError < StandardError; end
30
-
31
30
  class ReactionInputError < ReactionError; end
32
31
 
33
- # {
34
- # "description": "POSX greater than 200",
35
- # "snooze": 300,
36
- # "review": true,
37
- # "triggers": [
38
- # {
39
- # "name": "TV0-1234",
40
- # "group": "foo",
41
- # }
42
- # ],
43
- # "actions": [
44
- # {
45
- # "type": "command",
46
- # "value": "INST CLEAR",
47
- # }
48
- # ]
49
- # }
50
32
  class ReactionModel < Model
51
33
  PRIMARY_KEY = '__openc3__reaction'.freeze
52
- COMMAND_REACTION = 'command'.freeze
53
34
  SCRIPT_REACTION = 'script'.freeze
54
-
55
- def self.create_mini_id
56
- time = (Time.now.to_f * 10_000_000).to_i
57
- jitter = rand(10_000_000)
58
- key = "#{jitter}#{time}".to_i.to_s(36)
59
- return "RV0-#{key}"
60
- end
61
-
62
- # @return [Array<ReactionModel>]
63
- def self.reactions(scope:)
64
- reactions = Array.new
65
- Store.hgetall("#{scope}#{PRIMARY_KEY}").each do |key, value|
66
- data = JSON.parse(value, :allow_nan => true, :create_additions => true)
67
- reaction = self.from_json(data, name: data['name'], scope: data['scope'])
68
- reactions << reaction if reaction.active
35
+ COMMAND_REACTION = 'command'.freeze
36
+ NOTIFY_REACTION = 'notify'.freeze
37
+ ACTION_TYPES = [SCRIPT_REACTION, COMMAND_REACTION, NOTIFY_REACTION]
38
+
39
+ def self.create_unique_name(scope:)
40
+ reaction_names = self.names(scope: scope) # comes back sorted
41
+ num = 1 # Users count with 1
42
+ if reaction_names[-1]
43
+ num = reaction_names[-1][5..-1].to_i + 1
69
44
  end
70
- return reactions
45
+ return "REACT#{num}"
71
46
  end
72
47
 
73
48
  # @return [ReactionModel] Return the object with the name at
@@ -89,10 +64,10 @@ module OpenC3
89
64
  end
90
65
 
91
66
  # Check dependents before delete.
92
- def self.delete(name:, scope:, force: false)
67
+ def self.delete(name:, scope:)
93
68
  model = self.get(name: name, scope: scope)
94
69
  if model.nil?
95
- raise ReactionInputError.new "failed to find reaction: #{name}"
70
+ raise ReactionInputError.new "reaction '#{name}' does not exist"
96
71
  end
97
72
  model.triggers.each do | trigger |
98
73
  trigger_model = TriggerModel.get(name: trigger['name'], group: trigger['group'], scope: scope)
@@ -100,29 +75,79 @@ module OpenC3
100
75
  trigger_model.update()
101
76
  end
102
77
  Store.hdel("#{scope}#{PRIMARY_KEY}", name)
103
- model.notify(kind: 'deleted')
78
+ # No notification as this is only called via reaction_controller which already notifies
79
+
80
+ # undeploy only actually runs if no reactions are left
81
+ model.undeploy()
104
82
  end
105
83
 
106
- #
107
- def validate_snooze(snooze:)
108
- unless snooze.is_a?(Integer)
109
- raise ReactionInputError.new "invalid snooze value: #{snooze}"
84
+ attr_reader :name, :scope, :snooze, :triggers, :actions, :enabled, :triggerLevel, :snoozed_until
85
+ attr_accessor :username
86
+
87
+ def initialize(
88
+ name:,
89
+ scope:,
90
+ snooze:,
91
+ actions:,
92
+ triggers:,
93
+ triggerLevel:,
94
+ enabled: true,
95
+ snoozed_until: nil,
96
+ username: nil,
97
+ updated_at: nil
98
+ )
99
+ super("#{scope}#{PRIMARY_KEY}", name: name, scope: scope)
100
+ @microservice_name = "#{scope}__OPENC3__REACTION"
101
+ @enabled = enabled
102
+ @snoozed_until = snoozed_until
103
+ @triggerLevel = validate_level(triggerLevel)
104
+ @snooze = validate_snooze(snooze)
105
+ @actions = validate_actions(actions)
106
+ @triggers = validate_triggers(triggers)
107
+ @username = username
108
+ @updated_at = updated_at
109
+ end
110
+
111
+ # Modifiers for the reaction_controller update action
112
+ def triggerLevel=(triggerLevel)
113
+ @triggerLevel = validate_level(triggerLevel)
114
+ end
115
+ def snooze=(snooze)
116
+ @snooze = validate_snooze(snooze)
117
+ end
118
+ def actions=(actions)
119
+ @actions = validate_actions(actions)
120
+ end
121
+ def triggers=(triggers)
122
+ @triggers = validate_triggers(triggers)
123
+ end
124
+
125
+ def validate_level(level)
126
+ case level
127
+ when 'EDGE', 'LEVEL'
128
+ return level
129
+ else
130
+ raise ReactionInputError.new "invalid triggerLevel, must be EDGE or LEVEL: #{level}"
110
131
  end
111
- return snooze
112
132
  end
113
133
 
114
- #
115
- def validate_triggers(triggers:)
134
+ def validate_snooze(snooze)
135
+ Integer(snooze)
136
+ rescue
137
+ raise ReactionInputError.new "invalid snooze value: #{snooze}"
138
+ end
139
+
140
+ def validate_triggers(triggers)
116
141
  unless triggers.is_a?(Array)
117
- raise ReactionInputError.new "invalid operator: #{operator}"
142
+ raise ReactionInputError.new "invalid triggers, must be array of hashes: #{triggers}"
118
143
  end
119
144
  trigger_hash = Hash.new()
120
145
  triggers.each do | trigger |
121
146
  unless trigger.is_a?(Hash)
122
- raise ReactionInputError.new "invalid trigger object: #{trigger}"
147
+ raise ReactionInputError.new "invalid trigger, must be hash: #{trigger}"
123
148
  end
124
149
  if trigger['name'].nil? || trigger['group'].nil?
125
- raise ReactionInputError.new "allowed: #{triggers}"
150
+ raise ReactionInputError.new "invalid trigger, must contain 'name' and 'group' keys: #{trigger}"
126
151
  end
127
152
  trigger_name = trigger['name']
128
153
  unless trigger_hash[trigger_name].nil?
@@ -134,60 +159,27 @@ module OpenC3
134
159
  return triggers
135
160
  end
136
161
 
137
- #
138
- def validate_actions(actions:)
162
+ def validate_actions(actions)
139
163
  unless actions.is_a?(Array)
140
- raise ReactionInputError.new "invalid actions object: #{actions}"
164
+ raise ReactionInputError.new "invalid actions, must be array of hashes: #{actions}"
141
165
  end
142
166
  actions.each do | action |
143
167
  unless action.is_a?(Hash)
144
- raise ReactionInputError.new "invalid action object: #{action}"
168
+ raise ReactionInputError.new "invalid action, must be a hash: #{action}"
145
169
  end
146
170
  action_type = action['type']
147
171
  if action_type.nil?
148
- raise ReactionInputError.new "reaction action must contain type: #{action_type}"
172
+ raise ReactionInputError.new "invalid action, must contain 'type': #{action}"
149
173
  elsif action['value'].nil?
150
- raise ReactionInputError.new "reaction action: #{action} does not contain 'value'"
174
+ raise ReactionInputError.new "invalid action, must contain 'value': #{action}"
151
175
  end
152
- unless [COMMAND_REACTION, SCRIPT_REACTION].include?(action_type)
153
- raise ReactionInputError.new "reaction action contains invalid type: #{action_type}"
176
+ unless ACTION_TYPES.include?(action_type)
177
+ raise ReactionInputError.new "invalid action type '#{action_type}', must be one of #{ACTION_TYPES}"
154
178
  end
155
179
  end
156
180
  return actions
157
181
  end
158
182
 
159
- attr_reader :name, :scope, :description, :snooze, :triggers, :actions, :active, :review, :snoozed_until
160
- attr_accessor :username
161
-
162
- def initialize(
163
- name:,
164
- scope:,
165
- description:,
166
- snooze:,
167
- actions:,
168
- triggers:,
169
- active: true,
170
- review: true,
171
- snoozed_until: nil,
172
- username: nil,
173
- updated_at: nil
174
- )
175
- if name.nil? || scope.nil? || description.nil? || snooze.nil? || triggers.nil? || actions.nil?
176
- raise ReactionInputError.new "#{name}, #{scope}, #{description}, #{snooze}, #{triggers}, or #{actions} must not be nil"
177
- end
178
- super("#{scope}#{PRIMARY_KEY}", name: name, scope: scope)
179
- @microservice_name = "#{scope}__OPENC3__REACTION"
180
- @active = active
181
- @review = review
182
- @description = description
183
- @snoozed_until = snoozed_until
184
- @snooze = validate_snooze(snooze: snooze)
185
- @actions = validate_actions(actions: actions)
186
- @triggers = validate_triggers(triggers: triggers)
187
- @username = username
188
- @updated_at = updated_at
189
- end
190
-
191
183
  def verify_triggers
192
184
  trigger_models = []
193
185
  @triggers.each do | trigger |
@@ -208,7 +200,7 @@ module OpenC3
208
200
 
209
201
  def create
210
202
  unless Store.hget(@primary_key, @name).nil?
211
- raise ReactionInputError.new "exsisting Reaction found: #{@name}"
203
+ raise ReactionInputError.new "existing reaction found: #{@name}"
212
204
  end
213
205
  verify_triggers()
214
206
  @updated_at = Time.now.to_nsec_from_epoch
@@ -220,41 +212,43 @@ module OpenC3
220
212
  verify_triggers()
221
213
  @updated_at = Time.now.to_nsec_from_epoch
222
214
  Store.hset(@primary_key, @name, JSON.generate(as_json(:allow_nan => true)))
223
- notify(kind: 'updated')
215
+ # No notification as this is only called via reaction_controller which already notifies
224
216
  end
225
217
 
226
- def activate
227
- @active = true
228
- @snoozed_until = nil if @snoozed_until && @snoozed_until < Time.now.to_i
229
- @updated_at = Time.now.to_nsec_from_epoch
230
- Store.hset(@primary_key, @name, JSON.generate(as_json(:allow_nan => true)))
231
- notify(kind: 'activated')
218
+ def notify_enable
219
+ @enabled = true
220
+ notify(kind: 'enabled')
221
+ # update() will be called by the reaction_microservice
222
+ end
223
+
224
+ def notify_disable
225
+ @enabled = false
226
+ # disabling clears the snooze so when it's enabled it can immediately run
227
+ @snoozed_until = nil
228
+ notify(kind: 'disabled')
229
+ # update() will be called by the reaction_microservice
232
230
  end
233
231
 
234
- def deactivate
235
- @active = false
232
+ def notify_execute
233
+ # Set updated_at because the event is all we get ... no update later
236
234
  @updated_at = Time.now.to_nsec_from_epoch
237
- Store.hset(@primary_key, @name, JSON.generate(as_json(:allow_nan => true)))
238
- notify(kind: 'deactivated')
235
+ notify(kind: 'executed')
239
236
  end
240
237
 
241
238
  def sleep
242
- @snoozed_until = Time.now.to_i + @snooze
243
- @updated_at = Time.now.to_nsec_from_epoch
244
- Store.hset(@primary_key, @name, JSON.generate(as_json(:allow_nan => true)))
245
- notify(kind: 'sleep')
239
+ if @snooze > 0
240
+ @snoozed_until = Time.now.to_i + @snooze
241
+ @updated_at = Time.now.to_nsec_from_epoch
242
+ Store.hset(@primary_key, @name, JSON.generate(as_json(:allow_nan => true)))
243
+ notify(kind: 'snoozed')
244
+ end
246
245
  end
247
246
 
248
247
  def awaken
249
248
  @snoozed_until = nil
250
249
  @updated_at = Time.now.to_nsec_from_epoch
251
250
  Store.hset(@primary_key, @name, JSON.generate(as_json(:allow_nan => true)))
252
- notify(kind: 'awaken')
253
- end
254
-
255
- # @return [String] generated from the TriggerModel
256
- def to_s
257
- return "(ReactionModel :: #{@name} :: #{@active} :: #{@review} :: #{@description} :: #{@snooze} :: #{@snoozed_until})"
251
+ notify(kind: 'awakened')
258
252
  end
259
253
 
260
254
  # @return [Hash] generated from the ReactionModel
@@ -262,9 +256,8 @@ module OpenC3
262
256
  return {
263
257
  'name' => @name,
264
258
  'scope' => @scope,
265
- 'active' => @active,
266
- 'review' => @review,
267
- 'description' => @description,
259
+ 'enabled' => @enabled,
260
+ 'triggerLevel' => @triggerLevel,
268
261
  'snooze' => @snooze,
269
262
  'snoozed_until' => @snoozed_until,
270
263
  'triggers' => @triggers,
@@ -278,9 +271,7 @@ module OpenC3
278
271
  def self.from_json(json, name:, scope:)
279
272
  json = JSON.parse(json, :allow_nan => true, :create_additions => true) if String === json
280
273
  raise "json data is nil" if json.nil?
281
-
282
- json.transform_keys!(&:to_sym)
283
- self.new(**json, name: name, scope: scope)
274
+ self.new(**json.transform_keys(&:to_sym), name: name, scope: scope)
284
275
  end
285
276
 
286
277
  # @return [] update the redis stream / reaction topic that something has changed
@@ -317,9 +308,23 @@ module OpenC3
317
308
  end
318
309
 
319
310
  def undeploy
320
- if ReactionModel.names(scope: @scope).empty?
321
- model = MicroserviceModel.get_model(name: @microservice_name, scope: @scope)
322
- model.destroy if model
311
+ return unless ReactionModel.names(scope: @scope).empty?
312
+
313
+ model = MicroserviceModel.get_model(name: @microservice_name, scope: @scope)
314
+ if model
315
+ # Let the frontend know that the microservice is shutting down
316
+ # Custom event which matches the 'deployed' event in ReactionMicroservice
317
+ notification = {
318
+ 'kind' => 'undeployed',
319
+ 'type' => 'reaction',
320
+ # name and updated_at fields are required for Event formatting
321
+ 'data' => JSON.generate({
322
+ 'name' => @microservice_name,
323
+ 'updated_at' => Time.now.to_nsec_from_epoch,
324
+ }),
325
+ }
326
+ AutonomicTopic.write_notification(notification, scope: @scope)
327
+ model.destroy
323
328
  end
324
329
  end
325
330
  end
@@ -25,6 +25,7 @@ require 'openc3/models/model'
25
25
  require 'openc3/models/plugin_model'
26
26
  require 'openc3/models/microservice_model'
27
27
  require 'openc3/models/setting_model'
28
+ require 'openc3/models/trigger_group_model'
28
29
 
29
30
  module OpenC3
30
31
  class ScopeModel < Model
@@ -49,9 +50,7 @@ module OpenC3
49
50
  def self.from_json(json, scope: nil)
50
51
  json = JSON.parse(json, :allow_nan => true, :create_additions => true) if String === json
51
52
  raise "json data is nil" if json.nil?
52
-
53
- json.transform_keys!(&:to_sym)
54
- self.new(**json, scope: scope)
53
+ self.new(**json.transform_keys(&:to_sym), scope: scope)
55
54
  end
56
55
 
57
56
  def self.get_model(name:, scope: nil)
@@ -210,6 +209,14 @@ module OpenC3
210
209
  def deploy(gem_path, variables)
211
210
  seed_database()
212
211
 
212
+ # Create DEFAULT trigger group model
213
+ model = TriggerGroupModel.get(name: 'DEFAULT', scope: @scope)
214
+ unless model
215
+ model = TriggerGroupModel.new(name: 'DEFAULT', scope: @scope)
216
+ model.create()
217
+ model.deploy()
218
+ end
219
+
213
220
  # Create UNKNOWN target for display of unknown data
214
221
  model = TargetModel.new(name: "UNKNOWN", scope: @scope)
215
222
  model.create
@@ -250,11 +257,16 @@ module OpenC3
250
257
  model.destroy if model
251
258
  model = MicroserviceModel.get_model(name: "#{@scope}__PERIODIC__#{@scope}", scope: @scope)
252
259
  model.destroy if model
260
+ model = MicroserviceModel.get_model(name: "#{@scope}__TRIGGER_GROUP__DEFAULT", scope: @scope)
261
+ model.destroy if model
262
+
253
263
  # Delete the topics we created for the scope
254
264
  Topic.del("#{@scope}__COMMAND__{UNKNOWN}__UNKNOWN")
255
265
  Topic.del("#{@scope}__TELEMETRY__{UNKNOWN}__UNKNOWN")
256
266
  Topic.del("#{@scope}__openc3_targets")
257
267
  Topic.del("#{@scope}__CONFIG")
268
+ Topic.del("#{@scope}__openc3_autonomic")
269
+ Topic.del("#{@scope}__TRIGGER__GROUP")
258
270
  end
259
271
 
260
272
  def seed_database
@@ -71,9 +71,7 @@ module OpenC3
71
71
  def self.from_json(json, name:, scope:)
72
72
  json = JSON.parse(json, :allow_nan => true, :create_additions => true) if String === json
73
73
  raise "json data is nil" if json.nil?
74
-
75
- json.transform_keys!(&:to_sym)
76
- self.new(**json, name: name, scope: scope)
74
+ self.new(**json.transform_keys(&:to_sym), name: name, scope: scope)
77
75
  end
78
76
 
79
77
  def initialize(name:, scope:, updated_at: nil, color: nil)