diff --git a/README.md b/README.md index ec4965b..c594bf4 100644 --- a/README.md +++ b/README.md @@ -2,11 +2,6 @@ [![License](https://img.shields.io/badge/License-BSD%203--Clause-blue.svg)](https://opensource.org/licenses/BSD-3-Clause) -[![MELODIC build status](https://github.com/ipa320/ros-model-extractors/actions/workflows/build_melodic.yml/badge.svg)](https://github.com/ipa320/ros-model-extractors/actions/workflows/build_melodic.yml) -[![NOETIC ros-model-extractors](https://github.com/ipa320/ros-model-extractors/actions/workflows/build_noetic.yml/badge.svg)](https://github.com/ipa320/ros-model-extractors/actions/workflows/build_noetic.yml) -[![FOXY build status](https://github.com/ipa320/ros-model-extractors/actions/workflows/build_foxy.yml/badge.svg)](https://github.com/ipa320/ros-model-extractors/actions/workflows/build_foxy.yml) - - Technical Maintainer: [**ipa-nhg**](https://github.com/ipa-nhg/) (**Nadia Hammoudeh Garcia**, **Fraunhofer IPA**) - **nadia.hammoudeh.garcia@ipa.fraunhofer.de** This repository contains the HAROS framework plugin to automatically generate models according to the DSLs defined for RosModel. @@ -17,6 +12,7 @@ This package also contains a set of ROS containers where you can easily do the a As related work you can read the following paper: [Bootstrapping MDE development from ROS manual code: Part 2—Model generation and leveraging models at runtime](https://link.springer.com/article/10.1007/s10270-021-00873-2?wt_mc=Internal.Event.1.SEM.ArticleAuthorOnlineFirst&utm_source=ArticleAuthorOnlineFirst&utm_medium=email&utm_content=AA_en_06082018&ArticleAuthorOnlineFirst_20210420) +:bangbang: The updated version of the development is only being supported for ROS2, the RO1 implementation (old version) is available under [ros1 branch](https://github.com/ipa320/ros-model-extractors/tree/ros1) ### HowTo Use the docker container to run the ros-model plugin for HAROS @@ -26,7 +22,7 @@ Build the HAROS docker image, for your desired ROS distro version: ``` cd path-to-ros-model-extractors-repo -[sudo] docker build --tag=haros_ROSDISTRO -f ROSDISTRO/Dockerfile . +[sudo] docker build --tag=haros_ROSDISTRO -f docker/ROSDISTRO/Dockerfile . ``` Call the ros-model extractor plugin, remember you have to also clone the repository to be analysed: @@ -48,13 +44,8 @@ Additionally, the analysis offers the option to analyze all the nodes of a packa ``` Please check the available examples for the supported distros: - -- [ROS1 melodic](melodic/README.md) -- [ROS1 noetic](noetic/README.md) -- [ROS2 foxy](foxy/README.md) - [ROS2 humble](humble/README.md) ToDo: - - Extractor of interfaces types (msgs, srvs and actions) - Parser for launch files and analysis of the full system diff --git a/docker/haros_runner.sh b/docker/haros_runner.sh index b0e3834..342fb48 100755 --- a/docker/haros_runner.sh +++ b/docker/haros_runner.sh @@ -132,4 +132,4 @@ echo "###########" done ## Clean and finish -#rm -rf ${5}/src/* +#find ${5}/src -maxdepth 1 -type d ! -iname ros2model -exec rm -rvf {} \; diff --git a/docker/humble/Dockerfile b/docker/humble/Dockerfile index 843108f..defdcce 100644 --- a/docker/humble/Dockerfile +++ b/docker/humble/Dockerfile @@ -41,8 +41,7 @@ RUN usermod -a -G root extractor RUN echo "extractor ALL=(ALL:ALL) NOPASSWD: ALL" | sudo tee /etc/sudoers.d/extractor USER extractor RUN mkdir -p /home/extractor/ws/src -#RUN git clone https://github.com/ipa320/ros2model.git /home/extractor/ws/src/ros2model -RUN git clone https://github.com/ipa-nhg/ros2model.git -b PythonInstallTemplatesFolder /home/extractor/ws/src/ros2model +RUN git clone https://github.com/ipa320/ros2model.git /home/extractor/ws/src/ros2model RUN mkdir -p /home/extractor/results RUN chown extractor:extractor /home/extractor/results @@ -73,7 +72,7 @@ RUN source /opt/ros/$ROS_DISTRO/setup.bash;\ ENV PYTHON_VERSION 3 RUN echo 'source /home/extractor/ws/install/setup.bash' >> /home/extractor/.bashrc -RUN echo "test" +#RUN echo "test" #COPY ${path_to_scripts}messages_generator_runner.sh / #COPY ${path_to_scripts}generate_messages_model_helper.sh / COPY docker/haros_runner.sh / diff --git a/docker/humble/README.md b/docker/humble/README.md index cb10028..aae1a0c 100644 --- a/docker/humble/README.md +++ b/docker/humble/README.md @@ -5,7 +5,7 @@ Install docker https://docs.docker.com/install/linux/docker-ce/ubuntu/ Build the HAROS docker image, for your desired ROS distro version: ``` cd path-to-ros-model-extractors-repo -[sudo] docker build --tag=haros_humble -f humble/Dockerfile . +[sudo] docker build --tag=haros_humble -f docker/humble/Dockerfile . ``` Call the ros-model extractor plugin, remember you have to also clone the repository to be analysed: @@ -21,13 +21,8 @@ For example: [sudo] docker run -it haros_humble:latest /haros_runner.sh turtlesim turtlesim_node node . /home/extractor/ws "https://github.com/ros/ros_tutorials -b humble" -[sudo] docker run -it haros_humble:latest /haros_runner.sh test_pkg test_node node . /home/extractor/ws "https://github.com/ipa-nhg/test_ros2_code_extractor -b ros2" +[sudo] docker run -it haros_humble:latest /haros_runner.sh cpp_basic --all node . /home/extractor/ws "https://github.com/ipa-nhg/cpp_basic_ros2" -[sudo] docker run -it haros_humble:latest /haros_runner.sh depthai_ros_driver --all node . /home/extractor/ws "https://github.com/luxonis/depthai-ros/" https://github.com/ros-perception/vision_msgs "https://github.com/ros-perception/image_common/ -b humble" - -docker run -it haros_humble:latest /haros_runner.sh nav2_controller --all node . /home/extractor/ws https://github.com/ros-planning/navigation2 "https://github.com/ros-geographic-info/geographic_info/ -b ros2 " - -docker run -it haros_humble:latest /haros_runner.sh examples_rclcpp_minimal_publisher --all node . /home/extractor/ws https://github.com/ros2/examples/ ``` diff --git a/ros_code_analysis/ros_code_analysis/ros1_python_extractor.py b/ros_code_analysis/ros_code_analysis/ros1_python_extractor.py new file mode 100644 index 0000000..cd93979 --- /dev/null +++ b/ros_code_analysis/ros_code_analysis/ros1_python_extractor.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python +# +# Copyright 2024 Fraunhofer Institute for Manufacturing Engineering and Automation (IPA) +# +# Nadia Hammoudeh Garcia +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from bonsai.analysis import CodeQuery, resolve_expression +from ros_common_extractor import RosCommonExtractor +import ros2model.core.metamodels.metamodel_ros as RosModelMetamodel + +class Ros1PythonExtractor(): + def extract_primitives(node, parser, analysis): + gs = parser.global_scope + node.source_tree = parser.global_scope + publishers=[] + subscribers=[] + serviceservers=[] + serviceclients=[] + actionservers=[] + actionclients=[] + parameters=[] + + msgs_list = [] + pkgs_list = [] + for imp_name in parser.imported_names_list: + s = str(imp_name) + if "msg" in s or "srv" in s: + ss = s.split(".") + if len(ss) < 2: + continue + if ss[-1] == "msg" or ss[-1] == "srv": + pkgs_list.append(ss[0]) + elif ss[1] == "msg" or ss[1] == "srv": + msgs_list.append((ss[0], ss[2])) + else: + log.debug(("Python import with 'msg' or 'srv', " + "but unable to process it: ") + + s) + for call in CodeQuery(gs).all_calls.get(): + if "rospy.Publisher" in str(call): + if len(call.arguments) > 1: + name = analysis._extract_topic(call, topic_pos=0) + msg_type = analysis._extract_message_type(call, '', msgs_list, pkgs_list) + pub = RosModelMetamodel.Publisher(name=name,type=msg_type.replace(".msg","")) + publishers.append(pub) + if "rospy.Subscriber" in str(call): + if len(call.arguments) > 1: + name = analysis._extract_topic(call, topic_pos=0) + msg_type = analysis._extract_message_type(call, '', msgs_list, pkgs_list) + sub = RosModelMetamodel.Subscriber(name=name,type=msg_type.replace(".msg","")) + subscribers.append(sub) + if "rospy.Service" in str(call): + if len(call.arguments) > 1: + name = analysis._extract_topic(call, topic_pos=0) + srv_type = analysis._extract_message_type(call, '', msgs_list) + ss = RosModelMetamodel.ServiceServer(name=name,type=srv_type.replace(".srv","").replace("Request","")) + serviceservers.append(ss) + if "rospy.ServiceProxy" in str(call): + if len(call.arguments) > 1: + name = analysis._extract_topic(call, topic_pos=0) + srv_type = analysis._extract_message_type(call, '', msgs_list) + sc = RosModelMetamodel.ServiceClient(name=name,type=srv_type.replace(".srv","").replace("Response","")) + serviceclients.append(sc) + if "rospy.set_param" in str(call): + param_name = analysis._extract_topic(call, topic_pos=0) + param_type = default_value = None + default_value = resolve_expression(call.arguments[1]) + param = RosModelMetamodel.Parameter(name=param_name,type=param_type,value=default_value) + parameters.append(param) \ No newline at end of file diff --git a/ros_code_analysis/ros_code_analysis/ros_model_extractor.py b/ros_code_analysis/ros_code_analysis/ros_model_extractor.py index dab4828..6d83f89 100755 --- a/ros_code_analysis/ros_code_analysis/ros_model_extractor.py +++ b/ros_code_analysis/ros_code_analysis/ros_model_extractor.py @@ -32,6 +32,7 @@ from bonsai.analysis import CodeQuery, resolve_expression from ros2_cpp_extractor import Ros2CppExtractor from ros1_cpp_extractor import Ros1CppExtractor +from ros1_python_extractor import Ros1PythonExtractor try: @@ -123,7 +124,10 @@ def extract_node(self, name, node_name, pkg_name, ns, ws, rossystem): actionservers, actionclients, parameters] = Ros1CppExtractor.extract_primitives(node, parser, analysis) elif (node.language=="python"): - print("ROS1 python") + [publishers, subscribers, + serviceservers, serviceclients, + actionservers, actionclients, + parameters] = Ros1PythonExtractor.extract_primitives(node, parser, analysis) elif os.environ.get("ROS_VERSION") == "2": if (node.language=="cpp"): [publishers, subscribers, @@ -131,7 +135,7 @@ def extract_node(self, name, node_name, pkg_name, ns, ws, rossystem): actionservers, actionclients, parameters] = Ros2CppExtractor.extract_primitives(node, parser, analysis) elif (node.language=="python"): - print("ROS2 python") + print("ROS2 python...tbd...") RosModel_node=RosModelMetamodel.Node(name=graph_name, publisher=publishers, subscriber=subscribers, serviceserver=serviceservers, serviceclient=serviceclients, @@ -144,7 +148,6 @@ def extract_node(self, name, node_name, pkg_name, ns, ws, rossystem): RosModel_node=RosModelMetamodel.Node(name=graph_name) RosModel_artifact=RosModelMetamodel.Artifact(name=node_name, node=[RosModel_node]) RosModel_package=RosModelMetamodel.Package(name=self.args.package_name, artifact=[RosModel_artifact]) - print(RosModel_package) #Model file generator node_generator = ComponentGenerator() node_generator.generate_a_file(