-
Notifications
You must be signed in to change notification settings - Fork 17
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Add new initialized object in ExecutionEngine, the ServiceManager, which behaves as a singleton - When specified in the config (example & tutorial to come in a later commit), a Service is made an attribute of the ServiceManager - The ServiceManager can be imported in any plugin. As a singleton, when called to initialize, the original ServiceManager instance (along with instantiated Services) will be returned - Services are defined in the config and imported automatically when ServiceManager is first generated
- Loading branch information
1 parent
686df36
commit 4eb5153
Showing
5 changed files
with
127 additions
and
0 deletions.
There are no files selected for viewing
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
from onair.src.util.singleton import Singleton | ||
from onair.src.util.service_import import import_services | ||
|
||
|
||
class ServiceManager(Singleton): | ||
def __init__(self, service_dict=None): | ||
# Only initialize if not already initialized | ||
if not hasattr(self, "_initialized"): | ||
# Ensure service info is provided on the first instantiation | ||
if service_dict is None: | ||
raise ValueError("'service_dict' parameter required on first instantiation") | ||
services = import_services(service_dict) | ||
for service_name, service in services.items(): | ||
# Set attribute name = service name | ||
setattr(self, service_name, service) | ||
self._initialized = True # mark as initialized to avoid re-initializing | ||
|
||
def get_services(self): | ||
# Return list of services and their functions | ||
services = {} | ||
for service_name, service in vars(self).items(): | ||
service_funcs = set() | ||
if service_name.startswith('_'): | ||
# avoid "private" attributes, e.g. _initialized | ||
continue | ||
for f in dir(service): | ||
# only add the f if it's a function and not "private" | ||
if callable(getattr(service, f)) and not f.startswith('_'): | ||
service_funcs.add(f) | ||
services[service_name] = service_funcs | ||
|
||
return services |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
# GSC-19165-1, "The On-Board Artificial Intelligence Research (OnAIR) Platform" | ||
# | ||
# Copyright © 2023 United States Government as represented by the Administrator of | ||
# the National Aeronautics and Space Administration. No copyright is claimed in the | ||
# United States under Title 17, U.S. Code. All Other Rights Reserved. | ||
# | ||
# Licensed under the NASA Open Source Agreement version 1.3 | ||
# See "NOSA GSC-19165-1 OnAIR.pdf" | ||
|
||
""" | ||
plugin_import.py | ||
Function to import user-specified plugins (from config files) into interfaces | ||
""" | ||
|
||
import importlib.util | ||
import sys | ||
import os | ||
|
||
|
||
def import_services(service_dict): | ||
services_dict = {} | ||
init_filename = "__init__.py" | ||
""" | ||
services_dict format = {service1: service1_args, service2: service2_args} | ||
e.g. | ||
fleetinterface: {'path': 'service/redis_fleet_interface', | ||
'agent_callsign': 'agent1', | ||
'connection_timeout': '3'} | ||
""" | ||
for service, kwargs in service_dict.items(): | ||
true_path = kwargs.pop('path') # path no longer needed afterwards | ||
# Last directory name is the module name | ||
mod_name = os.path.basename(true_path) # redis_fleet_interface | ||
# import module if not already available | ||
if mod_name not in sys.modules: | ||
# add init file to get proper path for spec | ||
full_path = os.path.join(true_path, init_filename) | ||
# define spec for module loading | ||
spec = importlib.util.spec_from_file_location(mod_name, full_path) | ||
# create uninitialize module from spec | ||
module = importlib.util.module_from_spec(spec) | ||
# initialize the created module | ||
spec.loader.exec_module(module) | ||
# add plugin module to system for importation | ||
sys.modules[mod_name] = module | ||
# import the created module's plugin file for use | ||
service_name = f"{mod_name}_service" | ||
service = __import__(f"{mod_name}.{service_name}", fromlist=[service_name]) | ||
# add an instance of the module's was an OnAIR plugin | ||
services_dict[mod_name] = service.Service(**kwargs) | ||
return services_dict |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
# class Singleton(object): | ||
# _instance = None | ||
# def __new__(class_, *args, **kwargs): | ||
# if not isinstance(class_._instance, class_): | ||
# class_._instance = object.__new__(class_, *args, **kwargs) | ||
# return class_._instance | ||
|
||
|
||
class Singleton(object): | ||
def __new__(cls, *args, **kwargs): | ||
if not hasattr(cls, 'instance'): | ||
# Create the one and only instance of this class | ||
cls.instance = super(Singleton, cls).__new__(cls) | ||
return cls.instance |