openc3 5.9.0 → 5.10.0

Sign up to get free protection for your applications and to get access to all the features.
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