def delete_log(cls, nodename, grpc_uri, auto_pw_request=False, user=None, pw=None): ''' Deletes the log file associated with the given node. :param str nodename: the name of the node (with name space) :param str grpc_uri: uri of the node manager daemon where to delete log :raise Exception: on errors while resolving host :see: :meth:`fkie_node_manager.is_local()` ''' try: nm.nmd().screen.delete_log(grpc_uri, [nodename]) except Exception as err: rospy.logwarn("delete log using SSH because of error: %s" % utf8(err)) host = get_hostname(grpc_uri) if nm.is_local(host): screenLog = screen.get_logfile(node=nodename) pidFile = screen.get_pidfile(node=nodename) roslog = screen.get_ros_logfile(nodename) if os.path.isfile(screenLog): os.remove(screenLog) if os.path.isfile(pidFile): os.remove(pidFile) if os.path.isfile(roslog): os.remove(roslog) else: try: # output ignored: output, error, ok _, stdout, _, ok = nm.ssh().ssh_exec(host, [nm.settings().start_remote_script, '--delete_logs', nodename], user, pw, auto_pw_request, close_stdin=True, close_stdout=False, close_stderr=True) if ok: stdout.readlines() stdout.close() except nm.AuthenticationRequest as e: raise nm.InteractionNeededError(e, cls.delete_log, {'nodename': nodename, 'grpc_uri': host, 'auto_pw_request': auto_pw_request, 'user': user, 'pw': pw})
def mastername(self, masteruri, address=None): with self.mutex: for m in self._masters: if m.masteruri == masteruri: if address is not None: if m.has_address(address): return m.get_mastername() else: return m.get_mastername() return get_hostname(masteruri)
def _prepareROSMaster(cls, masteruri): if masteruri is None: masteruri = masteruri_from_ros() # start roscore, if needed try: if not os.path.isdir(screen.LOG_PATH): os.makedirs(screen.LOG_PATH) socket.setdefaulttimeout(3) master = xmlrpcclient.ServerProxy(masteruri) master.getUri(rospy.get_name()) # restart ROSCORE on different masteruri?, not now... # master_uri = master.getUri(rospy.get_name()) # if masteruri != master_uri[2]: # # kill the local roscore... # raise except Exception: # run a roscore master_host = get_hostname(masteruri) if nm.is_local(master_host, True): master_port = get_port(masteruri) new_env = dict(os.environ) new_env['ROS_MASTER_URI'] = masteruri ros_hostname = nmdhost.get_ros_hostname(masteruri) if ros_hostname: new_env['ROS_HOSTNAME'] = ros_hostname cmd_args = '%s roscore --port %d' % (screen.get_cmd('/roscore--%d' % master_port), master_port) for n in [1, 2, 3, 4]: try: if n == 1: print("Launch ROS Master in screen ... %s" % (cmd_args)) SupervisedPopen(shlex.split(cmd_args), env=new_env, object_id="ROSCORE", description="Start roscore") elif n == 2: print("ROS Master takes too long for start, wait for next 10 sec ...") elif n == 3: print("A really slow start, wait for last 10 sec ...") # wait for roscore to avoid connection problems while init_node result = -1 count = 1 while result == -1 and count < 11: try: master = xmlrpcclient.ServerProxy(masteruri) result, _, _ = master.getUri(rospy.get_name()) # _:=uri, msg return except Exception: time.sleep(1) count += 1 if n == 4 and count >= 11: raise StartException('Cannot connect to ROS-Master: %s\n--> please run "roscore" manually!' % utf8(masteruri)) except Exception as e: raise Exception("Error while call '%s': %s" % (cmd_args, utf8(e))) else: raise Exception("ROS master '%s' is not reachable" % masteruri) finally: socket.setdefaulttimeout(None)
def _prepareROSMaster(cls, masteruri): if not masteruri: masteruri = masteruri_from_ros() # start roscore, if needed try: if not os.path.isdir(screen.LOG_PATH): os.makedirs(screen.LOG_PATH) socket.setdefaulttimeout(3) master = xmlrpcclient.ServerProxy(masteruri) master.getUri(rospy.get_name()) except Exception: # run a roscore screen.test_screen() master_host = get_hostname(masteruri) if cls.is_local(master_host, True): print("Start ROS-Master with %s ..." % masteruri) master_port = get_port(masteruri) new_env = dict(os.environ) new_env['ROS_MASTER_URI'] = masteruri ros_hostname = nmdhost.get_ros_hostname(masteruri) if ros_hostname: new_env['ROS_HOSTNAME'] = ros_hostname cmd_args = '%s roscore --port %d' % (screen.get_cmd( '/roscore--%d' % master_port), master_port) try: subprocess.Popen(shlex.split(cmd_args), env=new_env) # wait for roscore to avoid connection problems while init_node result = -1 count = 1 while result == -1 and count < 11: try: print(" retry connect to ROS master %d/10" % count) master = xmlrpcclient.ServerProxy(masteruri) result, _, _ = master.getUri( rospy.get_name()) # _:=uri, msg except Exception: time.sleep(1) count += 1 if count >= 11: raise StartException( 'Cannot connect to the ROS-Master: ' + str(masteruri)) except Exception as e: import sys sys.stderr.write("%s\n" % e) raise else: raise Exception("ROS master '%s' is not reachable" % masteruri) finally: socket.setdefaulttimeout(None)
def transfer_file_nmd(cls, grpc_url, path, auto_pw_request=False, user=None, pw=None): ''' Copies the given file to the remote host. Uses caching of remote paths. :param str grpc_url: destination grpc server :param str path: file to transfer ''' try: nm.nmd().file.copy(path, grpc_url) except Exception as err: host = get_hostname(grpc_url) _uri, path = nmdurl.split(path) rospy.logwarn("use SSH to transfer file '%s' to '%s', because of error: %s" % (path, host, utf8(err))) cls.transfer_files(host, path, auto_pw_request, user, pw)
def _rosclean_wo(self, grpc_uri, auto_pw_request=False, user=None, pw=None): try: nm.nmd().screen.rosclean(grpc_uri) except Exception as err: host = get_hostname(grpc_uri) if nm.is_local(host): rospy.loginfo("rosclean purge on localhost!") cmd = nm.settings().terminal_cmd(['rosclean purge -y'], "rosclean") SupervisedPopen(shlex.split(cmd), object_id="rosclean", description="rosclean") else: rospy.logwarn("use SSH to run 'rosclean' because of error: %s" % utf8(err)) # kill on a remote machine cmd = ['rosclean purge -y'] _ = nm.ssh().ssh_x11_exec(host, cmd, 'rosclean purge on %s' % host, user)
def __gt__(self, item): if isstring(item): local = False try: local = nm.is_local(get_hostname(nm.nameres().masteruri(item))) except Exception: pass if self.local and not local: # local hosts are at the top return False return self.master.name.lower() > item.lower() elif not (item is None): if self.local and not item.local: # local hosts are at the top return False return self.master.name.lower() > item.master.name.lower() return False
def __init__(self): ''' Creates a new instance. Find the topic of the master_discovery node using U{fkie_master_discovery.interface_finder.get_changes_topic() <http://docs.ros.org/api/fkie_master_discovery/html/modules.html#interface-finder-module>}. Also the parameter C{~ignore_hosts} will be analyzed to exclude hosts from sync. ''' self.masters = {} # the connection to the local service master self.masteruri = masteruri_from_master() self.hostname = get_hostname(self.masteruri) self._localname = '' '''@ivar: the ROS master URI of the C{local} ROS master. ''' self.__lock = threading.RLock() # load interface self._load_interface() # subscribe to changes notifier topics self._check_host = rospy.get_param('~check_host', True) topic_names = interface_finder.get_changes_topic( masteruri_from_master(), check_host=self._check_host) self.sub_changes = dict() '''@ivar: `dict` with topics {name: U{rospy.Subscriber<http://docs.ros.org/api/rospy/html/rospy.topics.Subscriber-class.html>}} publishes the changes of the discovered ROS masters.''' for topic_name in topic_names: rospy.loginfo("listen for updates on %s", topic_name) self.sub_changes[topic_name] = rospy.Subscriber( topic_name, MasterState, self._rosmsg_callback_master_state) self.__timestamp_local = None self.__own_state = None self.update_timer = None self.resync_timer = None self.own_state_getter = None self._timer_update_diagnostics = None self._join_threads = dict( ) # threads waiting for stopping the sync thread # initialize the ROS services rospy.Service('~get_sync_info', GetSyncInfo, self._rosservice_get_sync_info) rospy.on_shutdown(self.finish) self._current_diagnistic_level = None self.pub_diag = rospy.Publisher("/diagnostics", DiagnosticArray, queue_size=10, latch=True) self.obtain_masters()
def __get_ip(self): try: # get the IP of the master uri result = getaddrinfo(get_hostname(self.master.uri), None) ips = [] for r in result: if r[0] == AF_INET6: (_family, _socktype, _proto, _canonname, (ip, _port, _flow, _scope)) = r else: (_family, _socktype, _proto, _canonname, (ip, _port)) = r if self.master_ip is None and ip: self.master_ip = '' if ip and ip not in ips: self.master_ip = ' '.join([self.master_ip, ip]) ips.append(ip) # self.updateNameView(self.master, self.quality, self) except Exception: import traceback print(traceback.format_exc(1))
def updateMaster(self, master): ''' Updates the information of the ros master. If the ROS master not exists, it will be added. @param master: the ROS master to update @type master: U{fkie_master_discovery.msg.ROSMaster<http://docs.ros.org/api/fkie_multimaster_msgs/html/msg/ROSMaster.html>} ''' # remove master, if his name was changed but not the ROS master URI root = self.invisibleRootItem() for i in reversed(range(root.rowCount())): masterItem = root.child(i) if masterItem.master.uri == master.uri and masterItem.master.name != master.name: root.removeRow(i) try: del self.pyqt_workaround[masterItem.master.name] except Exception: pass break # update or add a the item root = self.invisibleRootItem() doAddItem = True is_local = nm.is_local(get_hostname(master.uri)) for index in range(root.rowCount()): masterItem = root.child(index, self.COL_NAME) if (masterItem == master.name): # update item masterItem.master = master masterItem.updateMasterView(root) doAddItem = False break elif (masterItem > master.name): self.addRow(master, is_local, root, index) doAddItem = False break if doAddItem: self.addRow(master, is_local, root, -1)
def init_globals(masteruri): ''' :return: True if the masteruri referred to localhost :rtype: bool ''' # initialize the global handler global _NMD_CLIENT global _SSH_HANDLER global _SCREEN_HANDLER global _START_HANDLER global _NAME_RESOLUTION global _HISTORY _NMD_CLIENT = NmdClient() # _NMD_CLIENT.start() _SSH_HANDLER = SSHhandler() _SCREEN_HANDLER = ScreenHandler() _START_HANDLER = StartHandler() _NAME_RESOLUTION = NameResolution() _HISTORY = History() # test where the roscore is running (local or remote) __is_local('localhost') # fill cache return __is_local(get_hostname(masteruri)) # fill cache
def updateTypeView(cls, service, item): ''' Updates the representation of the column contains the type of the service. :param service: the service data :type service: fkie_master_discovery.ServiceInfo<http://docs.ros.org/kinetic/api/fkie_master_discovery/html/modules.html#fkie_master_discovery.master_info.ServiceInfo> :param item: corresponding item in the model :type item: :class:`ServiceItem` ''' try: if service.isLocal and service.type: service_class = service.get_service_class( nm.is_local(get_hostname(service.uri))) item.setText(service_class._type) elif service.type: item.setText(service.type) else: item.setText('unknown type') # removed tooltip for clarity !!! # tooltip = '' # tooltip = ''.join([tooltip, '<h4>', service_class._type, '</h4>']) # tooltip = ''.join([tooltip, '<b><u>', 'Request', ':</u></b>']) # tooltip = ''.join([tooltip, '<dl><dt>', utf8(service_class._request_class.__slots__), '</dt></dl>']) # # tooltip = ''.join([tooltip, '<b><u>', 'Response', ':</u></b>']) # tooltip = ''.join([tooltip, '<dl><dt>', utf8(service_class._response_class.__slots__), '</dt></dl>']) # # item.setToolTip(''.join(['<div>', tooltip, '</div>'])) item.setToolTip('') except Exception: if not service.isLocal: tooltip = ''.join([ '<h4>', 'Service type is not available due to he running on another host.', '</h4>' ]) item.setToolTip(''.join(['<div>', tooltip, '</div>']))
def address(self, masteruri): with self.mutex: for m in self._masters: if m.masteruri == masteruri or m.has_mastername(masteruri): return m.get_address(prefer_hostname=False) return get_hostname(masteruri)
def on_load_profile_file(self, grpc_url): ''' Load the profile file. :param str grpc_url: the path of the profile file. ''' _url, path = nmdurl.split(grpc_url) rospy.loginfo("Load profile %s" % path) self.progressBar.setValue(0) self.setVisible(True) self.setWindowTitle("%s profile started" % os.path.basename(path).rstrip('.nmprofile')) hasstart = False if path: try: with open(path, 'r') as f: content = ruamel.yaml.load(f.read(), Loader=ruamel.yaml.Loader) if not isinstance(content, dict): raise Exception("Mailformed profile: %s" % os.path.basename(path)) for muri, master_dict in content.items(): local_hostname = get_hostname( self._main_window.getMasteruri()) rmuri = muri.replace('$LOCAL$', local_hostname) master = self._main_window.getMaster(rmuri) running_nodes = master.get_nodes_runningIfLocal() usr = None self._current_profile[rmuri] = set() if 'user' in master_dict: usr = master_dict['user'] if master_dict['mastername'] and master_dict[ 'mastername']: nm.nameres().add_master_entry( master.masteruri, master_dict['mastername'], master_dict['address']) hostname = master_dict['address'].replace( '$LOCAL$', local_hostname) if 'master_discovery' in master_dict: self._start_node_from_profile( master, hostname, 'fkie_master_discovery', 'master_discovery', usr, cfg=master_dict['master_discovery']) self._current_profile[rmuri].add( '/master_discovery') if 'master_sync' in master_dict: self._start_node_from_profile( master, hostname, 'fkie_master_sync', 'master_sync', usr, cfg=master_dict['master_sync']) self._current_profile[rmuri].add('/master_sync') if 'zeroconf' in master_dict: self._start_node_from_profile( master, hostname, 'fkie_master_discovery', 'zeroconf', usr, cfg=master_dict['zeroconf']) self._current_profile[rmuri].add('/zeroconf') if 'node_manager_daemon' in master_dict: self._start_node_from_profile( master, hostname, 'fkie_node_manager_daemon', 'node_manager_daemon', usr, cfg=master_dict['node_manager_daemon']) self._current_profile[rmuri].add( '/node_manager_daemon') try: do_start = [] do_not_stop = { '/rosout', rospy.get_name(), '/node_manager', '/master_discovery', '/master_sync', '*default_cfg', '/zeroconf', '/node_manager_daemon' } configs = master_dict['configs'] conf_set = set() for cfg_name, cmdict in configs.items(): cfg_name = cfg_name.replace( '$LOCAL$', local_hostname) if cfg_name.startswith("pkg://"): cfg_name = resolve_pkg( cfg_name, nmdurl.nmduri(rmuri)) conf_set.add(cfg_name) reload_launch = True args = {} if 'args' in cmdict: if 'args' in cmdict: args = cmdict['args'] # do we need to load to load/reload launch file if cfg_name in master.launchfiles: reload_launch = set( master.launchfiles[cfg_name].args. itervalues()) != set( args.itervalues()) if reload_launch: self._main_window.launch_dock.load_file( cfg_name, args, master.masteruri) if 'nodes' in cmdict: self._current_profile[rmuri].update( cmdict['nodes']) force_start = True cfg = cfg_name if not reload_launch: force_start = False do_not_stop.update(set( cmdict['nodes'])) do_start.append( (reload_launch, cfg, cmdict['nodes'], force_start)) else: do_start.append( (reload_launch, cfg, cmdict['nodes'], force_start)) # close unused configurations for lfile in set( master.launchfiles.keys()) - conf_set: master._close_cfg(lfile) master.stop_nodes_by_name(running_nodes.keys(), True, do_not_stop) for reload_launch, cfg, nodes, force_start in do_start: if nodes: hasstart = True if reload_launch: master.start_nodes_after_load_cfg( cfg, list(nodes), force_start) else: master.start_nodes_by_name( list(nodes), cfg, force_start) except Exception as ml: import traceback print(utf8(traceback.format_exc(1))) rospy.logwarn( "Can not load launch file for %s: %s" % (muri, utf8(ml))) except Exception as e: import traceback print(traceback.format_exc(1)) MessageBox.warning(self, "Load profile error", 'Error while load profile', utf8(e)) if not hasstart: self.update_progress() else: QTimer.singleShot(1000, self.update_progress)
def on_save_profile(self, masteruri='', path=None): ''' Saves the current environment to a node manager profile. :param str masteruri: If not empty, save the profile only for given master :param str path: the path of file to save. If None :meth:`get_profile_file` is called to get a path. ''' try: if path is None: path = self.get_profile_file() if path is None: return rospy.loginfo("Save profile %s" % path) content = {} for muri, master in self._main_window.masters.items(): if not masteruri or masteruri == muri: running_nodes = master.get_nodes_runningIfLocal() configs = {} md_param = {} ms_param = {} zc_param = {} nmd_param = {} smuri = muri addr = nm.nameres().address(smuri) hostname = get_hostname(smuri) mastername = '' if nm.is_local(addr): smuri = smuri.replace(hostname, '$LOCAL$') addr = '$LOCAL$' else: mastername = nm.nameres().mastername( smuri, nm.nameres().address(smuri)) for node_name in running_nodes.keys(): node_items = master.getNode(node_name) for node in node_items: if node.is_running( ) and node.launched_cfg is not None: # node.has_launch_cfgs(node.cfgs): # it is a loaded launch file, get the filename cfg = to_pkg(node.launched_cfg) if cfg not in configs: configs[cfg] = {'nodes': []} configs[cfg]['nodes'].append(node_name) elif node_name.endswith('master_discovery'): md_param = get_rosparam( 'master_discovery', muri) elif node_name.endswith('master_sync'): ms_param = get_rosparam('master_sync', muri) elif node_name.endswith('zeroconf'): zc_param = get_rosparam('zeroconf', muri) elif node_name.endswith('node_manager_daemon'): nmd_param = get_rosparam( 'node_manager_daemon', muri) # store arguments for launchfiles for a, b in master.launchfiles.items(): resolved_a = to_pkg(a) if resolved_a not in configs: configs[resolved_a] = {} configs[resolved_a]['default'] = False configs[resolved_a]['args'] = b.args # fill the configuration content for yaml as dictionary content[smuri] = { 'mastername': mastername, 'address': addr, 'configs': configs } if md_param: content[smuri]['master_discovery'] = md_param if ms_param: content[smuri]['master_sync'] = ms_param if zc_param: content[smuri]['zeroconf'] = zc_param if nmd_param: content[smuri]['node_manager_daemon'] = nmd_param buf = ruamel.yaml.compat.StringIO() ruamel.yaml.dump(content, buf, Dumper=ruamel.yaml.RoundTripDumper) with open(path, 'w+') as f: f.write(buf.getvalue()) except Exception as e: import traceback print(utf8(traceback.format_exc(3))) MessageBox.warning(self, "Save profile Error", 'Error while save profile', utf8(e))
def __init__(self, name, uri, discoverer_name, monitoruri, timestamp, sync_on_demand=False, callback_resync=None): ''' Initialization method for the SyncThread. @param name: the name of the ROS master synchronized with. @type name: C{str} @param uri: the URI of the ROS master synchronized with @type uri: C{str} @param discoverer_name: the name of the discovery node running on ROS master synchronized with. @type discoverer_name: C{str} @param monitoruri: The URI of RPC server of the discovery node to get the ROS master state by calling a method only once. @type monitoruri: C{str} @param timestamp: The timestamp of the current state of the ROS master info. @type timestamp: C{float64} @param sync_on_demand: Synchronize topics on demand @type sync_on_demand: bool ''' self.name = name self.uri = uri self.discoverer_name = discoverer_name self.monitoruri = monitoruri self.timestamp = timestamp self.timestamp_local = 0. self.timestamp_remote = 0. self._online = True self._offline_ts = 0 self.masteruri_local = masteruri_from_ros() self.hostname_local = get_hostname(self.masteruri_local) rospy.logdebug( "SyncThread[%s]: create this sync thread, discoverer_name: %s", self.name, self.discoverer_name) # synchronization variables self.__lock_info = threading.RLock() self.__lock_intern = threading.RLock() self._use_filtered_method = None self._use_md5check_topics = None self._md5warnings = { } # ditionary of {(topicname, node, nodeuri) : (topictype, md5sum)} self._topic_type_warnings = { } # ditionary of {(topicname, node, nodeuri) : remote topictype} # SyncMasterInfo with currently synchronized nodes, publisher (topic, node, nodeuri), subscriber(topic, node, nodeuri) and services self.__sync_info = None self.__unregistered = False # a list with published topics as a tuple of (topic name, node name, node URL) self.__publisher = [] # a list with subscribed topics as a tuple of (topic name, node name, node URL) self.__subscriber = [] # a list with services as a tuple of (service name, service URL, node name, node URL) self.__services = [] # the state of the own ROS master is used if `sync_on_demand` is enabled or # to determine the type of topic subscribed remote with `Empty` type self.__own_state = None self.__callback_resync = callback_resync self.__has_remove_sync = False # setup the filter self._filter = FilterInterface() self._filter.load( self.name, [ '/rosout', self.discoverer_name, '/master_discovery', '/master_sync', '/node_manager', '/node_manager_daemon', '/zeroconf', '/param_sync' ], [], [ '/rosout', '/rosout_agg', '/master_discovery/*', '/master_sync/*', '/zeroconf/*' ], ['/'] if sync_on_demand else [], [ '/*get_loggers', '/*set_logger_level', '/master_discovery/*', '/master_sync/*', '/node_manager_daemon/*', '/zeroconf/*' ], [], # do not sync the bond message of the nodelets!! [ 'bond/Status', 'fkie_multimaster_msgs/SyncTopicInfo', 'fkie_multimaster_msgs/SyncServiceInfo', 'fkie_multimaster_msgs/SyncMasterInfo', 'fkie_multimaster_msgs/MasterState' ], [], [], []) # congestion avoidance: wait for random.random*2 sec. If an update request # is received try to cancel and restart the current timer. The timer can be # canceled for maximal MAX_UPDATE_DELAY times. self._update_timer = None self._delayed_update = 0 self.__on_update = False