Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web interaction handling for the remocon and... #99

Merged
merged 3 commits into from
Apr 29, 2014
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion rocon_remocon/.project
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<comment></comment>
<projects>
<project>rocon_console</project>
<project>rocon_hue</project>
<project>rocon_interactions</project>
<project>rocon_python_comms</project>
<project>rocon_python_utils</project>
<project>rocon_uri</project>
Expand Down
1 change: 1 addition & 0 deletions rocon_remocon/package.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<run_depend>rocon_app_manager_msgs</run_depend>
<run_depend>rocon_console</run_depend>
<run_depend>rocon_icons</run_depend>
<run_depend>rocon_interactions</run_depend>
<run_depend>rocon_interaction_msgs</run_depend>
<run_depend>rocon_python_comms</run_depend>
<run_depend>rocon_python_utils</run_depend>
Expand Down
306 changes: 117 additions & 189 deletions rocon_remocon/src/rocon_remocon/remocon.py

Large diffs are not rendered by default.

137 changes: 83 additions & 54 deletions rocon_remocon/src/rocon_remocon/remocon_info.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -102,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
Expand All @@ -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")
Expand All @@ -136,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)

Expand All @@ -158,33 +161,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")
Expand Down Expand Up @@ -219,28 +222,25 @@ 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

if self.is_app_running == True:
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'])

if call_result.error_code == ErrorCodes.SUCCESS:
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"
Expand All @@ -253,8 +253,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:
Expand All @@ -271,10 +271,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)
Expand Down Expand Up @@ -358,10 +362,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)
Expand All @@ -375,7 +404,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[])
Expand All @@ -395,7 +424,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"):
Expand All @@ -407,48 +436,48 @@ 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"])
self._pub_remocon_status(app_hash, False)
print "[remocon_info] updated app list- %s" % str(self.interactions[app_hash]["launch_list"])
self._pub_remocon_status()
return True

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 unused_interaction_hash, v in self.interactions.iteritems():
if name in v['launch_list']:
del v['launch_list'][name]
if not v['launch_list']:
console.logdebug("RemoconInfo : process_listener caught terminating app [%s]" % name)
# 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()
Loading