diff --git a/README.md b/README.md index 424186f8..c3d36f36 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ ## TL;DR Scenario execution is a backend- and middleware-agnostic library written in Python based on the generic scenario description language [OpenSCENARIO 2](https://www.asam.net/static_downloads/public/asam-openscenario/2.0.0/welcome.html) and [pytrees](https://py-trees.readthedocs.io/en/devel/). -It reads a scenario definition from a file and then executes it, reusing available checks and behaviors. It is easily extendable through a library mechanism. +It reads a scenario definition from a file and then executes it, reusing available checks and actions. It is easily extendable through a library mechanism. This separation of the scenario definition from implementation massively reduces the manual efforts of scenario creation. To give an impression of the functionality of scenario execution, the following animation shows an example scenario with a turtlebot-like robot in simulation using Nav2 to navigate towards a specified navigation goal in a simulated warehouse environment. diff --git a/examples/example_simulation/README.md b/examples/example_simulation/README.md index dabc1e48..b2fd2015 100644 --- a/examples/example_simulation/README.md +++ b/examples/example_simulation/README.md @@ -1,6 +1,6 @@ # Example Simulation Navigation -To run the Example Simulation Navigation with scenario, first build the Example Simulation Navigation Package: +To run the Example Simulation Navigation with scenario, first build the `example_simulation` package: ```bash colcon build --packages-up-to example_simulation @@ -18,6 +18,6 @@ Now, run the following command to launch the scenario: ros2 launch tb4_sim_scenario sim_nav_scenario_launch.py scenario:=examples/example_simulation/scenarios/example_simulation.osc ``` -A turtlebot is initialsed with nav2 which drives to a point and back. During the ride an object is spawned in front of the turtlebot which will then drive around the object. +A turtlebot is initialised with nav2 which drives to a goal and back. During the ride an obstacle is spawned in front of the turtlebot which will then drive around the object. For a more detailed understanding of the code structure and scenario implementation please refer to the [tutorial documentation](https://intellabs.github.io/scenario_execution/tutorials.html). diff --git a/examples/example_simulation/package.xml b/examples/example_simulation/package.xml index 188464fb..abd93489 100644 --- a/examples/example_simulation/package.xml +++ b/examples/example_simulation/package.xml @@ -3,18 +3,15 @@ example_simulation 1.0.0 - Simulation example for scenario execution + Scenario Execution Example for Simulation Intel Labs Intel Labs Apache-2.0 scenario_execution - scenario_execution_rviz scenario_execution_gazebo gazebo_tf_publisher tb4_sim_scenario - navigation2 - nav2_bringup rclpy diff --git a/scenario_coverage/scenario_coverage/scenario_batch_execution.py b/scenario_coverage/scenario_coverage/scenario_batch_execution.py index 2af86b89..d687be80 100644 --- a/scenario_coverage/scenario_coverage/scenario_batch_execution.py +++ b/scenario_coverage/scenario_coverage/scenario_batch_execution.py @@ -82,11 +82,11 @@ def log_output(out, buffer): process = subprocess.Popen(launch_command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, stdin=subprocess.PIPE) log_stdout_thread = Thread(target=log_output, args=(process.stdout, output, )) - log_stdout_thread.daemon = True # die with the program TODO: remove + log_stdout_thread.daemon = True # die with the program log_stdout_thread.start() log_stderr_thread = Thread(target=log_output, args=(process.stderr, output, )) - log_stderr_thread.daemon = True # die with the program TODO: remove + log_stderr_thread.daemon = True # die with the program log_stderr_thread.start() print(f"### Waiting for process to finish...") diff --git a/scenario_execution/package.xml b/scenario_execution/package.xml index b0664f14..df600db1 100644 --- a/scenario_execution/package.xml +++ b/scenario_execution/package.xml @@ -3,7 +3,7 @@ scenario_execution 1.0.0 - Package for ROS scenario execution + ROS Scenario Execution Intel Labs Intel Labs Apache-2.0 diff --git a/scenario_execution/scenario_execution/__init__.py b/scenario_execution/scenario_execution/__init__.py index 3c1ff17b..c48c9ebf 100644 --- a/scenario_execution/scenario_execution/__init__.py +++ b/scenario_execution/scenario_execution/__init__.py @@ -16,14 +16,12 @@ """ Main entry for scenario execution """ -from . import action_plugins -from . import external_methods +from . import actions from .logging_ros import RosLogger from .scenario_execution_ros import ROSScenarioExecution __all__ = [ - 'action_plugins', - 'external_methods', + 'actions', 'RosLogger', 'ROSScenarioExecution' ] diff --git a/scenario_execution/scenario_execution/action_plugins/ros_event_to_blackboard.py b/scenario_execution/scenario_execution/action_plugins/ros_event_to_blackboard.py deleted file mode 100644 index 775fa1c8..00000000 --- a/scenario_execution/scenario_execution/action_plugins/ros_event_to_blackboard.py +++ /dev/null @@ -1,43 +0,0 @@ -# Copyright (C) 2024 Intel Corporation -# -# 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. -# -# SPDX-License-Identifier: Apache-2.0 - -""" Scenario execution plugin to write data to the blackboard on reception of empty ros message """ -import py_trees_ros # pylint: disable=import-error -from scenario_execution.action_plugins.conversions import get_qos_preset_profile - - -class RosEventToBlackboard(py_trees_ros.subscribers.EventToBlackboard): - """ - Class to receive a empty ros message and publish to blackboard - - Args: - topic_name: name of the topic to connect to - qos_profile: qos profile for the subscriber - variable_name: name to write the boolean result on the blackboard - """ - - def __init__(self, - name: str, - topic_name: str, - qos_profile: str, - variable_name: str, - ): - - super().__init__( - name=name, - topic_name=topic_name, - qos_profile=get_qos_preset_profile(qos_profile), - variable_name=variable_name) diff --git a/scenario_execution/scenario_execution/action_plugins/ros_topic_from_blackboard.py b/scenario_execution/scenario_execution/action_plugins/ros_topic_from_blackboard.py deleted file mode 100644 index d6652770..00000000 --- a/scenario_execution/scenario_execution/action_plugins/ros_topic_from_blackboard.py +++ /dev/null @@ -1,52 +0,0 @@ -# Copyright (C) 2024 Intel Corporation -# -# 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. -# -# SPDX-License-Identifier: Apache-2.0 - -""" Scenario execution plugin to put ros data into the blackboard """ -import importlib -import py_trees_ros # pylint: disable=import-error -from scenario_execution.action_plugins.conversions import get_qos_preset_profile - - -class RosTopicFromBlackboard(py_trees_ros.publishers.FromBlackboard): - """ - Class to publish a blackboard variable as ros message - - Args: - - topic_name: name of the topic to connect to - topic_type: class of the message type (e.g. std_msgs.msg.String) - qos_profile: qos profile for the publisher - blackboard_variable: name of the variable on the blackboard (can be nested) - """ - - def __init__(self, - name: str, - topic_name: str, - topic_type: str, - qos_profile: str, - blackboard_variable: str - ): - datatype_in_list = topic_type.split(".") - topic_type = getattr( - importlib.import_module(".".join(datatype_in_list[0:-1])), - datatype_in_list[-1] - ) - super().__init__( - name=name, - topic_name=topic_name, - topic_type=topic_type, - qos_profile=get_qos_preset_profile(qos_profile), - blackboard_variable=blackboard_variable) diff --git a/scenario_execution/scenario_execution/action_plugins/ros_topic_to_blackboard.py b/scenario_execution/scenario_execution/action_plugins/ros_topic_to_blackboard.py deleted file mode 100644 index fcaf973d..00000000 --- a/scenario_execution/scenario_execution/action_plugins/ros_topic_to_blackboard.py +++ /dev/null @@ -1,67 +0,0 @@ -# Copyright (C) 2024 Intel Corporation -# -# 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. -# -# SPDX-License-Identifier: Apache-2.0 - -""" Scenario execution plugin to put ros data into the blackboard """ -from ast import literal_eval -import importlib -import py_trees_ros # pylint: disable=import-error -from scenario_execution.action_plugins.conversions import get_qos_preset_profile, get_clearing_policy - - -class RosTopicToBlackboard(py_trees_ros.subscribers.ToBlackboard): - """ - Class to receive a ros message and save it in the blacbboard - - Args: - topic_name: name of the topic to connect to - topic_type: class of the message type (e.g. :obj:`std_msgs.msg.String`) - qos_profile: qos profile for the subscriber - blackboard_variables: blackboard variable string or dict {names (keys) - - message subfields (values)}, use a value of None to indicate the entire message - initialise_variables: initialise the blackboard variables to some defaults - clearing_policy: when to clear the data - """ - - def __init__(self, - name: str, - topic_name: str, - topic_type: str, - qos_profile: str, - blackboard_variables: str, - initialise_variables: str, - clearing_policy: str - ): - datatype_in_list = topic_type.split(".") - topic_type = getattr(importlib.import_module(".".join(datatype_in_list[0:-1])), datatype_in_list[-1]) - try: - if blackboard_variables.startswith('{'): - blackboard_variables = literal_eval(blackboard_variables.encode('utf-8').decode('unicode_escape')) - except Exception as e: - raise ValueError(f"Error while parsing blackboard variables '{blackboard_variables}'") from e - - try: - initialise_variables = literal_eval(initialise_variables.encode('utf-8').decode('unicode_escape')) - except Exception as e: - raise ValueError(f"Error while parsing initialise variables '{initialise_variables}'") from e - - super().__init__( - name=name, - topic_name=topic_name, - topic_type=topic_type, - qos_profile=get_qos_preset_profile(qos_profile), - blackboard_variables=blackboard_variables, - initialise_variables=initialise_variables, - clearing_policy=get_clearing_policy(clearing_policy)) diff --git a/scenario_execution/scenario_execution/action_plugins/__init__.py b/scenario_execution/scenario_execution/actions/__init__.py similarity index 100% rename from scenario_execution/scenario_execution/action_plugins/__init__.py rename to scenario_execution/scenario_execution/actions/__init__.py diff --git a/scenario_execution/scenario_execution/action_plugins/conversions.py b/scenario_execution/scenario_execution/actions/conversions.py similarity index 100% rename from scenario_execution/scenario_execution/action_plugins/conversions.py rename to scenario_execution/scenario_execution/actions/conversions.py diff --git a/scenario_execution/scenario_execution/action_plugins/init_nav2.py b/scenario_execution/scenario_execution/actions/init_nav2.py similarity index 96% rename from scenario_execution/scenario_execution/action_plugins/init_nav2.py rename to scenario_execution/scenario_execution/actions/init_nav2.py index 766e32f9..72f3d54e 100644 --- a/scenario_execution/scenario_execution/action_plugins/init_nav2.py +++ b/scenario_execution/scenario_execution/actions/init_nav2.py @@ -36,7 +36,7 @@ class InitNav2State(Enum): """ - States for executing a nav-to-pose with nav2 + States for executing a initialization of nav2 """ IDLE = 1 LOCALIZER_STATE_REQUESTED = 2 @@ -54,14 +54,7 @@ class InitNav2State(Enum): class InitNav2(py_trees.behaviour.Behaviour): """ - Class to navigate to a pose - - Args: - initial_pose: a 6 numbers list in str form containing the initial pose of the entity - in the shape of [x, y, z. roll, pitch, yaw]. - goal_pose: a 6 numbers list in str form containing the goal pose of the entity - in the shape of [x, y, z. roll, pitch, yaw]. - + Class to initialize nav2 """ def __init__(self, name, associated_actor, initial_pose: list, base_frame_id: str, wait_for_initial_pose: bool, use_initial_pose: bool, namespace_override: str): diff --git a/scenario_execution/scenario_execution/action_plugins/nav2_common.py b/scenario_execution/scenario_execution/actions/nav2_common.py similarity index 100% rename from scenario_execution/scenario_execution/action_plugins/nav2_common.py rename to scenario_execution/scenario_execution/actions/nav2_common.py diff --git a/scenario_execution/scenario_execution/action_plugins/nav_through_poses.py b/scenario_execution/scenario_execution/actions/nav_through_poses.py similarity index 97% rename from scenario_execution/scenario_execution/action_plugins/nav_through_poses.py rename to scenario_execution/scenario_execution/actions/nav_through_poses.py index 3ff56d92..e64110af 100644 --- a/scenario_execution/scenario_execution/action_plugins/nav_through_poses.py +++ b/scenario_execution/scenario_execution/actions/nav_through_poses.py @@ -41,11 +41,6 @@ class NavThroughPosesState(Enum): class NavThroughPoses(py_trees.behaviour.Behaviour): """ Class to navigate through poses - - Args: - goal_pose: a 6 numbers list in str form containing the goal pose of the entity - in the shape of [x, y, z. roll, pitch, yaw]. - """ def __init__(self, name: str, associated_actor, goal_poses: list, monitor_progress: bool, namespace_override: str): diff --git a/scenario_execution/scenario_execution/action_plugins/nav_to_pose.py b/scenario_execution/scenario_execution/actions/nav_to_pose.py similarity index 97% rename from scenario_execution/scenario_execution/action_plugins/nav_to_pose.py rename to scenario_execution/scenario_execution/actions/nav_to_pose.py index 5c7b207d..c56151f6 100644 --- a/scenario_execution/scenario_execution/action_plugins/nav_to_pose.py +++ b/scenario_execution/scenario_execution/actions/nav_to_pose.py @@ -44,11 +44,6 @@ class NavToPoseState(Enum): class NavToPose(py_trees.behaviour.Behaviour): """ Class to navigate to a pose - - Args: - goal_pose: a 6 numbers list in str form containing the goal pose of the entity - in the shape of [x, y, z. roll, pitch, yaw]. - """ def __init__(self, name: str, associated_actor, goal_pose: list, monitor_progress: bool, action_topic: str, namespace_override: str) -> None: diff --git a/scenario_execution/scenario_execution/action_plugins/odometry_distance_traveled.py b/scenario_execution/scenario_execution/actions/odometry_distance_traveled.py similarity index 97% rename from scenario_execution/scenario_execution/action_plugins/odometry_distance_traveled.py rename to scenario_execution/scenario_execution/actions/odometry_distance_traveled.py index d12c427e..ec900150 100644 --- a/scenario_execution/scenario_execution/action_plugins/odometry_distance_traveled.py +++ b/scenario_execution/scenario_execution/actions/odometry_distance_traveled.py @@ -26,9 +26,6 @@ class OdometryDistanceTraveled(py_trees.behaviour.Behaviour): """ Class to wait for a certain covered distance, based on odometry - Args: - distance [float]: expected distance traveled - namespace [str]: namspace of odom topic """ def __init__(self, name, associated_actor, distance: float): diff --git a/scenario_execution/scenario_execution/action_plugins/py_trees_ros_common.py b/scenario_execution/scenario_execution/actions/py_trees_ros_common.py similarity index 100% rename from scenario_execution/scenario_execution/action_plugins/py_trees_ros_common.py rename to scenario_execution/scenario_execution/actions/py_trees_ros_common.py diff --git a/scenario_execution/scenario_execution/action_plugins/ros_bag_record.py b/scenario_execution/scenario_execution/actions/ros_bag_record.py similarity index 94% rename from scenario_execution/scenario_execution/action_plugins/ros_bag_record.py rename to scenario_execution/scenario_execution/actions/ros_bag_record.py index 06729ef6..3cda1771 100644 --- a/scenario_execution/scenario_execution/action_plugins/ros_bag_record.py +++ b/scenario_execution/scenario_execution/actions/ros_bag_record.py @@ -21,7 +21,7 @@ from enum import Enum import py_trees -from scenario_execution_base.behaviors import RunExternalProcess +from scenario_execution_base.actions import RunExternalProcess import shutil import signal @@ -38,13 +38,6 @@ class RosBagRecordActionState(Enum): class RosBagRecord(RunExternalProcess): """ Class to execute ros bag recording - - Args: - destination_dir [str]: destination directory - topics [str]: topics to record - timestamp_suffix [bool]: add timestamp suffix to output dir? - hidden_topics [bool]: whether to record hidden topics - """ def __init__(self, name, destination_dir: str, topics: list, timestamp_suffix: bool, hidden_topics: bool, storage: str): diff --git a/scenario_execution/scenario_execution/action_plugins/ros_log_check.py b/scenario_execution/scenario_execution/actions/ros_log_check.py similarity index 100% rename from scenario_execution/scenario_execution/action_plugins/ros_log_check.py rename to scenario_execution/scenario_execution/actions/ros_log_check.py diff --git a/scenario_execution/scenario_execution/action_plugins/ros_service_call.py b/scenario_execution/scenario_execution/actions/ros_service_call.py similarity index 100% rename from scenario_execution/scenario_execution/action_plugins/ros_service_call.py rename to scenario_execution/scenario_execution/actions/ros_service_call.py diff --git a/scenario_execution/scenario_execution/action_plugins/ros_set_node_parameter.py b/scenario_execution/scenario_execution/actions/ros_set_node_parameter.py similarity index 100% rename from scenario_execution/scenario_execution/action_plugins/ros_set_node_parameter.py rename to scenario_execution/scenario_execution/actions/ros_set_node_parameter.py diff --git a/scenario_execution/scenario_execution/action_plugins/ros_topic_check_data.py b/scenario_execution/scenario_execution/actions/ros_topic_check_data.py similarity index 100% rename from scenario_execution/scenario_execution/action_plugins/ros_topic_check_data.py rename to scenario_execution/scenario_execution/actions/ros_topic_check_data.py diff --git a/scenario_execution/scenario_execution/action_plugins/ros_topic_publish.py b/scenario_execution/scenario_execution/actions/ros_topic_publish.py similarity index 100% rename from scenario_execution/scenario_execution/action_plugins/ros_topic_publish.py rename to scenario_execution/scenario_execution/actions/ros_topic_publish.py diff --git a/scenario_execution/scenario_execution/action_plugins/ros_topic_wait_for_data.py b/scenario_execution/scenario_execution/actions/ros_topic_wait_for_data.py similarity index 100% rename from scenario_execution/scenario_execution/action_plugins/ros_topic_wait_for_data.py rename to scenario_execution/scenario_execution/actions/ros_topic_wait_for_data.py diff --git a/scenario_execution/scenario_execution/action_plugins/ros_topic_wait_for_topics.py b/scenario_execution/scenario_execution/actions/ros_topic_wait_for_topics.py similarity index 100% rename from scenario_execution/scenario_execution/action_plugins/ros_topic_wait_for_topics.py rename to scenario_execution/scenario_execution/actions/ros_topic_wait_for_topics.py diff --git a/scenario_execution/scenario_execution/action_plugins/tf_close_to.py b/scenario_execution/scenario_execution/actions/tf_close_to.py similarity index 100% rename from scenario_execution/scenario_execution/action_plugins/tf_close_to.py rename to scenario_execution/scenario_execution/actions/tf_close_to.py diff --git a/scenario_execution/scenario_execution/external_methods/__init__.py b/scenario_execution/scenario_execution/external_methods/__init__.py deleted file mode 100644 index 17c270db..00000000 --- a/scenario_execution/scenario_execution/external_methods/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -# Copyright (C) 2024 Intel Corporation -# -# 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. -# -# SPDX-License-Identifier: Apache-2.0 - -""" Module for external methods """ -from .power import power -from .string_concat import string_concat diff --git a/scenario_execution/scenario_execution/external_methods/power.py b/scenario_execution/scenario_execution/external_methods/power.py deleted file mode 100644 index 6fe737f6..00000000 --- a/scenario_execution/scenario_execution/external_methods/power.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (C) 2024 Intel Corporation -# -# 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. -# -# SPDX-License-Identifier: Apache-2.0 - -""" Scenario execution external method to calculate the power of a value """ - - -def power(exponent: float, value: float): - """ - Function to calculate value to the power of exponent, i.e., "value**exponent". - - Args: - exponent [float]: exponent - value [float]: base - """ - return pow(value, exponent) diff --git a/scenario_execution/scenario_execution/external_methods/string_concat.py b/scenario_execution/scenario_execution/external_methods/string_concat.py deleted file mode 100644 index f8dd232d..00000000 --- a/scenario_execution/scenario_execution/external_methods/string_concat.py +++ /dev/null @@ -1,28 +0,0 @@ -# Copyright (C) 2024 Intel Corporation -# -# 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. -# -# SPDX-License-Identifier: Apache-2.0 - -""" Scenario execution external methods for string concatenation """ - - -def string_concat(string_1: str, string_2: str): - """ - Function to concatenate two strings - - Args: - string_1 [str]: first string - string_2 [str]: second string - """ - return string_1 + string_2 diff --git a/scenario_execution/setup.py b/scenario_execution/setup.py index 75fe0ef2..889e9016 100644 --- a/scenario_execution/setup.py +++ b/scenario_execution/setup.py @@ -51,9 +51,6 @@ 'differential_drive_robot.nav_through_poses = scenario_execution.action_plugins.nav_through_poses:NavThroughPoses', 'wait_for_data = scenario_execution.action_plugins.ros_topic_wait_for_data:RosTopicWaitForData', 'check_data = scenario_execution.action_plugins.ros_topic_check_data:RosTopicCheckData', - 'event_to_blackboard = scenario_execution.action_plugins.ros_event_to_blackboard:RosEventToBlackboard', - 'topic_to_blackboard = scenario_execution.action_plugins.ros_topic_to_blackboard:RosTopicToBlackboard', - 'topic_from_blackboard = scenario_execution.action_plugins.ros_topic_from_blackboard:RosTopicFromBlackboard', 'service_call = scenario_execution.action_plugins.ros_service_call:RosServiceCall', 'topic_publish = scenario_execution.action_plugins.ros_topic_publish:RosTopicPublish', 'odometry_distance_traveled = ' diff --git a/scenario_execution_base/scenario_execution_base/__init__.py b/scenario_execution_base/scenario_execution_base/__init__.py index e2d93114..b3fd9469 100644 --- a/scenario_execution_base/scenario_execution_base/__init__.py +++ b/scenario_execution_base/scenario_execution_base/__init__.py @@ -16,14 +16,14 @@ """Main entry point for the scenario execution base package """ -from . import behaviors +from . import actions from . import utils from . import model from scenario_execution_base.scenario_execution import ScenarioExecution from scenario_execution_base.utils.logging import BaseLogger, Logger __all__ = [ - 'behaviors', + 'actions', 'utils', 'model', 'BaseLogger', diff --git a/scenario_execution_base/scenario_execution_base/behaviors/__init__.py b/scenario_execution_base/scenario_execution_base/actions/__init__.py similarity index 100% rename from scenario_execution_base/scenario_execution_base/behaviors/__init__.py rename to scenario_execution_base/scenario_execution_base/actions/__init__.py diff --git a/scenario_execution_base/scenario_execution_base/behaviors/bool_expression.py b/scenario_execution_base/scenario_execution_base/actions/bool_expression.py similarity index 100% rename from scenario_execution_base/scenario_execution_base/behaviors/bool_expression.py rename to scenario_execution_base/scenario_execution_base/actions/bool_expression.py diff --git a/scenario_execution_base/scenario_execution_base/behaviors/log.py b/scenario_execution_base/scenario_execution_base/actions/log.py similarity index 100% rename from scenario_execution_base/scenario_execution_base/behaviors/log.py rename to scenario_execution_base/scenario_execution_base/actions/log.py diff --git a/scenario_execution_base/scenario_execution_base/behaviors/open_port.py b/scenario_execution_base/scenario_execution_base/actions/open_port.py similarity index 100% rename from scenario_execution_base/scenario_execution_base/behaviors/open_port.py rename to scenario_execution_base/scenario_execution_base/actions/open_port.py diff --git a/scenario_execution_base/scenario_execution_base/behaviors/run_external_process.py b/scenario_execution_base/scenario_execution_base/actions/run_external_process.py similarity index 100% rename from scenario_execution_base/scenario_execution_base/behaviors/run_external_process.py rename to scenario_execution_base/scenario_execution_base/actions/run_external_process.py diff --git a/scenario_execution_base/scenario_execution_base/behaviors/topic_equals.py b/scenario_execution_base/scenario_execution_base/actions/topic_equals.py similarity index 100% rename from scenario_execution_base/scenario_execution_base/behaviors/topic_equals.py rename to scenario_execution_base/scenario_execution_base/actions/topic_equals.py diff --git a/scenario_execution_base/scenario_execution_base/behaviors/topic_publish.py b/scenario_execution_base/scenario_execution_base/actions/topic_publish.py similarity index 100% rename from scenario_execution_base/scenario_execution_base/behaviors/topic_publish.py rename to scenario_execution_base/scenario_execution_base/actions/topic_publish.py diff --git a/scenario_execution_base/scenario_execution_base/behaviors/wait_for_open_ports.py b/scenario_execution_base/scenario_execution_base/actions/wait_for_open_ports.py similarity index 100% rename from scenario_execution_base/scenario_execution_base/behaviors/wait_for_open_ports.py rename to scenario_execution_base/scenario_execution_base/actions/wait_for_open_ports.py diff --git a/scenario_execution_base/scenario_execution_base/model/model_to_py_tree.py b/scenario_execution_base/scenario_execution_base/model/model_to_py_tree.py index d3768376..7d0d6dd8 100644 --- a/scenario_execution_base/scenario_execution_base/model/model_to_py_tree.py +++ b/scenario_execution_base/scenario_execution_base/model/model_to_py_tree.py @@ -15,15 +15,13 @@ # SPDX-License-Identifier: Apache-2.0 import py_trees -from py_trees.common import Access +from py_trees.common import Access, Status from pkg_resources import iter_entry_points import inspect from scenario_execution_base.model.types import EventReference, CoverDeclaration, ScenarioDeclaration, DoMember, WaitDirective, EmitDirective, BehaviorInvocation, EventCondition, EventDeclaration, RelationExpression, LogicalExpression, ElapsedExpression, PhysicalLiteral from scenario_execution_base.model.model_base_visitor import ModelBaseVisitor -from scenario_execution_base.behaviors import TopicPublish -from scenario_execution_base.behaviors import TopicEquals from scenario_execution_base.model.error import OSC2ParsingError @@ -41,6 +39,72 @@ def create_py_tree(model, logger): return [scenario] +class TopicEquals(py_trees.behaviour.Behaviour): + """ + Class to listen to a topic in Blackboard and check if it equals the defined message + + Args: + key [str]: topic to listen to + msg [str]: target message to match + namespace [str]: namespace of the key + """ + + def __init__(self, key: str, msg: str, namespace: str = None): + super().__init__(self.__class__.__name__) + + self.namespace = namespace + self.key = key + self.msg = msg + + self.client = self.attach_blackboard_client(namespace=self.namespace) + self.client.register_key(self.key, access=Access.READ) + + def update(self): + """ + Check the message on the topic equals the target message + """ + msg_on_blackboard = self.client.get(self.key) + if msg_on_blackboard == self.msg: + return Status.SUCCESS + return Status.RUNNING + + +class TopicPublish(py_trees.behaviour.Behaviour): + """ + Class to publish a message to a topic + + Args: + key [str]: topic to publish on + msg [str]: message to publish on that topic + namespace [str]: namespace of the key + """ + + def __init__(self, name: "TopicPublish", key: str, msg: str, namespace: str = None): + super().__init__(name) + + self.namespace = namespace + self.key = key + self.msg = msg + + self.client = self.attach_blackboard_client(namespace=self.namespace) + self.client.register_key(self.key, access=Access.WRITE) + + def setup(self, **kwargs): + """ + Setup empty topic on blackboard + + This is to prevent the "Reader" from reading before the topic exists. + """ + self.client.set(self.key, '') + + def update(self): + """ + publish the message to topic + """ + self.client.set(self.key, self.msg) + return Status.SUCCESS + + class ModelToPyTree(object): def __init__(self, logger): diff --git a/scenario_execution_base/setup.py b/scenario_execution_base/setup.py index 42d31302..a26789b3 100644 --- a/scenario_execution_base/setup.py +++ b/scenario_execution_base/setup.py @@ -44,10 +44,10 @@ 'scenario_execution_base = scenario_execution_base.scenario_execution:main', ], 'scenario_execution.action_plugins': [ - 'log = scenario_execution_base.behaviors.log:Log', - 'run_external_process = scenario_execution_base.behaviors.run_external_process:RunExternalProcess', - 'open_port = scenario_execution_base.behaviors.open_port:OpenPort', - 'wait_for_open_ports = scenario_execution_base.behaviors.wait_for_open_ports:WaitForOpenPorts', + 'log = scenario_execution_base.actions.log:Log', + 'run_external_process = scenario_execution_base.actions.run_external_process:RunExternalProcess', + 'open_port = scenario_execution_base.actions.open_port:OpenPort', + 'wait_for_open_ports = scenario_execution_base.actions.wait_for_open_ports:WaitForOpenPorts', ], 'scenario_execution.osc_libraries': [ 'helpers = scenario_execution_base.get_osc_library:get_helpers_library', diff --git a/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_actor_exists.py b/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_actor_exists.py index 6532c120..ab162f12 100644 --- a/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_actor_exists.py +++ b/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_actor_exists.py @@ -16,7 +16,7 @@ """ Class to check the existance of an actor in Gazebo """ -from scenario_execution_base.behaviors import RunExternalProcess +from scenario_execution_base.actions import RunExternalProcess import py_trees from enum import Enum diff --git a/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_delete_actor.py b/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_delete_actor.py index 0d96e9eb..e6810ae7 100644 --- a/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_delete_actor.py +++ b/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_delete_actor.py @@ -20,7 +20,7 @@ import py_trees from enum import Enum -from scenario_execution_base.behaviors import RunExternalProcess +from scenario_execution_base.actions import RunExternalProcess class DeleteActionState(Enum): diff --git a/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_spawn_actor.py b/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_spawn_actor.py index 5d06262d..28a4bb8d 100644 --- a/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_spawn_actor.py +++ b/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_spawn_actor.py @@ -26,7 +26,7 @@ from rclpy.logging import get_logger from rclpy.node import Node import py_trees -from scenario_execution_base.behaviors import RunExternalProcess +from scenario_execution_base.actions import RunExternalProcess from scenario_execution_gazebo.utils import SpawnUtils diff --git a/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_spawn_moving_actor.py b/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_spawn_moving_actor.py index e1b1898b..d4d30c32 100644 --- a/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_spawn_moving_actor.py +++ b/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_spawn_moving_actor.py @@ -28,7 +28,7 @@ from rclpy.logging import get_logger from rclpy.node import Node import py_trees -from scenario_execution_base.behaviors import RunExternalProcess +from scenario_execution_base.actions import RunExternalProcess from scenario_execution_gazebo.utils import SpawnUtils diff --git a/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_wait_for_sim.py b/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_wait_for_sim.py index f484d014..f17fc9f2 100644 --- a/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_wait_for_sim.py +++ b/scenario_execution_gazebo/scenario_execution_gazebo/gazebo_wait_for_sim.py @@ -19,7 +19,7 @@ import py_trees from enum import Enum -from scenario_execution_base.behaviors import RunExternalProcess +from scenario_execution_base.actions import RunExternalProcess class WaitForSimulationActionState(Enum):