Skip to content
This repository was archived by the owner on Jul 16, 2025. It is now read-only.

Commit 6582941

Browse files
author
fred-labs
committed
Merge branch 'main' into additional_comp
2 parents bb338ab + 6f05d87 commit 6582941

File tree

67 files changed

+2956
-3941
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+2956
-3941
lines changed

docs/development.rst

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ Implement an Action
9595
- Make use of ``kwargs['logger']``, available in ``setup()``
9696
- If you want to draw markers for RViz, use ``kwargs['marker_handler']``, available in ``setup()`` (with ROS backend)
9797
- Use arguments from ``__init__()`` for a longer running initialization in ``setup()`` and the arguments from ``execute()`` to set values just before executing the action.
98+
- ``__init__()`` and ``setup()`` are called once, ``execute()`` might be called multiple times.
99+
- osc2 arguments can only be consumed once, either in ``__init__()`` or ``execute()``. Exception: If an ``associated_actor`` exists, it's an argument of both methods.
100+
- Arguments that need late resolving (e.g. referring to variables or external methods) need to be consumed in ``execute()``.
98101
- ``setup()`` provides several arguments that might be useful:
99102
- ``input_dir``: Directory containing the scenario file
100103
- ``output_dir``: If given on command-line, contains the directory to save output to

docs/libraries.rst

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -437,10 +437,14 @@ Patch an existing Kubernetes network policy.
437437
- ``string``
438438
-
439439
- The target network policy to patch
440-
* - ``network_enabled``
440+
* - ``ingress_enabled``
441441
- ``bool``
442442
-
443-
- Should the network be enabled
443+
- Should ingress (i.e., incoming) network traffic be enabled
444+
* - ``egress_enabled``
445+
- ``bool``
446+
-
447+
- Should egress (i.e., outgoing) network traffic be enabled
444448
* - ``match_label``
445449
- ``key_value``
446450
-

docs/tutorials.rst

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ Then, we can write the implementation of action plugin in Python.
9191
9292
class CustomAction(BaseAction):
9393
94-
def __init__(self, data: str):
94+
def __init__(self):
9595
super().__init__()
9696
9797
def execute(self, data: str):
@@ -105,9 +105,9 @@ Then, we can write the implementation of action plugin in Python.
105105
In the example, we created a custom action plugin to print a message on the
106106
screen. The first step is to create an action implementation, based on the class ``BaseAction``.
107107
There are two methods that can be overloaded in order to receive the action arguments as defined in the osc file.
108-
The first is the ``__init__()`` function which gets the argument values as they get initialized during parsing the scenario file.
109-
The second is the ``execute()`` function which gets the argument values as they are currently defined at the time the action gets executed.
110-
This allows to initialize the action and then set the latest values just before the action gets triggered.
108+
109+
1. ``__init__()`` function which is called once for each action instance. It can consume any of the arguments defined within the scenario file. If there are long-running initialization tasks, it is good practice to execute those within ``setup()``, which is also only called once.
110+
2. ``execute()`` function is called when the action gets active. It receives all remaining arguments, that are not consumed within ``__init__()``. It is good practice to consume as many arguments as possible here, to allow late-resolving (e.g. receiving the latest value from variables or external methods).
111111

112112
The action plugin ``custom_action`` only defines one parameter ``data``, so the behavior only has to accept ``data`` as an
113113
argument. Then, override the ``update()`` function to define how the

examples/example_library/example_library/actions/custom_action.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121

2222
class CustomAction(BaseAction):
2323

24-
def __init__(self, data: str): # get action arguments, at the time of initialization
24+
def __init__(self): # get action arguments, at the time of initialization
2525
super().__init__()
2626

2727
def execute(self, data: str): # get action arguments, at the time of execution (may got updated during scenario execution)

libs/scenario_execution_floorplan_dsl/scenario_execution_floorplan_dsl/actions/generate_floorplan.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
from enum import Enum
1919

2020
import py_trees
21-
from scenario_execution.actions.base_action import BaseAction
21+
from scenario_execution.actions.base_action import BaseAction, ActionError
2222

2323
import docker
2424
import tempfile
@@ -46,20 +46,20 @@ def setup(self, **kwargs):
4646
# self.output_dir = tempfile.mkdtemp() # for testing: does not remove directory afterwards
4747

4848
if "input_dir" not in kwargs:
49-
raise ValueError("input_dir not defined.")
49+
raise ActionError("input_dir not defined.", action=self)
5050
input_dir = kwargs["input_dir"]
5151
# check docker image
5252
self.client = docker.from_env()
5353
image_name = 'floorplan:latest'
5454
filterred_images = self.client.images.list(filters={'reference': image_name})
5555
if len(filterred_images) == 0:
56-
raise ValueError(f"Required docker image '{image_name}' does not exist.")
56+
raise ActionError(f"Required docker image '{image_name}' does not exist.", action=self)
5757

5858
# check files
5959
if not os.path.isabs(self.file_path):
6060
self.file_path = os.path.join(input_dir, self.file_path)
6161
if not os.path.isfile(self.file_path):
62-
raise ValueError(f"Floorplan file {self.file_path} not found.")
62+
raise ActionError(f"Floorplan file {self.file_path} not found.", action=self)
6363
self.floorplan_name = os.path.splitext(os.path.basename(self.file_path))[0]
6464

6565
def update(self) -> py_trees.common.Status:

libs/scenario_execution_floorplan_dsl/scenario_execution_floorplan_dsl/actions/generate_gazebo_world.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,32 @@
1717
import os
1818
import py_trees
1919
from scenario_execution_gazebo.actions.utils import SpawnUtils
20-
from scenario_execution.actions.base_action import BaseAction
20+
from scenario_execution.actions.base_action import BaseAction, ActionError
2121
from shutil import which
2222
import tempfile
2323

2424

2525
class GenerateGazeboWorld(BaseAction):
2626

27-
def __init__(self, associated_actor, sdf_template: str, arguments: list):
27+
def __init__(self, associated_actor, sdf_template: str):
2828
super().__init__()
2929
self.sdf_template = sdf_template
3030
self.spawn_utils = SpawnUtils(self.logger)
3131

3232
def setup(self, **kwargs):
3333
if which("xacro") is None:
34-
raise ValueError("'xacro' not found.")
34+
raise ActionError("'xacro' not found.", action=self)
3535
if "input_dir" not in kwargs:
36-
raise ValueError("input_dir not defined.")
36+
raise ActionError("input_dir not defined.", action=self)
3737
input_dir = kwargs["input_dir"]
3838

3939
if not os.path.isabs(self.sdf_template):
4040
self.sdf_template = os.path.join(input_dir, self.sdf_template)
4141
if not os.path.isfile(self.sdf_template):
42-
raise ValueError(f"SDF Template {self.sdf_template} not found.")
42+
raise ActionError(f"SDF Template {self.sdf_template} not found.", action=self)
4343
self.tmp_file = tempfile.NamedTemporaryFile(suffix=".sdf") # for testing, do not delete temp file: delete=False
4444

45-
def execute(self, associated_actor, sdf_template: str, arguments: list):
45+
def execute(self, associated_actor, arguments: list):
4646
self.arguments_string = ""
4747
for elem in arguments:
4848
self.arguments_string += f'{elem["key"]}:={elem["value"]}'

libs/scenario_execution_gazebo/scenario_execution_gazebo/actions/gazebo_actor_exists.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ class GazeboActorExists(RunProcess):
3737
3838
"""
3939

40-
def __init__(self, entity_name: str, world_name: str):
40+
def __init__(self):
4141
super().__init__()
42+
self.entity_name = None
4243
self.current_state = ActorExistsActionState.IDLE
4344

4445
def execute(self, entity_name: str, world_name: str): # pylint: disable=arguments-differ

libs/scenario_execution_gazebo/scenario_execution_gazebo/actions/gazebo_delete_actor.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ class GazeboDeleteActor(RunProcess):
3737
3838
"""
3939

40-
def __init__(self, associated_actor, entity_name: str, world_name: str):
40+
def __init__(self, associated_actor):
4141
super().__init__()
42+
self.entity_name = None
4243
self.current_state = DeleteActionState.IDLE
4344

4445
def execute(self, associated_actor, entity_name: str, world_name: str): # pylint: disable=arguments-differ

libs/scenario_execution_gazebo/scenario_execution_gazebo/actions/gazebo_relative_spawn_actor.py

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from tf2_ros.transform_listener import TransformListener
2222
from tf2_geometry_msgs import PoseStamped
2323
from .gazebo_spawn_actor import GazeboSpawnActor
24+
from scenario_execution.actions.base_action import ActionError
2425

2526

2627
class GazeboRelativeSpawnActor(GazeboSpawnActor):
@@ -29,20 +30,17 @@ class GazeboRelativeSpawnActor(GazeboSpawnActor):
2930
3031
"""
3132

32-
def __init__(self, associated_actor,
33-
frame_id: str, parent_frame_id: str,
34-
distance: float, world_name: str, xacro_arguments: list,
35-
model: str):
36-
super().__init__(associated_actor, None, world_name, xacro_arguments, model)
33+
def __init__(self, associated_actor, xacro_arguments: list, model: str):
34+
super().__init__(associated_actor, xacro_arguments, model)
3735
self._pose = '{}'
36+
self.model = model
37+
self.world_name = None
38+
self.xacro_arguments = xacro_arguments
3839
self.tf_buffer = Buffer()
3940
self.tf_listener = None
4041

41-
def execute(self, associated_actor, # pylint: disable=arguments-differ
42-
frame_id: str, parent_frame_id: str,
43-
distance: float, world_name: str, xacro_arguments: list,
44-
model: str):
45-
super().execute(associated_actor, None, world_name, xacro_arguments, model)
42+
def execute(self, associated_actor, frame_id: str, parent_frame_id: str, distance: float, world_name: str): # pylint: disable=arguments-differ
43+
super().execute(associated_actor, None, world_name)
4644
self.frame_id = frame_id
4745
self.parent_frame_id = parent_frame_id
4846
self.distance = distance
@@ -97,4 +95,4 @@ def calculate_new_pose(self):
9795
f' w: {new_pose.pose.orientation.w} x: {new_pose.pose.orientation.x} y: {new_pose.pose.orientation.y} z: {new_pose.pose.orientation.z}' \
9896
' } }'
9997
except TransformException as e:
100-
raise ValueError(f"No transform available ({self.parent_frame_id}->{self.frame_id})") from e
98+
raise ActionError(f"No transform available ({self.parent_frame_id}->{self.frame_id})", action=self) from e

libs/scenario_execution_gazebo/scenario_execution_gazebo/actions/gazebo_spawn_actor.py

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
from rclpy.qos import QoSProfile, QoSDurabilityPolicy, QoSHistoryPolicy, QoSReliabilityPolicy
2424
from rclpy.node import Node
2525
import py_trees
26+
from scenario_execution.actions.base_action import ActionError
2627
from scenario_execution.actions.run_process import RunProcess
2728
from .utils import SpawnUtils
2829

@@ -44,7 +45,7 @@ class GazeboSpawnActor(RunProcess):
4445
4546
"""
4647

47-
def __init__(self, associated_actor, spawn_pose: list, world_name: str, xacro_arguments: list, model: str):
48+
def __init__(self, associated_actor, xacro_arguments: list, model: str):
4849
"""
4950
init
5051
"""
@@ -68,7 +69,7 @@ def setup(self, **kwargs):
6869
except KeyError as e:
6970
error_message = "didn't find 'node' in setup's kwargs [{}][{}]".format(
7071
self.name, self.__class__.__name__)
71-
raise KeyError(error_message) from e
72+
raise ActionError(error_message, action=self) from e
7273

7374
self.utils = SpawnUtils(logger=self.logger)
7475

@@ -88,12 +89,10 @@ def setup(self, **kwargs):
8889
self.entity_model, self.entity_name, self.xacro_arguments)
8990

9091
if not self.sdf:
91-
raise ValueError(f'Invalid model specified ({self.entity_model})')
92+
raise ActionError(f'Invalid model specified ({self.entity_model})', action=self)
9293
self.current_state = SpawnActionState.MODEL_AVAILABLE
9394

94-
def execute(self, associated_actor, spawn_pose: list, world_name: str, xacro_arguments: list, model: str): # pylint: disable=arguments-differ
95-
if self.entity_model != model or set(self.xacro_arguments) != set(xacro_arguments):
96-
raise ValueError("Runtime change of model not supported.")
95+
def execute(self, associated_actor, spawn_pose: list, world_name: str): # pylint: disable=arguments-differ
9796
self.spawn_pose = spawn_pose
9897
self.world_name = world_name
9998

@@ -175,7 +174,7 @@ def get_spawn_pose(self):
175174
f' w: {quaternion[0]} x: {quaternion[1]} y: {quaternion[2]} z: {quaternion[3]}' \
176175
' } }'
177176
except KeyError as e:
178-
raise ValueError("Could not get values") from e
177+
raise ActionError("Could not get values", action=self) from e
179178
return pose
180179

181180
def set_command(self, command):

0 commit comments

Comments
 (0)