def setUsesTransformationFrame(self): # Controllo che il system in cui è incluso il process che include il thread abbia # un subcomponent di tipo tf if not tfs.hasTransformationFrameEnabledInSystem(self.system_root): self.associated_class.setUsesTransformationFrame(False) return False # Controllo che ho una connessione che parte dal mio thread ed esce verso il # process di tipo transformation frame tf_source_port_name = "tf" tf_connection_port_info = tfs.getConnectionPortInfoBySource(self.process, self.type, tf_source_port_name) # Il thread NON usa sicuramente i transformation frame if tf_connection_port_info is None: self.associated_class.setUsesTransformationFrame(False) return False (parent_name, parent_port) = tfs.getDestFromPortInfo(tf_connection_port_info) if not tfs.isConnectedToSystemTransformationFrame(self.system_root, parent_name, parent_port): self.associated_class.setUsesTransformationFrame(False) return False self.associated_class.setUsesTransformationFrame(True) return True
def __init__(self, process): self.process = process self.name = tfs.getName(process) self.type = tfs.getType(process) self.namespace = tfs.getNamespace(process) self.remap = [] self.topic_ports = {}
def populateData(self): self.main_thread = self.associated_class.getMainThread() if self.main_thread is None: return False, "Unable to get the Main Thread" thread_function = tfs.getSubprogram(self.thread) if thread_function is None: return False, "Unable to find the right Subprogram" ############################ ### TRANSFORMATION FRAME ### ############################ # Controllo l'uso del Transformation Frame self.thread_uses_tf = self.setUsesTransformationFrame() ################## ### SUBSCRIBER ### ################## (status, desc) = self.populateSubscriberData() if not status: return status, desc ################# ### PUBLISHER ### ################# (status, desc) = self.populatePublisherData() if not status: return status, desc ################### ### SOURCE TEXT ### ################### self.source_text_function = self.createSourceTextFileFromSourceText( tfs.getSourceText(thread_function), tfs.getSourceName(thread_function)) # Aggiungo la chiamata alla funzione custome if self.source_text_function: self.source_text_function.setFunctionType(self.output_type) self.source_text_function.addFunctionParameter(self.sub_input_var) self.source_text_function.setTF(self.thread_uses_tf) code = "{}.publish({});".format( self.var_publisher_pub.name, self.source_text_function.generateInlineCode()) self.sub_callback.addMiddleCode(code) else: return False, "Unable to find Source_Text or Source_Name" return True, ""
def __init__(self, _system_root, _process, _thread, _associated_class): # AADLProcess a cui un AADLThread fa riferimento self.associated_class = _associated_class # Processo e thread relativi self.system_root = _system_root self.process = _process self.thread = _thread # Tipo thread self.type = tfs.getType(self.thread) # Nome del thread self.name = tfs.getName(self.thread) # TF self.thread_uses_tf = False
def getStateJSON(self): parameters = [] variables = [] # get the name of the JSON file json_file = tfs.getSourceText(self.process) json_schema = tfs.getInternalStateSourceText(self.process) if not json_file or not json_schema: log.warning("No Params and Vars file specified for {}".format(self.associated_class.node_name)) return parameters, variables # absolute location of the JSON file # TODO - assumption: AADL and XML file locations are the same json_file = os.path.join(global_filepath.aadl_model_dir, json_file) json_schema = os.path.join(global_filepath.aadl_model_dir, json_schema) json_base_schema = sys.path[0]+"/internal_state_base.schema.json" try: loaded_json_file = json.load(open(json_file)) loaded_json_schema = json.load(open(json_schema)) loaded_json_base_schema = json.load(open(json_base_schema)) except json.JSONDecodeError: print("invalid json file") return parameters, variables try: jsonschema.validate(loaded_json_file, loaded_json_base_schema) jsonschema.validate(loaded_json_file, loaded_json_schema) except jsonschema.exceptions.ValidationError: print("validation failed") return parameters, variables if self.JSON_PARAMS in loaded_json_schema[self.JSON_STATE]["properties"]: list_of_params = loaded_json_schema[self.JSON_STATE]["properties"][self.JSON_PARAMS]["properties"] values_of_params = loaded_json_file[self.JSON_PARAMS] for key, value in list_of_params.items(): parameters.append(self.getVariableFromJSON(key, value, values_of_params.get(key))) if self.JSON_VARS in loaded_json_schema[self.JSON_STATE]["properties"]: list_of_vars = loaded_json_schema[self.JSON_STATE]["properties"][self.JSON_VARS]["properties"] values_of_vars = loaded_json_file[self.JSON_VARS] for key, value in list_of_vars.items(): variables.append(self.getVariableFromJSON(key, value, values_of_vars.get(key))) return parameters, variables
def startGeneration(): XMLTags.initialize() # Parameters # Input ocarina_ros_path = global_filepath.xml_folder_path xml_filename = global_filepath.xml_filename # Setup # Salvo il momento della generazione automatica, in modo da poterlo segnare nel file today = datetime.datetime.now() generated_on = today.strftime("%d/%m/%Y %H:%M:%S") print("Avvio generazione: {}".format(generated_on)) # Read XML # Leggo il file XML generato dal backend ever_xml tree = etree.parse(os.path.join(ocarina_ros_path, xml_filename)) # Ottengo la root del system preso in considerazione system_root = tree.getroot() # Siccome itererò su tutti i vari system disponbili, inzio ad aggiungere # la mia system root, dopo mano a mano aggiungerò anche tutti gli altri system su # cui fare code-generation systems = [{'system': system_root, 'parent': None}] while len(systems) > 0: s = systems.pop(0) generateSystemCode(s['system'], s['parent']) # Mi cerco eventuali system dentro ad altri system. Questo serve nel caso in cui un # system sia usato come subcomponents di un altro system. La cosa è ricorsiva, poiché # di volta in volta la system_root diventa il system considerato. # La prima visita si fa comunque alla system root, dopo si passa a visitare ricorsivamente # tutti i vari system sub_systems = s['system'].findall("./" + XMLTags.tags['TAG_SUBCOMPONENTS'] + "/" + XMLTags.tags['TAG_SUBCOMPONENT'] + "/" + XMLTags.tags['TAG_SYSTEM'] + "/" + "[" + XMLTags.tags['TAG_CATEGORY'] + "='system']") # Io so chi è il genitore dei nodi, mi basta chiedere il system con quel certo # namespace al systems manager system_parent = sm.getSystemForNamespace(tfs.getNamespace(s['system'])) for sub_sys in sub_systems: systems.append({'system': sub_sys, 'parent': system_parent}) sm.generateAllSystems()
def getDefaultTopicName(self, thread_port_name, input=False, output=False): if not input and not output: return False, "No direction specified" thread_name = tfs.getName(self.thread) # Ottengo tutte le connesioni di un processo che mappano la porta specificata in process_port_name # del thread associato a questo processo con una porta qualunque del thread. connections = tfs.getAllConnectionsPerPort(self.process, thread_name, thread_port_name, input=input, output=output) names = [] # Per ognuna delle connesioni trovate sopra bisogna cercare la porta verso cui (o da cui) è presente # la connessione e poi cercare la proprietà del topic name su di essa for c in connections: process_port_name = None # Ottengo il nome della porta if input: port_info = tfs.getPortInfoByDestPortInfo(c, thread_name, thread_port_name) (process_name, process_port_name) = tfs.getSourceFromPortInfo(port_info) elif output: port_info = tfs.getPortInfoBySourcePortInfo(c, thread_name, thread_port_name) (process_name, process_port_name) = tfs.getDestFromPortInfo(port_info) # La porta è una feature, quindi si usa getFeatureByName process_port = tfs.getFeatureByName(self.process, process_port_name) (topic_namespace, topic_name) = tfs.getDefaultTopicName(process_port) if topic_namespace is None or topic_name is None: return False, "Unable to get topic name" names.append(topic_name) # Rimuovo i duplicati names = list(set(names)) if len(names) < 1: return False, "No topic name defined" if len(names) > 1: return False, "Multiple topic names defined" self.topic = names[0] return True, ""
def __init__(self, system_root, namespace=None): # Caso di system veri e propri che contengono process if system_root is not None: self.system_root = system_root self.namespace = tfs.getNamespace(system_root) # Log dell'inizio della generazione del system log.info("System {}".format(tfs.getType(system_root))) # Caso di system che sono composti da data e basta e quindi # non hanno una vera e propria system root else: self.system_root = None self.namespace = namespace log.info("Data system {}".format(self.namespace)) # Creo i file CMakeLists e PackageXML self.cmake_list = CMakeLists(self) self.package_xml = PackageXML(self) # Variabile che conterrà il launch file self.launch_file = None # Resetto la struttura di cartelle (eliminando anche tutti i file) solamente alla prima generazione # del namespace, in tutte le altre i file non vengono eliminati self.system_folder = folderTree.createSystemFolderTree(self.namespace, delete=(not sm.isSystemAlreadyReset(self.namespace))) sm.addResetSystem(self.namespace) # Contiene tutti i nodi da generare self.nodes = [] # Contiene tutti i messaggi da generare self.messages = [] # Contiene tutti i servizi da generare self.services = []
def addPortForTopicFromPortName(self, port_name, connection): try: port = self.process.find("./" + XMLTags.tags['TAG_FEATURES'] + "/" + XMLTags.tags['TAG_FEATURE'] + "/" + "[" + XMLTags.tags['TAG_NAME'] + "='" + port_name + "']") except Exception: return False (topic_properties_namespace, default_topic_name) = tfs.getDefaultTopicName(port) (topic_properties_namespace, new_topic_name) = tfs.getTopicName(connection) if default_topic_name is None or new_topic_name is None: log.warning("No topic remap for port {} of process {}".format( port_name, self.name)) return True # Se ho già controllato quella porta vado oltre if self.hasPortForTopic(port_name): if self.topic_ports[port_name] == new_topic_name: return True else: log.error( "Multiple topics defined for port {} of process {}".format( port_name, self.name)) return False r = Remap(default_topic_name, new_topic_name) self.topic_ports[port_name] = new_topic_name self.addRemap(r) return True
def populateData(self): # Ottengo tutti i dati relativi al main thread (prepare, tearDown, errorHandler) = tfs.getMainThreadFunctions(self.thread) self.prepare = Prepare(self.associated_class) self.prepare.source_text_function = self.createSourceTextFileFromSourceText(tfs.getSourceText(prepare), "custom_prepare") self.tearDown = TearDown(self.associated_class) self.tearDown.source_text_function = self.createSourceTextFileFromSourceText(tfs.getSourceText(tearDown), "custom_teardown") # Aggiungo la chiamata alla funzione custome if self.tearDown.source_text_function: code = "{};".format(self.tearDown.source_text_function.generateInlineCode()) self.tearDown.addMiddleCode(code) self.errorHandling = ErrorHandling(self.associated_class) self.errorHandling.source_text_function = self.createSourceTextFileFromSourceText( tfs.getSourceText(errorHandler), "custom_errorHandling") # Aggiungo la chiamata alla funzione custome if self.errorHandling.source_text_function: code = "{};".format(self.errorHandling.source_text_function.generateInlineCode()) self.errorHandling.addBottomCode(code) self.associated_class.addPrivateMethod(self.prepare) self.associated_class.addPrivateMethod(self.tearDown) self.associated_class.addPrivateMethod(self.errorHandling) # Ottengo lo stato (parameters, variables) in ASN.1 del nodo # (parameters, variables) = self.getStateASN() (parameters, variables) = self.getStateJSON() # Se ho almeno parametri o variabili, allora genero # anche la node configuration, altrimenti no if len(parameters) > 0 or len(variables) > 0: type_internal_state = Type() type_internal_state.setTypeName("InternalState") internal_state_is = Variable(self.associated_class) internal_state_is.setName("is") internal_state_is.setType(type_internal_state) self.associated_class.addInternalVariable(internal_state_is) node_conf = NodeConfiguration(self.associated_class) self.associated_class.setNodeConfiguration(node_conf) if len(variables) > 0: vars_struct = VariablesStruct(self.associated_class) for v in variables: vars_struct.addVariable(v) self.associated_class.node_configuration.addStruct(vars_struct) self.associated_class.node_configuration.has_variables = True # Se ho dei parametri, allora genero la struct if len(parameters) > 0: params_struct = ParametersStruct(self.associated_class) self.prepare.addTopCode("Parameters p;") for p in parameters: params_struct.addVariable(p) self.createHandlerForParameter(p) self.prepare.addMiddleCode("is.initialize(&p);") self.associated_class.node_configuration.addStruct(params_struct) self.associated_class.node_configuration.has_parameters = True else: self.prepare.addMiddleCode("is.initialize();") # Aggiungo la chiamata alla funzione custome if self.prepare.source_text_function: self.prepare.source_text_function.setFunctionType(Bool(self.associated_class)) code = "return {};".format(self.prepare.source_text_function.generateInlineCode()) self.prepare.addBottomCode(code) else: # Return alla fine del metodo main self.prepare.addBottomCode("return true;") return True, ""
def populateData(self): main_thread = self.associated_class.getMainThread() if main_thread == None: return (False, "Unable to get the Main Thread") ################### ### Output Port ### ################### # Essendo bidirezionale posso trovare la connessione sia come source che come dest conn_by_source = True process_output_port = tfs.getConnectionPortInfoBySource( self.process, self.type, self.output_port_name) if process_output_port == None: conn_by_source = False process_output_port = tfs.getConnectionPortInfoByDest( self.process, self.type, self.output_port_name) if process_output_port == None: return ( False, "Unable to find the right binding between process requires subprogram access port and " "thread input port") if conn_by_source: (dest_parent_name, dest_name) = tfs.getDestFromPortInfo(process_output_port) else: (dest_parent_name, dest_name) = tfs.getSourceFromPortInfo(process_output_port) if dest_parent_name == None or dest_name == None: return ( False, "Unable to find the process requires subprogram access port name" ) self.process_port = tfs.getFeatureByName(self.process, name=dest_name) if self.process_port == None: return ( False, "Unable to find the process requires subprogram access port feature" ) # Dopo aver trovato la porta del process, controllo il nome di default del # services associato (topic_namespace, self.default_service_name) = tfs.getDefaultTopicName( self.process_port) if self.default_service_name == None: self.default_service_name = "service_name_default" log.warning( "Default Service Name not found, set as {} as default.".format( self.default_service_name)) ################################## ### ASN.1 Request and Response ### ################################## (aadl_namespace, aadl_type) = tfs.getPortDatatypeByPort(self.process_port) if aadl_namespace is None or aadl_type is None: return False, "Unable to identify process port type" # Controllo se c'è un file ASN.1 associato alla porta. Se c'è allora il tipo di servizio # è custom e lo dovrò generare, mentre se non c'è allora è un servizio standard ROS port_data_info = tfs.getPortDataInfo(self.process_port) if port_data_info is None: return False, "Unable to get the port data info for process port" self.asn_description = tfs.getSourceText(port_data_info) if self.asn_description is None: self.service = Service(aadl_namespace, aadl_type) else: # Creo il servizio custom e lo associo al nodo che lo ha generato self.service = sfs.getServiceFromJSON(self.asn_description, self.associated_class) # if self.service == None: # return (False, "Error in ASN.1 parsing") # self.associated_class.addService( self.service ) # Genero ed aggiungo la libreria del services al nodo service_library = Library(self.service.namespace) service_library.setPath("{}/{}.h".format(self.service.namespace, self.service.name)) self.associated_class.addLibrary(service_library) ########################## ### SERVICE CLIENT VAR ### ########################## var_serviceclient = Variable(self.associated_class) var_serviceclient.setName("service_client_{}".format(self.name)) var_serviceclient.setType(ROS_ServiceClient(self.associated_class)) self.associated_class.addInternalVariable(var_serviceclient) main_thread.prepare.addMiddleCode( "{} = handle.serviceClient<{}::{}>(\"{}\");".format( var_serviceclient.name, self.service.namespace, self.service.name, self.default_service_name)) return True, ""
def populateData(self): main_thread = self.associated_class.getMainThread() if main_thread == None: return False, "Unable to get the Main Thread" ######################### # TRANSFORMATION FRAME # ######################## # Controllo l'uso del Transformation Frame self.thread_uses_tf = self.setUsesTransformationFrame() ############### # Output Port # ############### # Essendo birezeizonale posso trovare la connessione sia come source che come dest conn_by_source = True process_input_port = tfs.getConnectionPortInfoBySource( self.process, self.type, self.input_port_name) if process_input_port is None: conn_by_source = False process_output_port = tfs.getConnectionPortInfoByDest( self.process, self.type, self.input_port_name) if process_input_port is None: return ( False, "Unable to find the right binding between process requires subprogram access port and " "thread input port") if conn_by_source: (source_parent_name, source_name) = tfs.getDestFromPortInfo(process_input_port) else: (source_parent_name, source_name) = tfs.getSourceFromPortInfo(process_output_port) if source_parent_name is None or source_name is None: return False, "Unable to find the process provides subprogram access port name" self.process_port = tfs.getFeatureByName(self.process, name=source_name) if self.process_port is None: return False, "Unable to find the process provides subprogram access port feature" # Dopo aver trovato la porta del process, controllo il nome di default del # services associato (topic_namespace, self.default_service_name) = tfs.getDefaultTopicName( self.process_port) if self.default_service_name == None: self.default_service_name = "service_name_default" log.warning( "Default Service Name not found, set as {} as default.".format( self.default_service_name)) ################################## ### ASN.1 Request and Response ### ################################## (aadl_namespace, aadl_type) = tfs.getPortDatatypeByPort(self.process_port) if aadl_namespace == None or aadl_type == None: return (False, "Unable to identify process port type") # Controllo se c'è un file ASN.1 associato alla porta. Se c'è allora il tipo di servizio # è custom e lo dovrò generare, mentre se non c'è allora è un servizio standard ROS port_data_info = tfs.getPortDataInfo(self.process_port) if port_data_info == None: return (False, "Unable to get the port data info for process port") self.asn_description = tfs.getSourceText(port_data_info) if self.asn_description == None: # @TODO: Standard Service log.warning("STANDARD SERVICE") # return (False, "Unable to find property Source_Text for the services caller with ASN.1 description") else: # Creo il servizio custom e lo associo al nodo che lo ha generato self.service = sfs.getServiceFromJSON(aadl_namespace, aadl_type, self.asn_description, self.associated_class) if self.service == None: return (False, "Error in ASN.1 parsing") # Genero ed aggiungo la libreria del services al nodo service_library = Library(self.associated_class) service_library.setPath("{}/{}.h".format(self.service.namespace, self.service.name)) self.associated_class.addLibrary(service_library) ########################## ### SERVICE SERVER VAR ### ########################## var_service_server = Variable(self.associated_class) var_service_server.setName("service_server_{}".format(self.name)) var_service_server.setType(ROS_ServiceServer(self.associated_class)) self.associated_class.addInternalVariable(var_service_server) ############################### ### SERVICE SERVER CALLBACK ### ############################### self.server_callback = Method(self.associated_class) self.server_callback.method_name = "{}_service_callback".format( self.name) self.server_callback.return_type = Bool(self.associated_class) self.server_callback.namespace = self.associated_class.class_name # REQUEST Parameter input_param_req = Variable(self.associated_class) input_param_req.setName("&req") input_param_req.setType( ROS_ServiceServer_Request( self.associated_class, "{}::{}".format(self.service.namespace, self.service.name))) input_param_req.setIsParameter() # RESPONSE Parameter input_param_res = Variable(self.associated_class) input_param_res.setName("&res") input_param_res.setType( ROS_ServiceServer_Response( self.associated_class, "{}::{}".format(self.service.namespace, self.service.name))) input_param_res.setIsParameter() self.server_callback.addInputParameter(input_param_req) self.server_callback.addInputParameter(input_param_res) ############### # SOURCE TEXT # ############### function = tfs.getSubcomponentByInfo(self.thread, name=self.function_name, namespace="ros", category="subprogram") if function is None: return False, "Unable to find the function subprogram" self.source_text_function = self.createSourceTextFileFromSourceText( tfs.getSourceText(function), tfs.getSourceName(function)) if self.source_text_function is None: return False, "Unable to find property Source_Text or Source_Name" self.source_text_function.setTF(self.thread_uses_tf) # Aggiungo la chiamata alla funzione custom if self.source_text_function != None: self.source_text_function.addServiceReqAndRes( input_param_req, input_param_res) self.source_text_function.addLibrary(service_library) self.source_text_function.setFunctionType( Bool(self.associated_class)) code = "return {};".format( self.source_text_function.generateInlineCode()) self.server_callback.addMiddleCode(code) self.associated_class.addPrivateMethod(self.server_callback) main_thread.prepare.addMiddleCode( "{} = handle.advertiseService(\"{}\", {}, this);".format( var_service_server.name, self.default_service_name, self.server_callback.getThreadPointer())) return (True, "")
def populateData(self): main_thread = self.associated_class.getMainThread() if main_thread is None: return False, "Unable to get the Main Thread" # Ottengo le informazioni necessarie per i thread di tipo Timer: # - Source Text # - Period thread_function = tfs.getSubprogram(self.thread) if thread_function is None: return False, "Unable to find the right Subprogram" # TRANSFORMATION FRAME # Controllo l'uso del Transformation Frame self.thread_uses_tf = self.setUsesTransformationFrame() # Source Text self.source_text_function = self.createSourceTextFileFromSourceText( tfs.getSourceText(thread_function), tfs.getSourceName(thread_function)) if self.source_text_function is None: return False, "Unable to find property Source_Text or Source_Name" self.source_text_function.setTF(self.thread_uses_tf) # FREQUENCY (period, period_unit) = tfs.getPeriod(self.thread) if period is None or period_unit is None: return False, "Unable to find property Period with relative value and unit" # Conversione in secondi della frequenza a partire da qualunque unità di misura try: period_quantity = ureg("{} {}".format(period, period_unit)) period_quantity.ito(ureg.second) self.frequency_in_hz = 1.0 / period_quantity.magnitude self.period_in_seconds = period_quantity.magnitude except ValueError: return False, "Unable to convert Period in seconds" # TIMER var_timer = Variable(self.associated_class) var_timer.setName("timer_{}".format(self.name)) var_timer.setType(ROS_Timer(self.associated_class)) self.associated_class.addInternalVariable(var_timer) ###################### ### TIMER CALLBACK ### ###################### self.timerCallback = Method(self.associated_class) self.timerCallback.method_name = "{}_callback".format(self.name) self.timerCallback.return_type = Void(self.associated_class) self.timerCallback.namespace = self.associated_class.class_name input_par = Variable(self.associated_class) input_par.setIsParameter() input_par.setType(ROS_TimerEvent(self.associated_class)) input_par.setName("") self.timerCallback.addInputParameter(input_par) # Aggiungo la chiamata alla funzione custom if self.source_text_function: code = "{};".format(self.source_text_function.generateInlineCode()) self.timerCallback.addMiddleCode(code) self.associated_class.addPrivateMethod(self.timerCallback) main_thread.prepare.addMiddleCode( "{} = handle.createTimer(ros::Duration({}), {}, this);".format( var_timer.name, self.period_in_seconds, self.timerCallback.getThreadPointer())) return True, ""
def populateSubscriberData(self): ################## ### Input Port ### ################## # Ottengo la connesione che mappa la porta di input del thread subscriber # con quella che entra nel process process_input_port = tfs.getConnectionPortInfoByDest( self.process, self.type, self.input_port_name) if process_input_port == None: return ( False, "Unable to find the right binding between process input port and thread input port" ) (source_parent_name, source_name) = tfs.getSourceFromPortInfo(process_input_port) if source_parent_name == None or source_name == None: return (False, "Unable to find the process input port name") self.sub_process_port = tfs.getFeatureByName(self.process, name=source_name) if self.sub_process_port == None: return (False, "Unable to find the process input port name feature") ################## ### INPUT TYPE ### ################## (aadl_namespace, aadl_type) = tfs.getPortDatatypeByPort(self.sub_process_port) if aadl_namespace == None or aadl_type == None: return (False, "Unable to identify process port type") # Controllo se c'è un file ASN.1 associato alla porta. Se c'è allora il tipo di messaggio # è custom e lo dovrò generare, mentre se non c'è allora è un messaggio standard ROS port_data_info = tfs.getPortDataInfo(self.sub_process_port) if port_data_info == None: return (False, "Unable to get the port data info for process port") port_data_source_asn = tfs.getSourceText(port_data_info) if port_data_source_asn == None: # Se è None allora non c'è alcun file ASN.1 associato e quindi è un messaggio standard ROS raw_output_type = dt.getROSDatatypeFromAADL( aadl_namespace, aadl_type, self.associated_class) if raw_output_type == None: return (False, "Datatype {} NOT supported".format(raw_output_type)) else: self.input_type = raw_output_type else: self.custom_message = mfs.getMessageFromJSON( aadl_namespace, aadl_type, port_data_source_asn, self.associated_class) self.input_type = Type(self.associated_class) self.input_type.setTypeName(self.custom_message.name) self.input_type.setNamespace(self.custom_message.namespace) self.input_type.setConst(_const=True) self.input_type.setAfterTypeName("::ConstPtr&") # Associo la librerie del messaggio al tipo di output, sia custom che standard input_type_library = Library() input_type_library.setPath("{}/{}.h".format(self.input_type.namespace, self.input_type.type_name)) self.input_type.setLibrary(input_type_library) ######################## ### SUBSCRIBER TOPIC ### ######################## (status, desc) = self.getDefaultTopicName(self.input_port_name, input=True) if status == False: return (status, desc) ################## ### QUEUE SIZE ### ################## queue_size_default_value = 1 self.queue_size = tfs.getSubscriberQueueSize( self.thread, port_name=self.input_port_name) if self.queue_size == None: self.queue_size = queue_size_default_value log.info("Queue size set to default value: {}".format( self.queue_size)) ###################### ### SUBSCRIBER VAR ### ###################### var_subscriber_pub = Variable(self.associated_class) var_subscriber_pub.setName("sub_{}".format(self.name)) var_subscriber_pub.setType(ROS_Subscriber(self.associated_class)) self.associated_class.addInternalVariable(var_subscriber_pub) ########################### ### SUBSCRIBER CALLBACK ### ########################### self.sub_callback = Method(self.associated_class) self.sub_callback.method_name = "{}_callback".format(self.name) self.sub_callback.return_type = Void(self.associated_class) self.sub_callback.namespace = self.associated_class.class_name self.sub_input_var = Variable(self.associated_class) self.sub_input_var.setType(self.input_type) self.sub_input_var.setName("msg") self.sub_input_var.setIsParameter() self.sub_callback.addInputParameter(self.sub_input_var) self.associated_class.addPrivateMethod(self.sub_callback) self.main_thread.prepare.addMiddleCode( "{} = handle.subscribe(\"{}\", {}, {}, this);".format( var_subscriber_pub.name, self.topic, self.queue_size, self.sub_callback.getThreadPointer())) return (True, "")
def populatePublisherData(self): ################### ### Output Port ### ################### # Ottengo la connesione che mappa la porta di input del thread subscriber # con quella che entra nel process process_output_port = tfs.getConnectionPortInfoBySource( self.process, self.type, self.output_port_name) if process_output_port == None: return ( False, "Unable to find the right binding between process input port and thread input port" ) (dest_parent_name, dest_name) = tfs.getDestFromPortInfo(process_output_port) if dest_parent_name == None or dest_name == None: return (False, "Unable to find the process input port name") self.pub_process_port = tfs.getFeatureByName(self.process, name=dest_name) if self.pub_process_port == None: return (False, "Unable to find the process input port name feature") ################### ### OUTPUT TYPE ### ################### (aadl_namespace, aadl_type) = tfs.getPortDatatypeByPort(self.pub_process_port) if aadl_namespace == None or aadl_type == None: return (False, "Unable to identify process port type") # Controllo se c'è un file ASN.1 associato alla porta. Se c'è allora il tipo di messaggio # è custom e lo dovrò generare, mentre se non c'è allora è un messaggio standard ROS port_data_info = tfs.getPortDataInfo(self.pub_process_port) if port_data_info == None: return (False, "Unable to get the port data info for process port") port_data_source_asn = tfs.getSourceText(port_data_info) if port_data_source_asn == None: # Se è None allora non c'è alcun file ASN.1 associato e quindi è un messaggio standard ROS raw_output_type = dt.getROSDatatypeFromAADL( aadl_namespace, aadl_type, self.associated_class) if raw_output_type == None: return (False, "Datatype {} NOT supported".format(raw_output_type)) else: self.output_type = raw_output_type else: self.custom_message = mfs.getMessageFromJSON( aadl_namespace, aadl_type, port_data_source_asn, self.associated_class) self.output_type = Type(self.associated_class) self.output_type.setTypeName(self.custom_message.name) self.output_type.setNamespace(self.custom_message.namespace) # Associo la librerie del messaggio al tipo di output, sia custom che standard output_type_library = Library() output_type_library.setPath("{}/{}.h".format( self.output_type.namespace, self.output_type.type_name)) self.output_type.setLibrary(output_type_library) ############# ### TOPIC ### ############# (status, desc) = self.getDefaultTopicName(self.output_port_name, output=True) if not status: return status, desc ##################### ### PUBLISHER VAR ### ##################### self.var_publisher_pub = Variable(self.associated_class) self.var_publisher_pub.setName("pub_{}".format(self.name)) self.var_publisher_pub.setType(ROS_Publisher(self.associated_class)) self.associated_class.addInternalVariable(self.var_publisher_pub) self.main_thread.prepare.addMiddleCode( "{} = handle.advertise<{}>(\"{}\", 10);".format( self.var_publisher_pub.name, self.output_type.generateCode(), self.topic)) return True, ""
def generateSystemCode(system_root, system_parent): # Generando il system si generano anche i file CMakeLists e PackageXML # Viene generata tutto l'albero delle cartelle e viene resettato se necessario namespace = tfs.getNamespace(system_root) system = sm.getSystemForNamespace(namespace) if system is None: system = System(system_root) sm.addSystem(system) # Se il system non ha un launch file associato e serve crearlo, allora lo creo # Il launch file a questo punto è vuoto if system.launch_file is None: system.createLaunchFile() # Se ho un genitore, ovvero un system che mi ha incluso, # lo avviso che dovrà includermi if system_parent: if system_parent.launch_file: system_parent.launch_file.addSubSystem(system.launch_file) # Ricerco tutti i processi all'interno del system processes = system_root.findall("./" + XMLTags.tags['TAG_SUBCOMPONENTS'] + "/" + XMLTags.tags['TAG_SUBCOMPONENT'] + "/" + "[" + XMLTags.tags['TAG_CATEGORY'] + "='process']" + "[" + XMLTags.tags['TAG_NAMESPACE'] + "='" + system.namespace + "']") # Scorro ogni processo. Per ogni processo controllo i subcomponent: in base alle varie tipologie # di subcomponent avvio la generazione di diversi nodi ROS for process in processes: threads = process.findall("./" + XMLTags.tags['TAG_SUBCOMPONENTS'] + "/" + XMLTags.tags['TAG_SUBCOMPONENT'] + "/" + "[" + XMLTags.tags['TAG_CATEGORY'] + "='thread']") # Cerco il main thread, che formerà la base per tutti gli altri thread. main_thread = process.find("./" + XMLTags.tags['TAG_SUBCOMPONENTS'] + "/" + XMLTags.tags['TAG_SUBCOMPONENT'] + "/" + "[" + XMLTags.tags['TAG_CATEGORY'] + "='thread']" + "/" + "[" + XMLTags.tags['TAG_NAME'] + "='main_thread']" + "/" + "[" + XMLTags.tags['TAG_NAMESPACE'] + "='ros']") if main_thread: thread_type = (tfs.getType(main_thread)).lower() p = AADLProcess(process, system_root, system) renameExistingNodeClass(p, system.system_folder) gen_main_thread = createNewThread( system_root, process, main_thread, getPythonClassFromAADLThreadType(thread_type), p) p.threads.append(gen_main_thread) for thread in threads: # name = (tfs.getName(thread)).lower() thread_type = (tfs.getType(thread)).lower() namespace = (tfs.getNamespace(thread)).lower() if (namespace == "ros" or namespace == "global_state_machine" ) and not isMainThread(thread_type): new_thread = createNewThread( system_root, process, thread, getPythonClassFromAADLThreadType(thread_type), p) if new_thread: p.threads.append(new_thread) p.addTransformationFrameComponent() system.addNode(p)
def populateData(self): main_thread = self.associated_class.getMainThread() if main_thread == None: return (False, "Unable to get the Main Thread") # Ottengo le informazioni necessarie per i thread di tipo Publisher: # - Source Text # - Period thread_function = tfs.getSubprogram(self.thread) if thread_function == None: return (False, "Unable to find the right Subprogram") ############################ ### TRANSFORMATION FRAME ### ############################ # Controllo l'uso del Transformation Frame self.thread_uses_tf = self.setUsesTransformationFrame() ################### ### Output Port ### ################### # Ottengo la connesione che mappa la porta di input del thread subscriber # con quella che entra nel process process_output_port = tfs.getConnectionPortInfoBySource(self.process, self.type, self.output_port_name) if process_output_port is None: return False, "Unable to find the right binding between process input port and thread input port" (dest_parent_name, dest_name) = tfs.getDestFromPortInfo(process_output_port) if dest_parent_name is None or dest_name is None: return False, "Unable to find the process input port name" self.process_port = tfs.getFeatureByName(self.process, name=dest_name) if self.process_port is None: return False, "Unable to find the process input port name feature" (aadl_namespace, aadl_type) = tfs.getPortDatatypeByPort(self.process_port) if aadl_namespace is None or aadl_type is None: return False, "Unable to identify process port type" # Controllo se c'è un file ASN.1 associato alla porta. Se c'è allora il tipo di messaggio # è custom e lo dovrò generare, mentre se non c'è allora è un messaggio standard ROS port_data_info = tfs.getPortDataInfo(self.process_port) if port_data_info is None: return False, "Unable to get the port data info for process port" port_data_source_asn = tfs.getSourceText(port_data_info) if port_data_source_asn is None: # Se è None allora non c'è alcun file ASN.1 associato e quindi è un messaggio standard ROS raw_output_type = dt.getROSDatatypeFromAADL(aadl_namespace, aadl_type, self.associated_class) if raw_output_type is None: return False, "Datatype {} NOT supported".format(raw_output_type) else: self.output_type = raw_output_type else: self.custom_message = mfs.getMessageFromJSON(aadl_namespace, aadl_type, port_data_source_asn, self.associated_class) self.output_type = Type(self.associated_class) self.output_type.setTypeName(self.custom_message.name) self.output_type.setNamespace(self.custom_message.namespace) # Associo la librerie del messaggio al tipo di output, sia custom che standard output_type_library = Library() output_type_library.setPath("{}/{}.h".format(self.output_type.namespace, self.output_type.type_name)) self.output_type.setLibrary(output_type_library) ################### ### Source Text ### ################### self.source_text_function = self.createSourceTextFileFromSourceText(tfs.getSourceText(thread_function), tfs.getSourceName(thread_function)) if self.source_text_function is None: return False, "Unable to find property Source_Text or Source_Name" self.source_text_function.setTF(self.thread_uses_tf) self.source_text_function.setFunctionType(self.output_type) ################# ### FREQUENCY ### ################# (period, period_unit) = tfs.getPeriod(self.thread) if period is None or period_unit is None: return False, "Unable to find property Period with relative value and unit" # Conversione in secondi della frequenza a partire da qualunque unità di misura try: period_quantity = ureg("{} {}".format(period, period_unit)) period_quantity.ito(ureg.second) self.frequency_in_hz = 1.0 / period_quantity.magnitude self.period_in_seconds = period_quantity.magnitude except ValueError: return False, "Unable to convert Period in seconds" # param_freq = Variable( self.associated_class ) # param_freq.setName( "frequency_{}".format(self.name) ) # param_freq.setType( Int( self.associated_class )) # self.associated_class.addParameter( param_freq ) ############# ### TOPIC ### ############# (status, desc) = self.getDefaultTopicName(self.output_port_name, output=True) if status is False: return status, desc ##################### ### PUBLISHER VAR ### ##################### var_publisher_pub = Variable(self.associated_class) var_publisher_pub.setName("pub_{}".format(self.name)) var_publisher_pub.setType(ROS_Publisher(self.associated_class)) self.associated_class.addInternalVariable(var_publisher_pub) ####################### ### PUBLISHER TIMER ### ####################### var_timer_pub = Variable(self.associated_class) var_timer_pub.setName("timer_{}".format(self.name)) var_timer_pub.setType(ROS_Timer(self.associated_class)) self.associated_class.addInternalVariable(var_timer_pub) ########################## ### PUBLISHER CALLBACK ### ########################## self.publisherCallback = Method(self.associated_class) self.publisherCallback.method_name = "{}_callback".format(self.name) self.publisherCallback.return_type = Void(self.associated_class) self.publisherCallback.namespace = self.associated_class.class_name input_par = Variable(self.associated_class) input_par.setIsParameter() input_par.setType(ROS_TimerEvent(self.associated_class)) input_par.setName("") self.publisherCallback.addInputParameter(input_par) # Aggiungo la chiamata alla funzione custome if self.source_text_function: code = "{}.publish({});".format(var_publisher_pub.name, self.source_text_function.generateInlineCode()) self.publisherCallback.addMiddleCode(code) self.associated_class.addPrivateMethod(self.publisherCallback) main_thread.prepare.addMiddleCode("{} = handle.advertise < {} > (\"{}\", 10);" .format(var_publisher_pub.name, self.output_type.generateCode(), self.topic)) main_thread.prepare.addMiddleCode("{} = handle.createTimer(ros::Duration({}), {}, this);" .format(var_timer_pub.name, self.period_in_seconds, self.publisherCallback.getThreadPointer())) return True, ""