From 42a6b9afa28bab5855d0239b892da42b40bb445c Mon Sep 17 00:00:00 2001 From: Daniel Stonier Date: Mon, 28 Apr 2014 09:05:20 +0900 Subject: [PATCH 1/3] web interaction handling, closes #76. --- rocon_remocon/.project | 2 +- rocon_remocon/package.xml | 1 + rocon_remocon/src/rocon_remocon/remocon.py | 101 +++++++-------- .../src/rocon_remocon/remocon_info.py | 118 +++++++++++------- .../ui/{applist.ui => interactions_list.ui} | 21 ++-- .../ui/{rolelist.ui => role_list.ui} | 9 +- 6 files changed, 132 insertions(+), 120 deletions(-) rename rocon_remocon/ui/{applist.ui => interactions_list.ui} (83%) rename rocon_remocon/ui/{rolelist.ui => role_list.ui} (88%) diff --git a/rocon_remocon/.project b/rocon_remocon/.project index 1b8c73e..d5508c8 100644 --- a/rocon_remocon/.project +++ b/rocon_remocon/.project @@ -4,7 +4,7 @@ rocon_console - rocon_hue + rocon_interactions rocon_python_comms rocon_python_utils rocon_uri diff --git a/rocon_remocon/package.xml b/rocon_remocon/package.xml index 9ee1bb5..58e3040 100644 --- a/rocon_remocon/package.xml +++ b/rocon_remocon/package.xml @@ -22,6 +22,7 @@ rocon_app_manager_msgs rocon_console rocon_icons + rocon_interactions rocon_interaction_msgs rocon_python_comms rocon_python_utils diff --git a/rocon_remocon/src/rocon_remocon/remocon.py b/rocon_remocon/src/rocon_remocon/remocon.py index 3866704..6d0257b 100644 --- a/rocon_remocon/src/rocon_remocon/remocon.py +++ b/rocon_remocon/src/rocon_remocon/remocon.py @@ -27,6 +27,7 @@ import rospkg import rocon_python_utils from rocon_console import console +import rocon_interactions.web_interactions as web_interactions from .remocon_info import RemoconInfo from . import utils @@ -48,40 +49,40 @@ def __init__(self, parent, title, application, rocon_master_index="", rocon_mast super(RemoconSub, self).__init__(parent) self.initialised = False - self._widget_app_list = QWidget() - self._widget_role_list = QWidget() + self.interactions_widget = QWidget() + self.roles_widget = QWidget() self.rocon_master_list = {} self.cur_selected_role = 0 - self.app_list = {} + self.interactions = {} self.cur_selected_app = None self.remocon_info = RemoconInfo(stop_app_postexec_fn=self._set_stop_app_button) - path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../ui/applist.ui") - uic.loadUi(path, self._widget_app_list) + path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../ui/interactions_list.ui") + uic.loadUi(path, self.interactions_widget) - path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../ui/rolelist.ui") - uic.loadUi(path, self._widget_role_list) + path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../ui/role_list.ui") + uic.loadUi(path, self.roles_widget) utils.setup_home_dirs() self.rocon_master_list_cache_path = os.path.join(utils.get_settings_cache_home(), "rocon_master.cache") self.scripts_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../scripts/") #role list widget - self._widget_role_list.role_list_widget.setIconSize(QSize(50, 50)) - self._widget_role_list.role_list_widget.itemDoubleClicked.connect(self._select_role_list) - self._widget_role_list.back_btn.pressed.connect(self._back_role_list) - self._widget_role_list.refresh_btn.pressed.connect(self._refresh_role_list) + self.roles_widget.role_list_widget.setIconSize(QSize(50, 50)) + self.roles_widget.role_list_widget.itemDoubleClicked.connect(self._select_role_list) + self.roles_widget.back_btn.pressed.connect(self._back_role_list) + self.roles_widget.refresh_btn.pressed.connect(self._refresh_role_list) #app list widget - self._widget_app_list.app_list_widget.setIconSize(QSize(50, 50)) - self._widget_app_list.app_list_widget.itemDoubleClicked.connect(self._start_app) - self._widget_app_list.back_btn.pressed.connect(self._uninit_app_list) - self._widget_app_list.app_list_widget.itemClicked.connect(self._select_app_list) # rocon master item click event - self._widget_app_list.stop_app_btn.pressed.connect(self._stop_app) - self._widget_app_list.refresh_btn.pressed.connect(self._refresh_app_list) - self._widget_app_list.stop_app_btn.setDisabled(True) + self.interactions_widget.interactions_list_widget.setIconSize(QSize(50, 50)) + self.interactions_widget.interactions_list_widget.itemDoubleClicked.connect(self._start_app) + self.interactions_widget.back_btn.pressed.connect(self._uninit_app_list) + self.interactions_widget.interactions_list_widget.itemClicked.connect(self._select_app_list) # rocon master item click event + self.interactions_widget.stop_interactions_button.pressed.connect(self._stop_app) + self.interactions_widget.refresh_btn.pressed.connect(self._refresh_app_list) + self.interactions_widget.stop_interactions_button.setDisabled(True) #init self._init() @@ -93,8 +94,8 @@ def _init(self): # is getting confused when the original program doesn't have the focus. # # Taking control of it ourselves works... - self._widget_app_list.stop_app_btn.setStyleSheet(QString.fromUtf8("QPushButton:disabled { color: gray }")) - self._widget_role_list.show() + self.interactions_widget.stop_interactions_button.setStyleSheet(QString.fromUtf8("QPushButton:disabled { color: gray }")) + self.roles_widget.show() self.initialised = True ################################################################################################################ @@ -119,9 +120,9 @@ def _select_role_list(self, Item): self.remocon_info._select_role(self.cur_selected_role) - self._widget_app_list.show() - self._widget_app_list.move(self._widget_role_list.pos()) - self._widget_role_list.hide() + self.interactions_widget.show() + self.interactions_widget.move(self.roles_widget.pos()) + self.roles_widget.hide() self._init_app_list() def _back_role_list(self): @@ -131,17 +132,17 @@ def _back_role_list(self): os.execv(self.scripts_path + 'rocon_remocon', ['', self.host_name]) def _refresh_role_list(self): - self._widget_role_list.role_list_widget.clear() + self.roles_widget.role_list_widget.clear() role_list = self.remocon_info.get_role_list() #set list widget item (reverse order because we push them on the top) for role in reversed(role_list): - self._widget_role_list.role_list_widget.insertItem(0, role) + self.roles_widget.role_list_widget.insertItem(0, role) #setting the list font - font = self._widget_role_list.role_list_widget.item(0).font() + font = self.roles_widget.role_list_widget.item(0).font() font.setPointSize(13) - self._widget_role_list.role_list_widget.item(0).setFont(font) + self.roles_widget.role_list_widget.item(0).setFont(font) ################################################################################################################ ##app list widget @@ -151,50 +152,50 @@ def _init_app_list(self): self._refresh_app_list() def _uninit_app_list(self): - self._widget_role_list.show() - self._widget_role_list.move(self._widget_app_list.pos()) - self._widget_app_list.hide() + self.roles_widget.show() + self.roles_widget.move(self.interactions_widget.pos()) + self.interactions_widget.hide() def _refresh_app_list(self): - self.app_list = {} - self.app_list = self.remocon_info._get_app_list() - self._widget_app_list.app_list_widget.clear() + self.interactions = {} + self.interactions = self.remocon_info.interactions + self.interactions_widget.interactions_list_widget.clear() index = 0 - for k in self.app_list.values(): + for k in self.interactions.values(): k['index'] = index index = index + 1 - self._widget_app_list.app_list_widget.insertItem(0, k['display_name']) + self.interactions_widget.interactions_list_widget.insertItem(0, k['display_name']) #setting the list font - font = self._widget_app_list.app_list_widget.item(0).font() + font = self.interactions_widget.interactions_list_widget.item(0).font() font.setPointSize(13) - self._widget_app_list.app_list_widget.item(0).setFont(font) + self.interactions_widget.interactions_list_widget.item(0).setFont(font) #setting the icon app_icon = k['icon'] if app_icon == "unknown.png": icon = QIcon(self.icon_paths['unknown']) - self._widget_app_list.app_list_widget.item(0).setIcon(icon) + self.interactions_widget.interactions_list_widget.item(0).setIcon(icon) elif len(app_icon): icon = QIcon(os.path.join(utils.get_icon_cache_home(), app_icon)) - self._widget_app_list.app_list_widget.item(0).setIcon(icon) + self.interactions_widget.interactions_list_widget.item(0).setIcon(icon) else: - print console.logdebug("%s : No icon" % str(self.rocon_master_name)) - pass def _select_app_list(self, Item): list_widget = Item.listWidget() cur_index = list_widget.count() - list_widget.currentRow() - 1 - for k in self.app_list.values(): + for k in self.interactions.values(): if(k['index'] == cur_index): self.cur_selected_app = k break - self._widget_app_list.app_info.clear() + self.interactions_widget.app_info.clear() info_text = "" info_text += "

-------------------------------------------

" - info_text += "

name: " + self.cur_selected_app['name'] + "

" + web_interaction = web_interactions.parse(self.cur_selected_app['name']) + name = self.cur_selected_app['name'] if web_interaction is None else web_interaction.url + info_text += "

name: " + name + "

" info_text += "

---------------------" + "

" info_text += "

compatibility: " + self.cur_selected_app['compatibility'] + "

" info_text += "

display name: " + self.cur_selected_app['display_name'] + "

" @@ -206,7 +207,7 @@ def _select_app_list(self, Item): info_text += "

parameters: " + str(self.cur_selected_app['parameters']) + "

" info_text += "" - self._widget_app_list.app_info.appendHtml(info_text) + self.interactions_widget.app_info.appendHtml(info_text) self._set_stop_app_button() def _set_stop_app_button(self): @@ -215,15 +216,15 @@ def _set_stop_app_button(self): toggle the state of the stop app button whenever a running app terminates itself. ''' - if not self.app_list: + if not self.interactions: return try: if self.cur_selected_app["launch_list"]: console.logdebug("Remocon : enabling stop app button") - self._widget_app_list.stop_app_btn.setDisabled(False) + self.interactions_widget.stop_interactions_button.setDisabled(False) else: console.logdebug("Remocon : disabling stop app button") - self._widget_app_list.stop_app_btn.setEnabled(False) + self.interactions_widget.stop_interactions_button.setEnabled(False) except KeyError: pass # do nothing @@ -231,12 +232,12 @@ def _stop_app(self): console.logdebug("Remocon : Stop app %s " % str(self.cur_selected_app['name'])) if self.remocon_info._stop_app(self.cur_selected_app['hash']): self._set_stop_app_button() - #self._widget_app_list.stop_app_btn.setDisabled(True) + #self.interactions_widget.stop_interactions_button.setDisabled(True) def _start_app(self): console.logdebug("Remocon : Start app %s " % str(self.cur_selected_app['name'])) if self.remocon_info._start_app(self.cur_selected_app['hash']): - self._widget_app_list.stop_app_btn.setDisabled(False) + self.interactions_widget.stop_interactions_button.setDisabled(False) ################################################################# ##Remocon Main diff --git a/rocon_remocon/src/rocon_remocon/remocon_info.py b/rocon_remocon/src/rocon_remocon/remocon_info.py index b5662a0..623f16d 100644 --- a/rocon_remocon/src/rocon_remocon/remocon_info.py +++ b/rocon_remocon/src/rocon_remocon/remocon_info.py @@ -28,6 +28,7 @@ from rocon_app_manager_msgs.msg import ErrorCodes import rocon_interaction_msgs.msg as rocon_interaction_msgs import rocon_interaction_msgs.srv as rocon_interaction_srvs +import rocon_interactions.web_interactions as web_interactions import rocon_python_comms from . import utils @@ -44,14 +45,12 @@ def __init__(self, stop_app_postexec_fn): @type method with no args ''' self._stop_app_postexec_fn = stop_app_postexec_fn - self.app_list = {} + self.interactions = {} self.rocon_master_info = {} self.is_connect = False self.is_app_running = False self.key = uuid.uuid4() - self.app_pid = 0 - # this might be naive and only work well on ubuntu... os_codename = OsDetect().get_codename() # this would be great as a configurable parameter @@ -118,10 +117,10 @@ def _shutdown(self): console.logwarn("RemoconInfo : tried to shutdown already disconnected remocon") else: console.logdebug("RemoconInfo : shutting down all apps") - for app_hash in self.app_list.keys(): + for app_hash in self.interactions.keys(): self._stop_app(app_hash) - self.app_list = {} + self.interactions = {} self.is_connect = False rospy.signal_shutdown("shut down remocon_info") @@ -158,33 +157,33 @@ def _select_role(self, role_name): call_result = self.get_interactions_service_proxy(roles, self.platform_info.uri) print "[remocon_info]: call result" - self.app_list = {} + self.interactions = {} for interaction in call_result.interactions: if(interaction.role == role_name): - app_hash = interaction.hash - #if self.app_list.has_key(app_hash): + # TODO should create a class out of this mash, dicts of dicts are prone to mistakes and hard to introspect. + #if self.interactions.has_key(interaction.hash): # pass #else: - # self.app_list[app_hash]={} - # self.app_list[app_hash]['launch_list'] ={} - self.app_list[app_hash] = {} - self.app_list[app_hash]['launch_list'] = {} + # self.interactions[interaction.hash]={} + # self.interactions[interaction.hash]['launch_list'] ={} + self.interactions[interaction.hash] = {} + self.interactions[interaction.hash]['launch_list'] = {} - self.app_list[app_hash]['name'] = interaction.name - self.app_list[app_hash]['compatibility'] = interaction.compatibility + self.interactions[interaction.hash]['name'] = interaction.name + self.interactions[interaction.hash]['compatibility'] = interaction.compatibility icon_name = interaction.icon.resource_name.split('/').pop() if interaction.icon.data: icon = open(os.path.join(utils.get_icon_cache_home(), icon_name), 'w') icon.write(interaction.icon.data) icon.close() - self.app_list[app_hash]['icon'] = icon_name - self.app_list[app_hash]['display_name'] = interaction.display_name - self.app_list[app_hash]['description'] = interaction.description - self.app_list[app_hash]['namespace'] = interaction.namespace - self.app_list[app_hash]['max'] = interaction.max - self.app_list[app_hash]['remappings'] = interaction.remappings - self.app_list[app_hash]['parameters'] = interaction.parameters - self.app_list[app_hash]['hash'] = interaction.hash + self.interactions[interaction.hash]['icon'] = icon_name + self.interactions[interaction.hash]['display_name'] = interaction.display_name + self.interactions[interaction.hash]['description'] = interaction.description + self.interactions[interaction.hash]['namespace'] = interaction.namespace + self.interactions[interaction.hash]['max'] = interaction.max + self.interactions[interaction.hash]['remappings'] = interaction.remappings + self.interactions[interaction.hash]['parameters'] = interaction.parameters + self.interactions[interaction.hash]['hash'] = interaction.hash def get_rocon_master_info(self): console.logdebug("RemoconInfo : retrieving rocon master information") @@ -219,12 +218,8 @@ def _info_callback(self, data): self.is_valid_info = True - def _get_app_list(self): - return self.app_list - pass - def _start_app(self, app_hash): - if not app_hash in self.app_list.keys(): + if not app_hash in self.interactions.keys(): print "[remocon_info] HAS NO KEY" return False @@ -232,7 +227,7 @@ def _start_app(self, app_hash): print "[remocon_info] APP ALREADY RUNNING NOW " return False - app = self.app_list[app_hash] + app = self.interactions[app_hash] #get the permission call_result = self.request_interaction_service_proxy(app['hash']) @@ -253,8 +248,8 @@ def _determine_app_type(self, app_name): (well reasonably) parsing of that string. - ros launcher (by .launch extension) - ros runnable (by roslib find_resource success) - - web app (by web_interactions.parse and urlparse scheme check against http) - - web url (by web_interactions.parse and by urlparse scheme check against http) + - web app (by web_interactions.parse) + - web url (by web_interactions.parse) - global executable (fallback option) ''' try: @@ -271,10 +266,14 @@ def _determine_app_type(self, app_name): pass except Exception: pass - o = urlparse(app_name) - if o.scheme == 'http': - console.logdebug("RemoconInfo : start_app_webapp [%s]") - return (app_name, self._start_app_webapp) + web_interaction = web_interactions.parse(app_name) + if web_interaction is not None: + if web_interaction.is_web_url(): + console.logdebug("RemoconInfo : start_app_weburl [%s]" % web_interaction.url) + return (web_interaction.url, self._start_app_weburl) + elif web_interaction.is_web_app(): + console.logdebug("RemoconInfo : start_app_webapp [%s]" % web_interaction.url) + return (web_interaction.url, self._start_app_webapp) else: console.logdebug("RemoconInfo : start_app_global_executable [%s]") return (app_name, self._start_app_global_executable) @@ -358,10 +357,35 @@ def _start_app_global_executable(self, app, rosrunnable_filename): app['launch_list'][anonymous_name]['shutdown'] = partial(process.send_signal, signal.SIGINT) return True - def _start_app_webapp(self, app, rosrunnable_filename): + def _start_app_weburl(self, app, url): + """ + We only need the url here and then do a system check for a web browser. + """ + web_browser = self._check_webbrowser() + if web_browser is not None: + name = os.path.basename(web_browser).replace('.', '_') + anonymous_name = name + "_" + uuid.uuid4().hex + process_listener = partial(self.process_listeners, anonymous_name, 1) + process = rocon_python_utils.system.Popen([web_browser, "--new-window", url], postexec_fn=process_listener) + app['launch_list'][anonymous_name] = {} + app['launch_list'][anonymous_name]['name'] = anonymous_name + app['launch_list'][anonymous_name]['running'] = str(True) + app['launch_list'][anonymous_name]['process'] = process + app['launch_list'][anonymous_name]['shutdown'] = partial(process.send_signal, signal.SIGINT) + return True + else: + return False + + def _start_app_webapp(self, app, base_url): + """ + Need to work out the extended url (with args, parameters and remappings) here and then feed that to a + detected browser. + + :param base_url str: the web app url without all of the attached variables. + """ web_browser = self._check_webbrowser() if web_browser is not None: - url = self._get_webapp_url(app) + url = self._get_webapp_url(app, base_url) name = os.path.basename(web_browser).replace('.', '_') anonymous_name = name + "_" + uuid.uuid4().hex process_listener = partial(self.process_listeners, anonymous_name, 1) @@ -375,7 +399,7 @@ def _start_app_webapp(self, app, rosrunnable_filename): else: return False - def _get_webapp_url(self, app): + def _get_webapp_url(self, app, base_url): """ url syntheiser for sending remappings and parameters information. We convert the interaction parameter (yaml string) and remapping (rocon_std_msgs.Remapping[]) @@ -395,7 +419,7 @@ def _get_webapp_url(self, app): query_string_mappings = {} query_string_mappings['interaction_data'] = json.dumps(interaction_data) # constructing the url - return app['name'] + "?" + urllib.urlencode(query_string_mappings) + return base_url + "?" + urllib.urlencode(query_string_mappings) def _check_webbrowser(self): if rocon_python_utils.system.which("google-chrome"): @@ -407,29 +431,29 @@ def _check_webbrowser(self): return None def _stop_app(self, app_hash): - if not app_hash in self.app_list: + if not app_hash in self.interactions: print "[remocon_info] HAS NO KEY" return False - print "[remocon_info]Launched App List- %s" % str(self.app_list[app_hash]["launch_list"]) + print "[remocon_info]Launched App List- %s" % str(self.interactions[app_hash]["launch_list"]) try: - for k in self.app_list[app_hash]["launch_list"].values(): + for k in self.interactions[app_hash]["launch_list"].values(): process_name = k["name"] is_app_running = k["running"] if is_app_running == "True": k['shutdown']() - del self.app_list[app_hash]["launch_list"][k["name"]] + del self.interactions[app_hash]["launch_list"][k["name"]] print "[remocon_info] %s APP STOP" % (process_name) elif k['process'] == None: k["running"] = "False" - del self.app_list[app_hash]["launch_list"][k["name"]] + del self.interactions[app_hash]["launch_list"][k["name"]] print "[remocon_info] %s APP LAUNCH IS NONE" % (process_name) else: - del self.app_list[app_hash]["launch_list"][k["name"]] + del self.interactions[app_hash]["launch_list"][k["name"]] print "[remocon_info] %s APP IS ALREADY STOP" % (process_name) except Exception as e: print "[remocon_info] APP STOP PROCESS IS FAILURE %s %s" % (str(e), type(e)) return False - print "[remocon_info] updated app list- %s" % str(self.app_list[app_hash]["launch_list"]) + print "[remocon_info] updated app list- %s" % str(self.interactions[app_hash]["launch_list"]) self._pub_remocon_status(app_hash, False) return True @@ -437,13 +461,13 @@ 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 app_list index. + @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 ''' - for app_hash, v in self.app_list.iteritems(): + for app_hash, v in self.interactions.iteritems(): if name in v['launch_list']: del v['launch_list'][name] if not v['launch_list']: diff --git a/rocon_remocon/ui/applist.ui b/rocon_remocon/ui/interactions_list.ui similarity index 83% rename from rocon_remocon/ui/applist.ui rename to rocon_remocon/ui/interactions_list.ui index e2e4f94..710d34d 100644 --- a/rocon_remocon/ui/applist.ui +++ b/rocon_remocon/ui/interactions_list.ui @@ -1,7 +1,7 @@ - Remocon - + interactions + 0 @@ -11,23 +11,16 @@ - App List + Interactions - - - - App List - - - 5 - + 0 @@ -70,14 +63,14 @@ - Refresh App List + Refresh List - + - Stop All Apps + Stop All Interactions diff --git a/rocon_remocon/ui/rolelist.ui b/rocon_remocon/ui/role_list.ui similarity index 88% rename from rocon_remocon/ui/rolelist.ui rename to rocon_remocon/ui/role_list.ui index c40583b..709c56a 100644 --- a/rocon_remocon/ui/rolelist.ui +++ b/rocon_remocon/ui/role_list.ui @@ -11,16 +11,9 @@ - Role List + Roles - - - - Role List - - - From 36f7b0e96e97ecc27340cb581a917be9f3e1d113 Mon Sep 17 00:00:00 2001 From: Daniel Stonier Date: Mon, 28 Apr 2014 23:46:10 +0900 Subject: [PATCH 2/3] we can run more than one interaction in parallel, now we publish the fact, #98. --- rocon_remocon/src/rocon_remocon/remocon.py | 1 - .../src/rocon_remocon/remocon_info.py | 21 ++++++++++++------- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/rocon_remocon/src/rocon_remocon/remocon.py b/rocon_remocon/src/rocon_remocon/remocon.py index 6d0257b..fdcc26f 100644 --- a/rocon_remocon/src/rocon_remocon/remocon.py +++ b/rocon_remocon/src/rocon_remocon/remocon.py @@ -308,7 +308,6 @@ def _init(self): self.cur_selected_rocon_master = None self._refresh_all_rocon_master_list() self.is_init = True - pass def _check_up_one(self, rocon_master): rocon_master_uri = rocon_master['master_uri'] diff --git a/rocon_remocon/src/rocon_remocon/remocon_info.py b/rocon_remocon/src/rocon_remocon/remocon_info.py index 623f16d..75203cb 100644 --- a/rocon_remocon/src/rocon_remocon/remocon_info.py +++ b/rocon_remocon/src/rocon_remocon/remocon_info.py @@ -101,7 +101,7 @@ def _connect(self, rocon_master_name="", ros_master_uri="http://localhost:11311" self.request_interaction_service_proxy = rospy.ServiceProxy(request_interaction_service_name, rocon_interaction_srvs.RequestInteraction) self.remocon_status_pub = rospy.Publisher("remocons/" + unique_name, rocon_interaction_msgs.RemoconStatus, latch=True) - self._pub_remocon_status(0, False) + self._pub_remocon_status() self.is_connect = True self.is_valid_info = False return True @@ -135,12 +135,16 @@ def _shutdown(self): self.remocon_status_pub = None console.logdebug("RemoconInfo : has shutdown.") - def _pub_remocon_status(self, app_hash, running_app): + def _pub_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.running_app = running_app - remocon_status.hash = app_hash + remocon_status.version = rocon_std_msgs.Strings.ROCON_VERSION + running_interactions = [] + for interaction_hash in self.interactions.keys(): + for unused_process_name in self.interactions[interaction_hash]["launch_list"].keys(): + running_interactions.append(interaction_hash) + remocon_status.running_interactions = running_interactions print "[remocon_info] publish remocon status" self.remocon_status_pub.publish(remocon_status) @@ -235,7 +239,8 @@ def _start_app(self, app_hash): print "[remocon_info] permisson ok" (app_executable, start_app_handler) = self._determine_app_type(app['name']) result = start_app_handler(app, app_executable) - self._pub_remocon_status(app_hash, result) + if result: + self._pub_remocon_status() return result else: print "[remocon_info] permission failure" @@ -454,7 +459,7 @@ def _stop_app(self, app_hash): print "[remocon_info] APP STOP PROCESS IS FAILURE %s %s" % (str(e), type(e)) return False print "[remocon_info] updated app list- %s" % str(self.interactions[app_hash]["launch_list"]) - self._pub_remocon_status(app_hash, False) + self._pub_remocon_status() return True def process_listeners(self, name, exit_code): @@ -467,7 +472,7 @@ def process_listeners(self, name, exit_code): @param exit_code : could be utilised from roslaunched processes but not currently used. @type int ''' - for app_hash, v in self.interactions.iteritems(): + for unused_interaction_hash, v in self.interactions.iteritems(): if name in v['launch_list']: del v['launch_list'][name] if not v['launch_list']: @@ -475,4 +480,4 @@ def process_listeners(self, name, exit_code): # inform the gui to update if necessary self._stop_app_postexec_fn() # update the rocon interactions handler - self._pub_remocon_status(app_hash, False) + self._pub_remocon_status() From e14c21ea1d5b04fd9c82c8b10417d37c79e092b7 Mon Sep 17 00:00:00 2001 From: Daniel Stonier Date: Tue, 29 Apr 2014 02:58:42 +0900 Subject: [PATCH 3/3] decouple rocon master lookups from the gui code. --- rocon_remocon/src/rocon_remocon/remocon.py | 204 ++++++------------ .../src/rocon_remocon/rocon_masters.py | 160 ++++++++++++++ 2 files changed, 226 insertions(+), 138 deletions(-) create mode 100644 rocon_remocon/src/rocon_remocon/rocon_masters.py diff --git a/rocon_remocon/src/rocon_remocon/remocon.py b/rocon_remocon/src/rocon_remocon/remocon.py index fdcc26f..8abce4f 100644 --- a/rocon_remocon/src/rocon_remocon/remocon.py +++ b/rocon_remocon/src/rocon_remocon/remocon.py @@ -31,6 +31,7 @@ from .remocon_info import RemoconInfo from . import utils +from .rocon_masters import RoconMasters ############################################################################## # Remocon @@ -52,7 +53,6 @@ def __init__(self, parent, title, application, rocon_master_index="", rocon_mast self.interactions_widget = QWidget() self.roles_widget = QWidget() - self.rocon_master_list = {} self.cur_selected_role = 0 self.interactions = {} @@ -67,7 +67,6 @@ def __init__(self, parent, title, application, rocon_master_index="", rocon_mast uic.loadUi(path, self.roles_widget) utils.setup_home_dirs() - self.rocon_master_list_cache_path = os.path.join(utils.get_settings_cache_home(), "rocon_master.cache") self.scripts_path = os.path.join(os.path.dirname(os.path.abspath(__file__)), "../../scripts/") #role list widget @@ -265,7 +264,7 @@ def __init__(self, parent, title, application): self.application = application self._widget_main = QWidget() - self.rocon_master_list = {} + self.rocon_masters = RoconMasters() self.cur_selected_rocon_master = None self.is_init = False @@ -273,7 +272,6 @@ def __init__(self, parent, title, application): uic.loadUi(path, self._widget_main) utils.setup_home_dirs() - self.rocon_master_list_cache_path = os.path.join(utils.get_settings_cache_home(), "rocon_master.cache") self.icon_paths = {} try: @@ -310,8 +308,8 @@ def _init(self): self.is_init = True def _check_up_one(self, rocon_master): - rocon_master_uri = rocon_master['master_uri'] - host_name = rocon_master['host_name'] + rocon_master_uri = rocon_master.uri + host_name = rocon_master.host_name output = subprocess.Popen([self.scripts_path + "rocon_remocon_check_up", rocon_master_uri, host_name], stdout=subprocess.PIPE) time_out_cnt = 0 while True: @@ -323,31 +321,33 @@ def _check_up_one(self, rocon_master): except: console.logdebug("Error: output.terminate()") - rocon_master['name'] = "Unknown" - rocon_master['description'] = "Unknown." - rocon_master['icon'] = "unknown.png" - rocon_master['flag'] = '0' + rocon_master.name = "Unknown" + rocon_master.description = "Unknown." + rocon_master.icon = "unknown.png" + rocon_master.flag = '0' break elif result == 0: args = output.communicate()[0] - rocon_master['name'] = args.split('\n')[0] - rocon_master['description'] = args.split('\n')[1] - rocon_master['icon'] = args.split('\n')[2] + rocon_master.name = args.split('\n')[0] + rocon_master.description = args.split('\n')[1] + rocon_master.icon = args.split('\n')[2] - if rocon_master['name'] == "Unknown": - rocon_master['flag'] = '0' + if rocon_master.name == "Unknown": + rocon_master.flag = '0' else: - rocon_master['flag'] = '1' + rocon_master.flag = '1' break time.sleep(0.1) time_out_cnt += 1 def _check_up_all(self): - for k in self.rocon_master_list.values(): - rocon_master_uri = k['master_uri'] - host_name = k['host_name'] + print("Check up all") + print("%s" % self.rocon_masters) + for k in self.rocon_masters.values(): + rocon_master_uri = k.uri + host_name = k.host_name output = subprocess.Popen([self.scripts_path + "rocon_remocon_check_up", rocon_master_uri, host_name], stdout=subprocess.PIPE) time_out_cnt = 0 while True: @@ -359,80 +359,42 @@ def _check_up_all(self): except: console.logdebug("Error: output.terminate()") - k['name'] = "Unknown" - k['description'] = "Unknown." - k['icon'] = "unknown.png" - k['flag'] = '0' + k.name = "Unknown" + k.description = "Unknown." + k.icon = "unknown.png" + k.flag = '0' break elif result == 0: console.logdebug("find: %s" % (str(rocon_master_uri))) args = output.communicate()[0] - k['name'] = args.split('\n')[0] - k['description'] = args.split('\n')[1] - k['icon'] = args.split('\n')[2] + k.name = args.split('\n')[0] + k.description = args.split('\n')[1] + k.icon = args.split('\n')[2] - if k['name'] == "Unknown": - k['flag'] = '0' + if k.name == "Unknown": + k.flag = '0' else: - k['flag'] = '1' + k.flag = '1' break time.sleep(0.1) time_out_cnt += 1 - 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 _delete_all_rocon_masters(self): - for k in self.rocon_master_list.values(): - del self.rocon_master_list[k["index"]] + self.rocon_masters.clear() self._update_rocon_master_list() + self._widget_main.list_info_widget.clear() def _delete_rocon_master(self): - if self.cur_selected_rocon_master in self.rocon_master_list.keys(): - del self.rocon_master_list[self.cur_selected_rocon_master] + if self.cur_selected_rocon_master in self.rocon_masters.keys(): + self.rocon_masters.delete(self.cur_selected_rocon_master) self._update_rocon_master_list() + self._widget_main.list_info_widget.clear() - def _add_rocon_master(self, params): - rocon_master_uri = str(params['param1'].toPlainText()) - rocon_master_host_name = str(params['param2'].toPlainText()) - rocon_master_index = str(uuid.uuid4()) - 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'] = "Unknown" - 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'] = "unknown.png" - self.rocon_master_list[rocon_master_index]['description'] = "" - self.rocon_master_list[rocon_master_index]['flag'] = "0" - - self._refresh_rocon_master(self.rocon_master_list[rocon_master_index]) + def _add_rocon_master(self, uri_text_widget, host_name_text_widget): + rocon_master = self.rocon_masters.add(uri_text_widget.toPlainText(), host_name_text_widget.toPlainText()) + self._refresh_rocon_master(rocon_master) def _set_add_rocon_master(self): @@ -484,9 +446,8 @@ def _set_add_rocon_master(self): button_hor_sub_widget = QWidget() button_hor_layout = QHBoxLayout(button_hor_sub_widget) - params = {} - params['param1'] = context_widget1 - params['param2'] = context_widget2 + uri_text_widget = context_widget1 + host_name_text_widget = context_widget2 #check box use_env_var_check = QCheckBox("Use environment variables") @@ -513,7 +474,7 @@ def check_event(data): btn_cancel = QPushButton("Cancel") btn_call.clicked.connect(lambda: self._connect_dlg.done(0)) - btn_call.clicked.connect(lambda: self._add_rocon_master(params)) + btn_call.clicked.connect(lambda: self._add_rocon_master(uri_text_widget, host_name_text_widget)) btn_cancel.clicked.connect(lambda: self._connect_dlg.done(0)) @@ -531,57 +492,24 @@ def _refresh_rocon_master(self, rocon_master): self._check_up_one(rocon_master) self._widget_main.list_info_widget.clear() self._update_rocon_master_list() - pass def _refresh_all_rocon_master_list(self): - self._read_cache() if self.is_init: self._check_up_all() self._widget_main.list_info_widget.clear() self._update_rocon_master_list() def _update_rocon_master_list(self): - self._widget_main.list_widget.clear() - try: - cache_rocon_master_info_list = open(self.rocon_master_list_cache_path, 'w') - except: - console.logdebug("No directory or file: %s" % (self.rocon_master_list_cache_path)) - return - for k in self.rocon_master_list.values(): - self._add_rocon_master_list_item(k) - rocon_master_index = k['index'] - rocon_master_name = k['name'] - rocon_master_uri = k['master_uri'] - rocon_master_host_name = k['host_name'] - rocon_master_icon = k['icon'] - rocon_master_description = k['description'] - rocon_master_flag = k['flag'] - - rocon_master_elem = '[' - rocon_master_elem += 'index=' + str(rocon_master_index) + ',' - rocon_master_elem += 'name=' + str(rocon_master_name) + ',' - rocon_master_elem += 'master_uri=' + str(rocon_master_uri) + ',' - rocon_master_elem += 'host_name=' + str(rocon_master_host_name) + ',' - rocon_master_elem += 'description=' + str(rocon_master_description) + ',' - rocon_master_elem += 'icon=' + rocon_master_icon + ',' - rocon_master_elem += 'flag=' + rocon_master_flag - rocon_master_elem += ']\n' - - cache_rocon_master_info_list.write(rocon_master_elem) - cache_rocon_master_info_list.close() + for rocon_master in self.rocon_masters.values(): + self._add_rocon_master_list_item(rocon_master) + self.rocon_masters.dump() def _add_rocon_master_list_item(self, rocon_master): - rocon_master_index = rocon_master['index'] - rocon_master_name = rocon_master['name'] - rocon_master_uri = rocon_master['master_uri'] - rocon_master_host_name = rocon_master['host_name'] - rocon_master_icon = rocon_master['icon'] - rocon_master_description = rocon_master['description'] - rocon_master['cur_row'] = str(self._widget_main.list_widget.count()) + rocon_master.current_row = str(self._widget_main.list_widget.count()) - display_name = str(rocon_master_name) + "\n" + "[" + str(rocon_master_uri) + "]" + display_name = str(rocon_master.name) + "\n" + "[" + str(rocon_master.uri) + "]" self._widget_main.list_widget.insertItem(self._widget_main.list_widget.count(), display_name) #setting the list font @@ -592,37 +520,37 @@ def _add_rocon_master_list_item(self, rocon_master): #setToolTip rocon_master_info = "" - rocon_master_info += "rocon_master_index: " + str(rocon_master_index) + "\n" - rocon_master_info += "rocon_master_name: " + str(rocon_master_name) + "\n" - rocon_master_info += "master_uri: " + str(rocon_master_uri) + "\n" - rocon_master_info += "host_name: " + str(rocon_master_host_name) + "\n" - rocon_master_info += "description: " + str(rocon_master_description) + rocon_master_info += "rocon_master_index: " + str(rocon_master.index) + "\n" + rocon_master_info += "rocon_master_name: " + str(rocon_master.name) + "\n" + rocon_master_info += "master_uri: " + str(rocon_master.uri) + "\n" + rocon_master_info += "host_name: " + str(rocon_master.host_name) + "\n" + rocon_master_info += "description: " + str(rocon_master.description) self._widget_main.list_widget.item(self._widget_main.list_widget.count() - 1).setToolTip(rocon_master_info) #set icon - if rocon_master_icon == "unknown.png": + if rocon_master.icon == "unknown.png": icon = QIcon(self.icon_paths['unknown']) self._widget_main.list_widget.item(self._widget_main.list_widget.count() - 1).setIcon(icon) - elif len(rocon_master_icon): - icon = QIcon(os.path.join(utils.get_icon_cache_home(), rocon_master_icon)) + elif len(rocon_master.icon): + icon = QIcon(os.path.join(utils.get_icon_cache_home(), rocon_master.icon)) self._widget_main.list_widget.item(self._widget_main.list_widget.count() - 1).setIcon(icon) else: - console.logdebug("%s : No icon" % rocon_master_name) + console.logdebug("%s : No icon" % rocon_master.name) pass def _select_rocon_master(self, Item): list_widget = Item.listWidget() - for k in self.rocon_master_list.values(): - if k["cur_row"] == str(list_widget.currentRow()): - self.cur_selected_rocon_master = k['index'] + for k in self.rocon_masters.values(): + if k.current_row == str(list_widget.currentRow()): + self.cur_selected_rocon_master = k.index break self._widget_main.list_info_widget.clear() info_text = "" info_text += "

-------------------------------------------

" - info_text += "

name: " + str(self.rocon_master_list[self.cur_selected_rocon_master]['name']) + "

" - info_text += "

master_uri: " + str(self.rocon_master_list[self.cur_selected_rocon_master]['master_uri']) + "

" - info_text += "

host_name: " + str(self.rocon_master_list[self.cur_selected_rocon_master]['host_name']) + "

" - info_text += "

description: " + str(self.rocon_master_list[self.cur_selected_rocon_master]['description']) + "

" + info_text += "

name: " + str(self.rocon_masters[self.cur_selected_rocon_master].name) + "

" + info_text += "

master_uri: " + str(self.rocon_masters[self.cur_selected_rocon_master].uri) + "

" + info_text += "

host_name: " + str(self.rocon_masters[self.cur_selected_rocon_master].host_name) + "

" + info_text += "

description: " + str(self.rocon_masters[self.cur_selected_rocon_master].description) + "

" info_text += "

-------------------------------------------

" info_text += "" self._widget_main.list_info_widget.appendHtml(info_text) @@ -631,13 +559,13 @@ def _destroy_connect_dlg(self): self._connect_dlg_isValid = False def _connect_rocon_master(self): - rocon_master_name = str(self.rocon_master_list[self.cur_selected_rocon_master]['name']) - rocon_master_uri = str(self.rocon_master_list[self.cur_selected_rocon_master]['master_uri']) - rocon_master_host_name = str(self.rocon_master_list[self.cur_selected_rocon_master]['host_name']) + 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._check_up_one(self.rocon_master_list[rocon_master_index]) - if self.rocon_master_list[rocon_master_index]['flag'] == '0': + self._check_up_one(self.rocon_masters[rocon_master_index]) + if self.rocon_masters[rocon_master_index].flag == '0': # DJS: unused reply box? QMessageBox.warning(self, 'ERROR', "YOU SELECT NO CONCERT", QMessageBox.Ok | QMessageBox.Ok) return diff --git a/rocon_remocon/src/rocon_remocon/rocon_masters.py b/rocon_remocon/src/rocon_remocon/rocon_masters.py new file mode 100644 index 0000000..2317c4a --- /dev/null +++ b/rocon_remocon/src/rocon_remocon/rocon_masters.py @@ -0,0 +1,160 @@ +#!/usr/bin/env python +# +# License: BSD +# https://raw.github.com/robotics-in-concert/rocon_qt_gui/license/LICENSE +# +############################################################################## +# Imports +############################################################################## + +import os +import string +import uuid + +import rocon_console.console as console + +from . import utils + +############################################################################## +# Methods +############################################################################## + + +def rocon_masters_cache_path(): + return os.path.join(utils.get_settings_cache_home(), "rocon_master.cache") + +############################################################################## +# Classes +############################################################################## + + +class RoconMaster(object): + __slots__ = [ + 'index', + 'name', + 'uri', + 'host_name', + 'description', + 'icon', + 'flag', + 'current_row', + ] + + def __str__(self): + s = self.name + return s + + +class RoconMasters(object): + + __slots__ = [ + 'rocon_masters', # { uuid : RoconMaster } + ] + + def __init__(self): + self.rocon_masters = {} + self.load() + + ###################################### + # Emulating a dict + ###################################### + + def __contains__(self, index): + return index in self.rocon_masters.keys() + + def __getitem__(self, index): + return self.rocon_masters[index] + + def __len__(self): + return len(self.rocon_masters) + + def keys(self): + return self.rocon_masters.keys() + + def values(self): + return self.rocon_masters.values() + + def clear(self): + self.rocon_masters.clear() + + ###################################### + # Introspection + ###################################### + + def __str__(self): + s = console.cyan + 'Rocon Masters:' + console.reset + for rocon_master in self.rocon_masters.values(): + s += '\n ' + console.yellow + str(rocon_master) + console.reset + return s + + ###################################### + # Utility Functions + ###################################### + + def delete(self, index): + del self.rocon_masters[index] + + def add(self, uri, host_name): + rocon_master = RoconMaster() + rocon_master.index = str(uuid.uuid4()) + rocon_master.name = "Unknown" + rocon_master.uri = uri + rocon_master.host_name = host_name + rocon_master.icon = "unknown.png" + rocon_master.description = "" + rocon_master.flag = "0" + self.rocon_masters[rocon_master.index] = rocon_master + return rocon_master + + ###################################### + # Caching + ###################################### + + def load(self): + """ + Gets a list of rocon masters and their info from cached data stored upon + last exit of this application. + """ + #read cache and display the rocon master list + self.rocon_masters = {} + try: + cache_rocon_master_info_list = open(rocon_masters_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 = RoconMaster() + 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_masters[rocon_master.index] = rocon_master + cache_rocon_master_info_list.close() + + def dump(self): + """ + Dump the rocon masters to a cache file. + """ + try: + cache_rocon_master_info_list = open(rocon_masters_cache_path(), 'w') + except: + console.logerror("Remocon : no directory or file: %s" % rocon_masters_cache_path()) + return + for rocon_master in self.rocon_masters.values(): + rocon_master_elem = '[' + rocon_master_elem += 'index=' + str(rocon_master.index) + ',' + rocon_master_elem += 'name=' + str(rocon_master.name) + ',' + rocon_master_elem += 'master_uri=' + str(rocon_master.uri) + ',' + rocon_master_elem += 'host_name=' + str(rocon_master.host_name) + ',' + rocon_master_elem += 'description=' + str(rocon_master.description) + ',' + rocon_master_elem += 'icon=' + rocon_master.icon + ',' + rocon_master_elem += 'flag=' + rocon_master.flag + rocon_master_elem += ']\n' + + cache_rocon_master_info_list.write(rocon_master_elem) + cache_rocon_master_info_list.close()