From a854af8cb53481f6e324735ce1ea696a761d9d42 Mon Sep 17 00:00:00 2001 From: dwlee Date: Mon, 12 Jan 2015 17:48:38 +0900 Subject: [PATCH 01/21] create utils.py and create cache maker regarding solution configuration --- .../src/concert_service_manager/__init__.py | 1 + .../service_manager.py | 3 +- .../concert_service_manager/service_pool.py | 81 +++++++++++++++---- .../service_profile.py | 48 ++++++++--- .../src/concert_service_manager/utils.py | 61 ++++++++++++++ 5 files changed, 164 insertions(+), 30 deletions(-) create mode 100644 concert_service_manager/src/concert_service_manager/utils.py diff --git a/concert_service_manager/src/concert_service_manager/__init__.py b/concert_service_manager/src/concert_service_manager/__init__.py index a4b4c5c..e5f64a7 100644 --- a/concert_service_manager/src/concert_service_manager/__init__.py +++ b/concert_service_manager/src/concert_service_manager/__init__.py @@ -11,3 +11,4 @@ from .service_pool import ServicePool from .exceptions import InvalidSolutionConfigurationException, InvalidServiceProfileException from .service_profile import ServiceProfile +from .utils import * diff --git a/concert_service_manager/src/concert_service_manager/service_manager.py b/concert_service_manager/src/concert_service_manager/service_manager.py index e056054..8faf6b5 100644 --- a/concert_service_manager/src/concert_service_manager/service_manager.py +++ b/concert_service_manager/src/concert_service_manager/service_manager.py @@ -50,7 +50,7 @@ def __init__(self): self._interactions_loader = rocon_interactions.InteractionsLoader() roslaunch.pmon._init_signal_handlers() try: - self._service_pool = ServicePool(self._parameters['solution_configuration']) + self._service_pool = ServicePool(self._parameters['solution_configuration'], self._parameters['concert_name']) except (rospkg.ResourceNotFound, InvalidSolutionConfigurationException) as e: raise e self._publishers = self._setup_ros_publishers() @@ -72,6 +72,7 @@ def __init__(self): def _setup_ros_parameters(self): rospy.logdebug("Service Manager : parsing parameters") parameters = {} + parameters['concert_name'] = rospy.get_param('/concert/name/', "") parameters['solution_configuration'] = rospy.get_param('~services', "") #@IgnorePep8 parameters['auto_enable_services'] = rospy.get_param('~auto_enable_services', []) #@IgnorePep8 return parameters diff --git a/concert_service_manager/src/concert_service_manager/service_pool.py b/concert_service_manager/src/concert_service_manager/service_pool.py index deff4bb..93b7926 100644 --- a/concert_service_manager/src/concert_service_manager/service_pool.py +++ b/concert_service_manager/src/concert_service_manager/service_pool.py @@ -19,6 +19,7 @@ from .exceptions import InvalidSolutionConfigurationException from .exceptions import InvalidServiceProfileException, NoServiceExistsException from .service_profile import ServiceProfile +from .utils import * ############################################################################## # Classes @@ -70,16 +71,18 @@ def load_solution_configuration(yaml_file): class ServiceConfigurationData(object): + """ Represents a single entry in the solution service configuration (.services) file. We hash the important bits so we can track when the file changes. """ __slots__ = [ - 'resource_name', # ros resource name for the service - 'overrides', # dictionary of override keys for the service (use a dic so we can pack it into a ros msg later) - 'hash', # hash of this configuration data - ] + 'resource_name', # ros resource name for the service + 'overrides', # dictionary of override keys for the service (use a dic so we can pack it into a ros msg later) + 'hash', # hash of this configuration data + '_is_reading_from_cache' # flag for checking whether reading chache or not + ] override_keys = ['name', 'description', 'icon', 'priority', 'interactions', 'parameters'] @@ -110,6 +113,7 @@ def __init__(self, resource_name, overrides): class ServicePool(object): + """ Stores the current solution's service related configuration. This is obtained from a ros resource yaml file which typically specifies the @@ -117,13 +121,13 @@ class ServicePool(object): configuration they may have. """ __slots__ = [ - '_yaml_file', # os.path to solution configuration file - '_last_modified', # timestamp of last file modifications - 'service_profiles', # { name : ServiceProfile } - '_cached_service_profile_locations' # all known service profiles on the ros package path : { resource_name : os.path to .service file } - ] + '_yaml_file', # os.path to solution configuration file + '_last_modified', # timestamp of last file modifications + 'service_profiles', # { name : ServiceProfile } + '_cached_service_profile_locations' # all known service profiles on the ros package path : { resource_name : os.path to .service file } + ] - def __init__(self, resource_name): + def __init__(self, resource_name, concert_name): """ Initialise the class with a pointer to the yaml that will be scanned and later monitored for changes that can be applied @@ -131,6 +135,8 @@ def __init__(self, resource_name): :param resource_name: pkg/filename of a yaml formatted service configuration for a solution :type resource_name: str + :param resource_name: concert name for storing cache file + :type resource_name: str :raises: :exc:`rospkg.ResourceNotFound` if resource_name cannot be resolved. :raises: :exc:`concert_service_manager.InvalidSolutionConfigurationException` if the yaml provides invalid configuration @@ -141,16 +147,56 @@ def __init__(self, resource_name): self._cached_service_profile_locations = {} for cached_resource_name, (cached_filename, unused_catkin_package) in cached_service_profile_information.iteritems(): self._cached_service_profile_locations[cached_resource_name] = cached_filename + # init cache path + concert_name = concert_name.lower().replace(' ', '_') + setup_home_dirs(concert_name) # load if resource_name != "": try: - self._yaml_file = rocon_python_utils.ros.find_resource_from_string(resource_name) # rospkg.ResourceNotFound - self._last_modified = time.ctime(os.path.getmtime(self._yaml_file)) + (self._is_reading_from_cache, self._yaml_file) = self._check_cache(rocon_python_utils.ros.find_resource_from_string(resource_name), concert_name) # rospkg.ResourceNotFound service_configurations = load_solution_configuration(self._yaml_file) # InvalidSolutionConfigurationException self._load_service_profiles(service_configurations) + self._save_yaml_cache(concert_name) + self._last_modified = time.ctime(os.path.getmtime(self._yaml_file)) except (rospkg.ResourceNotFound, InvalidSolutionConfigurationException) as e: raise e + def _save_yaml_cache(self, concert_name): + """ + Save yaml cache file in temporary directory. If service manager is restarted, it load cached yaml file. + + :param yaml_file: temporary location for storing cache file as concert name + :type str + """ + + yaml_file_name = self._yaml_file.split('/')[-1] + if '.services' in yaml_file_name: + yaml_stream = yaml.load(open(self._yaml_file, 'r')) + cache_yaml_file = get_home(concert_name) + '/' + yaml_file_name + cache_yaml_stream = file(cache_yaml_file, 'w') + yaml.safe_dump(yaml_stream, cache_yaml_stream, default_flow_style=False) + + def _check_cache(self, yaml_file, concert_name): + """ + Check whether cached yaml file is existed or not + + :param yaml_file: yaml file name about services list for checking + :type str + :param yaml_file: temporary location for checking as concert name + :type str + + :returns: flag and full path yaml file as result of check existence about cache file. If cache file is existed, return true and cache path. Otherwise, return false and original path + :rtype: (bool, str) + + """ + + yaml_file_name = yaml_file.split('/')[-1] + cache_yaml_file = get_home(concert_name) + '/' + yaml_file_name + if os.path.isfile(cache_yaml_file): + return (True, cache_yaml_file) + else: + return (False, yaml_file) + def __str__(self): s = '' s += console.bold + 'Service Profiles: \n' + console.reset @@ -173,11 +219,12 @@ def _load_service_profiles(self, service_configurations): for service_configuration in service_configurations: try: service_profile = ServiceProfile( - service_configuration.hash, - service_configuration.resource_name, - service_configuration.overrides, - self._cached_service_profile_locations - ) + service_configuration.hash, + service_configuration.resource_name, + service_configuration.overrides, + self._cached_service_profile_locations, + self._is_reading_from_cache + ) except (InvalidServiceProfileException) as e: rospy.logwarn("Service Manager : %s" % e) continue diff --git a/concert_service_manager/src/concert_service_manager/service_profile.py b/concert_service_manager/src/concert_service_manager/service_profile.py index be032e0..0c012eb 100644 --- a/concert_service_manager/src/concert_service_manager/service_profile.py +++ b/concert_service_manager/src/concert_service_manager/service_profile.py @@ -17,6 +17,8 @@ import unique_id import rocon_console.console as console import scheduler_msgs.msg as scheduler_msgs +import rocon_std_msgs.msg as rocon_std_msgs + from .exceptions import InvalidServiceProfileException @@ -27,17 +29,19 @@ class ServiceProfile(object): __slots__ = [ - 'msg', # basic data storage for the profile - concert_msgs.ServiceProfile - '_last_modified', # timestamp of last .service file modification - 'hash', # hashlib.sha224 used by the service pool to track this profile - 'resource_name', # local copy of the resource name - '_overrides', # any overrides that need to be applied after any loading - '_location_cache', # pointer to the service profile location cache maintained by the service pool { resource_name : filename } - # aliases - 'name' - ] - - def __init__(self, hash_id, resource_name, overrides, service_profile_location_cache): + 'msg', # basic data storage for the profile - concert_msgs.ServiceProfile + '_last_modified', # timestamp of last .service file modification + 'hash', # hashlib.sha224 used by the service pool to track this profile + 'resource_name', # local copy of the resource name + '_overrides', # any overrides that need to be applied after any loading + '_location_cache', # pointer to the service profile location cache maintained by the service pool { resource_name : filename } + # aliases + 'name', + '_concert_name', # concert name for searching cached service profile + '_is_config_from_cache' # flag informed whether solution config is loaded from cahce or not + ] + + def __init__(self, hash_id, resource_name, overrides, service_profile_location_cache, is_reading_solution_config_from_cache): """ Loads the service profile given the data provided by the solution configuration (i.e. resource name + overrides). We provide an arg for the filename here even @@ -56,6 +60,9 @@ def __init__(self, hash_id, resource_name, overrides, service_profile_location_c :param service_profile_location_cache: this class will use and update the cache if it's own filename changes :type service_profile_location_cache: { resource_name : os pathname } + :param is_reading_service_profile_from_cache: flag informed whether solution config is loaded from cahce file or not + :type is_reading_service_profile_from_cache: bool + :returns: the solution configuration data for services in this concert :rtype: concert_msgs.ServiceProfile @@ -66,6 +73,7 @@ def __init__(self, hash_id, resource_name, overrides, service_profile_location_c self._overrides = overrides self.resource_name = resource_name self._last_modified = None # gets updated when we load the profile + self._is_config_from_cache = is_reading_solution_config_from_cache try: self.msg = self._load_profile() self._last_modified = time.ctime(os.path.getmtime(self._filename())) @@ -145,7 +153,23 @@ def _load_profile(self): # fill in unique identifier used by services and their requesters msg.uuid = unique_id.toMsg(unique_id.fromRandom()) - + # dump parameter detail as type of key-value + if 'parameters' in loaded_profile.keys(): + replace['parameters_detail'] = [] + # todo if cache, if original + parameters_yaml_file = '' + if self._is_config_from_cache: + parameters_yaml_file = rocon_python_utils.ros.find_resource_from_string(loaded_profile['parameters'] + '.parameters') + else: + parameters_yaml_file = '' + try: + with open(parameters_yaml_file) as f: + parameters_yaml = yaml.load(f) + for param_key in parameters_yaml.keys(): + msg.parameters_detail.append(rocon_std_msgs.KeyValue(param_key, parameters_yaml[param_key])) + except rospkg.ResourceNotFound as e: + raise e + return msg def reload(self): diff --git a/concert_service_manager/src/concert_service_manager/utils.py b/concert_service_manager/src/concert_service_manager/utils.py new file mode 100644 index 0000000..0223e9c --- /dev/null +++ b/concert_service_manager/src/concert_service_manager/utils.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# +# License: BSD +# https://raw.github.com/robotics-in-concert/rocon_concert/license/LICENSE +# +############################################################################## +# Imports +############################################################################## + +import os +import rocon_python_utils +import rospkg +import rocon_std_msgs.msg as rocon_std_msgs + +############################################################################## +# Methods +############################################################################## + + +def setup_home_dirs(concert_name): + if not os.path.isdir(get_home(concert_name)): + print 'create path: ' + str(get_home(concert_name)) + os.makedirs(get_home(concert_name)) + + +def setup_service_home_dirs(concert_name, service_name): + if not os.path.isdir(get_service_icon_cache_home(concert_name, service_name)): + os.makedirs(get_service_icon_cache_home(concert_name, service_name)) + if not os.path.isdir(get_service_config_cache_home(concert_name, service_name)): + os.makedirs(get_service_config_cache_home(concert_name, service_name)) + + +def get_home(concert_name): + ''' + Retrieve the location of the home directory for the service manager + temporary storage needs as concert name + + @return the service manager home directory (path object). + @type str + ''' + return os.path.join(rospkg.get_ros_home(), 'rocon', concert_name) + + +def get_service_icon_cache_home(concert_name, service_name): + ''' + Retrieve the location of the directory used for storing icons about service. + + @return the rocon remocon icons directory (path object). + @type str + ''' + return os.path.join(get_home(concert_name), 'icons') + + +def get_service_config_cache_home(concert_name, service_name): + ''' + Retrieve the location of the directory used for storing service configuration. + + @return the rocon remocon qt settings directory (path object). + @type str + ''' + return os.path.join(get_home(concert_name), 'service_name') From 9f3b170820abe0d4e5336119c934151195b9ad3b Mon Sep 17 00:00:00 2001 From: dwlee Date: Thu, 15 Jan 2015 16:10:26 +0900 Subject: [PATCH 02/21] function name change --- .../src/concert_service_manager/utils.py | 23 ++++--------------- 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/concert_service_manager/src/concert_service_manager/utils.py b/concert_service_manager/src/concert_service_manager/utils.py index 0223e9c..980b93c 100644 --- a/concert_service_manager/src/concert_service_manager/utils.py +++ b/concert_service_manager/src/concert_service_manager/utils.py @@ -23,12 +23,9 @@ def setup_home_dirs(concert_name): os.makedirs(get_home(concert_name)) -def setup_service_home_dirs(concert_name, service_name): - if not os.path.isdir(get_service_icon_cache_home(concert_name, service_name)): - os.makedirs(get_service_icon_cache_home(concert_name, service_name)) - if not os.path.isdir(get_service_config_cache_home(concert_name, service_name)): - os.makedirs(get_service_config_cache_home(concert_name, service_name)) - +def setup_service_profile_home_dirs(concert_name, service_name): + if not os.path.isdir(get_service_profile_cache_home(concert_name, service_name)): + os.makedirs(get_service_profile_cache_home(concert_name, service_name)) def get_home(concert_name): ''' @@ -41,21 +38,11 @@ def get_home(concert_name): return os.path.join(rospkg.get_ros_home(), 'rocon', concert_name) -def get_service_icon_cache_home(concert_name, service_name): - ''' - Retrieve the location of the directory used for storing icons about service. - - @return the rocon remocon icons directory (path object). - @type str - ''' - return os.path.join(get_home(concert_name), 'icons') - - -def get_service_config_cache_home(concert_name, service_name): +def get_service_profile_cache_home(concert_name, service_name): ''' Retrieve the location of the directory used for storing service configuration. @return the rocon remocon qt settings directory (path object). @type str ''' - return os.path.join(get_home(concert_name), 'service_name') + return os.path.join(get_home(concert_name), 'services', service_name) From e7837f34ec27015c89bc092b89964472fc5897cd Mon Sep 17 00:00:00 2001 From: dwlee Date: Thu, 15 Jan 2015 16:11:44 +0900 Subject: [PATCH 03/21] create service profile cache manager --- .../src/concert_service_manager/__init__.py | 1 + .../service_cache_manager.py | 422 ++++++++++++++++++ 2 files changed, 423 insertions(+) create mode 100644 concert_service_manager/src/concert_service_manager/service_cache_manager.py diff --git a/concert_service_manager/src/concert_service_manager/__init__.py b/concert_service_manager/src/concert_service_manager/__init__.py index e5f64a7..f6ae1ca 100644 --- a/concert_service_manager/src/concert_service_manager/__init__.py +++ b/concert_service_manager/src/concert_service_manager/__init__.py @@ -11,4 +11,5 @@ from .service_pool import ServicePool from .exceptions import InvalidSolutionConfigurationException, InvalidServiceProfileException from .service_profile import ServiceProfile +from .service_cache_manager import ServiceCacheManager from .utils import * diff --git a/concert_service_manager/src/concert_service_manager/service_cache_manager.py b/concert_service_manager/src/concert_service_manager/service_cache_manager.py new file mode 100644 index 0000000..ba6149d --- /dev/null +++ b/concert_service_manager/src/concert_service_manager/service_cache_manager.py @@ -0,0 +1,422 @@ +# License: BSD +# https://raw.github.com/robotics-in-concert/rocon_concert/license/LICENSE +# +############################################################################## +# Imports +############################################################################## + +import hashlib +import os.path +import time +import yaml +import copy + +import rospkg +import roslib.names +import rospy +import unique_id +import rocon_python_utils +import rocon_std_msgs.msg as rocon_std_msgs +import concert_msgs.msg as concert_msgs +import scheduler_msgs.msg as scheduler_msgs + +from .exceptions import InvalidSolutionConfigurationException +from .exceptions import InvalidServiceProfileException, NoServiceExistsException +from .utils import * + +############################################################################## +# Classes +############################################################################## + + +def load_solution_configuration_from_resource(yaml_file): + """ + Load the solution configuration from a yaml file. This is a pretty + simple file, just a list of services specified by resource names + along with overrides for that service. + + The overrides can be empty (None) or is an unstructured yaml + representing configuration of the service that is modified. + This is not validated against the actual service here. + + :param yaml_file: filename of the solution configuration file + :type yaml_file: str + :param is_cached: todo + :type is_cached: bool + + :returns: the solution configuration data for services in this concert + :rtype: [ServiceData] + + :raises: :exc:`concert_service_manager.InvalidSolutionConfigurationException` if the yaml provides invalid configuration + """ + service_configurations = [] + + # read + override_keys = ['name', 'description', 'icon', 'priority', 'interactions', 'parameters'] + with open(yaml_file) as f: + service_list = yaml.load(f) + for s in service_list: + service_data = {} + service_data['resource_name'] = s['resource_name'] + loaded_overrides = s['overrides'] if 'overrides' in s else None + overrides = {} + for key in override_keys: + overrides[key] = loaded_overrides[key] if loaded_overrides and key in loaded_overrides.keys() else None + # warnings + if loaded_overrides: + invalid_keys = [key for key in loaded_overrides.keys() if key not in override_keys] + for key in invalid_keys: + rospy.logwarn("Service Manager : invalid key in the service soln configuration yaml [%s]" % key) + service_data['overrides'] = copy.deepcopy(overrides) + service_configurations.append(service_data) + + # validate + identifiers = [] + for service_data in service_configurations: + if service_data['overrides']['name'] is not None and 'name' in service_data['overrides'].keys(): + identifier = service_data['overrides']['name'] + else: + identifier = service_data['resource_name'] + + if identifier in identifiers: + raise InvalidSolutionConfigurationException("service configuration found with duplicate names [%s]" % identifier) + else: + identifiers.append(identifier) + return service_configurations + + +class ServiceCacheManager(object): + + __slots__ = [ + '_concert_name', # todo + '_resource_name', # todo + 'service_profiles', # todo + 'msg', # todo + '_cache_list', # todo + ] + + def __init__(self, concert_name, resource_name): + self._concert_name = concert_name.lower().replace(' ', '_') + self._resource_name = resource_name + self.service_profiles = {} + setup_home_dirs(self._concert_name) + self._cache_list = {} + + yaml_file = self._check_cache() + print "checking cache result: " + yaml_file + self._load_services_cache(yaml_file) + self._init_cache_list() + + def _init_cache_list(self): + """ + Todo + + """ + self._cache_list = {} + for root, dirs, files in os.walk(get_home(self._concert_name)): + for dir_name in dirs: + dir_path = str(os.path.join(root, dir_name)) + self._cache_list[dir_path] = {} + self._cache_list[dir_path]['type'] = 'directory' + self._cache_list[dir_path]['last_modified_time'] = time.ctime(os.path.getmtime(dir_path)) + for file_name in files: + file_path = str(os.path.join(root, file_name)) + self._cache_list[file_path] = {} + self._cache_list[file_path]['type'] = 'file' + self._cache_list[file_path]['last_modified_time'] = time.ctime(os.path.getmtime(file_path)) + + def _get_cache_list(self): + """ + Todo + + :returns: cache list + :rtype: dict + + """ + cache_list = {} + for root, dirs, files in os.walk(get_home(self._concert_name)): + for dir_name in dirs: + dir_path = str(os.path.join(root, dir_name)) + cache_list[dir_path] = {} + cache_list[dir_path]['type'] = 'directory' + cache_list[dir_path]['last_modified_time'] = time.ctime(os.path.getmtime(dir_path)) + + for file_name in files: + file_path = str(os.path.join(root, file_name)) + cache_list[file_path] = {} + cache_list[file_path]['type'] = 'file' + cache_list[file_path]['last_modified_time'] = time.ctime(os.path.getmtime(file_path)) + return cache_list + + def _check_cache(self): + """ + Check whether cached yaml file is existed or not + + :returns: flag and full path yaml file as result of check existence about cache file. If cache file is existed, return true and cache path. Otherwise, return false and original path + :rtype: str + + """ + yaml_file = rocon_python_utils.ros.find_resource_from_string(self._resource_name) + yaml_file_name = yaml_file.split('/')[-1] + cache_yaml_file = get_home(self._concert_name) + '/' + yaml_file_name + if not os.path.isfile(cache_yaml_file) or os.stat(cache_yaml_file).st_size <= 0: + print "create cache!!" + self._create_cache() + print "Save solution config" + self._save_solution_configuration() + return cache_yaml_file + + def _create_cache(self): + """ + Todo + + """ + # read resource file + service_configurations = load_solution_configuration_from_resource(rocon_python_utils.ros.find_resource_from_string(self._resource_name)) + for service_configuration in service_configurations: + resource_name = service_configuration['resource_name'] + '.service' + overrides = service_configuration['overrides'] + + s_time = time.time() + print "create service: " + resource_name + + e_time = time.time() + print "create total time: " + str(e_time - s_time) + + try: + file_name = rocon_python_utils.ros.find_resource_from_string(resource_name) + with open(file_name) as f: + loaded_profile = yaml.load(f) + except rospkg.ResourceNotFound as e: + raise e + + loaded_profile['resource_name'] = resource_name + # set priority to default if it was not configured + if 'priority' not in loaded_profile.keys(): + loaded_profile['priority'] = scheduler_msgs.Request.DEFAULT_PRIORITY + for key in loaded_profile: + if key in overrides and overrides[key] is not None: + loaded_profile[key] = overrides[key] + if 'launcher_type' not in loaded_profile.keys(): # not set + loaded_profile['launcher_type'] = concert_msgs.ServiceProfile.TYPE_SHADOW + loaded_profile['name'] = loaded_profile['name'].lower().replace(" ", "_") + + if 'parameters' in loaded_profile.keys(): + loaded_profile['parameters_detail'] = [] + # todo if cache, if original + parameters_yaml_file = rocon_python_utils.ros.find_resource_from_string(loaded_profile['parameters'] + '.parameters') + try: + with open(parameters_yaml_file) as f: + parameters_yaml = yaml.load(f) + loaded_profile['parameters_detail'] = parameters_yaml + except rospkg.ResourceNotFound as e: + raise e + + # dump interaction detail as type of key-value + if 'interactions' in loaded_profile.keys(): + interactions_yaml_file = rocon_python_utils.ros.find_resource_from_string(loaded_profile['interactions'] + '.interactions') + try: + with open(interactions_yaml_file) as f: + interactions_yaml = yaml.load(f) + loaded_profile['interactions_detail'] = interactions_yaml + except rospkg.ResourceNotFound as e: + raise e + + print "complete making service profile" + self.service_profiles[loaded_profile['name']] = copy.deepcopy(loaded_profile) + print "complete making service profile message" + self._save_profile(loaded_profile) + print "complete saving service profile" + + def _load_services_cache(self, services_file_name): + ''' + Todo + + :param service_file_name: todo + :type service_file_name: str + + ''' + with open(services_file_name) as f: + service_list = yaml.load(f) + print 'service_list: ' + str(service_list) + for service in service_list: + print 'service name: ' + service['name'] + try: + service_file_name = get_service_profile_cache_home(self._concert_name, service['name']) + "/" + service['name'] + '.service' + with open(service_file_name) as f: + loaded_profile = yaml.load(f) + except rospkg.ResourceNotFound as e: + raise e + + loaded_profile['msg'] = self._generate_msg(loaded_profile) + self.service_profiles[loaded_profile['name']] = copy.deepcopy(loaded_profile) + + def _generate_msg(self, loaded_profile): + ''' + Todo + + :returns: generated service profile message + :rtype: [concert_msgs.ServiceProfile] + + ''' + + msg = concert_msgs.ServiceProfile() + msg.uuid = unique_id.toMsg(unique_id.fromRandom()) + # todo change more nice method + if 'resource_name' in loaded_profile: + msg.resource_name = loaded_profile['resource_name'] + if 'name' in loaded_profile: + msg.name = loaded_profile['name'] + if 'description' in loaded_profile: + msg.description = loaded_profile['description'] + if 'author' in loaded_profile: + msg.author = loaded_profile['author'] + if 'priority' in loaded_profile: + msg.priority = loaded_profile['priority'] + if 'launcher_type' in loaded_profile: + msg.launcher_type = loaded_profile['launcher_type'] + if 'icon' in loaded_profile: + msg.icon = rocon_python_utils.ros.icon_resource_to_msg(loaded_profile['icon']) + if 'launcher' in loaded_profile: + msg.launcher = loaded_profile['launcher'] + if 'interactions' in loaded_profile: + msg.interactions = loaded_profile['interactions'] + if 'parameters' in loaded_profile: + msg.parameters = loaded_profile['parameters'] + if 'parameters_detail' in loaded_profile: + for param_key in loaded_profile['parameters_detail'].keys(): + msg.parameters_detail.append(rocon_std_msgs.KeyValue(param_key, loaded_profile['parameters_detail'][param_key])) + + return msg + + def _save_profile(self, loaded_service_profile_from_file): + ''' + Todo + + :param loaded_service_profile_from_file: Todo + :type loaded_service_profile_from_file: dict + + :returns: generated service profile message + :rtype: [concert_msgs.ServiceProfile] + + ''' + loaded_profile = copy.deepcopy(loaded_service_profile_from_file) + service_name = loaded_profile['name'] + + setup_service_profile_home_dirs(self._concert_name, service_name) + service_profile_cache_home = get_service_profile_cache_home(self._concert_name, service_name) + + # writting interaction data + if 'interactions_detail' in loaded_profile.keys(): + service_interactions_file_name = service_profile_cache_home + '/' + service_name + '.interactions' + loaded_profile['interactions'] = service_interactions_file_name.split('/')[-1] + with file(service_interactions_file_name, 'w') as f: + yaml.safe_dump(loaded_profile['interactions_detail'], f, default_flow_style=False) + del (loaded_profile['interactions_detail']) + + # writting parameter data + if 'parameters_detail' in loaded_profile.keys(): + service_parameters_file_name = service_profile_cache_home + '/' + service_name + '.parameters' + loaded_profile['parameters'] = service_parameters_file_name.split('/')[-1] + with file(service_parameters_file_name, 'w') as f: + yaml.safe_dump(loaded_profile['parameters_detail'], f, default_flow_style=False) + + # if 'launcher' in loaded_profile.keys(): + # loaded_profile['launcher'] = rocon_python_utils.ros.find_resource_from_string(loaded_profile['launcher'] + '.launch') + + # writting service profile data + service_profile_file_name = service_profile_cache_home + '/' + service_name + '.service' + with file(service_profile_file_name, 'w') as f: + yaml.safe_dump(loaded_profile, f, default_flow_style=False) + + def _save_solution_configuration(self): + ''' + Todo + + ''' + solution_configuration = [] + for service_profile in self.service_profiles.keys(): + configuration_item = {} + configuration_item['name'] = service_profile + configuration_item['enabled'] = False + solution_configuration.append(configuration_item) + yaml_file_name = rocon_python_utils.ros.find_resource_from_string(self._resource_name).split('/')[-1] + if '.services' in yaml_file_name: + cache_srv_config_file = get_home(self._concert_name) + '/' + yaml_file_name + with file(cache_srv_config_file, 'w') as f: + yaml.safe_dump(solution_configuration, f, default_flow_style=False) + + def _find_resource_from_string(self, resource): + ''' + Todo, It is not implemented,yet. + + :param resource: Todo + :type resource: string + + ''' + # todo package name cahce + package, filename = roslib.names.package_resource_name(resource) + if not package: + raise rospkg.ResourceNotFound("resource could not be split with a valid leading package name [%s]" % (resource)) + try: + resolved = roslib.packages.find_resource(package, filename, None) + if not resolved: + raise rospkg.ResourceNotFound("cannot locate [%s] in package [%s]" % (filename, package)) + elif len(resolved) == 1: + return resolved[0] + elif len(resolved) > 1: + raise rospkg.ResourceNotFound("multiple resources named [%s] in package [%s]:%s\nPlease specify full path instead" % (filename, package, ''.join(['\n- %s' % r for r in resolved]))) + except rospkg.ResourceNotFound: + raise rospkg.ResourceNotFound("[%s] is not a package or launch file name [%s]" % (package, package + '/' + filename)) + return None + + def _find_by_hash(self, hash_id): + """ + Scan the service pool looking for profiles with the specified hash. This is + an internal convenience function. + + :returns: the service configuration for a match or None if not found + :rtype: ServiceConfigurationData or None + """ + for service_profile in self.service_profiles.values(): + if service_profile.hash.digest() == hash_id.digest(): + return service_profile + return None + + def find(self, name): + """ + Scan the service data to see if a service has been configured with the specified name. Check if it + has changed internally and reload it if necessary before returning it. + + :param name: name of the service profile to find + :type name: str + + :returns: the service profile that matches + :rtype: dict + + :raises: :exc:`concert_service_manager.NoServiceExistsException` if the service profile is not available + """ + try: + service_profile = self.service_profiles[name] + # check if the name changed + if service_profile['name'] != name: + rospy.logwarn("Service Manager : we are in the shits, the service profile name changed %s->%s" % (name, service_profile['name'])) + rospy.logwarn("Service Manager : TODO upgrade the find function with a full reloader") + raise NoServiceExistsException("we are in the shits, the service profile name changed %s->%s" % (name, service_profile['name'])) + return service_profile + except KeyError: + raise NoServiceExistsException("service profile not found in the configured service pool [%s]" % name) + + def check_cache_modification(self, update_callback): + ''' + Todo + + :param update_callback: + :type update_callback: method with no args + + ''' + if self._cache_list != self._get_cache_list(): + yaml_file = self._check_cache() + self._load_services_cache(yaml_file) + self._init_cache_list() + update_callback() From 2b1960cb5db243f3a141b714477973dc2885124e Mon Sep 17 00:00:00 2001 From: dwlee Date: Thu, 15 Jan 2015 16:12:29 +0900 Subject: [PATCH 04/21] add function for loading parameter from cache --- .../concert_service_manager/load_params.py | 20 +++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/concert_service_manager/src/concert_service_manager/load_params.py b/concert_service_manager/src/concert_service_manager/load_params.py index a812e02..827a19f 100644 --- a/concert_service_manager/src/concert_service_manager/load_params.py +++ b/concert_service_manager/src/concert_service_manager/load_params.py @@ -10,16 +10,32 @@ import yaml import rocon_python_utils +INVALID_PARAM = ['name', 'description', 'uuid'] + ############################################################################## # Methods ############################################################################## def load_parameters_from_file(parameter_file, namespace, name, load): + filepath = rocon_python_utils.ros.find_resource_from_string(parameter_file, extension='parameters') + + with open(filepath) as f: + params = yaml.load(f) + for k, v in params.items(): + if k in INVALID_PARAM: + if load: + rospy.logwarn("Service Manager: %s%s [%s]" % (str(k), ' is prohibitted parameter. Ignoring...', name)) + continue + param_name = namespace + '/' + k + if load: + rospy.set_param(param_name, v) + else: + rospy.delete_param(param_name) - INVALID_PARAM = ['name', 'description', 'uuid'] - filepath = rocon_python_utils.ros.find_resource_from_string(parameter_file, extension='parameters') +def load_parameters_from_cache(catche_file_path, namespace, name, load): + filepath = catche_file_path with open(filepath) as f: params = yaml.load(f) From 024e332d684b922f2e3007143929ebdf46232dff Mon Sep 17 00:00:00 2001 From: dwlee Date: Thu, 15 Jan 2015 16:13:32 +0900 Subject: [PATCH 05/21] update service manager and instance as adding service cache manager --- .../service_instance.py | 41 ++++++++++------- .../service_manager.py | 46 +++++++++---------- 2 files changed, 48 insertions(+), 39 deletions(-) diff --git a/concert_service_manager/src/concert_service_manager/service_instance.py b/concert_service_manager/src/concert_service_manager/service_instance.py index f9135b7..045d2de 100644 --- a/concert_service_manager/src/concert_service_manager/service_instance.py +++ b/concert_service_manager/src/concert_service_manager/service_instance.py @@ -19,7 +19,8 @@ import rocon_interactions import unique_id -from .load_params import load_parameters_from_file +from .load_params import load_parameters_from_cache +from .utils import * ############################################################################## # Methods @@ -33,25 +34,27 @@ def dummy_cb(): class ServiceInstance(object): __slots__ = [ - 'msg', # concert_msgs.ServiceProfile fixed and variable parameters - '_update_callback', # used to trigger an external callback (service manager publisher) when the state changes. - '_namespace', # namespace that the service will run in - '_lock', # protect service enabling/disabling - '_proc', # holds the custom subprocess variable if TYPE_CUSTOM - '_roslaunch', # holds the roslaunch parent variable if TYPE_ROSLAUNCH - '_shutdown_publisher', # used for disabling the service - # aliases - 'name', - ] + 'msg', # concert_msgs.ServiceProfile fixed and variable parameters + '_update_callback', # used to trigger an external callback (service manager publisher) when the state changes. + '_namespace', # namespace that the service will run in + '_lock', # protect service enabling/disabling + '_proc', # holds the custom subprocess variable if TYPE_CUSTOM + '_roslaunch', # holds the roslaunch parent variable if TYPE_ROSLAUNCH + '_shutdown_publisher', # used for disabling the service + '_concert_name', # todo + # aliases + 'name', + ] shutdown_timeout = 5 kill_timeout = 10 - def __init__(self, service_profile=None, env=os.environ, update_callback=dummy_cb): + def __init__(self, concert_name=None, service_profile=None, env=os.environ, update_callback=dummy_cb): ''' @param service_profile : @type concert_msgs.msg.ConcertService ''' + self._concert_name = concert_name.lower().replace(' ', '_') self.msg = service_profile # aliases self.name = self.msg.name @@ -84,13 +87,16 @@ def enable(self, unique_identifier, interactions_loader): # load up parameters first so that when start runs, it can find the params immediately if self.msg.parameters != '': namespace = concert_msgs.Strings.SERVICE_NAMESPACE + '/' + self.msg.name - load_parameters_from_file(self.msg.parameters, namespace, self.msg.name, load=True) + #load_parameters_from_file(self.msg.parameters, namespace, self.msg.name, load=True) + parameter_path = os.path.join(get_service_profile_cache_home(self._concert_name, self.name), self.name + '.parameters') + load_parameters_from_cache(parameter_path, namespace, self.msg.name, load=True) # Refresh the unique id self.msg.uuid = unique_id.toMsg(unique_identifier) self._start() if self.msg.interactions != '': # Can raise YamlResourceNotFoundException, MalformedInteractionsYaml - interactions_loader.load(self.msg.interactions, namespace=self._namespace, load=True) + interaction_path = os.path.join(get_service_profile_cache_home(self._concert_name, self.name), self.name + '.interactions') + interactions_loader.load(interaction_path, namespace=self._namespace, load=True, is_relative_path=False) # if there's a failure point, it will have thrown an exception before here. success = True self._update_callback() @@ -114,10 +120,13 @@ def disable(self, interactions_loader): try: if self.msg.interactions != '': # Can raise YamlResourceNotFoundException, MalformedInteractionsYaml - interactions_loader.load(self.msg.interactions, namespace=self._namespace, load=False) + interaction_path = os.path.join(get_service_profile_cache_home(self._concert_name, self.name), self.name + '.interactions') + interactions_loader.load(interaction_path, namespace=self._namespace, load=False, is_relative_path=False) if self.msg.parameters != '': namespace = concert_msgs.Strings.SERVICE_NAMESPACE + '/' + self.msg.name - load_parameters_from_file(self.msg.parameters, namespace, self.msg.name, load=False) + #load_parameters_from_file(self.msg.parameters, namespace, self.msg.name, load=False) + parameter_path = os.path.join(get_service_profile_cache_home(self._concert_name, self.name), self.name + '.parameters') + load_parameters_from_cache(parameter_path, namespace, self.msg.name, load=False) launcher_type = self.msg.launcher_type force_kill = False diff --git a/concert_service_manager/src/concert_service_manager/service_manager.py b/concert_service_manager/src/concert_service_manager/service_manager.py index 8faf6b5..c33e214 100644 --- a/concert_service_manager/src/concert_service_manager/service_manager.py +++ b/concert_service_manager/src/concert_service_manager/service_manager.py @@ -17,7 +17,7 @@ from .exceptions import NoServiceExistsException from .service_instance import ServiceInstance -from .service_pool import ServicePool +from .service_cache_manager import ServiceCacheManager from .exceptions import InvalidSolutionConfigurationException ############################################################################## @@ -28,14 +28,14 @@ class ServiceManager(object): __slots__ = [ - '_parameters', - '_services', - '_publishers', - '_service_pool', # all service profiles that are permitted to be enabled, ServicePool - '_enabled_services', # enabled services { resource_name : ConcertServiceInstance } - '_interactions_loader', # rocon_interactions.InteractionLoader - 'lock' - ] + '_parameters', + '_services', + '_publishers', + '_enabled_services', # enabled services { resource_name : ConcertServiceInstance } + '_interactions_loader', # rocon_interactions.InteractionLoader + 'lock', + '_service_cache_manager', # todd + ] def __init__(self): """ @@ -50,22 +50,23 @@ def __init__(self): self._interactions_loader = rocon_interactions.InteractionsLoader() roslaunch.pmon._init_signal_handlers() try: - self._service_pool = ServicePool(self._parameters['solution_configuration'], self._parameters['concert_name']) + self._service_cache_manager = ServiceCacheManager(self._parameters['concert_name'], self._parameters['solution_configuration']) except (rospkg.ResourceNotFound, InvalidSolutionConfigurationException) as e: raise e self._publishers = self._setup_ros_publishers() - + # auto enable service if self._parameters['auto_enable_services'] == 'all': - for name in self._service_pool.service_profiles.keys(): + for name in self._service_cache_manager.service_profiles.keys(): self._ros_service_enable_concert_service(concert_srvs.EnableServiceRequest(name, True)) elif type(self._parameters['auto_enable_services']) is list: for name in self._parameters['auto_enable_services']: - if name in self._service_pool.service_profiles.keys(): + if name in self._service_cache_manager.service_profiles.keys(): self._ros_service_enable_concert_service(concert_srvs.EnableServiceRequest(name, True)) else: - rospy.logwarn("Service Manager : '%s' is not available. cannot auto enable"%str(name)) + rospy.logwarn("Service Manager : '%s' is not available. cannot auto enable" % str(name)) else: self.publish_update() # publish the available list + # now we let the service threads compete self._services = self._setup_ros_services() @@ -73,8 +74,8 @@ def _setup_ros_parameters(self): rospy.logdebug("Service Manager : parsing parameters") parameters = {} parameters['concert_name'] = rospy.get_param('/concert/name/', "") - parameters['solution_configuration'] = rospy.get_param('~services', "") #@IgnorePep8 - parameters['auto_enable_services'] = rospy.get_param('~auto_enable_services', []) #@IgnorePep8 + parameters['solution_configuration'] = rospy.get_param('~services', "") # @IgnorePep8 + parameters['auto_enable_services'] = rospy.get_param('~auto_enable_services', []) # @IgnorePep8 return parameters def _setup_service_parameters(self, name, description, priority, unique_identifier): @@ -111,26 +112,22 @@ def _setup_ros_publishers(self): return publishers def _ros_service_enable_concert_service(self, req): - name = req.name success = False message = "unknown error" - if req.enable: self.loginfo("serving request to enable '%s'" % name) else: self.loginfo("serving request to disable '%s'" % name) self.lock.acquire() # could be an expensive lock? - # DJS : reload the service pool try: if req.enable: - self._service_pool.reload() # check if the solution specs have updated # Check if the service name is in the currently loaded service profiles if name not in self._enabled_services.keys(): try: - service_instance = ServiceInstance(self._service_pool.find(name).msg, update_callback=self.publish_update) + service_instance = ServiceInstance(self._parameters['concert_name'], self._service_cache_manager.find(name)['msg'], update_callback=self.publish_update) except NoServiceExistsException: # do some updating of the service pool here raise NoServiceExistsException("service not found on the package path [%s]" % name) @@ -153,6 +150,7 @@ def _ros_service_enable_concert_service(self, req): self._cleanup_service_parameters(self._enabled_services[name].msg.name) success, message = self._enabled_services[name].disable(self._interactions_loader) del self._enabled_services[name] + except NoServiceExistsException as e: rospy.logwarn("Service Manager : %s" % str(e)) success = False @@ -165,7 +163,7 @@ def publish_update(self): ''' This is not locked here - it should always be called inside a locked scope. ''' - services = [service_profile.msg for service_profile in self._service_pool.service_profiles.values()] + services = [service_profile['msg'] for service_profile in self._service_cache_manager.service_profiles.values()] for service in services: service.enabled = True if service.name in self._enabled_services.keys() else False self._publishers['list_concert_services'].publish(services) @@ -177,4 +175,6 @@ def logwarn(self, msg): rospy.logwarn("Service Manager : " + str(msg)) def spin(self): - rospy.spin() + while not rospy.is_shutdown(): + self._service_cache_manager.check_cache_modification(update_callback = self.publish_update) + rospy.sleep(0.5) From a4f4654185f5b8fe23bfdd72575cce9b92747104 Mon Sep 17 00:00:00 2001 From: dwlee Date: Fri, 16 Jan 2015 10:47:19 +0900 Subject: [PATCH 06/21] update --- .../concert_service_manager/load_params.py | 4 +- .../service_cache_manager.py | 118 +++++++++++++----- .../service_instance.py | 6 +- .../service_manager.py | 20 ++- .../src/concert_service_manager/utils.py | 9 +- 5 files changed, 119 insertions(+), 38 deletions(-) diff --git a/concert_service_manager/src/concert_service_manager/load_params.py b/concert_service_manager/src/concert_service_manager/load_params.py index 827a19f..ca794cc 100644 --- a/concert_service_manager/src/concert_service_manager/load_params.py +++ b/concert_service_manager/src/concert_service_manager/load_params.py @@ -25,7 +25,7 @@ def load_parameters_from_file(parameter_file, namespace, name, load): for k, v in params.items(): if k in INVALID_PARAM: if load: - rospy.logwarn("Service Manager: %s%s [%s]" % (str(k), ' is prohibitted parameter. Ignoring...', name)) + rospy.logwarn("Service Manager : %s%s [%s]" % (str(k), ' is prohibitted parameter. Ignoring...', name)) continue param_name = namespace + '/' + k if load: @@ -42,7 +42,7 @@ def load_parameters_from_cache(catche_file_path, namespace, name, load): for k, v in params.items(): if k in INVALID_PARAM: if load: - rospy.logwarn("Service Manager: %s%s [%s]" % (str(k), ' is prohibitted parameter. Ignoring...', name)) + rospy.logwarn("Service Manager : %s%s [%s]" % (str(k), ' is prohibitted parameter. Ignoring...', name)) continue param_name = namespace + '/' + k if load: diff --git a/concert_service_manager/src/concert_service_manager/service_cache_manager.py b/concert_service_manager/src/concert_service_manager/service_cache_manager.py index ba6149d..7693f0d 100644 --- a/concert_service_manager/src/concert_service_manager/service_cache_manager.py +++ b/concert_service_manager/src/concert_service_manager/service_cache_manager.py @@ -20,6 +20,7 @@ import concert_msgs.msg as concert_msgs import scheduler_msgs.msg as scheduler_msgs +from rospy_message_converter import message_converter from .exceptions import InvalidSolutionConfigurationException from .exceptions import InvalidServiceProfileException, NoServiceExistsException from .utils import * @@ -88,25 +89,61 @@ def load_solution_configuration_from_resource(yaml_file): class ServiceCacheManager(object): __slots__ = [ - '_concert_name', # todo - '_resource_name', # todo - 'service_profiles', # todo + '_concert_name', # todo + '_resource_name', # todo + '_cache_list', # todo + 'service_profiles', # todo 'msg', # todo - '_cache_list', # todo + ] def __init__(self, concert_name, resource_name): self._concert_name = concert_name.lower().replace(' ', '_') self._resource_name = resource_name + self.service_profiles = {} setup_home_dirs(self._concert_name) self._cache_list = {} yaml_file = self._check_cache() - print "checking cache result: " + yaml_file - self._load_services_cache(yaml_file) + self._load_cache(yaml_file) self._init_cache_list() + def update_cache(serlf, service_profile_msg): + ''' + Todo + + :param service_profile_msg: Todo + :type service_profile_msg: concert_msgs/ServiceProfile + + :return: boolean result of update cache with message + :rtype: (bool, str) + ''' + + result = True + message = "" + + service_profile = message_converter.convert_ros_message_to_dictionary(service_profile_msg) + print service_profile + service_name = service_profile['name'] + service_parameter_detail = service_profile['parameters_detail'] + + if service_profile['name'] in self.service_profiles.keys(): + service_profile = self.service_profiles[service_profile['name']] + service_profile['parameters_detail'] = service_parameter_detail + try: + self._save_service_profile(service_profile) + result = True + message = 'Success' + except: + result = False + message = "Fail during saveing service profile" + else: + result = False + message = "Can not find service: %s" % service_name + + return (result, message) + def _init_cache_list(self): """ Todo @@ -160,9 +197,7 @@ def _check_cache(self): yaml_file_name = yaml_file.split('/')[-1] cache_yaml_file = get_home(self._concert_name) + '/' + yaml_file_name if not os.path.isfile(cache_yaml_file) or os.stat(cache_yaml_file).st_size <= 0: - print "create cache!!" self._create_cache() - print "Save solution config" self._save_solution_configuration() return cache_yaml_file @@ -180,9 +215,6 @@ def _create_cache(self): s_time = time.time() print "create service: " + resource_name - e_time = time.time() - print "create total time: " + str(e_time - s_time) - try: file_name = rocon_python_utils.ros.find_resource_from_string(resource_name) with open(file_name) as f: @@ -222,13 +254,16 @@ def _create_cache(self): except rospkg.ResourceNotFound as e: raise e + e_time = time.time() + print "create total time: " + str(e_time - s_time) + print "complete making service profile" self.service_profiles[loaded_profile['name']] = copy.deepcopy(loaded_profile) print "complete making service profile message" - self._save_profile(loaded_profile) + self._save_service_profile(loaded_profile) print "complete saving service profile" - def _load_services_cache(self, services_file_name): + def _load_cache(self, services_file_name): ''' Todo @@ -238,13 +273,35 @@ def _load_services_cache(self, services_file_name): ''' with open(services_file_name) as f: service_list = yaml.load(f) - print 'service_list: ' + str(service_list) for service in service_list: - print 'service name: ' + service['name'] try: service_file_name = get_service_profile_cache_home(self._concert_name, service['name']) + "/" + service['name'] + '.service' + if not os.path.isfile(service_file_name): + rospy.logwarn("Service Manager : can not find service file in cache [%s]" % (service['name'] + '.service')) + continue with open(service_file_name) as f: loaded_profile = yaml.load(f) + + if 'parameters' in loaded_profile.keys(): + loaded_profile['parameters_detail'] = [] + parameters_yaml_file = os.path.join(get_service_profile_cache_home(self._concert_name, service['name']), loaded_profile['parameters']) + try: + with open(parameters_yaml_file) as f: + parameters_yaml = yaml.load(f) + loaded_profile['parameters_detail'] = parameters_yaml + except rospkg.ResourceNotFound as e: + raise e + + if 'interactions' in loaded_profile.keys(): + loaded_profile['interactions_detail'] = [] + interactions_yaml_file = os.path.join(get_service_profile_cache_home(self._concert_name, service['name']), loaded_profile['interactions']) + try: + with open(interactions_yaml_file) as f: + interactions_yaml = yaml.load(f) + loaded_profile['interactions_detail'] = interactions_yaml + except rospkg.ResourceNotFound as e: + raise e + except rospkg.ResourceNotFound as e: raise e @@ -289,7 +346,7 @@ def _generate_msg(self, loaded_profile): return msg - def _save_profile(self, loaded_service_profile_from_file): + def _save_service_profile(self, loaded_service_profile_from_file): ''' Todo @@ -320,6 +377,7 @@ def _save_profile(self, loaded_service_profile_from_file): loaded_profile['parameters'] = service_parameters_file_name.split('/')[-1] with file(service_parameters_file_name, 'w') as f: yaml.safe_dump(loaded_profile['parameters_detail'], f, default_flow_style=False) + del (loaded_profile['parameters_detail']) # if 'launcher' in loaded_profile.keys(): # loaded_profile['launcher'] = rocon_python_utils.ros.find_resource_from_string(loaded_profile['launcher'] + '.launch') @@ -383,6 +441,20 @@ def _find_by_hash(self, hash_id): return service_profile return None + def check_cache_modification(self, update_callback): + ''' + Todo + + :param update_callback: + :type update_callback: method with no args + + ''' + if self._cache_list != self._get_cache_list(): + yaml_file = self._check_cache() + self._load_cache(yaml_file) + self._init_cache_list() + update_callback() + def find(self, name): """ Scan the service data to see if a service has been configured with the specified name. Check if it @@ -406,17 +478,3 @@ def find(self, name): return service_profile except KeyError: raise NoServiceExistsException("service profile not found in the configured service pool [%s]" % name) - - def check_cache_modification(self, update_callback): - ''' - Todo - - :param update_callback: - :type update_callback: method with no args - - ''' - if self._cache_list != self._get_cache_list(): - yaml_file = self._check_cache() - self._load_services_cache(yaml_file) - self._init_cache_list() - update_callback() diff --git a/concert_service_manager/src/concert_service_manager/service_instance.py b/concert_service_manager/src/concert_service_manager/service_instance.py index 045d2de..db6ed55 100644 --- a/concert_service_manager/src/concert_service_manager/service_instance.py +++ b/concert_service_manager/src/concert_service_manager/service_instance.py @@ -210,10 +210,10 @@ def to_msg(self): return self.msg def loginfo(self, msg): - rospy.loginfo("Service Manager: %s [%s]" % (str(msg), str(self.msg.name))) + rospy.loginfo("Service Manager : %s [%s]" % (str(msg), str(self.msg.name))) def logerr(self, msg): - rospy.logerr("Service Manager: %s [%s]" % (str(msg), str(self.msg.name))) + rospy.logerr("Service Manager : %s [%s]" % (str(msg), str(self.msg.name))) def logwarn(self, msg): - rospy.logwarn("Service Manager: %s [%s]" % (str(msg), str(self.msg.name))) + rospy.logwarn("Service Manager : %s [%s]" % (str(msg), str(self.msg.name))) diff --git a/concert_service_manager/src/concert_service_manager/service_manager.py b/concert_service_manager/src/concert_service_manager/service_manager.py index c33e214..4b59d07 100644 --- a/concert_service_manager/src/concert_service_manager/service_manager.py +++ b/concert_service_manager/src/concert_service_manager/service_manager.py @@ -34,7 +34,7 @@ class ServiceManager(object): '_enabled_services', # enabled services { resource_name : ConcertServiceInstance } '_interactions_loader', # rocon_interactions.InteractionLoader 'lock', - '_service_cache_manager', # todd + '_service_cache_manager', # todd ] def __init__(self): @@ -104,6 +104,7 @@ def _cleanup_service_parameters(self, name): def _setup_ros_services(self): services = {} services['enable_service'] = rospy.Service('~enable', concert_srvs.EnableService, self._ros_service_enable_concert_service) + services['update_service_config'] = rospy.Service('~update_service_config', concert_srvs.UpdateServiceConfig, self._ros_service_update_service_config) return services def _setup_ros_publishers(self): @@ -111,6 +112,21 @@ def _setup_ros_publishers(self): publishers['list_concert_services'] = rospy.Publisher('~list', concert_msgs.Services, latch=True, queue_size=1) return publishers + def _ros_service_update_service_config(self, req): + + success = False + message = "" + service_profile = req.service_profile + + # write at cache + (success, message) = self._service_cache_manager.update_cache(service_profile) + + if service_name in self._enabled_services.keys(): + success = True + message = "%s service is running. restart service" % (service_name) + + return concert_srvs.EnableServiceResponse(success, message) + def _ros_service_enable_concert_service(self, req): name = req.name success = False @@ -176,5 +192,5 @@ def logwarn(self, msg): def spin(self): while not rospy.is_shutdown(): - self._service_cache_manager.check_cache_modification(update_callback = self.publish_update) + self._service_cache_manager.check_cache_modification(update_callback=self.publish_update) rospy.sleep(0.5) diff --git a/concert_service_manager/src/concert_service_manager/utils.py b/concert_service_manager/src/concert_service_manager/utils.py index 980b93c..9fe6c40 100644 --- a/concert_service_manager/src/concert_service_manager/utils.py +++ b/concert_service_manager/src/concert_service_manager/utils.py @@ -19,14 +19,21 @@ def setup_home_dirs(concert_name): if not os.path.isdir(get_home(concert_name)): - print 'create path: ' + str(get_home(concert_name)) os.makedirs(get_home(concert_name)) def setup_service_profile_home_dirs(concert_name, service_name): + ''' + Retrieve the location of the home directory for the service manager + temporary storage needs as concert name + + @return the service manager home directory (path object). + @type str + ''' if not os.path.isdir(get_service_profile_cache_home(concert_name, service_name)): os.makedirs(get_service_profile_cache_home(concert_name, service_name)) + def get_home(concert_name): ''' Retrieve the location of the home directory for the service manager From 022f02671a00b62608aed62823c9be4f19fb7192 Mon Sep 17 00:00:00 2001 From: dwlee Date: Fri, 16 Jan 2015 13:05:03 +0900 Subject: [PATCH 07/21] add the concert_name arg in service manager launcher --- concert_master/launch/concert_master.launch | 1 + 1 file changed, 1 insertion(+) diff --git a/concert_master/launch/concert_master.launch b/concert_master/launch/concert_master.launch index 3153574..72bfd7d 100644 --- a/concert_master/launch/concert_master.launch +++ b/concert_master/launch/concert_master.launch @@ -43,6 +43,7 @@ + From 38ecc444ae2e0508522eba9b28a0188e2aa9ce6e Mon Sep 17 00:00:00 2001 From: dwlee Date: Fri, 16 Jan 2015 13:08:42 +0900 Subject: [PATCH 08/21] update service manager launch --- concert_service_manager/launch/service_manager.launch | 2 ++ 1 file changed, 2 insertions(+) diff --git a/concert_service_manager/launch/service_manager.launch b/concert_service_manager/launch/service_manager.launch index e0ea2c1..3f033b2 100644 --- a/concert_service_manager/launch/service_manager.launch +++ b/concert_service_manager/launch/service_manager.launch @@ -1,8 +1,10 @@ + + $(arg services) $(arg services) $(arg auto_enable_services) From 54a4189a97eb0786959ae27d1e5872bf385dc8b3 Mon Sep 17 00:00:00 2001 From: dwlee Date: Fri, 16 Jan 2015 13:16:11 +0900 Subject: [PATCH 09/21] add the string strip --- .../src/concert_service_manager/service_cache_manager.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/concert_service_manager/src/concert_service_manager/service_cache_manager.py b/concert_service_manager/src/concert_service_manager/service_cache_manager.py index 7693f0d..cc3d569 100644 --- a/concert_service_manager/src/concert_service_manager/service_cache_manager.py +++ b/concert_service_manager/src/concert_service_manager/service_cache_manager.py @@ -98,7 +98,7 @@ class ServiceCacheManager(object): ] def __init__(self, concert_name, resource_name): - self._concert_name = concert_name.lower().replace(' ', '_') + self._concert_name = concert_name.strip().lower().replace(' ', '_') self._resource_name = resource_name self.service_profiles = {} @@ -231,7 +231,7 @@ def _create_cache(self): loaded_profile[key] = overrides[key] if 'launcher_type' not in loaded_profile.keys(): # not set loaded_profile['launcher_type'] = concert_msgs.ServiceProfile.TYPE_SHADOW - loaded_profile['name'] = loaded_profile['name'].lower().replace(" ", "_") + loaded_profile['name'] = loaded_profile['name'].strip().lower().replace(" ", "_") if 'parameters' in loaded_profile.keys(): loaded_profile['parameters_detail'] = [] From dfc55a84a9b71009f31fd9e467b57c16eb4b1f85 Mon Sep 17 00:00:00 2001 From: dwlee Date: Fri, 16 Jan 2015 17:30:55 +0900 Subject: [PATCH 10/21] fix wrong argument --- concert_service_manager/launch/service_manager.launch | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/concert_service_manager/launch/service_manager.launch b/concert_service_manager/launch/service_manager.launch index 3f033b2..89ca56f 100644 --- a/concert_service_manager/launch/service_manager.launch +++ b/concert_service_manager/launch/service_manager.launch @@ -1,10 +1,10 @@ - - $(arg services) + $(arg concert_name) $(arg services) $(arg auto_enable_services) From 2d62256285e0ed8356a73b499506159ed33de0bd Mon Sep 17 00:00:00 2001 From: dwlee Date: Fri, 16 Jan 2015 17:36:48 +0900 Subject: [PATCH 11/21] rename and code clear --- .../service_cache_manager.py | 317 +++++++++--------- .../service_instance.py | 8 +- .../service_manager.py | 23 +- 3 files changed, 181 insertions(+), 167 deletions(-) diff --git a/concert_service_manager/src/concert_service_manager/service_cache_manager.py b/concert_service_manager/src/concert_service_manager/service_cache_manager.py index cc3d569..58f1ebc 100644 --- a/concert_service_manager/src/concert_service_manager/service_cache_manager.py +++ b/concert_service_manager/src/concert_service_manager/service_cache_manager.py @@ -30,7 +30,7 @@ ############################################################################## -def load_solution_configuration_from_resource(yaml_file): +def load_solution_configuration_from_default(yaml_file): """ Load the solution configuration from a yaml file. This is a pretty simple file, just a list of services specified by resource names @@ -89,80 +89,32 @@ def load_solution_configuration_from_resource(yaml_file): class ServiceCacheManager(object): __slots__ = [ - '_concert_name', # todo - '_resource_name', # todo - '_cache_list', # todo - 'service_profiles', # todo - 'msg', # todo - + '_concert_name', # todo + '_resource_name', # todo + '_cache_service_list', # todo + '_modification_callback', # todo + 'service_profiles', # todo + 'msg', # todo ] - def __init__(self, concert_name, resource_name): + def __init__(self, concert_name, resource_name, modification_callback=None): self._concert_name = concert_name.strip().lower().replace(' ', '_') self._resource_name = resource_name - + self._modification_callback = modification_callback + self._cache_service_list = {} self.service_profiles = {} + setup_home_dirs(self._concert_name) - self._cache_list = {} - - yaml_file = self._check_cache() - self._load_cache(yaml_file) - self._init_cache_list() - - def update_cache(serlf, service_profile_msg): - ''' - Todo - - :param service_profile_msg: Todo - :type service_profile_msg: concert_msgs/ServiceProfile + self.load_service_cache() - :return: boolean result of update cache with message - :rtype: (bool, str) - ''' - - result = True - message = "" - - service_profile = message_converter.convert_ros_message_to_dictionary(service_profile_msg) - print service_profile - service_name = service_profile['name'] - service_parameter_detail = service_profile['parameters_detail'] - - if service_profile['name'] in self.service_profiles.keys(): - service_profile = self.service_profiles[service_profile['name']] - service_profile['parameters_detail'] = service_parameter_detail - try: - self._save_service_profile(service_profile) - result = True - message = 'Success' - except: - result = False - message = "Fail during saveing service profile" - else: - result = False - message = "Can not find service: %s" % service_name - - return (result, message) - - def _init_cache_list(self): + def _init_service_cache_list(self): """ Todo """ - self._cache_list = {} - for root, dirs, files in os.walk(get_home(self._concert_name)): - for dir_name in dirs: - dir_path = str(os.path.join(root, dir_name)) - self._cache_list[dir_path] = {} - self._cache_list[dir_path]['type'] = 'directory' - self._cache_list[dir_path]['last_modified_time'] = time.ctime(os.path.getmtime(dir_path)) - for file_name in files: - file_path = str(os.path.join(root, file_name)) - self._cache_list[file_path] = {} - self._cache_list[file_path]['type'] = 'file' - self._cache_list[file_path]['last_modified_time'] = time.ctime(os.path.getmtime(file_path)) + self._cache_service_list = self._get_service_cache_list() - def _get_cache_list(self): + def _get_service_cache_list(self): """ Todo @@ -185,7 +137,7 @@ def _get_cache_list(self): cache_list[file_path]['last_modified_time'] = time.ctime(os.path.getmtime(file_path)) return cache_list - def _check_cache(self): + def _check_service_cache(self): """ Check whether cached yaml file is existed or not @@ -193,95 +145,111 @@ def _check_cache(self): :rtype: str """ - yaml_file = rocon_python_utils.ros.find_resource_from_string(self._resource_name) - yaml_file_name = yaml_file.split('/')[-1] - cache_yaml_file = get_home(self._concert_name) + '/' + yaml_file_name - if not os.path.isfile(cache_yaml_file) or os.stat(cache_yaml_file).st_size <= 0: - self._create_cache() - self._save_solution_configuration() - return cache_yaml_file - - def _create_cache(self): + check_result = True + default_service_configuration_file = rocon_python_utils.ros.find_resource_from_string(self._resource_name) + service_configuration_file_name = default_service_configuration_file.split('/')[-1] + service_configuration_file = get_home(self._concert_name) + '/' + service_configuration_file_name + if not os.path.isfile(service_configuration_file) or os.stat(service_configuration_file).st_size <= 0: + check_result = False + self._loginfo("load from default: [%s]" % self._resource_name) + else: + self._loginfo("load from cache: [%s]" % service_configuration_file) + return (check_result, service_configuration_file) + + def _create_service_cache(self): """ - Todo + Todo: Create cache as loading service configuration from default value. """ # read resource file - service_configurations = load_solution_configuration_from_resource(rocon_python_utils.ros.find_resource_from_string(self._resource_name)) + loaded_profiles = {} + service_configurations = load_solution_configuration_from_default(rocon_python_utils.ros.find_resource_from_string(self._resource_name)) for service_configuration in service_configurations: resource_name = service_configuration['resource_name'] + '.service' overrides = service_configuration['overrides'] - s_time = time.time() - print "create service: " + resource_name + loaded_profile = self._load_service_profile_from_default(resource_name, overrides) + self._save_service_profile(loaded_profile) + loaded_profiles[loaded_profile['name']] = copy.deepcopy(loaded_profile) + # save solution configuration + self._save_solution_configuration(loaded_profiles) + + def _load_service_profile_from_default(self, resource_name, overrides): + """ + Todo: Create cache as loading service configuration from default value. + + :param resource_name: Todo + :type resource_name: str + :param overrides: Todo + :type overrides: dict + :return: dictionary about service profile + :rtype: dict + + :raises: :exc:`rospkg.ResourceNotFound` Todo + + """ + # load service profile from default + try: + file_name = rocon_python_utils.ros.find_resource_from_string(resource_name) + with open(file_name) as f: + loaded_profile = yaml.load(f) + except rospkg.ResourceNotFound as e: + raise e + + loaded_profile['resource_name'] = resource_name + # set priority to default if it was not configured + if 'priority' not in loaded_profile.keys(): + loaded_profile['priority'] = scheduler_msgs.Request.DEFAULT_PRIORITY + for key in loaded_profile: + if key in overrides and overrides[key] is not None: + loaded_profile[key] = overrides[key] + if 'launcher_type' not in loaded_profile.keys(): # not set + loaded_profile['launcher_type'] = concert_msgs.ServiceProfile.TYPE_SHADOW + loaded_profile['name'] = loaded_profile['name'].strip().lower().replace(" ", "_") + + if 'parameters' in loaded_profile.keys(): + loaded_profile['parameters_detail'] = [] + # todo if cache, if original + parameters_yaml_file = rocon_python_utils.ros.find_resource_from_string(loaded_profile['parameters'] + '.parameters') try: - file_name = rocon_python_utils.ros.find_resource_from_string(resource_name) - with open(file_name) as f: - loaded_profile = yaml.load(f) + with open(parameters_yaml_file) as f: + parameters_yaml = yaml.load(f) + loaded_profile['parameters_detail'] = parameters_yaml except rospkg.ResourceNotFound as e: raise e - loaded_profile['resource_name'] = resource_name - # set priority to default if it was not configured - if 'priority' not in loaded_profile.keys(): - loaded_profile['priority'] = scheduler_msgs.Request.DEFAULT_PRIORITY - for key in loaded_profile: - if key in overrides and overrides[key] is not None: - loaded_profile[key] = overrides[key] - if 'launcher_type' not in loaded_profile.keys(): # not set - loaded_profile['launcher_type'] = concert_msgs.ServiceProfile.TYPE_SHADOW - loaded_profile['name'] = loaded_profile['name'].strip().lower().replace(" ", "_") - - if 'parameters' in loaded_profile.keys(): - loaded_profile['parameters_detail'] = [] - # todo if cache, if original - parameters_yaml_file = rocon_python_utils.ros.find_resource_from_string(loaded_profile['parameters'] + '.parameters') - try: - with open(parameters_yaml_file) as f: - parameters_yaml = yaml.load(f) - loaded_profile['parameters_detail'] = parameters_yaml - except rospkg.ResourceNotFound as e: - raise e - - # dump interaction detail as type of key-value - if 'interactions' in loaded_profile.keys(): - interactions_yaml_file = rocon_python_utils.ros.find_resource_from_string(loaded_profile['interactions'] + '.interactions') - try: - with open(interactions_yaml_file) as f: - interactions_yaml = yaml.load(f) - loaded_profile['interactions_detail'] = interactions_yaml - except rospkg.ResourceNotFound as e: - raise e - - e_time = time.time() - print "create total time: " + str(e_time - s_time) - - print "complete making service profile" - self.service_profiles[loaded_profile['name']] = copy.deepcopy(loaded_profile) - print "complete making service profile message" - self._save_service_profile(loaded_profile) - print "complete saving service profile" + if 'interactions' in loaded_profile.keys(): + interactions_yaml_file = rocon_python_utils.ros.find_resource_from_string(loaded_profile['interactions'] + '.interactions') + try: + with open(interactions_yaml_file) as f: + interactions_yaml = yaml.load(f) + loaded_profile['interactions_detail'] = interactions_yaml + except rospkg.ResourceNotFound as e: + raise e + + return loaded_profile - def _load_cache(self, services_file_name): + def _load_service_cache_from_cache(self, services_file_name): ''' - Todo + Todo :param service_file_name: todo :type service_file_name: str + :raises: :exc:`rospkg.ResourceNotFound` Todo + ''' with open(services_file_name) as f: service_list = yaml.load(f) for service in service_list: try: service_file_name = get_service_profile_cache_home(self._concert_name, service['name']) + "/" + service['name'] + '.service' - if not os.path.isfile(service_file_name): + if not os.path.isfile(service_file_name) or os.stat(service_file_name).st_size <= 0: rospy.logwarn("Service Manager : can not find service file in cache [%s]" % (service['name'] + '.service')) continue with open(service_file_name) as f: loaded_profile = yaml.load(f) - if 'parameters' in loaded_profile.keys(): loaded_profile['parameters_detail'] = [] parameters_yaml_file = os.path.join(get_service_profile_cache_home(self._concert_name, service['name']), loaded_profile['parameters']) @@ -305,10 +273,10 @@ def _load_cache(self, services_file_name): except rospkg.ResourceNotFound as e: raise e - loaded_profile['msg'] = self._generate_msg(loaded_profile) + loaded_profile['msg'] = self._service_profile_to_msg(loaded_profile) self.service_profiles[loaded_profile['name']] = copy.deepcopy(loaded_profile) - def _generate_msg(self, loaded_profile): + def _service_profile_to_msg(self, loaded_profile): ''' Todo @@ -353,9 +321,6 @@ def _save_service_profile(self, loaded_service_profile_from_file): :param loaded_service_profile_from_file: Todo :type loaded_service_profile_from_file: dict - :returns: generated service profile message - :rtype: [concert_msgs.ServiceProfile] - ''' loaded_profile = copy.deepcopy(loaded_service_profile_from_file) service_name = loaded_profile['name'] @@ -381,26 +346,34 @@ def _save_service_profile(self, loaded_service_profile_from_file): # if 'launcher' in loaded_profile.keys(): # loaded_profile['launcher'] = rocon_python_utils.ros.find_resource_from_string(loaded_profile['launcher'] + '.launch') - + + # delete msg data + if 'msg' in loaded_profile.keys(): + del (loaded_profile['msg']) + # writting service profile data service_profile_file_name = service_profile_cache_home + '/' + service_name + '.service' with file(service_profile_file_name, 'w') as f: yaml.safe_dump(loaded_profile, f, default_flow_style=False) - def _save_solution_configuration(self): + def _save_solution_configuration(self, service_profiles): ''' Todo + :param service_profiles: Todo + :type service_profiles: dict + ''' solution_configuration = [] - for service_profile in self.service_profiles.keys(): + for service_profile in service_profiles.keys(): configuration_item = {} configuration_item['name'] = service_profile configuration_item['enabled'] = False solution_configuration.append(configuration_item) - yaml_file_name = rocon_python_utils.ros.find_resource_from_string(self._resource_name).split('/')[-1] - if '.services' in yaml_file_name: - cache_srv_config_file = get_home(self._concert_name) + '/' + yaml_file_name + + service_configuration_file_name = rocon_python_utils.ros.find_resource_from_string(self._resource_name).split('/')[-1] + if '.services' in service_configuration_file_name: + cache_srv_config_file = get_home(self._concert_name) + '/' + service_configuration_file_name with file(cache_srv_config_file, 'w') as f: yaml.safe_dump(solution_configuration, f, default_flow_style=False) @@ -427,33 +400,69 @@ def _find_resource_from_string(self, resource): except rospkg.ResourceNotFound: raise rospkg.ResourceNotFound("[%s] is not a package or launch file name [%s]" % (package, package + '/' + filename)) return None + + def _loginfo(self, msg): + rospy.loginfo("Service Manager : " + str(msg)) - def _find_by_hash(self, hash_id): - """ - Scan the service pool looking for profiles with the specified hash. This is - an internal convenience function. + def _logwarn(self, msg): + rospy.logwarn("Service Manager : " + str(msg)) - :returns: the service configuration for a match or None if not found - :rtype: ServiceConfigurationData or None - """ - for service_profile in self.service_profiles.values(): - if service_profile.hash.digest() == hash_id.digest(): - return service_profile - return None + def update_service_cache(self, service_profile_msg): + ''' + Todo - def check_cache_modification(self, update_callback): + :param service_profile_msg: Todo + :type service_profile_msg: concert_msgs/ServiceProfile + + :return: boolean result of update cache with message + :rtype: (bool, str) + ''' + + result = True + message = "" + + service_profile = message_converter.convert_ros_message_to_dictionary(service_profile_msg) + service_name = service_profile['name'] + service_parameter_detail = {} + for param_pair in service_profile['parameters_detail']: + service_parameter_detail[param_pair['key']] = param_pair['value'] + + if service_profile['name'] in self.service_profiles.keys(): + service_profile = self.service_profiles[service_profile['name']] + service_profile['parameters_detail'] = service_parameter_detail + try: + self._save_service_profile(service_profile) + result = True + message = 'Success' + except: + result = False + message = "Fail during saveing service profile" + else: + result = False + message = "Can not find service: %s" % service_name + + return (result, message) + + def load_service_cache(self): ''' Todo - :param update_callback: - :type update_callback: method with no args + ''' + (check_result, solution_configuration_cache) = self._check_service_cache() + if not check_result: + self._create_service_cache() + self._load_service_cache_from_cache(solution_configuration_cache) + self._init_service_cache_list() + + def check_service_cache_modification(self): + ''' + Todo ''' - if self._cache_list != self._get_cache_list(): - yaml_file = self._check_cache() - self._load_cache(yaml_file) - self._init_cache_list() - update_callback() + if self._cache_service_list != self._get_service_cache_list(): + self.load_service_cache() + if self._modification_callback: + self._modification_callback() def find(self, name): """ diff --git a/concert_service_manager/src/concert_service_manager/service_instance.py b/concert_service_manager/src/concert_service_manager/service_instance.py index db6ed55..6e6b6c9 100644 --- a/concert_service_manager/src/concert_service_manager/service_instance.py +++ b/concert_service_manager/src/concert_service_manager/service_instance.py @@ -35,7 +35,7 @@ class ServiceInstance(object): __slots__ = [ 'msg', # concert_msgs.ServiceProfile fixed and variable parameters - '_update_callback', # used to trigger an external callback (service manager publisher) when the state changes. + '_namespace', # namespace that the service will run in '_lock', # protect service enabling/disabling '_proc', # holds the custom subprocess variable if TYPE_CUSTOM @@ -49,7 +49,7 @@ class ServiceInstance(object): shutdown_timeout = 5 kill_timeout = 10 - def __init__(self, concert_name=None, service_profile=None, env=os.environ, update_callback=dummy_cb): + def __init__(self, concert_name=None, service_profile=None, env=os.environ): ''' @param service_profile : @type concert_msgs.msg.ConcertService @@ -60,7 +60,7 @@ def __init__(self, concert_name=None, service_profile=None, env=os.environ, upda self.name = self.msg.name # other self._namespace = '/services/' + str(self.msg.name) - self._update_callback = update_callback + self._lock = threading.Lock() self._proc = None self._roslaunch = None @@ -99,7 +99,7 @@ def enable(self, unique_identifier, interactions_loader): interactions_loader.load(interaction_path, namespace=self._namespace, load=True, is_relative_path=False) # if there's a failure point, it will have thrown an exception before here. success = True - self._update_callback() + self.loginfo("service enabled [%s]" % self.msg.name) message = "success" except (rocon_interactions.YamlResourceNotFoundException, rocon_interactions.MalformedInteractionsYaml) as e: diff --git a/concert_service_manager/src/concert_service_manager/service_manager.py b/concert_service_manager/src/concert_service_manager/service_manager.py index 4b59d07..05dc1f6 100644 --- a/concert_service_manager/src/concert_service_manager/service_manager.py +++ b/concert_service_manager/src/concert_service_manager/service_manager.py @@ -34,7 +34,7 @@ class ServiceManager(object): '_enabled_services', # enabled services { resource_name : ConcertServiceInstance } '_interactions_loader', # rocon_interactions.InteractionLoader 'lock', - '_service_cache_manager', # todd + '_service_cache_manager', # todo ] def __init__(self): @@ -50,7 +50,7 @@ def __init__(self): self._interactions_loader = rocon_interactions.InteractionsLoader() roslaunch.pmon._init_signal_handlers() try: - self._service_cache_manager = ServiceCacheManager(self._parameters['concert_name'], self._parameters['solution_configuration']) + self._service_cache_manager = ServiceCacheManager(self._parameters['concert_name'], self._parameters['solution_configuration'], self.publish_update) except (rospkg.ResourceNotFound, InvalidSolutionConfigurationException) as e: raise e self._publishers = self._setup_ros_publishers() @@ -73,7 +73,7 @@ def __init__(self): def _setup_ros_parameters(self): rospy.logdebug("Service Manager : parsing parameters") parameters = {} - parameters['concert_name'] = rospy.get_param('/concert/name/', "") + parameters['concert_name'] = rospy.get_param('~concert_name', "") parameters['solution_configuration'] = rospy.get_param('~services', "") # @IgnorePep8 parameters['auto_enable_services'] = rospy.get_param('~auto_enable_services', []) # @IgnorePep8 return parameters @@ -116,16 +116,18 @@ def _ros_service_update_service_config(self, req): success = False message = "" + service_profile = req.service_profile - + service_name = service_profile.name # write at cache - (success, message) = self._service_cache_manager.update_cache(service_profile) - + self.lock.acquire() # could be an expensive lock? + (success, message) = self._service_cache_manager.update_service_cache(service_profile) + self.lock.release() # could be an expensive lock? if service_name in self._enabled_services.keys(): success = True message = "%s service is running. restart service" % (service_name) - return concert_srvs.EnableServiceResponse(success, message) + return concert_srvs.UpdateServiceConfigResponse(success, message) def _ros_service_enable_concert_service(self, req): name = req.name @@ -140,10 +142,11 @@ def _ros_service_enable_concert_service(self, req): # DJS : reload the service pool try: if req.enable: + self._service_cache_manager.load_service_cache() # Check if the service name is in the currently loaded service profiles if name not in self._enabled_services.keys(): try: - service_instance = ServiceInstance(self._parameters['concert_name'], self._service_cache_manager.find(name)['msg'], update_callback=self.publish_update) + service_instance = ServiceInstance(self._parameters['concert_name'], self._service_cache_manager.find(name)['msg']) except NoServiceExistsException: # do some updating of the service pool here raise NoServiceExistsException("service not found on the package path [%s]" % name) @@ -192,5 +195,7 @@ def logwarn(self, msg): def spin(self): while not rospy.is_shutdown(): - self._service_cache_manager.check_cache_modification(update_callback=self.publish_update) + self.lock.acquire() # could be an expensive lock? + self._service_cache_manager.check_service_cache_modification() + self.lock.release() # could be an expensive lock? rospy.sleep(0.5) From 37a5c97cc6ef5feeafb8c96d740a3c67fad3a515 Mon Sep 17 00:00:00 2001 From: dwlee Date: Mon, 19 Jan 2015 08:32:01 +0900 Subject: [PATCH 12/21] add extension name checker --- .../service_cache_manager.py | 28 +++++++++---------- .../src/concert_service_manager/utils.py | 14 ++++++++++ 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/concert_service_manager/src/concert_service_manager/service_cache_manager.py b/concert_service_manager/src/concert_service_manager/service_cache_manager.py index 58f1ebc..66e102f 100644 --- a/concert_service_manager/src/concert_service_manager/service_cache_manager.py +++ b/concert_service_manager/src/concert_service_manager/service_cache_manager.py @@ -103,7 +103,7 @@ def __init__(self, concert_name, resource_name, modification_callback=None): self._modification_callback = modification_callback self._cache_service_list = {} self.service_profiles = {} - + setup_home_dirs(self._concert_name) self.load_service_cache() @@ -165,7 +165,7 @@ def _create_service_cache(self): loaded_profiles = {} service_configurations = load_solution_configuration_from_default(rocon_python_utils.ros.find_resource_from_string(self._resource_name)) for service_configuration in service_configurations: - resource_name = service_configuration['resource_name'] + '.service' + resource_name = check_extension_name(service_configuration['resource_name'], '.service') overrides = service_configuration['overrides'] loaded_profile = self._load_service_profile_from_default(resource_name, overrides) @@ -211,7 +211,7 @@ def _load_service_profile_from_default(self, resource_name, overrides): if 'parameters' in loaded_profile.keys(): loaded_profile['parameters_detail'] = [] # todo if cache, if original - parameters_yaml_file = rocon_python_utils.ros.find_resource_from_string(loaded_profile['parameters'] + '.parameters') + parameters_yaml_file = rocon_python_utils.ros.find_resource_from_string(check_extension_name(loaded_profile['parameters'], '.parameters')) try: with open(parameters_yaml_file) as f: parameters_yaml = yaml.load(f) @@ -220,7 +220,7 @@ def _load_service_profile_from_default(self, resource_name, overrides): raise e if 'interactions' in loaded_profile.keys(): - interactions_yaml_file = rocon_python_utils.ros.find_resource_from_string(loaded_profile['interactions'] + '.interactions') + interactions_yaml_file = rocon_python_utils.ros.find_resource_from_string(check_extension_name(loaded_profile['interactions'], '.interactions')) try: with open(interactions_yaml_file) as f: interactions_yaml = yaml.load(f) @@ -244,9 +244,9 @@ def _load_service_cache_from_cache(self, services_file_name): service_list = yaml.load(f) for service in service_list: try: - service_file_name = get_service_profile_cache_home(self._concert_name, service['name']) + "/" + service['name'] + '.service' + service_file_name = os.path.join(get_service_profile_cache_home(self._concert_name, service['name']), check_extension_name(service['name'], '.service')) if not os.path.isfile(service_file_name) or os.stat(service_file_name).st_size <= 0: - rospy.logwarn("Service Manager : can not find service file in cache [%s]" % (service['name'] + '.service')) + rospy.logwarn("Service Manager : can not find service file in cache [%s]" % check_extension_name(service['name'], '.service')) continue with open(service_file_name) as f: loaded_profile = yaml.load(f) @@ -330,7 +330,7 @@ def _save_service_profile(self, loaded_service_profile_from_file): # writting interaction data if 'interactions_detail' in loaded_profile.keys(): - service_interactions_file_name = service_profile_cache_home + '/' + service_name + '.interactions' + service_interactions_file_name = os.path.join(service_profile_cache_home, check_extension_name(service_name, '.interactions')) loaded_profile['interactions'] = service_interactions_file_name.split('/')[-1] with file(service_interactions_file_name, 'w') as f: yaml.safe_dump(loaded_profile['interactions_detail'], f, default_flow_style=False) @@ -338,7 +338,7 @@ def _save_service_profile(self, loaded_service_profile_from_file): # writting parameter data if 'parameters_detail' in loaded_profile.keys(): - service_parameters_file_name = service_profile_cache_home + '/' + service_name + '.parameters' + service_parameters_file_name = os.path.join(service_profile_cache_home, check_extension_name(service_name, '.parameters')) loaded_profile['parameters'] = service_parameters_file_name.split('/')[-1] with file(service_parameters_file_name, 'w') as f: yaml.safe_dump(loaded_profile['parameters_detail'], f, default_flow_style=False) @@ -346,13 +346,13 @@ def _save_service_profile(self, loaded_service_profile_from_file): # if 'launcher' in loaded_profile.keys(): # loaded_profile['launcher'] = rocon_python_utils.ros.find_resource_from_string(loaded_profile['launcher'] + '.launch') - + # delete msg data if 'msg' in loaded_profile.keys(): del (loaded_profile['msg']) - + # writting service profile data - service_profile_file_name = service_profile_cache_home + '/' + service_name + '.service' + service_profile_file_name = os.path.join(service_profile_cache_home, check_extension_name(service_name, '.service')) with file(service_profile_file_name, 'w') as f: yaml.safe_dump(loaded_profile, f, default_flow_style=False) @@ -400,7 +400,7 @@ def _find_resource_from_string(self, resource): except rospkg.ResourceNotFound: raise rospkg.ResourceNotFound("[%s] is not a package or launch file name [%s]" % (package, package + '/' + filename)) return None - + def _loginfo(self, msg): rospy.loginfo("Service Manager : " + str(msg)) @@ -426,7 +426,7 @@ def update_service_cache(self, service_profile_msg): service_parameter_detail = {} for param_pair in service_profile['parameters_detail']: service_parameter_detail[param_pair['key']] = param_pair['value'] - + if service_profile['name'] in self.service_profiles.keys(): service_profile = self.service_profiles[service_profile['name']] service_profile['parameters_detail'] = service_parameter_detail @@ -459,7 +459,7 @@ def check_service_cache_modification(self): Todo ''' - if self._cache_service_list != self._get_service_cache_list(): + if self._cache_service_list != self._get_service_cache_list(): self.load_service_cache() if self._modification_callback: self._modification_callback() diff --git a/concert_service_manager/src/concert_service_manager/utils.py b/concert_service_manager/src/concert_service_manager/utils.py index 9fe6c40..7252e92 100644 --- a/concert_service_manager/src/concert_service_manager/utils.py +++ b/concert_service_manager/src/concert_service_manager/utils.py @@ -53,3 +53,17 @@ def get_service_profile_cache_home(concert_name, service_name): @type str ''' return os.path.join(get_home(concert_name), 'services', service_name) + + +def check_extension_name(file_name, extension_name): + ''' + Check whether file name include extension name. If it does not include extension name, return file name added extension name. + + @return file name included extension name + @type str + ''' + + if extension_name in file_name: + return file_name + else: + return file_name + extension_name From 93267bd1fb4bad157ecd2586be0073af5ad5b2e9 Mon Sep 17 00:00:00 2001 From: dwlee Date: Mon, 19 Jan 2015 08:47:02 +0900 Subject: [PATCH 13/21] change modification checker name --- .../service_cache_manager.py | 6 +++--- .../service_manager.py | 19 +++++++++---------- 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/concert_service_manager/src/concert_service_manager/service_cache_manager.py b/concert_service_manager/src/concert_service_manager/service_cache_manager.py index 66e102f..a406d22 100644 --- a/concert_service_manager/src/concert_service_manager/service_cache_manager.py +++ b/concert_service_manager/src/concert_service_manager/service_cache_manager.py @@ -139,7 +139,7 @@ def _get_service_cache_list(self): def _check_service_cache(self): """ - Check whether cached yaml file is existed or not + Check whether cached yaml file regarding service profile is generated or not :returns: flag and full path yaml file as result of check existence about cache file. If cache file is existed, return true and cache path. Otherwise, return false and original path :rtype: str @@ -158,7 +158,7 @@ def _check_service_cache(self): def _create_service_cache(self): """ - Todo: Create cache as loading service configuration from default value. + Create cache as loading service configuration from default value. """ # read resource file @@ -454,7 +454,7 @@ def load_service_cache(self): self._load_service_cache_from_cache(solution_configuration_cache) self._init_service_cache_list() - def check_service_cache_modification(self): + def check_modification_service_cache(self): ''' Todo diff --git a/concert_service_manager/src/concert_service_manager/service_manager.py b/concert_service_manager/src/concert_service_manager/service_manager.py index 05dc1f6..10f67d1 100644 --- a/concert_service_manager/src/concert_service_manager/service_manager.py +++ b/concert_service_manager/src/concert_service_manager/service_manager.py @@ -113,19 +113,18 @@ def _setup_ros_publishers(self): return publishers def _ros_service_update_service_config(self, req): - success = False message = "" - service_profile = req.service_profile service_name = service_profile.name # write at cache - self.lock.acquire() # could be an expensive lock? - (success, message) = self._service_cache_manager.update_service_cache(service_profile) - self.lock.release() # could be an expensive lock? if service_name in self._enabled_services.keys(): - success = True - message = "%s service is running. restart service" % (service_name) + success = False + message = "%s service is running. First, stop %s service" % (service_name, service_name) + else: + self.lock.acquire() + (success, message) = self._service_cache_manager.update_service_cache(service_profile) + self.lock.release() return concert_srvs.UpdateServiceConfigResponse(success, message) @@ -195,7 +194,7 @@ def logwarn(self, msg): def spin(self): while not rospy.is_shutdown(): - self.lock.acquire() # could be an expensive lock? - self._service_cache_manager.check_service_cache_modification() - self.lock.release() # could be an expensive lock? + self.lock.acquire() + self._service_cache_manager.check_modification_service_cache() + self.lock.release() rospy.sleep(0.5) From c71059249366aa3897a979ce6c0f722e536f06b9 Mon Sep 17 00:00:00 2001 From: dwlee Date: Mon, 19 Jan 2015 08:58:30 +0900 Subject: [PATCH 14/21] change name of auto enable service argument --- concert_master/launch/concert_master.launch | 4 ++-- concert_service_manager/launch/service_manager.launch | 4 ++-- .../src/concert_service_manager/service_manager.py | 8 ++++---- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/concert_master/launch/concert_master.launch b/concert_master/launch/concert_master.launch index 72bfd7d..d539730 100644 --- a/concert_master/launch/concert_master.launch +++ b/concert_master/launch/concert_master.launch @@ -18,7 +18,7 @@ - + @@ -45,7 +45,7 @@ - + diff --git a/concert_service_manager/launch/service_manager.launch b/concert_service_manager/launch/service_manager.launch index 89ca56f..8c3c215 100644 --- a/concert_service_manager/launch/service_manager.launch +++ b/concert_service_manager/launch/service_manager.launch @@ -1,11 +1,11 @@ - + $(arg concert_name) $(arg services) - $(arg auto_enable_services) + $(arg default_auto_enable_services) diff --git a/concert_service_manager/src/concert_service_manager/service_manager.py b/concert_service_manager/src/concert_service_manager/service_manager.py index 10f67d1..3494cab 100644 --- a/concert_service_manager/src/concert_service_manager/service_manager.py +++ b/concert_service_manager/src/concert_service_manager/service_manager.py @@ -55,11 +55,11 @@ def __init__(self): raise e self._publishers = self._setup_ros_publishers() # auto enable service - if self._parameters['auto_enable_services'] == 'all': + if self._parameters['default_auto_enable_services'] == 'all': for name in self._service_cache_manager.service_profiles.keys(): self._ros_service_enable_concert_service(concert_srvs.EnableServiceRequest(name, True)) - elif type(self._parameters['auto_enable_services']) is list: - for name in self._parameters['auto_enable_services']: + elif type(self._parameters['default_auto_enable_services']) is list: + for name in self._parameters['default_auto_enable_services']: if name in self._service_cache_manager.service_profiles.keys(): self._ros_service_enable_concert_service(concert_srvs.EnableServiceRequest(name, True)) else: @@ -75,7 +75,7 @@ def _setup_ros_parameters(self): parameters = {} parameters['concert_name'] = rospy.get_param('~concert_name', "") parameters['solution_configuration'] = rospy.get_param('~services', "") # @IgnorePep8 - parameters['auto_enable_services'] = rospy.get_param('~auto_enable_services', []) # @IgnorePep8 + parameters['default_auto_enable_services'] = rospy.get_param('~default_auto_enable_services', []) # @IgnorePep8 return parameters def _setup_service_parameters(self, name, description, priority, unique_identifier): From 9caad3ecd54bcc317cd3f91fe13b3a608f068ca9 Mon Sep 17 00:00:00 2001 From: dwlee Date: Mon, 19 Jan 2015 14:44:41 +0900 Subject: [PATCH 15/21] add code documentation --- .../service_cache_manager.py | 57 +++++++++---------- .../src/concert_service_manager/utils.py | 15 +++-- 2 files changed, 38 insertions(+), 34 deletions(-) diff --git a/concert_service_manager/src/concert_service_manager/service_cache_manager.py b/concert_service_manager/src/concert_service_manager/service_cache_manager.py index a406d22..d42b149 100644 --- a/concert_service_manager/src/concert_service_manager/service_cache_manager.py +++ b/concert_service_manager/src/concert_service_manager/service_cache_manager.py @@ -42,8 +42,6 @@ def load_solution_configuration_from_default(yaml_file): :param yaml_file: filename of the solution configuration file :type yaml_file: str - :param is_cached: todo - :type is_cached: bool :returns: the solution configuration data for services in this concert :rtype: [ServiceData] @@ -89,12 +87,12 @@ def load_solution_configuration_from_default(yaml_file): class ServiceCacheManager(object): __slots__ = [ - '_concert_name', # todo - '_resource_name', # todo - '_cache_service_list', # todo - '_modification_callback', # todo - 'service_profiles', # todo - 'msg', # todo + '_concert_name', # concert name to classify cache directory + '_resource_name', # service resource name. It is composed 'package name/service resource name'. ex. )chatter_concert/chatter.service + '_cache_service_list', # file and directory list cached the service profile. It is used to check file modification + '_modification_callback', # callback function about file modification. It is called when _cache_service_list's item is changed. + 'service_profiles', # dictionary data of service profile to use in service manager + 'msg', # ros message regarding service profile to use in servvice manager ] def __init__(self, concert_name, resource_name, modification_callback=None): @@ -109,14 +107,14 @@ def __init__(self, concert_name, resource_name, modification_callback=None): def _init_service_cache_list(self): """ - Todo + Initialize last modification time about cached file and directory to check cache modification """ self._cache_service_list = self._get_service_cache_list() def _get_service_cache_list(self): """ - Todo + Get the current modification time about cached file and directory :returns: cache list :rtype: dict @@ -176,17 +174,17 @@ def _create_service_cache(self): def _load_service_profile_from_default(self, resource_name, overrides): """ - Todo: Create cache as loading service configuration from default value. + Load service profile information from default. - :param resource_name: Todo + :param resource_name: default resource name. It is composed package name/services name. :type resource_name: str - :param overrides: Todo + :param overrides: overrided informantion. Its propertise are 'name', 'description', 'icon', 'priority', 'interactions' and 'parameters'. If the property does not setting, its has default value as None :type overrides: dict - :return: dictionary about service profile + :return: dictionary data about service profile :rtype: dict - :raises: :exc:`rospkg.ResourceNotFound` Todo + :raises: :exc:`rospkg.ResourceNotFound` """ # load service profile from default @@ -210,7 +208,6 @@ def _load_service_profile_from_default(self, resource_name, overrides): if 'parameters' in loaded_profile.keys(): loaded_profile['parameters_detail'] = [] - # todo if cache, if original parameters_yaml_file = rocon_python_utils.ros.find_resource_from_string(check_extension_name(loaded_profile['parameters'], '.parameters')) try: with open(parameters_yaml_file) as f: @@ -232,12 +229,12 @@ def _load_service_profile_from_default(self, resource_name, overrides): def _load_service_cache_from_cache(self, services_file_name): ''' - Todo + Load service profile information from cache. - :param service_file_name: todo + :param service_file_name: cached services file. It is included service name and enabled status. :type service_file_name: str - :raises: :exc:`rospkg.ResourceNotFound` Todo + :raises: :exc:`rospkg.ResourceNotFound` ''' with open(services_file_name) as f: @@ -278,7 +275,7 @@ def _load_service_cache_from_cache(self, services_file_name): def _service_profile_to_msg(self, loaded_profile): ''' - Todo + Change service proflies data to ros message :returns: generated service profile message :rtype: [concert_msgs.ServiceProfile] @@ -316,9 +313,9 @@ def _service_profile_to_msg(self, loaded_profile): def _save_service_profile(self, loaded_service_profile_from_file): ''' - Todo + Save cache from loaded service profile - :param loaded_service_profile_from_file: Todo + :param loaded_service_profile_from_file: data of dictionary type regarding service profile :type loaded_service_profile_from_file: dict ''' @@ -358,9 +355,9 @@ def _save_service_profile(self, loaded_service_profile_from_file): def _save_solution_configuration(self, service_profiles): ''' - Todo + Save solution configuration about loaded service profiles - :param service_profiles: Todo + :param service_profiles: data of dictionary type regarding service profiles :type service_profiles: dict ''' @@ -381,11 +378,11 @@ def _find_resource_from_string(self, resource): ''' Todo, It is not implemented,yet. - :param resource: Todo + :param resource: finding resource name :type resource: string ''' - # todo package name cahce + package, filename = roslib.names.package_resource_name(resource) if not package: raise rospkg.ResourceNotFound("resource could not be split with a valid leading package name [%s]" % (resource)) @@ -409,9 +406,9 @@ def _logwarn(self, msg): def update_service_cache(self, service_profile_msg): ''' - Todo + Update service cache with ros message regarding service profile - :param service_profile_msg: Todo + :param service_profile_msg: ros message of service profile. concert_msgs/ServiceProfile :type service_profile_msg: concert_msgs/ServiceProfile :return: boolean result of update cache with message @@ -445,7 +442,7 @@ def update_service_cache(self, service_profile_msg): def load_service_cache(self): ''' - Todo + load service profile from cache ''' (check_result, solution_configuration_cache) = self._check_service_cache() @@ -456,7 +453,7 @@ def load_service_cache(self): def check_modification_service_cache(self): ''' - Todo + Check modification of service cahce file and directory.If they are changed, modification callback is called. ''' if self._cache_service_list != self._get_service_cache_list(): diff --git a/concert_service_manager/src/concert_service_manager/utils.py b/concert_service_manager/src/concert_service_manager/utils.py index 7252e92..0e0d988 100644 --- a/concert_service_manager/src/concert_service_manager/utils.py +++ b/concert_service_manager/src/concert_service_manager/utils.py @@ -18,14 +18,20 @@ def setup_home_dirs(concert_name): + ''' + Set up the location of the home directory as concert name for storing + the solution configuration and profiles of services + + @return the service manager home directory (path object). + @type str + ''' if not os.path.isdir(get_home(concert_name)): os.makedirs(get_home(concert_name)) def setup_service_profile_home_dirs(concert_name, service_name): ''' - Retrieve the location of the home directory for the service manager - temporary storage needs as concert name + Set up the location of the home directory as concert and service name @return the service manager home directory (path object). @type str @@ -47,7 +53,7 @@ def get_home(concert_name): def get_service_profile_cache_home(concert_name, service_name): ''' - Retrieve the location of the directory used for storing service configuration. + Retrieve the location of the directory used for storing service profile. @return the rocon remocon qt settings directory (path object). @type str @@ -57,7 +63,8 @@ def get_service_profile_cache_home(concert_name, service_name): def check_extension_name(file_name, extension_name): ''' - Check whether file name include extension name. If it does not include extension name, return file name added extension name. + Check whether file name include extension name. + If it does not include extension name, return the file name added extension name. @return file name included extension name @type str From 658e2213301a451070c7267f0e9df17e0184a50b Mon Sep 17 00:00:00 2001 From: dwlee Date: Mon, 19 Jan 2015 16:26:08 +0900 Subject: [PATCH 16/21] revert src --- .../concert_service_manager/service_pool.py | 81 ++++--------------- .../service_profile.py | 48 +++-------- 2 files changed, 29 insertions(+), 100 deletions(-) diff --git a/concert_service_manager/src/concert_service_manager/service_pool.py b/concert_service_manager/src/concert_service_manager/service_pool.py index 93b7926..deff4bb 100644 --- a/concert_service_manager/src/concert_service_manager/service_pool.py +++ b/concert_service_manager/src/concert_service_manager/service_pool.py @@ -19,7 +19,6 @@ from .exceptions import InvalidSolutionConfigurationException from .exceptions import InvalidServiceProfileException, NoServiceExistsException from .service_profile import ServiceProfile -from .utils import * ############################################################################## # Classes @@ -71,18 +70,16 @@ def load_solution_configuration(yaml_file): class ServiceConfigurationData(object): - """ Represents a single entry in the solution service configuration (.services) file. We hash the important bits so we can track when the file changes. """ __slots__ = [ - 'resource_name', # ros resource name for the service - 'overrides', # dictionary of override keys for the service (use a dic so we can pack it into a ros msg later) - 'hash', # hash of this configuration data - '_is_reading_from_cache' # flag for checking whether reading chache or not - ] + 'resource_name', # ros resource name for the service + 'overrides', # dictionary of override keys for the service (use a dic so we can pack it into a ros msg later) + 'hash', # hash of this configuration data + ] override_keys = ['name', 'description', 'icon', 'priority', 'interactions', 'parameters'] @@ -113,7 +110,6 @@ def __init__(self, resource_name, overrides): class ServicePool(object): - """ Stores the current solution's service related configuration. This is obtained from a ros resource yaml file which typically specifies the @@ -121,13 +117,13 @@ class ServicePool(object): configuration they may have. """ __slots__ = [ - '_yaml_file', # os.path to solution configuration file - '_last_modified', # timestamp of last file modifications - 'service_profiles', # { name : ServiceProfile } - '_cached_service_profile_locations' # all known service profiles on the ros package path : { resource_name : os.path to .service file } - ] + '_yaml_file', # os.path to solution configuration file + '_last_modified', # timestamp of last file modifications + 'service_profiles', # { name : ServiceProfile } + '_cached_service_profile_locations' # all known service profiles on the ros package path : { resource_name : os.path to .service file } + ] - def __init__(self, resource_name, concert_name): + def __init__(self, resource_name): """ Initialise the class with a pointer to the yaml that will be scanned and later monitored for changes that can be applied @@ -135,8 +131,6 @@ def __init__(self, resource_name, concert_name): :param resource_name: pkg/filename of a yaml formatted service configuration for a solution :type resource_name: str - :param resource_name: concert name for storing cache file - :type resource_name: str :raises: :exc:`rospkg.ResourceNotFound` if resource_name cannot be resolved. :raises: :exc:`concert_service_manager.InvalidSolutionConfigurationException` if the yaml provides invalid configuration @@ -147,56 +141,16 @@ def __init__(self, resource_name, concert_name): self._cached_service_profile_locations = {} for cached_resource_name, (cached_filename, unused_catkin_package) in cached_service_profile_information.iteritems(): self._cached_service_profile_locations[cached_resource_name] = cached_filename - # init cache path - concert_name = concert_name.lower().replace(' ', '_') - setup_home_dirs(concert_name) # load if resource_name != "": try: - (self._is_reading_from_cache, self._yaml_file) = self._check_cache(rocon_python_utils.ros.find_resource_from_string(resource_name), concert_name) # rospkg.ResourceNotFound + self._yaml_file = rocon_python_utils.ros.find_resource_from_string(resource_name) # rospkg.ResourceNotFound + self._last_modified = time.ctime(os.path.getmtime(self._yaml_file)) service_configurations = load_solution_configuration(self._yaml_file) # InvalidSolutionConfigurationException self._load_service_profiles(service_configurations) - self._save_yaml_cache(concert_name) - self._last_modified = time.ctime(os.path.getmtime(self._yaml_file)) except (rospkg.ResourceNotFound, InvalidSolutionConfigurationException) as e: raise e - def _save_yaml_cache(self, concert_name): - """ - Save yaml cache file in temporary directory. If service manager is restarted, it load cached yaml file. - - :param yaml_file: temporary location for storing cache file as concert name - :type str - """ - - yaml_file_name = self._yaml_file.split('/')[-1] - if '.services' in yaml_file_name: - yaml_stream = yaml.load(open(self._yaml_file, 'r')) - cache_yaml_file = get_home(concert_name) + '/' + yaml_file_name - cache_yaml_stream = file(cache_yaml_file, 'w') - yaml.safe_dump(yaml_stream, cache_yaml_stream, default_flow_style=False) - - def _check_cache(self, yaml_file, concert_name): - """ - Check whether cached yaml file is existed or not - - :param yaml_file: yaml file name about services list for checking - :type str - :param yaml_file: temporary location for checking as concert name - :type str - - :returns: flag and full path yaml file as result of check existence about cache file. If cache file is existed, return true and cache path. Otherwise, return false and original path - :rtype: (bool, str) - - """ - - yaml_file_name = yaml_file.split('/')[-1] - cache_yaml_file = get_home(concert_name) + '/' + yaml_file_name - if os.path.isfile(cache_yaml_file): - return (True, cache_yaml_file) - else: - return (False, yaml_file) - def __str__(self): s = '' s += console.bold + 'Service Profiles: \n' + console.reset @@ -219,12 +173,11 @@ def _load_service_profiles(self, service_configurations): for service_configuration in service_configurations: try: service_profile = ServiceProfile( - service_configuration.hash, - service_configuration.resource_name, - service_configuration.overrides, - self._cached_service_profile_locations, - self._is_reading_from_cache - ) + service_configuration.hash, + service_configuration.resource_name, + service_configuration.overrides, + self._cached_service_profile_locations + ) except (InvalidServiceProfileException) as e: rospy.logwarn("Service Manager : %s" % e) continue diff --git a/concert_service_manager/src/concert_service_manager/service_profile.py b/concert_service_manager/src/concert_service_manager/service_profile.py index 0c012eb..be032e0 100644 --- a/concert_service_manager/src/concert_service_manager/service_profile.py +++ b/concert_service_manager/src/concert_service_manager/service_profile.py @@ -17,8 +17,6 @@ import unique_id import rocon_console.console as console import scheduler_msgs.msg as scheduler_msgs -import rocon_std_msgs.msg as rocon_std_msgs - from .exceptions import InvalidServiceProfileException @@ -29,19 +27,17 @@ class ServiceProfile(object): __slots__ = [ - 'msg', # basic data storage for the profile - concert_msgs.ServiceProfile - '_last_modified', # timestamp of last .service file modification - 'hash', # hashlib.sha224 used by the service pool to track this profile - 'resource_name', # local copy of the resource name - '_overrides', # any overrides that need to be applied after any loading - '_location_cache', # pointer to the service profile location cache maintained by the service pool { resource_name : filename } - # aliases - 'name', - '_concert_name', # concert name for searching cached service profile - '_is_config_from_cache' # flag informed whether solution config is loaded from cahce or not - ] - - def __init__(self, hash_id, resource_name, overrides, service_profile_location_cache, is_reading_solution_config_from_cache): + 'msg', # basic data storage for the profile - concert_msgs.ServiceProfile + '_last_modified', # timestamp of last .service file modification + 'hash', # hashlib.sha224 used by the service pool to track this profile + 'resource_name', # local copy of the resource name + '_overrides', # any overrides that need to be applied after any loading + '_location_cache', # pointer to the service profile location cache maintained by the service pool { resource_name : filename } + # aliases + 'name' + ] + + def __init__(self, hash_id, resource_name, overrides, service_profile_location_cache): """ Loads the service profile given the data provided by the solution configuration (i.e. resource name + overrides). We provide an arg for the filename here even @@ -60,9 +56,6 @@ def __init__(self, hash_id, resource_name, overrides, service_profile_location_c :param service_profile_location_cache: this class will use and update the cache if it's own filename changes :type service_profile_location_cache: { resource_name : os pathname } - :param is_reading_service_profile_from_cache: flag informed whether solution config is loaded from cahce file or not - :type is_reading_service_profile_from_cache: bool - :returns: the solution configuration data for services in this concert :rtype: concert_msgs.ServiceProfile @@ -73,7 +66,6 @@ def __init__(self, hash_id, resource_name, overrides, service_profile_location_c self._overrides = overrides self.resource_name = resource_name self._last_modified = None # gets updated when we load the profile - self._is_config_from_cache = is_reading_solution_config_from_cache try: self.msg = self._load_profile() self._last_modified = time.ctime(os.path.getmtime(self._filename())) @@ -153,23 +145,7 @@ def _load_profile(self): # fill in unique identifier used by services and their requesters msg.uuid = unique_id.toMsg(unique_id.fromRandom()) - # dump parameter detail as type of key-value - if 'parameters' in loaded_profile.keys(): - replace['parameters_detail'] = [] - # todo if cache, if original - parameters_yaml_file = '' - if self._is_config_from_cache: - parameters_yaml_file = rocon_python_utils.ros.find_resource_from_string(loaded_profile['parameters'] + '.parameters') - else: - parameters_yaml_file = '' - try: - with open(parameters_yaml_file) as f: - parameters_yaml = yaml.load(f) - for param_key in parameters_yaml.keys(): - msg.parameters_detail.append(rocon_std_msgs.KeyValue(param_key, parameters_yaml[param_key])) - except rospkg.ResourceNotFound as e: - raise e - + return msg def reload(self): From f316776480151e48697009578afa1f54549840b4 Mon Sep 17 00:00:00 2001 From: dwlee Date: Tue, 20 Jan 2015 09:14:57 +0900 Subject: [PATCH 17/21] share logic about load parameter from cahce, file, resource --- .../concert_service_manager/load_params.py | 27 +++++-------------- .../service_instance.py | 8 +++--- 2 files changed, 9 insertions(+), 26 deletions(-) diff --git a/concert_service_manager/src/concert_service_manager/load_params.py b/concert_service_manager/src/concert_service_manager/load_params.py index ca794cc..8278666 100644 --- a/concert_service_manager/src/concert_service_manager/load_params.py +++ b/concert_service_manager/src/concert_service_manager/load_params.py @@ -16,27 +16,12 @@ # Methods ############################################################################## +def load_parameters_from_resource(parameter_resource_name, namespace, name, load): + filepath = rocon_python_utils.ros.find_resource_from_string(resource_name, extension='parameters') + load_parameters_from_file(filepath, namespace, name, load) -def load_parameters_from_file(parameter_file, namespace, name, load): - filepath = rocon_python_utils.ros.find_resource_from_string(parameter_file, extension='parameters') - - with open(filepath) as f: - params = yaml.load(f) - for k, v in params.items(): - if k in INVALID_PARAM: - if load: - rospy.logwarn("Service Manager : %s%s [%s]" % (str(k), ' is prohibitted parameter. Ignoring...', name)) - continue - param_name = namespace + '/' + k - if load: - rospy.set_param(param_name, v) - else: - rospy.delete_param(param_name) - - -def load_parameters_from_cache(catche_file_path, namespace, name, load): - filepath = catche_file_path - +def load_parameters_from_file(parameter_file_path, namespace, name, load): + filepath = parameter_file_path with open(filepath) as f: params = yaml.load(f) for k, v in params.items(): @@ -48,4 +33,4 @@ def load_parameters_from_cache(catche_file_path, namespace, name, load): if load: rospy.set_param(param_name, v) else: - rospy.delete_param(param_name) + rospy.delete_param(param_name) \ No newline at end of file diff --git a/concert_service_manager/src/concert_service_manager/service_instance.py b/concert_service_manager/src/concert_service_manager/service_instance.py index 6e6b6c9..cdd53fb 100644 --- a/concert_service_manager/src/concert_service_manager/service_instance.py +++ b/concert_service_manager/src/concert_service_manager/service_instance.py @@ -19,7 +19,7 @@ import rocon_interactions import unique_id -from .load_params import load_parameters_from_cache +from .load_params import load_parameters_from_file from .utils import * ############################################################################## @@ -87,9 +87,8 @@ def enable(self, unique_identifier, interactions_loader): # load up parameters first so that when start runs, it can find the params immediately if self.msg.parameters != '': namespace = concert_msgs.Strings.SERVICE_NAMESPACE + '/' + self.msg.name - #load_parameters_from_file(self.msg.parameters, namespace, self.msg.name, load=True) parameter_path = os.path.join(get_service_profile_cache_home(self._concert_name, self.name), self.name + '.parameters') - load_parameters_from_cache(parameter_path, namespace, self.msg.name, load=True) + load_parameters_from_file(parameter_path, namespace, self.msg.name, load=True) # Refresh the unique id self.msg.uuid = unique_id.toMsg(unique_identifier) self._start() @@ -124,9 +123,8 @@ def disable(self, interactions_loader): interactions_loader.load(interaction_path, namespace=self._namespace, load=False, is_relative_path=False) if self.msg.parameters != '': namespace = concert_msgs.Strings.SERVICE_NAMESPACE + '/' + self.msg.name - #load_parameters_from_file(self.msg.parameters, namespace, self.msg.name, load=False) parameter_path = os.path.join(get_service_profile_cache_home(self._concert_name, self.name), self.name + '.parameters') - load_parameters_from_cache(parameter_path, namespace, self.msg.name, load=False) + load_parameters_from_file(parameter_path, namespace, self.msg.name, load=False) launcher_type = self.msg.launcher_type force_kill = False From 8928561d918c600d6305adfafae9d9c379da6fcc Mon Sep 17 00:00:00 2001 From: dwlee Date: Tue, 20 Jan 2015 13:47:50 +0900 Subject: [PATCH 18/21] add exception handling at some part --- .../service_cache_manager.py | 141 ++++++++---------- 1 file changed, 63 insertions(+), 78 deletions(-) diff --git a/concert_service_manager/src/concert_service_manager/service_cache_manager.py b/concert_service_manager/src/concert_service_manager/service_cache_manager.py index d42b149..314be5d 100644 --- a/concert_service_manager/src/concert_service_manager/service_cache_manager.py +++ b/concert_service_manager/src/concert_service_manager/service_cache_manager.py @@ -139,19 +139,25 @@ def _check_service_cache(self): """ Check whether cached yaml file regarding service profile is generated or not - :returns: flag and full path yaml file as result of check existence about cache file. If cache file is existed, return true and cache path. Otherwise, return false and original path + :returns: flag and full path of cache file. If cache file is existed, return true and cache path. Otherwise, return false and default resource path :rtype: str + :raises: :exc:`rospkg.ResourceNotFound` + """ check_result = True - default_service_configuration_file = rocon_python_utils.ros.find_resource_from_string(self._resource_name) - service_configuration_file_name = default_service_configuration_file.split('/')[-1] - service_configuration_file = get_home(self._concert_name) + '/' + service_configuration_file_name - if not os.path.isfile(service_configuration_file) or os.stat(service_configuration_file).st_size <= 0: - check_result = False - self._loginfo("load from default: [%s]" % self._resource_name) + try: + default_service_configuration_file = rocon_python_utils.ros.find_resource_from_string(self._resource_name) + except rospkg.ResourceNotFound as e: + raise e else: - self._loginfo("load from cache: [%s]" % service_configuration_file) + service_configuration_file_name = default_service_configuration_file.split('/')[-1] + service_configuration_file = get_home(self._concert_name) + '/' + service_configuration_file_name + if not os.path.isfile(service_configuration_file) or os.stat(service_configuration_file).st_size <= 0: + check_result = False + self._loginfo("load from default: [%s]" % self._resource_name) + else: + self._loginfo("load from cache: [%s]" % service_configuration_file) return (check_result, service_configuration_file) def _create_service_cache(self): @@ -165,10 +171,13 @@ def _create_service_cache(self): for service_configuration in service_configurations: resource_name = check_extension_name(service_configuration['resource_name'], '.service') overrides = service_configuration['overrides'] - - loaded_profile = self._load_service_profile_from_default(resource_name, overrides) - self._save_service_profile(loaded_profile) - loaded_profiles[loaded_profile['name']] = copy.deepcopy(loaded_profile) + try: + loaded_profile = self._load_service_profile_from_default(resource_name, overrides) + self._save_service_profile(loaded_profile) + loaded_profiles[loaded_profile['name']] = copy.deepcopy(loaded_profile) + except rospkg.ResourceNotFound as e: + self.logwarn('Cannot load service configuration: [%s]' % resource_name) + continue # save solution configuration self._save_solution_configuration(loaded_profiles) @@ -194,8 +203,8 @@ def _load_service_profile_from_default(self, resource_name, overrides): loaded_profile = yaml.load(f) except rospkg.ResourceNotFound as e: raise e - loaded_profile['resource_name'] = resource_name + # set priority to default if it was not configured if 'priority' not in loaded_profile.keys(): loaded_profile['priority'] = scheduler_msgs.Request.DEFAULT_PRIORITY @@ -208,8 +217,8 @@ def _load_service_profile_from_default(self, resource_name, overrides): if 'parameters' in loaded_profile.keys(): loaded_profile['parameters_detail'] = [] - parameters_yaml_file = rocon_python_utils.ros.find_resource_from_string(check_extension_name(loaded_profile['parameters'], '.parameters')) try: + parameters_yaml_file = rocon_python_utils.ros.find_resource_from_string(check_extension_name(loaded_profile['parameters'], '.parameters')) with open(parameters_yaml_file) as f: parameters_yaml = yaml.load(f) loaded_profile['parameters_detail'] = parameters_yaml @@ -217,14 +226,13 @@ def _load_service_profile_from_default(self, resource_name, overrides): raise e if 'interactions' in loaded_profile.keys(): - interactions_yaml_file = rocon_python_utils.ros.find_resource_from_string(check_extension_name(loaded_profile['interactions'], '.interactions')) try: + interactions_yaml_file = rocon_python_utils.ros.find_resource_from_string(check_extension_name(loaded_profile['interactions'], '.interactions')) with open(interactions_yaml_file) as f: interactions_yaml = yaml.load(f) loaded_profile['interactions_detail'] = interactions_yaml except rospkg.ResourceNotFound as e: raise e - return loaded_profile def _load_service_cache_from_cache(self, services_file_name): @@ -237,38 +245,39 @@ def _load_service_cache_from_cache(self, services_file_name): :raises: :exc:`rospkg.ResourceNotFound` ''' + if not os.path.isfile(services_file_name): + raise rospkg.ResourceNotFound('Cannot find service file: [%s]' % services_file_name) + with open(services_file_name) as f: service_list = yaml.load(f) for service in service_list: - try: - service_file_name = os.path.join(get_service_profile_cache_home(self._concert_name, service['name']), check_extension_name(service['name'], '.service')) - if not os.path.isfile(service_file_name) or os.stat(service_file_name).st_size <= 0: - rospy.logwarn("Service Manager : can not find service file in cache [%s]" % check_extension_name(service['name'], '.service')) - continue - with open(service_file_name) as f: - loaded_profile = yaml.load(f) - if 'parameters' in loaded_profile.keys(): - loaded_profile['parameters_detail'] = [] - parameters_yaml_file = os.path.join(get_service_profile_cache_home(self._concert_name, service['name']), loaded_profile['parameters']) - try: - with open(parameters_yaml_file) as f: - parameters_yaml = yaml.load(f) - loaded_profile['parameters_detail'] = parameters_yaml - except rospkg.ResourceNotFound as e: - raise e - - if 'interactions' in loaded_profile.keys(): - loaded_profile['interactions_detail'] = [] - interactions_yaml_file = os.path.join(get_service_profile_cache_home(self._concert_name, service['name']), loaded_profile['interactions']) - try: - with open(interactions_yaml_file) as f: - interactions_yaml = yaml.load(f) - loaded_profile['interactions_detail'] = interactions_yaml - except rospkg.ResourceNotFound as e: - raise e - - except rospkg.ResourceNotFound as e: - raise e + service_file_name = os.path.join(get_service_profile_cache_home(self._concert_name, service['name']), check_extension_name(service['name'], '.service')) + if not os.path.isfile(service_file_name) or os.stat(service_file_name).st_size <= 0: + rospy.logwarn("Service Manager : can not find service file in cache [%s]" % check_extension_name(service['name'], '.service')) + continue + with open(service_file_name) as f: + loaded_profile = yaml.load(f) + if 'parameters' in loaded_profile.keys(): + loaded_profile['parameters_detail'] = [] + parameters_yaml_file = os.path.join(get_service_profile_cache_home(self._concert_name, service['name']), loaded_profile['parameters']) + if not os.path.isfile(parameters_yaml_file) or os.stat(parameters_yaml_file).st_size <= 0: + rospy.logwarn("Service Manager : can not find parameters file in cache [%s]" % parameters_yaml_file) + continue + + with open(parameters_yaml_file) as f: + parameters_yaml = yaml.load(f) + loaded_profile['parameters_detail'] = parameters_yaml + + if 'interactions' in loaded_profile.keys(): + loaded_profile['interactions_detail'] = [] + interactions_yaml_file = os.path.join(get_service_profile_cache_home(self._concert_name, service['name']), loaded_profile['interactions']) + if not os.path.isfile(interactions_yaml_file) or os.stat(interactions_yaml_file).st_size <= 0: + rospy.logwarn("Service Manager : can not find interactions file in cache [%s]" % interactions_yaml_file) + continue + + with open(interactions_yaml_file) as f: + interactions_yaml = yaml.load(f) + loaded_profile['interactions_detail'] = interactions_yaml loaded_profile['msg'] = self._service_profile_to_msg(loaded_profile) self.service_profiles[loaded_profile['name']] = copy.deepcopy(loaded_profile) @@ -341,10 +350,7 @@ def _save_service_profile(self, loaded_service_profile_from_file): yaml.safe_dump(loaded_profile['parameters_detail'], f, default_flow_style=False) del (loaded_profile['parameters_detail']) - # if 'launcher' in loaded_profile.keys(): - # loaded_profile['launcher'] = rocon_python_utils.ros.find_resource_from_string(loaded_profile['launcher'] + '.launch') - - # delete msg data + # delete data unused in msg if 'msg' in loaded_profile.keys(): del (loaded_profile['msg']) @@ -374,30 +380,6 @@ def _save_solution_configuration(self, service_profiles): with file(cache_srv_config_file, 'w') as f: yaml.safe_dump(solution_configuration, f, default_flow_style=False) - def _find_resource_from_string(self, resource): - ''' - Todo, It is not implemented,yet. - - :param resource: finding resource name - :type resource: string - - ''' - - package, filename = roslib.names.package_resource_name(resource) - if not package: - raise rospkg.ResourceNotFound("resource could not be split with a valid leading package name [%s]" % (resource)) - try: - resolved = roslib.packages.find_resource(package, filename, None) - if not resolved: - raise rospkg.ResourceNotFound("cannot locate [%s] in package [%s]" % (filename, package)) - elif len(resolved) == 1: - return resolved[0] - elif len(resolved) > 1: - raise rospkg.ResourceNotFound("multiple resources named [%s] in package [%s]:%s\nPlease specify full path instead" % (filename, package, ''.join(['\n- %s' % r for r in resolved]))) - except rospkg.ResourceNotFound: - raise rospkg.ResourceNotFound("[%s] is not a package or launch file name [%s]" % (package, package + '/' + filename)) - return None - def _loginfo(self, msg): rospy.loginfo("Service Manager : " + str(msg)) @@ -445,11 +427,14 @@ def load_service_cache(self): load service profile from cache ''' - (check_result, solution_configuration_cache) = self._check_service_cache() - if not check_result: - self._create_service_cache() - self._load_service_cache_from_cache(solution_configuration_cache) - self._init_service_cache_list() + try: + (check_result, solution_configuration_cache) = self._check_service_cache() + if not check_result: + self._create_service_cache() + self._load_service_cache_from_cache(solution_configuration_cache) + self._init_service_cache_list() + except rospkg.ResourceNotFound as e: + self._logwarn(str(e)) def check_modification_service_cache(self): ''' From 3a93b98792f0ad203cefb5c3d475fdb77257404c Mon Sep 17 00:00:00 2001 From: dwlee Date: Tue, 20 Jan 2015 16:00:49 +0900 Subject: [PATCH 19/21] move common util function and fix code as that --- .../service_cache_manager.py | 27 +++++----- .../service_instance.py | 4 +- .../src/concert_service_manager/utils.py | 54 +++++-------------- 3 files changed, 26 insertions(+), 59 deletions(-) diff --git a/concert_service_manager/src/concert_service_manager/service_cache_manager.py b/concert_service_manager/src/concert_service_manager/service_cache_manager.py index 314be5d..c3210db 100644 --- a/concert_service_manager/src/concert_service_manager/service_cache_manager.py +++ b/concert_service_manager/src/concert_service_manager/service_cache_manager.py @@ -102,7 +102,6 @@ def __init__(self, concert_name, resource_name, modification_callback=None): self._cache_service_list = {} self.service_profiles = {} - setup_home_dirs(self._concert_name) self.load_service_cache() def _init_service_cache_list(self): @@ -121,7 +120,7 @@ def _get_service_cache_list(self): """ cache_list = {} - for root, dirs, files in os.walk(get_home(self._concert_name)): + for root, dirs, files in os.walk(get_concert_home(self._concert_name)): for dir_name in dirs: dir_path = str(os.path.join(root, dir_name)) cache_list[dir_path] = {} @@ -152,7 +151,7 @@ def _check_service_cache(self): raise e else: service_configuration_file_name = default_service_configuration_file.split('/')[-1] - service_configuration_file = get_home(self._concert_name) + '/' + service_configuration_file_name + service_configuration_file = get_concert_home(self._concert_name) + '/' + service_configuration_file_name if not os.path.isfile(service_configuration_file) or os.stat(service_configuration_file).st_size <= 0: check_result = False self._loginfo("load from default: [%s]" % self._resource_name) @@ -169,7 +168,7 @@ def _create_service_cache(self): loaded_profiles = {} service_configurations = load_solution_configuration_from_default(rocon_python_utils.ros.find_resource_from_string(self._resource_name)) for service_configuration in service_configurations: - resource_name = check_extension_name(service_configuration['resource_name'], '.service') + resource_name = rocon_python_utils.ros.check_extension_name(service_configuration['resource_name'], '.service') overrides = service_configuration['overrides'] try: loaded_profile = self._load_service_profile_from_default(resource_name, overrides) @@ -218,7 +217,7 @@ def _load_service_profile_from_default(self, resource_name, overrides): if 'parameters' in loaded_profile.keys(): loaded_profile['parameters_detail'] = [] try: - parameters_yaml_file = rocon_python_utils.ros.find_resource_from_string(check_extension_name(loaded_profile['parameters'], '.parameters')) + parameters_yaml_file = rocon_python_utils.ros.find_resource_from_string(rocon_python_utils.ros.check_extension_name(loaded_profile['parameters'], '.parameters')) with open(parameters_yaml_file) as f: parameters_yaml = yaml.load(f) loaded_profile['parameters_detail'] = parameters_yaml @@ -227,7 +226,7 @@ def _load_service_profile_from_default(self, resource_name, overrides): if 'interactions' in loaded_profile.keys(): try: - interactions_yaml_file = rocon_python_utils.ros.find_resource_from_string(check_extension_name(loaded_profile['interactions'], '.interactions')) + interactions_yaml_file = rocon_python_utils.ros.find_resource_from_string(rocon_python_utils.ros.check_extension_name(loaded_profile['interactions'], '.interactions')) with open(interactions_yaml_file) as f: interactions_yaml = yaml.load(f) loaded_profile['interactions_detail'] = interactions_yaml @@ -251,9 +250,9 @@ def _load_service_cache_from_cache(self, services_file_name): with open(services_file_name) as f: service_list = yaml.load(f) for service in service_list: - service_file_name = os.path.join(get_service_profile_cache_home(self._concert_name, service['name']), check_extension_name(service['name'], '.service')) + service_file_name = os.path.join(get_service_profile_cache_home(self._concert_name, service['name']), rocon_python_utils.ros.check_extension_name(service['name'], '.service')) if not os.path.isfile(service_file_name) or os.stat(service_file_name).st_size <= 0: - rospy.logwarn("Service Manager : can not find service file in cache [%s]" % check_extension_name(service['name'], '.service')) + rospy.logwarn("Service Manager : can not find service file in cache [%s]" % rocon_python_utils.ros.check_extension_name(service['name'], '.service')) continue with open(service_file_name) as f: loaded_profile = yaml.load(f) @@ -331,12 +330,11 @@ def _save_service_profile(self, loaded_service_profile_from_file): loaded_profile = copy.deepcopy(loaded_service_profile_from_file) service_name = loaded_profile['name'] - setup_service_profile_home_dirs(self._concert_name, service_name) service_profile_cache_home = get_service_profile_cache_home(self._concert_name, service_name) # writting interaction data if 'interactions_detail' in loaded_profile.keys(): - service_interactions_file_name = os.path.join(service_profile_cache_home, check_extension_name(service_name, '.interactions')) + service_interactions_file_name = os.path.join(service_profile_cache_home, rocon_python_utils.ros.check_extension_name(service_name, '.interactions')) loaded_profile['interactions'] = service_interactions_file_name.split('/')[-1] with file(service_interactions_file_name, 'w') as f: yaml.safe_dump(loaded_profile['interactions_detail'], f, default_flow_style=False) @@ -344,7 +342,7 @@ def _save_service_profile(self, loaded_service_profile_from_file): # writting parameter data if 'parameters_detail' in loaded_profile.keys(): - service_parameters_file_name = os.path.join(service_profile_cache_home, check_extension_name(service_name, '.parameters')) + service_parameters_file_name = os.path.join(service_profile_cache_home, rocon_python_utils.ros.check_extension_name(service_name, '.parameters')) loaded_profile['parameters'] = service_parameters_file_name.split('/')[-1] with file(service_parameters_file_name, 'w') as f: yaml.safe_dump(loaded_profile['parameters_detail'], f, default_flow_style=False) @@ -355,7 +353,7 @@ def _save_service_profile(self, loaded_service_profile_from_file): del (loaded_profile['msg']) # writting service profile data - service_profile_file_name = os.path.join(service_profile_cache_home, check_extension_name(service_name, '.service')) + service_profile_file_name = os.path.join(service_profile_cache_home, rocon_python_utils.ros.check_extension_name(service_name, '.service')) with file(service_profile_file_name, 'w') as f: yaml.safe_dump(loaded_profile, f, default_flow_style=False) @@ -376,7 +374,7 @@ def _save_solution_configuration(self, service_profiles): service_configuration_file_name = rocon_python_utils.ros.find_resource_from_string(self._resource_name).split('/')[-1] if '.services' in service_configuration_file_name: - cache_srv_config_file = get_home(self._concert_name) + '/' + service_configuration_file_name + cache_srv_config_file = get_concert_home(self._concert_name) + '/' + service_configuration_file_name with file(cache_srv_config_file, 'w') as f: yaml.safe_dump(solution_configuration, f, default_flow_style=False) @@ -396,7 +394,6 @@ def update_service_cache(self, service_profile_msg): :return: boolean result of update cache with message :rtype: (bool, str) ''' - result = True message = "" @@ -434,7 +431,7 @@ def load_service_cache(self): self._load_service_cache_from_cache(solution_configuration_cache) self._init_service_cache_list() except rospkg.ResourceNotFound as e: - self._logwarn(str(e)) + self._logwarn(str(e)) def check_modification_service_cache(self): ''' diff --git a/concert_service_manager/src/concert_service_manager/service_instance.py b/concert_service_manager/src/concert_service_manager/service_instance.py index cdd53fb..18cd6b6 100644 --- a/concert_service_manager/src/concert_service_manager/service_instance.py +++ b/concert_service_manager/src/concert_service_manager/service_instance.py @@ -95,7 +95,7 @@ def enable(self, unique_identifier, interactions_loader): if self.msg.interactions != '': # Can raise YamlResourceNotFoundException, MalformedInteractionsYaml interaction_path = os.path.join(get_service_profile_cache_home(self._concert_name, self.name), self.name + '.interactions') - interactions_loader.load(interaction_path, namespace=self._namespace, load=True, is_relative_path=False) + interactions_loader.load_from_file(interaction_path, namespace=self._namespace, load=True) # if there's a failure point, it will have thrown an exception before here. success = True @@ -120,7 +120,7 @@ def disable(self, interactions_loader): if self.msg.interactions != '': # Can raise YamlResourceNotFoundException, MalformedInteractionsYaml interaction_path = os.path.join(get_service_profile_cache_home(self._concert_name, self.name), self.name + '.interactions') - interactions_loader.load(interaction_path, namespace=self._namespace, load=False, is_relative_path=False) + interactions_loader.load_from_file(interaction_path, namespace=self._namespace, load=False) if self.msg.parameters != '': namespace = concert_msgs.Strings.SERVICE_NAMESPACE + '/' + self.msg.name parameter_path = os.path.join(get_service_profile_cache_home(self._concert_name, self.name), self.name + '.parameters') diff --git a/concert_service_manager/src/concert_service_manager/utils.py b/concert_service_manager/src/concert_service_manager/utils.py index 0e0d988..0d7e9aa 100644 --- a/concert_service_manager/src/concert_service_manager/utils.py +++ b/concert_service_manager/src/concert_service_manager/utils.py @@ -10,67 +10,37 @@ import os import rocon_python_utils import rospkg -import rocon_std_msgs.msg as rocon_std_msgs ############################################################################## # Methods ############################################################################## -def setup_home_dirs(concert_name): - ''' - Set up the location of the home directory as concert name for storing - the solution configuration and profiles of services - - @return the service manager home directory (path object). - @type str - ''' - if not os.path.isdir(get_home(concert_name)): - os.makedirs(get_home(concert_name)) - - -def setup_service_profile_home_dirs(concert_name, service_name): - ''' - Set up the location of the home directory as concert and service name - - @return the service manager home directory (path object). - @type str - ''' - if not os.path.isdir(get_service_profile_cache_home(concert_name, service_name)): - os.makedirs(get_service_profile_cache_home(concert_name, service_name)) - - -def get_home(concert_name): +def get_concert_home(concert_name): ''' Retrieve the location of the home directory for the service manager - temporary storage needs as concert name + temporary storage needs as concert name. + If it is not existed, create new one and return this. @return the service manager home directory (path object). @type str ''' - return os.path.join(rospkg.get_ros_home(), 'rocon', concert_name) + concert_home = os.path.join(rocon_python_utils.ros.get_rocon_home(), concert_name) + if not os.path.isdir(concert_home): + os.makedirs(concert_home) + return concert_home def get_service_profile_cache_home(concert_name, service_name): ''' Retrieve the location of the directory used for storing service profile. + If it is not existed, create new one and return this. @return the rocon remocon qt settings directory (path object). @type str ''' - return os.path.join(get_home(concert_name), 'services', service_name) - - -def check_extension_name(file_name, extension_name): - ''' - Check whether file name include extension name. - If it does not include extension name, return the file name added extension name. - - @return file name included extension name - @type str - ''' - if extension_name in file_name: - return file_name - else: - return file_name + extension_name + service_profile_cache_home = os.path.join(get_concert_home(concert_name), 'services', service_name) + if not os.path.isdir(service_profile_cache_home): + os.makedirs(service_profile_cache_home) + return service_profile_cache_home From a387d992875f908daddd7d893ef42595230859bc Mon Sep 17 00:00:00 2001 From: dwlee Date: Tue, 20 Jan 2015 16:40:36 +0900 Subject: [PATCH 20/21] adjust get rocon friendly name function --- .../src/concert_service_manager/service_cache_manager.py | 4 ++-- .../src/concert_service_manager/service_instance.py | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/concert_service_manager/src/concert_service_manager/service_cache_manager.py b/concert_service_manager/src/concert_service_manager/service_cache_manager.py index c3210db..e3ae28e 100644 --- a/concert_service_manager/src/concert_service_manager/service_cache_manager.py +++ b/concert_service_manager/src/concert_service_manager/service_cache_manager.py @@ -96,7 +96,7 @@ class ServiceCacheManager(object): ] def __init__(self, concert_name, resource_name, modification_callback=None): - self._concert_name = concert_name.strip().lower().replace(' ', '_') + self._concert_name = rocon_python_utils.ros.get_ros_friendly_name(concert_name) self._resource_name = resource_name self._modification_callback = modification_callback self._cache_service_list = {} @@ -212,7 +212,7 @@ def _load_service_profile_from_default(self, resource_name, overrides): loaded_profile[key] = overrides[key] if 'launcher_type' not in loaded_profile.keys(): # not set loaded_profile['launcher_type'] = concert_msgs.ServiceProfile.TYPE_SHADOW - loaded_profile['name'] = loaded_profile['name'].strip().lower().replace(" ", "_") + loaded_profile['name'] = rocon_python_utils.ros.get_ros_friendly_name(loaded_profile['name']) if 'parameters' in loaded_profile.keys(): loaded_profile['parameters_detail'] = [] diff --git a/concert_service_manager/src/concert_service_manager/service_instance.py b/concert_service_manager/src/concert_service_manager/service_instance.py index 18cd6b6..254b7f9 100644 --- a/concert_service_manager/src/concert_service_manager/service_instance.py +++ b/concert_service_manager/src/concert_service_manager/service_instance.py @@ -35,7 +35,7 @@ class ServiceInstance(object): __slots__ = [ 'msg', # concert_msgs.ServiceProfile fixed and variable parameters - + '_namespace', # namespace that the service will run in '_lock', # protect service enabling/disabling '_proc', # holds the custom subprocess variable if TYPE_CUSTOM @@ -54,13 +54,13 @@ def __init__(self, concert_name=None, service_profile=None, env=os.environ): @param service_profile : @type concert_msgs.msg.ConcertService ''' - self._concert_name = concert_name.lower().replace(' ', '_') + self._concert_name = rocon_python_utils.ros.get_ros_friendly_name(concert_name) self.msg = service_profile # aliases self.name = self.msg.name # other self._namespace = '/services/' + str(self.msg.name) - + self._lock = threading.Lock() self._proc = None self._roslaunch = None @@ -98,7 +98,7 @@ def enable(self, unique_identifier, interactions_loader): interactions_loader.load_from_file(interaction_path, namespace=self._namespace, load=True) # if there's a failure point, it will have thrown an exception before here. success = True - + self.loginfo("service enabled [%s]" % self.msg.name) message = "success" except (rocon_interactions.YamlResourceNotFoundException, rocon_interactions.MalformedInteractionsYaml) as e: From dcc63c0b5aa618210ba7f5d3204a8cbb980a8631 Mon Sep 17 00:00:00 2001 From: dwlee Date: Tue, 20 Jan 2015 17:54:37 +0900 Subject: [PATCH 21/21] fix wrong try/catch --- .../service_cache_manager.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/concert_service_manager/src/concert_service_manager/service_cache_manager.py b/concert_service_manager/src/concert_service_manager/service_cache_manager.py index e3ae28e..769a0e1 100644 --- a/concert_service_manager/src/concert_service_manager/service_cache_manager.py +++ b/concert_service_manager/src/concert_service_manager/service_cache_manager.py @@ -149,14 +149,14 @@ def _check_service_cache(self): default_service_configuration_file = rocon_python_utils.ros.find_resource_from_string(self._resource_name) except rospkg.ResourceNotFound as e: raise e + + service_configuration_file_name = default_service_configuration_file.split('/')[-1] + service_configuration_file = get_concert_home(self._concert_name) + '/' + service_configuration_file_name + if not os.path.isfile(service_configuration_file) or os.stat(service_configuration_file).st_size <= 0: + check_result = False + self._loginfo("load from default: [%s]" % self._resource_name) else: - service_configuration_file_name = default_service_configuration_file.split('/')[-1] - service_configuration_file = get_concert_home(self._concert_name) + '/' + service_configuration_file_name - if not os.path.isfile(service_configuration_file) or os.stat(service_configuration_file).st_size <= 0: - check_result = False - self._loginfo("load from default: [%s]" % self._resource_name) - else: - self._loginfo("load from cache: [%s]" % service_configuration_file) + self._loginfo("load from cache: [%s]" % service_configuration_file) return (check_result, service_configuration_file) def _create_service_cache(self):