lib/openc3/models/cvt_model.rb in openc3-5.2.0 vs lib/openc3/models/cvt_model.rb in openc3-5.3.0

- old
+ new

@@ -23,34 +23,33 @@ require 'openc3/utilities/store' module OpenC3 class CvtModel VALUE_TYPES = [:RAW, :CONVERTED, :FORMATTED, :WITH_UNITS] - # Stores telemetry item overrides which are returned on every request to get_item - @overrides = {} - def self.build_json_from_packet(packet) packet.decom end # Delete the current value table for a target - def self.del(target_name:, packet_name:, scope:) + def self.del(target_name:, packet_name:, scope: $openc3_scope) Store.hdel("#{scope}__tlm__#{target_name}", packet_name) end # Set the current value table for a target, packet - def self.set(hash, target_name:, packet_name:, scope:) + def self.set(hash, target_name:, packet_name:, scope: $openc3_scope) Store.hset("#{scope}__tlm__#{target_name}", packet_name, JSON.generate(hash.as_json(:allow_nan => true))) end # Set an item in the current value table - def self.set_item(target_name, packet_name, item_name, value, type:, scope:) + def self.set_item(target_name, packet_name, item_name, value, type:, scope: $openc3_scope) case type when :WITH_UNITS field = "#{item_name}__U" + value = value.to_s # WITH_UNITS should always be a string when :FORMATTED field = "#{item_name}__F" + value = value.to_s # FORMATTED should always be a string when :CONVERTED field = "#{item_name}__C" when :RAW field = item_name else @@ -60,31 +59,41 @@ hash[field] = value Store.hset("#{scope}__tlm__#{target_name}", packet_name, JSON.generate(hash.as_json(:allow_nan => true))) end # Get an item from the current value table - def self.get_item(target_name, packet_name, item_name, type:, scope:) - if @overrides["#{target_name}__#{packet_name}__#{item_name}__#{type}"] - return @overrides["#{target_name}__#{packet_name}__#{item_name}__#{type}"] - end - + def self.get_item(target_name, packet_name, item_name, type:, scope: $openc3_scope) + override_key = item_name types = [] case type when :WITH_UNITS types = ["#{item_name}__U", "#{item_name}__F", "#{item_name}__C", item_name] + override_key = "#{item_name}__U" when :FORMATTED types = ["#{item_name}__F", "#{item_name}__C", item_name] + override_key = "#{item_name}__F" when :CONVERTED types = ["#{item_name}__C", item_name] + override_key = "#{item_name}__C" when :RAW types = [item_name] else raise "Unknown type '#{type}' for #{target_name} #{packet_name} #{item_name}" end + overrides = Store.hget("#{scope}__override__#{target_name}", packet_name) + if overrides + result = JSON.parse(overrides, :allow_nan => true, :create_additions => true)[override_key] + return result if result + end hash = JSON.parse(Store.hget("#{scope}__tlm__#{target_name}", packet_name), :allow_nan => true, :create_additions => true) hash.values_at(*types).each do |result| - return result if result + if result + if type == :FORMATTED or type == :WITH_UNITS + return result.to_s + end + return result + end end return nil end # Return all item values and limit state from the CVT @@ -95,75 +104,108 @@ def self.get_tlm_values(items, stale_time: 30, scope: $openc3_scope) now = Time.now.sys.to_f results = [] lookups = [] packet_lookup = {} + overrides = {} # First generate a lookup hash of all the items represented so we can query the CVT - items.each { |item| _parse_item(lookups, item) } + items.each { |item| _parse_item(lookups, overrides, item, scope: scope) } - lookups.each do |target_packet_key, target_name, packet_name, packet_values| + lookups.each do |target_packet_key, target_name, packet_name, value_keys| unless packet_lookup[target_packet_key] packet = Store.hget("#{scope}__tlm__#{target_name}", packet_name) raise "Packet '#{target_name} #{packet_name}' does not exist" unless packet packet_lookup[target_packet_key] = JSON.parse(packet, :allow_nan => true, :create_additions => true) end hash = packet_lookup[target_packet_key] item_result = [] - packet_values.each do |value| - item_result[0] = hash[value] - break if item_result[0] # We want the first value - end - # If we were able to find a value, try to get the limits state - if item_result[0] - if now - hash['RECEIVED_TIMESECONDS'] > stale_time - item_result[1] = :STALE + if value_keys.is_a?(Hash) # Set in _parse_item to indicate override + item_result[0] = value_keys['value'] + else + value_keys.each do |key| + item_result[0] = hash[key] + break if item_result[0] # We want the first value + end + # If we were able to find a value, try to get the limits state + if item_result[0] + if now - hash['RECEIVED_TIMESECONDS'] > stale_time + item_result[1] = :STALE + else + # The last key is simply the name (RAW) so we can append __L + # If there is no limits then it returns nil which is acceptable + item_result[1] = hash["#{value_keys[-1]}__L"] + item_result[1] = item_result[1].intern if item_result[1] # Convert to symbol + end else - # The last key is simply the name (RAW) so we can append __L - # If there is no limits then it returns nil which is acceptable - item_result[1] = hash["#{packet_values[-1]}__L"] - item_result[1] = item_result[1].intern if item_result[1] # Convert to symbol + raise "Item '#{target_name} #{packet_name} #{value_keys[-1]}' does not exist" unless hash.key?(value_keys[-1]) end - else - raise "Item '#{target_name} #{packet_name} #{packet_values[-1]}' does not exist" unless hash.key?(packet_values[-1]) - item_result[1] = nil end results << item_result end results end # Override a current value table item such that it always returns the same value # for the given type - def self.override(target_name, packet_name, item_name, value, type:, scope: $openc3_scope) - if VALUE_TYPES.include?(type) - @overrides["#{target_name}__#{packet_name}__#{item_name}__#{type}"] = value + def self.override(target_name, packet_name, item_name, value, type: :ALL, scope: $openc3_scope) + hash = Store.hget("#{scope}__override__#{target_name}", packet_name) + hash = JSON.parse(hash, :allow_nan => true, :create_additions => true) if hash + hash ||= {} # In case the above didn't create anything + case type + when :ALL + hash[item_name] = value + hash["#{item_name}__C"] = value + hash["#{item_name}__F"] = value.to_s + hash["#{item_name}__U"] = value.to_s + when :RAW + hash[item_name] = value + when :CONVERTED + hash["#{item_name}__C"] = value + when :FORMATTED + hash["#{item_name}__F"] = value.to_s # Always a String + when :WITH_UNITS + hash["#{item_name}__U"] = value.to_s # Always a String else raise "Unknown type '#{type}' for #{target_name} #{packet_name} #{item_name}" end + Store.hset("#{scope}__override__#{target_name}", packet_name, JSON.generate(hash.as_json(:allow_nan => true))) end # Normalize a current value table item such that it returns the actual value def self.normalize(target_name, packet_name, item_name, type: :ALL, scope: $openc3_scope) - if type == :ALL - VALUE_TYPES.each do |type| - @overrides.delete("#{target_name}__#{packet_name}__#{item_name}__#{type}") - end + hash = Store.hget("#{scope}__override__#{target_name}", packet_name) + hash = JSON.parse(hash, :allow_nan => true, :create_additions => true) if hash + hash ||= {} # In case the above didn't create anything + case type + when :ALL + hash.delete(item_name) + hash.delete("#{item_name}__C") + hash.delete("#{item_name}__F") + hash.delete("#{item_name}__U") + when :RAW + hash.delete(item_name) + when :CONVERTED + hash.delete("#{item_name}__C") + when :FORMATTED + hash.delete("#{item_name}__F") + when :WITH_UNITS + hash.delete("#{item_name}__U") else - if VALUE_TYPES.include?(type) - @overrides.delete("#{target_name}__#{packet_name}__#{item_name}__#{type}") - else - raise "Unknown type '#{type}' for #{target_name} #{packet_name} #{item_name}" - end + raise "Unknown type '#{type}' for #{target_name} #{packet_name} #{item_name}" end + if hash.empty? + Store.hdel("#{scope}__override__#{target_name}", packet_name) + else + Store.hset("#{scope}__override__#{target_name}", packet_name, JSON.generate(hash.as_json(:allow_nan => true))) + end end # PRIVATE METHODS - def self._parse_item(lookups, item) - # parse item and update lookups with packet_name and target_name and keys - # - # return an ordered array of hash with keys + # parse item and update lookups with packet_name and target_name and keys + # return an ordered array of hash with keys + def self._parse_item(lookups, overrides, item, scope:) target_name, packet_name, item_name, value_type = item.split('__') raise ArgumentError, "items must be formatted as TGT__PKT__ITEM__TYPE" if target_name.nil? || packet_name.nil? || item_name.nil? || value_type.nil? # We build lookup keys by including all the less formatted types to gracefully degrade lookups # This allows the user to specify WITH_UNITS and if there is no conversions it will simply return the RAW value @@ -175,11 +217,25 @@ when 'FORMATTED' keys = ["#{item_name}__F", "#{item_name}__C", item_name] when 'WITH_UNITS' keys = ["#{item_name}__U", "#{item_name}__F", "#{item_name}__C", item_name] else - raise "Unknown value type #{value_type}" + raise "Unknown value type '#{value_type}'" end - lookups << ["#{target_name}__#{packet_name}", target_name, packet_name, keys] + tgt_pkt_key = "#{target_name}__#{packet_name}" + # Check the overrides cache for this target / packet + unless overrides[tgt_pkt_key] + override_data = Store.hget("#{scope}__override__#{target_name}", packet_name) + if override_data + overrides[tgt_pkt_key] = JSON.parse(override_data, :allow_nan => true, :create_additions => true) + else + overrides[tgt_pkt_key] = {} + end + end + if overrides[tgt_pkt_key][keys[0]] + # Set the result as a Hash to distingish it from the key array and from an overridden Array value + keys = {'value' => overrides[tgt_pkt_key][keys[0]]} + end + lookups << [tgt_pkt_key, target_name, packet_name, keys] end end end