openc3/lib/openc3/packets/packet_item_limits.rb
# encoding: ascii-8bit
# Copyright 2022 Ball Aerospace & Technologies Corp.
# All Rights Reserved.
#
# This program is free software; you can modify and/or redistribute it
# under the terms of the GNU Affero General Public License
# as published by the Free Software Foundation; version 3 with
# attribution addendums as found in the LICENSE.txt
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
# Modified by OpenC3, Inc.
# All changes Copyright 2022, OpenC3, Inc.
# All Rights Reserved
#
# This file may also be used under the terms of a commercial license
# if purchased from OpenC3, Inc.
require 'openc3/packets/limits_response'
require 'openc3/utilities/python_proxy'
module OpenC3
# Maintains knowledge of limits for a PacketItem
class PacketItemLimits
# Array of all limit states
LIMITS_STATES = [:RED, :RED_HIGH, :RED_LOW, :YELLOW, :YELLOW_HIGH, :YELLOW_LOW, :GREEN, :GREEN_HIGH, :GREEN_LOW, :BLUE, :STALE, nil]
# Array of all limit states which should be considered in error
OUT_OF_LIMITS_STATES = [:RED, :RED_HIGH, :RED_LOW, :YELLOW, :YELLOW_HIGH, :YELLOW_LOW]
# Hash of arrays - Hash key is uppercase symbol designating limits set.
# :DEFAULT limits set is required if item has limits. nil indicates the
# item does not have limits. For defined limits, each array in the hash
# contains [:RED_LOW, :YELLOW_LOW, :YELLOW_HIGH, :RED_HIGH,
# :GREEN_LOW (optional), GREEN_HIGH (optional)].
# @return [Hash{Symbol=>Array}] Hash of all the limits defined for this
# item. Must include a hash key of :DEFAULT which returns the default
# limits.
attr_reader :values
# Every item effectively always has "limits" enabled because it can go from
# :STALE to nil as needed. This flag indicates whether an item with defined
# limit values can transition from nil to the various other states.
#
# @return [Boolean] Whether limits are enabled on this item
attr_accessor :enabled
# Current limits state of the item. One of nil, :STALE, :BLUE, :GREEN,
# :GREEN_HIGH, :GREEN_LOW, :YELLOW, :YELLOW_HIGH, :YELLOW_LOW,
# :RED, :RED_HIGH, :RED_LOW. Items initialize to :STALE and change states
# as the item value changes. If the limits are disabled the state changes
# to nil. If a packet becomes stale the items state changes to :STALE.
# @return [Symbol, nil] Current limits state of the item
attr_reader :state
# @return [LimitsResponse] Response method to be called on limits changes
attr_reader :response
# @return [Integer] Number of out of limits samples at which the limits
# state will change
attr_reader :persistence_setting
# Current persistent count. The count will be reset to zero if the limits
# state hasn't changed (i.e. remained :GREEN).
# @return [Integer] Current running persistence count
attr_reader :persistence_count
# Create a PacketItemLimits
def initialize
@values = nil
@enabled = false
@state = nil
@response = nil
@persistence_setting = 1
@persistence_count = 0
end
def values=(values)
if values
raise ArgumentError, "values must be a Hash but is a #{values.class}" unless Hash === values
raise ArgumentError, "values must be a Hash with a :DEFAULT key" unless values.keys.include?(:DEFAULT)
@values = values.clone
else
@values = nil
end
end
def state=(state)
raise ArgumentError, "state must be one of #{LIMITS_STATES} but is #{state}" unless LIMITS_STATES.include?(state)
@state = state
end
def response=(response)
if response
raise ArgumentError, "response must be a OpenC3::LimitsResponse but is a #{response.class}" unless OpenC3::LimitsResponse === response or OpenC3::PythonProxy === response
@response = response.clone
else
@response = nil
end
end
def persistence_setting=(persistence_setting)
if 0.class == Integer
# Ruby version >= 2.4.0
raise ArgumentError, "persistence_setting must be an Integer but is a #{persistence_setting.class}" unless Integer === persistence_setting
else
# Ruby version < 2.4.0
raise ArgumentError, "persistence_setting must be a Fixnum but is a #{persistence_setting.class}" unless Fixnum === persistence_setting
end
@persistence_setting = persistence_setting
end
def persistence_count=(persistence_count)
if 0.class == Integer
# Ruby version >= 2.4.0
raise ArgumentError, "persistence_count must be an Integer but is a #{persistence_count.class}" unless Integer === persistence_count
else
# Ruby version < 2.4.0
raise ArgumentError, "persistence_count must be a Fixnum but is a #{persistence_count.class}" unless Fixnum === persistence_count
end
@persistence_count = persistence_count
end
# Make a light weight clone of this limits
def clone
limits = super()
limits.values = self.values.clone if self.values
limits.response = self.response.clone if self.response
limits
end
alias dup clone
def as_json(*a)
hash = {}
hash['values'] = self.values
hash['enabled'] = self.enabled
hash['state'] = self.state
if self.response
hash['response'] = self.response.to_s
else
hash['response'] = nil
end
hash['persistence_setting'] = self.persistence_setting
hash['persistence_count'] = self.persistence_count
hash
end
def self.from_json(hash)
limits = PacketItemLimits.new
limits.values = hash['values'].transform_keys(&:to_sym) if hash['values']
limits.enabled = hash['enabled']
limits.state = hash['state'] ? hash['state'].to_sym : nil
# Can't recreate a LimitsResponse class
# limits.response = hash['response']
limits.persistence_setting = hash['persistence_setting'] if hash['persistence_setting']
limits.persistence_count = hash['persistence_count'] if hash['persistence_count']
limits
end
end
end