class OpenC3::InterfaceModel
def self._get_key
def self._get_key type = _get_type case type when 'INTERFACE' INTERFACES_PRIMARY_KEY when 'ROUTER' ROUTERS_PRIMARY_KEY else raise "Unknown type #{type} from class #{self.name}" end end
def self._get_type
def self._get_type self.name.to_s.split("Model")[0].upcase.split("::")[-1] end
def self.all(scope:)
def self.all(scope:) super("#{scope}__#{_get_key}") end
def self.get(name:, scope:)
NOTE: The following three class methods are used by the ModelController
def self.get(name:, scope:) super("#{scope}__#{_get_key}", name: name) end
def self.handle_config(parser, keyword, parameters, plugin: nil, needs_dependencies: false, scope:)
Interface/Router specific keywords are handled by the instance method "handle_config"
Called by the PluginModel to allow this class to validate it's top-level keyword: "INTERFACE"
def self.handle_config(parser, keyword, parameters, plugin: nil, needs_dependencies: false, scope:) case keyword when 'INTERFACE' parser.verify_num_parameters(2, nil, "INTERFACE <Name> <Filename> <Specific Parameters>") return self.new(name: parameters[0].upcase, config_params: parameters[1..-1], plugin: plugin, needs_dependencies: needs_dependencies, scope: scope) else raise ConfigParser::Error.new(parser, "Unknown keyword and parameters for Interface: #{keyword} #{parameters.join(" ")}") end end
def self.names(scope:)
def self.names(scope:) super("#{scope}__#{_get_key}") end
def as_config
def as_config result = "#{self.class._get_type} #{@name} #{@config_params.join(' ')}\n" @target_names.each do |target_name| result << " MAP_TARGET #{target_name}\n" end result << " DONT_CONNECT\n" unless @connect_on_startup result << " DONT_RECONNECT\n" unless @auto_reconnect result << " RECONNECT_DELAY #{@reconnect_delay}\n" result << " DISABLE_DISCONNECT\n" if @disable_disconnect @options.each do |option| result << " OPTION #{option.join(' ')}\n" end @protocols.each do |protocol| result << " PROTOCOL #{protocol.join(' ')}\n" end result << " DONT_LOG" unless @log result << " LOG_RAW" if @log_raw result end
def as_json(*a)
def as_json(*a) { 'name' => @name, 'config_params' => @config_params, 'target_names' => @target_names, 'connect_on_startup' => @connect_on_startup, 'auto_reconnect' => @auto_reconnect, 'reconnect_delay' => @reconnect_delay, 'disable_disconnect' => @disable_disconnect, 'options' => @options, 'protocols' => @protocols, 'log' => @log, 'log_raw' => @log_raw, 'plugin' => @plugin, 'needs_dependencies' => @needs_dependencies, 'updated_at' => @updated_at } end
def build
by the model configuration. Must be called after get_model which
Called by InterfaceMicroservice to instantiate the Interface defined
def build klass = OpenC3.require_class(@config_params[0]) if @config_params.length > 1 interface_or_router = klass.new(*@config_params[1..-1]) else interface_or_router = klass.new end interface_or_router.target_names = @target_names.dup interface_or_router.connect_on_startup = @connect_on_startup interface_or_router.auto_reconnect = @auto_reconnect interface_or_router.reconnect_delay = @reconnect_delay interface_or_router.disable_disconnect = @disable_disconnect @options.each do |option| interface_or_router.set_option(option[0], option[1..-1]) end @protocols.each do |protocol| klass = OpenC3.require_class(protocol[1]) interface_or_router.add_protocol(klass, protocol[2..-1], protocol[0].upcase.intern) end interface_or_router end
def deploy(gem_path, variables, validate_only: false)
def deploy(gem_path, variables, validate_only: false) type = self.class._get_type microservice_name = "#{@scope}__#{type}__#{@name}" microservice = MicroserviceModel.new( name: microservice_name, work_dir: '/openc3/lib/openc3/microservices', cmd: ["ruby", "#{type.downcase}_microservice.rb", microservice_name], target_names: @target_names, plugin: @plugin, needs_dependencies: @needs_dependencies, scope: @scope ) unless validate_only microservice.create microservice.deploy(gem_path, variables) ConfigTopic.write({ kind: 'created', type: type.downcase, name: @name, plugin: @plugin }, scope: @scope) Logger.info "Configured #{type.downcase} microservice #{microservice_name}" end microservice end
def handle_config(parser, keyword, parameters)
def handle_config(parser, keyword, parameters) case keyword when 'MAP_TARGET' parser.verify_num_parameters(1, 1, "#{keyword} <Target Name>") @target_names << parameters[0].upcase when 'DONT_CONNECT' parser.verify_num_parameters(0, 0, "#{keyword}") @connect_on_startup = false when 'DONT_RECONNECT' parser.verify_num_parameters(0, 0, "#{keyword}") @auto_reconnect = false when 'RECONNECT_DELAY' parser.verify_num_parameters(1, 1, "#{keyword} <Delay in Seconds>") @reconnect_delay = Float(parameters[0]) when 'DISABLE_DISCONNECT' parser.verify_num_parameters(0, 0, "#{keyword}") @disable_disconnect = true when 'OPTION' parser.verify_num_parameters(2, nil, "#{keyword} <Option Name> <Option Value 1> <Option Value 2 (optional)> <etc>") @options << parameters.dup when 'PROTOCOL' usage = "#{keyword} <READ WRITE READ_WRITE> <protocol filename or classname> <Protocol specific parameters>" parser.verify_num_parameters(2, nil, usage) unless %w(READ WRITE READ_WRITE).include? parameters[0].upcase raise parser.error("Invalid protocol type: #{parameters[0]}", usage) end @protocols << parameters.dup when 'DONT_LOG' parser.verify_num_parameters(0, 0, "#{keyword}") @log = false when 'LOG_RAW' parser.verify_num_parameters(0, 0, "#{keyword}") @log_raw = true else raise ConfigParser::Error.new(parser, "Unknown keyword and parameters for Interface/Router: #{keyword} #{parameters.join(" ")}") end return nil end
def initialize(
def initialize( name:, config_params: [], target_names: [], connect_on_startup: true, auto_reconnect: true, reconnect_delay: 5.0, disable_disconnect: false, options: [], protocols: [], log: true, log_raw: false, updated_at: nil, plugin: nil, needs_dependencies: false, scope: ) if self.class._get_type == 'INTERFACE' super("#{scope}__#{INTERFACES_PRIMARY_KEY}", name: name, updated_at: updated_at, plugin: plugin, scope: scope) else super("#{scope}__#{ROUTERS_PRIMARY_KEY}", name: name, updated_at: updated_at, plugin: plugin, scope: scope) end @config_params = config_params @target_names = target_names @connect_on_startup = connect_on_startup @auto_reconnect = auto_reconnect @reconnect_delay = reconnect_delay @disable_disconnect = disable_disconnect @options = options @protocols = protocols @log = log @log_raw = log_raw @needs_dependencies = needs_dependencies end
def map_target(target_name)
def map_target(target_name) target_name = target_name.to_s.upcase # Remove from old interface all_interfaces = InterfaceModel.all(scope: scope) old_interface = nil all_interfaces.each do |old_interface_name, old_interface_details| if old_interface_details['target_names'].include?(target_name) old_interface = InterfaceModel.from_json(old_interface_details, scope: scope) break end end old_interface.unmap_target(target_name) if old_interface # Add to this interface @target_names << target_name unless @target_names.include?(target_name) update() # Respawn the microservice type = self.class._get_type microservice_name = "#{@scope}__#{type}__#{@name}" microservice = MicroserviceModel.get_model(name: microservice_name, scope: scope) microservice.target_names << target_name unless microservice.target_names.include?(target_name) microservice.update end
def undeploy
should should trigger the operator to kill the microservice that in turn
Looks up the deployed MicroserviceModel and destroy the microservice model
def undeploy type = self.class._get_type name = "#{@scope}__#{type}__#{@name}" model = MicroserviceModel.get_model(name: name, scope: @scope) if model model.destroy ConfigTopic.write({ kind: 'deleted', type: type.downcase, name: @name, plugin: @plugin }, scope: @scope) end if type == 'INTERFACE' status_model = InterfaceStatusModel.get_model(name: @name, scope: @scope) else status_model = RouterStatusModel.get_model(name: @name, scope: @scope) end status_model.destroy if status_model end
def unmap_target(target_name)
def unmap_target(target_name) target_name = target_name.to_s.upcase # Remove from this interface @target_names.delete(target_name) update() # Respawn the microservice type = self.class._get_type microservice_name = "#{@scope}__#{type}__#{@name}" microservice = MicroserviceModel.get_model(name: microservice_name, scope: scope) microservice.target_names.delete(target_name) microservice.update end