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

#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



99
100
101
102
103
104
# File 'lib/openc3/models/sorted_model.rb', line 99

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.



94
95
96
# File 'lib/openc3/models/sorted_model.rb', line 94

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



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

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



78
79
80
# File 'lib/openc3/models/sorted_model.rb', line 78

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



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

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



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

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



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

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



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

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



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

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



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

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



163
164
165
166
167
168
# File 'lib/openc3/models/sorted_model.rb', line 163

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



122
123
124
125
126
127
128
129
130
131
132
# File 'lib/openc3/models/sorted_model.rb', line 122

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



142
143
144
145
# File 'lib/openc3/models/sorted_model.rb', line 142

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



148
149
150
151
152
153
154
155
156
157
158
159
160
# File 'lib/openc3/models/sorted_model.rb', line 148

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



135
136
137
138
139
# File 'lib/openc3/models/sorted_model.rb', line 135

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

#validate_start(update: false) ⇒ Object

start MUST be a positive integer



107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/openc3/models/sorted_model.rb', line 107

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