Class: OpenC3::ReactionModel
- Defined in:
- lib/openc3/models/reaction_model.rb
Constant Summary collapse
- PRIMARY_KEY =
'__openc3__reaction'.freeze
- SCRIPT_REACTION =
'script'.freeze
- COMMAND_REACTION =
'command'.freeze
- NOTIFY_REACTION =
'notify'.freeze
- ACTION_TYPES =
[SCRIPT_REACTION, COMMAND_REACTION, NOTIFY_REACTION]
Instance Attribute Summary collapse
-
#actions ⇒ Object
Returns the value of attribute actions.
-
#enabled ⇒ Object
readonly
Returns the value of attribute enabled.
-
#name ⇒ Object
readonly
Returns the value of attribute name.
-
#scope ⇒ Object
readonly
Returns the value of attribute scope.
-
#shard ⇒ Object
Returns the value of attribute shard.
-
#snooze ⇒ Object
Returns the value of attribute snooze.
-
#snoozed_until ⇒ Object
readonly
Returns the value of attribute snoozed_until.
-
#trigger_level ⇒ Object
Returns the value of attribute trigger_level.
-
#triggers ⇒ Object
Returns the value of attribute triggers.
-
#username ⇒ Object
Returns the value of attribute username.
Attributes inherited from Model
Class Method Summary collapse
-
.all(scope:) ⇒ Array<Hash>
All the Key, Values stored under the name key.
- .create_unique_name(scope:) ⇒ Object
-
.delete(name:, scope:) ⇒ Object
Check dependents before delete.
-
.from_json(json, name:, scope:) ⇒ ReactionModel
Model generated from the passed JSON.
-
.get(name:, scope:) ⇒ ReactionModel
Return the object with the name at.
-
.names(scope:) ⇒ Array<String>
All the uuids stored under the name key.
Instance Method Summary collapse
-
#as_json(*a) ⇒ Hash
Generated from the ReactionModel.
- #awaken ⇒ Object
- #create ⇒ Object
- #create_microservice(topics:) ⇒ Object
- #deploy ⇒ Object
-
#initialize(name:, scope:, snooze:, actions:, triggers:, trigger_level:, enabled: true, snoozed_until: nil, username: nil, shard: 0, updated_at: nil) ⇒ ReactionModel
constructor
A new instance of ReactionModel.
-
#notify(kind:) ⇒ Object
-
update the redis stream / reaction topic that something has changed.
-
- #notify_disable ⇒ Object
- #notify_enable ⇒ Object
- #notify_execute ⇒ Object
- #sleep ⇒ Object
- #undeploy ⇒ Object
- #update ⇒ Object
- #validate_actions(actions) ⇒ Object
- #validate_level(level) ⇒ Object
- #validate_snooze(snooze) ⇒ Object
- #validate_triggers(triggers) ⇒ Object
- #verify_triggers ⇒ Object
Methods inherited from Model
#check_disable_erb, #destroy, #destroyed?, filter, find_all_by_plugin, get_all_models, get_model, handle_config, set, store, store_queued
Constructor Details
#initialize(name:, scope:, snooze:, actions:, triggers:, trigger_level:, enabled: true, snoozed_until: nil, username: nil, shard: 0, updated_at: nil) ⇒ ReactionModel
Returns a new instance of ReactionModel.
87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
# File 'lib/openc3/models/reaction_model.rb', line 87 def initialize( name:, scope:, snooze:, actions:, triggers:, trigger_level:, enabled: true, snoozed_until: nil, username: nil, shard: 0, updated_at: nil ) super("#{scope}#{PRIMARY_KEY}", name: name, scope: scope) @microservice_name = "#{scope}__OPENC3__REACTION" @enabled = enabled @snoozed_until = snoozed_until @trigger_level = validate_level(trigger_level) @snooze = validate_snooze(snooze) @actions = validate_actions(actions) @triggers = validate_triggers(triggers) @username = username @shard = shard.to_i # to_i to handle nil @updated_at = updated_at end |
Instance Attribute Details
#actions ⇒ Object
Returns the value of attribute actions.
84 85 86 |
# File 'lib/openc3/models/reaction_model.rb', line 84 def actions @actions end |
#enabled ⇒ Object (readonly)
Returns the value of attribute enabled.
84 85 86 |
# File 'lib/openc3/models/reaction_model.rb', line 84 def enabled @enabled end |
#name ⇒ Object (readonly)
Returns the value of attribute name.
84 85 86 |
# File 'lib/openc3/models/reaction_model.rb', line 84 def name @name end |
#scope ⇒ Object (readonly)
Returns the value of attribute scope.
84 85 86 |
# File 'lib/openc3/models/reaction_model.rb', line 84 def scope @scope end |
#shard ⇒ Object
Returns the value of attribute shard.
85 86 87 |
# File 'lib/openc3/models/reaction_model.rb', line 85 def shard @shard end |
#snooze ⇒ Object
Returns the value of attribute snooze.
84 85 86 |
# File 'lib/openc3/models/reaction_model.rb', line 84 def snooze @snooze end |
#snoozed_until ⇒ Object (readonly)
Returns the value of attribute snoozed_until.
84 85 86 |
# File 'lib/openc3/models/reaction_model.rb', line 84 def snoozed_until @snoozed_until end |
#trigger_level ⇒ Object
Returns the value of attribute trigger_level.
84 85 86 |
# File 'lib/openc3/models/reaction_model.rb', line 84 def trigger_level @trigger_level end |
#triggers ⇒ Object
Returns the value of attribute triggers.
84 85 86 |
# File 'lib/openc3/models/reaction_model.rb', line 84 def triggers @triggers end |
#username ⇒ Object
Returns the value of attribute username.
85 86 87 |
# File 'lib/openc3/models/reaction_model.rb', line 85 def username @username end |
Class Method Details
.all(scope:) ⇒ Array<Hash>
Returns All the Key, Values stored under the name key.
57 58 59 |
# File 'lib/openc3/models/reaction_model.rb', line 57 def self.all(scope:) super("#{scope}#{PRIMARY_KEY}") end |
.create_unique_name(scope:) ⇒ Object
39 40 41 42 43 44 45 46 |
# File 'lib/openc3/models/reaction_model.rb', line 39 def self.create_unique_name(scope:) reaction_names = self.names(scope: scope) # comes back sorted num = 1 # Users count with 1 if reaction_names[-1] num = reaction_names[-1][5..-1].to_i + 1 end return "REACT#{num}" end |
.delete(name:, scope:) ⇒ Object
Check dependents before delete.
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
# File 'lib/openc3/models/reaction_model.rb', line 67 def self.delete(name:, scope:) model = self.get(name: name, scope: scope) if model.nil? raise ReactionInputError.new "reaction '#{name}' does not exist" end model.triggers.each do | trigger | trigger_model = TriggerModel.get(name: trigger['name'], group: trigger['group'], scope: scope) trigger_model.update_dependents(dependent: name, remove: true) trigger_model.update() end Store.hdel("#{scope}#{PRIMARY_KEY}", name) # No notification as this is only called via reaction_controller which already notifies # undeploy only actually runs if no reactions are left model.undeploy() end |
.from_json(json, name:, scope:) ⇒ ReactionModel
Returns Model generated from the passed JSON.
290 291 292 293 294 |
# File 'lib/openc3/models/reaction_model.rb', line 290 def self.from_json(json, name:, scope:) json = JSON.parse(json, allow_nan: true, create_additions: true) if String === json raise "json data is nil" if json.nil? self.new(**json.transform_keys(&:to_sym), name: name, scope: scope) end |
.get(name:, scope:) ⇒ ReactionModel
Return the object with the name at
49 50 51 52 53 54 |
# File 'lib/openc3/models/reaction_model.rb', line 49 def self.get(name:, scope:) json = super("#{scope}#{PRIMARY_KEY}", name: name) unless json.nil? self.from_json(json, name: name, scope: scope) end end |
.names(scope:) ⇒ Array<String>
Returns All the uuids stored under the name key.
62 63 64 |
# File 'lib/openc3/models/reaction_model.rb', line 62 def self.names(scope:) super("#{scope}#{PRIMARY_KEY}") end |
Instance Method Details
#as_json(*a) ⇒ Hash
Returns generated from the ReactionModel.
273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 |
# File 'lib/openc3/models/reaction_model.rb', line 273 def as_json(*a) return { 'name' => @name, 'scope' => @scope, 'enabled' => @enabled, 'trigger_level' => @trigger_level, 'snooze' => @snooze, 'snoozed_until' => @snoozed_until, 'triggers' => @triggers, 'actions' => @actions, 'username' => @username, 'shard' => @shard, 'updated_at' => @updated_at } end |
#awaken ⇒ Object
265 266 267 268 269 270 |
# File 'lib/openc3/models/reaction_model.rb', line 265 def awaken @snoozed_until = nil @updated_at = Time.now.to_nsec_from_epoch Store.hset(@primary_key, @name, JSON.generate(as_json, allow_nan: true)) notify(kind: 'awakened') end |
#create ⇒ Object
200 201 202 203 204 205 206 207 208 |
# File 'lib/openc3/models/reaction_model.rb', line 200 def create unless Store.hget(@primary_key, @name).nil? raise ReactionInputError.new "existing reaction found: #{@name}" end verify_triggers() @updated_at = Time.now.to_nsec_from_epoch Store.hset(@primary_key, @name, JSON.generate(as_json, allow_nan: true)) notify(kind: 'created') end |
#create_microservice(topics:) ⇒ Object
306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 |
# File 'lib/openc3/models/reaction_model.rb', line 306 def create_microservice(topics:) # reaction Microservice microservice = MicroserviceModel.new( name: @microservice_name, folder_name: nil, cmd: ['ruby', 'reaction_microservice.rb', @microservice_name], work_dir: '/openc3-enterprise/lib/openc3-enterprise/microservices', options: [], topics: topics, target_names: [], plugin: nil, shard: @shard, scope: @scope ) microservice.create end |
#deploy ⇒ Object
323 324 325 326 327 328 |
# File 'lib/openc3/models/reaction_model.rb', line 323 def deploy topics = ["#{@scope}__openc3_autonomic"] if MicroserviceModel.get_model(name: @microservice_name, scope: @scope).nil? create_microservice(topics: topics) end end |
#notify(kind:) ⇒ Object
Returns [] update the redis stream / reaction topic that something has changed.
297 298 299 300 301 302 303 304 |
# File 'lib/openc3/models/reaction_model.rb', line 297 def notify(kind:) notification = { 'kind' => kind, 'type' => 'reaction', 'data' => JSON.generate(as_json, allow_nan: true), } AutonomicTopic.write_notification(notification, scope: @scope) end |
#notify_disable ⇒ Object
242 243 244 245 246 247 248 |
# File 'lib/openc3/models/reaction_model.rb', line 242 def notify_disable @enabled = false # disabling clears the snooze so when it's enabled it can immediately run @snoozed_until = nil notify(kind: 'disabled') # update() will be called by the reaction_microservice end |
#notify_enable ⇒ Object
236 237 238 239 240 |
# File 'lib/openc3/models/reaction_model.rb', line 236 def notify_enable @enabled = true notify(kind: 'enabled') # update() will be called by the reaction_microservice end |
#notify_execute ⇒ Object
250 251 252 253 254 |
# File 'lib/openc3/models/reaction_model.rb', line 250 def notify_execute # Set updated_at because the event is all we get ... no update later @updated_at = Time.now.to_nsec_from_epoch notify(kind: 'executed') end |
#sleep ⇒ Object
256 257 258 259 260 261 262 263 |
# File 'lib/openc3/models/reaction_model.rb', line 256 def sleep if @snooze > 0 @snoozed_until = Time.now.to_i + @snooze @updated_at = Time.now.to_nsec_from_epoch Store.hset(@primary_key, @name, JSON.generate(as_json, allow_nan: true)) notify(kind: 'snoozed') end end |
#undeploy ⇒ Object
330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 |
# File 'lib/openc3/models/reaction_model.rb', line 330 def undeploy return unless ReactionModel.names(scope: @scope).empty? model = MicroserviceModel.get_model(name: @microservice_name, scope: @scope) if model # Let the frontend know that the microservice is shutting down # Custom event which matches the 'deployed' event in ReactionMicroservice notification = { 'kind' => 'undeployed', 'type' => 'reaction', # name and updated_at fields are required for Event formatting 'data' => JSON.generate({ 'name' => @microservice_name, 'updated_at' => Time.now.to_nsec_from_epoch, }), } AutonomicTopic.write_notification(notification, scope: @scope) model.destroy end end |
#update ⇒ Object
210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 |
# File 'lib/openc3/models/reaction_model.rb', line 210 def update old_reaction = ReactionModel.get(name: @name, scope: @scope) if old_reaction # Find triggers that are being removed (in old but not in new) old_trigger_keys = old_reaction.triggers.map { |t| "#{t['group']}:#{t['name']}" } new_trigger_keys = @triggers.map { |t| "#{t['group']}:#{t['name']}" } removed_trigger_keys = old_trigger_keys - new_trigger_keys # Remove this reaction from old triggers' dependents removed_trigger_keys.each do |trigger_key| group, name = trigger_key.split(':', 2) trigger_model = TriggerModel.get(name: name, group: group, scope: @scope) if trigger_model trigger_model.update_dependents(dependent: @name, remove: true) trigger_model.update() end end end verify_triggers() @updated_at = Time.now.to_nsec_from_epoch Store.hset(@primary_key, @name, JSON.generate(as_json, allow_nan: true)) # No notification as this is only called via reaction_controller which already notifies end |
#validate_actions(actions) ⇒ Object
164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
# File 'lib/openc3/models/reaction_model.rb', line 164 def validate_actions(actions) unless actions.is_a?(Array) raise ReactionInputError.new "invalid actions, must be array of hashes: #{actions}" end actions.each do | action | unless action.is_a?(Hash) raise ReactionInputError.new "invalid action, must be a hash: #{action}" end action_type = action['type'] if action_type.nil? raise ReactionInputError.new "invalid action, must contain 'type': #{action}" elsif action['value'].nil? raise ReactionInputError.new "invalid action, must contain 'value': #{action}" end unless ACTION_TYPES.include?(action_type) raise ReactionInputError.new "invalid action type '#{action_type}', must be one of #{ACTION_TYPES}" end end return actions end |
#validate_level(level) ⇒ Object
127 128 129 130 131 132 133 134 |
# File 'lib/openc3/models/reaction_model.rb', line 127 def validate_level(level) case level when 'EDGE', 'LEVEL' return level else raise ReactionInputError.new "invalid trigger level, must be EDGE or LEVEL: #{level}" end end |
#validate_snooze(snooze) ⇒ Object
136 137 138 139 140 |
# File 'lib/openc3/models/reaction_model.rb', line 136 def validate_snooze(snooze) Integer(snooze) rescue raise ReactionInputError.new "invalid snooze value: #{snooze}" end |
#validate_triggers(triggers) ⇒ Object
142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/openc3/models/reaction_model.rb', line 142 def validate_triggers(triggers) unless triggers.is_a?(Array) raise ReactionInputError.new "invalid triggers, must be array of hashes: #{triggers}" end trigger_hash = {} triggers.each do | trigger | unless trigger.is_a?(Hash) raise ReactionInputError.new "invalid trigger, must be hash: #{trigger}" end if trigger['name'].nil? || trigger['group'].nil? raise ReactionInputError.new "invalid trigger, must contain 'name' and 'group' keys: #{trigger}" end trigger_name = trigger['name'] unless trigger_hash[trigger_name].nil? raise ReactionInputError.new "no duplicate triggers allowed: #{triggers}" else trigger_hash[trigger_name] = 1 end end return triggers end |
#verify_triggers ⇒ Object
185 186 187 188 189 190 191 192 193 194 195 196 197 198 |
# File 'lib/openc3/models/reaction_model.rb', line 185 def verify_triggers if @triggers.empty? raise ReactionInputError.new "reaction must contain at least one valid trigger: #{@triggers}" end @triggers.each do | trigger | model = TriggerModel.get(name: trigger['name'], group: trigger['group'], scope: @scope) if model.nil? raise ReactionInputError.new "failed to find trigger: #{trigger}" end model.update_dependents(dependent: @name) model.update() end end |