openc3 5.8.1 → 5.9.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 (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)