From d18cee3922a54f647fda9d473a01a460b4c26dc6 Mon Sep 17 00:00:00 2001 From: Ruichao Wu Date: Thu, 15 Feb 2024 07:49:13 +0100 Subject: [PATCH 1/7] fix template: node without interface --- templates/rossystem.rossystem.j2 | 2 ++ 1 file changed, 2 insertions(+) diff --git a/templates/rossystem.rossystem.j2 b/templates/rossystem.rossystem.j2 index 63ad47e..a339e79 100644 --- a/templates/rossystem.rossystem.j2 +++ b/templates/rossystem.rossystem.j2 @@ -3,6 +3,7 @@ {% for node in model.nodes %} "{{ node.name.full_name }}": from: "TODO.{{ node.name.full_name }}" + {% if node.publisher | length > 0 or node.subscriber | length > 0 or node.actionserver | length > 0 or node.actionclient | length > 0 or node.serviceserver | length > 0 or node.serviceclient | length > 0%} interfaces: {% for interface in node.publisher %} - "{{ interface.name }}": pub-> "TODO::{{ interface.name }}" @@ -22,4 +23,5 @@ {% for interface in node.serviceclient %} - "{{ interface.name }}": sc-> "TODO::{{ interface.name }}" {% endfor %} + {% endif %} {% endfor %} From b71830a47df2406094daab8bfecd0e6f59fee27b Mon Sep 17 00:00:00 2001 From: Ruichao Wu Date: Tue, 27 Feb 2024 19:43:36 +0100 Subject: [PATCH 2/7] update readme --- README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 098cd1d..54ceef5 100644 --- a/README.md +++ b/README.md @@ -25,9 +25,10 @@ For the static code analysis we made available a web interface able to inspect c ``` source /opt/ros/humble/setup.bash ``` -3. Back to the folder "ws", create venv +3. Back to the folder "ws", create and active venv ``` python3 -m venv venv --system-site-packages --symlinks + source venv/bin/activate ``` 4. Install poetry and install dependencies run @@ -37,7 +38,7 @@ For the static code analysis we made available a web interface able to inspect c ``` 5. compile it as ROS package ``` - colcon build --packages-select ros2model --symlink-install + python -m colcon build --packages-select ros2model --symlink-install ``` ## Run From 8a57193dace35ebfbab03a6c855af56b1babef14 Mon Sep 17 00:00:00 2001 From: Ruichao Wu Date: Thu, 15 Feb 2024 08:13:21 +0100 Subject: [PATCH 3/7] move find_process_by_node_name to core.utils --- ros2model/core/utils.py | 13 +++++++++++++ ros2model/verb/runtime_node.py | 20 +++----------------- 2 files changed, 16 insertions(+), 17 deletions(-) create mode 100644 ros2model/core/utils.py diff --git a/ros2model/core/utils.py b/ros2model/core/utils.py new file mode 100644 index 0000000..6c38a4d --- /dev/null +++ b/ros2model/core/utils.py @@ -0,0 +1,13 @@ +import psutil + + +def find_process_by_node_name(node_name, namespace): + for process in psutil.process_iter(["pid", "cmdline"]): + try: + cmdline = process.info["cmdline"] + if namespace in str(cmdline) and node_name in str(cmdline): + cmdline_list = cmdline[0].split("/") + return cmdline_list[-2], cmdline_list[-1] + except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): + pass + return "TODO", "TODO" diff --git a/ros2model/verb/runtime_node.py b/ros2model/verb/runtime_node.py index c947acb..7647b21 100644 --- a/ros2model/verb/runtime_node.py +++ b/ros2model/verb/runtime_node.py @@ -1,5 +1,7 @@ from pathlib import Path +from ros2model.core.utils import find_process_by_node_name + from ros2cli.node.strategy import add_arguments from ros2model.verb import VerbExtension @@ -7,9 +9,6 @@ import ros2model.api.runtime_parser.rosmodel_runtime_parser as RuntimeParser import ros2model.core.metamodels.metamodel_ros as ROSModel -import psutil -import sys - class RuntimeNodeVerb(VerbExtension): """Create .ros2 for each node in a runtime system""" @@ -47,17 +46,6 @@ def name_component_file(self, grapg_name: ROSModel.GraphName): file_name = f"{grapg_name.namespace[1:]}__{n}" return file_name - def find_process_by_node_name(self, node_name, namespace): - for process in psutil.process_iter(["pid", "cmdline"]): - try: - cmdline = process.info["cmdline"] - if namespace in str(cmdline) and node_name in str(cmdline): - cmdline_list = cmdline[0].split("/") - return cmdline_list[-2], cmdline_list[-1] - except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): - pass - return "TODO", "TODO" - def main(self, *, args): output_dir = Path(args.output_folder) if output_dir.is_absolute() != True: @@ -74,9 +62,7 @@ def main(self, *, args): args.include_hidden_nodes, args.include_hidden_interfaces, ) - package_name, artifact_name = self.find_process_by_node_name( - n.name, n.namespace - ) + package_name, artifact_name = find_process_by_node_name(n.name, n.namespace) runtime_pkg = ROSModel.Package( name=package_name, artifact=[ROSModel.Artifact(name=artifact_name, node=[node_instance])], From a5ba6787acab3677a54d1c919ce9f9c681698792 Mon Sep 17 00:00:00 2001 From: Ruichao Wu Date: Tue, 27 Feb 2024 17:42:10 +0100 Subject: [PATCH 4/7] parse parameter --- .../runtime_parser/rosmodel_runtime_parser.py | 24 +++++++---- ros2model/core/utils.py | 41 ++++++++++++++++++- 2 files changed, 56 insertions(+), 9 deletions(-) diff --git a/ros2model/api/runtime_parser/rosmodel_runtime_parser.py b/ros2model/api/runtime_parser/rosmodel_runtime_parser.py index 79227ae..a3d6251 100644 --- a/ros2model/api/runtime_parser/rosmodel_runtime_parser.py +++ b/ros2model/api/runtime_parser/rosmodel_runtime_parser.py @@ -10,11 +10,13 @@ from rclpy.topic_endpoint_info import TopicEndpointInfo import rclpy from rcl_interfaces.msg import ParameterType +import ros2model.core.utils as utils from collections import namedtuple from ros2param.api import ( call_list_parameters, call_describe_parameters, + call_get_parameters, ) Topic_BlackList = ["/parameter_events", "/rosout"] @@ -38,11 +40,11 @@ def get_parameter_type_string(parameter_type): ParameterType.PARAMETER_INTEGER: "Integer", ParameterType.PARAMETER_DOUBLE: "Double", ParameterType.PARAMETER_STRING: "String", - ParameterType.PARAMETER_BYTE_ARRAY: "Array: Byte", - ParameterType.PARAMETER_BOOL_ARRAY: "Array: Boolean", - ParameterType.PARAMETER_INTEGER_ARRAY: "Array: Integer", - ParameterType.PARAMETER_DOUBLE_ARRAY: "Array: Double", - ParameterType.PARAMETER_STRING_ARRAY: "Array: String", + ParameterType.PARAMETER_BYTE_ARRAY: "Array[Byte]", + ParameterType.PARAMETER_BOOL_ARRAY: "Array[Boolean]", + ParameterType.PARAMETER_INTEGER_ARRAY: "Array[Integer]", + ParameterType.PARAMETER_DOUBLE_ARRAY: "Array[Double]", + ParameterType.PARAMETER_STRING_ARRAY: "Array[String]", ParameterType.PARAMETER_NOT_SET: "Any", } return mapping[parameter_type] @@ -275,12 +277,18 @@ def get_parameters(self, node, include_hidden_nodes=False): node_name=self.name.full_name, parameter_names=sorted_names, ) - for descriptor in des_resp.descriptors: + + get_parameters_response = call_get_parameters( + node=node, node_name=self.name.full_name, parameter_names=sorted_names + ) + + for param_name, pvalue in zip(sorted_names, get_parameters_response.values): self.parameter.append( ROSModel.Parameter( - name=descriptor.name, + name=param_name, namespace=self.name.namespace, - type=get_parameter_type_string(descriptor.type), + type=get_parameter_type_string(pvalue.type), + value=str(utils.get_param_value(pvalue)), ) ) diff --git a/ros2model/core/utils.py b/ros2model/core/utils.py index 6c38a4d..10c3148 100644 --- a/ros2model/core/utils.py +++ b/ros2model/core/utils.py @@ -1,4 +1,6 @@ import psutil +from rclpy.parameter import Parameter +from rcl_interfaces.msg import ParameterType def find_process_by_node_name(node_name, namespace): @@ -7,7 +9,44 @@ def find_process_by_node_name(node_name, namespace): cmdline = process.info["cmdline"] if namespace in str(cmdline) and node_name in str(cmdline): cmdline_list = cmdline[0].split("/") - return cmdline_list[-2], cmdline_list[-1] + if len(cmdline_list) >= 3: + return cmdline_list[-2], cmdline_list[-1] except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess): pass return "TODO", "TODO" + + +def get_param_value(pvalue: Parameter): + if pvalue.type == ParameterType.PARAMETER_BOOL: + label = "Boolean value is:" + value = pvalue.bool_value + elif pvalue.type == ParameterType.PARAMETER_INTEGER: + label = "Integer value is:" + value = pvalue.integer_value + elif pvalue.type == ParameterType.PARAMETER_DOUBLE: + label = "Double value is:" + value = pvalue.double_value + elif pvalue.type == ParameterType.PARAMETER_STRING: + label = "String value is:" + value = pvalue.string_value + elif pvalue.type == ParameterType.PARAMETER_BYTE_ARRAY: + label = "Byte values are:" + value = pvalue.byte_array_value + elif pvalue.type == ParameterType.PARAMETER_BOOL_ARRAY: + label = "Boolean values are:" + value = pvalue.bool_array_value + elif pvalue.type == ParameterType.PARAMETER_INTEGER_ARRAY: + label = "Integer values are:" + value = pvalue.integer_array_value.tolist() + elif pvalue.type == ParameterType.PARAMETER_DOUBLE_ARRAY: + label = "Double values are:" + value = pvalue.double_array_value.tolist() + elif pvalue.type == ParameterType.PARAMETER_STRING_ARRAY: + label = "String values are:" + value = pvalue.string_array_value + elif pvalue.type == ParameterType.PARAMETER_NOT_SET: + label = "Parameter not set." + value = None + else: + return f"Unknown parameter type '{pvalue.type}'" + return value From 60a085dd1dc9184aff9411955267a550edc511f6 Mon Sep 17 00:00:00 2001 From: Ruichao Wu Date: Tue, 27 Feb 2024 17:41:32 +0100 Subject: [PATCH 5/7] add parameters in rossystem template --- templates/rossystem.rossystem.j2 | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/templates/rossystem.rossystem.j2 b/templates/rossystem.rossystem.j2 index a339e79..ff3c5da 100644 --- a/templates/rossystem.rossystem.j2 +++ b/templates/rossystem.rossystem.j2 @@ -24,4 +24,17 @@ - "{{ interface.name }}": sc-> "TODO::{{ interface.name }}" {% endfor %} {% endif %} + {% if node.parameter | length > 0 %} + parameters: + {% for parameter in node.parameter %} + - {{ parameter.name }}: "{{ node.name.full_name }}.{{ parameter.name }}" + {% if parameter.type == "String" %} + value: "{{ parameter.value }}" + {% elif parameter.type == "Boolean" %} + value: {{ parameter.value | lower }} + {% else %} + value: {{ parameter.value }} + {% endif %} + {% endfor %} + {% endif %} {% endfor %} From 6d6a87bb4a574fb9bce0f08bfc6e6f78423eb2c5 Mon Sep 17 00:00:00 2001 From: Ruichao Wu Date: Tue, 27 Feb 2024 20:13:57 +0100 Subject: [PATCH 6/7] parse parameter value in component model --- templates/component.ros2.j2 | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/templates/component.ros2.j2 b/templates/component.ros2.j2 index f44ea2a..c77750c 100644 --- a/templates/component.ros2.j2 +++ b/templates/component.ros2.j2 @@ -59,6 +59,15 @@ {% for parameter in node.parameter %} '{{ parameter.name }}': type: {{ parameter.type }} + {% if parameter.value != none %} + {% if parameter.type == "String" %} + value: "{{ parameter.value }}" + {% elif parameter.type == "Boolean" %} + value: {{ parameter.value | lower }} + {% else %} + value: {{ parameter.value }} + {% endif %} + {% endif %} {% endfor %} {% endif %} {% endfor %} From d37210299049ff96a9b5f7d18c71e1c0efc3540a Mon Sep 17 00:00:00 2001 From: Ruichao Wu Date: Tue, 27 Feb 2024 20:03:15 +0100 Subject: [PATCH 7/7] fix path in tests, update result --- test/outputs/test_model.ros2 | 1 + test/outputs/test_system.rossystem | 3 +++ test/unittest/test_generate_component.py | 3 ++- test/unittest/test_generate_interface.py | 2 +- test/unittest/test_generate_system.py | 5 ++++- 5 files changed, 11 insertions(+), 3 deletions(-) diff --git a/test/outputs/test_model.ros2 b/test/outputs/test_model.ros2 index 5e5a284..1d1fd29 100644 --- a/test/outputs/test_model.ros2 +++ b/test/outputs/test_model.ros2 @@ -22,3 +22,4 @@ test_model: parameters: 'shadows/min_angle': type: Double + value: -1.52 diff --git a/test/outputs/test_system.rossystem b/test/outputs/test_system.rossystem index 3d67547..4ad2bf9 100644 --- a/test/outputs/test_system.rossystem +++ b/test/outputs/test_system.rossystem @@ -9,3 +9,6 @@ test_system: - "static_map": ac-> "TODO::static_map" - "static_map": ss-> "TODO::static_map" - "static_map": sc-> "TODO::static_map" + parameters: + - shadows/min_angle: "/map_server.shadows/min_angle" + value: -1.52 diff --git a/test/unittest/test_generate_component.py b/test/unittest/test_generate_component.py index a21d89a..76177da 100644 --- a/test/unittest/test_generate_component.py +++ b/test/unittest/test_generate_component.py @@ -48,7 +48,7 @@ # pprint(test_model) test_dir = "test" -output_folder = "outputs" +output_folder = Path(__file__).parent.parent / "outputs" expect_result = """ test_model: @@ -75,6 +75,7 @@ parameters: 'shadows/min_angle': type: Double + value: -1.52 """ diff --git a/test/unittest/test_generate_interface.py b/test/unittest/test_generate_interface.py index c331c55..11c9aba 100644 --- a/test/unittest/test_generate_interface.py +++ b/test/unittest/test_generate_interface.py @@ -46,7 +46,7 @@ ) test_dir = "test" -output_folder = "outputs" +output_folder = Path(__file__).parent.parent / "outputs" expect_result = """ test_interfaces: diff --git a/test/unittest/test_generate_system.py b/test/unittest/test_generate_system.py index 64dc449..1aec531 100644 --- a/test/unittest/test_generate_system.py +++ b/test/unittest/test_generate_system.py @@ -37,7 +37,7 @@ pprint(test_model) test_dir = "test" -output_folder = "outputs" +output_folder = Path(__file__).parent.parent / "outputs" expect_result = """ test_system: @@ -51,6 +51,9 @@ - "static_map": ac-> "TODO::static_map" - "static_map": ss-> "TODO::static_map" - "static_map": sc-> "TODO::static_map" + parameters: + - shadows/min_angle: "/map_server.shadows/min_angle" + value: -1.52 """