Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

55 rewrite interface to enable non ai plugins #58

Merged
merged 24 commits into from
Oct 25, 2023
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
6279476
Started building out the interface for custom complex reasoning plug …
Oct 9, 2023
f5dc9cc
This change is still in process. Added a few stubs, but need to have …
Oct 9, 2023
041fdc8
Got the new plug in structure set up, where there is a clear delineat…
Oct 13, 2023
5a413a8
WIP: created 4 interfacs for plugins: learners, planners, knowledge r…
Oct 13, 2023
0e95407
Fix kwarg naming issue in generic_plugin
cfirth-nasa Oct 13, 2023
7c39d39
Abstract plugin import function in interfaces
cfirth-nasa Oct 13, 2023
cd30ef0
WIP: Fixed some ExecutionEngine tests
asgibson Oct 13, 2023
48882ea
Fix bug where config filepath wasn't stored
cfirth-nasa Oct 16, 2023
d6ae231
Fix plugin import function
cfirth-nasa Oct 16, 2023
f0a8d84
Changes to ensure data flow
cfirth-nasa Oct 16, 2023
d32bc3c
Add check for presence of PluginLists
cfirth-nasa Oct 16, 2023
ce89ddb
Updates for plugin changes
asgibson Oct 18, 2023
27642a3
Fixed sim \_\_init\_\_ test
asgibson Oct 19, 2023
d9e30b2
Updates for ExecutionEngine
asgibson Oct 19, 2023
3b571ff
Fixed VehicleRepresentation initialization issues in test_vechicle_re…
tbchase Oct 20, 2023
3b06568
Updates for LearnerInterface
asgibson Oct 20, 2023
228b86d
Fixed Planner unit tests and coverage
asgibson Oct 20, 2023
b72cc59
Fixed Initialization and assertion issues in test_agent
tbchase Oct 20, 2023
611778f
Added ComplexResoningInterface unit tests
asgibson Oct 20, 2023
b95f641
Unit test cleanup
asgibson Oct 20, 2023
6609b63
Added vehicle_rep tests for get_state_information and knowledge synth…
tbchase Oct 20, 2023
b4eded1
VehicleRepresentation updates
asgibson Oct 24, 2023
e6b1368
Agent updates
asgibson Oct 24, 2023
4d955ce
Simplified tlm_json_parserreorganizeTlmDict tests
asgibson Oct 24, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion onair/config/default_config.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ TelemetryFile = 700_crash_to_earth_1.csv
TelemetryMetadataFilePath = onair/data/telemetry_configs/
MetaFile = data_physics_generation_CONFIG.json
ParserFileName = onair/data_handling/csv_parser.py
PluginList = {'generic_plugin':'plugins/generic/generic_plugin.py'}

KnowledgeRepPluginDict = {'generic':'plugins/generic/__init__.py'}
LearnersPluginDict = {'generic':'plugins/generic/__init__.py'}
PlannersPluginDict = {'generic':'plugins/generic/__init__.py'}
ComplexPluginDict = {'generic':'plugins/generic/__init__.py'}

[RUN_FLAGS]
IO_Flag = true
Expand Down
6 changes: 5 additions & 1 deletion onair/config/redis_example.ini
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,11 @@ TelemetryFile = 700_crash_to_earth_1.csv
TelemetryMetadataFilePath = onair/data/telemetry_configs/
MetaFile = data_physics_generation_CONFIG.json
ParserFileName = onair/data_handling/redis_adapter.py
PluginList = {'generic_plugin':'plugins/generic/generic_plugin.py'}

KnowledgeRepPluginDict = {'generic':'plugins/generic/__init__.py'}
LearnersPluginDict = {'generic':'plugins/generic/__init__.py'}
PlannersPluginDict = {'generic':'plugins/generic/__init__.py'}
ComplexPluginDict = {'generic':'plugins/generic/__init__.py'}

[RUN_FLAGS]
IO_Flag = true
Expand Down
2 changes: 1 addition & 1 deletion onair/src/ai_components/ai_plugin_abstract/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def apriori_training(self, batch_data=[]):
raise NotImplementedError

@abstractmethod
def update(self, frame=[]):
def update(self, low_level_data=[], high_level_data={}):
"""
Given streamed data point, system should update internally
"""
Expand Down
19 changes: 6 additions & 13 deletions onair/src/ai_components/learners_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,30 +10,23 @@
"""
Data driven learning class for managing all data driven AI components
"""
import importlib.util

from ..util.plugin_import import import_plugins
from ..util.data_conversion import *

class LearnersInterface:
def __init__(self, headers, _ai_plugins={}):
def __init__(self, headers, _learner_plugins={}):
assert(len(headers)>0), 'Headers are required'
self.headers = headers
self.ai_constructs = []
for module_name in list(_ai_plugins.keys()):
spec = importlib.util.spec_from_file_location(module_name, _ai_plugins[module_name])
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
self.ai_constructs.append(module.Plugin(module_name,headers))
self.ai_constructs = import_plugins(self.headers, _learner_plugins)
the-other-james marked this conversation as resolved.
Show resolved Hide resolved


def apriori_training(self, batch_data):
for plugin in self.ai_constructs:
plugin.apriori_training(batch_data)

def update(self, curr_data, status):
input_data = curr_data
output_data = status_to_oneHot(status)
def update(self, low_level_data, high_level_data):
for plugin in self.ai_constructs:
plugin.update(input_data)
plugin.update(low_level_data)
asgibson marked this conversation as resolved.
Show resolved Hide resolved

def check_for_salient_event(self):
pass
Expand Down
14 changes: 4 additions & 10 deletions onair/src/ai_components/planners_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,16 @@
"""
Planners interface class for managing all planning-based AI components
"""
import importlib.util

from ..util.plugin_import import import_plugins
from ..util.data_conversion import *

class PlannersInterface:
def __init__(self, headers, _ai_plugins={}):
def __init__(self, headers, _planner_plugins={}):
assert(len(headers)>0), 'Headers are required'
self.headers = headers
self.ai_constructs = []
for module_name in list(_ai_plugins.keys()):
spec = importlib.util.spec_from_file_location(module_name, _ai_plugins[module_name])
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
self.ai_constructs.append(module.Plugin(module_name,headers))
self.ai_constructs = import_plugins(self.headers,_planner_plugins)
the-other-james marked this conversation as resolved.
Show resolved Hide resolved

def update(self, curr_raw_tlm, status):
def update(self, high_level_data):
# Raw TLM should be transformed into high-leve state representation here
# Can store something as stale unless a planning thread is launched
pass
Expand Down
37 changes: 24 additions & 13 deletions onair/src/reasoning/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,29 +13,40 @@
"""
from ..ai_components.learners_interface import LearnersInterface
from ..ai_components.planners_interface import PlannersInterface
from ..reasoning.complex_reasoning_interface import ComplexReasoningInterface
from ..reasoning.diagnosis import Diagnosis

class Agent:
def __init__(self, vehicle, plugin_list):
def __init__(self, vehicle, learners_plugin_dict, planners_plugin_dict, complex_plugin_dict):

self.vehicle_rep = vehicle
self.mission_status = self.vehicle_rep.get_status()
self.bayesian_status = self.vehicle_rep.get_bayesian_status()

# AI Interfaces
self.learning_systems = LearnersInterface(self.vehicle_rep.get_headers(),plugin_list)
self.planning_systems = PlannersInterface(self.vehicle_rep.get_headers(),plugin_list)
self.learning_systems = LearnersInterface(self.vehicle_rep.get_headers(),learners_plugin_dict)
self.planning_systems = PlannersInterface(self.vehicle_rep.get_headers(),planners_plugin_dict)
self.complex_reasoning_systems = ComplexReasoningInterface(self.vehicle_rep.get_headers(),complex_plugin_dict)


def render_reasoning(self):
return self.complex_reasoning_systems.render_reasoning()

# Markov Assumption holds
def reason(self, frame):
the-other-james marked this conversation as resolved.
Show resolved Hide resolved
# Update with new telemetry
self.vehicle_rep.update(frame)
self.mission_status = self.vehicle_rep.get_status()
self.learning_systems.update(frame, self.mission_status)
self.planning_systems.update(frame, self.mission_status)

# Check for a salient event, needing acionable outcome
self.learning_systems.check_for_salient_event()
self.planning_systems.check_for_salient_event()
self.vehicle_rep.update(frame)
self.learning_systems.update(frame, self.vehicle_rep.get_state_information(['status']))
self.planning_systems.update(self.vehicle_rep.get_state_information('PDDL_state'))


aggregate_high_level_info = {'vehicle_rep' : self.vehicle_rep.get_state_information(),
'learning_systems' : self.learning_systems.render_reasoning(),
'planning_systems' : self.planning_systems.render_reasoning()}

self.complex_reasoning_systems.update(aggregate_high_level_info)

return self.render_reasoning()
# Does this need further separation?


def diagnose(self, time_step):
""" Grab the mnemonics from the """
Expand Down
34 changes: 34 additions & 0 deletions onair/src/reasoning/complex_reasoning_interface.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# 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"

"""
Reasoning interface class for managing all complex custom reasoning components
"""

from ..util.data_conversion import *
from ..util.plugin_import import import_plugins

class ComplexReasoningInterface:
def __init__(self, headers, _reasoning_plugins={}):
assert(len(headers)>0), 'Headers are required'
self.headers = headers
self.reasoning_constructs = import_plugins(self.headers,_reasoning_plugins)

def update(self, high_level_data):
for plugin in self.reasoning_constructs:
plugin.update(high_level_data=high_level_data)

def check_for_salient_event(self):
pass

def render_reasoning(self):
intelligent_outcomes = {}
for plugin in self.reasoning_constructs:
intelligent_outcomes[plugin.component_name] = plugin.render_reasoning()
return intelligent_outcomes
53 changes: 35 additions & 18 deletions onair/src/run_scripts/execution_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ def __init__(self, config_file='', run_name='', save_flag=False):

# Init Housekeeping
self.run_name = run_name
self.config_filepath = config_file

# Init Flags
self.IO_Flag = False
Expand All @@ -49,7 +50,10 @@ def __init__(self, config_file='', run_name='', save_flag=False):
self.sim = None

# Init plugins
self.plugin_list = ['']
self.knowledge_rep_plugin_dict = ['']
self.learners_plugin_dict = ['']
self.planners_plugin_dict = ['']
self.complex_plugin_dict = ['']

self.save_flag = save_flag
self.save_name = run_name
Expand Down Expand Up @@ -78,25 +82,21 @@ def parse_configs(self, config_filepath):
## Parse Required Data: Names
self.parser_file_name = config['DEFAULT']['ParserFileName']

## Parse Required Data: Plugin name to path dict
config_plugin_list = config['DEFAULT']['PluginList']
ast_plugin_list = self.ast_parse_eval(config_plugin_list)
if isinstance(ast_plugin_list.body, ast.Dict) and len(ast_plugin_list.body.keys) > 0:
temp_plugin_list = ast.literal_eval(config_plugin_list)
else:
raise ValueError(f"{config_plugin_list} is an invalid PluginList. It must be a dict of at least 1 key/value pair.")
for plugin_name in temp_plugin_list.values():
if not(os.path.exists(plugin_name)):
raise FileNotFoundError(f"In config file '{config_filepath}', path '{plugin_name}' does not exist or is formatted incorrectly.")
self.plugin_list = temp_plugin_list
## Parse Required Data: Plugins
self.knowledge_rep_plugin_dict = self.parse_plugins_dict(config['DEFAULT']['KnowledgeRepPluginDict'])
self.learners_plugin_dict = self.parse_plugins_dict(config['DEFAULT']['LearnersPluginDict'])
self.planners_plugin_dict = self.parse_plugins_dict(config['DEFAULT']['PlannersPluginDict'])
self.complex_plugin_dict = self.parse_plugins_dict(config['DEFAULT']['ComplexPluginDict'])

## Parse Optional Data: Flags
## 'RUN_FLAGS' must exist, but individual flags return False if missing
self.IO_Flag = config['RUN_FLAGS'].getboolean('IO_Flag')
the-other-james marked this conversation as resolved.
Show resolved Hide resolved
self.Dev_Flag = config['RUN_FLAGS'].getboolean('Dev_Flag')
self.Viz_Flag = config['RUN_FLAGS'].getboolean('Viz_Flag')

except KeyError as e:
new_message = f"Config file: '{config_filepath}', missing key: {e.args[0]}"
raise KeyError(new_message) from e

## Parse Optional Data: Flags
self.IO_Flag = config['RUN_FLAGS'].getboolean('IO_Flag')
self.Dev_Flag = config['RUN_FLAGS'].getboolean('Dev_Flag')
self.Viz_Flag = config['RUN_FLAGS'].getboolean('Viz_Flag')

## Parse Optional Data: Benchmarks
try:
Expand All @@ -106,14 +106,31 @@ def parse_configs(self, config_filepath):
except:
pass

def parse_plugins_dict(self, config_plugin_dict):
## Parse Required Data: Plugin name to path dict
ast_plugin_dict = self.ast_parse_eval(config_plugin_dict)
if isinstance(ast_plugin_dict.body, ast.Dict):
temp_plugin_dict = ast.literal_eval(config_plugin_dict)
else:
raise ValueError(f"Plugin dict {config_plugin_dict} from {self.config_filepath} is invalid. It must be a dict.")

for plugin_file in temp_plugin_dict.values():
if not(os.path.exists(plugin_file)):
raise FileNotFoundError(f"In config file '{self.config_filepath}' Plugin path '{plugin_file}' does not exist.")
return temp_plugin_dict

def parse_data(self, parser_file_name, data_file_name, metadata_file_name, subsystems_breakdown=False):
data_source_spec = importlib.util.spec_from_file_location('data_source', parser_file_name)
data_source_module = importlib.util.module_from_spec(data_source_spec)
data_source_spec.loader.exec_module(data_source_module)
self.simDataParser = data_source_module.DataSource(data_file_name, metadata_file_name, subsystems_breakdown)
the-other-james marked this conversation as resolved.
Show resolved Hide resolved

def setup_sim(self):
self.sim = Simulator(self.simDataParser, self.plugin_list)
self.sim = Simulator(self.simDataParser,
self.knowledge_rep_plugin_dict,
self.learners_plugin_dict,
self.planners_plugin_dict,
self.complex_plugin_dict)
try:
fls = ast.literal_eval(self.benchmarkFiles)
fp = os.path.dirname(os.path.realpath(__file__)) + '/../..' + self.benchmarkFilePath
Expand Down
7 changes: 3 additions & 4 deletions onair/src/run_scripts/sim.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,11 @@
DIAGNOSIS_INTERVAL = 100

class Simulator:
def __init__(self, dataParser, plugin_list):
def __init__(self, dataParser, knowledge_rep_plugin_dict, learners_plugin_dict, planners_plugin_dict, complex_plugin_dict):
self.simData = dataParser

headers, tests = dataParser.get_vehicle_metadata()
vehicle = VehicleRepresentation(headers, tests)
self.agent = Agent(vehicle, plugin_list)
vehicle = VehicleRepresentation(headers, tests, knowledge_rep_plugin_dict)
self.agent = Agent(vehicle, learners_plugin_dict, planners_plugin_dict, complex_plugin_dict)

def run_sim(self, IO_Flag=False, dev_flag=False, viz_flag = True):
if IO_Flag == True: print_sim_header()
Expand Down
23 changes: 20 additions & 3 deletions onair/src/systems/vehicle_rep.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,28 @@
from .telemetry_test_suite import TelemetryTestSuite

from ..util.print_io import *
from ..util.plugin_import import import_plugins
# from ..util.data_conversion import *

class VehicleRepresentation:
def __init__(self, headers, tests):
def __init__(self, headers, tests, _knowledge_rep_plugins={}):
assert(len(headers) == len(tests))
self.status = Status('MISSION')
self.headers = headers
self.knowledge_synthesis_constructs = import_plugins(self.headers,_knowledge_rep_plugins)


self.status = Status('MISSION')
self.test_suite = TelemetryTestSuite(headers, tests)
self.curr_data = ['-']* len(self.headers)


self.curr_data = ['-']* len(self.headers) #stale data

##### UPDATERS #################################
def update(self, frame):
# Update constructs
for construct in self.knowledge_synthesis_constructs:
construct.update(frame)

for i in range(len(frame)):
if frame[i] != '-':
self.curr_data[i] = frame[i]
Expand Down Expand Up @@ -55,4 +66,10 @@ def get_bayesian_status(self):
def get_batch_status_reports(self, batch_data):
return

def get_state_information(self, scope=['status']):
state_info = {}
for construct in self.knowledge_synthesis_constructs:
state_info[construct.component_name] = construct.render_reasoning()
return state_info


27 changes: 27 additions & 0 deletions onair/src/util/plugin_import.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# 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

def import_plugins(headers, module_dict):
plugin_list = []
for module_name in list(module_dict.keys()):
spec = importlib.util.spec_from_file_location(module_name, module_dict[module_name])
module = importlib.util.module_from_spec(spec)
spec.loader.exec_module(module)
sys.modules[module_name] = module
plugin = __import__(f'{module_name}.{module_name}_plugin', fromlist=[f'{module_name}_plugin'])
plugin_list.append(plugin.Plugin(module_name,headers))
return(plugin_list)
the-other-james marked this conversation as resolved.
Show resolved Hide resolved
1 change: 0 additions & 1 deletion plugins/generic/__init__.py
Original file line number Diff line number Diff line change
@@ -1 +0,0 @@
from .generic_plugin import Plugin
2 changes: 1 addition & 1 deletion plugins/generic/generic_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def apriori_training(self,batch_data=[]):
"""
pass

def update(self,frame=[]):
def update(self,low_level_data=[], high_level_data={}):
asgibson marked this conversation as resolved.
Show resolved Hide resolved
"""
Given streamed data point, system should update internally
"""
Expand Down
Empty file added plugins/kalman/__init__.py
Empty file.
File renamed without changes.
1 change: 0 additions & 1 deletion plugins/kalman_plugin/__init__.py

This file was deleted.

Loading
Loading