Home | Trees | Indices | Help |
---|
|
1 # Software License Agreement (BSD License) 2 # 3 # Copyright (c) 2012, Fraunhofer FKIE/US, Alexander Tiderko 4 # All rights reserved. 5 # 6 # Redistribution and use in source and binary forms, with or without 7 # modification, are permitted provided that the following conditions 8 # are met: 9 # 10 # * Redistributions of source code must retain the above copyright 11 # notice, this list of conditions and the following disclaimer. 12 # * Redistributions in binary form must reproduce the above 13 # copyright notice, this list of conditions and the following 14 # disclaimer in the documentation and/or other materials provided 15 # with the distribution. 16 # * Neither the name of Fraunhofer nor the names of its 17 # contributors may be used to endorse or promote products derived 18 # from this software without specific prior written permission. 19 # 20 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 21 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 22 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS 23 # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE 24 # COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, 25 # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 26 # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 27 # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 28 # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN 30 # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 31 # POSSIBILITY OF SUCH DAMAGE. 32 from python_qt_binding import loadUi 33 from python_qt_binding.QtCore import QTimer 34 try: 35 from python_qt_binding.QtGui import QDockWidget, QFileDialog 36 except: 37 from python_qt_binding.QtWidgets import QDockWidget, QFileDialog 38 import os 39 import roslib 40 import rospy 41 import uuid 42 from master_discovery_fkie.common import get_hostname, resolve_url 43 44 import node_manager_fkie as nm 45 from .common import get_rosparam, delete_rosparam, package_name, to_url, utf8 46 from .detailed_msg_box import MessageBox 47 4850 ''' 51 Profile widget to show the current load state of the profile 52 ''' 5335355 ''' 56 Creates the window, connects the signals and init the class. 57 ''' 58 QDockWidget.__init__(self, parent) 59 # load the UI file 60 profile_dock_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'ProfileWidget.ui') 61 loadUi(profile_dock_file, self) 62 self._main_window = main_window 63 self.setVisible(False) 64 self._current_profile = dict()6567 # save the profile 68 (path, _) = QFileDialog.getSaveFileName(self, 69 "New profile file", 70 nm.settings().current_dialog_path, 71 "node manager profile files (*.nmprofile);;All files (*)") # _:=filter 72 if path: 73 if not path.endswith('.nmprofile'): 74 path = "%s.nmprofile" % path 75 nm.settings().current_dialog_path = os.path.dirname(path) 76 try: 77 (pkg, _) = package_name(os.path.dirname(path)) # _:=pkg_path 78 if pkg is None: 79 ret = MessageBox.warning(self, "New File Error", 80 'The new file is not in a ROS package', buttons=MessageBox.Ok | MessageBox.Cancel) 81 if ret == MessageBox.Cancel: 82 return None 83 return path 84 except EnvironmentError as e: 85 MessageBox.warning(self, "New File Error", 86 'Error while create a new file', 87 utf8(e)) 88 return None8991 ''' 92 Saves the current environment to a node manager profile. 93 :param path: the pach the file to save 94 :type path: str 95 :param masteruri: If not empty, save the profile only for given master 96 :type masteruri: str 97 ''' 98 try: 99 if path is None: 100 path = self.get_profile_file() 101 if path is None: 102 return 103 rospy.loginfo("Save profile %s" % path) 104 import yaml 105 content = {} 106 for muri, master in self._main_window.masters.items(): 107 if not masteruri or masteruri == muri: 108 running_nodes = master.getRunningNodesIfLocal() 109 configs = {} 110 md_param = {} 111 ms_param = {} 112 zc_param = {} 113 smuri = muri 114 addr = nm.nameres().address(smuri) 115 hostname = get_hostname(smuri) 116 mastername = '' 117 if nm.is_local(addr): 118 smuri = smuri.replace(hostname, '$LOCAL$') 119 addr = '$LOCAL$' 120 else: 121 mastername = nm.nameres().mastername(smuri, nm.nameres().address(smuri)) 122 for node_name in running_nodes.keys(): 123 node_items = master.getNode(node_name) 124 for node in node_items: 125 if node.is_running() and node.launched_cfg is not None: # node.has_launch_cfgs(node.cfgs): 126 cfg = node.launched_cfg 127 if isinstance(node.launched_cfg, (str, unicode)): 128 # it is default config 129 cfg = node.launched_cfg.replace("/%s" % hostname, '/$LOCAL$').rstrip('/run') 130 else: 131 # it is a loaded launch file, get the filename 132 cfg = to_url(node.launched_cfg.Filename) 133 if cfg not in configs: 134 configs[cfg] = {'nodes': []} 135 configs[cfg]['nodes'].append(node_name) 136 elif node_name.endswith('master_discovery'): 137 md_param = get_rosparam('master_discovery', muri) 138 elif node_name.endswith('master_sync'): 139 ms_param = get_rosparam('master_sync', muri) 140 elif node_name.endswith('zeroconf'): 141 zc_param = get_rosparam('zeroconf', muri) 142 elif node_name.endswith('default_cfg'): 143 # store parameter for default configuration 144 nn = node_name.replace("/%s" % hostname, '/$LOCAL$') 145 if nn not in configs: 146 configs[nn] = {'nodes': []} 147 configs[nn]['params'] = get_rosparam(node_name, muri) 148 configs[nn]['default'] = True 149 # store arguments for launchfiles 150 for a, b in master.launchfiles.items(): 151 resolved_a = to_url(a) 152 if resolved_a not in configs: 153 if resolved_a.endswith('default_cfg/run'): 154 pass 155 else: 156 configs[resolved_a] = {} 157 configs[resolved_a]['default'] = False 158 configs[resolved_a]['argv'] = b.argv 159 # fill the configuration content for yaml as dictionary 160 content[smuri] = {'mastername': mastername, 161 'address': addr, 162 'configs': configs} 163 if md_param: 164 content[smuri]['master_discovery'] = md_param 165 if ms_param: 166 content[smuri]['master_sync'] = ms_param 167 if zc_param: 168 content[smuri]['zeroconf'] = zc_param 169 text = yaml.dump(content, default_flow_style=False) 170 with open(path, 'w+') as f: 171 f.write(text) 172 except Exception as e: 173 import traceback 174 print utf8(traceback.format_exc(3)) 175 MessageBox.warning(self, "Save profile Error", 176 'Error while save profile', 177 utf8(e))178180 ''' 181 Load the profile file. 182 :param path: the path of the profile file. 183 :type path: str 184 ''' 185 rospy.loginfo("Load profile %s" % path) 186 self.progressBar.setValue(0) 187 self.setVisible(True) 188 self.setWindowTitle("%s profile started" % os.path.basename(path).rstrip('.nmprofile')) 189 hasstart = False 190 if path: 191 try: 192 import yaml 193 with open(path, 'r') as f: 194 # print yaml.load(f.read()) 195 content = yaml.load(f.read()) 196 if not isinstance(content, dict): 197 raise Exception("Mailformed profile: %s" % os.path.basename(path)) 198 for muri, master_dict in content.items(): 199 local_hostname = get_hostname(self._main_window.getMasteruri()) 200 rmuri = muri.replace('$LOCAL$', local_hostname) 201 master = self._main_window.getMaster(rmuri) 202 running_nodes = master.getRunningNodesIfLocal() 203 usr = None 204 self._current_profile[rmuri] = set() 205 if 'user' in master_dict: 206 usr = master_dict['user'] 207 if master_dict['mastername'] and master_dict['mastername']: 208 nm.nameres().add_master_entry(master.masteruri, master_dict['mastername'], master_dict['address']) 209 hostname = master_dict['address'].replace('$LOCAL$', local_hostname) 210 if 'master_discovery' in master_dict: 211 self._start_node_from_profile(master, hostname, 'master_discovery_fkie', 'master_discovery', usr, cfg=master_dict['master_discovery']) 212 self._current_profile[rmuri].add('/master_discovery') 213 if 'master_sync' in master_dict: 214 self._start_node_from_profile(master, hostname, 'master_sync_fkie', 'master_sync', usr, cfg=master_dict['master_sync']) 215 self._current_profile[rmuri].add('/master_sync') 216 if 'zeroconf' in master_dict: 217 self._start_node_from_profile(master, hostname, 'master_discovery_fkie', 'zeroconf', usr, cfg=master_dict['zeroconf']) 218 self._current_profile[rmuri].add('/zeroconf') 219 try: 220 do_start = [] 221 do_not_stop = {'/rosout', rospy.get_name(), '/node_manager', '/master_discovery', '/master_sync', '*default_cfg', '/zeroconf'} 222 configs = master_dict['configs'] 223 conf_set = set() 224 for cfg_name, cmdict in configs.items(): 225 cfg_name = cfg_name.replace('$LOCAL$', local_hostname) 226 if cfg_name.startswith("pkg://"): 227 cfg_name = os.path.abspath(resolve_url(cfg_name)) 228 conf_set.add(cfg_name) 229 reload_launch = True 230 argv = [] 231 is_default = False 232 if 'default' in cmdict: 233 if cmdict['default']: 234 # it is a default configuration, test for load or not 235 is_default = True 236 if 'argv_used' in cmdict['params']: 237 argv = cmdict['params']['argv_used'] 238 if cfg_name in master.default_cfgs: 239 reload_launch = False 240 if argv: 241 params = get_rosparam(cfg_name, rmuri) 242 if 'argv_used' in params: 243 reload_launch = set(params['argv_used']) != set(argv) 244 if reload_launch: 245 self._main_window.on_load_launch_as_default_bypkg(cmdict['params']['package'], cmdict['params']['launch_file'], master, argv, local_hostname) 246 else: 247 do_not_stop.add(cfg_name) 248 elif 'argv' in cmdict: 249 if 'argv' in cmdict: 250 argv = cmdict['argv'] 251 # do we need to load to load/reload launch file 252 if cfg_name in master.launchfiles: 253 reload_launch = set(master.launchfiles[cfg_name].argv) != set(argv) 254 if reload_launch: 255 self._main_window.launch_dock.load_file(cfg_name, argv, master.masteruri) 256 if 'nodes' in cmdict: 257 self._current_profile[rmuri].update(cmdict['nodes']) 258 force_start = True 259 cfg = cfg_name if not is_default else roslib.names.ns_join(cfg_name, 'run') 260 if not reload_launch: 261 force_start = False 262 do_not_stop.update(set(cmdict['nodes'])) 263 do_start.append((reload_launch, cfg, cmdict['nodes'], force_start)) 264 else: 265 do_start.append((reload_launch, cfg, cmdict['nodes'], force_start)) 266 # close unused configurations 267 for lfile in set(master.launchfiles.keys()) - conf_set: 268 master._close_cfg(lfile) 269 master.stop_nodes_by_name(running_nodes.keys(), True, do_not_stop) 270 for reload_launch, cfg, nodes, force_start in do_start: 271 if nodes: 272 hasstart = True 273 if reload_launch: 274 master.start_nodes_after_load_cfg(cfg.rstrip('/run'), list(nodes), force_start) 275 else: 276 master.start_nodes_by_name(list(nodes), cfg, force_start) 277 except Exception as ml: 278 import traceback 279 print utf8(traceback.format_exc(1)) 280 rospy.logwarn("Can not load launch file for %s: %s" % (muri, utf8(ml))) 281 except Exception as e: 282 import traceback 283 print traceback.format_exc(1) 284 MessageBox.warning(self, "Load profile error", 285 'Error while load profile', 286 utf8(e)) 287 if not hasstart: 288 self.update_progress() 289 else: 290 QTimer.singleShot(1000, self.update_progress)291293 if self.isVisible(): 294 count = 0 295 count_run = 0 296 for muri, nodes in self._current_profile.items(): 297 count += len(nodes) 298 master = self._main_window.getMaster(muri, False) 299 if master is not None: 300 running_nodes = master.getRunningNodesIfLocal() 301 profile_nodes = nodes & set(running_nodes.keys()) 302 count_run += len(profile_nodes) 303 if count > 0: 304 progress = float(count_run) / float(count) * 100 305 if progress >= 100: 306 self.setVisible(False) 307 else: 308 self.progressBar.setValue(progress)309311 rospy.loginfo("Cancel profile loading...") 312 QDockWidget.closeEvent(self, event) 313 ret = MessageBox.warning(self, "Cancel Start?", 314 'This stops all starting queues!', buttons=MessageBox.Ok | MessageBox.Cancel) 315 if ret == MessageBox.Cancel: 316 return None 317 self._main_window._progress_queue.stop() 318 self._main_window.launch_dock.progress_queue.stop() 319 for muri, _ in self._current_profile.items(): 320 master = self._main_window.getMaster(muri, False) 321 if master is not None: 322 master.start_nodes_after_load_cfg_clear() 323 master._progress_queue.stop() 324 rospy.loginfo("Profile loading canceled!")325327 try: 328 args = [] 329 restart = False 330 # test for start or not 331 node = master.getNode("/%s" % binary) 332 if node: 333 param = get_rosparam(binary, master.masteruri) 334 if set(param.keys()) == set(cfg.keys()): 335 for k, v in param.items(): 336 if v != cfg[k]: 337 restart = True 338 master.stop_node(node[0], True) 339 break 340 else: 341 restart = True 342 if restart: 343 delete_rosparam(binary, master.masteruri) 344 for pname, pval in cfg.items(): 345 args.append('_%s:=%s' % (pname, pval)) 346 self._main_window._progress_queue.add2queue(utf8(uuid.uuid4()), 347 'start %s on %s' % (binary, hostname), 348 nm.starter().runNodeWithoutConfig, 349 (utf8(hostname), pkg, utf8(binary), utf8(binary), args, master.masteruri, False, usr)) 350 self._main_window._progress_queue.start() 351 except Exception as me: 352 rospy.logwarn("Can not start %s for %s: %s" % (binary, master.masteruri, utf8(me)))
Home | Trees | Indices | Help |
---|
Generated by Epydoc 3.0.1 on Tue Mar 1 06:56:10 2022 | http://epydoc.sourceforge.net |