Class: OpenC3::TriggerModel

Inherits:
Model show all
Defined in:
lib/openc3/models/trigger_model.rb

Overview

INPUT:

{
  "group": "someGroup",
  "left": {
    "type": "item",
    "target": "INST",
    "packet": "ADCS",
    "item": "POSX",
    "valueType": "RAW",
  },
  "operator": ">",
  "right": {
    "type": "value",
    "value": 690000,
  }
}

Constant Summary collapse

PRIMARY_KEY =
'__TRIGGERS__'.freeze
ITEM_TYPE =
'item'.freeze
LIMIT_TYPE =
'limit'.freeze
FLOAT_TYPE =
'float'.freeze
STRING_TYPE =
'string'.freeze
REGEX_TYPE =
'regex'.freeze
TRIGGER_TYPE =
'trigger'.freeze

Instance Attribute Summary collapse

Attributes inherited from Model

#plugin, #updated_at

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Model

#check_disable_erb, #deploy, #destroy, #destroyed?, filter, find_all_by_plugin, get_all_models, get_model, handle_config, set, store, store_queued, #undeploy

Constructor Details

#initialize(name:, scope:, group:, left:, operator:, right:, state: false, enabled: true, dependents: nil, updated_at: nil) ⇒ TriggerModel



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/openc3/models/trigger_model.rb', line 106

def initialize(
  name:,
  scope:,
  group:,
  left:,
  operator:,
  right:,
  state: false,
  enabled: true,
  dependents: nil,
  updated_at: nil
)
  super("#{scope}#{PRIMARY_KEY}#{group}", name: name, scope: scope)
  @roots = []
  @group = group
  @state = state
  @enabled = enabled
  @left = validate_operand(operand: left)
  @operator = validate_operator(operator: operator)
  @right = validate_operand(operand: right, right: true)
  @dependents = dependents
  @updated_at = updated_at
  selected_group = TriggerGroupModel.get(name: @group, scope: @scope)
  if selected_group.nil?
    raise TriggerInputError.new "failed to find group: '#{@group}'"
  end
end

Instance Attribute Details

#dependentsObject (readonly)

Returns the value of attribute dependents.



104
105
106
# File 'lib/openc3/models/trigger_model.rb', line 104

def dependents
  @dependents
end

#enabledObject (readonly)

Returns the value of attribute enabled.



104
105
106
# File 'lib/openc3/models/trigger_model.rb', line 104

def enabled
  @enabled
end

#groupObject (readonly)

Returns the value of attribute group.



104
105
106
# File 'lib/openc3/models/trigger_model.rb', line 104

def group
  @group
end

#leftObject

Returns the value of attribute left.



104
105
106
# File 'lib/openc3/models/trigger_model.rb', line 104

def left
  @left
end

#nameObject (readonly)

Returns the value of attribute name.



104
105
106
# File 'lib/openc3/models/trigger_model.rb', line 104

def name
  @name
end

#operatorObject

Returns the value of attribute operator.



104
105
106
# File 'lib/openc3/models/trigger_model.rb', line 104

def operator
  @operator
end

#rightObject

Returns the value of attribute right.



104
105
106
# File 'lib/openc3/models/trigger_model.rb', line 104

def right
  @right
end

#rootsObject (readonly)

Returns the value of attribute roots.



104
105
106
# File 'lib/openc3/models/trigger_model.rb', line 104

def roots
  @roots
end

#scopeObject (readonly)

Returns the value of attribute scope.



104
105
106
# File 'lib/openc3/models/trigger_model.rb', line 104

def scope
  @scope
end

#stateObject

Returns the value of attribute state.



104
105
106
# File 'lib/openc3/models/trigger_model.rb', line 104

def state
  @state
end

Class Method Details

.all(group:, scope:) ⇒ Array<Hash>



77
78
79
# File 'lib/openc3/models/trigger_model.rb', line 77

def self.all(group:, scope:)
  super("#{scope}#{PRIMARY_KEY}#{group}")
end

.create_unique_name(group:, scope:) ⇒ Object



59
60
61
62
63
64
65
66
# File 'lib/openc3/models/trigger_model.rb', line 59

def self.create_unique_name(group:, scope:)
  trigger_names = self.names(group: group, scope: scope) # comes back sorted
  num = 1 # Users count with 1
  if trigger_names[-1]
    num = trigger_names[-1][4..-1].to_i + 1
  end
  return "TRIG#{num}"
end

.delete(name:, group:, scope:) ⇒ Object

Check dependents before delete.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/openc3/models/trigger_model.rb', line 87

def self.delete(name:, group:, scope:)
  model = self.get(name: name, group: group, scope: scope)
  if model.nil?
    raise TriggerInputError.new "trigger #{group}:#{name} does not exist"
  end
  unless model.dependents.empty?
    raise TriggerError.new "#{group}:#{name} has dependents: #{model.dependents}"
  end
  model.roots.each do | trigger |
    trigger_model = self.get(name: trigger, group: group, scope: scope)
    trigger_model.update_dependents(dependent: name, remove: true)
    trigger_model.update()
  end
  Store.hdel("#{scope}#{PRIMARY_KEY}#{group}", name)
  # No notification as this is only called via trigger_controller which already notifies
end

.from_json(json, name:, scope:) ⇒ TriggerModel



280
281
282
283
284
# File 'lib/openc3/models/trigger_model.rb', line 280

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:, group:, scope:) ⇒ TriggerModel



69
70
71
72
73
74
# File 'lib/openc3/models/trigger_model.rb', line 69

def self.get(name:, group:, scope:)
  json = super("#{scope}#{PRIMARY_KEY}#{group}", name: name)
  unless json.nil?
    self.from_json(json, name: name, scope: scope)
  end
end

.names(group:, scope:) ⇒ Array<String>



82
83
84
# File 'lib/openc3/models/trigger_model.rb', line 82

def self.names(group:, scope:)
  super("#{scope}#{PRIMARY_KEY}#{group}")
end

Instance Method Details

#as_json(*a) ⇒ Hash



264
265
266
267
268
269
270
271
272
273
274
275
276
277
# File 'lib/openc3/models/trigger_model.rb', line 264

def as_json(*a)
  return {
    'name' => @name,
    'scope' => @scope,
    'state' => @state,
    'enabled' => @enabled,
    'group' => @group,
    'dependents' => @dependents,
    'left' => @left,
    'operator' => @operator,
    'right' => @right,
    'updated_at' => @updated_at,
  }
end

#createObject



197
198
199
200
201
202
203
204
205
# File 'lib/openc3/models/trigger_model.rb', line 197

def create
  unless Store.hget(@primary_key, @name).nil?
    raise TriggerInputError.new "existing trigger 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

#disableObject



232
233
234
235
236
# File 'lib/openc3/models/trigger_model.rb', line 232

def disable
  notify_disable()
  @updated_at = Time.now.to_nsec_from_epoch
  Store.hset(@primary_key, @name, JSON.generate(as_json(:allow_nan => true)))
end

#generate_topicsObject

“#@scope__DECOM__#{@target}__#@packet”


239
240
241
242
243
244
245
246
247
248
# File 'lib/openc3/models/trigger_model.rb', line 239

def generate_topics
  topics = Hash.new
  if @left['type'] == ITEM_TYPE
    topics["#{@scope}__DECOM__{#{left['target']}}__#{left['packet']}"] = 1
  end
  if @right and @right['type'] == ITEM_TYPE
    topics["#{@scope}__DECOM__{#{right['target']}}__#{right['packet']}"] = 1
  end
  return topics.keys
end

#notify(kind:) ⇒ Object



287
288
289
290
291
292
293
294
# File 'lib/openc3/models/trigger_model.rb', line 287

def notify(kind:)
  notification = {
    'kind' => kind,
    'type' => 'trigger',
    'data' => JSON.generate(as_json(:allow_nan => true)),
  }
  AutonomicTopic.write_notification(notification, scope: @scope)
end

#notify_disableObject



226
227
228
229
230
# File 'lib/openc3/models/trigger_model.rb', line 226

def notify_disable
  @enabled = false
  @state = false
  notify(kind: 'disabled')
end

#notify_enableObject



221
222
223
224
# File 'lib/openc3/models/trigger_model.rb', line 221

def notify_enable
  @enabled = true
  notify(kind: 'enabled')
end

#to_sString



259
260
261
# File 'lib/openc3/models/trigger_model.rb', line 259

def to_s
  return "OpenC3::TriggerModel:#{@scope}:#{group}:#{@name})"
end

#updateObject



207
208
209
210
211
212
# File 'lib/openc3/models/trigger_model.rb', line 207

def update
  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 trigger_controller which already notifies
end

#update_dependents(dependent:, remove: false) ⇒ Object



250
251
252
253
254
255
256
# File 'lib/openc3/models/trigger_model.rb', line 250

def update_dependents(dependent:, remove: false)
  if remove
    @dependents.delete(dependent)
  elsif @dependents.index(dependent).nil?
    @dependents << dependent
  end
end

#validate_operand(operand:, right: false) ⇒ Object



145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# File 'lib/openc3/models/trigger_model.rb', line 145

def validate_operand(operand:, right: false)
  return operand if right and @operator.include?('CHANGE')
  unless operand.is_a?(Hash)
    raise TriggerInputError.new "invalid operand: #{operand}"
  end
  operand_types = [ITEM_TYPE, LIMIT_TYPE, FLOAT_TYPE, STRING_TYPE, REGEX_TYPE, TRIGGER_TYPE]
  unless operand_types.include?(operand['type'])
    raise TriggerInputError.new "invalid operand, type '#{operand['type']}' must be one of #{operand_types}"
  end
  if operand[operand['type']].nil?
    raise TriggerInputError.new "invalid operand, type value '#{operand['type']}' must be a key: #{operand}"
  end
  case operand['type']
  when ITEM_TYPE
    # We don't need to check for 'item' because the above check already does it
    if operand['target'].nil? || operand['packet'].nil? || operand['valueType'].nil?
      raise TriggerInputError.new "invalid operand, must contain target, packet, item and valueType: #{operand}"
    end
  when TRIGGER_TYPE
    @roots << operand[operand['type']]
  end
  return operand
end

#validate_operator(operator:) ⇒ Object



169
170
171
172
173
174
175
176
177
178
179
180
181
# File 'lib/openc3/models/trigger_model.rb', line 169

def validate_operator(operator:)
  operators = ['>', '<', '>=', '<=', '==', '!=', 'CHANGES', 'DOES NOT CHANGE']
  trigger_operators = ['AND', 'OR']
  if @roots.empty? && operators.include?(operator)
    return operator
  elsif !@roots.empty? && trigger_operators.include?(operator)
    return operator
  elsif operators.include?(operator)
    raise TriggerInputError.new "invalid operator for triggers: '#{operator}' must be one of #{trigger_operators}"
  else
    raise TriggerInputError.new "invalid operator: '#{operator}' must be one of #{operators}"
  end
end

#verify_triggersObject



183
184
185
186
187
188
189
190
191
192
193
194
195
# File 'lib/openc3/models/trigger_model.rb', line 183

def verify_triggers
  @dependents = [] if @dependents.nil?
  @roots.each do | trigger |
    model = TriggerModel.get(name: trigger, group: @group, scope: @scope)
    if model.nil?
      raise TriggerInputError.new "failed to find dependent trigger: '#{@group}:#{trigger}'"
    end
    unless model.dependents.include?(@name)
      model.update_dependents(dependent: @name)
      model.update()
    end
  end
end