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 overriden by base class

'sorted'.freeze
PRIMARY_KEY =

To be overriden 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

#as_config, #deploy, #destroyed?, filter, find_all_by_plugin, from_json, get_all_models, get_model, handle_config, names, set, store, #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



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

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.



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

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



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

def self.all(scope:, limit: 100)
  result = Store.zrevrangebyscore(self.pk(scope), '+inf', '-inf', limit: [0, limit])
  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



75
76
77
# File 'lib/openc3/models/sorted_model.rb', line 75

def self.count(scope:)
  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



81
82
83
# File 'lib/openc3/models/sorted_model.rb', line 81

def self.destroy(scope:, start:)
  Store.zremrangebyscore(self.pk(scope), start, start)
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



43
44
45
46
47
# File 'lib/openc3/models/sorted_model.rb', line 43

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



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

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

.pk(scope) ⇒ Object

MUST be overriden by any subclasses



38
39
40
# File 'lib/openc3/models/sorted_model.rb', line 38

def self.pk(scope)
  "#{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



66
67
68
69
70
71
72
# File 'lib/openc3/models/sorted_model.rb', line 66

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])
  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



87
88
89
# File 'lib/openc3/models/sorted_model.rb', line 87

def self.range_destroy(scope:, start:, stop:)
  Store.zremrangebyscore(self.pk(scope), start, stop)
end

Instance Method Details

#as_json(*a) ⇒ Hash

Returns JSON encoding of this model.

Returns:

  • (Hash)

    JSON encoding of this model



160
161
162
163
164
165
# File 'lib/openc3/models/sorted_model.rb', line 160

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



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

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



139
140
141
142
# File 'lib/openc3/models/sorted_model.rb', line 139

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



145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/openc3/models/sorted_model.rb', line 145

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



132
133
134
135
136
# File 'lib/openc3/models/sorted_model.rb', line 132

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

#validate_start(update: false) ⇒ Object

start MUST be a positive integer



104
105
106
107
108
109
110
111
112
113
114
115
# File 'lib/openc3/models/sorted_model.rb', line 104

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