|
| 1 | +from ...utils import settings,config |
| 2 | +from ..component import Component |
| 3 | +from .execution import EXECUTION_PREFIX,ExecutorBase,ComponentExecutor,load_computation_graph,make_class |
| 4 | +import multiprocessing |
| 5 | +from typing import Dict,List,Optional |
| 6 | +import sys |
| 7 | +import os |
| 8 | + |
| 9 | +def main(): |
| 10 | + """The main entrypoint for the execution stack.""" |
| 11 | + #first, process settings variants |
| 12 | + if settings.get('variant',None) is not None: |
| 13 | + variant = settings.get('variant') |
| 14 | + variants = variant.split(',') |
| 15 | + def caution_callback(k,variant): |
| 16 | + print(EXECUTION_PREFIX,"variant {} defines key {} which is not found in normal settings".format(variant,k)) |
| 17 | + for v in variants: |
| 18 | + if v not in settings.get('variants',{}): |
| 19 | + if v not in settings.get('run.variants',{}): |
| 20 | + print(EXECUTION_PREFIX,"Error, variant",v,"not found in settings") |
| 21 | + print(EXECUTION_PREFIX,"Available variants are",list(settings.get('variants',{}).keys())+list(settings.get('run.variants',{}).keys())) |
| 22 | + return 1 |
| 23 | + else: |
| 24 | + overrides = settings.get('run.variants.'+v) |
| 25 | + print(EXECUTION_PREFIX,"APPYING VARIANT",overrides) |
| 26 | + config.update_recursive(settings.settings(),overrides,lambda k:caution_callback(k,v)) |
| 27 | + else: |
| 28 | + overrides = settings.get('variants.'+v) |
| 29 | + print(EXECUTION_PREFIX,"APPYING VARIANT",overrides) |
| 30 | + config.update_recursive(settings.settings(),overrides,lambda k:caution_callback(k,v)) |
| 31 | + #don't need this to be saved in the log |
| 32 | + if 'variants' in settings.settings(): |
| 33 | + del settings.settings()['variants'] |
| 34 | + if 'svariant' in settings.get('run'): |
| 35 | + del settings.get('run')['variants'] |
| 36 | + |
| 37 | + multiprocessing.set_start_method('spawn') |
| 38 | + runconfig = settings.get('run') |
| 39 | + mode = settings.get('run.mode') |
| 40 | + vehicle_interface_settings = settings.get('run.vehicle_interface') |
| 41 | + mission_executor_settings = settings.get('run.mission_execution') |
| 42 | + log_settings = settings.get('run.log',{}) |
| 43 | + replay_settings = settings.get('run.replay',{}) |
| 44 | + load_computation_graph() |
| 45 | + |
| 46 | + #initialize ros node |
| 47 | + has_ros = False |
| 48 | + try: |
| 49 | + import rospy |
| 50 | + rospy.init_node('GEM_executor',disable_signals=True) |
| 51 | + has_ros = True |
| 52 | + except (ImportError,ModuleNotFoundError): |
| 53 | + if mode == 'simulation': |
| 54 | + print(EXECUTION_PREFIX,"Warning, ROS not found, but simulation mode requested") |
| 55 | + else: |
| 56 | + print(EXECUTION_PREFIX,"Error, ROS not found on system.") |
| 57 | + raise |
| 58 | + |
| 59 | + #create top-level components |
| 60 | + vehicle_interface = make_class(vehicle_interface_settings,'default','GEMstack.onboard.interface') |
| 61 | + mission_executor = make_class(mission_executor_settings,'execution','GEMstack.onboard.execution',{'vehicle_interface':vehicle_interface}) |
| 62 | + if not isinstance(mission_executor,ExecutorBase): |
| 63 | + raise ValueError("Mission executor must be an Executor") |
| 64 | + |
| 65 | + recovery_pipeline_settings = settings.get('run.recovery') |
| 66 | + visualization_settings = settings.get('run.visualization',None) |
| 67 | + state_settings = settings.get('run.state') |
| 68 | + |
| 69 | + # create pipelines and add to executor |
| 70 | + pipeline_settings = {'recovery': recovery_pipeline_settings} |
| 71 | + |
| 72 | + for k,v in runconfig.items(): |
| 73 | + if isinstance(v,dict) and ('perception' in v or 'planning' in v or 'other' in v): |
| 74 | + # possible pipeline |
| 75 | + if k not in pipeline_settings: |
| 76 | + print(EXECUTION_PREFIX,"Adding non-standard pipeline",k) |
| 77 | + pipeline_settings[k] = v |
| 78 | + |
| 79 | + visualizers = [] |
| 80 | + if isinstance(visualization_settings,dict): |
| 81 | + #one visualizer |
| 82 | + visualizers.append(mission_executor.make_component(visualization_settings,'visualization','GEMstack.onboard.visualization',{'vehicle_interface':vehicle_interface})) |
| 83 | + elif isinstance(visualization_settings,list): |
| 84 | + #multiple visualizers |
| 85 | + for v in visualization_settings: |
| 86 | + visualizers.append(mission_executor.make_component(v,'visualization','GEMstack.onboard.visualization',{'vehicle_interface':vehicle_interface})) |
| 87 | + for v in visualizers: |
| 88 | + mission_executor.always_run(v.c.__class__.__name__,v) |
| 89 | + |
| 90 | + # mark the components that will be replayed rather than run |
| 91 | + if replay_settings: |
| 92 | + logfolder = replay_settings.get('log',None) |
| 93 | + if logfolder is not None: |
| 94 | + replay_components = replay_settings.get('components',[]) |
| 95 | + mission_executor.replay_components(replay_components,logfolder) |
| 96 | + |
| 97 | + #TODO: ROS topic replay |
| 98 | + logmeta = config.load_config_recursive(os.path.join(logfolder,'meta.yaml')) |
| 99 | + replay_topics = replay_settings.get('ros_topics',[]) |
| 100 | + |
| 101 | + #TODO: launch a roslog replay of the topics in ros_topics, disable in the vehicle interface |
| 102 | + |
| 103 | + for state in state_settings: |
| 104 | + pipeline_settings[state] = settings.get('run.' + state) |
| 105 | + |
| 106 | + for (name,s) in pipeline_settings.items(): |
| 107 | + perception_settings = s.get('perception',{}) |
| 108 | + planning_settings = s.get('planning',{}) |
| 109 | + other_settings = s.get('other',{}) |
| 110 | + |
| 111 | + perception_components = {} #type: Dict[str,ComponentExecutor] |
| 112 | + for (k,v) in perception_settings.items(): |
| 113 | + if v is None: continue |
| 114 | + perception_components[k] = mission_executor.make_component(v,k,'GEMstack.onboard.perception', {'vehicle_interface':vehicle_interface}) |
| 115 | + planning_components = {} #type: Dict[str,ComponentExecutor] |
| 116 | + for (k,v) in planning_settings.items(): |
| 117 | + if v is None: continue |
| 118 | + planning_components[k] = mission_executor.make_component(v,k,'GEMstack.onboard.planning', {'vehicle_interface':vehicle_interface}) |
| 119 | + other_components = {} #type: Dict[str,ComponentExecutor] |
| 120 | + for (k,v) in other_settings.items(): |
| 121 | + if v is None: continue |
| 122 | + other_components[k] = mission_executor.make_component(v,k,'GEMstack.onboard.other', {'vehicle_interface':vehicle_interface}) |
| 123 | + |
| 124 | + mission_executor.add_pipeline(name, perception_components, planning_components, other_components) |
| 125 | + |
| 126 | + #configure logging |
| 127 | + if log_settings: |
| 128 | + topfolder = log_settings.get('folder','logs') |
| 129 | + prefix = log_settings.get('prefix','') |
| 130 | + from datetime import datetime |
| 131 | + default_suffix = datetime.now().strftime('%Y-%m-%d_%H-%M-%S') |
| 132 | + suffix = log_settings.get('suffix',default_suffix) |
| 133 | + logfolder = os.path.join(topfolder,prefix+suffix) |
| 134 | + print(EXECUTION_PREFIX,"Logging to",logfolder) |
| 135 | + auto_plot = log_settings.get("auto_plot", False) |
| 136 | + os.makedirs(logfolder,exist_ok=True) |
| 137 | + |
| 138 | + #configure logging for components |
| 139 | + mission_executor.set_log_folder(logfolder) |
| 140 | + mission_executor.set_auto_plot(auto_plot) |
| 141 | + #configure ROS logging |
| 142 | + log_topics = log_settings.get('ros_topics',[]) |
| 143 | + rosbag_options = log_settings.get('rosbag_options','') |
| 144 | + mission_executor.log_ros_topics(log_topics, rosbag_options) |
| 145 | + #determine whether to log vehicle interface |
| 146 | + log_vehicle_interface = log_settings.get('vehicle_interface',False) |
| 147 | + if log_vehicle_interface: |
| 148 | + mission_executor.log_vehicle_interface(log_vehicle_interface) |
| 149 | + #determine whether to log components |
| 150 | + log_components = log_settings.get('components',[]) |
| 151 | + mission_executor.log_components(log_components) |
| 152 | + #determine whether to log state |
| 153 | + log_state_attributes = log_settings.get('state',[]) |
| 154 | + log_state_rate = log_settings.get('state_rate',None) |
| 155 | + if log_state_attributes: |
| 156 | + mission_executor.log_state(log_state_attributes,log_state_rate) |
| 157 | + |
| 158 | + vehicle_interface.start() |
| 159 | + try: |
| 160 | + mission_executor.run() |
| 161 | + except Exception as e: |
| 162 | + raise |
| 163 | + finally: |
| 164 | + vehicle_interface.stop() |
| 165 | + |
| 166 | + if has_ros: |
| 167 | + #need manual ros node shutdown due to disable_signals=True |
| 168 | + rospy.signal_shutdown('GEM_executor finished') |
| 169 | + |
| 170 | + print(EXECUTION_PREFIX,"---------------- DONE ----------------") |
| 171 | + if log_settings and settings.get('run.after.show_log_folder',False): |
| 172 | + if sys.platform.startswith('linux'): |
| 173 | + os.system('nautilus '+logfolder) |
| 174 | + else: |
| 175 | + import webbrowser |
| 176 | + webbrowser.open(logfolder) |
| 177 | + return 0 |
0 commit comments