openc3 5.9.0 → 5.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of openc3 might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 35a757bea46ad5b21ac348aa90b6163595d04c702b752b728f628d7dd82f6132
4
- data.tar.gz: 149be2f48cdb7864a3665bf46982a1e16fb37541f68203387297c14d0d55e7d8
3
+ metadata.gz: 9c37c79f2062a5921e13c3370ce281c443b9abba929ca36f152df7cd1bcea92e
4
+ data.tar.gz: 643d74e05f61eb9016eaefbe5dca61316939e3d4e063fcf4150423cee4d95474
5
5
  SHA512:
6
- metadata.gz: 1595ea101a9473b1aa5a1894d08bcd5704943fae771f9e58c4506b905cae9cb5957ab0fc961349ec9ecc4466a3ae34b6987a2a9d21ed8b9d2b0d31592c16908b
7
- data.tar.gz: afbf06dfc4d4dbbfd7916146b6ee2b3005959bae650735127710b257a675487591adb5739744e603ef5eb20cc545b4dc79962021aaef00127c9aa76017a4ba3e
6
+ metadata.gz: 25285376d600c16a3d0b6d485a359998fda7226fc3781f639cdde992370291fa7d23b0ce6ca89e02ddcf4e6386dd596839ed4d8c704003b893e37e726ed1a7e4
7
+ data.tar.gz: 3ae81b7ea67047a8e23cfb161aeb77b4e7224e44b2c9c1dc4fa2dd78cb06eae50710b0a5c882cfcaf8da9e0214b6b96e517ec2a6af0b7671f5b8e8a0dc1448fe
@@ -17,7 +17,7 @@
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
- # This file may also be used under the terms of a commercial license
20
+ # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
23
  # This file contains the implementation of the BinaryAccessor class.
@@ -280,7 +280,10 @@ module OpenC3
280
280
 
281
281
  if upper_bound > lower_bound
282
282
  # Combine bytes into a FixNum
283
- temp_data[1..temp_upper].each_byte { |temp_value| temp = temp << 8; temp = temp + temp_value }
283
+ temp_data[1..temp_upper].each_byte do |temp_value|
284
+ temp = temp << 8
285
+ temp = temp + temp_value
286
+ end
284
287
  end
285
288
 
286
289
  # Shift off unwanted bits at end
@@ -172,7 +172,7 @@ module OpenC3
172
172
 
173
173
  TargetModel.set_packet(target_name, packet_name, packet, scope: scope)
174
174
 
175
- message = "Disabling Limits For '#{target_name} #{packet_name} #{item_name}'"
175
+ message = "Disabling Limits for '#{target_name} #{packet_name} #{item_name}'"
176
176
  Logger.info(message, scope: scope)
177
177
 
178
178
  event = { type: :LIMITS_ENABLE_STATE, target_name: target_name, packet_name: packet_name,
@@ -344,11 +344,11 @@ module OpenC3
344
344
  if action == :enable
345
345
  enabled = true
346
346
  item['limits']['enabled'] = true
347
- message = "Enabling Limits For '#{target_name} #{packet_name} #{item_name}'"
347
+ message = "Enabling Limits for '#{target_name} #{packet_name} #{item_name}'"
348
348
  elsif action == :disable
349
349
  enabled = false
350
350
  item['limits'].delete('enabled')
351
- message = "Disabling Limits For '#{target_name} #{packet_name} #{item_name}'"
351
+ message = "Disabling Limits for '#{target_name} #{packet_name} #{item_name}'"
352
352
  end
353
353
  Logger.info(message, scope: scope)
354
354
 
@@ -373,26 +373,10 @@ module OpenC3
373
373
  # @param item_name [String] item name
374
374
  # @param scope [String] scope
375
375
  # @return Hash The requested item based on the packet name
376
- def _get_item(target_name, packet_name, item_name, scope:)
377
- requested_item = nil
378
- if packet_name == 'LATEST'
379
- latest = -1
380
- TargetModel.packets(target_name, scope: scope).each do |packet|
381
- item = packet['items'].find { |item| item['name'] == item_name }
382
- if item
383
- hash = CvtModel.get(target_name: target_name, packet_name: packet['packet_name'], scope: scope)
384
- if hash['PACKET_TIMESECONDS'] && hash['PACKET_TIMESECONDS'] > latest
385
- latest = hash['PACKET_TIMESECONDS']
386
- requested_item = item
387
- end
388
- end
389
- end
390
- raise "Item '#{target_name} LATEST #{item_name}' does not exist" if latest == -1
391
- else
392
- # Determine if this item exists, it will raise appropriate errors if not
393
- requested_item = TargetModel.packet_item(target_name, packet_name, item_name, scope: scope)
394
- end
395
- return requested_item
376
+ def _get_item(target_name, packet_name, item_name, cache_timeout: 0.1, scope:)
377
+ # Determine if this item exists, it will raise appropriate errors if not
378
+ packet_name = CvtModel.determine_latest_packet_for_item(target_name, item_name, cache_timeout: cache_timeout, scope: $openc3_scope) if packet_name == 'LATEST'
379
+ return TargetModel.packet_item(target_name, packet_name, item_name, scope: scope)
396
380
  end
397
381
  end
398
382
  end
@@ -26,19 +26,22 @@ module OpenC3
26
26
  module Api
27
27
  WHITELIST ||= []
28
28
  WHITELIST.concat([
29
- 'get_target_list',
30
- 'get_target',
31
- 'get_target_interfaces',
32
- 'get_all_target_info', # DEPRECATED
33
- ])
29
+ 'get_target_names',
30
+ 'get_target_list', # DEPRECATED
31
+ 'get_target',
32
+ 'get_target_interfaces',
33
+ 'get_all_target_info', # DEPRECATED
34
+ ])
34
35
 
35
36
  # Returns the list of all target names
36
37
  #
37
38
  # @return [Array<String>] All target names
38
- def get_target_list(scope: $openc3_scope, token: $openc3_token)
39
+ def get_target_names(scope: $openc3_scope, token: $openc3_token)
39
40
  authorize(permission: 'tlm', scope: scope, token: token)
40
41
  TargetModel.names(scope: scope)
41
42
  end
43
+ # get_target_list is DEPRECATED
44
+ alias get_target_list get_target_names
42
45
 
43
46
  # Gets the full target hash
44
47
  #
@@ -57,7 +60,7 @@ module OpenC3
57
60
  authorize(permission: 'system', scope: scope, token: token)
58
61
  info = []
59
62
  interfaces = InterfaceModel.all(scope: scope)
60
- get_target_list(scope: scope, token: token).each do |target_name|
63
+ get_target_names(scope: scope, token: token).each do |target_name|
61
64
  interface_names = []
62
65
  interfaces.each do |name, interface|
63
66
  if interface['target_names'].include? target_name
@@ -76,7 +79,7 @@ module OpenC3
76
79
  def get_all_target_info(scope: $openc3_scope, token: $openc3_token)
77
80
  authorize(permission: 'system', scope: scope, token: token)
78
81
  info = []
79
- get_target_list(scope: scope, token: token).each do |target_name|
82
+ get_target_names(scope: scope, token: token).each do |target_name|
80
83
  cmd_cnt = 0
81
84
  packets = TargetModel.packets(target_name, type: :CMD, scope: scope)
82
85
  packets.each do |packet|
@@ -66,30 +66,30 @@ module OpenC3
66
66
  # @param args [String|Array<String>] See the description for calling style
67
67
  # @param type [Symbol] Telemetry type, :RAW, :CONVERTED (default), :FORMATTED, or :WITH_UNITS
68
68
  # @return [Object] The telemetry value formatted as requested
69
- def tlm(*args, type: :CONVERTED, scope: $openc3_scope, token: $openc3_token)
70
- target_name, packet_name, item_name = tlm_process_args(args, 'tlm', scope: scope)
69
+ def tlm(*args, type: :CONVERTED, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
70
+ target_name, packet_name, item_name = tlm_process_args(args, 'tlm', cache_timeout: cache_timeout, scope: scope)
71
71
  authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
72
- CvtModel.get_item(target_name, packet_name, item_name, type: type.intern, scope: scope)
72
+ CvtModel.get_item(target_name, packet_name, item_name, type: type.intern, cache_timeout: cache_timeout, scope: scope)
73
73
  end
74
74
 
75
75
  # @deprecated Use tlm with type: :RAW
76
- def tlm_raw(*args, scope: $openc3_scope, token: $openc3_token)
77
- tlm(*args, type: :RAW, scope: scope, token: token)
76
+ def tlm_raw(*args, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
77
+ tlm(*args, type: :RAW, cache_timeout: cache_timeout, scope: scope, token: token)
78
78
  end
79
79
 
80
80
  # @deprecated Use tlm with type: :FORMATTED
81
- def tlm_formatted(*args, scope: $openc3_scope, token: $openc3_token)
82
- tlm(*args, type: :FORMATTED, scope: scope, token: token)
81
+ def tlm_formatted(*args, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
82
+ tlm(*args, type: :FORMATTED, cache_timeout: cache_timeout, scope: scope, token: token)
83
83
  end
84
84
 
85
85
  # @deprecated Use tlm with type: :WITH_UNITS
86
- def tlm_with_units(*args, scope: $openc3_scope, token: $openc3_token)
87
- tlm(*args, type: :WITH_UNITS, scope: scope, token: token)
86
+ def tlm_with_units(*args, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
87
+ tlm(*args, type: :WITH_UNITS, cache_timeout: cache_timeout, scope: scope, token: token)
88
88
  end
89
89
 
90
90
  # @deprecated Use tlm with type:
91
- def tlm_variable(*args, scope: $openc3_scope, token: $openc3_token)
92
- tlm(*args[0..-2], type: args[-1].intern, scope: scope, token: token)
91
+ def tlm_variable(*args, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
92
+ tlm(*args[0..-2], type: args[-1].intern, cache_timeout: cache_timeout, scope: scope, token: token)
93
93
  end
94
94
 
95
95
  # Set a telemetry item in the current value table.
@@ -227,7 +227,7 @@ module OpenC3
227
227
  # @return [Array<String, Object, Symbol|nil>] Returns an Array consisting
228
228
  # of [item name, item value, item limits state] where the item limits
229
229
  # state can be one of {OpenC3::Limits::LIMITS_STATES}
230
- def get_tlm_packet(target_name, packet_name, stale_time: 30, type: :CONVERTED, scope: $openc3_scope, token: $openc3_token)
230
+ def get_tlm_packet(target_name, packet_name, stale_time: 30, type: :CONVERTED, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
231
231
  target_name = target_name.upcase
232
232
  packet_name = packet_name.upcase
233
233
  authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
@@ -235,8 +235,8 @@ module OpenC3
235
235
  t = _validate_tlm_type(type)
236
236
  raise ArgumentError, "Unknown type '#{type}' for #{target_name} #{packet_name}" if t.nil?
237
237
  items = packet['items'].map { | item | item['name'].upcase }
238
- cvt_items = items.map { | item | "#{target_name}__#{packet_name}__#{item}__#{type}" }
239
- current_values = CvtModel.get_tlm_values(cvt_items, stale_time: stale_time, scope: scope)
238
+ cvt_items = items.map { | item | [target_name, packet_name, item, type] }
239
+ current_values = CvtModel.get_tlm_values(cvt_items, stale_time: stale_time, cache_timeout: cache_timeout, scope: scope)
240
240
  items.zip(current_values).map { | item , values | [item, values[0], values[1]]}
241
241
  end
242
242
 
@@ -250,25 +250,26 @@ module OpenC3
250
250
  # @return [Array<Object, Symbol>]
251
251
  # Array consisting of the item value and limits state
252
252
  # given as symbols such as :RED, :YELLOW, :STALE
253
- def get_tlm_values(items, stale_time: 30, scope: $openc3_scope, token: $openc3_token)
253
+ def get_tlm_values(items, stale_time: 30, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
254
254
  if !items.is_a?(Array) || !items[0].is_a?(String)
255
255
  raise ArgumentError, "items must be array of strings: ['TGT__PKT__ITEM__TYPE', ...]"
256
256
  end
257
+ packets = []
258
+ cvt_items = []
257
259
  items.each_with_index do |item, index|
258
- target_name, packet_name, item_name, value_type = item.split('__')
260
+ item_upcase = item.to_s.upcase
261
+ target_name, packet_name, item_name, value_type = item_upcase.split('__')
259
262
  raise ArgumentError, "items must be formatted as TGT__PKT__ITEM__TYPE" if target_name.nil? || packet_name.nil? || item_name.nil? || value_type.nil?
260
- target_name = target_name.upcase
261
- packet_name = packet_name.upcase
262
- item_name = item_name.upcase
263
- value_type = value_type.upcase
264
- if packet_name == 'LATEST'
265
- _, packet_name, _ = tlm_process_args([target_name, packet_name, item_name], 'get_tlm_values', scope: scope) # Figure out which packet is LATEST
266
- end
263
+ packet_name = CvtModel.determine_latest_packet_for_item(target_name, item_name, cache_timeout: cache_timeout, scope: scope) if packet_name == 'LATEST'
267
264
  # Change packet_name in case of LATEST and ensure upcase
268
- items[index] = "#{target_name}__#{packet_name}__#{item_name}__#{value_type}"
265
+ cvt_items[index] = [target_name, packet_name, item_name, value_type]
266
+ packets << [target_name, packet_name]
267
+ end
268
+ packets.uniq!
269
+ packets.each do |target_name, packet_name|
269
270
  authorize(permission: 'tlm', target_name: target_name, packet_name: packet_name, scope: scope, token: token)
270
271
  end
271
- CvtModel.get_tlm_values(items, stale_time: stale_time, scope: scope)
272
+ CvtModel.get_tlm_values(cvt_items, stale_time: stale_time, cache_timeout: cache_timeout, scope: scope)
272
273
  end
273
274
 
274
275
  # Returns an array of all the telemetry packet hashes
@@ -429,7 +430,7 @@ module OpenC3
429
430
  return nil
430
431
  end
431
432
 
432
- def tlm_process_args(args, method_name, scope: $openc3_scope, token: $openc3_token)
433
+ def tlm_process_args(args, method_name, cache_timeout: 0.1, scope: $openc3_scope, token: $openc3_token)
433
434
  case args.length
434
435
  when 1
435
436
  target_name, packet_name, item_name = extract_fields_from_tlm_text(args[0])
@@ -444,19 +445,9 @@ module OpenC3
444
445
  target_name = target_name.upcase
445
446
  packet_name = packet_name.upcase
446
447
  item_name = item_name.upcase
448
+
447
449
  if packet_name == 'LATEST'
448
- latest = -1
449
- TargetModel.packets(target_name, scope: scope).each do |packet|
450
- item = packet['items'].find { |item| item['name'] == item_name }
451
- if item
452
- hash = CvtModel.get(target_name: target_name, packet_name: packet['packet_name'], scope: scope)
453
- if hash['PACKET_TIMESECONDS'] && hash['PACKET_TIMESECONDS'] > latest
454
- latest = hash['PACKET_TIMESECONDS']
455
- packet_name = packet['packet_name']
456
- end
457
- end
458
- end
459
- raise "Item '#{target_name} LATEST #{item_name}' does not exist" if latest == -1
450
+ packet_name = CvtModel.determine_latest_packet_for_item(target_name, item_name, cache_timeout: cache_timeout, scope: scope)
460
451
  else
461
452
  # Determine if this item exists, it will raise appropriate errors if not
462
453
  TargetModel.packet_item(target_name, packet_name, item_name, scope: scope)
@@ -115,7 +115,11 @@ module OpenC3
115
115
  def limits_change_callback(packet, item, old_limits_state, value, log_change)
116
116
  return if @cancel_thread
117
117
  packet_time = packet.packet_time
118
- message = "#{packet.target_name} #{packet.packet_name} #{item.name} = #{value} is #{item.limits.state}"
118
+ if value
119
+ message = "#{packet.target_name} #{packet.packet_name} #{item.name} = #{value} is #{item.limits.state}"
120
+ else
121
+ message = "#{packet.target_name} #{packet.packet_name} #{item.name} is disabled"
122
+ end
119
123
  message << " (#{packet.packet_time.sys.formatted})" if packet_time
120
124
 
121
125
  time_nsec = packet_time ? packet_time.to_nsec_from_epoch : Time.now.to_nsec_from_epoch
@@ -17,7 +17,7 @@
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
- # This file may also be used under the terms of a commercial license
20
+ # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
23
  require 'digest'
@@ -28,6 +28,12 @@ module OpenC3
28
28
  PRIMARY_KEY = 'OPENC3__TOKEN'
29
29
  SERVICE_KEY = 'OPENC3__SERVICE__TOKEN'
30
30
 
31
+ TOKEN_CACHE_TIMEOUT = 5
32
+ @@token_cache = nil
33
+ @@token_cache_time = nil
34
+ @@service_token_cache = nil
35
+ @@service_token_cache_time = nil
36
+
31
37
  def self.is_set?(key = PRIMARY_KEY)
32
38
  Store.exists(key) == 1
33
39
  end
@@ -36,15 +42,21 @@ module OpenC3
36
42
  return false if token.nil? or token.empty?
37
43
 
38
44
  token_hash = hash(token)
39
- return true if Store.get(PRIMARY_KEY) == token_hash
45
+ return true if @@token_cache and (Time.now - @@token_cache_time) < TOKEN_CACHE_TIMEOUT and @@token_cache == token_hash
46
+ return true if @@service_token_cache and (Time.now - @@service_token_cache_time) < TOKEN_CACHE_TIMEOUT and @@service_token_cache == token_hash and permission != 'admin'
47
+
48
+ @@token_cache = Store.get(PRIMARY_KEY)
49
+ @@token_cache_time = Time.now
50
+ return true if @@token_cache == token_hash
40
51
 
41
- service_hash = Store.get(SERVICE_KEY)
42
- if ENV['OPENC3_SERVICE_PASSWORD'] and hash(ENV['OPENC3_SERVICE_PASSWORD']) != service_hash
52
+ @@service_token_cache = Store.get(SERVICE_KEY)
53
+ @@service_token_cache_time = @@token_cache_time
54
+ if ENV['OPENC3_SERVICE_PASSWORD'] and hash(ENV['OPENC3_SERVICE_PASSWORD']) != @@service_token_cache
43
55
  set_hash = hash(ENV['OPENC3_SERVICE_PASSWORD'])
44
56
  OpenC3::Store.set(SERVICE_KEY, set_hash)
45
- service_hash = set_hash
57
+ @@service_token_cache = set_hash
46
58
  end
47
- return true if service_hash == token_hash and permission != 'admin'
59
+ return true if @@service_token_cache == token_hash and permission != 'admin'
48
60
  return false
49
61
  end
50
62
 
@@ -25,6 +25,9 @@ require 'openc3/models/target_model'
25
25
 
26
26
  module OpenC3
27
27
  class CvtModel
28
+ @@packet_cache = {}
29
+ @@override_cache = {}
30
+
28
31
  VALUE_TYPES = [:RAW, :CONVERTED, :FORMATTED, :WITH_UNITS]
29
32
  def self.build_json_from_packet(packet)
30
33
  packet.decom
@@ -32,24 +35,39 @@ module OpenC3
32
35
 
33
36
  # Delete the current value table for a target
34
37
  def self.del(target_name:, packet_name:, scope: $openc3_scope)
35
- Store.hdel("#{scope}__tlm__#{target_name}", packet_name)
38
+ key = "#{scope}__tlm__#{target_name}"
39
+ tgt_pkt_key = key + "__#{packet_name}"
40
+ @@packet_cache[tgt_pkt_key] = nil
41
+ Store.hdel(key, packet_name)
36
42
  end
37
43
 
38
44
  # Set the current value table for a target, packet
39
45
  def self.set(hash, target_name:, packet_name:, scope: $openc3_scope)
40
- Store.hset("#{scope}__tlm__#{target_name}", packet_name, JSON.generate(hash.as_json(:allow_nan => true)))
46
+ packet_json = JSON.generate(hash.as_json(:allow_nan => true))
47
+ key = "#{scope}__tlm__#{target_name}"
48
+ tgt_pkt_key = key + "__#{packet_name}"
49
+ @@packet_cache[tgt_pkt_key] = [Time.now, hash]
50
+ Store.hset(key, packet_name, packet_json)
41
51
  end
42
52
 
43
53
  # Get the hash for packet in the CVT
44
- def self.get(target_name:, packet_name:, scope: $openc3_scope)
45
- packet = Store.hget("#{scope}__tlm__#{target_name}", packet_name)
54
+ # Note: Does not apply overrides
55
+ def self.get(target_name:, packet_name:, cache_timeout: 0.1, scope: $openc3_scope)
56
+ key = "#{scope}__tlm__#{target_name}"
57
+ tgt_pkt_key = key + "__#{packet_name}"
58
+ cache_time, hash = @@packet_cache[tgt_pkt_key]
59
+ now = Time.now
60
+ return hash if hash and (now - cache_time) < cache_timeout
61
+ packet = Store.hget(key, packet_name)
46
62
  raise "Packet '#{target_name} #{packet_name}' does not exist" unless packet
47
- JSON.parse(packet, :allow_nan => true, :create_additions => true)
63
+ hash = JSON.parse(packet, :allow_nan => true, :create_additions => true)
64
+ @@packet_cache[tgt_pkt_key] = [now, hash]
65
+ hash
48
66
  end
49
67
 
50
68
  # Set an item in the current value table
51
69
  def self.set_item(target_name, packet_name, item_name, value, type:, scope: $openc3_scope)
52
- hash = get(target_name: target_name, packet_name: packet_name, scope: scope)
70
+ hash = get(target_name: target_name, packet_name: packet_name, cache_timeout: 0.0, scope: scope)
53
71
  case type
54
72
  when :WITH_UNITS
55
73
  hash["#{item_name}__U"] = value.to_s # WITH_UNITS should always be a string
@@ -67,33 +85,13 @@ module OpenC3
67
85
  else
68
86
  raise "Unknown type '#{type}' for #{target_name} #{packet_name} #{item_name}"
69
87
  end
70
- Store.hset("#{scope}__tlm__#{target_name}", packet_name, JSON.generate(hash.as_json(:allow_nan => true)))
88
+ set(hash, target_name: target_name, packet_name: packet_name, scope: scope)
71
89
  end
72
90
 
73
91
  # Get an item from the current value table
74
- def self.get_item(target_name, packet_name, item_name, type:, scope: $openc3_scope)
75
- override_key = item_name
76
- types = []
77
- case type
78
- when :WITH_UNITS
79
- types = ["#{item_name}__U", "#{item_name}__F", "#{item_name}__C", item_name]
80
- override_key = "#{item_name}__U"
81
- when :FORMATTED
82
- types = ["#{item_name}__F", "#{item_name}__C", item_name]
83
- override_key = "#{item_name}__F"
84
- when :CONVERTED
85
- types = ["#{item_name}__C", item_name]
86
- override_key = "#{item_name}__C"
87
- when :RAW
88
- types = [item_name]
89
- else
90
- raise "Unknown type '#{type}' for #{target_name} #{packet_name} #{item_name}"
91
- end
92
- overrides = Store.hget("#{scope}__override__#{target_name}", packet_name)
93
- if overrides
94
- result = JSON.parse(overrides, :allow_nan => true, :create_additions => true)[override_key]
95
- return result if result
96
- end
92
+ def self.get_item(target_name, packet_name, item_name, type:, cache_timeout: 0.1, scope: $openc3_scope)
93
+ result, types = self._handle_item_override(target_name, packet_name, item_name, type: type, cache_timeout: cache_timeout, scope: scope)
94
+ return result if result
97
95
  hash = get(target_name: target_name, packet_name: packet_name, scope: scope)
98
96
  hash.values_at(*types).each do |result|
99
97
  if result
@@ -111,18 +109,19 @@ module OpenC3
111
109
  # @param items [Array<String>] Items to return. Must be formatted as TGT__PKT__ITEM__TYPE
112
110
  # @param stale_time [Integer] Time in seconds from Time.now that value will be marked stale
113
111
  # @return [Array] Array of values
114
- def self.get_tlm_values(items, stale_time: 30, scope: $openc3_scope)
115
- now = Time.now.sys.to_f
112
+ def self.get_tlm_values(items, stale_time: 30, cache_timeout: 0.1, scope: $openc3_scope)
113
+ now = Time.now
116
114
  results = []
117
115
  lookups = []
118
116
  packet_lookup = {}
119
117
  overrides = {}
120
118
  # First generate a lookup hash of all the items represented so we can query the CVT
121
- items.each { |item| _parse_item(lookups, overrides, item, scope: scope) }
119
+ items.each { |item| _parse_item(now, lookups, overrides, item, cache_timeout: cache_timeout, scope: scope) }
122
120
 
121
+ now = now.to_f
123
122
  lookups.each do |target_packet_key, target_name, packet_name, value_keys|
124
123
  unless packet_lookup[target_packet_key]
125
- packet_lookup[target_packet_key] = get(target_name: target_name, packet_name: packet_name, scope: scope)
124
+ packet_lookup[target_packet_key] = get(target_name: target_name, packet_name: packet_name, cache_timeout: cache_timeout, scope: scope)
126
125
  end
127
126
  hash = packet_lookup[target_packet_key]
128
127
  item_result = []
@@ -153,6 +152,7 @@ module OpenC3
153
152
  end
154
153
 
155
154
  # Return all the overrides
155
+ # Note: Does not use cache to benefit from hgetall
156
156
  def self.overrides(scope: $openc3_scope)
157
157
  overrides = []
158
158
  TargetModel.names(scope: scope).each do |target_name|
@@ -207,6 +207,9 @@ module OpenC3
207
207
  else
208
208
  raise "Unknown type '#{type}' for #{target_name} #{packet_name} #{item_name}"
209
209
  end
210
+
211
+ tgt_pkt_key = "#{scope}__tlm__#{target_name}__#{packet_name}"
212
+ @@override_cache[tgt_pkt_key] = [Time.now, hash]
210
213
  Store.hset("#{scope}__override__#{target_name}", packet_name, JSON.generate(hash.as_json(:allow_nan => true)))
211
214
  end
212
215
 
@@ -232,6 +235,9 @@ module OpenC3
232
235
  else
233
236
  raise "Unknown type '#{type}' for #{target_name} #{packet_name} #{item_name}"
234
237
  end
238
+
239
+ tgt_pkt_key = "#{scope}__tlm__#{target_name}__#{packet_name}"
240
+ @@override_cache[tgt_pkt_key] = [Time.now, hash]
235
241
  if hash.empty?
236
242
  Store.hdel("#{scope}__override__#{target_name}", packet_name)
237
243
  else
@@ -239,16 +245,78 @@ module OpenC3
239
245
  end
240
246
  end
241
247
 
248
+ def self.determine_latest_packet_for_item(target_name, item_name, cache_timeout: 0.1, scope: $openc3_scope)
249
+ item_map = TargetModel.get_item_to_packet_map(target_name, scope: scope)
250
+ packet_names = item_map[item_name]
251
+ raise "Item '#{target_name} LATEST #{item_name}' does not exist for scope: #{scope}" unless packet_names
252
+
253
+ latest = -1
254
+ latest_packet_name = nil
255
+ packet_names.each do |packet_name|
256
+ hash = get(target_name: target_name, packet_name: packet_name, cache_timeout: cache_timeout, scope: scope)
257
+ if hash['PACKET_TIMESECONDS'] && hash['PACKET_TIMESECONDS'] > latest
258
+ latest = hash['PACKET_TIMESECONDS']
259
+ latest_packet_name = packet_name
260
+ end
261
+ end
262
+ raise "Item '#{target_name} LATEST #{item_name}' does not exist for scope: #{scope}" if latest == -1
263
+ return latest_packet_name
264
+ end
265
+
242
266
  # PRIVATE METHODS
243
267
 
268
+ def self._handle_item_override(target_name, packet_name, item_name, type:, cache_timeout:, scope: $openc3_scope)
269
+ override_key = item_name
270
+ types = []
271
+ case type
272
+ when :WITH_UNITS
273
+ types = ["#{item_name}__U", "#{item_name}__F", "#{item_name}__C", item_name]
274
+ override_key = "#{item_name}__U"
275
+ when :FORMATTED
276
+ types = ["#{item_name}__F", "#{item_name}__C", item_name]
277
+ override_key = "#{item_name}__F"
278
+ when :CONVERTED
279
+ types = ["#{item_name}__C", item_name]
280
+ override_key = "#{item_name}__C"
281
+ when :RAW
282
+ types = [item_name]
283
+ else
284
+ raise "Unknown type '#{type}' for #{target_name} #{packet_name} #{item_name}"
285
+ end
286
+
287
+ tgt_pkt_key = "#{scope}__tlm__#{target_name}__#{packet_name}"
288
+ overrides = _get_overrides(Time.now, tgt_pkt_key, {}, target_name, packet_name, cache_timeout: cache_timeout, scope: scope)
289
+ result = overrides[override_key]
290
+ return result, types if result
291
+ return nil, types
292
+ end
293
+
294
+ def self._get_overrides(now, tgt_pkt_key, overrides, target_name, packet_name, cache_timeout:, scope:)
295
+ cache_time, hash = @@override_cache[tgt_pkt_key]
296
+ if hash and (now - cache_time) < cache_timeout
297
+ overrides[tgt_pkt_key] = hash
298
+ return hash
299
+ end
300
+ override_data = Store.hget("#{scope}__override__#{target_name}", packet_name)
301
+ if override_data
302
+ hash = JSON.parse(override_data, :allow_nan => true, :create_additions => true)
303
+ overrides[tgt_pkt_key] = hash
304
+ else
305
+ hash = {}
306
+ overrides[tgt_pkt_key] = {}
307
+ end
308
+ @@override_cache[tgt_pkt_key] = [now, hash] # always update
309
+ return hash
310
+ end
311
+
244
312
  # parse item and update lookups with packet_name and target_name and keys
245
313
  # return an ordered array of hash with keys
246
- def self._parse_item(lookups, overrides, item, scope:)
247
- target_name, packet_name, item_name, value_type = item.split('__')
314
+ def self._parse_item(now, lookups, overrides, item, cache_timeout:, scope:)
315
+ target_name, packet_name, item_name, value_type = item
248
316
 
249
317
  # We build lookup keys by including all the less formatted types to gracefully degrade lookups
250
318
  # This allows the user to specify WITH_UNITS and if there is no conversions it will simply return the RAW value
251
- case value_type
319
+ case value_type.to_s
252
320
  when 'RAW'
253
321
  keys = [item_name]
254
322
  when 'CONVERTED'
@@ -260,20 +328,15 @@ module OpenC3
260
328
  else
261
329
  raise "Unknown value type '#{value_type}'"
262
330
  end
263
- tgt_pkt_key = "#{target_name}__#{packet_name}"
331
+
264
332
  # Check the overrides cache for this target / packet
265
- unless overrides[tgt_pkt_key]
266
- override_data = Store.hget("#{scope}__override__#{target_name}", packet_name)
267
- if override_data
268
- overrides[tgt_pkt_key] = JSON.parse(override_data, :allow_nan => true, :create_additions => true)
269
- else
270
- overrides[tgt_pkt_key] = {}
271
- end
272
- end
273
- if overrides[tgt_pkt_key][keys[0]]
274
- # Set the result as a Hash to distingish it from the key array and from an overridden Array value
275
- keys = {'value' => overrides[tgt_pkt_key][keys[0]]}
276
- end
333
+ tgt_pkt_key = "#{scope}__tlm__#{target_name}__#{packet_name}"
334
+ _get_overrides(now, tgt_pkt_key, overrides, target_name, packet_name, cache_timeout: cache_timeout, scope: scope) unless overrides[tgt_pkt_key]
335
+
336
+ # Set the result as a Hash to distinguish it from the key array and from an overridden Array value
337
+ value = overrides[tgt_pkt_key][keys[0]]
338
+ keys = {'value' => value} if value
339
+
277
340
  lookups << [tgt_pkt_key, target_name, packet_name, keys]
278
341
  end
279
342
  end
@@ -389,6 +389,8 @@ module OpenC3
389
389
  status_model = RouterStatusModel.get_model(name: @name, scope: @scope)
390
390
  end
391
391
  status_model.destroy if status_model
392
+ rescue Exception => error
393
+ Logger.error("Error undeploying interface/router model #{@name} in scope #{@scope} due to #{error}")
392
394
  end
393
395
 
394
396
  def unmap_target(target_name, cmd_only: false, tlm_only: false)
@@ -17,7 +17,7 @@
17
17
  # All changes Copyright 2022, OpenC3, Inc.
18
18
  # All Rights Reserved
19
19
  #
20
- # This file may also be used under the terms of a commercial license
20
+ # This file may also be used under the terms of a commercial license
21
21
  # if purchased from OpenC3, Inc.
22
22
 
23
23
  require 'openc3/models/model'
@@ -297,6 +297,8 @@ module OpenC3
297
297
  microservices.each do |name, model_instance|
298
298
  model_instance.cleanup
299
299
  end
300
+ rescue Exception => error
301
+ Logger.error("Error undeploying plugin model #{@name} in scope #{@scope} due to #{error}")
300
302
  end
301
303
 
302
304
  # Reinstall