# 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. # # This program may also be used under the terms of a commercial or # enterprise edition license of COSMOS if purchased from the # copyright holder require 'cosmos/config/config_parser' require 'cosmos/packets/packet_config' require 'cosmos/packets/commands' # TODO: System requires telemetry and Telemetry require system ... circular reference require 'cosmos/packets/telemetry' require 'cosmos/packets/limits' require 'cosmos/system/target' require 'cosmos/utilities/s3' require 'cosmos/utilities/zip' require 'cosmos/models/scope_model' require 'thread' require 'fileutils' module Cosmos class System # @return [Hash] Hash of all the known targets instance_attr_reader :targets # @return [PacketConfig] Access to the packet configuration instance_attr_reader :packet_config # @return [Commands] Access to the command definition instance_attr_reader :commands # @return [Telemetry] Access to the telemetry definition instance_attr_reader :telemetry # @return [Limits] Access to the limits definition instance_attr_reader :limits # Variable that holds the singleton instance @@instance = nil # Mutex used to ensure that only one instance of System is created @@instance_mutex = Mutex.new # @return [Symbol] The current limits_set of the system returned from Redis def self.limits_set ScopeModel.limits_set(scope: $cosmos_scope) end def self.setup_targets(target_names, base_dir, scope:) FileUtils.mkdir_p("#{base_dir}/targets") rubys3_client = Aws::S3::Client.new target_names.each do |target_name| # Retrieve bucket/targets/target_name/target_id.zip response_target = "#{base_dir}/targets/#{target_name}_current.zip" FileUtils.mkdir_p(File.dirname(response_target)) s3_key = "#{scope}/target_archives/#{target_name}/#{target_name}_current.zip" Logger.info("Retrieving #{s3_key} from targets bucket") rubys3_client.get_object(bucket: "config", key: s3_key, response_target: response_target) Zip::File.open(response_target) do |zip_file| zip_file.each do |entry| path = File.join("#{base_dir}/targets", entry.name) FileUtils.mkdir_p(File.dirname(path)) zip_file.extract(entry, path) unless File.exist?(path) end end end # Build System from targets System.instance(target_names, "#{base_dir}/targets") end # Get the singleton instance of System # # @param target_names [Array of target_names] # @param target_config_dir Directory where target config folders are # @return [System] The System singleton def self.instance(target_names = nil, target_config_dir = nil) return @@instance if @@instance raise "System.instance parameters are required on first call" unless target_names and target_config_dir @@instance_mutex.synchronize do @@instance ||= self.new(target_names, target_config_dir) return @@instance end end # Create a new System object. # # @param target_names [Array of target names] # @param target_config_dir Directory where target config folders are def initialize(target_names, target_config_dir) @targets = {} @packet_config = PacketConfig.new @commands = Commands.new(@packet_config) @telemetry = Telemetry.new(@packet_config) @limits = Limits.new(@packet_config) target_names.each { |target_name| add_target(target_name, target_config_dir) } end def add_target(target_name, target_config_dir) parser = ConfigParser.new folder_name = File.join(target_config_dir, target_name) raise parser.error("Target folder must exist '#{folder_name}'.") unless Dir.exist?(folder_name) target = Target.new(target_name, target_config_dir) @targets[target.name] = target target.cmd_tlm_files.each do |cmd_tlm_file| @packet_config.process_file(cmd_tlm_file, target.name) rescue Exception => err Logger.error "Problem processing #{cmd_tlm_file}: #{err}." raise err end end end end