def _process_listeners(self, name, exit_code): ''' This is usually run as a post-executing function to the interaction and so will do the cleanup when the user's side of the interaction has terminated. Note that there are other means of stopping & cleanup for interactions: - via the remocon stop buttons (self.stop_interaction, self.stop_all_interactions) - via a rapp manager status callback when it is a pairing interaction There is some common code (namely del launched_interactions element, check pairing, publish remocon) so if changing that flow, be sure to check the code in self.stop_interaction() @param str name : name of the launched process stored in the interactions launch_list dict. @param int exit_code : could be utilised from roslaunched processes but not currently used. ''' terminated = False for interaction in self.interactions_table.interactions: if self.launched_interactions.remove(interaction.hash, name): # toggle the pairing indicator if it was a pairing interaction if interaction.is_paired_type() and interaction.hash in self.active_paired_interaction_hashes: self.active_paired_interaction_hashes = [interaction_hash for interaction_hash in self.active_paired_interaction_hashes if interaction_hash != interaction.hash] if not self.launched_interactions.get_launch_details(interaction.hash): # inform the gui to update self.signal_updated.emit() # update the rocon interactions handler self._publish_remocon_status() terminated = True break if not terminated: console.logwarn("A process listener detected a terminating interaction, but nothing to do.") console.logwarn("Probably mopped up by gui or independently terminating pairing requirement. [%s]" % name) else: console.logdebug("A process listener detected a terminating interaction & mopped up appropriately [%s]" % name)
def _stop_all_interactions(self): """ Stopping all running interactions. If no interactions is running, stop interactions button is disables. """ console.logdebug("Role Chooser : stopping all running interactions") self.interactive_client_interface.stop_all_interactions() self.roles_widget.stop_all_interactions_button.setEnabled(False)
def _start_global_executable_interaction(self, interaction, filename): """ @param str filename: not really a filename, but the command to run The filename could be something like: @code /opt/ros/indigo/bin/rqt_plot rqt_plot rqt_plot -t @endcode """ console.logdebug( "Interactive Client : starting global executable [%s]" % interaction.command) name = os.path.basename(filename).replace('.', '_') anonymous_name = name + "_" + uuid.uuid4().hex process_listener = functools.partial(self._process_listeners, anonymous_name, 1) cmd = shlex.split(filename) remapping_args = [] for remap in interaction.remappings: remapping_args.append(remap.remap_from + ":=" + remap.remap_to) cmd.extend(remapping_args) cmd.extend( self._prepare_command_line_parameters(interaction.parameters)) console.logdebug("Global executable command: '%s'" % cmd) process = rocon_python_utils.system.Popen(cmd, postexec_fn=process_listener) self.launched_interactions.add( interaction.hash, anonymous_name, launch.LaunchInfo(anonymous_name, True, process)) return True
def __init__(self, interactive_client_interface=None): self.binded_function = {} self.cur_selected_role = '' self.cur_selected_interaction = None self.interactions = {} self.interactive_client_interface = interactive_client_interface self.interactions_widget = QWidget() # load ui rospack = rospkg.RosPack() path = os.path.join(rospack.get_path('rocon_remocon'), 'ui', 'interactions_list.ui') loadUi(path, self.interactions_widget) # interactions list widget self.interactions_widget.interactions_list_widget.setIconSize( QSize(50, 50)) self.interactions_widget.interactions_list_widget.itemDoubleClicked.connect( self._start_interaction) self.interactions_widget.back_btn.pressed.connect(self._back) self.interactions_widget.interactions_list_widget.itemClicked.connect( self._display_interaction_info) # rocon master item click event self.interactions_widget.stop_interactions_button.pressed.connect( self._stop_interaction) self.interactions_widget.stop_interactions_button.setDisabled(True) self.interactions_widget.closeEvent = self._close_event console.logdebug('init QInteractionsChooser')
def _connect_rocon_master(self): rocon_master_name = str( self.rocon_masters[self.cur_selected_rocon_master].name) rocon_master_uri = str( self.rocon_masters[self.cur_selected_rocon_master].uri) rocon_master_host_name = str( self.rocon_masters[self.cur_selected_rocon_master].host_name) rocon_master_index = str(self.cur_selected_rocon_master) self.rocon_masters[rocon_master_index].check() # Todo this use of flags is spanky if self.rocon_masters[rocon_master_index].flag == '0': QMessageBox.warning( self, 'Rocon Master Connection Error', "Could not find a rocon master at %s" % self.rocon_masters[rocon_master_index].uri, QMessageBox.Ok) return if self.rocon_masters[rocon_master_index].flag == '1': QMessageBox.warning( self, 'Rocon Master Communication Error', "Found a rocon master at %s but cannot communicate with it (are ROS_IP/ROS_MASTER_URI correctly configured locally and remotely?)" % self.rocon_masters[rocon_master_index].uri, QMessageBox.Ok) return self._widget_main.hide() arguments = ["", rocon_master_uri, rocon_master_host_name] os.execv(QMasterChooser.rocon_remocon_sub_script, arguments) console.logdebug("Spawning: %s with args %s" % (QMasterChooser.rocon_remocon_sub_script, arguments))
def _back(self): if 'back' in self.binded_function.keys( ) and self.binded_function['back'] is not None: self.binded_function['back']() else: console.logdebug( "Interactions Chooser : None binded functione: shutdown")
def _process_listeners(self, name, exit_code): ''' Callback function used to catch terminating applications and cleanup appropriately. @param name : name of the launched process stored in the interactions index. @type str @param exit_code : could be utilised from roslaunched processes but not currently used. @type int ''' console.logdebug( "Interactive Client : process_listener detected terminating interaction [%s]" % name) for interaction in self._interactions_table.interactions: if name in interaction.launch_list: del interaction.launch_list[name] # toggle the pairing indicator if it was a pairing interaction if interaction.is_paired_type(): self.pairing = None if not interaction.launch_list: # inform the gui to update self._stop_interaction_postexec_fn() # update the rocon interactions handler self._publish_remocon_status() else: console.logwarn( "Interactive Client : process_listener detected unknown terminating interaction [%s]" % name)
def _start_rosrunnable_interaction(self, interaction, rosrunnable_filename): ''' Launch a rosrunnable application. This does not apply any parameters or remappings (yet). ''' # the following is guaranteed since we came back from find_resource calls earlier # note we're overriding the rosrunnable filename here - rosrun doesn't actually take the full path. package_name, rosrunnable_filename = interaction.name.split('/') name = os.path.basename(rosrunnable_filename).replace('.', '_') anonymous_name = name + "_" + uuid.uuid4().hex process_listener = partial(self._process_listeners, anonymous_name, 1) cmd = [ 'rosrun', package_name, rosrunnable_filename, '__name:=%s' % anonymous_name ] remapping_args = [] for remap in interaction.remappings: remapping_args.append(remap.remap_from + ":=" + remap.remap_to) cmd.extend(remapping_args) cmd.extend( self._prepare_command_line_parameters(interaction.parameters)) console.logdebug("Interactive Client : rosrunnable command %s" % cmd) process = rocon_python_utils.system.Popen(cmd, postexec_fn=process_listener) interaction.launch_list[anonymous_name] = LaunchInfo( anonymous_name, True, process) return True
def _close_event(self, event): """ Re-implementation of close event handlers for the interaction chooser's children (i.e. role and interactions list widgets). """ console.logdebug("Interactions Chooser : remocon shutting down.") self.shutdown()
def _start_rosrunnable_interaction(self, interaction, rosrunnable_filename): ''' Launch a rosrunnable application. This does not apply any parameters yet. :param str rosrunnable_filename: full path to the rosrunnable filename ''' # the following is guaranteed since we came back from find_resource calls earlier # note we're overriding the rosrunnable filename here - rosrun doesn't actually take the full path. console.logdebug("rosrunnable_filename : %s" % rosrunnable_filename) unused_package_name, rosrunnable_command = interaction.command.split('/', 1) rosrunnable_name = rosrunnable_command.split(' ', 1)[0] rosrunnable_name_and_args = rosrunnable_command.split(' ', 1) rosrunnable_args = rosrunnable_name_and_args[1] if len(rosrunnable_name_and_args) > 1 else [] if rosrunnable_args: rosrunnable_args = roslaunch.substitution_args.resolve_args(rosrunnable_args) rosrunnable_args = rosrunnable_args.split(' ') name = os.path.basename(rosrunnable_name).replace('.', '_') anonymous_name = name + "_" + uuid.uuid4().hex process_listener = functools.partial(self._process_listeners, anonymous_name, 1) cmd = [rosrunnable_filename, '__name:=%s' % anonymous_name] cmd.extend(rosrunnable_args) remapping_args = [] for remap in interaction.remappings: remapping_args.append(remap.remap_from + ":=" + remap.remap_to) cmd.extend(remapping_args) cmd.extend(self._prepare_command_line_parameters(interaction.parameters)) console.logdebug("Rosrunnable command: '%s'" % cmd) process = rocon_python_utils.system.Popen(cmd, postexec_fn=process_listener) self.launched_interactions.add( interaction.hash, anonymous_name, launch.LaunchInfo(anonymous_name, True, process) ) return True
def _read_cache(self): #read cache and display the rocon master list try: cache_rocon_master_info_list = open(self.rocon_master_list_cache_path, 'r') except: console.logdebug("Remocon : no cached settings found, moving on.") return lines = cache_rocon_master_info_list.readlines() for line in lines: if line.count("[index="): rocon_master_index = line[string.find(line, "[index=") + len("[index="):string.find(line, ",name=")] rocon_master_name = line[string.find(line, "name=") + len("name="):string.find(line, ",master_uri=")] rocon_master_uri = line[string.find(line, ",master_uri=") + len(",master_uri="):string.find(line, ",host_name=")] rocon_master_host_name = line[string.find(line, ",host_name=") + len(",host_name="):string.find(line, ",description=")] rocon_master_description = line[string.find(line, ",description=") + len(",description="):string.find(line, ",icon=")] rocon_master_icon = line[string.find(line, ",icon=") + len(",icon="):string.find(line, ",flag=")] rocon_master_flag = line[string.find(line, ",flag=") + len(",flag="):string.find(line, "]")] self.rocon_master_list[rocon_master_index] = {} self.rocon_master_list[rocon_master_index]['index'] = rocon_master_index self.rocon_master_list[rocon_master_index]['name'] = rocon_master_name self.rocon_master_list[rocon_master_index]['master_uri'] = rocon_master_uri self.rocon_master_list[rocon_master_index]['host_name'] = rocon_master_host_name self.rocon_master_list[rocon_master_index]['icon'] = rocon_master_icon self.rocon_master_list[rocon_master_index]['description'] = rocon_master_description self.rocon_master_list[rocon_master_index]['flag'] = rocon_master_flag cache_rocon_master_info_list.close()
def shutdown(self): self._terminal_shutdown_hook(processes=[self.process], hold=False) for temporary_file in self.temporary_files: console.logdebug(" unlinking %s" % temporary_file.name) try: os.unlink(temporary_file.name) except OSError: pass
def _set_add_rocon_master(self): if self._connect_dlg: console.logdebug("Dialog is live!!") self._connect_dlg.done(0) self._connect_dlg = self._create_add_rocon_master_dialog() self._connect_dlg.setVisible(True) self._connect_dlg.finished.connect(self._destroy_connect_dlg)
def _switch_to_role_list(self): """ Switch to role chooser from interactions chooser. """ self._interactive_client_ui_widget.setWindowTitle('Role Chooser') console.logdebug("InteractiveClientUI : switching to the role list") self._role_chooser.show( self._interactions_chooser.interactions_widget.pos()) self._interactions_chooser.hide()
def _switch_to_master_chooser(self): """ Switch to master chooser from role chooser. If it was launced by rqt or rqt standalone, It is just shutdown. """ self.shutdown() if not self.with_rqt: console.logdebug( "InteractiveClientUI : switching back to the master chooser") os.execv(QMasterChooser.rocon_remocon_script, ['', self.host_name])
def _back(self): """ Public method to enable shutdown of the script - this function is primarily for shutting down the Role chooser from external signals (e.g. CTRL-C on the command line). """ console.logdebug("Role Chooser : Back") if 'back' in self.binded_function.keys() and self.binded_function['back'] is not None: self.binded_function['back']()
def _start_dummy_interaction(self, interaction, unused_filename): console.logdebug("Starting a dummy interaction [%s]" % interaction.command) anonymous_name = interaction.name.lower().replace(' ', '_') + "_" + uuid.uuid4().hex # process_listener = partial(self._process_listeners, anonymous_name, 1) # process = rocon_python_utils.system.Popen([rosrunnable_filename], postexec_fn=process_listener) self.launched_interactions.add(interaction.hash, anonymous_name, launch.LaunchInfo(anonymous_name, True, None) ) # empty shutdown function return True
def _stop_interaction(self): console.logdebug("Interactions Chooser : stopping interaction %s " % str(self.cur_selected_interaction.name)) (result, message) = self.interactive_client.stop_interaction(self.cur_selected_interaction.hash) if result: if self.cur_selected_interaction.is_paired_type(): self._refresh_interactions_list() # make sure the highlight is disabled self._set_stop_interactions_button() #self.interactions_widget.stop_interactions_button.setDisabled(True) else: QMessageBox.warning(self, 'Stop Interaction Failed', "%s." % message.capitalize(), QMessageBox.Ok) console.logwarn("Interactions Chooser : stop interaction failed [%s]" % message)
def append(self, interaction): """ Append an interaction to the table. :param :class:`.Interaction` interaction: """ matches = [i for i in self.interactions if i.hash == interaction.hash] if not matches: self.interactions.append(interaction) else: console.logdebug("Interactions Table : interaction already stored, not appending to the table [%s]" % interaction.hash)
def _select_role(self, item): """ Take the selected role to switch interactions viewer as it. :param item: qt list widget item of selected role. The user does double click on item wanted to launch :type item: python_qt_binding.QtGui.QListWidgetItem """ console.logdebug("Role Chooser : switching to the interactions list") self.cur_selected_role = str(item.text()) if 'select_role' in self.binded_function.keys() and self.binded_function['select_role'] is not None: self.binded_function['select_role']()
def _switch_to_interactions_list(self, Item): """ Take the selected role and switch to an interactions view of that role. """ console.logdebug("Interactions Chooser : switching to the interactions list") self.cur_selected_role = str(Item.text()) self.interactive_client.select_role(self.cur_selected_role) self.interactions_widget.show() self.interactions_widget.move(self.roles_widget.pos()) self.roles_widget.hide() self._refresh_interactions_list()
def _publish_remocon_status(self): remocon_status = interaction_msgs.RemoconStatus() remocon_status.platform_info = self.platform_info remocon_status.uuid = str(self.key.hex) remocon_status.version = rocon_std_msgs.Strings.ROCON_VERSION running_interactions = [] for interaction_hash in self.launched_interactions.active(): running_interactions.append(interaction_hash) remocon_status.running_interactions = running_interactions console.logdebug("Remocon : publishing remocon status") self.remocon_status_publisher.publish(remocon_status)
def stop_all_interactions(self): """ This is the big showstopper - stop them all! """ console.logdebug("Interactive client : stopping all interactions") running_interactions = [] for interaction in self._interactions_table.interactions: for unused_process_name in interaction.launch_list.keys(): running_interactions.append(interaction.hash) for interaction_hash in running_interactions: self.stop_interaction(interaction_hash)
def _publish_remocon_status(self): remocon_status = rocon_interaction_msgs.RemoconStatus() remocon_status.platform_info = self.platform_info remocon_status.uuid = str(self.key.hex) remocon_status.version = rocon_std_msgs.Strings.ROCON_VERSION running_interactions = [] for interaction in self._interactions_table.interactions: for unused_process_name in interaction.launch_list.keys(): running_interactions.append(interaction.hash) remocon_status.running_interactions = running_interactions console.logdebug("Interactive Client : publishing remocon status") self.remocon_status_pub.publish(remocon_status)
def _close_event(self, event): """ Re-implementation of close event handlers for the interaction chooser's children (i.e. role and interactions list widgets). """ console.logdebug("Interactions Chooser : remocon shutting down.") if 'shutdown' in self.binded_function.keys( ) and self.binded_function['shutdown'] is not None: self.binded_function['shutdown']() else: console.logdebug( "Interactions Chooser : None binded functione: shutdown")
def _switch_to_interactions_list(self): """ Take the selected role and switch to an interactions view of that role. """ console.logdebug( "InteractiveClientUI : switching to the interactions list") self._interactive_client_ui_widget.setWindowTitle( 'Interactions Chooser') self._interactions_chooser.select_role( self._role_chooser.cur_selected_role) self._interactions_chooser.show(self._role_chooser.pos()) self._role_chooser.hide()
def get_rocon_master_info(self): console.logdebug("RemoconInfo : retrieving rocon master information") time_out_cnt = 0 while not rospy.is_shutdown(): if len(self.rocon_master_info) != 0: break else: rospy.sleep(rospy.Duration(0.2)) time_out_cnt += 1 if time_out_cnt > 5: console.logwarn("RemoconInfo : timed out waiting for rocon master information") break return self.rocon_master_info
def _subscribe_pairing_status_callback(self, msg): console.logdebug( "Interactive Client : pairing status callback [%s][%s]" % (msg.rapp, msg.remocon)) if self.pairing: if not msg.rapp and msg.remocon == self.name: console.logdebug( "Interactive Client : the rapp in this paired interaction terminated" ) # there will only ever be one allowed instance of a paired interaction, so ok to call a stop to all instances of this interaction type self.stop_interaction(self.pairing) # updat the gui self._stop_interaction_postexec_fn()
def _set_stop_interactions_button(self): ''' Disable or enable the stop button depending on whether the selected interaction has any currently launched processes, ''' if not self.interactions: console.logwarn("No interactions") return if self.cur_selected_interaction.launch_list: console.logdebug("Interactions Chooser : enabling stop interactions button [%s]" % self.cur_selected_interaction.display_name) self.interactions_widget.stop_interactions_button.setEnabled(True) else: console.logdebug("Interactions Chooser : disabling stop interactions button [%s]" % self.cur_selected_interaction.display_name) self.interactions_widget.stop_interactions_button.setEnabled(False)
def _switch_to_role_list(self): console.logdebug("Interactions Chooser : switching to the role list") # show the roles widget if self.interactive_client.has_running_interactions(): self.roles_widget.stop_all_interactions_button.setEnabled(True) else: self.roles_widget.stop_all_interactions_button.setEnabled(False) self.roles_widget.show() self.roles_widget.move(self.interactions_widget.pos()) # show the interactions widget self.interactions_widget.stop_interactions_button.setEnabled(False) self.interactions_widget.hide()