1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33 from python_qt_binding import loadUi
34 from python_qt_binding.QtCore import QRegExp, Qt, Signal, QRect
35 from python_qt_binding.QtGui import QImage, QKeySequence, QBrush, QPen
36 from rosgraph.names import is_legal_name
37 import getpass
38 import os
39 import re
40 import roslib
41 import rospy
42 import socket
43 import sys
44 import time
45 import traceback
46 import uuid
47 import xmlrpclib
48
49 from master_discovery_fkie.common import get_hostname
50 from master_discovery_fkie.master_info import NodeInfo
51 from node_manager_fkie.common import masteruri_from_ros, get_packages, package_name, resolve_paths, utf8
52 from node_manager_fkie.default_cfg_handler import DefaultConfigHandler
53 from node_manager_fkie.detailed_msg_box import MessageBox, DetailedError
54 from node_manager_fkie.html_delegate import HTMLDelegate
55 from node_manager_fkie.launch_config import LaunchConfig
56 from node_manager_fkie.launch_server_handler import LaunchServerHandler
57 from node_manager_fkie.message_frame import MessageData, MessageFrame
58 from node_manager_fkie.node_tree_model import NodeTreeModel, NodeItem, GroupItem, HostItem, CellItem
59 from node_manager_fkie.parameter_dialog import ParameterDialog, MasterParameterDialog, ServiceDialog
60 from node_manager_fkie.parameter_handler import ParameterHandler
61 from node_manager_fkie.parameter_list_model import ParameterModel, ParameterNameItem, ParameterValueItem
62 from node_manager_fkie.progress_queue import ProgressQueue, InteractionNeededError
63 from node_manager_fkie.select_dialog import SelectDialog
64 from node_manager_fkie.service_list_model import ServiceModel, ServiceItem, ServiceGroupItem
65 from node_manager_fkie.start_handler import AdvRunCfg
66 from node_manager_fkie.supervised_popen import SupervisedPopen
67 from node_manager_fkie.topic_list_model import TopicModel, TopicItem, TopicGroupItem
68 import node_manager_fkie as nm
69 try:
70 from python_qt_binding.QtGui import QAction, QFileDialog, QMenu, QShortcut, QWidget
71 from python_qt_binding.QtGui import QApplication, QVBoxLayout, QItemDelegate, QStyle
72 except:
73 from python_qt_binding.QtWidgets import QAction, QFileDialog, QMenu, QShortcut, QWidget
74 from python_qt_binding.QtWidgets import QApplication, QVBoxLayout, QItemDelegate, QStyle
75
76
77 try:
78 from python_qt_binding.QtGui import QItemSelection, QItemSelectionModel, QItemSelectionRange, QSortFilterProxyModel
79 except:
80 from python_qt_binding.QtCore import QItemSelection, QItemSelectionModel, QItemSelectionRange, QSortFilterProxyModel
84 ''' Request needed to set the args of a launchfile from another thread.
85 @param args: a dictionary with args and values
86 @type args: dict
87 @param error: an error description
88 @type error: str
89 '''
90
91 - def __init__(self, launchfile, args, error):
92 Exception.__init__(self)
93 self.launchfile = launchfile
94 self.args_dict = args
95 self.error = error
96
98 return "LaunchArgsSelectionRequest for " + utf8(self.args_dict) + "::" + repr(self.error)
99
102 '''
103 This class stores the informations about a ROS master and shows it on request.
104 '''
105
106 updateHostRequest = Signal(str)
107 host_description_updated = Signal(str, str, str)
108 '''@ivar: the signal is emitted on description changes and contains the
109 ROS Master URI, host address and description a parameter.'''
110
111 capabilities_update_signal = Signal(str, str, str, list)
112 '''@ivar: the signal is emitted if a description with capabilities is received
113 and has the ROS master URI, host address, the name of the default_cfg node and a list with
114 descriptions (U{multimaster_msgs_fkie.srv.ListDescription<http://docs.ros.org/api/multimaster_msgs_fkie/html/srv/ListDescription.html>} Response) as parameter.'''
115 remove_config_signal = Signal(str)
116 '''@ivar: the signal is emitted if a default_cfg was removed'''
117
118 description_signal = Signal(str, str, bool)
119 '''@ivar: the signal is emitted to show a description (title, description)'''
120
121 request_xml_editor = Signal(list, str)
122 '''@ivar: the signal to open a xml editor dialog (list with files, search text)'''
123
124 stop_nodes_signal = Signal(str, list)
125 '''@ivar: the signal is emitted to stop on masteruri the nodes described in the list.'''
126
127 robot_icon_updated = Signal(str, str)
128 '''@ivar: the signal is emitted, if the robot icon was changed by a configuration (masteruri, path)'''
129
130 loaded_config = Signal(str, object)
131 '''@ivar: the signal is emitted, after a launchfile is successful loaded (launchfile, LaunchConfig)'''
132
133 save_profile_signal = Signal(str)
134 '''@ivar: the signal is emitted, to save profile. (masteruri) If masteruri is empty, save all masters else only for this master.'''
135
136 DIAGNOSTIC_LEVELS = {0: 'OK',
137 1: 'WARN',
138 2: 'ERROR',
139 3: 'STALE',
140 4: 'UNKNOWN',
141 5: 'UNKNOWN'}
142
143 - def __init__(self, masteruri, parent=None):
144 '''
145 Creates a new master.
146 @param masteruri: the URI of the ROS master
147 @type masteruri: C{str}
148 '''
149 QWidget.__init__(self, parent)
150 self.setObjectName(' - '.join(['MasterViewProxy', masteruri]))
151 self.masteruri = masteruri
152 self.mastername = masteruri
153 self.main_window = parent
154 try:
155 self.mastername = get_hostname(self.masteruri)
156 except:
157 pass
158
159 self._tmpObjects = []
160 self.__master_state = None
161 self.__master_info = None
162 self.__force_update = False
163 self.__configs = dict()
164 self.__online = False
165 self.__run_id = ''
166
167 self.__in_question = []
168
169 '''@ivar: stored the question dialogs for changed files '''
170 self._stop_ignores = ['rosout', rospy.get_name(), 'node_manager', 'master_discovery', 'master_sync', 'default_cfg', 'zeroconf']
171 ''' @todo: detect the names of master_discovery and master_sync ndoes'''
172
173 self.__echo_topics_dialogs = dict()
174 '''@ivar: stores the open EchoDialogs '''
175 self.__last_info_text = None
176 self.__use_sim_time = False
177 self.__current_user = nm.settings().host_user(self.mastername)
178 self.__robot_icons = []
179 self.__current_robot_icon = None
180 self.__current_parameter_robot_icon = ''
181 self.__republish_params = {}
182 self.__last_selection = 0
183 self._on_stop_kill_roscore = False
184 self._on_stop_poweroff = False
185 self._start_nodes_after_load_cfg = dict()
186
187 self.__running_nodes = dict()
188 self._nodelets = dict()
189 self.default_cfg_handler = DefaultConfigHandler()
190 self.default_cfg_handler.node_list_signal.connect(self.on_default_cfg_nodes_retrieved)
191 self.default_cfg_handler.description_signal.connect(self.on_default_cfg_descr_retrieved)
192 self.default_cfg_handler.err_signal.connect(self.on_default_cfg_err)
193
194 self.__launch_servers = {}
195 self.launch_server_handler = LaunchServerHandler()
196 self.launch_server_handler.launch_server_signal.connect(self.on_launch_server_retrieved)
197 self.launch_server_handler.error_signal.connect(self.on_launch_server_err)
198
199 self.masterTab = QWidget()
200 ui_file = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'MasterTab.ui')
201 loadUi(ui_file, self.masterTab)
202 tabLayout = QVBoxLayout(self)
203 tabLayout.setContentsMargins(0, 0, 0, 0)
204 tabLayout.addWidget(self.masterTab)
205 self._progress_queue_prio = ProgressQueue(self.masterTab.progressPrioFrame, self.masterTab.progressPrioBar, self.masterTab.progressCancelPrioButton, 'Prio Master - %s' % self.mastername)
206 self._progress_queue = ProgressQueue(self.masterTab.progressFrame, self.masterTab.progressBar, self.masterTab.progressCancelButton, 'Master - %s' % self.mastername)
207 self._progress_queue_prio.no_screen_error_signal.connect(self._on_no_screen_error)
208 self._progress_queue.no_screen_error_signal.connect(self._on_no_screen_error)
209
210
211 self.node_tree_model = NodeTreeModel(nm.nameres().address(self.masteruri), self.masteruri)
212 self.node_proxy_model = NodesSortFilterProxyModel(self)
213 self.node_proxy_model.setSourceModel(self.node_tree_model)
214 self.masterTab.nodeTreeView.setModel(self.node_proxy_model)
215 self.node_tree_model.hostInserted.connect(self.on_host_inserted)
216 for i, (_, width) in enumerate(NodeTreeModel.header):
217 self.masterTab.nodeTreeView.setColumnWidth(i, width)
218 self.nodeNameDelegate = HTMLDelegate(check_for_ros_names=False, is_node=True)
219 self.masterTab.nodeTreeView.setItemDelegateForColumn(0, self.nodeNameDelegate)
220 self.node_delegate = IconsDelegate()
221 self.masterTab.nodeTreeView.setItemDelegateForColumn(1, self.node_delegate)
222 self.masterTab.nodeTreeView.collapsed.connect(self.on_node_collapsed)
223 self.masterTab.nodeTreeView.expanded.connect(self.on_node_expanded)
224 sm = self.masterTab.nodeTreeView.selectionModel()
225 sm.selectionChanged.connect(self.on_node_selection_changed)
226 self.masterTab.nodeTreeView.activated.connect(self.on_node_activated)
227 self.masterTab.nodeTreeView.clicked.connect(self.on_node_clicked)
228
229
230
231
232 self.topic_model = TopicModel()
233 self.topic_proxyModel = TopicsSortFilterProxyModel(self)
234 self.topic_proxyModel.setSourceModel(self.topic_model)
235 self.masterTab.topicsView.setModel(self.topic_proxyModel)
236
237 self.masterTab.topicsView.expandAll()
238 self.masterTab.topicsView.sortByColumn(0, Qt.AscendingOrder)
239 for i, (_, width) in enumerate(TopicModel.header):
240 self.masterTab.topicsView.setColumnWidth(i, width)
241 self.topicNameDelegate = HTMLDelegate(check_for_ros_names=False, is_node=True)
242 self.topicTypeDelegate = HTMLDelegate()
243 self.masterTab.topicsView.setItemDelegateForColumn(0, self.topicNameDelegate)
244 self.masterTab.topicsView.setItemDelegateForColumn(3, self.topicTypeDelegate)
245 sm = self.masterTab.topicsView.selectionModel()
246 sm.selectionChanged.connect(self.on_topic_selection_changed)
247 self.masterTab.topicsView.activated.connect(self.on_topic_activated)
248 self.masterTab.topicsView.clicked.connect(self.on_topic_clicked)
249 self.masterTab.topicsView.setSortingEnabled(True)
250
251
252 self.service_model = ServiceModel()
253 self.service_proxyModel = ServicesSortFilterProxyModel(self)
254 self.service_proxyModel.setSourceModel(self.service_model)
255 self.masterTab.servicesView.setModel(self.service_proxyModel)
256 self.masterTab.servicesView.expandAll()
257 self.masterTab.servicesView.sortByColumn(0, Qt.AscendingOrder)
258 for i, (_, width) in enumerate(ServiceModel.header):
259 self.masterTab.servicesView.setColumnWidth(i, width)
260 self.serviceNameDelegate = HTMLDelegate(check_for_ros_names=False, is_node=True)
261 self.serviceTypeDelegate = HTMLDelegate()
262 self.masterTab.servicesView.setItemDelegateForColumn(0, self.serviceNameDelegate)
263 self.masterTab.servicesView.setItemDelegateForColumn(1, self.serviceTypeDelegate)
264 sm = self.masterTab.servicesView.selectionModel()
265 sm.selectionChanged.connect(self.on_service_selection_changed)
266 self.masterTab.servicesView.activated.connect(self.on_service_activated)
267 self.masterTab.servicesView.clicked.connect(self.on_service_clicked)
268 self.masterTab.servicesView.setSortingEnabled(True)
269
270
271 self.parameter_model = ParameterModel()
272 self.parameter_model.itemChanged.connect(self._on_parameter_item_changed)
273 self.parameter_proxyModel = ParameterSortFilterProxyModel(self)
274 self.parameter_proxyModel.setSourceModel(self.parameter_model)
275 self.masterTab.parameterView.setModel(self.parameter_proxyModel)
276 for i, (_, width) in enumerate(ParameterModel.header):
277 self.masterTab.parameterView.setColumnWidth(i, width)
278 self.parameterNameDelegate = HTMLDelegate()
279 self.masterTab.parameterView.setItemDelegateForColumn(0, self.parameterNameDelegate)
280 sm = self.masterTab.parameterView.selectionModel()
281 sm.selectionChanged.connect(self.on_parameter_selection_changed)
282 self.masterTab.parameterView.setSortingEnabled(True)
283
284
285
286
287
288 self.masterTab.startButton.clicked.connect(self.on_start_clicked)
289 self.masterTab.stopButton.clicked.connect(self.on_stop_clicked)
290
291 self.masterTab.ioButton.clicked.connect(self.on_io_clicked)
292 self.masterTab.logButton.clicked.connect(self.on_log_clicked)
293 self.masterTab.logDeleteButton.clicked.connect(self.on_log_delete_clicked)
294 self.masterTab.dynamicConfigButton.clicked.connect(self.on_dynamic_config_clicked)
295 self.masterTab.editConfigButton.clicked.connect(self.on_edit_config_clicked)
296 self.masterTab.editRosParamButton.clicked.connect(self.on_edit_rosparam_clicked)
297 self.masterTab.saveButton.clicked.connect(self.on_save_clicked)
298 self.masterTab.closeCfgButton.clicked.connect(self.on_close_clicked)
299
300 self.masterTab.echoTopicButton.clicked.connect(self.on_topic_echo_clicked)
301 self.masterTab.hzTopicButton.clicked.connect(self.on_topic_hz_clicked)
302 self.masterTab.hzSshTopicButton.clicked.connect(self.on_topic_hz_ssh_clicked)
303 self.masterTab.pubTopicButton.clicked.connect(self.on_topic_pub_clicked)
304 self.masterTab.pubStopTopicButton.clicked.connect(self.on_topic_pub_stop_clicked)
305
306 self.masterTab.callServiceButton.clicked.connect(self.on_service_call_clicked)
307 self.masterTab.nodeFilterInput.textChanged.connect(self.on_node_filter_changed)
308 self.masterTab.topicFilterInput.textChanged.connect(self.on_topic_filter_changed)
309 self.masterTab.serviceFilterInput.textChanged.connect(self.on_service_filter_changed)
310 self.masterTab.parameterFilterInput.textChanged.connect(self.on_parameter_filter_changed)
311 self.masterTab.getParameterButton.clicked.connect(self.on_get_parameter_clicked)
312 self.masterTab.addParameterButton.clicked.connect(self.on_add_parameter_clicked)
313 self.masterTab.deleteParameterButton.clicked.connect(self.on_delete_parameter_clicked)
314 self.masterTab.saveParameterButton.clicked.connect(self.on_save_parameter_clicked)
315
316
317 self.parameterHandler = ParameterHandler()
318 self.parameterHandler.parameter_list_signal.connect(self._on_param_list)
319 self.parameterHandler.parameter_values_signal.connect(self._on_param_values)
320 self.parameterHandler.delivery_result_signal.connect(self._on_delivered_values)
321
322 self.parameterHandler_sim = ParameterHandler()
323
324 self.parameterHandler_sim.parameter_values_signal.connect(self._on_sim_param_values)
325
326
327 self._shortcut_kill_node = QShortcut(QKeySequence(self.tr("Ctrl+Backspace", "Kill selected node")), self)
328 self._shortcut_kill_node.activated.connect(self.on_kill_nodes)
329 self._shortcut_kill_node = QShortcut(QKeySequence(self.tr("Ctrl+Delete", "Removes the registration of selected nodes from ROS master")), self)
330 self._shortcut_kill_node.activated.connect(self.on_unregister_nodes)
331
332 self.masterTab.ioButton.setEnabled(True)
333 self.masterTab.tabWidget.currentChanged.connect(self.on_tab_current_changed)
334 self._shortcut_screen_show_all = QShortcut(QKeySequence(self.tr("Shift+S", "Show all available screens")), self)
335 self._shortcut_screen_show_all.activated.connect(self.on_show_all_screens)
336 self._shortcut_screen_kill = QShortcut(QKeySequence(self.tr("Shift+Backspace", "Kill Screen")), self)
337 self._shortcut_screen_kill.activated.connect(self.on_kill_screens)
338
339 self.loaded_config.connect(self._apply_launch_config)
340
341
342 self._shortcut1 = QShortcut(QKeySequence(self.tr("Alt+1", "Select first group")), self)
343 self._shortcut1.activated.connect(self.on_shortcut1_activated)
344 self._shortcut2 = QShortcut(QKeySequence(self.tr("Alt+2", "Select second group")), self)
345 self._shortcut2.activated.connect(self.on_shortcut2_activated)
346 self._shortcut3 = QShortcut(QKeySequence(self.tr("Alt+3", "Select third group")), self)
347 self._shortcut3.activated.connect(self.on_shortcut3_activated)
348 self._shortcut4 = QShortcut(QKeySequence(self.tr("Alt+4", "Select fourth group")), self)
349 self._shortcut4.activated.connect(self.on_shortcut4_activated)
350 self._shortcut5 = QShortcut(QKeySequence(self.tr("Alt+5", "Select fifth group")), self)
351 self._shortcut5.activated.connect(self.on_shortcut5_activated)
352
353 self._shortcut_collapse_all = QShortcut(QKeySequence(self.tr("Alt+C", "Collapse all groups")), self)
354 self._shortcut_collapse_all.activated.connect(self.on_shortcut_collapse_all)
355 self._shortcut_expand_all = QShortcut(QKeySequence(self.tr("Alt+E", "Expand all groups")), self)
356 self._shortcut_expand_all.activated.connect(self.masterTab.nodeTreeView.expandAll)
357 self._shortcut_run = QShortcut(QKeySequence(self.tr("Alt+R", "run selected nodes")), self)
358 self._shortcut_run.activated.connect(self.on_start_clicked)
359 self._shortcut_stop = QShortcut(QKeySequence(self.tr("Alt+S", "stop selected nodes")), self)
360 self._shortcut_stop.activated.connect(self.on_stop_clicked)
361
362 self.message_frame = MessageFrame()
363 self.masterTab.questionFrameLayout.addWidget(self.message_frame.frameui)
364 self.message_frame.accept_signal.connect(self._on_question_ok)
365 self.message_frame.cancel_signal.connect(self._on_question_cancel)
366
367 self.info_frame = MessageFrame(info=True)
368 self.masterTab.infoFrameLayout.addWidget(self.info_frame.frameui)
369 self.info_frame.accept_signal.connect(self._on_info_ok)
370
371
372
373
374
375
376
377
378
379
380
395
396 @property
398 return self.__current_user
399
400 @current_user.setter
404
405 @property
408
409 @property
411 '''
412 The online meens that master is discovered and master_info was received.
413 '''
414 return self.__online
415
416 @online.setter
418 self.__online = state
419 self._start_queue(self._progress_queue)
420 self._start_queue(self._progress_queue_prio)
421
422 @property
424 return self.__master_state
425
426 @master_state.setter
429
430 @property
432 return self.__master_info
433
434 @master_info.setter
436 '''
437 Sets the new master information. To determine whether a node is running the
438 PID and his URI are needed. The PID of remote nodes (host of the ROS master
439 and the node are different) will be not determine by discovering. Thus this
440 information must be obtain from other MasterInfo object and stored while
441 updating.
442 @param master_info: the mater information object
443 @type master_info: U{master_discovery_fkie.msg.MasterInfo<http://docs.ros.org/kinetic/api/master_discovery_fkie/html/modules.html#module-master_discovery_fkie.master_info>}
444 '''
445 try:
446 update_result = (set(), set(), set(), set(), set(), set(), set(), set(), set())
447 if self.__master_info is None:
448 if (master_info.masteruri == self.masteruri):
449 self.__master_info = master_info
450 update_result[0].update(self.__master_info.node_names)
451 update_result[3].update(self.__master_info.topic_names)
452 update_result[6].update(self.__master_info.service_names)
453 else:
454 update_result = self.__master_info.updateInfo(master_info)
455
456
457 if self.__master_info is None:
458 return
459 try:
460 if (master_info.masteruri == self.masteruri):
461 self.update_system_parameter()
462 self.online = True
463
464 hosts2update = set([get_hostname(self.__master_info.getNode(nodename).uri) for nodename in update_result[0]])
465 hosts2update.update([get_hostname(self.__master_info.getService(nodename).uri) for nodename in update_result[6]])
466 for host in hosts2update:
467 if host != get_hostname(self.masteruri):
468 self.updateHostRequest.emit(host)
469 except:
470 pass
471
472
473
474 if update_result[0] or update_result[1] or update_result[2] or self.__force_update:
475 self.updateRunningNodesInModel(self.__master_info)
476 self.on_node_selection_changed(None, None)
477
478 if update_result[3] or update_result[4] or update_result[5] or self.__force_update:
479 self.topic_model.updateModelData(self.__master_info.topics, update_result[3], update_result[4], update_result[5])
480 self.on_topic_selection_changed(None, None)
481
482 if update_result[6] or update_result[7] or update_result[8] or self.__force_update:
483 self.service_model.updateModelData(self.__master_info.services, update_result[6], update_result[7], update_result[8])
484 self.on_service_selection_changed(None, None)
485
486 self.updateDefaultConfigs(self.__master_info)
487 self.__force_update = False
488
489
490
491 except:
492 print traceback.format_exc(1)
493
497
498 @property
500 return self.__use_sim_time
501
503 return self._progress_queue.count() > 0 or self._progress_queue_prio.count() > 0
504
506 self.__force_update = True
507
510
512 '''
513 Marks all nodes, which are not running and in a given list as a duplicates nodes.
514 @param running_nodes: The list with names of running nodes
515 @type running_nodes: C{[str]}
516 '''
517
518 self.__running_nodes = running_nodes
519 self.node_tree_model.markNodesAsDuplicateOf(running_nodes, (self.master_info is not None and self.master_info.getNodeEndsWith('master_sync')))
520
522 '''
523 Returns the list with all running nodes, which are registered by this ROS
524 master. Also the nodes, which are physically running on remote hosts.
525 @return: The list with names of running nodes
526 @rtype: C{[str]}
527 '''
528 if self.master_info is not None and self.master_info.getNodeEndsWith('master_sync'):
529 return self.master_info.node_names
530 return []
531
533 '''
534 Returns the list with all running nodes, which are running (has process) on this host.
535 The nodes registered on this ROS master, but running on remote hosts are not
536 returned.
537 @return: The dictionary with names of running nodes and their masteruri
538 @rtype: C{dict(str:str)}
539 '''
540 result = dict()
541 if self.master_info is not None:
542 for _, node in self.master_info.nodes.items():
543 if node.isLocal:
544 if not remove_system_nodes or not self._is_in_ignore_list(node.name):
545 result[node.name] = self.master_info.masteruri
546 return result
547
549 '''
550 Creates the dictionary with ExtendedNodeInfo objects and updates the nodes view.
551 @param master_info: the mater information object
552 @type master_info: U{master_discovery_fkie.msg.MasterInfo<http://docs.ros.org/kinetic/api/master_discovery_fkie/html/modules.html#module-master_discovery_fkie.master_info>}
553 '''
554 if master_info is not None:
555 self.node_tree_model.updateModelData(master_info.nodes)
556 self.updateButtons()
557
559 '''
560 @param node_name: The name of the node.
561 @type node_name: str
562 @return: The list the nodes with given name.
563 @rtype: []
564 '''
565 return self.node_tree_model.getNode("%s" % node_name, self.masteruri)
566
608
609
611 '''
612 @param path: the launch file
613 @type path: C{str}
614 @return: C{True} if the given launch file is open
615 @rtype: C{boolean}
616 '''
617 return path in self.launchfiles
618
619 @property
621 '''
622 Returns the copy of the dictionary with default configurations on this host
623 @rtype: C{[str(ROS node name)]}
624 '''
625 result = []
626 for (c, _) in self.__configs.items():
627 if isinstance(c, tuple):
628 result.append(c[0])
629 return result
630
631 @property
633 '''
634 Returns the copy of the dictionary with loaded launch files on this host
635 @rtype: C{dict(str(file) : L{LaunchConfig}, ...)}
636 '''
637 result = dict()
638 for (c, cfg) in self.__configs.items():
639 if not isinstance(c, tuple):
640 result[c] = cfg
641 return result
642
643 @launchfiles.setter
645 '''
646 Loads the launch file. If this file is already loaded, it will be reloaded.
647 After successful load the node view will be updated.
648 @param launchfile: the launch file path
649 @type launchfile: C{str}
650 '''
651 lfile = launchfile
652 argv = []
653 if isinstance(launchfile, tuple):
654 lfile, argv = launchfile
655 self._progress_queue_prio.add2queue(utf8(uuid.uuid4()),
656 'Loading %s' % os.path.basename(lfile),
657 self._load_launchfile,
658 (lfile, argv))
659 self._start_queue(self._progress_queue_prio)
660
662 '''
663 This method will be called in another thread. The configuration parameter
664 of the launch file will be requested using `LaunchArgsSelectionRequest` and
665 `InteractionNeededError`. After the file is successful loaded a
666 `loaded_config` signal will be emitted.
667 '''
668 if argv_forced:
669 rospy.loginfo("LOAD launch: %s with args: %s" % (launchfile, argv_forced))
670 else:
671 rospy.loginfo("LOAD launch: %s" % launchfile)
672 stored_argv = None
673 if launchfile in self.__configs:
674
675 stored_argv = self.__configs[launchfile].argv
676
677 try:
678
679 launchConfig = LaunchConfig(launchfile, masteruri=self.masteruri)
680 loaded = False
681
682 if stored_argv is None:
683 if argv_forced:
684
685 loaded = launchConfig.load(argv_forced)
686 else:
687
688 req_args = launchConfig.getArgs()
689 if req_args:
690 params = dict()
691 arg_dict = launchConfig.argvToDict(req_args)
692 for arg in arg_dict.keys():
693 params[arg] = ('string', [arg_dict[arg]])
694
695 req = LaunchArgsSelectionRequest(launchfile, params, 'Needs input for args')
696 raise nm.InteractionNeededError(req, self._load_launchfile, (launchfile,))
697
698 if not loaded or stored_argv is not None:
699 launchConfig.load(req_args if stored_argv is None else stored_argv)
700
701 for _, machine in launchConfig.Roscfg.machines.items():
702 if machine.name:
703 nm.nameres().add_info(machine.name, machine.address)
704
705 if self._progress_queue_prio.has_id(pqid):
706 self.loaded_config.emit(launchfile, launchConfig)
707 except InteractionNeededError:
708 raise
709 except Exception as e:
710 err_text = ''.join([os.path.basename(launchfile), ' loading failed!'])
711 err_details = ''.join([err_text, '\n\n', e.__class__.__name__, ": ", utf8(e)])
712 rospy.logwarn("Loading launch file: %s", err_details)
713 raise DetailedError("Loading launch file", err_text, err_details)
714
715 except:
716 print traceback.format_exc(3)
717
719 stored_roscfg = None
720 expanded_items = None
721 if launchfile in self.__configs:
722
723 expanded_items = self._get_expanded_groups()
724
725 self.removeConfigFromModel(launchfile)
726 stored_roscfg = self.__configs[launchfile].Roscfg
727 del self.__configs[launchfile]
728 try:
729
730 self.__configs[launchfile] = launchConfig
731 self.appendConfigToModel(launchfile, launchConfig.Roscfg)
732
733
734 try:
735 robot_descr = launchConfig.getRobotDescr()
736 capabilities = launchConfig.getCapabilitiesDesrc()
737 for (host, caps) in capabilities.items():
738 if not host:
739 host = nm.nameres().mastername(self.masteruri)
740 host_addr = nm.nameres().address(host)
741 self.node_tree_model.addCapabilities(self.masteruri, host_addr, launchfile, caps)
742 for (host, descr) in robot_descr.items():
743 if not host:
744 host = nm.nameres().mastername(self.masteruri)
745 host_addr = nm.nameres().address(host)
746 tooltip = self.node_tree_model.updateHostDescription(self.masteruri, host_addr, descr['type'], descr['name'], descr['description'])
747 self.host_description_updated.emit(self.masteruri, host_addr, tooltip)
748 except:
749 import traceback
750 print traceback.format_exc()
751
752
753 self.updateRunningNodesInModel(self.__master_info)
754
755 if stored_roscfg and self.__configs[launchfile].Roscfg:
756 stored_values = [(name, utf8(p.value)) for name, p in stored_roscfg.params.items()]
757 new_values = [(name, utf8(p.value)) for name, p in self.__configs[launchfile].Roscfg.params.items()]
758
759 paramset = set(name for name, _ in (set(new_values) - set(stored_values)))
760
761 paramset |= (set(self.__configs[launchfile].Roscfg.params.keys()) - set(stored_roscfg.params.keys()))
762
763 paramset |= (set(stored_roscfg.params.keys()) - set(self.__configs[launchfile].Roscfg.params.keys()))
764
765 stored_nodes = [roslib.names.ns_join(item.namespace, item.name) for item in stored_roscfg.nodes]
766 new_nodes = [roslib.names.ns_join(item.namespace, item.name) for item in self.__configs[launchfile].Roscfg.nodes]
767 nodes2start = set(new_nodes) - set(stored_nodes)
768
769 for p in paramset:
770 for n in new_nodes:
771 if p.startswith(n):
772 nodes2start.add(n)
773
774 for n in stored_roscfg.nodes:
775 for new_n in self.__configs[launchfile].Roscfg.nodes:
776 if n.name == new_n.name and n.namespace == new_n.namespace:
777 if n.args != new_n.args or n.remap_args != new_n.remap_args:
778 nodes2start.add(roslib.names.ns_join(n.namespace, n.name))
779
780 nodes2start = [n for n in nodes2start if not re.search(r"\d{3,6}_\d{10,}", n)]
781
782 if nodes2start:
783 restart, ok = SelectDialog.getValue('Restart nodes?', "Select nodes to restart <b>@%s</b>:" % self.mastername, nodes2start, False, True, '', self)
784 if ok:
785 self.stop_nodes_by_name(restart)
786 self.start_nodes_by_name(restart, launchfile, True)
787
788 if launchfile in self.__robot_icons:
789 self.__robot_icons.remove(launchfile)
790 self.__robot_icons.insert(0, launchfile)
791 self.markNodesAsDuplicateOf(self.__running_nodes)
792
793 if expanded_items is not None:
794 self._expand_groups(expanded_items)
795
796 nodelets = {}
797 for n in launchConfig.Roscfg.nodes:
798 if n.package == 'nodelet' and n.type == 'nodelet':
799 args = n.args.split(' ')
800 if len(args) == 3 and args[0] == 'load':
801 nodelet_mngr = roslib.names.ns_join(n.namespace, args[2])
802 if nodelet_mngr not in nodelets:
803 nodelets[nodelet_mngr] = []
804 nodelets[nodelet_mngr].append(roslib.names.ns_join(n.namespace, n.name))
805 for mngr, nlist in nodelets.iteritems():
806 mngr_nodes = self.node_tree_model.getNode(mngr, self.masteruri)
807 for mn in mngr_nodes:
808 mn.nodelets = nlist
809 for nlet in nlist:
810 nlet_nodes = self.node_tree_model.getNode(nlet, self.masteruri)
811 for nn in nlet_nodes:
812 nn.nodelet_mngr = mngr
813 self._nodelets[launchfile] = nodelets
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835 if launchfile in self._start_nodes_after_load_cfg:
836 self.start_nodes_by_name(self._start_nodes_after_load_cfg[launchfile], launchfile, True)
837 del self._start_nodes_after_load_cfg[launchfile]
838 except Exception as e:
839 err_text = ''.join([os.path.basename(launchfile), ' loading failed!'])
840 err_details = ''.join([err_text, '\n\n', e.__class__.__name__, ": ", utf8(e)])
841 rospy.logwarn("Loading launch file: %s", err_details)
842 MessageBox.warning(self, "Loading launch file", err_text, err_details)
843 import traceback
844 print traceback.format_exc(3)
845 except:
846 print traceback.format_exc(3)
847 self.update_robot_icon(True)
848
850 try:
851 self.__configs[launchfile].global_param_done.remove(self.masteruri)
852
853 except Exception:
854 pass
855
859
864
866 if configname and configname in self._nodelets:
867 if nodename in self._nodelets[configname]:
868 return self._nodelets[configname][nodename]
869 else:
870 for configname, mngrs in self._nodelets.iteritems():
871 if nodename in mngrs:
872 return mngrs[nodename]
873 return []
874
876 if configname and configname in self._nodelets:
877 for mngr, nodelets in self._nodelets[configname].iteritems():
878 if nodename in nodelets:
879 return mngr
880 else:
881 for configname, mngrs in self._nodelets.iteritems():
882 for mngr, nodelets in mngrs.iteritems():
883 if nodename in nodelets:
884 return mngr
885 return None
886
888 '''
889 Returns a list of group names, which are expanded.
890 '''
891 result = []
892 try:
893 for r in range(self.masterTab.nodeTreeView.model().rowCount()):
894 index_host = self.masterTab.nodeTreeView.model().index(r, 0)
895 if index_host.isValid() and self.masterTab.nodeTreeView.isExpanded(index_host):
896 if self.masterTab.nodeTreeView.model().hasChildren(index_host):
897 for c in range(self.masterTab.nodeTreeView.model().rowCount(index_host)):
898 index_cap = self.masterTab.nodeTreeView.model().index(c, 0, index_host)
899 if index_cap.isValid() and self.masterTab.nodeTreeView.isExpanded(index_cap):
900 model_index = self.node_proxy_model.mapToSource(index_cap)
901 item = self.node_tree_model.itemFromIndex(model_index)
902 if isinstance(item, (GroupItem, HostItem)):
903 result.append(item.name)
904 except:
905 print traceback.format_exc(3)
906 return result
907
909 '''
910 Expands all groups, which are in the given list. If no list is given,
911 expands all groups of expanded hosts.
912 '''
913 try:
914 for r in range(self.masterTab.nodeTreeView.model().rowCount()):
915 index_host = self.masterTab.nodeTreeView.model().index(r, 0)
916 if index_host.isValid() and self.masterTab.nodeTreeView.isExpanded(index_host):
917 if self.masterTab.nodeTreeView.model().hasChildren(index_host):
918 for c in range(self.masterTab.nodeTreeView.model().rowCount(index_host)):
919 index_cap = self.masterTab.nodeTreeView.model().index(c, 0, index_host)
920 if index_cap.isValid():
921 model_index = self.node_proxy_model.mapToSource(index_cap)
922 item = self.node_tree_model.itemFromIndex(model_index)
923 if isinstance(item, (GroupItem, HostItem)):
924 if groups is None or item.name in groups:
925 self.masterTab.nodeTreeView.setExpanded(index_cap, True)
926 except:
927 print traceback.format_exc(3)
928
930 '''
931 Update the current robot icon. If the icon was changed a `robot_icon_updated`
932 signal will be emitted.
933 :return: the path to the current robot icon
934 :rtype: str
935 '''
936 for l in self.__robot_icons:
937 try:
938 icon = self.__configs[l].get_robot_icon()
939 if icon:
940 if icon != self.__current_robot_icon or force:
941 self.__current_robot_icon = icon
942 self.robot_icon_updated.emit(self.masteruri, icon)
943 return icon
944 except:
945 pass
946 self.__current_robot_icon = self.__current_parameter_robot_icon
947 self.robot_icon_updated.emit(self.masteruri, utf8(self.__current_robot_icon))
948 return self.__current_robot_icon
949
951 '''
952 Update the node view
953 @param launchfile: the launch file path
954 @type launchfile: C{str}
955 @param rosconfig: the configuration
956 @type rosconfig: L{LaunchConfig}
957 '''
958 hosts = dict()
959 addr = nm.nameres().address(self.masteruri)
960 masteruri = self.masteruri
961 for n in rosconfig.nodes:
962 if n.machine_name and not n.machine_name == 'localhost':
963 if n.machine_name not in rosconfig.machines:
964 raise Exception(''.join(["ERROR: unknown machine [", n.machine_name, "]"]))
965 addr = rosconfig.machines[n.machine_name].address
966 masteruri = nm.nameres().masteruri(n.machine_name)
967 node = roslib.names.ns_join(n.namespace, n.name)
968 if (masteruri, addr) not in hosts:
969 hosts[(masteruri, addr)] = dict()
970 hosts[(masteruri, addr)][node] = launchfile
971
972 for ((masteruri, addr), nodes) in hosts.items():
973 self.node_tree_model.appendConfigNodes(masteruri, addr, nodes)
974 self.updateButtons()
975
986
988 '''
989 Updates the default configuration view based on the current master information.
990 @param master_info: the mater information object
991 @type master_info: U{master_discovery_fkie.msg.MasterInfo<http://docs.ros.org/kinetic/api/master_discovery_fkie/html/modules.html#module-master_discovery_fkie.master_info>}
992 '''
993 if self.__master_info is None:
994 return
995 default_cfgs = []
996 for name in self.__master_info.service_names:
997 if name.endswith('list_nodes'):
998 srv = self.__master_info.getService(name)
999 default_cfgs.append((roslib.names.namespace(name).rstrip(roslib.names.SEP), srv.uri, srv.masteruri))
1000
1001 removed = list(set([c for c in self.__configs.keys() if isinstance(c, tuple)]) - set(default_cfgs))
1002 if removed:
1003 for r in removed:
1004 self.node_tree_model.removeConfigNodes(r)
1005
1006 if r[2] == self.masteruri:
1007 self.remove_config_signal.emit(r[0])
1008 del self.__configs[r]
1009 if len(self.__configs) == 0:
1010 address = nm.nameres().address(master_info.masteruri)
1011 tooltip = self.node_tree_model.updateHostDescription(master_info.masteruri, address, '', '', '')
1012 self.host_description_updated.emit(master_info.masteruri, address, tooltip)
1013
1014 added = list(set(default_cfgs) - set(self.__configs.keys()))
1015 for (name, uri, _) in added:
1016 self.default_cfg_handler.requestNodeList(uri, roslib.names.ns_join(name, 'list_nodes'))
1017
1018 descr_service = self.__master_info.getService(roslib.names.ns_join(name, 'description'))
1019 if descr_service is not None:
1020 self.default_cfg_handler.requestDescriptionList(descr_service.uri, descr_service.name)
1021 self.updateButtons()
1022
1024 '''
1025 Handles the new list with nodes from default configuration service.
1026 @param service_uri: the URI of the service provided the default configuration
1027 @type service_uri: C{str}
1028 @param config_name: the name of default configuration service
1029 @type config_name: C{str}
1030 @param nodes: the name of the nodes with name spaces
1031 @type nodes: C{[str]}
1032 '''
1033
1034 masteruri = self.masteruri
1035 if self.__master_info is not None:
1036 service = self.__master_info.getService(config_name)
1037 if service is not None:
1038 masteruri = service.masteruri
1039 cfg_name = roslib.names.namespace(config_name).rstrip(roslib.names.SEP)
1040 key = (cfg_name, service_uri, masteruri)
1041
1042
1043
1044 node_cfgs = dict()
1045 for n in nodes:
1046 node_cfgs[n] = key
1047 host = get_hostname(service_uri)
1048 host_addr = nm.nameres().address(host)
1049 if host_addr is None:
1050 host_addr = host
1051 self.node_tree_model.appendConfigNodes(masteruri, host_addr, node_cfgs)
1052 self.__configs[key] = nodes
1053
1054 if cfg_name in self._start_nodes_after_load_cfg:
1055 self.start_nodes_by_name(self._start_nodes_after_load_cfg[cfg_name], roslib.names.ns_join(cfg_name, 'run'), True)
1056 del self._start_nodes_after_load_cfg[cfg_name]
1057 self.updateButtons()
1058
1060 '''
1061 Handles the description list from default configuration service.
1062 Emits a Qt signal L{host_description_updated} to notify about a new host
1063 description and a Qt signal L{capabilities_update_signal} to notify about a capabilities
1064 update.
1065 @param service_uri: the URI of the service provided the default configuration
1066 @type service_uri: C{str}
1067 @param config_name: the name of default configuration service
1068 @type config_name: C{str}
1069 @param items: list with descriptions
1070 @type items: C{[U{multimaster_msgs_fkie.srv.ListDescription<http://docs.ros.org/api/multimaster_msgs_fkie/html/srv/ListDescription.html>} Response]}
1071 '''
1072 if items:
1073 masteruri = self.masteruri
1074 if self.__master_info is not None:
1075 service = self.__master_info.getService(config_name)
1076 if service is not None:
1077 masteruri = service.masteruri
1078 key = (roslib.names.namespace(config_name).rstrip(roslib.names.SEP), service_uri, masteruri)
1079 host = get_hostname(service_uri)
1080 host_addr = nm.nameres().address(host)
1081
1082 caps = dict()
1083 for c in items[0].capabilities:
1084 if c.namespace not in caps:
1085 caps[c.namespace] = dict()
1086 caps[c.namespace][c.name.decode(sys.getfilesystemencoding())] = {'type': c.type, 'images': [resolve_paths(i) for i in c.images], 'description': resolve_paths(c.description.replace("\\n ", "\n").decode(sys.getfilesystemencoding())), 'nodes': list(c.nodes)}
1087 if host_addr is None:
1088 host_addr = get_hostname(key[1])
1089 self.node_tree_model.addCapabilities(masteruri, host_addr, key, caps)
1090
1091 tooltip = self.node_tree_model.updateHostDescription(masteruri, host_addr, items[0].robot_type, items[0].robot_name.decode(sys.getfilesystemencoding()), resolve_paths(items[0].robot_descr.decode(sys.getfilesystemencoding())))
1092 self.host_description_updated.emit(masteruri, host_addr, tooltip)
1093 self.capabilities_update_signal.emit(masteruri, host_addr, roslib.names.namespace(config_name).rstrip(roslib.names.SEP), items)
1094
1096 '''
1097 Handles the error messages from default configuration service.
1098 @param service_uri: the URI of the service provided the default configuration
1099 @type service_uri: C{str}
1100 @param service: the name of default configuration service
1101 @type service: C{str}
1102 @param msg: the error message
1103 @type msg: C{str}
1104 '''
1105 pass
1106
1107
1108
1109
1110 @property
1112 return self.__launch_servers
1113
1115 '''
1116 Returns `True` if the there are roslaunch server, which have no `master` as
1117 node or or have other nodes as `rosout-#` inside.
1118 '''
1119 for _, (_, nodes) in self.__launch_servers.items():
1120 if not self._is_master_launch_server(nodes):
1121 return True
1122 return False
1123
1125 if 'master' in nodes and len(nodes) < 3:
1126 return True
1127 return False
1128
1130 '''
1131 Handles the info about roslaunch server.
1132 Emits a Qt signal L{host_description_updated} to notify about a new host
1133 description and a Qt signal L{capabilities_update_signal} to notify about a capabilities
1134 update.
1135 @param serveruri: the URI of the roslaunch server
1136 @type serveruri: C{str}
1137 @param pid: the process id of the roslaunch server
1138 @type pid: C{str}
1139 @param nodes: list with nodes handled by the roslaunch server
1140 @type nodes: C{[L{str}]}
1141 '''
1142 self.__launch_servers[serveruri] = (pid, nodes)
1143
1145 '''
1146 Handles the error messages from launch server hanlder.
1147 @param serveruri: the URI of the launch server
1148 @type serveruri: C{str}
1149 @param msg: the error message
1150 @type msg: C{str}
1151 '''
1152 try:
1153 del self.__launch_servers[serveruri]
1154 except:
1155 pass
1156
1158 '''
1159 Kill all running launch server. The coresponding URIS are removed by master_monitor.
1160 '''
1161 for lsuri, (pid, nodes) in self.__launch_servers.items():
1162 try:
1163 if not self._is_master_launch_server(nodes):
1164 self._progress_queue.add2queue(utf8(uuid.uuid4()),
1165 ''.join(['kill roslaunch ', lsuri, '(', utf8(pid), ')']),
1166 nm.starter().kill,
1167 (get_hostname(lsuri), pid, False, self.current_user))
1168 self.launch_server_handler.updateLaunchServerInfo(lsuri, delayed_exec=3.0)
1169 except Exception as e:
1170 rospy.logwarn("Error while kill roslaunch %s: %s", utf8(lsuri), utf8(e))
1171 raise DetailedError("Kill error",
1172 ''.join(['Error while kill roslaunch ', lsuri]),
1173 utf8(e))
1174 self._start_queue(self._progress_queue)
1175
1176
1177
1178
1179
1181 '''
1182 Depending of the state of the node, it will be run or the screen output will
1183 be open.
1184 @param index: The index of the activated node
1185 @type index: U{QtCore.QModelIndex<https://srinikom.github.io/pyside-docs/PySide/QtCore/QModelIndex.html>}
1186 '''
1187 selectedNodes = []
1188 if index.column() == 0:
1189 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes(), False)
1190 if not selectedNodes:
1191 return
1192 has_running = False
1193 has_stopped = False
1194 has_invalid = False
1195 for node in selectedNodes:
1196 if node.uri is not None:
1197 has_running = True
1198 if node.pid is None:
1199 has_invalid = True
1200 else:
1201 has_stopped = True
1202 if has_stopped:
1203 self.on_start_clicked()
1204 elif has_running and not has_invalid:
1205 self.on_io_clicked()
1206 else:
1207 self.on_log_clicked()
1208
1214
1216 '''
1217 @param index: The index of the activated topic
1218 @type index: U{QtCore.QModelIndex<https://srinikom.github.io/pyside-docs/PySide/QtCore/QModelIndex.html>}
1219 '''
1220 model_index = self.topic_proxyModel.mapToSource(index)
1221 item = self.topic_model.itemFromIndex(model_index)
1222 if isinstance(item, TopicItem):
1223 self.on_topic_echo_clicked([item.topic])
1224
1228
1230 '''
1231 @param index: The index of the activated service
1232 @type index: U{QtCore.QModelIndex<https://srinikom.github.io/pyside-docs/PySide/QtCore/QModelIndex.html>}
1233 '''
1234 model_index = self.service_proxyModel.mapToSource(index)
1235 item = self.service_model.itemFromIndex(model_index)
1236 if isinstance(item, ServiceItem):
1237 self.on_service_call_clicked([item.service])
1238
1242
1249
1250
1252 if not index.parent().isValid():
1253 self.masterTab.nodeTreeView.selectionModel().clear()
1254
1257
1259 '''
1260 :param list_type: LAUNCH, TOPIC, NODE, SERVICE
1261 :type list_type: str
1262 '''
1263 result = ''
1264 if items:
1265 result = '%s<b><u>%s</u></b>' % (result, title)
1266 if len(items) > 1:
1267 result = '%s <span style="color:gray;">[%d]</span>' % (result, len(items))
1268 result = '%s<br><ul><span></span>' % result
1269 items.sort()
1270 for i in items:
1271 item = i
1272
1273 item_name = i
1274 if name:
1275 if item_name.startswith(name):
1276 item_name = item_name.replace('%s%s' % (name, roslib.names.SEP), '~', 1)
1277 ns = roslib.names.namespace(name)
1278 if item_name.startswith(ns) and ns != roslib.names.SEP:
1279 item_name = item_name.replace(ns, '', 1)
1280 if list_type in ['NODE']:
1281 item = '<a href="node://%s%s">%s</a>' % (self.mastername, i, item_name)
1282 elif list_type in ['TOPIC_PUB', 'TOPIC_SUB']:
1283
1284 count = None
1285 try:
1286 tpc = self.__master_info.getTopic(i)
1287 if list_type == 'TOPIC_SUB':
1288 count = len(tpc.publisherNodes)
1289 if name not in tpc.subscriberNodes:
1290 count = None
1291 else:
1292 count = len(tpc.subscriberNodes)
1293 if name not in tpc.publisherNodes:
1294 count = None
1295 except:
1296 pass
1297
1298 if count is not None:
1299 item = '<a href="topic://%s">%s</a>' % (i, item_name)
1300 item += ' <a href="topicecho://%s%s"><span style="color:gray;"><i>echo</i></span></a>' % (self.mastername, i)
1301 item = '<span style="color:gray;">_%d_/ </span>%s' % (count, item)
1302 else:
1303 item = '<a>%s</a>' % (item_name)
1304 item = '<span style="color:red;">!sync </span>%s' % (item)
1305 elif list_type == 'SERVICE':
1306 try:
1307 srv = self.__master_info.getService(i)
1308 if name in srv.serviceProvider:
1309 item = '<a href="service://%s%s">%s</a>' % (self.mastername, i, item_name)
1310 item += ' <a href="servicecall://%s%s"><span style="color:gray;"><i>call</i></span></a>' % (self.mastername, i)
1311 else:
1312 item = '<span style="color:red;">!sync </span>%s' % (item_name)
1313 except:
1314 item = '<span style="color:red;">!sync </span>%s' % (item_name)
1315 elif list_type == 'LAUNCH':
1316 item = '<a href="launch://%s">%s</a>' % (i, item_name)
1317 if i in self.__configs and self.masteruri in self.__configs[i].global_param_done:
1318 item += '%s<br><a href="reload-globals://%s"><font color="#339900">reload global parameter @next start</font></a>' % (item, i)
1319 result += '\n%s<br>' % (item)
1320 result += '</ul>'
1321 return result
1322
1324 tab_name = self.masterTab.tabWidget.currentWidget().objectName()
1325 if tab_name == 'tabTopics':
1326
1327 selections = self.masterTab.nodeTreeView.selectionModel().selectedIndexes()
1328 selectedNodes = self.nodesFromIndexes(selections)
1329 if len(selectedNodes) == 1:
1330 node = selectedNodes[0]
1331 selected_topics = self.topic_model.index_from_names(node.published, node.subscribed)
1332 for s in selected_topics:
1333 self.masterTab.topicsView.selectionModel().select(self.topic_proxyModel.mapFromSource(s), QItemSelectionModel.Select)
1334 elif tab_name == 'tabServices':
1335
1336 selections = self.masterTab.nodeTreeView.selectionModel().selectedIndexes()
1337 selectedNodes = self.nodesFromIndexes(selections)
1338 if len(selectedNodes) == 1:
1339 node = selectedNodes[0]
1340 selected_services = self.service_model.index_from_names(node.services)
1341 for s in selected_services:
1342 self.masterTab.servicesView.selectionModel().select(self.service_proxyModel.mapFromSource(s), QItemSelectionModel.Select)
1343
1345 return (self.masterTab.tabWidget.currentWidget().objectName() == tab_name)
1346
1348 '''
1349 updates the Buttons, create a description and emit L{description_signal} to
1350 show the description of host, group or node.
1351 '''
1352 if selected is not None:
1353
1354 self.__last_selection = time.time()
1355 selectedGroups = []
1356 if node_name and self.master_info is not None:
1357
1358 selectedNodes = self.getNode(node_name)
1359 if not selectedNodes or selectedNodes[0] is None:
1360 if node_name:
1361 self.description_signal.emit(node_name, "<b>%s</b> not found" % node_name, True if selected or deselected or force_emit else False)
1362 return
1363 selectedHosts = []
1364 selections = []
1365 else:
1366
1367 if not self._is_current_tab_name('tabNodes'):
1368 return
1369 selections = self.masterTab.nodeTreeView.selectionModel().selectedIndexes()
1370 selectedHosts = self.hostsFromIndexes(selections)
1371 selectedNodes = self.nodesFromIndexes(selections)
1372 selectedGroups = self.groupsFromIndexes(selections)
1373 self.masterTab.topicsView.selectionModel().clear()
1374 self.masterTab.servicesView.selectionModel().clear()
1375 name = ''
1376 text = ''
1377
1378 if len(selectedNodes) > 1 or selectedGroups > 0:
1379 restartable_nodes = [sn for sn in selectedNodes if len(sn.cfgs) > 0 and not self._is_in_ignore_list(sn.name)]
1380 restartable_nodes_with_launchfiles = [sn for sn in selectedNodes if sn.has_launch_cfgs(sn.cfgs) > 0 and not self._is_in_ignore_list(sn.name)]
1381 killable_nodes = [sn for sn in selectedNodes if sn.node_info.pid is not None and not self._is_in_ignore_list(sn.name)]
1382 unregisterble_nodes = [sn for sn in selectedNodes if sn.node_info.pid is None and sn.node_info.uri is not None and sn.node_info.isLocal and not self._is_in_ignore_list(sn.name)]
1383
1384 if restartable_nodes or killable_nodes or unregisterble_nodes:
1385 text += '<b>Selected nodes:</b><br>'
1386 if restartable_nodes:
1387 text += '<a href="restart-node://all_selected_nodes" title="Restart %s selected nodes"><img src=":icons/sekkyumu_restart_24.png" alt="restart">[%d]</a>' % (len(restartable_nodes), len(restartable_nodes))
1388 text += ' <a href="restart-node-g://all_selected_nodes" title="Reload global parameter and restart %s selected nodes"><img src=":icons/sekkyumu_restart_g_24.png" alt="restart">[%d]</a>' % (len(restartable_nodes), len(restartable_nodes))
1389 if killable_nodes:
1390
1391 text += ' <a href="kill-screen://all_selected_nodes" title="Kill %s screens of selected nodes"><img src=":icons/sekkyumu_kill_screen_24.png" alt="killscreen">[%d]</a>' % (len(killable_nodes), len(killable_nodes))
1392 if restartable_nodes_with_launchfiles:
1393 text += ' <a href="start-node-at-host://all_selected_nodes" title="Start %s nodes at another host"><img src=":icons/sekkyumu_start_athost_24.png" alt="start@host">[%d]</a>' % (len(restartable_nodes_with_launchfiles), len(restartable_nodes_with_launchfiles))
1394 text += ' <a href="start-node-adv://all_selected_nodes" title="Start %s nodes with additional options, e.g. loglevel"><img src=":icons/sekkyumu_play_alt_24.png" alt="play alt">[%d]</a>' % (len(restartable_nodes_with_launchfiles), len(restartable_nodes_with_launchfiles))
1395 if unregisterble_nodes:
1396 text += '<br><a href="unregister-node://all_selected_nodes">unregister [%d]</a>' % len(unregisterble_nodes)
1397
1398 if len(selectedHosts) == 1:
1399 host = selectedHosts[0]
1400 name = '%s - Robot' % host.name
1401 text += host.generateDescription()
1402 text += '<br>'
1403 else:
1404
1405 if len(selectedGroups) == 1 and len(selections) / 2 == 1:
1406 group = selectedGroups[0]
1407 name = '%s - Group' % group.name
1408 text += group.generateDescription()
1409 text += '<br>'
1410
1411 if len(selectedHosts) != 1 and len(selectedNodes) == 1 and len(selectedGroups) == 0:
1412 node = selectedNodes[0]
1413 text = self.get_node_description(node_name, node)
1414 name = node.name
1415 if (self._is_current_tab_name('tabNodes') and self.__last_info_text != text) or force_emit:
1416 self.__last_info_text = text
1417 self.description_signal.emit(name, text, True if selected or deselected or force_emit else False)
1418 self.updateButtons(selectedNodes)
1419
1421 text = ''
1422 if node_name and node is None and self.master_info is not None:
1423
1424 selectedNodes = self.getNode(node_name)
1425 if len(selectedNodes) == 1:
1426 node = selectedNodes[0]
1427
1428 if node is not None:
1429
1430 ns, sep, name = node.name.rpartition(rospy.names.SEP)
1431 text = '<font size="+1"><b><span style="color:gray;">%s%s</span><b>%s</b></font><br>' % (ns, sep, name)
1432 launches = [c for c in node.cfgs if not isinstance(c, tuple)]
1433 default_cfgs = [c[0] for c in node.cfgs if isinstance(c, tuple)]
1434 if launches or default_cfgs:
1435 text += '<a href="restart-node://%s" title="Restart node"><img src=":icons/sekkyumu_restart_24.png" alt="restart"></a>' % node.name
1436 text += ' <a href="restart-node-g://%s" title="Reload global parameter and restart node"><img src=":icons/sekkyumu_restart_g_24.png" alt="restart"></a>' % node.name
1437
1438 text += ' <a href="kill-screen://%s" title="Kill screen of the node"><img src=":icons/sekkyumu_kill_screen_24.png" alt="killscreen"></a>' % node.name
1439 if launches:
1440 text += ' <a href="start-node-at-host://%s" title="Start node at another host"><img src=":icons/sekkyumu_start_athost_24.png" alt="start@host"></a>' % node.name
1441
1442 text += ' <a href="start-node-adv://%s" title="Start node with additional options, e.g. loglevel"><img src=":icons/sekkyumu_play_alt_24.png" alt="play alt"></a>' % node.name
1443 text += ' <a href="copy-log-path://%s" title="copy log path to clipboard"><img src=":icons/crystal_clear_copy_log_path_24.png" alt="copy_log_path"></a>' % node.name
1444 text += '<dl>'
1445 text += '<dt><b>URI</b>: %s</dt>' % node.node_info.uri
1446 text += '<dt><b>PID</b>: %s</dt>' % node.node_info.pid
1447 text += '<dt><b>ORG.MASTERURI</b>: %s</dt>' % node.node_info.masteruri
1448 if not is_legal_name(node.name):
1449 text += '<dt><font color="#FF6600"><b>This node has an illegal <node> name.<br><a href="http://ros.org/wiki/Names">http://ros.org/wiki/Names</a><br>This will likely cause problems with other ROS tools.</b></font></dt>'
1450 if node.is_ghost:
1451 if node.name.endswith('master_sync') or node.name.endswith('node_manager'):
1452 text += '<dt><font color="#FF9900"><b>This node is not synchronized by default. To get info about this node select the related host.</b></font></dt>'
1453 else:
1454 text += '<dt><font color="#FF9900"><b>The node is running on remote host, but is not synchronized, because of filter or errors while sync, see log of <i>master_sync</i></b></font></dt>'
1455 text += '<dt><font color="#FF9900"><i>Are you use the same ROS packages?</i></font></dt>'
1456 if node.has_running and node.node_info.pid is None and node.node_info.uri is None:
1457 text += '<dt><font color="#FF9900"><b>There are nodes with the same name on remote hosts running. These will be terminated, if you run this node! (Only if master_sync is running or will be started somewhere!)</b></font></dt>'
1458 if node.node_info.uri is not None and node.node_info.masteruri != self.masteruri:
1459 text += '<dt><font color="#339900"><b>synchronized</b></font></dt>'
1460 if node.node_info.pid is None and node.node_info.uri is not None:
1461 if not node.node_info.isLocal:
1462 text += '<dt><font color="#FF9900"><b>remote nodes will not be ping, so they are always marked running</b></font>'
1463 else:
1464 text += '<dt><font color="#CC0000"><b>the node does not respond: </b></font>'
1465 text += '<a href="unregister-node://%s">unregister</a></dt>' % node.name
1466 if node.diagnostic_array and node.diagnostic_array[-1].level > 0:
1467 diag_status = node.diagnostic_array[-1]
1468 level_str = self.DIAGNOSTIC_LEVELS[diag_status.level]
1469 diag_color = '#FF6600'
1470 if diag_status.level == 2:
1471 diag_color = '#CC0000'
1472 elif diag_status.level == 3:
1473 diag_color = '#FFCC00'
1474 elif diag_status.level > 3:
1475 diag_color = '#0000CC'
1476 text += '<dt><font color="%s"><b>%s: %s</b></font></dt>' % (diag_color, level_str, node.diagnostic_array[-1].message)
1477
1478
1479 text += '</dl>'
1480 if node.nodelet_mngr:
1481 text += '<dt><b>Nodelet manager</b>: %s</dt>' % self._create_html_list('', [node.nodelet_mngr], 'NODE')
1482 if node.nodelets:
1483 text += '<dt>Manager for <b>%d</b> nodelets</dt>' % len(node.nodelets)
1484 if nm.settings().transpose_pub_sub_descr:
1485 text += self._create_html_list('Subscribed Topics:', node.subscribed, 'TOPIC_SUB', node.name)
1486 text += self._create_html_list('Published Topics:', node.published, 'TOPIC_PUB', node.name)
1487 else:
1488 text += self._create_html_list('Published Topics:', node.published, 'TOPIC_PUB', node.name)
1489 text += self._create_html_list('Subscribed Topics:', node.subscribed, 'TOPIC_SUB', node.name)
1490 text += self._create_html_list('Services:', node.services, 'SERVICE', node.name)
1491
1492 text += self._create_html_list('Loaded Launch Files:', launches, 'LAUNCH')
1493 text += self._create_html_list('Default Configurations:', default_cfgs, 'NODE')
1494
1495 return text
1496
1498 '''
1499 updates the Buttons, create a description and emit L{description_signal} to
1500 show the description of selected topic
1501 '''
1502 if selected is not None:
1503
1504 self.__last_selection = time.time()
1505 selectedTopics = []
1506 if topic_name and self.master_info is not None:
1507 selectedTopics = [self.master_info.getTopic("%s" % topic_name)]
1508 if len(selectedTopics) == 0:
1509 return
1510 else:
1511 if not self._is_current_tab_name('tabTopics'):
1512 return
1513 selectedTopics = self.topicsFromIndexes(self.masterTab.topicsView.selectionModel().selectedIndexes())
1514 topics_selected = (len(selectedTopics) > 0)
1515 self.masterTab.echoTopicButton.setEnabled(topics_selected)
1516 self.masterTab.hzTopicButton.setEnabled(topics_selected)
1517 self.masterTab.hzSshTopicButton.setEnabled(topics_selected)
1518 self.masterTab.pubStopTopicButton.setEnabled(topics_selected)
1519 if len(selectedTopics) == 1:
1520 try:
1521 topic = selectedTopics[0]
1522 text = self.get_topic_description(topic_name, topic)
1523 info_text = '<div>%s</div>' % text
1524 if (self._is_current_tab_name('tabTopics') and self.__last_info_text != info_text) or force_emit:
1525 self.__last_info_text = info_text
1526 self.description_signal.emit(topic.name, info_text, True if selected or deselected or force_emit else False)
1527 except Exception as _:
1528 pass
1529
1531 text = ''
1532 if topic is None:
1533 selectedTopics = []
1534 if topic_name and self.master_info is not None:
1535 selectedTopics = [self.master_info.getTopic("%s" % topic_name)]
1536 else:
1537 selectedTopics = self.topicsFromIndexes(self.masterTab.topicsView.selectionModel().selectedIndexes())
1538 if len(selectedTopics) == 1:
1539 topic = selectedTopics[0]
1540 if topic is not None:
1541 ns, sep, name = topic.name.rpartition(rospy.names.SEP)
1542 text = '<font size="+1"><b><span style="color:gray;">%s%s</span><b>%s</b></font><br>' % (ns, sep, name)
1543 text += ' <a href="topicecho://%s%s" title="Show the content of the topic"><img src=":icons/sekkyumu_topic_echo_24.png" alt="echo"></a>' % (self.mastername, topic.name)
1544 text += ' <a href="topichz://%s%s" title="Show only the receive rate of the topic.<br>All data is sent through the network"><img src=":icons/sekkyumu_topic_hz_24.png" alt="hz"></a>' % (self.mastername, topic.name)
1545 text += ' <a href="topichzssh://%s%s" title="Show only the receive rate of the topic.<br>Uses an SSH connection to execute `rostopic hz` on remote host."><img src=":icons/sekkyumu_topic_echo_ssh_hz_24.png" alt="sshhz"></a>' % (self.mastername, topic.name)
1546 text += ' <a href="topicpub://%s%s" title="Start a publisher for selected topic"><img src=":icons/sekkyumu_topic_pub_24.png" alt="pub"></a>' % (self.mastername, topic.name)
1547 if topic.name in self.__republish_params:
1548 text += ' <a href="topicrepub://%s%s" title="Start a publisher with last parameters"><img src=":icons/sekkyumu_topic_repub_24.png" alt="repub"></a>' % (self.mastername, topic.name)
1549 topic_publisher = []
1550 topic_prefix = '/rostopic_pub%s_' % topic.name
1551 node_names = self.master_info.node_names
1552 for n in node_names:
1553 if n.startswith(topic_prefix):
1554 topic_publisher.append(n)
1555 if topic_publisher:
1556 text += ' <a href="topicstop://%s%s"><img src=":icons/sekkyumu_topic_pub_stop_24.png" alt="stop"> [%d]</a>' % (self.mastername, topic.name, len(topic_publisher))
1557 text += '<p>'
1558 if nm.settings().transpose_pub_sub_descr:
1559 text += self._create_html_list('Subscriber:', topic.subscriberNodes, 'NODE')
1560 text += self._create_html_list('Publisher:', topic.publisherNodes, 'NODE')
1561 else:
1562 text += self._create_html_list('Publisher:', topic.publisherNodes, 'NODE')
1563 text += self._create_html_list('Subscriber:', topic.subscriberNodes, 'NODE')
1564 text += '<b><u>Type:</u></b> %s' % self._href_from_msgtype(topic.type)
1565 text += '<dl>'
1566 try:
1567 mclass = roslib.message.get_message_class(topic.type)
1568 if mclass is not None:
1569 for f in mclass.__slots__:
1570 idx = mclass.__slots__.index(f)
1571 idtype = mclass._slot_types[idx]
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582 text += '%s: <span style="color:gray;">%s</span><br>' % (f, idtype)
1583 text += '<br>'
1584 constants = {}
1585 for m in dir(mclass):
1586 if not m.startswith('_'):
1587 if type(getattr(mclass, m)) in [str, int, bool, float]:
1588 constants[m] = getattr(mclass, m)
1589 if constants:
1590 text += '<b><u>Constants:</u></b><br>'
1591 for n in sorted(constants.iterkeys()):
1592 text += '%s: <span style="color:gray;">%s</span><br>' % (n, constants[n])
1593 except ValueError:
1594 pass
1595 text += '</dl>'
1596 return text
1597
1599 result = msg_type
1600 if msg_type:
1601 result = '<a href="http://ros.org/doc/api/%s.html">%s</a>' % (msg_type.replace('/', '/html/msg/'), msg_type)
1602 return result
1603
1605 '''
1606 updates the Buttons, create a description and emit L{description_signal} to
1607 show the description of selected service
1608 '''
1609 if selected is not None:
1610
1611 self.__last_selection = time.time()
1612 if service_name and self.master_info is not None:
1613
1614 selectedServices = [self.master_info.getService("%s" % service_name)]
1615 if selectedServices[0] is None:
1616 return
1617 else:
1618
1619 selectedServices = self.servicesFromIndexes(self.masterTab.servicesView.selectionModel().selectedIndexes())
1620 self.masterTab.callServiceButton.setEnabled(len(selectedServices) > 0)
1621 if not self._is_current_tab_name('tabServices'):
1622 return
1623 if len(selectedServices) == 1:
1624 service = selectedServices[0]
1625 ns, sep, name = service.name.rpartition(rospy.names.SEP)
1626 text = '<font size="+1"><b><span style="color:gray;">%s%s</span><b>%s</b></font><br>' % (ns, sep, name)
1627 text += '<a href="servicecall://%s%s" title="call service"><img src=":icons/sekkyumu_call_service_24.png" alt="call"></a>' % (self.mastername, service.name)
1628 text += '<dl>'
1629 text += '<dt><b>URI</b>: %s</dt>' % service.uri
1630 text += '<dt><b>ORG.MASTERURI</b>: %s</dt>' % service.masteruri
1631 text += self._create_html_list('Provider:', service.serviceProvider, 'NODE')
1632 if service.masteruri != self.masteruri:
1633 text += '<dt><font color="#339900"><b>synchronized</b></font></dt>'
1634 text += '</dl>'
1635 try:
1636 service_class = service.get_service_class(nm.is_local(get_hostname(service.uri)))
1637 text += '<h4>%s</h4>' % self._href_from_svrtype(service_class._type)
1638 text += '<b><u>Request:</u></b>'
1639 text += '<dl><dt>%s</dt></dl>' % service_class._request_class.__slots__
1640 text += '<b><u>Response:</u></b>'
1641 text += '<dl><dt>%s</dt></dl>' % service_class._response_class.__slots__
1642 except:
1643 text += '<h4><font color=red>Unknown service type</font></h4>'
1644 if service.isLocal:
1645 text += '<font color=red>Unable to communicate with service, is provider node running?</font>'
1646 else:
1647 text += '<font color=red>Try to refresh <b>all</b> hosts. Is provider node running?</font>'
1648 info_text = '<div>%s</div>' % text
1649 self._is_current_tab_name('tabServices')
1650 if (self._is_current_tab_name('tabServices') and self.__last_info_text != info_text) or force_emit:
1651 self.__last_info_text = info_text
1652 self.description_signal.emit(service.name, info_text, True if selected or deselected or force_emit else False)
1653
1655 result = srv_type
1656 if srv_type:
1657 result = '<a href="http://ros.org/doc/api/%s.html">%s</a>' % (srv_type.replace('/', '/html/srv/'), srv_type)
1658 return result
1659
1661 selectedParameter = self.parameterFromIndexes(self.masterTab.parameterView.selectionModel().selectedIndexes())
1662 self.masterTab.deleteParameterButton.setEnabled(len(selectedParameter) > 0)
1663 self.masterTab.saveParameterButton.setEnabled(len(selectedParameter) > 0)
1664
1666 result = []
1667 for index in indexes:
1668 if index.column() == 0:
1669 model_index = self.node_proxy_model.mapToSource(index)
1670 item = self.node_tree_model.itemFromIndex(model_index)
1671 if item is not None:
1672 if isinstance(item, HostItem):
1673 result.append(item)
1674 return result
1675
1677 result = []
1678 for index in indexes:
1679 if index.column() == 0 and index.parent().isValid():
1680 model_index = self.node_proxy_model.mapToSource(index)
1681 item = self.node_tree_model.itemFromIndex(model_index)
1682 if item is not None:
1683 if isinstance(item, GroupItem):
1684 result.append(item)
1685 return result
1686
1688 result = []
1689 for index in indexes:
1690 if index.column() == 0:
1691 model_index = self.node_proxy_model.mapToSource(index)
1692 item = self.node_tree_model.itemFromIndex(model_index)
1693 res = self._nodesFromItems(item, recursive)
1694 for r in res:
1695 if r not in result:
1696 result.append(r)
1697 return result
1698
1700 result = []
1701 if item is not None:
1702 if isinstance(item, (GroupItem, HostItem)):
1703 if recursive:
1704 for j in range(item.rowCount()):
1705 result[len(result):] = self._nodesFromItems(item.child(j), recursive)
1706 elif isinstance(item, NodeItem):
1707 if item not in result:
1708 result.append(item)
1709 return result
1710
1712 result = []
1713 for index in indexes:
1714 model_index = self.topic_proxyModel.mapToSource(index)
1715 item = self.topic_model.itemFromIndex(model_index)
1716 if item is not None:
1717 if isinstance(item, TopicItem):
1718 result.append(item.topic)
1719 elif isinstance(item, TopicGroupItem):
1720 for titem in item.get_topic_items():
1721 result.append(titem.topic)
1722 return result
1723
1725 result = []
1726 for index in indexes:
1727 model_index = self.service_proxyModel.mapToSource(index)
1728 item = self.service_model.itemFromIndex(model_index)
1729 if item is not None:
1730 if isinstance(item, ServiceItem):
1731 result.append(item.service)
1732 elif isinstance(item, ServiceGroupItem):
1733 for sitem in item.get_service_items():
1734 result.append(sitem.service)
1735 return result
1736
1738 result = []
1739 for index in indexes:
1740 model_index = self.parameter_proxyModel.mapToSource(index)
1741 item = self.parameter_model.itemFromIndex(model_index)
1742 if item is not None and isinstance(item, ParameterValueItem):
1743 result.append((item.name, item.value))
1744 return result
1745
1746
1747
1748
1749
1751 '''
1752 Starts the selected nodes. If for a node more then one configuration is
1753 available, the selection dialog will be show.
1754 '''
1755 cursor = self.cursor()
1756 self.masterTab.startButton.setEnabled(False)
1757 self.setCursor(Qt.WaitCursor)
1758 try:
1759 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
1760 self.start_nodes(selectedNodes)
1761 finally:
1762 self.setCursor(cursor)
1763 self.masterTab.startButton.setEnabled(True)
1764
1766 '''
1767 Starts the selected nodes with additional options.
1768 '''
1769 cursor = self.cursor()
1770 self.setCursor(Qt.WaitCursor)
1771 try:
1772 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
1773 self.start_nodes(selectedNodes, force=True, use_adv_cfg=True)
1774 finally:
1775 self.setCursor(cursor)
1776
1777 - def start_node(self, node, force, config, force_host=None, logging=None):
1778
1779 if node is None:
1780 raise DetailedError("Start error", 'None is not valid node name!')
1781 if node.pid is None or force:
1782
1783 if config is None:
1784 raise DetailedError("Start error",
1785 'Error while start %s:\nNo configuration found!' % node.name)
1786 if isinstance(config, LaunchConfig):
1787 try:
1788 nm.starter().runNode(AdvRunCfg(node.name, config, force_host, self.masteruri, logging=logging, user=self.current_user))
1789 except socket.error as se:
1790 rospy.logwarn("Error while start '%s': %s\n\n Start canceled!", node.name, utf8(se))
1791 raise DetailedError("Start error",
1792 'Error while start %s\n\nStart canceled!' % node.name,
1793 '%s' % utf8(se))
1794 return False
1795 except nm.InteractionNeededError as _:
1796 raise
1797 except (Exception, nm.StartException) as e:
1798 print type(e)
1799 print traceback.format_exc(3)
1800 rospy.logwarn("Error while start '%s': %s" % (node.name, utf8(e)))
1801 raise DetailedError("Start error", 'Error while start %s' % node.name, '%s' % utf8(e))
1802 elif isinstance(config, (str, unicode)):
1803
1804 from multimaster_msgs_fkie.srv import Task
1805 try:
1806 nm.starter().callService(self.master_info.getService(config).uri, config, Task, [node.name])
1807 except (Exception, nm.StartException) as e:
1808 rospy.logwarn("Error while call a service of node '%s': %s" % (node.name, utf8(e)))
1809 raise DetailedError("Service error",
1810 'Error while call a service of node %s [%s]' % (node.name, self.master_info.getService(config).uri),
1811 '%s' % utf8(e))
1812
1813 - def start_nodes(self, nodes, force=False, force_host=None, use_adv_cfg=False, check_nodelets=True):
1814 '''
1815 Internal method to start a list with nodes
1816 @param nodes: the list with nodes to start
1817 @type nodes: C{[L{NodeItem}, ...]}
1818 @param force: force the start of the node, also if it is already started.
1819 @type force: C{bool}
1820 @param force_host: force the start of the node at specified host.
1821 @type force_host: C{str}
1822 '''
1823 cfg_choices = dict()
1824 cfg_nodes = dict()
1825 has_launch_files = False
1826 for node in nodes:
1827
1828 if (node.pid is None or (node.pid is not None and force)) and not node.is_ghost:
1829
1830 if node.uri is None and node.has_running:
1831 ret = MessageBox.question(self, 'Question', ''.join(['Some nodes, e.g. ', node.name, ' are already running on another host. If you start this node the other node will be terminated.\n Do you want proceed?']), buttons=MessageBox.Yes | MessageBox.No)
1832 if ret == MessageBox.No:
1833 return
1834
1835 if node.next_start_cfg is not None:
1836 lcfg = node.next_start_cfg
1837 if node.next_start_cfg in self.launchfiles:
1838 lcfg = self.launchfiles[node.next_start_cfg]
1839 cfg_nodes[node.name] = lcfg
1840 node.launched_cfg = lcfg
1841 node.next_start_cfg = None
1842 else:
1843 choices = self._getCfgChoises(node)
1844 ch_keys = choices.keys()
1845 if ch_keys:
1846 ch_keys.sort()
1847 choises_str = utf8(ch_keys)
1848 if choises_str not in cfg_choices.keys():
1849 choice, ok = self._getUserCfgChoice(choices, node.name)
1850 if choice is not None:
1851 cfg_choices[choises_str] = choices[choice]
1852 cfg_nodes[node.name] = choices[choice]
1853 node.launched_cfg = choices[choice]
1854 if isinstance(choices[choice], LaunchConfig):
1855 has_launch_files = True
1856 elif ok:
1857 MessageBox.warning(self, "Start error",
1858 'Error while start %s:\nNo configuration selected!' % node.name)
1859 else:
1860 break
1861 else:
1862 cfg_nodes[node.name] = cfg_choices[choises_str]
1863 node.launched_cfg = cfg_choices[choises_str]
1864
1865
1866 logging = None
1867 diag_canceled = False
1868 if use_adv_cfg and has_launch_files:
1869 log_params = {'Level': ('string', nm.settings().logging.get_alternatives('loglevel')),
1870 'Level (roscpp)': ('string', nm.settings().logging.get_alternatives('loglevel_roscpp')),
1871 'Level (super)': ('string', nm.settings().logging.get_alternatives('loglevel_superdebug')),
1872 'Format': ('string', nm.settings().logging.get_alternatives('console_format'))
1873 }
1874 params = {'Logging': ('dict', log_params)}
1875 dia = ParameterDialog(params)
1876 dia.setFilterVisible(False)
1877 dia.setWindowTitle('Start with parameters')
1878 dia.resize(480, 120)
1879 dia.setFocusField('Level')
1880 diag_canceled = not dia.exec_()
1881 if not diag_canceled:
1882 try:
1883 params = dia.getKeywords()
1884 nm.settings().logging.loglevel = params['Logging']['Level']
1885 nm.settings().logging.loglevel_roscpp = params['Logging']['Level (roscpp)']
1886 nm.settings().logging.loglevel_superdebug = params['Logging']['Level (super)']
1887 nm.settings().logging.console_format = params['Logging']['Format']
1888 nm.settings().store_logging()
1889 logging = nm.settings().logging
1890 except Exception, e:
1891 diag_canceled = True
1892 MessageBox.warning(self, "Get advanced start parameter",
1893 'Error while parse parameter',
1894 utf8(e))
1895 if not diag_canceled:
1896
1897 if check_nodelets:
1898 self._check_for_nodelets(nodes)
1899
1900 for node in nodes:
1901 if node.name in cfg_nodes:
1902 self._progress_queue.add2queue(utf8(uuid.uuid4()),
1903 ''.join(['start ', node.node_info.name]),
1904 self.start_node,
1905 (node.node_info, force, cfg_nodes[node.node_info.name], force_host, logging))
1906 self._start_queue(self._progress_queue)
1907
1909 self._restart_nodelets = {}
1910 nodenames = [n.name for n in nodes]
1911 nodelet_mngr = ''
1912 nlmngr = ''
1913 for node in nodes:
1914 try:
1915 cfg_name = node.launched_cfg
1916 if isinstance(node.launched_cfg, LaunchConfig):
1917 cfg_name = node.launched_cfg.Filename
1918 nodelets = self._get_nodelets(node.name, cfg_name)
1919 if nodelets:
1920 nodelets = self._get_nodelets(node.name, cfg_name)
1921 r_nn = []
1922 for nn in nodelets:
1923 if nn not in nodenames:
1924 r_nn.append(nn)
1925 if cfg_name not in self._restart_nodelets:
1926 self._restart_nodelets[cfg_name] = []
1927 self._restart_nodelets[cfg_name].append(nn)
1928 if self._restart_nodelets:
1929 nlmngr = node.name
1930 else:
1931 nodelet_mngr = self._get_nodelet_manager(node.name, cfg_name)
1932 if nodelet_mngr:
1933 if nodelet_mngr not in nodenames:
1934 if cfg_name not in self._restart_nodelets:
1935 self._restart_nodelets[cfg_name] = []
1936 self._restart_nodelets[cfg_name].append(nodelet_mngr)
1937 nodelets = self._get_nodelets(nodelet_mngr, cfg_name)
1938 r_nn = []
1939 for nn in nodelets:
1940 if nn not in nodenames:
1941 r_nn.append(nn)
1942 self._restart_nodelets[cfg_name].append(nn)
1943 nodelet_mngr = nodelet_mngr
1944 except Exception as err:
1945 rospy.logwarn("Error while test for nodelets: %s" % utf8(err))
1946 if nm.settings().check_for_nodelets_at_start:
1947 if nodelet_mngr and nodelet_mngr not in nodenames:
1948 self.message_frame.show_question(MessageFrame.TYPE_NODELET, "Nodelet manager '%s' not in current list. (Re)Start nodelet manager and all nodelets?" % nodelet_mngr, MessageData(self._restart_nodelets))
1949 elif self._restart_nodelets:
1950 self.message_frame.show_question(MessageFrame.TYPE_NODELET, "Not all nodelets of manager '%s' are in the start list. (Re)Start these?" % nlmngr, MessageData(self._restart_nodelets))
1951
1953 '''
1954 Start nodes given in a list by their names.
1955 @param nodes: a list with full node names
1956 @type nodes: C{[str]}
1957 '''
1958 result = []
1959 if self.master_info is not None:
1960 for n in nodes:
1961 node_items = self.getNode(n)
1962 if node_items:
1963 node_item = node_items[0]
1964
1965 if isinstance(cfg, tuple):
1966 node_item.next_start_cfg = cfg[0]
1967 else:
1968 node_item.next_start_cfg = cfg
1969 elif cfg:
1970 node_info = NodeInfo(n, self.masteruri)
1971 node_item = NodeItem(node_info)
1972
1973 if isinstance(cfg, tuple):
1974 node_item.next_start_cfg = cfg[0]
1975 else:
1976 node_item.next_start_cfg = cfg
1977 if node_item is not None:
1978 result.append(node_item)
1979 self.start_nodes(result, force, check_nodelets=check_nodelets)
1980
1982 '''
1983 Start nodes after the given configuration is loaded and applied to the model.
1984 :param cfg_name: the name of the cnofiguration
1985 :type cfg_name: str
1986 :param nodes: the list of node names
1987 :type nodes: list of strings
1988 '''
1989 if cfg_name not in self._start_nodes_after_load_cfg:
1990 self._start_nodes_after_load_cfg[cfg_name] = set(nodes)
1991 else:
1992 self._start_nodes_after_load_cfg[cfg_name].update(set(nodes))
1993
1995 '''
1996 Clears the list with nodes which should be startet after a launch file is loaded.
1997 '''
1998 self._start_nodes_after_load_cfg = dict()
1999
2001 '''
2002 Starts the selected nodes (also if it already running). If for a node more then one configuration is
2003 available, the selection dialog will be show.
2004 '''
2005 cursor = self.cursor()
2006 self.masterTab.startButton.setEnabled(False)
2007 self.setCursor(Qt.WaitCursor)
2008 try:
2009 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
2010 self.stop_nodes(selectedNodes)
2011 if reset_global_param:
2012
2013 for node in selectedNodes:
2014 for cfg in node.cfgs:
2015 if cfg in self.launchfiles:
2016 self.reload_global_parameter_at_next_start(cfg)
2017 self.start_nodes(selectedNodes, True)
2018 finally:
2019 self.setCursor(cursor)
2020 self.masterTab.startButton.setEnabled(True)
2021
2023 '''
2024 Starts the selected nodes on an another host.
2025 '''
2026 cursor = self.cursor()
2027 self.masterTab.startButton.setEnabled(False)
2028 params = {'Host': ('string', 'localhost')}
2029 dia = ParameterDialog(params)
2030 dia.setFilterVisible(False)
2031 dia.setWindowTitle('Start node on...')
2032 dia.resize(350, 120)
2033 dia.setFocusField('host')
2034 if dia.exec_():
2035 try:
2036 params = dia.getKeywords()
2037 host = params['Host']
2038 self.setCursor(Qt.WaitCursor)
2039 try:
2040 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
2041 self.start_nodes(selectedNodes, True, host)
2042 finally:
2043 self.setCursor(cursor)
2044 except Exception, e:
2045 MessageBox.warning(self, "Start error",
2046 'Error while parse parameter',
2047 utf8(e))
2048 self.masterTab.startButton.setEnabled(True)
2049
2051 result = {}
2052 for c in node.cfgs:
2053 if isinstance(c, tuple):
2054 result[' '.join(['[default]', c[0]])] = roslib.names.ns_join(c[0], 'run')
2055 return result
2056
2058 result = {}
2059 for c in node.cfgs:
2060 if c:
2061 if not isinstance(c, tuple):
2062 launch = self.launchfiles[c]
2063 result[''.join([utf8(launch.LaunchName), ' [', utf8(launch.PackageName), ']'])] = self.launchfiles[c]
2064 elif not ignore_defaults:
2065 result[' '.join(['[default]', c[0]])] = roslib.names.ns_join(c[0], 'run')
2066 return result
2067
2069 value = None
2070 ok = False
2071
2072 if len(choices) == 1:
2073 value = choices.keys()[0]
2074 ok = True
2075 elif len(choices) > 0:
2076 items, ok = SelectDialog.getValue('Configuration selection', 'Select configuration to launch <b>%s</b>' % nodename, choices.keys(), True)
2077 if items:
2078 value = items[0]
2079 return value, ok
2080
2082 '''
2083 Stops the selected and running nodes. If the node can't be stopped using his
2084 RPC interface, it will be unregistered from the ROS master using the masters
2085 RPC interface.
2086 '''
2087 key_mod = QApplication.keyboardModifiers()
2088 if (key_mod & Qt.ShiftModifier or key_mod & Qt.ControlModifier):
2089 self.masterTab.stopButton.showMenu()
2090 else:
2091 cursor = self.cursor()
2092 self.setCursor(Qt.WaitCursor)
2093 try:
2094 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
2095 self.stop_nodes(selectedNodes)
2096 finally:
2097 self.setCursor(cursor)
2098
2100 if node is not None and node.uri is not None and (not self._is_in_ignore_list(node.name) or force):
2101 try:
2102 rospy.loginfo("Stop node '%s'[%s]", utf8(node.name), utf8(node.uri))
2103 nm.filewatcher().rem_binary(node.name)
2104
2105 socket.setdefaulttimeout(10)
2106
2107 p = xmlrpclib.ServerProxy(node.uri)
2108
2109 p.shutdown(rospy.get_name(), ''.join(['[node manager] request from ', self.mastername]))
2110
2111 except Exception, e:
2112 rospy.logwarn("Error while stop node '%s': %s", utf8(node.name), utf8(e))
2113 if utf8(e).find(' 111') == 1:
2114 raise DetailedError("Stop error",
2115 ''.join(['Error while stop node ', node.name]),
2116 utf8(e))
2117 finally:
2118 socket.setdefaulttimeout(None)
2119 elif isinstance(node, NodeItem) and node.is_ghost:
2120
2121
2122 self.stop_nodes_signal.emit(node.masteruri, [node.name])
2123 return True
2124
2126 '''
2127 Internal method to stop a list with nodes
2128 @param nodes: the list with nodes to stop
2129 @type nodes: C{[U{master_discovery_fkie.NodeInfo<http://docs.ros.org/kinetic/api/master_discovery_fkie/html/modules.html#master_discovery_fkie.master_info.NodeInfo>}, ...]}
2130 '''
2131
2132 for node in nodes:
2133 self._progress_queue.add2queue(utf8(uuid.uuid4()),
2134 'stop %s' % node.name,
2135 self.stop_node,
2136 (node, (len(nodes) == 1) or force))
2137 self._start_queue(self._progress_queue)
2138
2140 '''
2141 Stop nodes given in a list by their names.
2142 @param nodes: a list with full node names
2143 @type nodes: C{[str]}
2144 '''
2145 result = []
2146 if self.master_info is not None:
2147 for n in nodes:
2148 if n not in ignore:
2149 node = self.master_info.getNode(n)
2150 if node is not None:
2151 result.append(node)
2152 self.stop_nodes(result, force)
2153
2155 if node is not None and node.uri is not None and (not self._is_in_ignore_list(node.name) or force):
2156 pid = node.pid
2157 if pid is None:
2158
2159 try:
2160 socket.setdefaulttimeout(10)
2161 rpc_node = xmlrpclib.ServerProxy(node.uri)
2162 _, _, pid = rpc_node.getPid(rospy.get_name())
2163 except:
2164 pass
2165 finally:
2166 socket.setdefaulttimeout(None)
2167
2168 if pid is not None:
2169 try:
2170 self._progress_queue.add2queue(utf8(uuid.uuid4()),
2171 ''.join(['kill ', node.name, '(', utf8(pid), ')']),
2172 nm.starter().kill,
2173 (self.getHostFromNode(node), pid, False, self.current_user))
2174 self._start_queue(self._progress_queue)
2175 except Exception as e:
2176 rospy.logwarn("Error while kill the node %s: %s", utf8(node.name), utf8(e))
2177 raise DetailedError("Kill error",
2178 ''.join(['Error while kill the node ', node.name]),
2179 utf8(e))
2180 return True
2181
2200
2202 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
2203
2204
2205 for node in selectedNodes:
2206 self._progress_queue.add2queue(utf8(uuid.uuid4()),
2207 ''.join(['kill ', node.name]),
2208 self.kill_node,
2209 (node, (len(selectedNodes) == 1)))
2210 self._start_queue(self._progress_queue)
2211
2213 if node is not None and node.uri is not None and (not self._is_in_ignore_list(node.name) or force):
2214
2215
2216
2217
2218
2219
2220
2221
2222 try:
2223 socket.setdefaulttimeout(10)
2224 master = xmlrpclib.ServerProxy(node.masteruri)
2225 master_multi = xmlrpclib.MultiCall(master)
2226
2227 for p in node.published:
2228 rospy.loginfo("unregister publisher '%s' [%s] from ROS master: %s", p, node.name, node.masteruri)
2229 master_multi.unregisterPublisher(node.name, p, node.uri)
2230 for t in node.subscribed:
2231 rospy.loginfo("unregister subscriber '%s' [%s] from ROS master: %s", t, node.name, node.masteruri)
2232 master_multi.unregisterSubscriber(node.name, t, node.uri)
2233 if self.master_state is not None:
2234 for s in node.services:
2235 rospy.loginfo("unregister service '%s' [%s] from ROS master: %s", s, node.name, node.masteruri)
2236 service = self.master_info.getService(s)
2237 if not (service is None):
2238 master_multi.unregisterService(node.name, s, service.uri)
2239 r = master_multi()
2240 for code, msg, _ in r:
2241 if code != 1:
2242 rospy.logwarn("unregistration failed: %s", msg)
2243 except Exception, e:
2244 rospy.logwarn("Error while unregister node %s: %s", utf8(node.name), utf8(e))
2245 raise DetailedError("Unregister error",
2246 ''.join(['Error while Unregister node ', node.name]),
2247 utf8(e))
2248 finally:
2249 socket.setdefaulttimeout(None)
2250 return True
2251
2253 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
2254
2255 for node in selectedNodes:
2256 if node.pid is None or len(selectedNodes) == 1:
2257 self._progress_queue.add2queue(utf8(uuid.uuid4()),
2258 ''.join(['unregister node ', node.name]),
2259 self.unregister_node,
2260 (node, (len(selectedNodes) == 1)))
2261 self._start_queue(self._progress_queue)
2262
2263 - def on_stop_context_toggled(self, state):
2264 menu = QMenu(self)
2265 self.killAct = QAction("&Kill Node", self, shortcut=QKeySequence.New, statusTip="Kill selected node", triggered=self.kill_nodes)
2266 self.unregAct = QAction("&Unregister Nodes...", self, shortcut=QKeySequence.Open, statusTip="Open an existing file", triggered=self.unreg_nodes)
2267 menu.addAction(self.killAct)
2268 menu.addAction(self.unregAct)
2269 menu.exec_(self.masterTab.stopContextButton.pos())
2270
2272 '''
2273 If the node is running the host the node URI will be returned. Otherwise
2274 tries to get the host from the launch configuration. If the configuration
2275 contains no machine assignment for this node the host of the ROS master URI
2276 will be used.
2277 @param node:
2278 @type node: U{master_discovery_fkie.NodeInfo<http://docs.ros.org/kinetic/api/master_discovery_fkie/html/modules.html#master_discovery_fkie.master_info.NodeInfo>}
2279 '''
2280 if node.uri is not None:
2281 return get_hostname(node.uri)
2282
2283 for c in node.cfgs:
2284 if not isinstance(c, tuple):
2285 launch_config = self.__configs[c]
2286 item = launch_config.getNode(node.name)
2287 if item is not None and item.machine_name and not item.machine_name == 'localhost':
2288 return launch_config.Roscfg.machines[item.machine_name].address
2289
2290 return get_hostname(node.masteruri)
2291
2293 '''
2294 Shows IO of the selected nodes.
2295 '''
2296
2297
2298
2299
2300
2301 cursor = self.cursor()
2302 self.setCursor(Qt.WaitCursor)
2303 try:
2304 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
2305 if selectedNodes:
2306 ret = True
2307 if len(selectedNodes) > 5:
2308 ret = MessageBox.question(self, "Show IO", "You are going to open the IO of " + utf8(len(selectedNodes)) + " nodes at once\nContinue?", buttons=MessageBox.Ok | MessageBox.Cancel)
2309 ret = (ret == MessageBox.Ok)
2310 if ret:
2311 queue = self._progress_queue_prio
2312
2313 if self._progress_queue.count() < 5:
2314 queue = self._progress_queue
2315 for node in selectedNodes:
2316 queue.add2queue(utf8(uuid.uuid4()),
2317 'show IO of %s' % node.name,
2318 nm.screen().openScreen,
2319 (node.name, self.getHostFromNode(node), False, self.current_user))
2320 self._start_queue(queue)
2321 else:
2322 self.on_show_all_screens()
2323 finally:
2324 self.setCursor(cursor)
2325
2336
2338 '''
2339 Kills selected screens, if some available.
2340 '''
2341 cursor = self.cursor()
2342 self.setCursor(Qt.WaitCursor)
2343 try:
2344 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
2345 for node in selectedNodes:
2346 self._progress_queue.add2queue(utf8(uuid.uuid4()),
2347 ''.join(['kill screen of ', node.name]),
2348 nm.screen().killScreens,
2349 (node.name, self.getHostFromNode(node), False, self.current_user))
2350 self._start_queue(self._progress_queue)
2351 finally:
2352 self.setCursor(cursor)
2353
2355 '''
2356 Shows all available screens.
2357 '''
2358 cursor = self.cursor()
2359 self.setCursor(Qt.WaitCursor)
2360 try:
2361 host = get_hostname(self.masteruri)
2362 sel_screen = []
2363 try:
2364 screens = nm.screen().getActiveScreens(host, auto_pw_request=True, user=self.current_user)
2365 sel_screen, _ = SelectDialog.getValue('Open screen', '', screens, False, False, self)
2366 except Exception, e:
2367 rospy.logwarn("Error while get screen list: %s", utf8(e))
2368 MessageBox.warning(self, "Screen list error",
2369 ''.join(['Error while get screen list from ', host]),
2370 utf8(e))
2371 for screen in sel_screen:
2372 try:
2373 if not nm.screen().openScreenTerminal(host, screen, screen, self.current_user):
2374 pass
2375 except Exception, e:
2376 rospy.logwarn("Error while show IO for %s: %s", utf8(screen), utf8(e))
2377 MessageBox.warning(self, "Show IO error",
2378 ''.join(['Error while show IO ', screen, ' on ', host]),
2379 utf8(e))
2380 finally:
2381 self.setCursor(cursor)
2382
2384 '''
2385 Shows log files of the selected nodes.
2386 '''
2387 try:
2388 only_screen = True
2389 key_mod = QApplication.keyboardModifiers()
2390 if (key_mod & Qt.ShiftModifier or key_mod & Qt.ControlModifier):
2391 only_screen = False
2392 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
2393 ret = True
2394 if len(selectedNodes) > 5:
2395 ret = MessageBox.question(self, "Show Log", "You are going to open the logs of " + utf8(len(selectedNodes)) + " nodes at once\nContinue?", buttons=MessageBox.Ok | MessageBox.Cancel)
2396 ret = (ret == MessageBox.Ok)
2397 if ret:
2398 for node in selectedNodes:
2399 self._progress_queue_prio.add2queue(utf8(uuid.uuid4()),
2400 ''.join(['show log of ', node.name]),
2401 nm.starter().openLog,
2402 (node.name, self.getHostFromNode(node), self.current_user, only_screen))
2403 self._start_queue(self._progress_queue_prio)
2404 except Exception, e:
2405 print traceback.format_exc(1)
2406 rospy.logwarn("Error while show log: %s", utf8(e))
2407 MessageBox.warning(self, "Show log error",
2408 'Error while show Log',
2409 utf8(e))
2410
2412 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
2413 nodenames = []
2414 for n in selectedNodes:
2415 nodenames.append(n.name)
2416 try:
2417 host = get_hostname(self.masteruri)
2418 path_on_host = nm.starter().get_log_path(host, nodenames, True)
2419 QApplication.clipboard().setText(''.join([getpass.getuser() if self.is_local else self.current_user, '@', host, ':', path_on_host]))
2420 except Exception as e:
2421 MessageBox.warning(self, "Get log path",
2422 'Error while get log path',
2423 utf8(e))
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2442 '''
2443 Deletes log files of the selected nodes.
2444 '''
2445 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
2446 for node in selectedNodes:
2447 self._progress_queue_prio.add2queue(utf8(uuid.uuid4()),
2448 ''.join(['delete Log of ', node.name]),
2449 nm.starter().deleteLog,
2450 (node.name, self.getHostFromNode(node), False, self.current_user))
2451 self._start_queue(self._progress_queue_prio)
2452
2454 '''
2455 Opens the dynamic configuration dialogs for selected nodes.
2456 '''
2457 if self.master_info is not None:
2458 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
2459 for n in selectedNodes:
2460 try:
2461 nodes = sorted([srv_name[:-len('/set_parameters')] for srv_name, srv in self.master_info.services.items() if (srv_name.endswith('/set_parameters') and n.name in srv.serviceProvider)])
2462 items = []
2463 if len(nodes) == 1:
2464 items = nodes
2465 elif len(nodes) > 1:
2466 items, _ = SelectDialog.getValue('Dynamic configuration selection', '', [i for i in nodes])
2467 if items is None:
2468 items = []
2469 if len(items) > 3:
2470 ret = MessageBox.question(self, 'Start dynamic reconfigure', 'It will starts %s dynamic reconfigure nodes?\n\n Are you sure?' % utf8(len(items)), buttons=MessageBox.Yes | MessageBox.No)
2471 if ret != MessageBox.Yes:
2472 return
2473 for node in items:
2474 env = dict(os.environ)
2475 env["ROS_MASTER_URI"] = utf8(self.master_info.masteruri)
2476 rospy.loginfo("Start dynamic reconfiguration for '%s'" % node)
2477 _ = SupervisedPopen(['rosrun', 'node_manager_fkie', 'dynamic_reconfigure', node, '__ns:=dynamic_reconfigure'], env=env, object_id=node, description='Start dynamic reconfiguration for %s failed' % node)
2478 except Exception, e:
2479 rospy.logwarn("Start dynamic reconfiguration for '%s' failed: %s" % (n.name, utf8(e)))
2480 MessageBox.warning(self, "Start dynamic reconfiguration error",
2481 'Start dynamic reconfiguration for %s failed!' % n.name,
2482 utf8(e))
2483
2485 '''
2486 '''
2487 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
2488 for node in selectedNodes:
2489 choices = self._getCfgChoises(node, True)
2490 choice, ok = self._getUserCfgChoice(choices, node.name)
2491 config = choices[choice] if choices and choice else ''
2492 if ok and isinstance(config, LaunchConfig):
2493
2494 node_cfg = config.getNode(node.name)
2495 files = [config.Filename]
2496 if node_cfg.filename not in files:
2497 files.append(node_cfg.filename)
2498 self.request_xml_editor.emit(files, ''.join(['name="', os.path.basename(node.name), '"']))
2499
2501 '''
2502 '''
2503 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
2504 for node in selectedNodes:
2505
2506 try:
2507 inputDia = MasterParameterDialog(node.masteruri if node.masteruri is not None else self.masteruri, ''.join([node.name, roslib.names.SEP]), parent=self)
2508 inputDia.setWindowTitle(' - '.join([os.path.basename(node.name), "parameter"]))
2509 if node.has_launch_cfgs(node.cfgs):
2510 inputDia.add_warning("The changes may not have any effect, because the launch file was also loaded as not 'default' and the parameter in the launch file will be reloaded on start of the ROS node.")
2511 inputDia.show()
2512 except:
2513 rospy.logwarn("Error on retrieve parameter for %s: %s", utf8(node.name), traceback.format_exc(1))
2514
2518
2520 '''
2521 Opens a dialog to select configurations to close or stop all nodes
2522 (with roscore) or shutdown the host.
2523 '''
2524 choices = dict()
2525
2526 for path, _ in self.__configs.items():
2527 if isinstance(path, tuple):
2528 if path[2] == self.masteruri:
2529 choices['DEFAULT CFG: %s' % path[0]] = path
2530 else:
2531 package = utf8(package_name(os.path.dirname(path))[0])
2532 choices['%s [%s]' % (os.path.basename(path), package)] = path
2533 cfg_items = choices.keys()
2534 cfg_items.sort()
2535 res = SelectDialog.getValue('Close/Stop/Shutdown', '',
2536 cfg_items, False, False,
2537 self, checkitem1='stop ROS',
2538 checkitem2='shutdown host')
2539 cfgs, _, stop_nodes, shutdown = res[0], res[1], res[2], res[3]
2540
2541 for config in cfgs:
2542 self._close_cfg(choices[config])
2543 if stop_nodes:
2544 self._on_stop_kill_roscore = True
2545
2546 ignore_nodes = [rospy.get_name(), '/master_discovery', '/rosout']
2547 self.stop_nodes_by_name(self.getRunningNodesIfLocal(), True, ignore_nodes)
2548 if shutdown:
2549 self.poweroff()
2550 else:
2551 self.stop_nodes_by_name(['/master_discovery'], True)
2552 self.stop_nodes_by_name(['/node_manager'], True)
2553 elif shutdown:
2554 self.poweroff()
2555 self.updateButtons()
2556 self.update_robot_icon()
2557
2559 try:
2560 if nm.is_local(self.mastername):
2561 ret = MessageBox.warning(self, "ROS Node Manager",
2562 "Do you really want to shutdown localhost?",
2563 buttons=MessageBox.Ok | MessageBox.Cancel)
2564 if ret == MessageBox.Cancel:
2565 return
2566 self._on_stop_poweroff = True
2567
2568
2569 self.stop_nodes_by_name(['/master_discovery'], True)
2570 self._progress_queue.add2queue(utf8(uuid.uuid4()),
2571 'poweroff `%s`' % self.mastername,
2572 nm.starter().poweroff,
2573 ('%s' % self.mastername,))
2574 self._start_queue(self._progress_queue)
2575 except (Exception, nm.StartException), emsg:
2576 rospy.logwarn("Error while poweroff %s: %s", self.mastername, utf8(emsg))
2577 MessageBox.warning(self, "Run error",
2578 'Error while poweroff %s' % self.mastername,
2579 '%s' % utf8(emsg))
2580
2582 try:
2583 self.removeConfigFromModel(cfg)
2584 if isinstance(cfg, tuple):
2585 if self.master_info is not None:
2586
2587 node = self.master_info.getNode(cfg[0])
2588 if node is not None:
2589 self.stop_nodes([node])
2590 else:
2591
2592 try:
2593 for _, machine in self.__configs[cfg].Roscfg.machines.items():
2594 if machine.name:
2595 nm.nameres().remove_info(machine.name, machine.address)
2596 except:
2597 pass
2598 del self.__configs[cfg]
2599 except:
2600 print traceback.format_exc(1)
2601 pass
2602
2604 '''
2605 Shows the output of the topic in a terminal.
2606 '''
2607 self._show_topic_output(False, use_ssh=False, topics=topics)
2608
2610 '''
2611 Shows the hz of the topic in a terminal.
2612 '''
2613 self._show_topic_output(True)
2614
2616 '''
2617 Shows the hz of the topic using ssh.
2618 '''
2619 self._show_topic_output(True, use_ssh=True)
2620
2622 selectedTopics = self.topicsFromIndexes(self.masterTab.topicsView.selectionModel().selectedIndexes())
2623 if len(selectedTopics) > 0:
2624 for topic in selectedTopics:
2625 if not self._start_publisher(topic.name, topic.type):
2626 break
2627 else:
2628
2629
2630 root_paths = [os.path.normpath(p) for p in os.getenv("ROS_PACKAGE_PATH").split(':')]
2631 packages = {}
2632 msg_types = []
2633 for p in root_paths:
2634 ret = get_packages(p)
2635 packages = dict(ret.items() + packages.items())
2636 for (p, direc) in packages.items():
2637 import rosmsg
2638 for f in rosmsg._list_types('%s/msg' % direc, 'msg', rosmsg.MODE_MSG):
2639 msg_types.append("%s/%s" % (p, f))
2640 msg_types.sort()
2641 fields = {'Type': ('string', msg_types), 'Name': ('string', [''])}
2642
2643
2644 dia = ParameterDialog(fields, parent=self)
2645 dia.setWindowTitle('Publish to topic')
2646 dia.setFilterVisible(False)
2647 dia.resize(300, 95)
2648 dia.setFocusField('Name')
2649 if dia.exec_():
2650 params = dia.getKeywords()
2651 try:
2652 if params['Name'] and params['Type']:
2653 try:
2654 self._start_publisher(params['Name'], params['Type'])
2655 except Exception, e:
2656 print traceback.format_exc(1)
2657 rospy.logwarn("Publish topic '%s' failed: %s", utf8(params['Name']), utf8(e))
2658 MessageBox.warning(self, "Publish topic error",
2659 ''.join(['Publish topic ', params['Name'], ' failed!']),
2660 utf8(e))
2661 else:
2662 MessageBox.warning(self, "Invalid name or type",
2663 "Can't publish to topic '%s' with type '%s'!" % (params['Name'], params['Type']))
2664 except (KeyError, ValueError), e:
2665 MessageBox.warning(self, "Warning",
2666 'Error while add a parameter to the ROS parameter server',
2667 utf8(e))
2668
2670 '''
2671 Starts a publisher to given topic.
2672 '''
2673 if self.master_info is not None:
2674 topic = self.master_info.getTopic("%s" % topic_name)
2675 if topic is not None:
2676 self._start_publisher(topic.name, topic.type, republish)
2677 else:
2678 rospy.logwarn("Error while start publisher, topic not found: %s" % topic_name)
2679
2681 try:
2682 topic_name = roslib.names.ns_join(roslib.names.SEP, topic_name)
2683 mclass = roslib.message.get_message_class(topic_type)
2684 if mclass is None:
2685 MessageBox.warning(self, "Publish error",
2686 'Error while publish to %s' % topic_name,
2687 ''.join(['invalid message type: ', topic_type, '.\nIf this is a valid message type, perhaps you need to run "rosmake"']))
2688 return
2689 slots = mclass.__slots__
2690 types = mclass._slot_types
2691 default_topic_values = {}
2692 rate_values = ['once', 'latch', '1']
2693 if republish and topic_name in self.__republish_params:
2694 default_topic_values = self.__republish_params[topic_name][topic_type]
2695 rate_values = self.__republish_params[topic_name]['! Publish rate']
2696 args = ServiceDialog._params_from_slots(slots, types, default_topic_values)
2697 p = {'! Publish rate': ('string', rate_values), topic_type: ('dict', args)}
2698 dia = ParameterDialog(p)
2699 dia.setWindowTitle(''.join(['Publish to ', topic_name]))
2700 dia.showLoadSaveButtons()
2701 dia.resize(450, 300)
2702 dia.setFocusField('! Publish rate')
2703
2704 if dia.exec_():
2705 params = dia.getKeywords()
2706
2707 self.__republish_params[topic_name] = params
2708 rate = params['! Publish rate']
2709 opt_str = ''
2710 opt_name_suf = '__latch_'
2711 if rate == 'latch':
2712 opt_str = ''
2713 elif rate == 'once' or rate == '-1':
2714 opt_str = '--once'
2715 opt_name_suf = '__once_'
2716 else:
2717 try:
2718 i = 0
2719 try:
2720 i = int(rate)
2721 except:
2722 i = float(rate)
2723 if i > 0:
2724 opt_str = ''.join(['-r ', rate])
2725 opt_name_suf = '__%sHz_' % (utf8(rate).replace('.', '_'))
2726 except:
2727 pass
2728
2729 topic_params = dict()
2730 if topic_type in params:
2731 topic_params = self._rem_empty_lists(params[topic_type])
2732 pub_cmd = ' '.join(['pub', topic_name, topic_type, '"', str(topic_params), '"', opt_str])
2733 self._progress_queue.add2queue(utf8(uuid.uuid4()),
2734 'start publisher for %s' % topic_name,
2735 nm.starter().runNodeWithoutConfig,
2736 (nm.nameres().address(self.masteruri), 'rostopic', 'rostopic', 'rostopic_pub%s%s%s' % (topic_name, opt_name_suf, str(rospy.Time.now())), [pub_cmd], self.masteruri, False, self.current_user))
2737 self._start_queue(self._progress_queue)
2738 return True
2739 else:
2740 return False
2741 except Exception, e:
2742 rospy.logwarn("Publish topic '%s' failed: %s", utf8(topic_name), utf8(e))
2743 MessageBox.warning(self, "Publish topic error",
2744 ''.join(['Publish topic ', topic_name, ' failed!']),
2745 utf8(e))
2746 print utf8(traceback.format_exc(1))
2747 return False
2748
2750 result = dict()
2751 for key, value in param_dict.iteritems():
2752 if isinstance(value, dict):
2753 result[key] = self._rem_empty_lists(value)
2754 elif not (isinstance(value, list) and not value):
2755 result[key] = value
2756 return result
2757
2759 topic_names = []
2760 if topic_name:
2761 topic_names.append(topic_name)
2762 else:
2763 selectedTopics = self.topicsFromIndexes(self.masterTab.topicsView.selectionModel().selectedIndexes())
2764 topic_names = ['%s' % topic.name for topic in selectedTopics]
2765 if self.master_info is not None:
2766 nodes2stop = []
2767 for topic in topic_names:
2768 topic_prefix = '/rostopic_pub%s_' % topic
2769 node_names = self.master_info.node_names
2770 for n in node_names:
2771 if n.startswith(topic_prefix):
2772 nodes2stop.append(n)
2773 self.stop_nodes_by_name(nodes2stop)
2774
2776 '''
2777 Shows the output of the topic in a terminal.
2778 '''
2779 selected_topics = topics
2780 if not selected_topics:
2781 selected_topics = self.topicsFromIndexes(self.masterTab.topicsView.selectionModel().selectedIndexes())
2782 ret = True
2783 if len(selected_topics) > 5:
2784 ret = MessageBox.question(self, "Show echo", "You are going to open the echo of " + utf8(len(selected_topics)) + " topics at once\nContinue?", buttons=MessageBox.Ok | MessageBox.Cancel)
2785 ret = (ret == MessageBox.Ok)
2786 if ret:
2787 for topic in selected_topics:
2788 self._add_topic_output2queue(topic, show_hz_only, use_ssh)
2789
2791 '''
2792 Shows the topic output in a new window.
2793 '''
2794 if self.master_info is not None:
2795 topic = self.master_info.getTopic("%s" % topic_name)
2796 if topic is not None:
2797 self._add_topic_output2queue(topic, show_hz_only, use_ssh)
2798 else:
2799 rospy.logwarn("topic not found: %s" % topic_name)
2800
2802 try:
2803
2804 import shlex
2805 env = dict(os.environ)
2806 env["ROS_MASTER_URI"] = utf8(self.masteruri)
2807 namespace = rospy.names.namespace(topic.name)
2808 nodename = os.path.basename(topic.name)
2809 namespace = 'echo_%s%s%s%s' % ('hz_' if show_hz_only else '', 'ssh_' if use_ssh else '', utf8(get_hostname(self.masteruri)), namespace)
2810 cmd = 'rosrun node_manager_fkie node_manager --echo %s %s %s %s __name:=%s __ns:=%s' % (topic.name, topic.type, '--hz' if show_hz_only else '', '--ssh' if use_ssh else '', nodename, namespace)
2811 rospy.loginfo("Echo topic: %s" % cmd)
2812 ps = SupervisedPopen(shlex.split(cmd), env=env, stderr=None, close_fds=True, object_id=topic.name, description='Echo topic: %s' % topic.name)
2813 ps.finished.connect(self._topic_dialog_closed)
2814 self.__echo_topics_dialogs[topic.name] = ps
2815 except Exception, e:
2816 rospy.logwarn("Echo topic '%s' failed: %s" % (topic.name, utf8(e)))
2817 MessageBox.warning(self, "Echo of topic error",
2818 'Echo of topic %s failed!' % topic.name,
2819 '%s' % utf8(e))
2820
2822 if topic_name in self.__echo_topics_dialogs:
2823 del self.__echo_topics_dialogs[topic_name]
2824
2826 '''
2827 calls a service.
2828 '''
2829 selected_services = services
2830 if not selected_services:
2831 selected_services = self.servicesFromIndexes(self.masterTab.servicesView.selectionModel().selectedIndexes())
2832 try:
2833 for service in selected_services:
2834 param = ServiceDialog(service, self)
2835 param.show()
2836 except Exception, e:
2837 rospy.logwarn("Call service '%s' failed: %s" % (service.name, utf8(e)))
2838 MessageBox.warning(self, "Call service error",
2839 'Call service %s failed!' % service.name,
2840 '%s' % utf8(e))
2841
2843 service = self.master_info.getService(utf8(service_name))
2844 if service is not None:
2845 try:
2846 param = ServiceDialog(service, self)
2847 param.show()
2848 except Exception, e:
2849 rospy.logwarn("Call service '%s' failed: %s" % (service.name, utf8(e)))
2850 MessageBox.warning(self, "Call service error",
2851 'Call service %s failed!' % service.name,
2852 '%s' % utf8(e))
2853
2855 '''
2856 Expand the first item and all selected items.
2857 '''
2858 tree_view.collapseAll()
2859 for selected in tree_view.selectionModel().selectedIndexes():
2860 index = selected
2861 while index is not None and index.isValid():
2862 item = proxy_model.sourceModel().itemFromIndex(index)
2863 if type(item) in [TopicGroupItem, ServiceGroupItem, GroupItem] and not tree_view.isExpanded(index):
2864 tree_view.setExpanded(index, True)
2865 tree_view.setExpanded(index, True)
2866 index = index.parent()
2867
2868 for root_idx in range(proxy_model.sourceModel().rowCount()):
2869 source_index = proxy_model.sourceModel().index(root_idx, 0)
2870 item = proxy_model.sourceModel().itemFromIndex(source_index)
2871 if type(item) in [HostItem] and not item._local:
2872 continue
2873 mapped_index = proxy_model.mapFromSource(source_index)
2874 tree_view.setExpanded(mapped_index, True)
2875
2877 '''
2878 Filter the displayed nodes
2879 '''
2880 self.node_proxy_model.setFilterRegExp(QRegExp(text, Qt.CaseInsensitive, QRegExp.Wildcard))
2881 if text:
2882 self.masterTab.nodeTreeView.expandAll()
2883 else:
2884 self._restore_expand_state(self.masterTab.nodeTreeView, self.node_proxy_model)
2885
2887 '''
2888 Filter the displayed topics
2889 '''
2890 self.topic_proxyModel.setFilterRegExp(QRegExp(text, Qt.CaseInsensitive, QRegExp.Wildcard))
2891 if text:
2892 self.masterTab.topicsView.expandAll()
2893 else:
2894 self._restore_expand_state(self.masterTab.topicsView, self.topic_proxyModel)
2895
2897 '''
2898 Filter the displayed services
2899 '''
2900 self.service_proxyModel.setFilterRegExp(QRegExp(text, Qt.CaseInsensitive, QRegExp.Wildcard))
2901 if text:
2902 self.masterTab.servicesView.expandAll()
2903 else:
2904 self._restore_expand_state(self.masterTab.servicesView, self.service_proxyModel)
2905
2907 '''
2908 Filter the displayed parameter
2909 '''
2910 self.parameter_proxyModel.setFilterRegExp(QRegExp(text, Qt.CaseInsensitive, QRegExp.Wildcard))
2911
2917
2919 '''
2920 Adds a parameter to the ROS parameter server.
2921 '''
2922 selectedParameter = self.parameterFromIndexes(self.masterTab.parameterView.selectionModel().selectedIndexes())
2923 ns = '/'
2924 if selectedParameter:
2925 ns = roslib.names.namespace(selectedParameter[0][0])
2926 fields = {'name': ('string', ns), 'type': ('string', ['string', 'int', 'float', 'bool', 'list']), 'value': ('string', '')}
2927 newparamDia = ParameterDialog(fields, parent=self)
2928 newparamDia.setWindowTitle('Add new parameter')
2929 newparamDia.setFilterVisible(False)
2930 newparamDia.resize(400, 120)
2931 newparamDia.accepted.connect(self._on_add_parameter_accepted)
2932 newparamDia.setFocusField('name')
2933 newparamDia.show()
2934 newparamDia.raise_()
2935 newparamDia.activateWindow()
2936
2938 if isinstance(self.sender(), ParameterDialog):
2939 params = self.sender().getKeywords()
2940 try:
2941 if params['type'] == 'int':
2942 value = int(params['value'])
2943 elif params['type'] == 'float':
2944 value = float(params['value'])
2945 elif params['type'] == 'bool':
2946 value = bool(params['value'].lower() in ("yes", "true", "t", "1"))
2947 elif params['type'] == 'list':
2948 try:
2949 import yaml
2950 value = [yaml.load(params['value'])]
2951
2952
2953
2954 if value is None:
2955 value = []
2956 except yaml.MarkedYAMLError, e:
2957 MessageBox.warning(self, self.tr("Warning"), "yaml error: %s" % utf8(e), buttons=MessageBox.Ok)
2958 return
2959 else:
2960 value = params['value']
2961 self.parameterHandler.deliverParameter(self.masteruri, {params['name']: value})
2962 self.parameterHandler.requestParameterList(self.masteruri)
2963 self.sender().close()
2964 except (KeyError, ValueError), e:
2965 MessageBox.warning(self, "Warning",
2966 'Error while add a parameter to the ROS parameter server',
2967 utf8(e))
2968
2970 '''
2971 Deletes the parameter from the ROS parameter server.
2972 '''
2973 selectedParameter = self.parameterFromIndexes(self.masterTab.parameterView.selectionModel().selectedIndexes())
2974 try:
2975 socket.setdefaulttimeout(15)
2976 name = rospy.get_name()
2977 master = xmlrpclib.ServerProxy(self.masteruri)
2978 master_multi = xmlrpclib.MultiCall(master)
2979 for (key, _) in selectedParameter:
2980 master_multi.deleteParam(name, key)
2981 r = master_multi()
2982 for code, msg, parameter in r:
2983 if code != 1:
2984 rospy.logwarn("Error on delete parameter '%s': %s", parameter, msg)
2985 except:
2986 rospy.logwarn("Error on delete parameter: %s", utf8(traceback.format_exc(1)))
2987 MessageBox.warning(self, "Warning",
2988 'Error while delete a parameter to the ROS parameter server',
2989 utf8(traceback.format_exc(1)))
2990 else:
2991 self.on_get_parameter_clicked()
2992 finally:
2993 socket.setdefaulttimeout(None)
2994
2996 '''
2997 Stores selected parameter to a file.
2998 '''
2999 selectedParameter = self.parameterFromIndexes(self.masterTab.parameterView.selectionModel().selectedIndexes())
3000 if selectedParameter:
3001
3002 (fileName, _) = QFileDialog.getSaveFileName(self,
3003 "Save parameter",
3004 nm.settings().current_dialog_path,
3005 "YAML files (*.yaml);;All files (*)")
3006 if fileName:
3007 nm.settings().current_dialog_path = os.path.dirname(fileName)
3008 try:
3009 with open(fileName, 'w+') as f:
3010 values = dict()
3011
3012 for (key, value) in selectedParameter:
3013 keys = key.strip(rospy.names.SEP).split(rospy.names.SEP)
3014 curr_v = values
3015 for k in keys:
3016 if k in curr_v:
3017 curr_v = curr_v[k]
3018 elif k != keys[-1]:
3019 curr_v[k] = dict()
3020 curr_v = curr_v[k]
3021 else:
3022 curr_v[k] = value
3023 import yaml
3024
3025 f.write(yaml.dump(values, default_flow_style=False))
3026 except Exception as e:
3027 print utf8(traceback.format_exc(1))
3028 MessageBox.warning(self, "Save parameter Error",
3029 'Error while save parameter',
3030 utf8(e))
3031
3033 '''
3034 used to avoid the adding of \\ for each \ in a string of a list
3035 '''
3036 if liste and isinstance(liste, list):
3037 result = []
3038 for l in liste:
3039 val = l
3040 if isinstance(l, (str, unicode)):
3041 val = l.replace("\\n", "\n")
3042
3043 elif isinstance(l, list):
3044 val = self._replaceDoubleSlash(l)
3045 result.append(val)
3046 return result
3047 return liste
3048
3050 '''
3051 add changes to the ROS parameter server
3052 '''
3053 if isinstance(item, ParameterValueItem):
3054 try:
3055 if isinstance(item.value, bool):
3056 value = bool(item.text().lower() in ("yes", "true", "t", "1"))
3057 elif isinstance(item.value, int):
3058 value = int(item.text())
3059 elif isinstance(item.value, float):
3060 value = float(item.text())
3061 elif isinstance(item.value, list):
3062 try:
3063 import yaml
3064 value = yaml.load(item.text())
3065
3066
3067
3068 if value is None:
3069 value = []
3070 value = self._replaceDoubleSlash(value)
3071 except yaml.MarkedYAMLError, e:
3072 MessageBox.warning(self, self.tr("Warning"), "yaml error: %s" % utf8(e), buttons=MessageBox.Ok)
3073 item.setText(utf8(item.value))
3074 return
3075 else:
3076 value = item.text()
3077 self.parameterHandler.deliverParameter(self.masteruri, {item.name: value})
3078 item.value = value
3079 except ValueError, e:
3080 MessageBox.warning(self, "Warning",
3081 'Error while add changes to the ROS parameter server',
3082 utf8(e))
3083 item.setText(item.value)
3084
3086 '''
3087 @param masteruri: The URI of the ROS parameter server
3088 @type masteruri: C{str}
3089 @param code: The return code of the request. If not 1, the message is set and the list can be ignored.
3090 @type code: C{int}
3091 @param msg: The message of the result.
3092 @type msg: C{str}
3093 @param params: The list the parameter names.
3094 @type params: C{[str]}
3095 '''
3096 if code == 1:
3097 params.sort()
3098 self.parameterHandler.requestParameterValues(masteruri, params)
3099
3101 '''
3102 @param masteruri: The URI of the ROS parameter server
3103 @type masteruri: C{str}
3104 @param code: The return code of the request. If not 1, the message is set and the list can be ignored.
3105 @type code: C{int}
3106 @param msg: The message of the result.
3107 @type msg: C{str}
3108 @param params: The dictionary the parameter names and request result.
3109 @type params: C{dict(paramName : (code, statusMessage, parameterValue))}
3110 '''
3111 if code == 1:
3112 result = {}
3113 for p, (code_n, _, val) in params.items():
3114 if code_n == 1:
3115 result[p] = val
3116 else:
3117 result[p] = ''
3118 if p == '/use_sim_time':
3119 self.__use_sim_time = (code_n == 1 and val)
3120 self.parameter_model.updateModelData(result)
3121 else:
3122 rospy.logwarn("Error on retrieve parameter from %s: %s", utf8(masteruri), utf8(msg))
3123
3125 '''
3126 @param masteruri: The URI of the ROS parameter server
3127 @type masteruri: C{str}
3128 @param code: The return code of the request. If not 1, the message is set and the list can be ignored.
3129 @type code: C{int}
3130 @param msg: The message of the result.
3131 @type msg: C{str}
3132 @param params: The dictionary the parameter names and request result.
3133 @type params: C{dict(paramName : (code, statusMessage, parameterValue))}
3134 '''
3135 errmsg = ''
3136 if code == 1:
3137 for p, (code_n, msg, _) in params.items():
3138 if code_n != 1:
3139 errmsg = '%s: %s\n%s' % (p, errmsg, msg)
3140 else:
3141 errmsg = msg if msg else 'Unknown error on set parameter'
3142 if errmsg:
3143 MessageBox.warning(self, "Warning",
3144 'Error while delivering parameter to the ROS parameter server',
3145 utf8(errmsg))
3146
3148 '''
3149 @param masteruri: The URI of the ROS parameter server
3150 @type masteruri: C{str}
3151 @param code: The return code of the request. If not 1, the message is set and the list can be ignored.
3152 @type code: C{int}
3153 @param msg: The message of the result.
3154 @type msg: C{str}
3155 @param params: The dictionary the parameter names and request result.
3156 @type params: C{dict(paramName : (code, statusMessage, parameterValue))}
3157 '''
3158 robot_icon_found = False
3159 if code == 1:
3160 for p, (code_n, _, val) in params.items():
3161 if p == '/use_sim_time':
3162 self.__use_sim_time = (code_n == 1 and val)
3163 elif p == '/robot_icon':
3164 robot_icon_found = True
3165 self.__current_parameter_robot_icon = val if code_n == 1 else ''
3166 self.update_robot_icon()
3167 elif p.startswith('/roslaunch/uris'):
3168 if code_n == 1:
3169 for _, value in val.items():
3170 self.launch_server_handler.updateLaunchServerInfo(value)
3171 elif p == "/run_id":
3172 if self.__run_id != val:
3173 self.__run_id = val
3174
3175 for _, launch_cfg in self.__configs.items():
3176 try:
3177 launch_cfg.global_param_done.remove(masteruri)
3178 except ValueError:
3179 pass
3180 else:
3181 rospy.logwarn("Error on retrieve sim parameter value from %s: %s", utf8(masteruri), utf8(msg))
3182 if not robot_icon_found:
3183 self.__current_parameter_robot_icon = ''
3184 self.update_robot_icon()
3185
3187 '''
3188 Requests the ROS master URI from the ROS master through the RPC interface and
3189 returns it. The 'materuri' attribute will be set to the requested value.
3190 @return: ROS master URI
3191 @rtype: C{str} or C{None}
3192 '''
3193 if not hasattr(self, '_nm_materuri') or self._nm_materuri is None:
3194 masteruri = masteruri_from_ros()
3195 master = xmlrpclib.ServerProxy(masteruri)
3196 _, _, self._nm_materuri = master.getUri(rospy.get_name())
3197 return self._nm_materuri
3198
3213
3214
3215
3216
3217
3220
3222 if questionid == MessageFrame.TYPE_NODELET:
3223 try:
3224 for cfgs, nodes in data.data.iteritems():
3225 self.stop_nodes_by_name(nodes)
3226 self.start_nodes_by_name(nodes, cfgs, force=True, check_nodelets=False)
3227 except Exception as err:
3228 rospy.logwarn("Error while start nodelets: %s" % utf8(err))
3229 elif questionid == MessageFrame.TYPE_LAUNCH_FILE:
3230 try:
3231 self.launchfiles = data.data
3232 except Exception as err:
3233 rospy.logwarn("Error while reload launch file %s: %s" % (data.data, utf8(err)))
3234 MessageBox.warning(self, "Loading launch file", data.data, '%s' % utf8(err))
3235 elif questionid == MessageFrame.TYPE_TRANSFER:
3236 try:
3237 host = '%s' % get_hostname(self.masteruri)
3238 username = self.current_user
3239 self.main_window.launch_dock.progress_queue.add2queue(utf8(uuid.uuid4()),
3240 'transfer %s to %s' % (host, data.data),
3241 nm.starter().transfer_files,
3242 (host, data.data, False, username))
3243 self.main_window.launch_dock.progress_queue.start()
3244 except Exception as err:
3245 rospy.logwarn("Error while transfer changed files %s: %s" % (data.data, utf8(err)))
3246 MessageBox.warning(self, "Loading launch file", data.data, '%s' % utf8(err))
3247
3250
3251
3252
3253
3254
3256 '''
3257 Selects all nodes of a host with given index
3258 @param index: the index of the host in the tree model
3259 @type index: C{int}
3260 '''
3261 root = self.masterTab.nodeTreeView.model().index(index, 0)
3262 if not root.isValid():
3263 return
3264 self.masterTab.nodeTreeView.expand(root)
3265
3266 last_row_index = len(self.node_tree_model.header) - 1
3267
3268 i = 0
3269 selection = QItemSelection()
3270 while root.child(i, 0).isValid():
3271 index = root.child(i, 0)
3272 model_index = self.node_proxy_model.mapToSource(index)
3273 item = self.node_tree_model.itemFromIndex(model_index)
3274 if item is not None and not self._is_in_ignore_list(item.name):
3275 selection.append(QItemSelectionRange(index, root.child(i, last_row_index)))
3276 i = i + 1
3277
3278 self.masterTab.nodeTreeView.selectionModel().select(selection, QItemSelectionModel.ClearAndSelect)
3279
3281 for i in self._stop_ignores:
3282 if name.endswith(i):
3283 return True
3284 return False
3285
3288
3291
3294
3297
3300
3302 self.masterTab.nodeTreeView.selectionModel().clearSelection()
3303 self.masterTab.nodeTreeView.collapseAll()
3304
3306 result = ''
3307 if self.masterTab.nodeTreeView.hasFocus():
3308 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
3309 for node in selectedNodes:
3310 try:
3311 result = '%s %s' % (result, node.name)
3312 except Exception:
3313 pass
3314 elif self.masterTab.topicsView.hasFocus():
3315 selectedTopics = self.topicsFromIndexes(self.masterTab.topicsView.selectionModel().selectedIndexes())
3316 for topic in selectedTopics:
3317 try:
3318 result = '%s %s' % (result, topic.name)
3319 except Exception:
3320 pass
3321 elif self.masterTab.servicesView.hasFocus():
3322 selectedServices = self.servicesFromIndexes(self.masterTab.servicesView.selectionModel().selectedIndexes())
3323 for service in selectedServices:
3324 try:
3325 result = '%s %s' % (result, service.name)
3326 except Exception:
3327 pass
3328 elif self.masterTab.parameterView.hasFocus():
3329 selectedParameter = self.parameterFromIndexes(self.masterTab.parameterView.selectionModel().selectedIndexes())
3330 for (name, _value) in selectedParameter:
3331 try:
3332 result = '%s %s' % (result, name)
3333 except Exception:
3334 pass
3335 QApplication.clipboard().setText(result.strip())
3336
3338 result = ''
3339 if self.masterTab.nodeTreeView.hasFocus():
3340 selectedNodes = self.nodesFromIndexes(self.masterTab.nodeTreeView.selectionModel().selectedIndexes())
3341 for node in selectedNodes:
3342 try:
3343 result = '%s %s' % (result, node.pid)
3344 except Exception:
3345 pass
3346 elif self.masterTab.topicsView.hasFocus():
3347 selectedTopics = self.topicsFromIndexes(self.masterTab.topicsView.selectionModel().selectedIndexes())
3348 for topic in selectedTopics:
3349 try:
3350 result = '%s %s' % (result, topic.type)
3351 except Exception:
3352 pass
3353 elif self.masterTab.servicesView.hasFocus():
3354 selectedServices = self.servicesFromIndexes(self.masterTab.servicesView.selectionModel().selectedIndexes())
3355 for service in selectedServices:
3356 try:
3357 result = '%s %s' % (result, service.type)
3358 except Exception:
3359 pass
3360 elif self.masterTab.parameterView.hasFocus():
3361 selectedParameter = self.parameterFromIndexes(self.masterTab.parameterView.selectionModel().selectedIndexes())
3362 for (_, value) in selectedParameter:
3363 try:
3364 result = '%s %s' % (result, value)
3365 except Exception:
3366 pass
3367 QApplication.clipboard().setText(result.strip())
3368
3375
3377 '''
3378 Perform filtering on column 0 (Name)
3379 '''
3380 if not self.filterRegExp().pattern():
3381 return True
3382 if (self.filterAcceptsRowItself(sourceRow, sourceParent)):
3383 return True
3384
3385
3386
3387
3388
3389
3390
3391 if (self.hasAcceptedChildren(sourceRow, sourceParent)):
3392 return True
3393 return False
3394
3396 index = self.sourceModel().index(sourceRow, 0, sourceParent)
3397 if not index.isValid():
3398 return False
3399
3400 childCount = index.model().rowCount(index)
3401 if childCount == 0:
3402 return False
3403 for i in range(childCount):
3404 if (self.filterAcceptsRowItself(i, index)):
3405 return True
3406
3407 if (self.hasAcceptedChildren(i, index)):
3408 return True
3409 return False
3410
3412 index0 = self.sourceModel().index(sourceRow, 0, sourceParent)
3413 item = self.sourceModel().data(index0)
3414 if item is not None:
3415
3416 if '{' not in item:
3417 regex = self.filterRegExp()
3418 return (regex.indexIn(self.sourceModel().data(index0)) != -1)
3419 return False
3420
3423
3425 '''
3426 Perform filtering on columns 0 and 3 (Name, Type)
3427 '''
3428 result = True
3429 index0 = self.sourceModel().index(sourceRow, 0, sourceParent)
3430 regex = self.filterRegExp()
3431 item = self.sourceModel().itemFromIndex(index0)
3432 if type(item) == TopicItem:
3433 result = (regex.indexIn(item.topic.name) != -1 or regex.indexIn(item.topic_type_str) != -1)
3434 elif type(item) == TopicGroupItem:
3435 result = True
3436 if regex.indexIn(item.name) != -1:
3437 result = True
3438 else:
3439 sitems = item.get_topic_items()
3440 for sitem in sitems:
3441 result = (regex.indexIn(sitem.topic.name) != -1 or regex.indexIn(sitem.topic_type_str) != -1)
3442 if result:
3443 break
3444 return result
3445
3448
3450 '''
3451 Perform filtering on columns 0 and 1 (Name, Type)
3452 '''
3453 index0 = self.sourceModel().index(sourceRow, 0, sourceParent)
3454 regex = self.filterRegExp()
3455 item = self.sourceModel().itemFromIndex(index0)
3456 if type(item) == ServiceItem:
3457 return (regex.indexIn(item.service.name) != -1 or regex.indexIn(item.service_type_str) != -1)
3458 elif type(item) == ServiceGroupItem:
3459 if regex.indexIn(item.name) != -1:
3460 return True
3461 grp_res = True
3462 sitems = item.get_service_items()
3463 for sitem in sitems:
3464 res = (regex.indexIn(sitem.service.name) != -1 or regex.indexIn(sitem.service_type_str) != -1)
3465 if res:
3466 return True
3467 grp_res = res
3468 return grp_res
3469 return True
3470
3473
3475 '''
3476 Perform filtering on columns 0 and 1 (Name, Value)
3477 '''
3478 index0 = self.sourceModel().index(sourceRow, 0, sourceParent)
3479
3480 index2 = self.sourceModel().index(sourceRow, 2, sourceParent)
3481 regex = self.filterRegExp()
3482 return (regex.indexIn(self.sourceModel().data(index0, ParameterNameItem.NAME_ROLE)) != -1 or
3483 regex.indexIn(self.sourceModel().data(index2, ParameterValueItem.VALUE_ROLE)) != -1)
3484
3487
3488 - def __init__(self, parent=None, *args):
3489 QItemDelegate.__init__(self, parent, *args)
3490 self._idx_icon = 1
3491 self.IMAGES = {'launchfile': QImage(':/icons/crystal_clear_launch_file.png').scaled(15, 15, Qt.IgnoreAspectRatio, Qt.SmoothTransformation),
3492 'defaultcfg': QImage(":/icons/default_cfg.png").scaled(15, 15, Qt.IgnoreAspectRatio, Qt.SmoothTransformation),
3493 'nodelet': QImage(":/icons/crystal_clear_nodelet.png").scaled(15, 15, Qt.IgnoreAspectRatio, Qt.SmoothTransformation),
3494 'nodelet_mngr': QImage(":/icons/crystal_clear_nodelet_mngr.png").scaled(15, 15, Qt.IgnoreAspectRatio, Qt.SmoothTransformation),
3495 'warning': QImage(':/icons/crystal_clear_warning.png').scaled(15, 15, Qt.IgnoreAspectRatio, Qt.SmoothTransformation),
3496 'noscreen': QImage(':/icons/crystal_clear_no_io.png').scaled(15, 15, Qt.IgnoreAspectRatio, Qt.SmoothTransformation),
3497 'misc': QImage(':/icons/crystal_clear_miscellaneous.png').scaled(15, 15, Qt.IgnoreAspectRatio, Qt.SmoothTransformation),
3498 'group': QImage(':/icons/crystal_clear_group.png').scaled(15, 15, Qt.IgnoreAspectRatio, Qt.SmoothTransformation)
3499 }
3500
3501 - def paint(self, painter, option, index):
3502 painter.save()
3503 self._idx_icon = 1
3504 model_index = index.model().mapToSource(index)
3505 item = model_index.model().itemFromIndex(model_index)
3506 if isinstance(item, CellItem):
3507 if isinstance(item.item, NodeItem):
3508 if not item.item.has_screen:
3509 rect = self.calcDecorationRect(option.rect)
3510 painter.drawImage(rect, self.IMAGES['noscreen'])
3511 lcfgs = item.item.count_launch_cfgs()
3512 if lcfgs > 0:
3513 rect = self.calcDecorationRect(option.rect)
3514 painter.drawImage(rect, self.IMAGES['launchfile'])
3515 if lcfgs > 1:
3516 painter.drawText(rect, Qt.AlignCenter, str(lcfgs))
3517 dcfgs = item.item.count_default_cfgs()
3518 if dcfgs > 0:
3519 rect = self.calcDecorationRect(option.rect)
3520 painter.drawImage(rect, self.IMAGES['defaultcfg'])
3521 if dcfgs > 1:
3522 painter.drawText(rect, Qt.AlignCenter, str(dcfgs))
3523 if item.item.nodelets:
3524 rect = self.calcDecorationRect(option.rect)
3525 painter.drawImage(rect, self.IMAGES['nodelet_mngr'])
3526 if item.item.nodelet_mngr:
3527 rect = self.calcDecorationRect(option.rect)
3528 painter.drawImage(rect, self.IMAGES['nodelet'])
3529 if item.item.nodelets:
3530 item.setToolTip("This is a nodelet manager")
3531 elif item.item.nodelet_mngr:
3532 item.setToolTip("This is a nodelet for %s" % item.item.nodelet_mngr)
3533 else:
3534 item.setToolTip("")
3535 elif isinstance(item.item, GroupItem):
3536 llcfgs, ddcfgs = item.item.get_configs()
3537 lcfgs = len(llcfgs)
3538 dcfgs = len(ddcfgs)
3539 rect = self.calcDecorationRect(option.rect)
3540 painter.drawImage(rect, self.IMAGES['group'])
3541 count_nodes = item.item.count_nodes()
3542 if count_nodes > 1:
3543 painter.drawText(rect, Qt.AlignCenter, str(count_nodes))
3544 if lcfgs > 0:
3545 rect = self.calcDecorationRect(option.rect)
3546 painter.drawImage(rect, self.IMAGES['launchfile'])
3547 if lcfgs > 1:
3548 painter.drawText(rect, Qt.AlignCenter, str(lcfgs))
3549 if dcfgs > 0:
3550 rect = self.calcDecorationRect(option.rect)
3551 painter.drawImage(rect, self.IMAGES['defaultcfg'])
3552 if dcfgs > 1:
3553 painter.drawText(rect, Qt.AlignCenter, str(dcfgs))
3554 painter.restore()
3555
3557 rect = QRect()
3558 rect.setX(main_rect.x() + self._idx_icon)
3559 rect.setY(main_rect.y() + 1)
3560 rect.setWidth(15)
3561 rect.setHeight(15)
3562 self._idx_icon += 17
3563 return rect
3564