def _check_haros_name_is_complete(self, haros_name, context=''): """ Check if the HAROS name does not hint at an incomplete model (does not contain `?` as the last token if it contains `/` or is not `?`) :param haros_name: the HAROS string name to check :type haros_name: str :param context: optional context string for logging, defaults to '' :type context: str, optional :return: boolean to indicate if most likely complete model (`True`) or not (`False`) :rtype: boolean """ # VALIDATION AND ERROR HANDLING: Handle HAROS Issue where Models may be Incomplete and thus, # Have Incomplete Names and Even Types. if '/' in haros_name: complete = (haros_name.strip().split('/')[-1] != '?') else: complete = (haros_name.strip() != '?') if not complete: if context: context = '. [In {}]'.format(context) Logger.get_logger().log(LoggerLevel.ERROR, '[{}] INCOMPLETE HAROS Model Information Encountered for "{}"{}.'.format( self.__class__.TYPE, haros_name, context)) return complete
def _haros_model_instances(self): """ Returns the appropriate HAROS Model Instances :return: A set of HAROS Service Specifications :rtype: set(ServicePrimitive) """ encountered_haros_service_spec_ids = set() haros_service_specs = set() for haros_service_instance in self._haros_configuration.services: haros_service_spec = haros_service_instance.server # Handle Error Case Where Service Spec was a `NoneType` - from Acceptance Testing # with `chris_rrbot_modeling` demo. if haros_service_spec: haros_service_spec_id = haros_service_spec.type if (haros_service_spec_id not in encountered_haros_service_spec_ids): # Add Required Fields to Allow HAROS Primitive to be Used in Remodeler Structure. haros_service_spec.id = haros_service_spec_id haros_service_spec.name = haros_service_spec_id haros_service_specs.add(haros_service_spec) encountered_haros_service_spec_ids.add(haros_service_spec_id) else: Logger.get_logger().log(LoggerLevel.ERROR, '[{}] Cannot Locate Service Spec for Service "{}".'.format( self.__class__.TYPE, haros_service_instance.id)) return haros_service_specs
def _merge_haros_parameter_links_with_chris_parameter_nodes(self, haros_parameter_model, chris_parameter_model): """ Helper merging method to merge the HAROS Parameter Node Links with the CHRIS Parameter Nodes :param haros_parameter_model: the HAROS Parameter model :type haros_parameter_model: Parameter :param chris_parameter_model: the CHRIS Parameter model :type chris_parameter_model: Parameter :return: boolean indicating if the model was updated (`True`) or not (`False`) :rtype: boolean """ model_updated = False for link in haros_parameter_model.writes: haros_node_instance_name = link.node.id if self._check_haros_name_is_complete(haros_node_instance_name, 'Check for Parameter Nodes'): if haros_node_instance_name not in chris_parameter_model.setting_node_names: Logger.get_logger().log(LoggerLevel.INFO, '[{}] Adding HAROS Param\'s Setting Node "{}" to CHRIS Param Instance.'.format( self.__class__.TYPE, haros_node_instance_name)) chris_parameter_model.setting_node_names.add( haros_node_instance_name) model_updated = True for link in haros_parameter_model.reads: haros_node_instance_name = link.node.id if self._check_haros_name_is_complete(haros_node_instance_name, 'Check for Parameter Nodes'): if haros_node_instance_name not in chris_parameter_model.reading_node_names: Logger.get_logger().log(LoggerLevel.INFO, '[{}] Adding HAROS Param\'s Reading Node "{}" to CHRIS Param Instance.'.format( self.__class__.TYPE, haros_node_instance_name)) chris_parameter_model.reading_node_names.add( haros_node_instance_name) model_updated = True return model_updated
def read_model_from_pickle(directory_path, base_file_name, spec_only=False): """ Read model banks from directory containing YAML files :param directory_path: file path to YAML files :param base_file_name: base file name used in YAML files :return : instance of ROSModel """ bank_dict = {} Logger.get_logger().log(LoggerLevel.INFO, 'Reading ROS model from pickle files ...') # ROSModel.get_yaml_processor() for bank_type, bank_output_name in ROSModel.BANK_TYPES_TO_OUTPUT_NAMES.items( ): if spec_only and bank_type not in ROSModel.SPECIFICATION_TYPES: print "Specifications only - skipping ", bank_output_name continue file_name = '{}/{}_{}.pkl'.format(directory_path, base_file_name, bank_output_name) try: with open(file_name, 'rb') as fin: bank_data = pickle.load(fin) bank_dict[bank_type] = bank_data except IOError: Logger.get_logger().log( LoggerLevel.ERROR, 'Failed to read Pickle data for {} : {}'.format( bank_output_name, file_name)) bank_dict[bank_type] = ROSModel.BANK_TYPES_TO_BANK_CLASS[ bank_type]() # Create instance of the model class return ROSModel(bank_dict)
def load_model(input_directory, spec_only=False): """ Load model from folder :param input_directory: the input directory pointing to either yaml or pickle files (e.g. output/yaml) :return: ROSModel instance with models stored in dictionary by type """ try: input_type, input_base_file_name = get_input_file_type( input_directory) except IOError as e: Logger.get_logger().log( LoggerLevel.ERROR, 'Failed to deduce type from ' + input_directory + ' ...') print " Did you specify input folder with yaml or pickle files?" print " " + e return None if input_type == 'yaml': return ROSModel.read_model_from_yaml(input_directory, input_base_file_name, spec_only) elif input_type == 'pkl': return ROSModel.read_model_from_pickle(input_directory, input_base_file_name, spec_only) else: Logger.get_logger().log( LoggerLevel.ERROR, 'Unknown ROS model input type from ' + input_directory + ' ...') return None
def save_model_yaml_files(self, directory_path, base_file_name): """ Save the ROS bank metamodel instances to yaml files :param directory_path : directory to store files :param base_file_name: :return: None """ try: Logger.get_logger().log( LoggerLevel.INFO, 'Saving YAML files for ROS Computation Graph.') # ROSModel.get_yaml_processor() create_directory_path(directory_path) for bank_type, bank in self._bank_dictionary.items(): bank_output_name = ROSModel.BANK_TYPES_TO_OUTPUT_NAMES[ bank_type] file_name = '{}/{}_{}.yaml'.format(directory_path, base_file_name, bank_output_name) yaml_file = open(file_name, 'w') yaml.dump(bank, yaml_file, sort_keys=True) yaml_file.close() except IOError as ex: Logger.get_logger().log( LoggerLevel.ERROR, 'Failed to save YAML files for ROS Computation Graph.') print " ", ex
def _helper_merge_haros_model_with_chris_model(self, bank_type, haros_model, chris_model): """ Main merging helper method that handles specific details regarding the merging of a specific Service HAROS model with a specific Service CHRIS model :param bank_type: the corresponding CHRIS Model Bank Type :type bank_type: BankType :param haros_model: the specific HAROS model to obtain data from :type haros_model: Service :param chris_model: the specific CHRIS Model to merge data into :type chris_model: Service """ model_updated = False # VALIDATE Service Types to Alert the User of INCONSISTENT / INACCURATE DATA. haros_service_type = haros_model.type chris_service_type = chris_model.construct_type if (haros_service_type != chris_service_type): Logger.get_logger().log( LoggerLevel.ERROR, '[{}] HAROS Service\'s Type "{}" does NOT Match CHRIS Service\'s Type "{}".' .format(self.__class__.TYPE, haros_service_type, chris_service_type)) model_updated |= self._helper_merge_haros_services_with_chris_model( [haros_model.server], chris_model.service_provider_node_names, 'Provided') chris_model_service_clients = set() if self._helper_merge_haros_services_with_chris_model( haros_model.clients, chris_model_service_clients, 'Client'): chris_model.service_client_node_names = chris_model_service_clients model_updated = True return model_updated
def update_bank(self, bank_type, bank_dictionary): """ Add a new bank data to ROS model :param bank_type: a BankType :param bank_dictionary: dictionary of name to entity instances """ if bank_type not in BankType: raise ValueError("Invalid bank type " + str(bank_type)) if bank_type not in self._bank_dictionary: self._bank_dictionary[bank_type] = {} # Validate the inputs for key, value in bank_dictionary.items(): if not isinstance(key, str): raise KeyError( "ROSModel.update_bank: All keys must be strings - not " + str(type(key))) if not isinstance(value, self._bank_dictionary[bank_type].entity_class): raise ValueError( "ROSModel.update_bank: All values must be {} - not {}". format( self._bank_dictionary[bank_type].entity_class.__name__, value.__class__.__name__)) # Merge dictionarys Logger.get_logger().log( LoggerLevel.INFO, "Update {}".format(self.BANK_TYPES_TO_OUTPUT_NAMES[bank_type])) self._bank_dictionary[bank_type].update(bank_dictionary)
def save_model_info_files(self, directory_path, base_file_name): """ Save the ROS model to human-readable files :param directory_path : directory to store files :param base_file_name: file name string :return: """ try: Logger.get_logger().log( LoggerLevel.INFO, 'Saving human-readable files for ROS Computation Graph.') create_directory_path(directory_path) for bank_type, bank in self._bank_dictionary.items(): rows = [] rows.append(str(bank)) bank_output_name = ROSModel.BANK_TYPES_TO_OUTPUT_NAMES[ bank_type] file_name = '{}/{}_{}.txt'.format(directory_path, base_file_name, bank_output_name) with open(file_name, 'w') as fout: fout.write('\n'.join(rows)) except IOError as ex: Logger.get_logger().log( LoggerLevel.ERROR, 'Failed to save human-readable files for ROS Computation Graph.' ) print " ", ex
def machine(self): """ Extracts the machine ID from the uri information :return: machine ID as string """ if self._machine is None: self._machine = "UNKNOWN MACHINE" # initialize if "UNKNOWN" in self._uri: # Abandon if uri is unknown return self._machine tokens = self.uri.split("/") if len(tokens) < 2: # expect at least one / in uri Logger.get_logger().log( LoggerLevel.ERROR, '{}: {}.'.format("UNKNOWN MACHINE", tokens)) else: if tokens[-1] == "": # Assumes last token is empty after trailing / address = tokens[-2] else: address = tokens[-1] # Assumes machine : pid format of uri self._machine = address.split(":")[0] return self._machine
def _helper_merge_haros_model_with_chris_model(self, bank_type, haros_model, chris_model): """ Main merging helper method that handles specific details regarding the merging of a specific Topic HAROS model with a specific Topic CHRIS model :param bank_type: the corresponding CHRIS Model Bank Type :type bank_type: BankType :param haros_model: the specific HAROS model to obtain data from :type haros_model: Topic :param chris_model: the specific CHRIS Model to merge data into :type chris_model: Topic """ model_updated = False haros_topic_type = haros_model.type chris_topic_type = chris_model.construct_type # VALIDATE Topic Types to Alert the User of INCONSISTENT / INACCURATE DATA. if haros_topic_type != chris_topic_type: Logger.get_logger().log( LoggerLevel.ERROR, '[{}] HAROS Topic\'s Type "{}" does NOT Match CHRIS Topic\'s Type "{}".' .format(self.__class__.TYPE, haros_topic_type, chris_topic_type)) model_updated |= self._merge_haros_topic_links_with_chris_topic_nodes( haros_model, chris_model) return model_updated
def _collect_parameters_info(self): """ Helper method to create ParameterBuilders within the ROSModelBuilder's ParameterBankBuilder and to map parameters to associated NodeBuilders (obtained from the ROS Master API) """ for parameter_name in self._master.getParamNames(): # Initialize parameter bank for each parameter name self.parameter_bank[parameter_name] # pylint: disable=W0104 try: for parameter_name, node_names in self._master.getParamsToSettingCallers().items(): for node_name in node_names: if (node_name == '/roslaunch') or (not filters.NodeFilter.get_filter().should_filter_out(node_name)): self.parameter_bank[parameter_name].add_setting_node_name(node_name) if not filters.NodeFilter.get_filter().should_filter_out(node_name): self.node_bank[node_name].add_parameter_name(parameter_name, 'set', None) for parameter_name, node_names in self._master.getParamsToReadingCallers().items(): for node_name in node_names: if (node_name == '/roslaunch') or not filters.NodeFilter.get_filter().should_filter_out(node_name): self.parameter_bank[parameter_name].add_reading_node_name(node_name) if not filters.NodeFilter.get_filter().should_filter_out(node_name): self.node_bank[node_name].add_parameter_name(parameter_name, 'read', None) except AttributeError: msg = '\n Standard roscore does NOT provide parameter setting/reading information!\n' + \ ' Use custom roscore if desired.\n' + \ ' See chris_ros_snapshot README for more info!' Logger.get_logger().log(LoggerLevel.WARNING, msg)
def _create_new_chris_model(self, instance_name, haros_instance): """ Creates a new Node CHRIS model with the instance name and based on the corresponding HAROS instance :param instance_name: the HAROS instance name :type instance_name: str :param haros_instance: the corresponding HAROS model :type haros_instance: NodeInstance :return: a tuple containing the Bank Type and new CHRIS model :rtype: Tuple(BankType, Node) """ # DECISION: If HAROS cannot provide the ability to distinguish between a Nodelet and a Nodelet Manager, we will # just attempt to populate the newly created model into a Nodelet Bank (if we can tell if the HAROS # model represents a Nodelet) or just the Node Bank otherwise (which will catch Nodelet Managers # since there is no easy way to tell if it is a Manager). Besides, we should be able to gather all # Nodes using our CHRIS ROS Snapshot tool. if haros_instance.node.nodelet_class: Logger.get_logger().log( LoggerLevel.ERROR, '[{}] Create new chris nodelet model"{}".'.format( self.__class__.TYPE, instance_name)) return (BankType.NODELET, self._chris_model_banks[BankType.NODELET][instance_name]) Logger.get_logger().log( LoggerLevel.ERROR, '[{}] Create new chris node model"{}".'.format( self.__class__.TYPE, instance_name)) return (BankType.NODE, self._chris_model_banks[BankType.NODE][instance_name])
def _update_chris_model_version(cls, chris_model, model_updated): """ Increments the version and updates the source list for the CHRIS model, if it has been updated :param chris_model: the CHRIS model to update version and source for :type chris_model: _EntityMetamodel :param model_updated: `True` if model has been updated; `False` if not :type model_updated: boolean """ if model_updated: chris_model.version = chris_model.version + 1 new_sources = set() if isinstance(chris_model.source, list): for source in chris_model.source: new_sources.add(source) new_sources.add(cls.REMODELER_SOURCE_NAME) chris_model.source = sorted(new_sources) elif isinstance(chris_model.source, str): for source in chris_model.source.split(','): new_sources.add(source.strip()) new_sources.add(cls.REMODELER_SOURCE_NAME) chris_model.source = ', '.join(sorted(new_sources)) else: Logger.get_logger().log(LoggerLevel.ERROR, '[{}] Error Setting Source(s) for CHRIS Model "{}".'.format(cls.TYPE, chris_model.name))
def __init__(self): """ Instantiates an instance of the PackageModeler """ self._package_bank = PackageSpecificationBank() self._node_bank = NodeSpecificationBank() self._action_bank = TypeSpecificationBank() self._message_bank = TypeSpecificationBank() self._service_bank = TypeSpecificationBank() self._ros_model = None self._rospack = rospkg.RosPack() self._packages = None self._package_paths = None self._message_paths = None self._service_paths = None self._action_paths = None self._installed_deb_cache = None try: cache = apt.Cache() self._installed_deb_cache = { pkg.name: pkg for pkg in cache if pkg.is_installed } except: Logger.get_logger().log( LoggerLevel.ERROR, 'Cannot get installed package version (is this Ubuntu?) ...')
def _match_token_types(node_name, io_names, io_builders, spec_types): """ Look for matched tokens and/or data types between node and spec :param node_name: :param io_names: relevant names to process (dict) :param io_builders: builder for relevant type :param spec_types: corresponding data in specification :return: True if all valid, False otherwise """ try: if spec_types is None: if len(io_names) > 0: # Nothing defined for spec return False else: # We have action clients to match up in the spec available_tokens = set(spec_types) io_is_valid = True for io_name in sorted(io_names): builder = io_builders[io_name] io_type = builder.construct_type token = io_name.split("/")[-1] if token not in available_tokens or io_type != spec_types[token]: # look from matching item remaining tokens # prefer substring match potential = set([ss for ss in available_tokens if token in ss]) remaining = available_tokens - potential for test in sorted(potential): if spec_types[test] == io_type: # found match io_names[io_name] = test available_tokens.remove(test) break if io_names[io_name] is None: for test in sorted(remaining): if spec_types[test] == io_type: # found match io_names[io_name] = test available_tokens.remove(test) break if io_names[io_name] is None: # If still None, then no match found Logger.get_logger().log(LoggerLevel.WARNING, ' Node {} unmatched data {} !'.format(node_name, io_name)) io_is_valid = False else: # Found valid match io_names[io_name] = token available_tokens.remove(token) return io_is_valid except Exception as ex: print type(ex) print ex track = traceback.format_exc() print track sys.exit(-1)
def create_directory_path(directory_path): """ Create directory path if required :param directory_path: :return: None """ if not os.path.exists(directory_path): Logger.get_logger().log( LoggerLevel.DEBUG, 'Creating directory path {}.'.format(directory_path)) os.makedirs(directory_path)
def _collect_packages(self): """ Dictionary of packages to location """ # Retrieve the package information self._packages = self._rospack.list() self._package_paths = { package: self._rospack.get_path(package) for package in self._packages } self._message_paths = { package: [os.path.join(self._rospack.get_path(package), 'msg')] for package in self._packages } self._service_paths = { package: [os.path.join(self._rospack.get_path(package), 'srv')] for package in self._packages } self._action_paths = { package: [os.path.join(self._rospack.get_path(package), 'action')] for package in self._packages } for package_name in self._packages: try: try: path = self._package_paths[package_name] except ValueError: path = "UNKNOWN PATH" try: package_dependencies = self._rospack.get_depends( package_name) except rospkg.common.ResourceNotFound: #pylint: disable=redefined-variable-type package_dependencies = "NO DEPENDENCIES DEFINED" installed_version = self._get_installed_version(package_name) package = self._package_bank[package_name] package.update_attributes(directory_path=path, dependencies=package_dependencies, source=PackageModeler.source_name, installed_version=installed_version) except Exception as ex: Logger.get_logger().log( LoggerLevel.ERROR, "Processing {} {} : {}".format(package_name, type(ex), ex)) raise ex # kill for now to debug
def _extract_action_names_and_remove_corresponding_topics( self, topic_bank_builder, action_bank_builder): """ Associates the ROS Node with Action Server / Client related Action names from the ActionBankBuilder and removes the Action-related Topics from the ROS Node's Topic store :param topic_bank_builder: the TopicBankBuilder to use for Action-related Topic name removal :type topic_bank_builder: TopicBankBuilder :param action_bank_builder: the ActionBankBuilder to use for Action-related Topic name removal and Action Server / Client reference :type action_bank_builder: ActionBankBuilder :return: the mapping of Action Server / Client roles ('server' or 'client') to extracted Action names :rtype: dict{str: dict{str:str}} """ extracted_action_names = {'server': dict(), 'client': dict()} valid_topic_names = [ topic_name for topic_name in topic_bank_builder.names_to_entity_builders.keys() ] for status, topic_name_dict in { 'published': self.published_topic_names, 'subscribed': self.subscribed_topic_names }.items(): #Logger.get_logger().log(LoggerLevel.INFO, # 'Searching for {} action topics for node {}.'.format(status, self.name)) for topic_name in set(topic_name_dict.keys()): if topic_name not in valid_topic_names: topic_builder = TopicBuilder(topic_name) name_base, name_suffix = topic_builder.name_base, topic_builder.name_suffix if (name_base in action_bank_builder.names_to_entity_builders) and \ (name_suffix in ActionBuilder.TOPIC_SUFFIXES): action_builder = action_bank_builder[name_base] log_message = 'Found action {}. Removing topic {} from node {}.'.format( name_base, topic_name, self.name) if self.name in action_builder.client_node_names: extracted_action_names['client'][name_base] = None self.remove_topic_name(topic_name, status) elif self.name in action_builder.server_node_names: extracted_action_names['server'][name_base] = None self.remove_topic_name(topic_name, status) else: log_message = 'Failed to find action {}. Will NOT remove topic {} from node {}.'.format( name_base, topic_name, self.name) Logger.get_logger().log(LoggerLevel.INFO, log_message) return extracted_action_names
def _helper_populate_new_chris_model(self, bank_type, instance_name, haros_model, new_chris_model_instance): """ Helper method to help populate the new CHRIS model based on the corresponding HAROS model :param bank_type: the Bank Type used to create the new CHRIS model :type bank_type: BankType :param instance_name: the HAROS instance name :type instance_name: str :param haros_model: the corresponding HAROS model :type haros_model: MetamodelObject :param new_chris_model_instance: the new CHRIS model to update/populate :type new_chris_model_instance: _EntityMetamodel """ Logger.get_logger().log(LoggerLevel.INFO, '[{}] Not Populating New Model "{}".'.format( self.__class__.TYPE, instance_name))
def check_for_version_request(options): """ Checks the command line options for a version request; if present and true, shows the version and exits; else, does nothing :param options: argparse command line options with `version` boolean present :type options: argparse.Namespace """ if options.version: file_name = os.path.join(os.path.dirname( __file__), '..', '..', 'VERSION') with open(file_name) as input_file: version = input_file.read().strip() Logger.get_logger().log(LoggerLevel.INFO, 'chris_haros_model_merger version: {}.'.format(version)) sys.exit(0)
def _gather_uri(self): """ Helper method to gather the ROS Node URI from the ROS Master :return: the gathered ROS Node URI from the ROS Master :rtype: str """ try: return ROSUtilities.get_ros_utilities().master.lookupNode( self.name) except rosgraph.masterapi.MasterError as ex: error_message = 'URI for node {} cannot be retrieved from ROS Master'.format( self.name) Logger.get_logger().log(LoggerLevel.ERROR, '{}: {}.'.format(error_message, ex)) return "UNKNOWN URI FOR {}".format(self.name)
def _helper_populate_new_chris_model(self, bank_type, instance_name, haros_model, new_chris_model_instance): """ Helper method to help populate the new Service Specification CHRIS model based on the corresponding HAROS model :param bank_type: the Bank Type used to create the new CHRIS model :type bank_type: BankType :param instance_name: the HAROS instance name :type instance_name: str :param haros_model: the corresponding HAROS model :type haros_model: ServicePrimitive :param new_chris_model_instance: the new CHRIS model to update/populate :type new_chris_model_instance: ServiceSpecification """ Logger.get_logger().log(LoggerLevel.INFO, '[{}] No Additional Population is Possible for Service Spec "{}".'.format( self.__class__.TYPE, instance_name))
def _create_new_chris_model(self, instance_name, haros_instance): """ Creates a new CHRIS model with the instance name and based on the corresponding HAROS instance :param instance_name: the HAROS instance name :type instance_name: str :param haros_instance: the corresponding HAROS model :type haros_instance: MetamodelObject :return: a tuple containing the Bank Type and new CHRIS model :rtype: Tuple(BankType, _EntityMetamodel) """ chosen_chris_model_bank_type = self._chris_model_banks.keys()[0] Logger.get_logger().log(LoggerLevel.INFO, '[{}] Create new chris model from base_remodeler type={} "{}"...'.format( self.__class__.TYPE, chosen_chris_model_bank_type, instance_name)) return (chosen_chris_model_bank_type, self._chris_model_banks[chosen_chris_model_bank_type][instance_name])
def get_yaml_processor(): """ Return YAML handler """ Logger.get_logger().log(LoggerLevel.INFO, " Set up YAML processing for meta models ...") for sub_class in _BankMetamodel.__subclasses__(): # print "adding representor and constructor for ", sub_class.yaml_tag yaml.add_representer(sub_class.yaml_tag, sub_class.__repr__) partial_func = partial(ROSModel.yaml_constructor, sub_class) yaml.add_constructor(sub_class.yaml_tag, partial_func) for sub_class in _EntityMetamodel.__subclasses__(): # print "adding representor and constructor for ", sub_class.yaml_tag yaml.add_representer(sub_class.yaml_tag, sub_class.__repr__) partial_func = partial(ROSModel.yaml_constructor, sub_class) yaml.add_constructor(sub_class.yaml_tag, partial_func)
def _merge_haros_node_links_with_chris_node_parameters( self, haros_node_model_links, chris_node_model_parameters, link_type): """ Helper merging method to merge the HAROS Node Parameters Links with the CHRIS Node Parameters :param haros_node_model_links: HAROS Read or Written Parameter Links :type haros_node_model_links: list[ParameterPrimitive] :param chris_node_model_parameters: CHRIS Written or Read Parameter Names to Types :type chris_node_model_parameters: dict{str: str} :param link_type: Read or Written Link Types (`Read` or `Set`) for logging :type link_type: str :return: boolean indicating if the model was updated (`True`) or not (`False`) :rtype: boolean """ model_updated = False for haros_link in haros_node_model_links: haros_parameter_instance_name = haros_link.param_name if self._check_haros_name_is_complete( haros_parameter_instance_name, 'Check for Node Parameters'): # VALIDATE Parameter Parent Types to Alert the User of INCONSISTENT / INACCURATE DATA. if haros_parameter_instance_name in chris_node_model_parameters: haros_parameter_parent_type = haros_link.parameter.name chris_parameter_parent_type = chris_node_model_parameters[ haros_parameter_instance_name] if haros_parameter_parent_type != chris_parameter_parent_type: Logger.get_logger().log( LoggerLevel.ERROR, '[{}] HAROS "{}" Parameter Parent\'s Type "{}" for Parameter "{}" does NOT Match CHRIS Parameter Parent\'s Type "{}".' .format(self.__class__.TYPE, link_type, haros_parameter_parent_type, haros_parameter_instance_name, chris_parameter_parent_type)) # elif haros_parameter_instance_name not in chris_node_model_parameters: else: Logger.get_logger().log( LoggerLevel.INFO, '[{}] Adding HAROS Node\'s {} Parameter "{}" to CHRIS Node Instance.' .format(self.__class__.TYPE, link_type, haros_parameter_instance_name)) chris_node_model_parameters[ haros_parameter_instance_name] = haros_link.parameter.name model_updated = True return model_updated
def _merge_haros_node_links_with_chris_node_services( self, haros_node_model_links, chris_node_model_services, link_type): """ Helper merging method to merge the HAROS Node Service Links with the CHRIS Node Services :param haros_node_model_links: HAROS Service Server or Client Links :type haros_node_model_links: list[ServicePrimitive] :param chris_node_model_services: CHRIS Provided or Client Service Names to Types :type chris_node_model_services: dict{str: str} :param link_type: Server or Client Link Types (`Provided` or `Client`) for logging :type link_type: str :return: boolean indicating if the model was updated (`True`) or not (`False`) :rtype: boolean """ model_updated = False for haros_link in haros_node_model_links: haros_service_instance_name = haros_link.service.id if self._check_haros_name_is_complete(haros_service_instance_name, 'Check for Node Services'): # VALIDATE Service Parent Types to Alert the User of INCONSISTENT / INACCURATE DATA. if haros_service_instance_name in chris_node_model_services: haros_service_parent_type = haros_link.service.name chris_service_parent_type = chris_node_model_services[ haros_service_instance_name] if haros_service_parent_type != chris_service_parent_type: Logger.get_logger().log( LoggerLevel.ERROR, '[{}] HAROS "{}" Service Parent\'s Type "{}" for Service "{}" does NOT Match CHRIS Service Parent\'s Type "{}".' .format(self.__class__.TYPE, link_type, haros_service_parent_type, haros_service_instance_name, chris_service_parent_type)) # elif haros_service_instance_name not in chris_node_model_services: else: Logger.get_logger().log( LoggerLevel.INFO, '[{}] Adding HAROS Node\'s {} Service "{}" to CHRIS Node Instance.' .format(self.__class__.TYPE, link_type, haros_service_instance_name)) chris_node_model_services[ haros_service_instance_name] = haros_link.service.name model_updated = True return model_updated
def _merge_haros_model_with_chris_model(self, bank_type, haros_instance_name, haros_model, chris_model): """ Method that handles merging of a specific HAROS model with a specific CHRIS model :param bank_type: the corresponding CHRIS Model Bank Type :type bank_type: BankType :param haros_instance_name: the string HAROS model instance name :type haros_instance_name: str :param haros_model: the specific HAROS model to obtain data from :type haros_model: MetamodelObject :param chris_model: the specific CHRIS Model to merge data into :type chris_model: _EntityMetamodel """ Logger.get_logger().log(LoggerLevel.INFO, '[{}] Merging HAROS Instance with CHRIS Instance "{}".'.format( self.__class__.TYPE, haros_instance_name)) model_updated = self._helper_merge_haros_model_with_chris_model( bank_type, haros_model, chris_model) self.__class__._update_chris_model_version(chris_model, model_updated)
def _helper_merge_haros_model_with_chris_model(self, bank_type, haros_model, chris_model): """ Main merging helper method that handles specific details regarding the merging of a specific Package Specification HAROS model with a specific Package Specification CHRIS model :param bank_type: the corresponding CHRIS Model Bank Type :type bank_type: BankType :param haros_model: the specific HAROS model to obtain data from :type haros_model: Package :param chris_model: the specific CHRIS Model to merge data into :type chris_model: PackageSpecification """ model_updated = False haros_package_name = haros_model.name # VALIDATE Package Directory Path to Alert the User of INCONSISTENT / INACCURATE DATA. haros_directory_path = haros_model.path chris_directory_path = chris_model.directory_path if (haros_directory_path and (haros_directory_path != chris_directory_path)): Logger.get_logger().log(LoggerLevel.ERROR, '[{}] HAROS Package Directory Path "{}" for Package "{}" does NOT Match CHRIS Package Directory Path "{}".'.format( self.__class__.TYPE, haros_directory_path, haros_package_name, chris_directory_path)) # VALIDATE Package Version to Alert the User of INCONSISTENT / INACCURATE DATA. haros_version = haros_model.version chris_installed_version = chris_model.installed_version if (haros_version and (haros_version != chris_installed_version)): Logger.get_logger().log(LoggerLevel.ERROR, '[{}] HAROS Package Version "{}" for Package "{}" does NOT Match CHRIS Package Installed Version "{}".'.format( self.__class__.TYPE, haros_version, haros_package_name, chris_installed_version)) # VALIDATE Package Metapackage Status to Alert the User of INCONSISTENT / INACCURATE DATA. haros_is_metapackage = haros_model.is_metapackage chris_is_metapackage = chris_model.is_metapackage if (haros_is_metapackage != chris_is_metapackage): Logger.get_logger().log(LoggerLevel.ERROR, '[{}] HAROS Package Metapackage Status "{}" for Package "{}" does NOT Match CHRIS Package Metapackage Status "{}".'.format( self.__class__.TYPE, haros_is_metapackage, haros_package_name, chris_is_metapackage)) # VALIDATE Package URL to Alert the User of INCONSISTENT / INACCURATE DATA. haros_vcs_url = haros_model.vcs_url chris_url = chris_model.url if haros_vcs_url: if (chris_url and (haros_vcs_url != chris_url)): Logger.get_logger().log(LoggerLevel.ERROR, '[{}] HAROS Package URL "{}" for Package "{}" does NOT Match CHRIS Package URL "{}".'.format( self.__class__.TYPE, haros_vcs_url, haros_package_name, chris_url)) else: chris_model.url = haros_vcs_url model_updated = True model_updated |= self._helper_merge_haros_dependencies_with_chris_model( haros_model, chris_model) model_updated |= self._helper_merge_haros_nodes_with_chris_model( haros_model, chris_model) haros_description = haros_model.description if (haros_description): chris_model.description = haros_description.strip() model_updated = True return model_updated
def _helper_merge_haros_dependencies_with_chris_model(self, haros_model, chris_model): """ Helper merging method to merge the HAROS Package Specification Dependencies with the CHRIS Package Specification Dependencies :param haros_node_model: the HAROS Package Specification model :type haros_node_model: Package :param chris_node_model: the CHRIS Package Specification model :type chris_node_model: PackageSpecification :return: boolean indicating if the model was updated (`True`) or not (`False`) :rtype: boolean """ model_updated = False for haros_dependency_name in haros_model.dependencies.packages: if self._check_haros_name_is_complete(haros_dependency_name, 'Check for Node Spec Dependency'): if haros_dependency_name not in chris_model.dependencies: Logger.get_logger().log(LoggerLevel.INFO, '[{}] Adding HAROS Package Spec\'s Dependency "{}" to CHRIS Package Spec.'.format( self.__class__.TYPE, haros_dependency_name)) chris_model.dependencies.append(haros_dependency_name) model_updated = True return model_updated