Class: OpenC3::SortedModel

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

Direct Known Subclasses

MetadataModel, NoteModel

Constant Summary collapse

SORTED_TYPE =

To be overridden by base class

'sorted'.freeze
PRIMARY_KEY =

To be overridden by base class

'__SORTED'.freeze

Instance Attribute Summary collapse

Attributes inherited from Model

#name, #plugin, #scope, #updated_at

Class Method Summary collapse

Instance Method Summary collapse

Methods inherited from Model

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

Constructor Details

#initialize(start:, scope:, type: SORTED_TYPE, **kwargs) ⇒ SortedModel

Returns a new instance of SortedModel.

Parameters:

  • start (Integer)
    • start used to store data

  • scope (String)
    • OpenC3 scope to track event to

  • kwargs (Anything)
    • Any kwargs to store in the JSON



108
109
110
111
112
113
# File 'lib/openc3/models/sorted_model.rb', line 108

def initialize(start:, scope:, type: SORTED_TYPE, **kwargs)
  # Name becomes the start in the base class
  super(self.class.pk(scope), name: start.to_s, scope: scope, **kwargs)
  @type = type # For the as_json, from_json round trip
  @start = start
end

Instance Attribute Details

#startObject (readonly)

Returns the value of attribute start.



103
104
105
# File 'lib/openc3/models/sorted_model.rb', line 103

def start
  @start
end

Class Method Details

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

Returns Array up to the limit of the models (as Hash objects) stored under the primary key.

Returns:

  • (Array<Hash>)

    Array up to the limit of the models (as Hash objects) stored under the primary key



58
59
60
61
# File 'lib/openc3/models/sorted_model.rb', line 58

def self.all(scope:, limit: 100)
  result = Store.zrevrangebyscore(self.pk(scope), '+inf', '-inf', limit: [0, limit])
  return result.map { |item| JSON.parse(item, :allow_nan => true, :create_additions => true) }
end

.count(scope:) ⇒ Integer

Returns count of the members stored under the primary key.

Returns:

  • (Integer)

    count of the members stored under the primary key



83
84
85
# File 'lib/openc3/models/sorted_model.rb', line 83

def self.count(scope:)
  return Store.zcard(self.pk(scope))
end

.destroy(scope:, start:) ⇒ Integer

Remove member from a sorted set

Returns:

  • (Integer)

    count of the members removed, 0 if not found



89
90
91
92
93
# File 'lib/openc3/models/sorted_model.rb', line 89

def self.destroy(scope:, start:)
  result = Store.zremrangebyscore(self.pk(scope), start, start)
  self.notify(kind: 'deleted', start: start, scope: scope)
  return result
end

.get(start:, scope:) ⇒ String|nil

Returns String of the saved json or nil if start not found.

Returns:

  • (String|nil)

    String of the saved json or nil if start not found



51
52
53
54
55
# File 'lib/openc3/models/sorted_model.rb', line 51

def self.get(start:, scope:)
  result = Store.zrangebyscore(self.pk(scope), start, start)
  return JSON.parse(result[0], :allow_nan => true, :create_additions => true) unless result.empty?
  nil
end

.get_current_value(scope:) ⇒ String|nil

Returns json or nil if metadata empty.

Returns:

  • (String|nil)

    json or nil if metadata empty



64
65
66
67
68
69
# File 'lib/openc3/models/sorted_model.rb', line 64

def self.get_current_value(scope:)
  start = Time.now.to_i
  array = Store.zrevrangebyscore(self.pk(scope), start, '-inf', limit: [0, 1])
  return nil if array.empty?
  return array[0]
end

.notify(scope:, kind:, start:, stop: nil) ⇒ Object

MUST be overridden by any subclasses



46
47
48
# File 'lib/openc3/models/sorted_model.rb', line 46

def self.notify(scope:, kind:, start:, stop: nil)
  # Do nothing by default
end

.pk(scope) ⇒ Object

MUST be overridden by any subclasses



41
42
43
# File 'lib/openc3/models/sorted_model.rb', line 41

def self.pk(scope)
  return "#{scope}#{PRIMARY_KEY}"
end

.range(start:, stop:, scope:, limit: 100) ⇒ Array|nil

Returns Array up to 100 of this model or empty array.

Parameters:

  • start (Integer)

    Start time to return values (inclusive)

  • stop (Integer)

    Stop time to return values (inclusive)

Returns:

  • (Array|nil)

    Array up to 100 of this model or empty array



74
75
76
77
78
79
80
# File 'lib/openc3/models/sorted_model.rb', line 74

def self.range(start:, stop:, scope:, limit: 100)
  if start > stop
    raise SortedInputError.new "start: #{start} must be before stop: #{stop}"
  end
  result = Store.zrangebyscore(self.pk(scope), start, stop, limit: [0, limit])
  return result.map { |item| JSON.parse(item, :allow_nan => true, :create_additions => true) }
end

.range_destroy(scope:, start:, stop:) ⇒ Integer

Remove members from min to max of the sorted set.

Returns:

  • (Integer)

    count of the members removed



97
98
99
100
101
# File 'lib/openc3/models/sorted_model.rb', line 97

def self.range_destroy(scope:, start:, stop:)
  result = Store.zremrangebyscore(self.pk(scope), start, stop)
  self.notify(kind: 'deleted', start: start, stop: stop, scope: scope)
  return result
end

Instance Method Details

#as_json(*a) ⇒ Hash

Returns JSON encoding of this model.

Returns:

  • (Hash)

    JSON encoding of this model



172
173
174
175
176
177
# File 'lib/openc3/models/sorted_model.rb', line 172

def as_json(*a)
  { **super(*a),
    'start' => @start,
    'type' => SORTED_TYPE,
  }
end

#create(update: false) ⇒ Object

Update the Redis hash at primary_key based on the initial passed start The member is set to the JSON generated via calling as_json



131
132
133
134
135
136
137
138
139
140
141
# File 'lib/openc3/models/sorted_model.rb', line 131

def create(update: false)
  validate_start(update: update)
  @updated_at = Time.now.to_nsec_from_epoch
  SortedModel.destroy(scope: @scope, start: update) if update
  Store.zadd(@primary_key, @start, JSON.generate(as_json(:allow_nan => true)))
  if update
    notify(kind: 'updated')
  else
    notify(kind: 'created')
  end
end

#destroyObject

destroy the activity from the redis database



151
152
153
154
# File 'lib/openc3/models/sorted_model.rb', line 151

def destroy
  self.class.destroy(scope: @scope, start: @start)
  notify(kind: 'deleted')
end

#notify(kind:, extra: nil) ⇒ Object

Returns [] update the redis stream / timeline topic that something has changed.

Returns:

  • update the redis stream / timeline topic that something has changed



157
158
159
160
161
162
163
164
165
166
167
168
169
# File 'lib/openc3/models/sorted_model.rb', line 157

def notify(kind:, extra: nil)
  notification = {
    'data' => JSON.generate(as_json(:allow_nan => true)),
    'kind' => kind,
    'type' => 'calendar',
  }
  notification['extra'] = extra unless extra.nil?
  begin
    CalendarTopic.write_entry(notification, scope: @scope)
  rescue StandardError => e
    raise SortedError.new "Failed to write to stream: #{notification}, #{e}"
  end
end

#update(start:) ⇒ Object

Update the Redis hash at primary_key



144
145
146
147
148
# File 'lib/openc3/models/sorted_model.rb', line 144

def update(start:)
  orig_start = @start
  @start = start
  create(update: orig_start)
end

#validate_start(update: false) ⇒ Object

start MUST be a positive integer



116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/openc3/models/sorted_model.rb', line 116

def validate_start(update: false)
  unless @start.is_a?(Integer)
    raise SortedInputError.new "start must be integer: #{@start}"
  end
  if @start.to_i < 0
    raise SortedInputError.new "start must be positive: #{@start}"
  end
  if !update and self.class.get(start: @start, scope: @scope)
    raise SortedOverlapError.new "duplicate, existing data at #{@start}"
  end
  @start = @start.to_i
end