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

Commit a426b68

Browse files
author
fred-labs
authored
Support usage of ros messages as parameters (#165)
1 parent 408ae37 commit a426b68

File tree

36 files changed

+345
-133
lines changed

36 files changed

+345
-133
lines changed

docs/development.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@ 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__()`` does not need to contain all osc2-defined arguments. This can be convenient as variable argument resolving might not be available during ``__init__()``.
99+
- ``execute()`` contains all osc2-arguments.
98100
- ``setup()`` provides several arguments that might be useful:
99101
- ``input_dir``: Directory containing the scenario file
100102
- ``output_dir``: If given on command-line, contains the directory to save output to

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: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
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

@@ -31,15 +31,15 @@ def __init__(self, associated_actor, sdf_template: str, arguments: list):
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

4545
def execute(self, associated_actor, sdf_template: str, arguments: list):

libs/scenario_execution_gazebo/scenario_execution_gazebo/actions/gazebo_relative_spawn_actor.py

Lines changed: 2 additions & 1 deletion
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):
@@ -97,4 +98,4 @@ def calculate_new_pose(self):
9798
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}' \
9899
' } }'
99100
except TransformException as e:
100-
raise ValueError(f"No transform available ({self.parent_frame_id}->{self.frame_id})") from e
101+
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: 5 additions & 4 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

@@ -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,12 @@ 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

9495
def execute(self, associated_actor, spawn_pose: list, world_name: str, xacro_arguments: list, model: str): # pylint: disable=arguments-differ
9596
if self.entity_model != model or set(self.xacro_arguments) != set(xacro_arguments):
96-
raise ValueError("Runtime change of model not supported.")
97+
raise ActionError("Runtime change of model not supported.", action=self)
9798
self.spawn_pose = spawn_pose
9899
self.world_name = world_name
99100

@@ -175,7 +176,7 @@ def get_spawn_pose(self):
175176
f' w: {quaternion[0]} x: {quaternion[1]} y: {quaternion[2]} z: {quaternion[3]}' \
176177
' } }'
177178
except KeyError as e:
178-
raise ValueError("Could not get values") from e
179+
raise ActionError("Could not get values", action=self) from e
179180
return pose
180181

181182
def set_command(self, command):

libs/scenario_execution_nav2/scenario_execution_nav2/actions/init_nav2.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
from .nav2_common import NamespaceAwareBasicNavigator
3333
from scenario_execution_ros.actions.common import get_pose_stamped, NamespacedTransformListener
34-
from scenario_execution.actions.base_action import BaseAction
34+
from scenario_execution.actions.base_action import BaseAction, ActionError
3535

3636

3737
class InitNav2State(Enum):
@@ -91,7 +91,7 @@ def setup(self, **kwargs):
9191
except KeyError as e:
9292
error_message = "didn't find 'node' in setup's kwargs [{}][{}]".format(
9393
self.name, self.__class__.__name__)
94-
raise KeyError(error_message) from e
94+
raise ActionError(error_message, action=self) from e
9595

9696
self.tf_buffer = Buffer()
9797
self.tf_listener = NamespacedTransformListener(

libs/scenario_execution_pybullet/scenario_execution_pybullet/actions/sim_run.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# SPDX-License-Identifier: Apache-2.0
1616

1717
import py_trees
18-
from scenario_execution.actions.base_action import BaseAction
18+
from scenario_execution.actions.base_action import BaseAction, ActionError
1919
import pybullet as p
2020
import math
2121

@@ -30,10 +30,10 @@ def setup(self, **kwargs):
3030
try:
3131
tick_period: float = kwargs['tick_period']
3232
except KeyError as e:
33-
raise KeyError("didn't find 'tick_period' in setup's kwargs") from e
33+
raise ActionError("didn't find 'tick_period' in setup's kwargs", action=self) from e
3434
if not math.isclose(240 % tick_period, 0., abs_tol=1e-4):
35-
raise ValueError(
36-
f"Scenario Execution Tick Period of {tick_period} is not compatible with PyBullet stepping. Please set step-duration to be a multiple of 1/240s")
35+
raise ActionError(
36+
f"Scenario Execution Tick Period of {tick_period} is not compatible with PyBullet stepping. Please set step-duration to be a multiple of 1/240s", action=self)
3737
self.sim_steps_per_tick = round(240 * tick_period)
3838
self.logger.info(f"Forward simulation by {self.sim_steps_per_tick} step per scenario tick.")
3939

libs/scenario_execution_x11/scenario_execution_x11/actions/capture_screen.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from enum import Enum
1818
import py_trees
1919
import os
20+
from scenario_execution.actions.base_action import ActionError
2021
from scenario_execution.actions.run_process import RunProcess
2122

2223

@@ -36,12 +37,12 @@ def __init__(self, output_filename: str, frame_rate: float):
3637

3738
def setup(self, **kwargs):
3839
if "DISPLAY" not in os.environ:
39-
raise ValueError("capture_screen() requires environment variable 'DISPLAY' to be set.")
40+
raise ActionError("capture_screen() requires environment variable 'DISPLAY' to be set.", action=self)
4041

4142
if kwargs['output_dir']:
4243
if not os.path.exists(kwargs['output_dir']):
43-
raise ValueError(
44-
f"Specified destination dir '{kwargs['output_dir']}' does not exist")
44+
raise ActionError(
45+
f"Specified destination dir '{kwargs['output_dir']}' does not exist", action=self)
4546
self.output_dir = kwargs['output_dir']
4647

4748
def execute(self, output_filename: str, frame_rate: float): # pylint: disable=arguments-differ

scenario_execution/scenario_execution/actions/base_action.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import py_trees
1818
from scenario_execution.model.types import ParameterDeclaration, ScenarioDeclaration
19+
from scenario_execution.model.error import OSC2Error
1920

2021

2122
class BaseAction(py_trees.behaviour.Behaviour):
@@ -78,7 +79,7 @@ def get_blackboard_namespace(node: ParameterDeclaration):
7879

7980
def register_access_to_associated_actor_variable(self, variable_name):
8081
if not self._model.actor:
81-
raise ValueError("Model does not have 'actor'.")
82+
raise ActionError("Model does not have 'actor'.", action=self)
8283
blackboard = self.get_blackboard_client()
8384
model_blackboard_name = self._model.actor.get_fully_qualified_var_name(include_scenario=False)
8485
model_blackboard_name += "/" + variable_name
@@ -94,3 +95,11 @@ def get_associated_actor_variable(self, variable_name):
9495
model_blackboard_name = self.register_access_to_associated_actor_variable(variable_name)
9596
self.logger.debug(f"Get variable '{model_blackboard_name}'")
9697
return getattr(self.get_blackboard_client(), model_blackboard_name)
98+
99+
100+
class ActionError(OSC2Error):
101+
102+
def __init__(self, msg: str, action: BaseAction, *args) -> None:
103+
if action is not None:
104+
ctx = action._model.get_ctx()
105+
super().__init__(msg, ctx, *args)

scenario_execution/scenario_execution/actions/log.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
import py_trees
1818
from py_trees.common import Status
19-
from scenario_execution.actions.base_action import BaseAction
19+
from scenario_execution.actions.base_action import BaseAction, ActionError
2020

2121

2222
class Log(BaseAction):
@@ -38,7 +38,7 @@ def update(self) -> py_trees.common.Status:
3838
if not self.published:
3939
self.published = True
4040
if not self.msg:
41-
raise ValueError("log(): Empty message.")
41+
raise ActionError("log(): Empty message.", action=self)
4242
self.logger.info(f"{self.msg}")
4343

4444
return Status.SUCCESS

0 commit comments

Comments
 (0)