From 9567a711f4f3556ec4b1fe4fe0489a5f8bdc62e2 Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Fri, 20 Dec 2024 10:50:48 +0100 Subject: [PATCH 01/26] Place conan deps inside project and change .gitignore --- .gitignore | 4 +++- utils/ci/scripts/15_prepare_thirdParty.sh | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index fc6f72e2..0ef100c2 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,6 @@ __pycache__ generator.log *.orig *.bkp -.devcontainer \ No newline at end of file +.devcontainer +build +deps \ No newline at end of file diff --git a/utils/ci/scripts/15_prepare_thirdParty.sh b/utils/ci/scripts/15_prepare_thirdParty.sh index 05617eb1..1273e1c3 100755 --- a/utils/ci/scripts/15_prepare_thirdParty.sh +++ b/utils/ci/scripts/15_prepare_thirdParty.sh @@ -148,4 +148,4 @@ done # Command to install all the packages into the required folder. # --build=missing argument is necessary as at this point protobuf is checked and if not available it builds from conancenter -"$PYTHON_COMMAND" -m conans.conan install $file --build=missing --deployer=direct_deploy -of="$REPO_ROOT/../deps" -pr:a "$conanprofile" || exit 1 \ No newline at end of file +"$PYTHON_COMMAND" -m conans.conan install $file --build=missing --deployer=direct_deploy -of="$REPO_ROOT/deps" -pr:a "$conanprofile" || exit 1 \ No newline at end of file -- GitLab From f8f9662a8e2f774e2f0a8fe1c24315790b5ae03b Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Fri, 20 Dec 2024 10:51:39 +0100 Subject: [PATCH 02/26] Change mantle_api branch for traffic area --- engine/bazel/deps.bzl | 46 +++++++++++++++++++ .../conan/recipe/mantleapi/all/conandata.yml | 4 ++ .../openscenario_engine/all/conanfile.py | 2 +- 3 files changed, 51 insertions(+), 1 deletion(-) create mode 100644 engine/bazel/deps.bzl diff --git a/engine/bazel/deps.bzl b/engine/bazel/deps.bzl new file mode 100644 index 00000000..cc30aa01 --- /dev/null +++ b/engine/bazel/deps.bzl @@ -0,0 +1,46 @@ +load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive") +load("@bazel_tools//tools/build_defs/repo:utils.bzl", "maybe") + +ECLIPSE_GITLAB = "https://gitlab.eclipse.org/eclipse" + +MANTLE_API_TAG = "v6.0.0" +UNITS_TAG = "2.3.3" +YASE_TAG = "v0.0.1" + +MANTLE_API_TAG = "15bf75e4e3238afa5f1aa85c4054f37804375453" + +def osc_engine_deps(): + """Load dependencies""" + maybe( + http_archive, + name = "mantle_api", + url = ECLIPSE_GITLAB + "/openpass/mantle-api/-/archive/{tag}/mantle-api-{tag}.tar.gz".format(tag = MANTLE_API_TAG), + sha256 = "9f898c45b0a0541a553e6d1a7721215d76516daf9ad70658aab40d669bd2364d", + strip_prefix = "mantle-api-{tag}".format(tag = MANTLE_API_TAG), + type = "tar.gz", + ) + + maybe( + http_archive, + name = "units_nhh", + url = "https://github.com/nholthaus/units/archive/refs/tags/v{tag}.tar.gz".format(tag = UNITS_TAG), + sha256 = "b1f3c1dd11afa2710a179563845ce79f13ebf0c8c090d6aa68465b18bd8bd5fc", + strip_prefix = "./units-{tag}".format(tag = UNITS_TAG), + build_file = "//bazel:units.BUILD", + ) + + maybe( + http_archive, + name = "yase", + url = ECLIPSE_GITLAB + "/openpass/yase/-/archive/{tag}/yase-{tag}.tar.gz".format(tag = YASE_TAG), + sha256 = "243d710172dff4c4e7ab95e5bd68a6d8335cf8a3fdd1efa87d118250e3b85ee3", + strip_prefix = "yase-{tag}".format(tag = YASE_TAG), + ) + + http_archive( + name = "open_scenario_parser", + build_file = "//bazel:openscenario_api.BUILD", + url = "https://github.com/RA-Consulting-GmbH/openscenario.api.test/releases/download/v1.4.0/OpenSCENARIO_API_LinuxSharedRelease_2024.11.18.tgz", + sha256 = "7a4cdb82ccaaeed5fccf12efd94cf4f9c9d3ac0fc7d7feedd4c0babe2404f853", + strip_prefix = "OpenSCENARIO_API_LinuxSharedRelease", + ) diff --git a/utils/ci/conan/recipe/mantleapi/all/conandata.yml b/utils/ci/conan/recipe/mantleapi/all/conandata.yml index 30e9ad2a..56d0b1a5 100644 --- a/utils/ci/conan/recipe/mantleapi/all/conandata.yml +++ b/utils/ci/conan/recipe/mantleapi/all/conandata.yml @@ -41,6 +41,10 @@ sources: url: https://gitlab.eclipse.org/eclipse/openpass/mantle-api.git sha256: "a5530013edcd2028ce48d1dcd7e90a512c158f27" + "6.0.1": + url: https://gitlab.eclipse.org/eclipse/openpass/mantle-api.git + sha256: "4c04afb373d26b9e4ddcae3fd920f6c5543a19f2" + "8.0.0": url: https://gitlab.eclipse.org/eclipse/openpass/mantle-api.git sha256: "18eadd8ad4f3ddd78c955ce3d4d637a8ff912091" diff --git a/utils/ci/conan/recipe/openscenario_engine/all/conanfile.py b/utils/ci/conan/recipe/openscenario_engine/all/conanfile.py index 43f2201c..b79fb3fa 100644 --- a/utils/ci/conan/recipe/openscenario_engine/all/conanfile.py +++ b/utils/ci/conan/recipe/openscenario_engine/all/conanfile.py @@ -25,7 +25,7 @@ class OpenScenarioEngineConan(ConanFile): settings = "os", "compiler", "build_type", "arch" options = {"shared": [True, False], "fPIC": [True, False], - "MantleAPI_version":["ANY"], + "MantleAPI_version":["6.0.1"], "openscenario_api_version":["ANY"], "units_version":["ANY"], "Yase_version":["ANY"] -- GitLab From 336edc8588571139cb0249544ff004e517568b49 Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Fri, 20 Dec 2024 10:55:51 +0100 Subject: [PATCH 03/26] Fix "entitiy" typo --- engine/tests/Utils/ControllerCreatorTest.cpp | 8 ++++---- generator/model/v1_3.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/engine/tests/Utils/ControllerCreatorTest.cpp b/engine/tests/Utils/ControllerCreatorTest.cpp index 2b5bc9ec..93f11830 100644 --- a/engine/tests/Utils/ControllerCreatorTest.cpp +++ b/engine/tests/Utils/ControllerCreatorTest.cpp @@ -42,11 +42,11 @@ public: mockController{} { ON_CALL(mockController, GetName()).WillByDefault(ReturnRef(TEST_CONTROLLER_NAME)); - ON_CALL(mockController, GetUniqueId()).WillByDefault(testing::Invoke([] { + ON_CALL(mockController, GetUniqueId()).WillByDefault(testing::Invoke([] + { // id will be incremented at every call, starting with 1000 static mantle_api::UniqueId id{1000}; - return id++; - })); + return id++; })); } void SET_VEHICLE_MOCK_NAME_AND_ID(const std::string& name, mantle_api::UniqueId id) @@ -168,7 +168,7 @@ TEST_F(ControllerCreator, GivenScenarioObjectWithController_WhenCreateIsCalled_T controller_creator.CreateControllers(scenario_objects); } -TEST_F(ControllerCreator, GivenScenarioObjectWithController_WhenCreateIsCalled_ThenControllerServiceContainsEntitiyControllers) +TEST_F(ControllerCreator, GivenScenarioObjectWithController_WhenCreateIsCalled_ThenControllerServiceContainsEntityControllers) { constexpr mantle_api::UniqueId ENTITY_ID = 1234; SETUP_ENTITY("test_entity", ENTITY_ID, true, true); diff --git a/generator/model/v1_3.json b/generator/model/v1_3.json index dc20d2c9..e74e23b4 100755 --- a/generator/model/v1_3.json +++ b/generator/model/v1_3.json @@ -10545,7 +10545,7 @@ "ScenarioObjectTemplate": { "isModelGroupChoice": false, "properties": { - "entitiyObject": { + "entityObject": { "isSimpleContentProperty": false, "isList": false, "isOptional": false, -- GitLab From b054b27025d99828d7d1cadc9d5bd93fc82e6dbd Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Fri, 20 Dec 2024 10:56:18 +0100 Subject: [PATCH 04/26] Rework TrafficAreaAction Spawning Add Spawning Utils --- engine/cmake/generated_files.cmake | 23 +-- .../GenericAction/TrafficAreaAction.h | 61 -------- .../GenericAction/TrafficAreaAction_base.h | 47 ------ .../GenericAction/TrafficAreaAction_impl.cpp | 27 ---- .../GenericAction/TrafficAreaAction_impl.h | 27 ---- .../GenericAction/TrafficSourceAction.h | 6 +- .../GenericAction/TrafficSourceAction_base.h | 2 +- .../ConvertScenarioTrafficArea.cpp | 51 ++++++ .../OscToMantle/ConvertScenarioTrafficArea.h | 15 +- .../ConvertScenarioTrafficDistribution.cpp | 46 ++++++ .../ConvertScenarioTrafficDistribution.h | 18 ++- engine/src/Storyboard/GenericAction/Action.h | 119 ++++++++++++++ .../ActivateControllerAction.cpp | 76 +++++---- .../GenericAction/ActivateControllerAction.h | 62 ++++++-- .../ActivateControllerAction_base.h | 57 ------- .../ActivateControllerAction_impl.cpp | 47 ------ .../ActivateControllerAction_impl.h | 29 ---- .../GenericAction/LightStateAction.cpp | 65 ++++++++ .../GenericAction/LightStateAction.h | 81 ++++++---- .../GenericAction/LightStateAction_base.h | 48 ------ .../GenericAction/LightStateAction_impl.cpp | 38 ----- .../GenericAction/LightStateAction_impl.h | 33 ---- .../GenericAction/TrafficAreaAction.cpp | 147 ++++++++++++++++++ .../GenericAction/TrafficAreaAction.h | 76 +++++++++ engine/src/Utils/Spawning/Common.h | 45 ++++++ engine/src/Utils/Spawning/Interval.cpp | 27 ++++ engine/src/Utils/Spawning/Interval.h | 112 +++++++++++++ engine/src/Utils/Spawning/Iterable.h | 131 ++++++++++++++++ engine/src/Utils/Spawning/Length.cpp | 52 +++++++ engine/src/Utils/Spawning/Length.h | 96 ++++++++++++ engine/src/Utils/Spawning/SpawnZone.cpp | 104 +++++++++++++ engine/src/Utils/Spawning/SpawnZone.h | 97 ++++++++++++ engine/src/Utils/Spawning/Transform.h | 49 ++++++ .../ActivateControllerActionTest.cpp | 2 +- 34 files changed, 1396 insertions(+), 520 deletions(-) delete mode 100644 engine/gen/Storyboard/GenericAction/TrafficAreaAction.h delete mode 100644 engine/gen/Storyboard/GenericAction/TrafficAreaAction_base.h delete mode 100644 engine/gen/Storyboard/GenericAction/TrafficAreaAction_impl.cpp delete mode 100644 engine/gen/Storyboard/GenericAction/TrafficAreaAction_impl.h create mode 100644 engine/src/Conversion/OscToMantle/ConvertScenarioTrafficArea.cpp create mode 100644 engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp create mode 100644 engine/src/Storyboard/GenericAction/Action.h delete mode 100644 engine/src/Storyboard/GenericAction/ActivateControllerAction_base.h delete mode 100644 engine/src/Storyboard/GenericAction/ActivateControllerAction_impl.cpp delete mode 100644 engine/src/Storyboard/GenericAction/ActivateControllerAction_impl.h create mode 100644 engine/src/Storyboard/GenericAction/LightStateAction.cpp delete mode 100644 engine/src/Storyboard/GenericAction/LightStateAction_base.h delete mode 100644 engine/src/Storyboard/GenericAction/LightStateAction_impl.cpp delete mode 100644 engine/src/Storyboard/GenericAction/LightStateAction_impl.h create mode 100644 engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp create mode 100644 engine/src/Storyboard/GenericAction/TrafficAreaAction.h create mode 100644 engine/src/Utils/Spawning/Common.h create mode 100644 engine/src/Utils/Spawning/Interval.cpp create mode 100644 engine/src/Utils/Spawning/Interval.h create mode 100644 engine/src/Utils/Spawning/Iterable.h create mode 100644 engine/src/Utils/Spawning/Length.cpp create mode 100644 engine/src/Utils/Spawning/Length.h create mode 100644 engine/src/Utils/Spawning/SpawnZone.cpp create mode 100644 engine/src/Utils/Spawning/SpawnZone.h create mode 100644 engine/src/Utils/Spawning/Transform.h diff --git a/engine/cmake/generated_files.cmake b/engine/cmake/generated_files.cmake index a1d1a403..7a4e54a1 100644 --- a/engine/cmake/generated_files.cmake +++ b/engine/cmake/generated_files.cmake @@ -133,7 +133,6 @@ list(APPEND ${PROJECT_NAME}_SOURCES gen/Storyboard/GenericAction/RandomRouteAction_impl.cpp gen/Storyboard/GenericAction/SetMonitorAction_impl.cpp gen/Storyboard/GenericAction/SpeedProfileAction_impl.cpp - gen/Storyboard/GenericAction/TrafficAreaAction_impl.cpp gen/Storyboard/GenericAction/TrafficSignalControllerAction_impl.cpp gen/Storyboard/GenericAction/TrafficSourceAction_impl.cpp gen/Storyboard/GenericAction/TrafficStopAction_impl.cpp @@ -163,7 +162,9 @@ list(APPEND ${PROJECT_NAME}_SOURCES src/Conversion/OscToMantle/ConvertScenarioSpeedActionTarget.cpp src/Conversion/OscToMantle/ConvertScenarioTimeReference.cpp src/Conversion/OscToMantle/ConvertScenarioTimeToCollisionConditionTarget.cpp + src/Conversion/OscToMantle/ConvertScenarioTrafficArea.cpp src/Conversion/OscToMantle/ConvertScenarioTrafficDefinition.cpp + src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp src/Conversion/OscToMantle/ConvertScenarioTrafficSignalController.cpp src/Conversion/OscToMantle/ConvertScenarioTrajectoryRef.cpp src/Conversion/OscToMantle/ConvertScenarioTransitionDynamics.cpp @@ -211,13 +212,13 @@ list(APPEND ${PROJECT_NAME}_SOURCES src/Storyboard/ByValueCondition/UserDefinedValueCondition_impl.cpp src/Storyboard/GenericAction/AcquirePositionAction_impl.cpp src/Storyboard/GenericAction/ActivateControllerAction.cpp - src/Storyboard/GenericAction/ActivateControllerAction_impl.cpp src/Storyboard/GenericAction/AssignControllerAction_impl.cpp src/Storyboard/GenericAction/AssignRouteAction_impl.cpp src/Storyboard/GenericAction/CustomCommandAction_impl.cpp src/Storyboard/GenericAction/EnvironmentAction_impl.cpp - src/Storyboard/GenericAction/LightStateAction_impl.cpp + src/Storyboard/GenericAction/LightStateAction.cpp src/Storyboard/GenericAction/TeleportAction_impl.cpp + src/Storyboard/GenericAction/TrafficAreaAction.cpp src/Storyboard/GenericAction/TrafficSignalStateAction_impl.cpp src/Storyboard/GenericAction/TrafficSinkAction_base.h src/Storyboard/GenericAction/TrafficSinkAction_impl.cpp @@ -231,6 +232,9 @@ list(APPEND ${PROJECT_NAME}_SOURCES src/Storyboard/MotionControlAction/LateralDistanceAction_impl.cpp src/Storyboard/MotionControlAction/LongitudinalDistanceAction_impl.cpp src/Storyboard/MotionControlAction/SpeedAction_impl.cpp + src/Utils/Spawning/Interval.cpp + src/Utils/Spawning/Length.cpp + src/Utils/Spawning/SpawnZone.cpp src/Utils/ConditionEdgeEvaluator.cpp src/Utils/ControllerCreator.cpp src/Utils/ControllerService.cpp @@ -458,9 +462,6 @@ list(APPEND ${PROJECT_NAME}_HEADERS gen/Storyboard/GenericAction/TeleportAction.h gen/Storyboard/GenericAction/TeleportAction_base.h gen/Storyboard/GenericAction/TeleportAction_impl.h - gen/Storyboard/GenericAction/TrafficAreaAction.h - gen/Storyboard/GenericAction/TrafficAreaAction_base.h - gen/Storyboard/GenericAction/TrafficAreaAction_impl.h gen/Storyboard/GenericAction/TrafficSignalControllerAction.h gen/Storyboard/GenericAction/TrafficSignalControllerAction_base.h gen/Storyboard/GenericAction/TrafficSignalControllerAction_impl.h @@ -573,12 +574,9 @@ list(APPEND ${PROJECT_NAME}_HEADERS src/Storyboard/ByEntityCondition/TraveledDistanceCondition_impl.h src/Storyboard/ByValueCondition/UserDefinedValueCondition_impl.h src/Storyboard/GenericAction/ActivateControllerAction.h - src/Storyboard/GenericAction/ActivateControllerAction_base.h - src/Storyboard/GenericAction/ActivateControllerAction_impl.h src/Storyboard/GenericAction/CustomCommandAction_impl.h src/Storyboard/GenericAction/LightStateAction.h - src/Storyboard/GenericAction/LightStateAction_base.h - src/Storyboard/GenericAction/LightStateAction_impl.h + src/Storyboard/GenericAction/TrafficAreaAction.h src/Storyboard/GenericAction/TrafficSignalAction.h src/Storyboard/GenericAction/TrafficSwarmAction.h src/Storyboard/GenericAction/TrafficSwarmAction_base.h @@ -592,6 +590,11 @@ list(APPEND ${PROJECT_NAME}_HEADERS src/Storyboard/MotionControlAction/LongitudinalDistanceAction_base.h src/Storyboard/MotionControlAction/MotionControlAction.h src/Storyboard/MotionControlAction/SpeedAction_impl.h + src/Utils/Spawning/Common.h + src/Utils/Spawning/Interval.h + src/Utils/Spawning/Length.h + src/Utils/Spawning/SpawnZone.h + src/Utils/Spawning/Transform.h src/Utils/ConditionEdgeEvaluator.h src/Utils/Constants.h src/Utils/ControllerCreator.h diff --git a/engine/gen/Storyboard/GenericAction/TrafficAreaAction.h b/engine/gen/Storyboard/GenericAction/TrafficAreaAction.h deleted file mode 100644 index 0a92698d..00000000 --- a/engine/gen/Storyboard/GenericAction/TrafficAreaAction.h +++ /dev/null @@ -1,61 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021-2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ - -#pragma once - -#include <MantleAPI/Execution/i_environment.h> -#include <agnostic_behavior_tree/action_node.h> -#include <openScenarioLib/generated/v1_3/api/ApiClassInterfacesV1_3.h> - -#include <cassert> -#include <utility> - -#include "Storyboard/GenericAction/TrafficAreaAction_impl.h" - -namespace OpenScenarioEngine::v1_3::Node -{ -class TrafficAreaAction : public yase::ActionNode -{ -public: - TrafficAreaAction(std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ITrafficAreaAction> trafficAreaAction) - : yase::ActionNode{"TrafficAreaAction"}, - trafficAreaAction_{trafficAreaAction} - { - } - - void onInit() override{}; - -private: - yase::NodeStatus tick() override - { - assert(impl_); - const auto is_finished = impl_->Step(); - return is_finished ? yase::NodeStatus::kSuccess : yase::NodeStatus::kRunning; - }; - - void lookupAndRegisterData(yase::Blackboard& blackboard) final - { - std::shared_ptr<mantle_api::IEnvironment> environment = blackboard.get<std::shared_ptr<mantle_api::IEnvironment>>("Environment"); - - impl_ = std::make_unique<OpenScenarioEngine::v1_3::TrafficAreaAction>( - OpenScenarioEngine::v1_3::TrafficAreaAction::Values{ - trafficAreaAction_->GetContinuous(), - trafficAreaAction_->GetNumberOfEntities(), - ConvertScenarioTrafficDistribution(trafficAreaAction_->GetTrafficDistribution()), - ConvertScenarioTrafficArea(trafficAreaAction_->GetTrafficArea())}, - OpenScenarioEngine::v1_3::TrafficAreaAction::Interfaces{ - environment}); - } - - std::unique_ptr<OpenScenarioEngine::v1_3::TrafficAreaAction> impl_{nullptr}; - std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ITrafficAreaAction> trafficAreaAction_; -}; - -} // namespace OpenScenarioEngine::v1_3::Node diff --git a/engine/gen/Storyboard/GenericAction/TrafficAreaAction_base.h b/engine/gen/Storyboard/GenericAction/TrafficAreaAction_base.h deleted file mode 100644 index 4768c8e8..00000000 --- a/engine/gen/Storyboard/GenericAction/TrafficAreaAction_base.h +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021-2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ - -#pragma once - -#include <MantleAPI/Execution/i_environment.h> - -#include "Conversion/OscToMantle/ConvertScenarioTrafficArea.h" -#include "Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h" - -namespace OpenScenarioEngine::v1_3 -{ - -class TrafficAreaActionBase -{ -public: - struct Values - { - bool continuous; - unsigned int numberOfEntities; - TrafficDistribution trafficDistribution; - TrafficArea trafficArea; - }; - struct Interfaces - { - std::shared_ptr<mantle_api::IEnvironment> environment; - }; - - TrafficAreaActionBase(Values values, Interfaces interfaces) - : values{std::move(values)}, - mantle{std::move(interfaces)} {}; - virtual ~TrafficAreaActionBase() = default; - virtual bool Step() = 0; - -protected: - Values values; - Interfaces mantle; -}; - -} // namespace OpenScenarioEngine::v1_3 \ No newline at end of file diff --git a/engine/gen/Storyboard/GenericAction/TrafficAreaAction_impl.cpp b/engine/gen/Storyboard/GenericAction/TrafficAreaAction_impl.cpp deleted file mode 100644 index 2c7b6a75..00000000 --- a/engine/gen/Storyboard/GenericAction/TrafficAreaAction_impl.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021-2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ - -#include "Storyboard/GenericAction/TrafficAreaAction_impl.h" - -#include "Utils/Logger.h" - -namespace OpenScenarioEngine::v1_3 -{ - -bool TrafficAreaAction::Step() -{ - // Note: - // - Access to values parse to mantle/ose datatypes: this->values.xxx - // - Access to mantle interfaces: this->mantle.xxx - Logger::Error("Method TrafficAreaAction::Step() not implemented yet (returning \"true\" by default)"); - return true; -} - -} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/gen/Storyboard/GenericAction/TrafficAreaAction_impl.h b/engine/gen/Storyboard/GenericAction/TrafficAreaAction_impl.h deleted file mode 100644 index 60371a16..00000000 --- a/engine/gen/Storyboard/GenericAction/TrafficAreaAction_impl.h +++ /dev/null @@ -1,27 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021-2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ - -#pragma once - -#include "Storyboard/GenericAction/TrafficAreaAction_base.h" - -namespace OpenScenarioEngine::v1_3 -{ - -class TrafficAreaAction : public TrafficAreaActionBase -{ -public: - TrafficAreaAction(Values values, Interfaces interfaces) - : TrafficAreaActionBase{std::move(values), std::move(interfaces)} {}; - - bool Step() override; -}; - -} // namespace OpenScenarioEngine::v1_3 \ No newline at end of file diff --git a/engine/gen/Storyboard/GenericAction/TrafficSourceAction.h b/engine/gen/Storyboard/GenericAction/TrafficSourceAction.h index 2a582a7a..dfbcfdf4 100644 --- a/engine/gen/Storyboard/GenericAction/TrafficSourceAction.h +++ b/engine/gen/Storyboard/GenericAction/TrafficSourceAction.h @@ -55,9 +55,9 @@ private: [=]() { return ConvertScenarioPosition(environment, trafficSourceAction_->GetPosition()); }, ConvertScenarioTrafficDefinition(trafficSourceAction_->GetTrafficDefinition()), - ConvertScenarioTrafficDistribution(trafficSourceAction_->GetTrafficDistribution())}, - OpenScenarioEngine::v1_3::TrafficSourceAction::Interfaces{ - environment}); + ConvertScenarioTrafficDistribution(trafficSourceAction_->GetTrafficDistribution()) // + }, + OpenScenarioEngine::v1_3::TrafficSourceAction::Interfaces{environment}); } std::unique_ptr<OpenScenarioEngine::v1_3::TrafficSourceAction> impl_{nullptr}; diff --git a/engine/gen/Storyboard/GenericAction/TrafficSourceAction_base.h b/engine/gen/Storyboard/GenericAction/TrafficSourceAction_base.h index afffc0b2..45bce206 100644 --- a/engine/gen/Storyboard/GenericAction/TrafficSourceAction_base.h +++ b/engine/gen/Storyboard/GenericAction/TrafficSourceAction_base.h @@ -32,7 +32,7 @@ public: double velocity; std::function<std::optional<mantle_api::Pose>()> GetPosition; TrafficDefinition trafficDefinition; - TrafficDistribution trafficDistribution; + TrafficDistributions trafficDistributions; }; struct Interfaces { diff --git a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficArea.cpp b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficArea.cpp new file mode 100644 index 00000000..9828f445 --- /dev/null +++ b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficArea.cpp @@ -0,0 +1,51 @@ + +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#include "ConvertScenarioTrafficArea.h" + +#include "Utils/Spawning/Transform.h" + +namespace OpenScenarioEngine::v1_3 +{ +struct ToLaneRestriction +{ + mantle_api::RoadCursor::LaneRestriction operator()(const NET_ASAM_OPENSCENARIO::v1_3::ILane& lane) const + { + return {lane.GetId()}; + } +}; + +struct ToRoadCursor +{ + mantle_api::RoadCursor operator()(const NET_ASAM_OPENSCENARIO::v1_3::IRoadCursor& cursor) const + { + return {units::length::meter_t{cursor.GetS()}, cursor.GetRoadId(), TransformSharedPointers(cursor.GetLane(), ToLaneRestriction{})}; + } +}; + +struct ToRoadRange +{ + mantle_api::RoadRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IRoadRange& range) const + { + return {(range.GetRoadCursorSize() > 1 && range.GetLength() == .0) ? std::nullopt : std::optional<units::length::meter_t>{range.GetLength()}, + TransformSharedPointers(range.GetRoadCursor(), ToRoadCursor{})}; + } +}; + +TrafficArea ConvertScenarioTrafficArea(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ITrafficArea>& trafficArea) +{ + if (trafficArea->GetPolygon() != nullptr) + { + throw std::runtime_error("ConvertScenarioTrafficArea: Conversion of polygon not yet supported"); + } + return TransformSharedPointers(trafficArea->GetRoadRange(), ToRoadRange{}); +} +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficArea.h b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficArea.h index 6e615fd8..cbc3b3be 100644 --- a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficArea.h +++ b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficArea.h @@ -11,20 +11,15 @@ #pragma once +#include <MantleAPI/Traffic/road_range.h> #include <openScenarioLib/generated/v1_3/api/ApiClassInterfacesV1_3.h> #include <memory> -#include <string> +#include <vector> namespace OpenScenarioEngine::v1_3 { -struct TrafficArea -{ -}; - -inline TrafficArea ConvertScenarioTrafficArea(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ITrafficArea>& /*trafficArea*/) -{ - return {}; -} +using TrafficArea = std::vector<mantle_api::RoadRange>; -} // namespace OpenScenarioEngine::v1_3 \ No newline at end of file +TrafficArea ConvertScenarioTrafficArea(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ITrafficArea>&); +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp new file mode 100644 index 00000000..04461351 --- /dev/null +++ b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp @@ -0,0 +1,46 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#include "ConvertScenarioTrafficDistribution.h" + +#include "Utils/Spawning/Transform.h" + +namespace OpenScenarioEngine::v1_3 +{ +struct ToEntities +{ + std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject> operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityDistributionEntry& entry) const + { + return entry.GetScenarioObjectTemplate()->GetEntityObject(); + } + + std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject>> operator()(const NET_ASAM_OPENSCENARIO::v1_3::ITrafficDistributionEntry& entry) const + { + return {TransformSharedPointers(entry.GetEntityDistribution()->GetEntityDistributionEntry(), *this)}; + } +}; + +struct ToTrafficDistribution +{ + TrafficDistribution operator()(const NET_ASAM_OPENSCENARIO::v1_3::ITrafficDistributionEntry& entry) const + { + return {TransformSharedPointers(entry.GetEntityDistribution()->GetEntityDistributionEntry(), [](const NET_ASAM_OPENSCENARIO::v1_3::IEntityDistributionEntry& entry) + { return entry.GetScenarioObjectTemplate()->GetEntityObject(); }), + entry.GetWeight()}; + } +}; + +TrafficDistributions ConvertScenarioTrafficDistribution(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ITrafficDistribution>& distribution) +{ + decltype(auto) entries{distribution->GetTrafficDistributionEntry()}; + return {TransformSharedPointers(entries, ToTrafficDistribution{}), + TransformSharedPointers(entries, ToAccumulatedWeight{})}; +} +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h index e3c7abcf..ae7421b7 100644 --- a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h +++ b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h @@ -1,4 +1,3 @@ - /******************************************************************************** * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * @@ -12,19 +11,26 @@ #pragma once #include <openScenarioLib/generated/v1_3/api/ApiClassInterfacesV1_3.h> +#include <units.h> #include <memory> -#include <string> +#include <vector> + +#include "Utils/Spawning/Length.h" namespace OpenScenarioEngine::v1_3 { struct TrafficDistribution { + std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject>> entities; + units::dimensionless::scalar_t weight; }; -inline TrafficDistribution ConvertScenarioTrafficDistribution(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ITrafficDistribution>& /*trafficDistribution*/) +struct TrafficDistributions { - return {}; -} + std::vector<TrafficDistribution> distributions; + std::vector<units::dimensionless::scalar_t> weights; +}; -} // namespace OpenScenarioEngine::v1_3 \ No newline at end of file +TrafficDistributions ConvertScenarioTrafficDistribution(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ITrafficDistribution>&); +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Storyboard/GenericAction/Action.h b/engine/src/Storyboard/GenericAction/Action.h new file mode 100644 index 00000000..d97f656d --- /dev/null +++ b/engine/src/Storyboard/GenericAction/Action.h @@ -0,0 +1,119 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#pragma once + +#include <agnostic_behavior_tree/action_node.h> + +#include <string> +#include <string_view> + +namespace OpenScenarioEngine::v1_3 +{ +template <typename> +struct Interfaces +{ + std::shared_ptr<mantle_api::IEnvironment> environment; +}; + +template <typename> +struct Values +{ +}; + +template <typename Instance> // CRTP +class Action +{ +public: + Action(Values<Instance> values, Interfaces<Instance> interfaces) + : values{std::move(values)}, + mantle{std::move(interfaces)} {}; + +protected: + Values<Instance> values; + Interfaces<Instance> mantle; +}; + +namespace Node +{ +namespace detail +{ +template <typename> +struct Identifier +{ + static constexpr std::string_view value = "UNASSIGNED_IDENTIFIER"; +}; +} // namespace detail + +template <typename Type> +constexpr std::string_view identifier = detail::Identifier<Type>::value; + +#define SET_NODE_IDENTIFIER(ACTION) \ + namespace detail \ + { \ + template <> \ + struct Identifier<ACTION> \ + { \ + static constexpr std::string_view value = #ACTION; \ + }; \ + } + +namespace detail +{ +template <typename> +struct Implementation +{ + using type = void; +}; +} // namespace detail + +template <typename Type> +using Implementation = typename detail::Implementation<Type>::type; + +#define SPECIALIZE_NODE_TYPE_TRAIT(TRAIT, ACTION, TYPE) \ + namespace detail \ + { \ + template <> \ + struct TRAIT<ACTION> \ + { \ + using type = TYPE; \ + }; \ + } + +#define SET_NODE_IMPLEMENTATION(ACTION, TYPE) SPECIALIZE_NODE_TYPE_TRAIT(Implementation, ACTION, TYPE) + +namespace detail +{ +template <typename> +struct Interface +{ + using type = void; +}; +} // namespace detail + +template <typename Type> +using Interface = typename detail::Interface<Type>::type; + +#define SET_NODE_INTERFACE(ACTION, TYPE) SPECIALIZE_NODE_TYPE_TRAIT(Interface, ACTION, TYPE) + +template <typename Instance> +class ActionNode : public yase::ActionNode +{ +public: + ActionNode(std::shared_ptr<Interface<Instance>> action) + : yase::ActionNode{std::string{identifier<Instance>}}, + action_{action} {} + +protected: + std::unique_ptr<Implementation<Instance>> impl_{nullptr}; + std::shared_ptr<Interface<Instance>> action_; +}; +} // namespace Node +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Storyboard/GenericAction/ActivateControllerAction.cpp b/engine/src/Storyboard/GenericAction/ActivateControllerAction.cpp index 646c8cfd..aa2ccf2a 100644 --- a/engine/src/Storyboard/GenericAction/ActivateControllerAction.cpp +++ b/engine/src/Storyboard/GenericAction/ActivateControllerAction.cpp @@ -15,12 +15,43 @@ #include <cassert> #include <utility> +#include "Storyboard/GenericAction/ActivateControllerAction.h" #include "Utils/EntityBroker.h" +#include "Utils/EntityUtils.h" #include "Utils/IControllerService.h" +#include "Utils/Logger.h" -namespace OpenScenarioEngine::v1_3::Node +namespace OpenScenarioEngine::v1_3 { +bool ActivateControllerAction::Step() +{ + const auto& entity_repository = mantle.environment->GetEntityRepository(); + + for (const auto& entity_name : values.entities) + { + // entities might have been despawned, leading to a dangling reference to the controllers + // so we check if the entity is still part of the simulation, before we access the controllers + if (auto entity_ref = entity_repository.Get(entity_name)) + { + const auto entity_id = entity_ref->get().GetUniqueId(); + if (const auto& entity_controllers = ose_services.controllerService->GetControllers(entity_id)) + { + auto controller_id = ose_services.controllerService->GetControllerId( + values.controllerRef, *entity_controllers); + ose_services.controllerService->ChangeState( + entity_id, controller_id, values.lateral_state, values.longitudinal_state); + } + else + { + Logger::Error("ActivateControllerAction: No user defined controller available for " + entity_name); + } + } + } + return true; +} +namespace Node +{ namespace detail { template <typename TargetState> @@ -34,13 +65,6 @@ auto convert_to(bool is_set, bool value) } } // namespace detail -ActivateControllerAction::ActivateControllerAction( - std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IActivateControllerAction> activateControllerAction) - : yase::ActionNode{"ActivateControllerAction"}, - activateControllerAction_{activateControllerAction} -{ -} - void ActivateControllerAction::onInit() {} yase::NodeStatus ActivateControllerAction::tick() @@ -57,30 +81,22 @@ void ActivateControllerAction::lookupAndRegisterData(yase::Blackboard& blackboar auto controllerService = blackboard.get<ControllerServicePtr>("ControllerService"); impl_ = std::make_unique<OpenScenarioEngine::v1_3::ActivateControllerAction>( - OpenScenarioEngine::v1_3::ActivateControllerAction::Values{ + Values<OpenScenarioEngine::v1_3::ActivateControllerAction>{ entityBroker->GetEntities(), - - activateControllerAction_->IsSetAnimation() ? std::make_optional<bool>(activateControllerAction_->GetAnimation()) : std::nullopt, - - ConvertScenarioControllerRef(activateControllerAction_->GetControllerRef()), - + action_->IsSetAnimation() ? std::make_optional<bool>(action_->GetAnimation()) : std::nullopt, + ConvertScenarioControllerRef(action_->GetControllerRef()), detail::convert_to<mantle_api::IController::LateralState>( - activateControllerAction_->IsSetLateral(), - activateControllerAction_->GetLateral()), - - activateControllerAction_->IsSetLighting() - ? std::make_optional<bool>(activateControllerAction_->GetLighting()) + action_->IsSetLateral(), + action_->GetLateral()), + action_->IsSetLighting() + ? std::make_optional<bool>(action_->GetLighting()) : std::nullopt, - detail::convert_to<mantle_api::IController::LongitudinalState>( - activateControllerAction_->IsSetLongitudinal(), - activateControllerAction_->GetLongitudinal()) - - }, // Values - OpenScenarioEngine::v1_3::ActivateControllerAction::Interfaces{ - environment}, - OpenScenarioEngine::v1_3::ActivateControllerAction::OseServices{ - controllerService}); + action_->IsSetLongitudinal(), + action_->GetLongitudinal()) // + }, + Interfaces<OpenScenarioEngine::v1_3::ActivateControllerAction>{environment}, + OseServices{controllerService}); } - -} // namespace OpenScenarioEngine::v1_3::Node +} // namespace Node +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Storyboard/GenericAction/ActivateControllerAction.h b/engine/src/Storyboard/GenericAction/ActivateControllerAction.h index 601a3d3b..a154b360 100644 --- a/engine/src/Storyboard/GenericAction/ActivateControllerAction.h +++ b/engine/src/Storyboard/GenericAction/ActivateControllerAction.h @@ -10,25 +10,65 @@ #pragma once -#include <agnostic_behavior_tree/action_node.h> -#include <openScenarioLib/generated/v1_3/api/ApiClassInterfacesV1_3.h> +#include <MantleAPI/Execution/i_environment.h> +#include <MantleAPI/Traffic/i_controller.h> -#include "Storyboard/GenericAction/ActivateControllerAction_impl.h" +#include "Action.h" +#include "Conversion/OscToMantle/ConvertScenarioController.h" +#include "Utils/EntityBroker.h" +#include "Utils/IControllerService.h" -namespace OpenScenarioEngine::v1_3::Node +namespace OpenScenarioEngine::v1_3 { -class ActivateControllerAction : public yase::ActionNode +struct OseServices +{ + ControllerServicePtr controllerService; +}; + +class ActivateControllerAction; + +template <> +struct Values<ActivateControllerAction> +{ + Entities entities; + std::optional<bool> animation; + std::optional<std::string> controllerRef; + mantle_api::IController::LateralState lateral_state; + std::optional<bool> lighting; + mantle_api::IController::LongitudinalState longitudinal_state; +}; + +class ActivateControllerAction : Action<ActivateControllerAction> { public: - ActivateControllerAction(std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IActivateControllerAction> activateControllerAction); + ActivateControllerAction(Values<ActivateControllerAction> values, Interfaces<ActivateControllerAction> interfaces, OseServices ose_services) + : Action<ActivateControllerAction>{std::move(values), std::move(interfaces)}, + ose_services{std::move(ose_services)} {} + + bool Step(); + +protected: + OseServices ose_services; +}; + +namespace Node +{ +class ActivateControllerAction; +SET_NODE_IDENTIFIER(ActivateControllerAction) +SET_NODE_IMPLEMENTATION(ActivateControllerAction, OpenScenarioEngine::v1_3::ActivateControllerAction) +SET_NODE_INTERFACE(ActivateControllerAction, NET_ASAM_OPENSCENARIO::v1_3::IActivateControllerAction) + +class ActivateControllerAction : public ActionNode<ActivateControllerAction> +{ +public: + using ActionNode<ActivateControllerAction>::ActionNode; + void onInit() override; private: yase::NodeStatus tick() override; - void lookupAndRegisterData(yase::Blackboard& blackboard) final; - std::unique_ptr<OpenScenarioEngine::v1_3::ActivateControllerAction> impl_{nullptr}; - std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IActivateControllerAction> activateControllerAction_; + void lookupAndRegisterData(yase::Blackboard& blackboard) final; }; - -} // namespace OpenScenarioEngine::v1_3::Node \ No newline at end of file +} // namespace Node +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Storyboard/GenericAction/ActivateControllerAction_base.h b/engine/src/Storyboard/GenericAction/ActivateControllerAction_base.h deleted file mode 100644 index bbdd2091..00000000 --- a/engine/src/Storyboard/GenericAction/ActivateControllerAction_base.h +++ /dev/null @@ -1,57 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021-2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ - -#pragma once - -#include <MantleAPI/Execution/i_environment.h> -#include <MantleAPI/Traffic/i_controller.h> - -#include "Conversion/OscToMantle/ConvertScenarioController.h" -#include "Utils/EntityBroker.h" -#include "Utils/IControllerService.h" - -namespace OpenScenarioEngine::v1_3 -{ -class ActivateControllerActionBase -{ -public: - struct Values - { - Entities entities; - std::optional<bool> animation; - std::optional<std::string> controllerRef; - mantle_api::IController::LateralState lateral_state; - std::optional<bool> lighting; - mantle_api::IController::LongitudinalState longitudinal_state; - }; - struct Interfaces - { - std::shared_ptr<mantle_api::IEnvironment> environment; - }; - - struct OseServices - { - ControllerServicePtr controllerService; - }; - - ActivateControllerActionBase(Values values, Interfaces interfaces, OseServices ose_services) - : values{std::move(values)}, - mantle{std::move(interfaces)}, - ose_services{std::move(ose_services)} {}; - virtual ~ActivateControllerActionBase() = default; - virtual bool Step() = 0; - -protected: - Values values; - Interfaces mantle; - OseServices ose_services; -}; - -} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Storyboard/GenericAction/ActivateControllerAction_impl.cpp b/engine/src/Storyboard/GenericAction/ActivateControllerAction_impl.cpp deleted file mode 100644 index 92d6399a..00000000 --- a/engine/src/Storyboard/GenericAction/ActivateControllerAction_impl.cpp +++ /dev/null @@ -1,47 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021-2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ - -#include "Storyboard/GenericAction/ActivateControllerAction_impl.h" - -#include "Utils/EntityUtils.h" -#include "Utils/Logger.h" - -namespace OpenScenarioEngine::v1_3 -{ - -bool ActivateControllerAction::Step() -{ - const auto& entity_repository = mantle.environment->GetEntityRepository(); - - for (const auto& entity_name : values.entities) - { - // entities might have been despawned, leading to a dangling reference to the controllers - // so we check if the entity is still part of the simulation, before we access the controllers - if (auto entity_ref = entity_repository.Get(entity_name)) - { - const auto entity_id = entity_ref->get().GetUniqueId(); - if (const auto& entity_controllers = ose_services.controllerService->GetControllers(entity_id)) - { - auto controller_id = ose_services.controllerService->GetControllerId( - values.controllerRef, *entity_controllers); - ose_services.controllerService->ChangeState( - entity_id, controller_id, values.lateral_state, values.longitudinal_state); - } - else - { - Logger::Error("ActivateControllerAction: No user defined controller available for " + entity_name); - } - } - } - - return true; -} - -} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Storyboard/GenericAction/ActivateControllerAction_impl.h b/engine/src/Storyboard/GenericAction/ActivateControllerAction_impl.h deleted file mode 100644 index 701d514f..00000000 --- a/engine/src/Storyboard/GenericAction/ActivateControllerAction_impl.h +++ /dev/null @@ -1,29 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021-2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ - -#pragma once - -#include "Storyboard/GenericAction/ActivateControllerAction_base.h" - -namespace OpenScenarioEngine::v1_3 -{ -class ActivateControllerAction : public ActivateControllerActionBase -{ -public: - ActivateControllerAction(Values values, Interfaces interfaces, OseServices ose_services) - : ActivateControllerActionBase{ - std::move(values), - std::move(interfaces), - std::move(ose_services)} {}; - - bool Step() override; -}; - -} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Storyboard/GenericAction/LightStateAction.cpp b/engine/src/Storyboard/GenericAction/LightStateAction.cpp new file mode 100644 index 00000000..7f225d0a --- /dev/null +++ b/engine/src/Storyboard/GenericAction/LightStateAction.cpp @@ -0,0 +1,65 @@ +/******************************************************************************** + * Copyright (c) 2021-2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#include "Storyboard/GenericAction/LightStateAction.h" + +#include <MantleAPI/Execution/i_environment.h> + +#include "Conversion/OscToMantle/ConvertScenarioLightState.h" +#include "Conversion/OscToMantle/ConvertScenarioLightType.h" +#include "Utils/EntityUtils.h" +#include "Utils/Logger.h" + +namespace OpenScenarioEngine::v1_3 +{ + +bool LightStateAction::Step() +{ + SetControlStrategy(); + return true; +} + +void LightStateAction::SetControlStrategy() +{ + control_strategy_->light_type = values.lightType; + control_strategy_->light_state = values.lightState; + for (const auto& actor : values.entities) + { + auto& entity = EntityUtils::GetEntityByName(mantle.environment, actor); + mantle.environment->UpdateControlStrategies( + entity.GetUniqueId(), + {control_strategy_}); + } +} +namespace Node +{ +yase::NodeStatus LightStateAction::tick() +{ + assert(impl_); + const auto is_finished = impl_->Step(); + return is_finished ? yase::NodeStatus::kSuccess : yase::NodeStatus::kRunning; +}; + +void LightStateAction::lookupAndRegisterData(yase::Blackboard& blackboard) +{ + std::shared_ptr<mantle_api::IEnvironment> environment = blackboard.get<std::shared_ptr<mantle_api::IEnvironment>>("Environment"); + const EntityBroker::Ptr entityBroker = blackboard.get<EntityBroker::Ptr>("EntityBroker"); + + impl_ = std::make_unique<OpenScenarioEngine::v1_3::LightStateAction>( + Values<OpenScenarioEngine::v1_3::LightStateAction>{ + entityBroker->GetEntities(), + action_->GetTransitionTime(), + ConvertScenarioLightType(action_->GetLightType()), + ConvertScenarioLightState(action_->GetLightState()) // + }, + Interfaces<OpenScenarioEngine::v1_3::LightStateAction>{environment}); +} +} // namespace Node +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Storyboard/GenericAction/LightStateAction.h b/engine/src/Storyboard/GenericAction/LightStateAction.h index 3311528a..982fb916 100644 --- a/engine/src/Storyboard/GenericAction/LightStateAction.h +++ b/engine/src/Storyboard/GenericAction/LightStateAction.h @@ -17,47 +17,60 @@ #include <cassert> #include <utility> -#include "Storyboard/GenericAction/LightStateAction_impl.h" +#include "Action.h" #include "Utils/EntityBroker.h" -namespace OpenScenarioEngine::v1_3::Node +namespace OpenScenarioEngine::v1_3 { -class LightStateAction : public yase::ActionNode +class LightStateAction; + +template <> +struct Values<LightStateAction> +{ + Entities entities; + double transitionTime; + mantle_api::LightType lightType; + mantle_api::LightState lightState; +}; + +template <> +struct Interfaces<LightStateAction> +{ + std::shared_ptr<mantle_api::IEnvironment> environment; +}; + +class LightStateAction : Action<LightStateAction> { public: - LightStateAction(std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ILightStateAction> lightStateAction) - : yase::ActionNode{"LightStateAction"}, - lightStateAction_{lightStateAction} - { - } + using Action<LightStateAction>::Action; - void onInit() override{}; + bool Step(); private: - yase::NodeStatus tick() override - { - assert(impl_); - const auto is_finished = impl_->Step(); - return is_finished ? yase::NodeStatus::kSuccess : yase::NodeStatus::kRunning; - }; - - void lookupAndRegisterData(yase::Blackboard& blackboard) final - { - std::shared_ptr<mantle_api::IEnvironment> environment = blackboard.get<std::shared_ptr<mantle_api::IEnvironment>>("Environment"); - const EntityBroker::Ptr entityBroker = blackboard.get<EntityBroker::Ptr>("EntityBroker"); - - impl_ = std::make_unique<OpenScenarioEngine::v1_3::LightStateAction>( - OpenScenarioEngine::v1_3::LightStateAction::Values{ - entityBroker->GetEntities(), - lightStateAction_->GetTransitionTime(), - ConvertScenarioLightType(lightStateAction_->GetLightType()), - ConvertScenarioLightState(lightStateAction_->GetLightState())}, - OpenScenarioEngine::v1_3::LightStateAction::Interfaces{ - environment}); - } - - std::unique_ptr<OpenScenarioEngine::v1_3::LightStateAction> impl_{nullptr}; - std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ILightStateAction> lightStateAction_; + void SetControlStrategy(); + + std::shared_ptr<mantle_api::VehicleLightStatesControlStrategy> control_strategy_{ + std::make_shared<mantle_api::VehicleLightStatesControlStrategy>()}; }; -} // namespace OpenScenarioEngine::v1_3::Node +namespace Node +{ +class LightStateAction; +SET_NODE_IDENTIFIER(LightStateAction) +SET_NODE_IMPLEMENTATION(LightStateAction, OpenScenarioEngine::v1_3::LightStateAction) +SET_NODE_INTERFACE(LightStateAction, NET_ASAM_OPENSCENARIO::v1_3::ILightStateAction) + +class LightStateAction : public ActionNode<LightStateAction> +{ +public: + using ActionNode<LightStateAction>::ActionNode; + + void onInit() override{}; + +private: + yase::NodeStatus tick() override; + + void lookupAndRegisterData(yase::Blackboard& blackboard) final; +}; +} // namespace Node +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Storyboard/GenericAction/LightStateAction_base.h b/engine/src/Storyboard/GenericAction/LightStateAction_base.h deleted file mode 100644 index efcd12a3..00000000 --- a/engine/src/Storyboard/GenericAction/LightStateAction_base.h +++ /dev/null @@ -1,48 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021-2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ - -#pragma once - -#include <MantleAPI/Execution/i_environment.h> - -#include "Conversion/OscToMantle/ConvertScenarioLightState.h" -#include "Conversion/OscToMantle/ConvertScenarioLightType.h" -#include "Utils/EntityBroker.h" - -namespace OpenScenarioEngine::v1_3 -{ - -class LightStateActionBase -{ -public: - struct Values - { - Entities entities; - double transitionTime; - mantle_api::LightType lightType; - mantle_api::LightState lightState; - }; - struct Interfaces - { - std::shared_ptr<mantle_api::IEnvironment> environment; - }; - - LightStateActionBase(Values values, Interfaces interfaces) - : values{std::move(values)}, - mantle{std::move(interfaces)} {}; - virtual ~LightStateActionBase() = default; - virtual bool Step() = 0; - -protected: - Values values; - Interfaces mantle; -}; - -} // namespace OpenScenarioEngine::v1_3 \ No newline at end of file diff --git a/engine/src/Storyboard/GenericAction/LightStateAction_impl.cpp b/engine/src/Storyboard/GenericAction/LightStateAction_impl.cpp deleted file mode 100644 index e993a670..00000000 --- a/engine/src/Storyboard/GenericAction/LightStateAction_impl.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021-2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ - -#include "Storyboard/GenericAction/LightStateAction_impl.h" - -#include "Utils/EntityUtils.h" -#include "Utils/Logger.h" - -namespace OpenScenarioEngine::v1_3 -{ - -bool LightStateAction::Step() -{ - SetControlStrategy(); - return true; -} - -void LightStateAction::SetControlStrategy() -{ - control_strategy_->light_type = values.lightType; - control_strategy_->light_state = values.lightState; - for (const auto& actor : values.entities) - { - auto& entity = EntityUtils::GetEntityByName(mantle.environment, actor); - mantle.environment->UpdateControlStrategies( - entity.GetUniqueId(), - {control_strategy_}); - } -} - -} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Storyboard/GenericAction/LightStateAction_impl.h b/engine/src/Storyboard/GenericAction/LightStateAction_impl.h deleted file mode 100644 index 2ea1c6fb..00000000 --- a/engine/src/Storyboard/GenericAction/LightStateAction_impl.h +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2021-2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ - -#pragma once - -#include "Storyboard/GenericAction/LightStateAction_base.h" - -namespace OpenScenarioEngine::v1_3 -{ - -class LightStateAction : public LightStateActionBase -{ -public: - LightStateAction(Values values, Interfaces interfaces) - : LightStateActionBase{std::move(values), std::move(interfaces)} {}; - - bool Step() override; - -private: - void SetControlStrategy(); - - std::shared_ptr<mantle_api::VehicleLightStatesControlStrategy> control_strategy_{ - std::make_shared<mantle_api::VehicleLightStatesControlStrategy>()}; -}; - -} // namespace OpenScenarioEngine::v1_3 \ No newline at end of file diff --git a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp new file mode 100644 index 00000000..1733fd88 --- /dev/null +++ b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp @@ -0,0 +1,147 @@ +/******************************************************************************** + * Copyright (c) 2021-2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#include "Storyboard/GenericAction/TrafficAreaAction.h" + +#include "Conversion/OscToMantle/ConvertScenarioTrafficArea.h" +#include "Utils/EntityCreator.h" +#include "Utils/Logger.h" +#include "Utils/Spawning/Length.h" +#include "Utils/Spawning/Transform.h" + +namespace OpenScenarioEngine::v1_3 +{ +void TrafficAreaAction::UpdateTrafficAreas() +{ + trafficAreas = Transform(values.trafficArea, [this](const mantle_api::RoadRange& range) + { return mantle.environment->GetTrafficAreaService().CreateTrafficArea(range); }); + lengths = Transform(trafficAreas, ToAccumulatedLength{}); +} + +bool TrafficAreaAction::Step() +{ + // Spawn entities in a randomly selected area among this action's defined traffic areas. + // Selection is weighted based on the length of each area's remaining spawnable area (SpawnZone). + + // Traffic distributions determine which vehicles can spawn and their likelihood of spawning. The distributions are sorted by + // entity length for efficient filtering in case the largest interval grows smaller than some spawnable entities. Similary, a + // SpawnZone tracks its largest interval (without sorting, as it would have to be reordered each time an entity is spawned). + + // The general process of the spawn loop must go as follows: + // - Filter out the entities of all traffic distributions longer than the largest interval of all areas. + // (can be skipped if the last spawned entity was not on the largest interval) + // - Sample the entity to be spawned based on the weights of all entity distributions + // - Filter out the intervals of all areas shorter than the entity to be spawned + // - Sample the spawn position from the total length of all remaining intervals + // - Shrink the sampled interval interval by the front and rear offset from the entity's center to its + // front and rear bounds, scaling the sampled value accordingly. + // - Spawn the entity and update the computed area intervals and lengths. + + if (values.numberOfEntities == 0) // No entities left to spawn + { + return true; + } + if (values.trafficDistributions.distributions.empty()) // No spawnable entities + { + return false; + } + if (trafficAreas.empty()) + { + UpdateTrafficAreas(); + if (trafficAreas.empty()) // No areas to spawn entities in + { + return false; + } + } + EntityCreator spawner{mantle.environment}; + + if (values.trafficDistributions.distributions.size() == 1) + { + auto& distributions{values.trafficDistributions.distributions.front()}; + if (distributions.entities.size() == 1) // Skip sampling entities if there is only one + { + if (trafficAreas.size() == 1) + { + SpawnZones zones{trafficAreas.front(), [threshold = GetLength(distributions.entities.front())](const Interval& interval) + { + return interval.GetLength() >= threshold; + }}; + while (values.numberOfEntities) + { + if (zones.empty()) + { // No room left + return false; + } + } + } + } + } + + if (trafficAreas.size() == 1) // Skip sampling areas if there is only one + { + auto& area{trafficAreas.front()}; + // TODO: Not yet implemented + while (values.numberOfEntities) + { + --values.numberOfEntities; + } + return true; + } + std::vector<SpawnZones> spawnZones{Transform(trafficAreas, ToSpawnZones{})}; + std::vector<units::length::meter_t> weights{Transform(spawnZones, ToAccumulatedLength{})}; + while (values.numberOfEntities) + { + auto [sampleA, indexA]{Sample(weights)}; + auto sampleB{sampleA - weights[indexA]}; + const SpawnZones& zones{spawnZones[indexA]}; + const auto upperBound{std::prev(std::upper_bound(std::next(zones.lengths.begin()), std::prev(zones.lengths.end()), sampleB))}; + const auto indexB{static_cast<size_t>(std::distance(zones.lengths.begin(), upperBound))}; + const SpawnZone& zone{zones[indexB]}; + auto sampleC{sampleB - zones.lengths[indexB]}; + + // TODO: WIP + // spawner.CreateEntity() + + --values.numberOfEntities; + } + // spawner.CreateEntity(values.trafficDistribution.entityDistribution); + // mantle.environment->GetEntityRepository().Create; + + // Note: + // - Access to values parse to mantle/ose datatypes: this->values.xxx + // - Access to mantle interfaces: this->mantle.xxx + return true; +} + +namespace Node +{ +void TrafficAreaAction::onInit(){}; + +yase::NodeStatus TrafficAreaAction::tick() +{ + assert(impl_); + const auto is_finished = impl_->Step(); + return is_finished ? yase::NodeStatus::kSuccess : yase::NodeStatus::kRunning; +}; + +void TrafficAreaAction::lookupAndRegisterData(yase::Blackboard& blackboard) +{ + std::shared_ptr<mantle_api::IEnvironment> environment = blackboard.get<std::shared_ptr<mantle_api::IEnvironment>>("Environment"); + + impl_ = std::make_unique<OpenScenarioEngine::v1_3::TrafficAreaAction>( + Values<OpenScenarioEngine::v1_3::TrafficAreaAction>{ + action_->GetContinuous(), + action_->GetNumberOfEntities(), + ConvertScenarioTrafficDistribution(action_->GetTrafficDistribution()), + ConvertScenarioTrafficArea(action_->GetTrafficArea())}, + Interfaces<OpenScenarioEngine::v1_3::TrafficAreaAction>{environment}); +} +} // namespace Node +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Storyboard/GenericAction/TrafficAreaAction.h b/engine/src/Storyboard/GenericAction/TrafficAreaAction.h new file mode 100644 index 00000000..ea72bbd6 --- /dev/null +++ b/engine/src/Storyboard/GenericAction/TrafficAreaAction.h @@ -0,0 +1,76 @@ +/******************************************************************************** + * Copyright (c) 2021-2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#pragma once + +#include <MantleAPI/Execution/i_environment.h> +#include <MantleAPI/Traffic/i_traffic_area_service.h> +#include <agnostic_behavior_tree/action_node.h> +#include <openScenarioLib/generated/v1_3/api/ApiClassInterfacesV1_3.h> +#include <units.h> + +#include <cassert> +#include <utility> +#include <vector> + +#include "Action.h" +#include "Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h" + +namespace OpenScenarioEngine::v1_3 +{ +class TrafficAreaAction; + +template <> +struct Values<TrafficAreaAction> +{ + bool continuous; + unsigned int numberOfEntities; + TrafficDistributions trafficDistributions; + std::vector<mantle_api::RoadRange> trafficArea; +}; + +class TrafficAreaAction : public Action<TrafficAreaAction> +{ +public: + using Action<TrafficAreaAction>::Action; + + bool Step(); + +private: + void UpdateTrafficAreas(); + + std::vector<mantle_api::TrafficArea> trafficAreas; + + /// The accumulated size of all traffic areas up to and including the index of the respective element. + /// Does not account for overlapping areas. + std::vector<units::length::meter_t> lengths; +}; + +namespace Node +{ +class TrafficAreaAction; +SET_NODE_IDENTIFIER(TrafficAreaAction) +SET_NODE_IMPLEMENTATION(TrafficAreaAction, OpenScenarioEngine::v1_3::TrafficAreaAction) +SET_NODE_INTERFACE(TrafficAreaAction, NET_ASAM_OPENSCENARIO::v1_3::ITrafficAreaAction) + +class TrafficAreaAction : public ActionNode<TrafficAreaAction> +{ +public: + using ActionNode<TrafficAreaAction>::ActionNode; + + void onInit() override; + +private: + yase::NodeStatus tick() override; + + void lookupAndRegisterData(yase::Blackboard& blackboard) final; +}; +} // namespace Node +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Common.h b/engine/src/Utils/Spawning/Common.h new file mode 100644 index 00000000..16a5dac1 --- /dev/null +++ b/engine/src/Utils/Spawning/Common.h @@ -0,0 +1,45 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#pragma once + +#include <units.h> + +#include <algorithm> +#include <memory> + +namespace OpenScenarioEngine::v1_3 +{ +template <typename T> +bool is_uninitialized(const std::weak_ptr<T>& pointer) +{ + return !pointer.owner_before(std::weak_ptr<T>{}) && !std::weak_ptr<T>{}.owner_before(pointer); +} + +/// Functor returning the specified value when invoked. +template <auto Value> +struct Return +{ + template <typename... Input> + constexpr decltype(Value) operator()(Input&&...) const + { + return Value; + } +}; + +template <typename Type> +std::pair<Type, size_t> Sample(const std::vector<Type>& range) +{ + const units::length::meter_t sample{range.back()}; // TODO: Use Stochastics + const auto upperBound{std::upper_bound(range.begin(), std::prev(range.end()), sample)}; + const auto index{static_cast<size_t>(std::distance(range.begin(), upperBound))}; + return {sample, index}; +} +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Interval.cpp b/engine/src/Utils/Spawning/Interval.cpp new file mode 100644 index 00000000..847e7ad3 --- /dev/null +++ b/engine/src/Utils/Spawning/Interval.cpp @@ -0,0 +1,27 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#include "Interval.h" + +#include <algorithm> + +#include "Common.h" + +namespace OpenScenarioEngine::v1_3 +{ +void OffsetIntervals(std::vector<Interval>& intervals, units::length::meter_t frontOffset, units::length::meter_t rearOffset) +{ + const auto shrinkage{frontOffset - rearOffset}; + intervals.erase(std::remove_if(intervals.begin(), intervals.end(), [shrinkage](const Interval& interval) + { return interval.GetLength() < shrinkage; }), + intervals.end()); + std::for_each(intervals.begin(), intervals.end(), OffsetBy{frontOffset, rearOffset}); +} +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Interval.h b/engine/src/Utils/Spawning/Interval.h new file mode 100644 index 00000000..755fb9d4 --- /dev/null +++ b/engine/src/Utils/Spawning/Interval.h @@ -0,0 +1,112 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#pragma once + +#include <MantleAPI/Traffic/i_traffic_area_stream.h> +#include <units.h> + +#include <vector> + +#include "Common.h" + +namespace OpenScenarioEngine::v1_3 +{ +struct Interval +{ + constexpr Interval(units::length::meter_t min, units::length::meter_t max) + : min{min}, max{max} {} + + constexpr units::length::meter_t GetLength() const; + + constexpr void OffsetBy(units::length::meter_t frontOffset, units::length::meter_t rearOffset); + + units::length::meter_t min, max; +}; + +struct OffsetBy +{ + constexpr void operator()(Interval&) const; + + units::length::meter_t frontOffset, rearOffset; +}; + +void OffsetIntervals(std::vector<Interval>& intervals, units::length::meter_t frontOffset, units::length::meter_t rearOffset); + +/// Returns all pairs of stream distances between which no entity is placed on the given stream +std::vector<Interval> GetFreeIntervals(const mantle_api::ITrafficAreaStream&); +} // namespace OpenScenarioEngine::v1_3 + +namespace OpenScenarioEngine::v1_3 +{ +constexpr units::length::meter_t Interval::GetLength() const +{ + return max - min; +} + +constexpr void Interval::OffsetBy(units::length::meter_t frontOffset, units::length::meter_t rearOffset) +{ + min += frontOffset; + max += rearOffset; +} + +constexpr void OffsetBy::operator()(Interval& interval) const +{ + interval.OffsetBy(frontOffset, rearOffset); +} + +template <typename Filter = Return<true>> +void EmplaceInterval(std::vector<Interval>& output, Interval&& input, Filter&& filter = {}) +{ + if constexpr (std::is_same_v<Filter, Return<true>>) + { + output.push_back(std::move(input)); + } + else + { + if (filter(input)) + { + output.push_back(std::move(input)); + } + } +} + +template <typename Filter> +std::vector<Interval> GetFreeIntervals(const mantle_api::ITrafficAreaStream& stream, Filter&& filter) +{ + // TODO: The interface of the ITrafficAreaStream does not provide an efficient way to compute free intervals. One must retrieve + // all entities on the stream, meaning the implementation must have computed their local positions, but they are not available + // through the interface. ITrafficAreaStream should be expanded to provide this information without redundant computation. + + std::vector<Interval> result; + units::length::meter_t min; + const units::length::meter_t max{stream.GetLength()}; + while (min < max) + { + std::weak_ptr<mantle_api::IEntity> pointer{stream.GetEntity(mantle_api::ITrafficAreaStream::SearchDirection::kDownstream, min, max - min)}; + if (is_uninitialized(pointer)) + { + EmplaceInterval(result, {min, max}, filter); + break; + } + auto entity{pointer.lock()}; + const auto length{entity->GetProperties()->bounding_box.dimension.length}; + const auto offset{entity->GetProperties()->bounding_box.geometric_center.x}; + auto [s, t]{stream.Convert(entity->GetPosition()).value()}; + if (const auto entityStartDistance{s + offset - length / units::dimensionless::scalar_t{2.0}}; entityStartDistance > min) + { + EmplaceInterval(result, {min, entityStartDistance}, filter); + } + min = s + offset + length / units::dimensionless::scalar_t{2.0}; + // TODO: This would loop endlessly if the object has a length of 0 and MIGHT loop endlessly if its offset lies outside of its bounds. + } + return result; +} +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Iterable.h b/engine/src/Utils/Spawning/Iterable.h new file mode 100644 index 00000000..1801f311 --- /dev/null +++ b/engine/src/Utils/Spawning/Iterable.h @@ -0,0 +1,131 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#pragma once + +#include <cstdint> + +namespace OpenScenarioEngine::v1_3 +{ +template <typename Container> +struct Iterable +{ + using const_iterator = typename Container::const_iterator; + using iterator = typename Container::iterator; + using const_reference = typename Container::const_reference; + using reference = typename Container::reference; + using value_type = typename Container::value_type; + + constexpr Iterable(Container&&); + + constexpr const Container& GetContainer() const; + constexpr Container& GetContainer(); + + constexpr operator const Container&() const; + constexpr operator Container&(); + + constexpr const_iterator begin() const; + constexpr iterator begin(); + + constexpr const_iterator end() const; + constexpr iterator end(); + + constexpr size_t size() const; + constexpr bool empty() const; + + const_reference operator[](size_t) const; + reference operator[](size_t); + +protected: + Container container; +}; +} // namespace OpenScenarioEngine::v1_3 + +namespace OpenScenarioEngine::v1_3 +{ + +template <typename Container> +constexpr Iterable<Container>::Iterable(Container&& input) + : container{std::forward<Container>(input)} +{ +} + +template <typename Container> +constexpr const Container& Iterable<Container>::GetContainer() const +{ + return (container); +} + +template <typename Container> +constexpr Container& Iterable<Container>::GetContainer() +{ + return (container); +} + +template <typename Container> +constexpr Iterable<Container>::operator const Container&() const +{ + return GetContainer(); +} + +template <typename Container> +constexpr Iterable<Container>::operator Container&() +{ + return GetContainer(); +} + +template <typename Container> +constexpr typename Container::const_iterator Iterable<Container>::begin() const +{ + return container.begin(); +} + +template <typename Container> +constexpr typename Container::iterator Iterable<Container>::begin() +{ + return container.begin(); +} + +template <typename Container> +constexpr typename Container::const_iterator Iterable<Container>::end() const +{ + return container.end(); +} + +template <typename Container> +constexpr typename Container::iterator Iterable<Container>::end() +{ + return container.end(); +} + +template <typename Container> +constexpr size_t Iterable<Container>::size() const +{ + return container.size(); +} + +template <typename Container> +constexpr bool Iterable<Container>::empty() const +{ + return container.empty(); +} + +template <typename Container> +typename Container::const_reference Iterable<Container>::operator[](size_t i) const +{ + return container[i]; +} + +template <typename Container> +typename Container::reference Iterable<Container>::operator[](size_t i) +{ + return container[i]; +} +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Length.cpp b/engine/src/Utils/Spawning/Length.cpp new file mode 100644 index 00000000..67c9aa5e --- /dev/null +++ b/engine/src/Utils/Spawning/Length.cpp @@ -0,0 +1,52 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#include "Length.h" + +#include <cassert> +#include <numeric> + +namespace OpenScenarioEngine::v1_3 +{ +units::length::meter_t ToLength::operator()(const mantle_api::ITrafficAreaStream& stream) const +{ + return stream.GetLength(); +} + +units::length::meter_t ToLength::operator()(const mantle_api::TrafficArea& area) const +{ + return std::transform_reduce(area.begin(), area.end(), units::length::meter_t{}, std::plus<>{}, *this); +} + +units::length::meter_t ToLength::operator()(const SpawnZone& zone) const +{ + assert(!zone.lengths.empty()); + return zone.lengths.back(); +} + +units::length::meter_t ToLength::operator()(const SpawnZones& zones) const +{ + assert(!zones.lengths.empty()); + return zones.lengths.back(); +} + +units::length::meter_t ToLength::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject& object) const +{ + if (decltype(auto) vehicle{object.GetVehicle()}; vehicle != nullptr) + { + return units::length::meter_t{vehicle->GetBoundingBox()->GetDimensions()->GetLength()}; + } + if (decltype(auto) pedestrian{object.GetPedestrian()}; pedestrian != nullptr) + { + return units::length::meter_t{pedestrian->GetBoundingBox()->GetDimensions()->GetLength()}; + } + return units::length::meter_t{std::numeric_limits<double>::quiet_NaN()}; +} +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Length.h b/engine/src/Utils/Spawning/Length.h new file mode 100644 index 00000000..2839ab18 --- /dev/null +++ b/engine/src/Utils/Spawning/Length.h @@ -0,0 +1,96 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#pragma once + +#include <MantleAPI/Traffic/i_traffic_area_service.h> +#include <MantleAPI/Traffic/i_traffic_area_stream.h> +#include <openScenarioLib/generated/v1_3/api/ApiClassInterfacesV1_3.h> +#include <units.h> + +#include <memory> + +#include "Interval.h" +#include "SpawnZone.h" +#include "Transform.h" + +namespace OpenScenarioEngine::v1_3 +{ +struct ToLength +{ + constexpr units::length::meter_t operator()(const Interval&) const; + + units::length::meter_t operator()(const mantle_api::ITrafficAreaStream&) const; + + template <typename Type> + units::length::meter_t operator()(const std::shared_ptr<Type>&) const; + + units::length::meter_t operator()(const mantle_api::TrafficArea&) const; + + units::length::meter_t operator()(const SpawnZone&) const; + + units::length::meter_t operator()(const SpawnZones&) const; + + units::length::meter_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject&) const; +}; + +template <typename Type> +units::length::meter_t GetLength(const Type&); + +struct ToAccumulatedLength +{ + template <typename Type> + units::length::meter_t operator()(const Type&); + + units::length::meter_t totalLength; +}; + +struct ToAccumulatedWeight +{ + template <typename Entry> + units::dimensionless::scalar_t operator()(const Entry&); + + units::dimensionless::scalar_t totalWeight; +}; +} // namespace OpenScenarioEngine::v1_3 + +namespace OpenScenarioEngine::v1_3 +{ +constexpr units::length::meter_t ToLength::operator()(const Interval& interval) const +{ + return interval.GetLength(); +} + +template <typename Type> +units::length::meter_t ToLength::operator()(const std::shared_ptr<Type>& input) const +{ + return (*this)(*input); +} + +template <typename Type> +units::length::meter_t GetLength(const Type& item) +{ + return ToLength{}(item); +} + +template <typename Type> +units::length::meter_t ToAccumulatedLength::operator()(const Type& input) +{ + totalLength += GetLength(input); + return totalLength; +} + +template <typename Entry> +units::dimensionless::scalar_t ToAccumulatedWeight::operator()(const Entry& entry) +{ + totalWeight += units::dimensionless::scalar_t{entry.GetWeight()}; + return totalWeight; +} +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/SpawnZone.cpp b/engine/src/Utils/Spawning/SpawnZone.cpp new file mode 100644 index 00000000..88a4e93e --- /dev/null +++ b/engine/src/Utils/Spawning/SpawnZone.cpp @@ -0,0 +1,104 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#include "SpawnZone.h" + +#include <algorithm> +#include <cassert> + +#include "Length.h" + +namespace OpenScenarioEngine::v1_3 +{ +SpawnZone::SpawnZone(std::vector<Interval> input) + : Iterable<std::vector<Interval>>{std::move(input)}, lengths{units::length::meter_t{}} +{ + Transform(GetContainer(), lengths, ToAccumulatedLength{}); +} + +void SpawnZone::UpdateIndexToLongest() +{ + indexToLongest = std::distance(begin(), std::max_element(begin(), end(), [](const auto& a, const auto& b) + { return a.GetLength() < b.GetLength(); })); +} + +void SpawnZone::RemoveInterval(Interval interval) +{ + auto candidate{std::upper_bound(begin(), end(), interval.max, [](units::length::meter_t threshold, const Interval& entry) + { return threshold <= entry.max; })}; + assert(candidate != end()); + assert(candidate->min <= interval.min); + assert(candidate->max >= interval.max); + const auto offset{std::distance(begin(), candidate)}; + if (candidate->min == interval.min) + { + if (candidate->max == interval.max) + { + lengths.erase(std::next(lengths.begin(), offset + 1)); + GetContainer().erase(candidate); + if (indexToLongest == offset) + { + UpdateIndexToLongest(); + } + else if (indexToLongest > offset + 1) + { + --indexToLongest; + } + } + else + { + candidate->min = interval.max; + if (indexToLongest == offset) + { + UpdateIndexToLongest(); + } + } + std::transform(std::next(begin(), offset), + end(), + std::next(lengths.begin(), offset), + ToAccumulatedLength{offset ? *std::next(lengths.begin(), offset - 1) : units::length::meter_t{}}); + return; + } + else if (candidate->max == interval.max) + { + candidate->max = interval.min; + if (indexToLongest == offset) + { + indexToLongest = std::distance(lengths.begin(), std::max_element(lengths.begin(), lengths.end())); + } + std::transform(std::next(begin(), offset), + end(), + std::next(lengths.begin(), offset), + ToAccumulatedLength{offset ? *std::next(lengths.begin(), offset - 1) : units::length::meter_t{}}); + return; + } + GetContainer().emplace(std::next(candidate), interval.max, candidate->max); + lengths.push_back(units::length::meter_t{}); + std::next(begin(), offset)->max = interval.min; + std::transform(std::next(begin(), offset), + end(), + std::next(lengths.begin(), offset), + ToAccumulatedLength{offset ? *std::next(lengths.begin(), offset - 1) : units::length::meter_t{}}); +} + +SpawnZones::SpawnZones(std::vector<SpawnZone> input) + : Iterable<std::vector<SpawnZone>>{std::move(input)}, lengths{units::length::meter_t{}} +{ + Transform(GetContainer(), lengths, ToAccumulatedLength{}); +} +} // namespace OpenScenarioEngine::v1_3 + +namespace OpenScenarioEngine::v1_3 +{ +SpawnZones ToSpawnZones::operator()(const mantle_api::TrafficArea& area) const +{ + return {area}; +} +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/SpawnZone.h b/engine/src/Utils/Spawning/SpawnZone.h new file mode 100644 index 00000000..92389b28 --- /dev/null +++ b/engine/src/Utils/Spawning/SpawnZone.h @@ -0,0 +1,97 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#pragma once + +#include <MantleAPI/Traffic/i_traffic_area_service.h> +#include <MantleAPI/Traffic/i_traffic_area_stream.h> +#include <units.h> + +#include <vector> + +#include "Common.h" +#include "Interval.h" +#include "Iterable.h" + +namespace OpenScenarioEngine::v1_3 +{ +struct SpawnZone : Iterable<std::vector<Interval>> +{ + SpawnZone(std::vector<Interval>); + + template <typename Filter = Return<true>> + SpawnZone(const mantle_api::ITrafficAreaStream&, Filter&&); + + /// Removes the given interval from this spawn zone and updates the interval lengths and index to the longest interval if necessary + /// + /// \param Interval Interval guaranteed to be a subset of an interval of this zone + void RemoveInterval(Interval); + + void UpdateIndexToLongest(); + + std::vector<units::length::meter_t> lengths; + std::ptrdiff_t indexToLongest; +}; + +template <typename Filter = Return<true>> +struct ToSpawnZone +{ + SpawnZone operator()(const mantle_api::ITrafficAreaStream&) const; + + SpawnZone operator()(const std::shared_ptr<mantle_api::ITrafficAreaStream>&) const; + + Filter filter; +}; + +template <typename Filter> +ToSpawnZone(Filter&&) -> ToSpawnZone<Filter>; + +struct SpawnZones : Iterable<std::vector<SpawnZone>> +{ + SpawnZones(std::vector<SpawnZone>); + + template <typename Filter = Return<true>> + SpawnZones(const mantle_api::TrafficArea&, Filter&& = {}); + + std::vector<units::length::meter_t> lengths; +}; + +struct ToSpawnZones +{ + SpawnZones operator()(const mantle_api::TrafficArea&) const; +}; +} // namespace OpenScenarioEngine::v1_3 + +namespace OpenScenarioEngine::v1_3 +{ +template <typename Filter> +SpawnZone ToSpawnZone<Filter>::operator()(const mantle_api::ITrafficAreaStream& stream) const +{ + return {stream, filter}; +} + +template <typename Filter> +SpawnZone ToSpawnZone<Filter>::operator()(const std::shared_ptr<mantle_api::ITrafficAreaStream>& stream) const +{ + return (*this)(*stream); +} + +template <typename Filter> +SpawnZone::SpawnZone(const mantle_api::ITrafficAreaStream& stream, Filter&& filter) + : SpawnZone{GetFreeIntervals(stream, std::forward<Filter>(filter))} +{ +} + +template <typename Filter> +SpawnZones::SpawnZones(const mantle_api::TrafficArea& area, Filter&& filter) + : SpawnZones{Transform(area, ToSpawnZone{std::forward<Filter>(filter)})} +{ +} +} // namespace OpenScenarioEngine::v1_3 \ No newline at end of file diff --git a/engine/src/Utils/Spawning/Transform.h b/engine/src/Utils/Spawning/Transform.h new file mode 100644 index 00000000..072c6733 --- /dev/null +++ b/engine/src/Utils/Spawning/Transform.h @@ -0,0 +1,49 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#pragma once + +#include <algorithm> +#include <memory> +#include <type_traits> +#include <vector> + +namespace OpenScenarioEngine::v1_3 +{ +template <typename Invocable, typename Type> +std::vector<std::invoke_result_t<Invocable, const Type&>> Transform(const std::vector<Type>& input, Invocable&& invocable) +{ + std::vector<std::invoke_result_t<Invocable, const Type&>> result; + result.reserve(input.size()); + std::transform(input.begin(), input.end(), std::back_inserter(result), std::forward<Invocable>(invocable)); + return result; +} + +template <typename Invocable, typename Type> +void Transform(const std::vector<Type>& input, std::vector<std::invoke_result_t<Invocable, const Type&>>& output, Invocable&& invocable) +{ + output.reserve(output.size() + input.size()); + std::transform(input.begin(), input.end(), std::back_inserter(output), std::forward<Invocable>(invocable)); +} + +template <typename Type, typename Invocable> +std::vector<std::invoke_result_t<Invocable, const Type&>> TransformSharedPointers( + const std::vector<std::shared_ptr<Type>>& items, Invocable&& invocable) +{ + std::vector<std::invoke_result_t<Invocable, const Type&>> result; + result.reserve(items.size()); + std::transform(items.begin(), + items.end(), + std::back_inserter(result), + [&](const std::shared_ptr<Type>& item) + { return invocable(*item); }); + return result; +} +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/tests/Storyboard/GenericAction/ActivateControllerActionTest.cpp b/engine/tests/Storyboard/GenericAction/ActivateControllerActionTest.cpp index d8f36438..389313cc 100644 --- a/engine/tests/Storyboard/GenericAction/ActivateControllerActionTest.cpp +++ b/engine/tests/Storyboard/GenericAction/ActivateControllerActionTest.cpp @@ -14,7 +14,7 @@ #include <gtest/gtest.h> #include "MantleAPI/Common/i_identifiable.h" -#include "Storyboard/GenericAction/ActivateControllerAction_impl.h" +#include "Storyboard/GenericAction/ActivateControllerAction.h" #include "TestUtils/MockControllerService.h" #include "gmock/gmock.h" -- GitLab From d54bd0c352e40c147b22d5f407c7da9c6f497b2c Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Mon, 23 Dec 2024 17:38:47 +0100 Subject: [PATCH 05/26] Simplify TrafficAreaAction::Step --- .../ConvertScenarioTrafficDistribution.h | 4 +- .../GenericAction/TrafficAreaAction.cpp | 38 ++++--------------- engine/src/Utils/Spawning/Iterable.h | 37 ++++++++++++++++-- 3 files changed, 42 insertions(+), 37 deletions(-) diff --git a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h index ae7421b7..50e4160a 100644 --- a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h +++ b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h @@ -16,6 +16,7 @@ #include <memory> #include <vector> +#include "Utils/Spawning/Iterable.h" #include "Utils/Spawning/Length.h" namespace OpenScenarioEngine::v1_3 @@ -26,9 +27,8 @@ struct TrafficDistribution units::dimensionless::scalar_t weight; }; -struct TrafficDistributions +struct TrafficDistributions : Iterable<std::vector<TrafficDistribution>> { - std::vector<TrafficDistribution> distributions; std::vector<units::dimensionless::scalar_t> weights; }; diff --git a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp index 1733fd88..f7b4118e 100644 --- a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp +++ b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp @@ -48,7 +48,7 @@ bool TrafficAreaAction::Step() { return true; } - if (values.trafficDistributions.distributions.empty()) // No spawnable entities + if (values.trafficDistributions.empty()) // No spawnable entities { return false; } @@ -62,38 +62,17 @@ bool TrafficAreaAction::Step() } EntityCreator spawner{mantle.environment}; - if (values.trafficDistributions.distributions.size() == 1) + // SpawnSpace spawnSpace{values.trafficDistrbutions, trafficAreas}; + while (values.numberOfEntities) { - auto& distributions{values.trafficDistributions.distributions.front()}; - if (distributions.entities.size() == 1) // Skip sampling entities if there is only one + if (/*!spawnSpace.Spawn()*/) { - if (trafficAreas.size() == 1) - { - SpawnZones zones{trafficAreas.front(), [threshold = GetLength(distributions.entities.front())](const Interval& interval) - { - return interval.GetLength() >= threshold; - }}; - while (values.numberOfEntities) - { - if (zones.empty()) - { // No room left - return false; - } - } - } + return false; } + --values.numberOfEntities; } + return true; - if (trafficAreas.size() == 1) // Skip sampling areas if there is only one - { - auto& area{trafficAreas.front()}; - // TODO: Not yet implemented - while (values.numberOfEntities) - { - --values.numberOfEntities; - } - return true; - } std::vector<SpawnZones> spawnZones{Transform(trafficAreas, ToSpawnZones{})}; std::vector<units::length::meter_t> weights{Transform(spawnZones, ToAccumulatedLength{})}; while (values.numberOfEntities) @@ -114,9 +93,6 @@ bool TrafficAreaAction::Step() // spawner.CreateEntity(values.trafficDistribution.entityDistribution); // mantle.environment->GetEntityRepository().Create; - // Note: - // - Access to values parse to mantle/ose datatypes: this->values.xxx - // - Access to mantle interfaces: this->mantle.xxx return true; } diff --git a/engine/src/Utils/Spawning/Iterable.h b/engine/src/Utils/Spawning/Iterable.h index 1801f311..aadc9e88 100644 --- a/engine/src/Utils/Spawning/Iterable.h +++ b/engine/src/Utils/Spawning/Iterable.h @@ -40,8 +40,14 @@ struct Iterable constexpr size_t size() const; constexpr bool empty() const; - const_reference operator[](size_t) const; - reference operator[](size_t); + constexpr const_reference front() const; + constexpr reference front(); + + constexpr const_reference back() const; + constexpr reference back(); + + constexpr const_reference operator[](size_t) const; + constexpr reference operator[](size_t); protected: Container container; @@ -118,13 +124,36 @@ constexpr bool Iterable<Container>::empty() const } template <typename Container> -typename Container::const_reference Iterable<Container>::operator[](size_t i) const +constexpr typename Container::const_reference Iterable<Container>::front() const +{ + return container.front(); +} +template <typename Container> +constexpr typename Container::reference Iterable<Container>::front() +{ + return container.front(); +} + +template <typename Container> +constexpr typename Container::const_reference Iterable<Container>::back() const +{ + return container.back(); +} + +template <typename Container> +constexpr typename Container::reference Iterable<Container>::back() +{ + return container.back(); +} + +template <typename Container> +constexpr typename Container::const_reference Iterable<Container>::operator[](size_t i) const { return container[i]; } template <typename Container> -typename Container::reference Iterable<Container>::operator[](size_t i) +constexpr typename Container::reference Iterable<Container>::operator[](size_t i) { return container[i]; } -- GitLab From 928311ee1308c5bd6996e9418378c79e30d7ad7c Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Thu, 26 Dec 2024 12:57:08 +0100 Subject: [PATCH 06/26] Introduce SpawnSpace for TrafficAreaAction --- .../GenericAction/TrafficAreaAction.cpp | 2 +- engine/src/Utils/Spawning/SpawnSpace.cpp | 24 +++++++ engine/src/Utils/Spawning/SpawnSpace.h | 62 +++++++++++++++++++ 3 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 engine/src/Utils/Spawning/SpawnSpace.cpp create mode 100644 engine/src/Utils/Spawning/SpawnSpace.h diff --git a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp index f7b4118e..f68775c0 100644 --- a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp +++ b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp @@ -65,7 +65,7 @@ bool TrafficAreaAction::Step() // SpawnSpace spawnSpace{values.trafficDistrbutions, trafficAreas}; while (values.numberOfEntities) { - if (/*!spawnSpace.Spawn()*/) + if (true /*!spawnSpace.Spawn(...)*/) { return false; } diff --git a/engine/src/Utils/Spawning/SpawnSpace.cpp b/engine/src/Utils/Spawning/SpawnSpace.cpp new file mode 100644 index 00000000..ca1490c4 --- /dev/null +++ b/engine/src/Utils/Spawning/SpawnSpace.cpp @@ -0,0 +1,24 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#include "SpawnSpace.h" + +#include <cassert> + +namespace OpenScenarioEngine::v1_3 +{ +std::pair<SpawnZones &, units::length::meter_t> SpawnSpace::GetZonesAndOffset(units::length::meter_t value) +{ + assert(weights.size() >= 2); + const auto offset{std::prev(std::upper_bound(std::next(weights.begin()), std::prev(weights.end()), value))}; + const auto index{std::distance(weights.begin(), offset)}; + return {container[static_cast<size_t>(index)], *offset}; +} +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/SpawnSpace.h b/engine/src/Utils/Spawning/SpawnSpace.h new file mode 100644 index 00000000..88cec5d4 --- /dev/null +++ b/engine/src/Utils/Spawning/SpawnSpace.h @@ -0,0 +1,62 @@ +/******************************************************************************** + * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#pragma once + +#include <utility> +#include <vector> + +#include "Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h" +#include "Iterable.h" +#include "SpawnZone.h" + +namespace OpenScenarioEngine::v1_3 +{ +struct SpawnSpace : Iterable<std::vector<SpawnZones>> +{ + /// Tries to spawn an entity and returns whether spawning was successful. + /// If successful, also adjusts the weighting of the spawn space. + /// + /// \tparam EntityCreator Provides CreateEntity() + /// \tparam Sampler Invocable with signature double(double value) returning a number within [0, value) + template <typename EntityCreator, typename Sampler> + bool Spawn(EntityCreator &&, Sampler &&); + + // Returns the spawn zones and weight offset containing the given sample + std::pair<SpawnZones &, units::length::meter_t> GetZonesAndOffset(units::length::meter_t); + + TrafficDistributions distributions; + std::vector<units::length::meter_t> weights; +}; +} // namespace OpenScenarioEngine::v1_3 + +namespace OpenScenarioEngine::v1_3 +{ + +template <typename EntityCreator, typename Sampler> +bool SpawnSpace::Spawn(EntityCreator &&spawner, Sampler &&sampler) +{ + if (empty() || distributions.empty()) + { + return false; + } + if (distribution.size() == 1) + { + return Spawn(distributions.front(), std::forward<EntityCreator>(spawner), std::forward<Sample>(sampler)); + } + if (size() == 1) + { + return front().Spawn(distributions, std::forward<EntityCreator>(spawner), std::forward<Sampler>(sampler)); + } + const units::length::meter_t sample{sampler(weights.back())}; + auto [zones, offset]{GetZonesAndOffset(sample)}; + return zones.Spawn(sample - offset, distributions, std::forward<EntityCreator>(spawner), std::forward<Sampler>(sampler)); +} +} // namespace OpenScenarioEngine::v1_3 -- GitLab From 0e861f2f46324084d875852a09121cf74a557329 Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Wed, 15 Jan 2025 20:50:18 +0100 Subject: [PATCH 07/26] Add stochastics & revamp spawning --- engine/CMakeLists.txt | 16 ++- engine/cmake/generated_files.cmake | 4 + .../ConvertScenarioTrafficArea.cpp | 2 + .../ConvertScenarioTrafficDistribution.cpp | 1 + .../ConvertScenarioTrafficDistribution.h | 6 +- .../GenericAction/TrafficAreaAction.cpp | 32 ++---- .../GenericAction/TrafficAreaAction.h | 1 + engine/src/Utils/Spawning/Common.h | 58 +++++++++- engine/src/Utils/Spawning/Interval.cpp | 21 +++- engine/src/Utils/Spawning/Interval.h | 50 +++++++- engine/src/Utils/Spawning/Iterable.h | 83 +++++++++++--- engine/src/Utils/Spawning/Length.cpp | 11 +- engine/src/Utils/Spawning/Length.h | 50 +++++++- engine/src/Utils/Spawning/LinkedInterval.cpp | 34 ++++++ engine/src/Utils/Spawning/LinkedInterval.h | 36 ++++++ engine/src/Utils/Spawning/Obstacle.cpp | 54 +++++++++ engine/src/Utils/Spawning/Obstacle.h | 50 ++++++++ engine/src/Utils/Spawning/SpawnSpace.cpp | 81 ++++++++++++- engine/src/Utils/Spawning/SpawnSpace.h | 107 ++++++++++++++---- engine/src/Utils/Spawning/SpawnSpot.h | 39 +++++++ engine/src/Utils/Spawning/SpawnZone.cpp | 82 +++----------- engine/src/Utils/Spawning/SpawnZone.h | 79 ++++++------- engine/src/Utils/Spawning/VelocityRange.cpp | 54 +++++++++ engine/src/Utils/Spawning/VelocityRange.h | 78 +++++++++++++ engine/src/Utils/Spawning/Weight.h | 92 +++++++++++++++ 25 files changed, 919 insertions(+), 202 deletions(-) create mode 100644 engine/src/Utils/Spawning/LinkedInterval.cpp create mode 100644 engine/src/Utils/Spawning/LinkedInterval.h create mode 100644 engine/src/Utils/Spawning/Obstacle.cpp create mode 100644 engine/src/Utils/Spawning/Obstacle.h create mode 100644 engine/src/Utils/Spawning/SpawnSpot.h create mode 100644 engine/src/Utils/Spawning/VelocityRange.cpp create mode 100644 engine/src/Utils/Spawning/VelocityRange.h create mode 100644 engine/src/Utils/Spawning/Weight.h diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 2e937ef7..fed70383 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -55,6 +55,12 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) find_package(OpenScenarioAPI REQUIRED MODULE) find_package(Antlr4Runtime REQUIRED MODULE) +include(FetchContent) +FetchContent_Declare(Stochastics + URL https://gitlab.eclipse.org/eclipse/openpass/stochastics-library/-/archive/main/stochastics-library-main.zip + EXCLUDE_FROM_ALL) +FetchContent_MakeAvailable(Stochastics) + include(cmake/generated_files.cmake) target_sources( @@ -70,12 +76,12 @@ target_include_directories( $<BUILD_INTERFACE:${CMAKE_CURRENT_LIST_DIR}/include> ) -target_link_libraries(${PROJECT_NAME} PUBLIC openscenario_api::shared - antlr4_runtime::shared - Yase::agnostic_behavior_tree - units::units +target_link_libraries(${PROJECT_NAME} PUBLIC antlr4_runtime::shared MantleAPI::MantleAPI - PRIVATE Stochastics::Stochastics) + openscenario_api::shared + units::units + Yase::agnostic_behavior_tree + PRIVATE Stochastics) if(USE_CCACHE) find_program(CCACHE_FOUND ccache) diff --git a/engine/cmake/generated_files.cmake b/engine/cmake/generated_files.cmake index 7a4e54a1..42b52e31 100644 --- a/engine/cmake/generated_files.cmake +++ b/engine/cmake/generated_files.cmake @@ -234,7 +234,11 @@ list(APPEND ${PROJECT_NAME}_SOURCES src/Storyboard/MotionControlAction/SpeedAction_impl.cpp src/Utils/Spawning/Interval.cpp src/Utils/Spawning/Length.cpp + src/Utils/Spawning/LinkedInterval.cpp + src/Utils/Spawning/Obstacle.cpp + src/Utils/Spawning/SpawnSpace.cpp src/Utils/Spawning/SpawnZone.cpp + src/Utils/Spawning/VelocityRange.cpp src/Utils/ConditionEdgeEvaluator.cpp src/Utils/ControllerCreator.cpp src/Utils/ControllerService.cpp diff --git a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficArea.cpp b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficArea.cpp index 9828f445..6a22f0ef 100644 --- a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficArea.cpp +++ b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficArea.cpp @@ -35,6 +35,8 @@ struct ToRoadRange { mantle_api::RoadRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IRoadRange& range) const { + // An IRoadRange always has a length, but a mantle_api::RoadRange can have a std::nullopt length. Thus, if + // the IRoadRange has a length 0 despite having more than one cursor, treat that length as representing std::nullopt: return {(range.GetRoadCursorSize() > 1 && range.GetLength() == .0) ? std::nullopt : std::optional<units::length::meter_t>{range.GetLength()}, TransformSharedPointers(range.GetRoadCursor(), ToRoadCursor{})}; } diff --git a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp index 04461351..ae0eb73c 100644 --- a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp +++ b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp @@ -10,6 +10,7 @@ #include "ConvertScenarioTrafficDistribution.h" +#include "Utils/Spawning/Length.h" #include "Utils/Spawning/Transform.h" namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h index 50e4160a..5442081e 100644 --- a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h +++ b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024-2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -17,13 +17,11 @@ #include <vector> #include "Utils/Spawning/Iterable.h" -#include "Utils/Spawning/Length.h" namespace OpenScenarioEngine::v1_3 { -struct TrafficDistribution +struct TrafficDistribution : Iterable<std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject>>> { - std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject>> entities; units::dimensionless::scalar_t weight; }; diff --git a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp index f68775c0..6777a273 100644 --- a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp +++ b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp @@ -10,10 +10,13 @@ #include "Storyboard/GenericAction/TrafficAreaAction.h" +#include <Stochastics/Stochastics.h> + #include "Conversion/OscToMantle/ConvertScenarioTrafficArea.h" #include "Utils/EntityCreator.h" #include "Utils/Logger.h" #include "Utils/Spawning/Length.h" +#include "Utils/Spawning/SpawnSpace.h" #include "Utils/Spawning/Transform.h" namespace OpenScenarioEngine::v1_3 @@ -61,39 +64,18 @@ bool TrafficAreaAction::Step() } } EntityCreator spawner{mantle.environment}; - - // SpawnSpace spawnSpace{values.trafficDistrbutions, trafficAreas}; + std::vector<SpawnZones> spawnZones{Transform(trafficAreas, ToSpawnZones{})}; + SpawnSpace spawnSpace{values.trafficDistributions, spawnZones}; + Stochastics rng; while (values.numberOfEntities) { - if (true /*!spawnSpace.Spawn(...)*/) + if (!spawnSpace.Spawn(spawner, rng)) { return false; } --values.numberOfEntities; } return true; - - std::vector<SpawnZones> spawnZones{Transform(trafficAreas, ToSpawnZones{})}; - std::vector<units::length::meter_t> weights{Transform(spawnZones, ToAccumulatedLength{})}; - while (values.numberOfEntities) - { - auto [sampleA, indexA]{Sample(weights)}; - auto sampleB{sampleA - weights[indexA]}; - const SpawnZones& zones{spawnZones[indexA]}; - const auto upperBound{std::prev(std::upper_bound(std::next(zones.lengths.begin()), std::prev(zones.lengths.end()), sampleB))}; - const auto indexB{static_cast<size_t>(std::distance(zones.lengths.begin(), upperBound))}; - const SpawnZone& zone{zones[indexB]}; - auto sampleC{sampleB - zones.lengths[indexB]}; - - // TODO: WIP - // spawner.CreateEntity() - - --values.numberOfEntities; - } - // spawner.CreateEntity(values.trafficDistribution.entityDistribution); - // mantle.environment->GetEntityRepository().Create; - - return true; } namespace Node diff --git a/engine/src/Storyboard/GenericAction/TrafficAreaAction.h b/engine/src/Storyboard/GenericAction/TrafficAreaAction.h index ea72bbd6..1e63a341 100644 --- a/engine/src/Storyboard/GenericAction/TrafficAreaAction.h +++ b/engine/src/Storyboard/GenericAction/TrafficAreaAction.h @@ -12,6 +12,7 @@ #include <MantleAPI/Execution/i_environment.h> #include <MantleAPI/Traffic/i_traffic_area_service.h> +#include <Stochastics/StochasticsInterface.h> #include <agnostic_behavior_tree/action_node.h> #include <openScenarioLib/generated/v1_3/api/ApiClassInterfacesV1_3.h> #include <units.h> diff --git a/engine/src/Utils/Spawning/Common.h b/engine/src/Utils/Spawning/Common.h index 16a5dac1..3d16791a 100644 --- a/engine/src/Utils/Spawning/Common.h +++ b/engine/src/Utils/Spawning/Common.h @@ -13,7 +13,9 @@ #include <units.h> #include <algorithm> +#include <functional> #include <memory> +#include <type_traits> namespace OpenScenarioEngine::v1_3 { @@ -34,12 +36,56 @@ struct Return } }; -template <typename Type> -std::pair<Type, size_t> Sample(const std::vector<Type>& range) +/// Functor returning the input as-is. +struct Forward { - const units::length::meter_t sample{range.back()}; // TODO: Use Stochastics - const auto upperBound{std::upper_bound(range.begin(), std::prev(range.end()), sample)}; - const auto index{static_cast<size_t>(std::distance(range.begin(), upperBound))}; - return {sample, index}; + template <typename Input> + constexpr decltype(auto) operator()(Input&& input) const + { + return std::forward<Input>(input); + } +}; + +template <typename Operation, typename Transformer = Forward> +struct BinaryOperation +{ + using is_transparent = bool; + + template <typename A, typename B> + constexpr decltype(auto) operator()(A&&, B&&) const; +}; + +template <typename Transform> +using Less = BinaryOperation<std::less<>, Transform>; + +template <typename Transform> +using Greater = BinaryOperation<std::greater<>, Transform>; + +template <typename Transform> +using Add = BinaryOperation<std::plus<>, Transform>; + +template <typename Operation, typename Transformer> +template <typename A, typename B> +constexpr decltype(auto) BinaryOperation<Operation, Transformer>::operator()(A&& a, B&& b) const +{ + static_assert(std::is_invocable_v<Transformer, A&&> && std::is_invocable_v<Transformer, B&&>); + return Operation{}(Transformer{}(std::forward<A>(a)), Transformer{}(std::forward<B>(b))); +} + +template <typename Elements, typename Thresholds> +typename Elements::const_reference Sample(const Elements& elements, const Thresholds& thresholds, typename Thresholds::const_reference value) +{ + assert(elements.size() == thresholds.size()); + const auto threshold{std::upper_bound(std::next(thresholds.begin()), thresholds.end(), value)}; + const size_t index{static_cast<size_t>(std::distance(thresholds.begin(), threshold) - 1)}; + return elements[index]; } +// template <typename Type> +// std::pair<Type, size_t> Sample(const std::vector<Type>& range) +// { +// const units::length::meter_t sample{range.back()}; // TODO: Use Stochastics +// const auto upperBound{std::upper_bound(range.begin(), std::prev(range.end()), sample)}; +// const auto index{static_cast<size_t>(std::distance(range.begin(), upperBound))}; +// return {sample, index}; +// } } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Interval.cpp b/engine/src/Utils/Spawning/Interval.cpp index 847e7ad3..d35e5077 100644 --- a/engine/src/Utils/Spawning/Interval.cpp +++ b/engine/src/Utils/Spawning/Interval.cpp @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024-2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -11,6 +11,7 @@ #include "Interval.h" #include <algorithm> +#include <cassert> #include "Common.h" @@ -24,4 +25,22 @@ void OffsetIntervals(std::vector<Interval>& intervals, units::length::meter_t fr intervals.end()); std::for_each(intervals.begin(), intervals.end(), OffsetBy{frontOffset, rearOffset}); } + +std::vector<Interval> Interval::SplitBy(Interval input) const +{ + assert(min <= input.min && input.max <= max); + if (min < input.min) + { + if (input.max < max) + { + return {{min, input.min}, {input.max, max}}; + } + return {{min, input.min}}; + } + if (input.max < max) + { + return {{input.max, max}}; + } + return {}; +} } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Interval.h b/engine/src/Utils/Spawning/Interval.h index 755fb9d4..1f16640c 100644 --- a/engine/src/Utils/Spawning/Interval.h +++ b/engine/src/Utils/Spawning/Interval.h @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024-2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -13,6 +13,7 @@ #include <MantleAPI/Traffic/i_traffic_area_stream.h> #include <units.h> +#include <set> #include <vector> #include "Common.h" @@ -24,10 +25,23 @@ struct Interval constexpr Interval(units::length::meter_t min, units::length::meter_t max) : min{min}, max{max} {} + /// Returns the length of this interval + /// + /// \return Difference between min and max constexpr units::length::meter_t GetLength() const; + /// Moves the min and max of this interval by the given offsets + /// + /// \param frontOffset The amount to be added to min + /// \param rearOffset The amount to be added to max constexpr void OffsetBy(units::length::meter_t frontOffset, units::length::meter_t rearOffset); + /// Returns the resulting list of intervals if the given interval were cut out of this interval. + /// + /// \param Interval A subrange of this interval + /// \return 0, 1 or 2 elements sorted by ascending (min/max) value. + std::vector<Interval> SplitBy(Interval) const; + units::length::meter_t min, max; }; @@ -42,6 +56,20 @@ void OffsetIntervals(std::vector<Interval>& intervals, units::length::meter_t fr /// Returns all pairs of stream distances between which no entity is placed on the given stream std::vector<Interval> GetFreeIntervals(const mantle_api::ITrafficAreaStream&); + +struct ToMin +{ + constexpr units::length::meter_t operator()(units::length::meter_t) const; + + constexpr units::length::meter_t operator()(const Interval&) const; +}; + +struct ToMax +{ + constexpr units::length::meter_t operator()(units::length::meter_t) const; + + constexpr units::length::meter_t operator()(const Interval&) const; +}; } // namespace OpenScenarioEngine::v1_3 namespace OpenScenarioEngine::v1_3 @@ -109,4 +137,24 @@ std::vector<Interval> GetFreeIntervals(const mantle_api::ITrafficAreaStream& str } return result; } + +constexpr units::length::meter_t ToMin::operator()(units::length::meter_t value) const +{ + return value; +} + +constexpr units::length::meter_t ToMin::operator()(const Interval& interval) const +{ + return interval.min; +} + +constexpr units::length::meter_t ToMax::operator()(units::length::meter_t value) const +{ + return value; +} + +constexpr units::length::meter_t ToMax::operator()(const Interval& interval) const +{ + return interval.max; +} } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Iterable.h b/engine/src/Utils/Spawning/Iterable.h index aadc9e88..00baa36f 100644 --- a/engine/src/Utils/Spawning/Iterable.h +++ b/engine/src/Utils/Spawning/Iterable.h @@ -14,11 +14,31 @@ namespace OpenScenarioEngine::v1_3 { +namespace detail +{ +template <typename Type> +struct is_vector +{ + static constexpr bool value{false}; +}; + +template <typename Type> +struct is_vector<std::vector<Type>> +{ + static constexpr bool value{true}; +}; +} // namespace detail + +template <typename Type> +constexpr bool is_vector{detail::is_vector<Type>::value}; + template <typename Container> struct Iterable { using const_iterator = typename Container::const_iterator; using iterator = typename Container::iterator; + using const_reverse_iterator = typename Container::const_reverse_iterator; + using reverse_iterator = typename Container::reverse_iterator; using const_reference = typename Container::const_reference; using reference = typename Container::reference; using value_type = typename Container::value_type; @@ -37,6 +57,12 @@ struct Iterable constexpr const_iterator end() const; constexpr iterator end(); + constexpr const_reverse_iterator rbegin() const; + constexpr reverse_iterator rbegin(); + + constexpr const_reverse_iterator rend() const; + constexpr reverse_iterator rend(); + constexpr size_t size() const; constexpr bool empty() const; @@ -46,8 +72,17 @@ struct Iterable constexpr const_reference back() const; constexpr reference back(); - constexpr const_reference operator[](size_t) const; - constexpr reference operator[](size_t); + template <typename Type = Container, std::enable_if_t<is_vector<Type>, bool> = true> + constexpr const_reference operator[](size_t i) const + { + return container[i]; + } + + template <typename Type = Container, std::enable_if_t<is_vector<Type>, bool> = true> + constexpr reference operator[](size_t i) + { + return container[i]; + } protected: Container container; @@ -112,49 +147,61 @@ constexpr typename Container::iterator Iterable<Container>::end() } template <typename Container> -constexpr size_t Iterable<Container>::size() const +constexpr typename Container::const_reverse_iterator Iterable<Container>::rbegin() const { - return container.size(); + return container.rbegin(); } template <typename Container> -constexpr bool Iterable<Container>::empty() const +constexpr typename Container::reverse_iterator Iterable<Container>::rbegin() { - return container.empty(); + return container.rbegin(); } template <typename Container> -constexpr typename Container::const_reference Iterable<Container>::front() const +constexpr typename Container::const_reverse_iterator Iterable<Container>::rend() const { - return container.front(); + return container.rend(); } + template <typename Container> -constexpr typename Container::reference Iterable<Container>::front() +constexpr typename Container::reverse_iterator Iterable<Container>::rend() { - return container.front(); + return container.rend(); } template <typename Container> -constexpr typename Container::const_reference Iterable<Container>::back() const +constexpr size_t Iterable<Container>::size() const { - return container.back(); + return container.size(); } template <typename Container> -constexpr typename Container::reference Iterable<Container>::back() +constexpr bool Iterable<Container>::empty() const { - return container.back(); + return container.empty(); } template <typename Container> -constexpr typename Container::const_reference Iterable<Container>::operator[](size_t i) const +constexpr typename Container::const_reference Iterable<Container>::front() const { - return container[i]; + return *begin(); +} +template <typename Container> +constexpr typename Container::reference Iterable<Container>::front() +{ + return *begin(); +} + +template <typename Container> +constexpr typename Container::const_reference Iterable<Container>::back() const +{ + return *rbegin(); } template <typename Container> -constexpr typename Container::reference Iterable<Container>::operator[](size_t i) +constexpr typename Container::reference Iterable<Container>::back() { - return container[i]; + return *rbegin(); } } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Length.cpp b/engine/src/Utils/Spawning/Length.cpp index 67c9aa5e..cd6310c9 100644 --- a/engine/src/Utils/Spawning/Length.cpp +++ b/engine/src/Utils/Spawning/Length.cpp @@ -27,14 +27,17 @@ units::length::meter_t ToLength::operator()(const mantle_api::TrafficArea& area) units::length::meter_t ToLength::operator()(const SpawnZone& zone) const { - assert(!zone.lengths.empty()); - return zone.lengths.back(); + return zone.GetLength() - std::transform_reduce(zone.obstacles.begin(), zone.obstacles.end(), units::length::meter_t{}, std::plus<>{}, *this); } units::length::meter_t ToLength::operator()(const SpawnZones& zones) const { - assert(!zones.lengths.empty()); - return zones.lengths.back(); + return std::transform_reduce(zones.begin(), zones.end(), units::length::meter_t{}, std::plus<>{}, *this); +} + +units::length::meter_t ToLength::operator()(const LinkedInterval& interval) const +{ + return ToLength{}(*interval.interval); } units::length::meter_t ToLength::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject& object) const diff --git a/engine/src/Utils/Spawning/Length.h b/engine/src/Utils/Spawning/Length.h index 2839ab18..01a96046 100644 --- a/engine/src/Utils/Spawning/Length.h +++ b/engine/src/Utils/Spawning/Length.h @@ -17,7 +17,9 @@ #include <memory> +#include "Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h" #include "Interval.h" +#include "LinkedInterval.h" #include "SpawnZone.h" #include "Transform.h" @@ -25,13 +27,20 @@ namespace OpenScenarioEngine::v1_3 { struct ToLength { + constexpr units::length::meter_t operator()(units::length::meter_t) const; + constexpr units::length::meter_t operator()(const Interval&) const; + units::length::meter_t operator()(const LinkedInterval&) const; + units::length::meter_t operator()(const mantle_api::ITrafficAreaStream&) const; template <typename Type> units::length::meter_t operator()(const std::shared_ptr<Type>&) const; + template <typename Type> + units::length::meter_t operator()(const Type*) const; + units::length::meter_t operator()(const mantle_api::TrafficArea&) const; units::length::meter_t operator()(const SpawnZone&) const; @@ -41,6 +50,17 @@ struct ToLength units::length::meter_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject&) const; }; +struct ToSize +{ + constexpr size_t operator()(size_t) const; + + template <typename Type> + constexpr size_t operator()(const Iterable<Type>&) const; + + template <typename Type> + constexpr size_t operator()(const std::vector<Type>&) const; +}; + template <typename Type> units::length::meter_t GetLength(const Type&); @@ -63,6 +83,11 @@ struct ToAccumulatedWeight namespace OpenScenarioEngine::v1_3 { +constexpr units::length::meter_t ToLength::operator()(units::length::meter_t length) const +{ + return length; +} + constexpr units::length::meter_t ToLength::operator()(const Interval& interval) const { return interval.GetLength(); @@ -71,7 +96,30 @@ constexpr units::length::meter_t ToLength::operator()(const Interval& interval) template <typename Type> units::length::meter_t ToLength::operator()(const std::shared_ptr<Type>& input) const { - return (*this)(*input); + return ToLength{}(*input); +} + +template <typename Type> +units::length::meter_t ToLength::operator()(const Type* input) const +{ + return ToLength{}(*input); +} + +constexpr size_t ToSize::operator()(size_t size) const +{ + return size; +} + +template <typename Type> +constexpr size_t ToSize::operator()(const Iterable<Type>& container) const +{ + return container.size(); +} + +template <typename Type> +constexpr size_t ToSize::operator()(const std::vector<Type>& container) const +{ + return container.size(); } template <typename Type> diff --git a/engine/src/Utils/Spawning/LinkedInterval.cpp b/engine/src/Utils/Spawning/LinkedInterval.cpp new file mode 100644 index 00000000..6556f22d --- /dev/null +++ b/engine/src/Utils/Spawning/LinkedInterval.cpp @@ -0,0 +1,34 @@ +/******************************************************************************** + * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#include "LinkedInterval.h" + +#include "SpawnZone.h" + +namespace OpenScenarioEngine::v1_3 +{ +VelocityRange LinkedInterval::GetVelocityRange() const +{ + const size_t index{static_cast<size_t>(std::distance(zone->begin(), interval))}; + return {zone->velocities[index], zone->velocities[index + 1]}; +} + +units::velocity::meters_per_second_t LinkedInterval::GetMinVelocity() const +{ + const size_t index{static_cast<size_t>(std::distance(zone->begin(), interval))}; + return zone->velocities[index]; +} + +units::velocity::meters_per_second_t LinkedInterval::GetMaxVelocity() const +{ + const size_t index{static_cast<size_t>(std::distance(zone->begin(), interval))}; + return zone->velocities[index + 1]; +} +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/LinkedInterval.h b/engine/src/Utils/Spawning/LinkedInterval.h new file mode 100644 index 00000000..cb3780ec --- /dev/null +++ b/engine/src/Utils/Spawning/LinkedInterval.h @@ -0,0 +1,36 @@ +/******************************************************************************** + * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ +#pragma once + +#include <vector> + +#include "Interval.h" +#include "VelocityRange.h" + +namespace OpenScenarioEngine::v1_3 +{ +struct SpawnZone; + +// A LinkedInterval holds an iterator to an Interval and a pointer to the container of that interval (its SpawnZone). +// This is done so that there can be a list of all Intervals regardless of their SpawnZone while still being able to refer +// back to the zone they are a part of. That way, an interval can be sampled with a fair distribution efficiently while +// also reducing the amount of length recomputations needed when an interval is changed/removed. (See SpawnSpace) +struct LinkedInterval +{ + units::velocity::meters_per_second_t GetMinVelocity() const; + + units::velocity::meters_per_second_t GetMaxVelocity() const; + + VelocityRange GetVelocityRange() const; + + std::vector<Interval>::iterator interval; + SpawnZone* zone; +}; +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Obstacle.cpp b/engine/src/Utils/Spawning/Obstacle.cpp new file mode 100644 index 00000000..fc2c97bd --- /dev/null +++ b/engine/src/Utils/Spawning/Obstacle.cpp @@ -0,0 +1,54 @@ +/******************************************************************************** + * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#include "Obstacle.h" + +namespace OpenScenarioEngine::v1_3 +{ +Obstacle::Obstacle(Interval interval, const std::shared_ptr<mantle_api::IEntity>& entity, units::velocity::meters_per_second_t velocity) + : Interval{std::move(interval)}, entity{entity}, velocity{velocity} {} + +Obstacle Obstacle::GetStartingSentinel(units::length::meter_t value) +{ + return {{value, value}, nullptr, units::velocity::meters_per_second_t{std::numeric_limits<double>::lowest()}}; +} + +Obstacle Obstacle::GetEndingSentinel(units::length::meter_t value) +{ + return {{value, value}, nullptr, units::velocity::meters_per_second_t{std::numeric_limits<double>::max()}}; +} + +Obstacle ToObstacle(const std::shared_ptr<mantle_api::IEntity>& entity, const mantle_api::ITrafficAreaStream& stream) +{ + const auto halfLength{entity->GetProperties()->bounding_box.dimension.length / units::dimensionless::scalar_t{2.0}}; + const auto offset{entity->GetProperties()->bounding_box.geometric_center.x}; + auto pose{stream.Convert(mantle_api::Pose{entity->GetPosition(), {}}).value()}; + const Interval interval{pose.s + offset - halfLength, pose.s + offset + halfLength}; + const auto velocity{detail::rotate_2d(entity->GetVelocity(), pose.hdg).x}; + return {interval, entity, velocity}; +} + +std::set<Obstacle, Less<ToMin>> GetObstacles(const mantle_api::ITrafficAreaStream& stream) +{ + std::set<Obstacle, Less<ToMin>> result; + units::length::meter_t min; + const units::length::meter_t max{stream.GetLength()}; + while (min < max) + { + std::weak_ptr<mantle_api::IEntity> pointer{stream.GetEntity(mantle_api::ITrafficAreaStream::SearchDirection::kDownstream, min, max - min)}; + if (is_uninitialized(pointer)) + { + break; + } + min = result.emplace(ToObstacle(pointer.lock(), stream)).first->max; + } + return result; +} +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Obstacle.h b/engine/src/Utils/Spawning/Obstacle.h new file mode 100644 index 00000000..79dae2d1 --- /dev/null +++ b/engine/src/Utils/Spawning/Obstacle.h @@ -0,0 +1,50 @@ +/******************************************************************************** + * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ +#pragma once + +#include <memory> + +#include "Common.h" +#include "Interval.h" + +namespace OpenScenarioEngine::v1_3 +{ +struct Obstacle : Interval +{ + Obstacle(Interval, const std::shared_ptr<mantle_api::IEntity>&, units::velocity::meters_per_second_t); + + static Obstacle GetStartingSentinel(units::length::meter_t); + + static Obstacle GetEndingSentinel(units::length::meter_t); + + std::shared_ptr<mantle_api::IEntity> entity; + + units::velocity::meters_per_second_t velocity; +}; + +std::set<Obstacle, Less<ToMin>> GetObstacles(const mantle_api::ITrafficAreaStream&); +} // namespace OpenScenarioEngine::v1_3 + +namespace OpenScenarioEngine::v1_3::detail +{ +template <typename Type> +struct Vec2 +{ + Type x, y; +}; + +template <typename Type> +inline Vec2<Type> rotate_2d(const mantle_api::Vec3<Type>& input, units::angle::radian_t angle) +{ + const auto cos{units::math::cos(angle)}; + const auto sin{units::math::sin(angle)}; + return {input.x * cos - input.y * sin, input.x * sin + input.y * cos}; +} +} // namespace OpenScenarioEngine::v1_3::detail diff --git a/engine/src/Utils/Spawning/SpawnSpace.cpp b/engine/src/Utils/Spawning/SpawnSpace.cpp index ca1490c4..1b1a5761 100644 --- a/engine/src/Utils/Spawning/SpawnSpace.cpp +++ b/engine/src/Utils/Spawning/SpawnSpace.cpp @@ -10,15 +10,86 @@ #include "SpawnSpace.h" +#include <algorithm> #include <cassert> +#include <numeric> + +#include "Length.h" +#include "Weight.h" namespace OpenScenarioEngine::v1_3 { -std::pair<SpawnZones &, units::length::meter_t> SpawnSpace::GetZonesAndOffset(units::length::meter_t value) +SpawnSpace::SpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &distributions, std::vector<SpawnZones> &zones) +{ + // Add entities + { + const size_t number_of_entities{std::transform_reduce(distributions.begin(), distributions.end(), size_t{}, std::plus<>{}, ToSize{})}; + entities.reserve(number_of_entities); + } + for (auto &distribution : distributions) + { + std::transform(distribution.begin(), distribution.end(), std::back_inserter(entities), // + [multiplier = distribution.weight](const Entity &entity) -> WeightedEntity + { return {entity, GetWeight(entity) * multiplier}; }); + } + std::sort(entities.begin(), entities.end(), Less<ToLength>{}); + weights = Transform(entities, ToAccumulatedWeight{}); + + // Add intervals + for (const SpawnZones ®ions : zones) + { + for (const SpawnZone &zone : regions) + { + for (auto obstacle{std::next(zone.obstacles.begin())}; obstacle != zone.obstacles.end(); ++obstacle) + { + spots.emplace(*std::prev(obstacle), *obstacle); + } + } + } +} + +units::length::meter_t SpawnSpace::GetMaxIntervalLength() const +{ + return GetLength(*spots.rbegin()); +} + +std::vector<WeightedEntity>::const_iterator SpawnSpace::FindSpawnableEntityEnd(units::length::meter_t upper_bound) const +{ + return std::upper_bound(entities.begin(), entities.end(), [upper_bound](const Entity &entity) + { return GetLength(entity) < upper_bound; }); +} +std::vector<WeightedEntity>::iterator SpawnSpace::FindSpawnableEntityEnd(units::length::meter_t upper_bound) +{ + return std::upper_bound(entities.begin(), entities.end(), [upper_bound](const Entity &entity) + { return GetLength(entity) < upper_bound; }); +} + +std::set<SpawnSpot, Less<ToLength>>::const_iterator SpawnSpace::SampleInterval(units::length::meter_t min_length, VelocityRange velocity, StochasticsInterface &rng) const { - assert(weights.size() >= 2); - const auto offset{std::prev(std::upper_bound(std::next(weights.begin()), std::prev(weights.end()), value))}; - const auto index{std::distance(weights.begin(), offset)}; - return {container[static_cast<size_t>(index)], *offset}; + auto spot{spots.lower_bound(min_length)}; + if (spot == spots.end()) + { + return spots.end(); + } + std::vector<decltype(spot)> candidates; + for (; spot != spots.end(); ++spot) + { + const bool can_keep_up{spot->velocity.Contains(velocity)}; + const bool is_enough_room_after_2_seconds{GetLength(*spot) - spot->velocity.GetDifference() * units::time::second_t{2.0} > min_length}; + if (can_keep_up && is_enough_room_after_2_seconds) + { + candidates.push_back(spot); + } + } + if (candidates.empty()) + { + return spots.end(); + } + ToAccumulatedLength accumulator; + std::vector<units::length::meter_t> lengths{Transform(candidates, + [&accumulator](const auto iterator) + { return accumulator(*iterator); })}; + const units::length::meter_t sampled_length{rng.GetUniformDistributed(.0, lengths.back().value())}; + return Sample(candidates, lengths, sampled_length); } } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/SpawnSpace.h b/engine/src/Utils/Spawning/SpawnSpace.h index 88cec5d4..d277b4b0 100644 --- a/engine/src/Utils/Spawning/SpawnSpace.h +++ b/engine/src/Utils/Spawning/SpawnSpace.h @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -10,53 +10,114 @@ #pragma once +#include <Stochastics/StochasticsInterface.h> + +#include <cassert> +#include <set> #include <utility> #include <vector> #include "Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h" #include "Iterable.h" +#include "Length.h" +#include "SpawnSpot.h" #include "SpawnZone.h" +#include "VelocityRange.h" namespace OpenScenarioEngine::v1_3 { -struct SpawnSpace : Iterable<std::vector<SpawnZones>> +using Entity = typename std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject>; + +struct WeightedEntity +{ + Entity handle; + units::dimensionless::scalar_t weight; // Product of the distribution's weight and the weight of this entity's handle +}; + +/// A SpawnSpace is essentially a self-managing, depleting list of TrafficAreas and weighted entities optimized +/// for the use-case of spawning entities at the rear end of a TrafficArea (with offsets). It restructures the +/// data to be sorted by interval and entity length, allowing efficient filtering of remaining spawnable entities +/// and intervals as the space is filled. +struct SpawnSpace { + SpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &, std::vector<SpawnZones> &); + + /// Returns the entity out of the set of entities in this space based on the given sample + /// + Entity GetEntity(units::dimensionless::scalar_t sample); + + Entity SampleEntity(StochasticsInterface &) const; + + std::set<SpawnSpot, Less<ToLength>>::const_iterator SampleSpot(units::length::meter_t min_length, VelocityRange, StochasticsInterface &) const; + /// Tries to spawn an entity and returns whether spawning was successful. - /// If successful, also adjusts the weighting of the spawn space. + /// If successful, also adjusts the spawn space's intervals. /// - /// \tparam EntityCreator Provides CreateEntity() - /// \tparam Sampler Invocable with signature double(double value) returning a number within [0, value) - template <typename EntityCreator, typename Sampler> - bool Spawn(EntityCreator &&, Sampler &&); + /// \tparam EntityCreator Provides CreateEntity(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IScenarioObject> &) + /// \param StochasticsInterface Used to sample the entity, its position and its velocity + template <typename EntityCreator> + bool Spawn(EntityCreator &&, StochasticsInterface &); + + units::length::meter_t GetMaxIntervalLength() const; - // Returns the spawn zones and weight offset containing the given sample - std::pair<SpawnZones &, units::length::meter_t> GetZonesAndOffset(units::length::meter_t); + std::vector<WeightedEntity>::const_iterator FindSpawnableEntityEnd(units::length::meter_t upper_bound) const; + std::vector<WeightedEntity>::iterator FindSpawnableEntityEnd(units::length::meter_t upper_bound); - TrafficDistributions distributions; - std::vector<units::length::meter_t> weights; + std::set<SpawnSpot, Less<ToLength>> spots; + + std::vector<WeightedEntity> entities; // Entities sorted by ascending length + std::vector<units::dimensionless::scalar_t> weights; // Used to sample an entity }; + } // namespace OpenScenarioEngine::v1_3 namespace OpenScenarioEngine::v1_3 { - -template <typename EntityCreator, typename Sampler> -bool SpawnSpace::Spawn(EntityCreator &&spawner, Sampler &&sampler) +Entity SpawnSpace::SampleEntity(StochasticsInterface &rng) const { - if (empty() || distributions.empty()) + const units::length::meter_t max_length{GetMaxIntervalLength()}; + const auto entity_end{FindSpawnableEntityEnd(max_length)}; + if (entity_end == entities.begin()) { - return false; + return nullptr; } - if (distribution.size() == 1) + assert(weights.size() == entities.size() + 1); + const units::dimensionless::scalar_t max_weight{weights[static_cast<size_t>(std::distance(entities.begin(), entity_end))]}; + const units::dimensionless::scalar_t sampled_weight{rng.GetUniformDistributed(.0, max_weight.value())}; + return Sample(entities, weights, sampled_weight).handle; +} + +template <typename EntityCreator> +bool SpawnSpace::Spawn(EntityCreator &&spawner, StochasticsInterface &rng) +{ + Entity entity{SampleEntity(rng)}; + if (entity == nullptr) { - return Spawn(distributions.front(), std::forward<EntityCreator>(spawner), std::forward<Sample>(sampler)); + return false; } - if (size() == 1) + const auto velocity_range{GetSpawnVelocityRange(entity)}; + const auto spot{SampleSpot(GetLength(entity), velocity_range, rng)}; + if (spot == spots.end()) { - return front().Spawn(distributions, std::forward<EntityCreator>(spawner), std::forward<Sampler>(sampler)); + return false; } - const units::length::meter_t sample{sampler(weights.back())}; - auto [zones, offset]{GetZonesAndOffset(sample)}; - return zones.Spawn(sample - offset, distributions, std::forward<EntityCreator>(spawner), std::forward<Sampler>(sampler)); + + // TODO: Find last spawnable spot on the zone of the sampled interval + // spawner.CreateEntity(*entity, place.zone->GetPosition(distance)); + // TODO: Remove/replace interval + // interval->zone->RemoveInterval(*interval->interval, sampler(restricted_velocity.min, restricted_velocity.max)); + return true; } + +// std::pair<const SpawnZones &, const SpawnZone &> SpawnSpace::SampleZone(units::dimensionless::scalar_t sample) const +// { +// assert(units::dimensionless::scalar_t{} <= sample && sample < units::dimensionless::scalar_t{1.0}); +// const units::length::meter_t weight{sample * weights.back()}; +// auto index{static_cast<size_t>(std::distance(weights.begin(), std::prev(std::upper_bound(std::next(weights.begin()), weights.end(), weight))))}; +// assert(index > 1); +// const SpawnZones &zones{(*this)[index]}; +// const units::length::meter_t zone_weight{weight - weights[index]}; +// auto zone_index{static_cast<size_t>(std::distance(zones.lengths.begin(), std::prev(std::upper_bound(std::next(zones.lengths.begin()), zones.lengths.end(), zone_weight))))}; +// return {zones, zones[zone_index]}; +// } } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/SpawnSpot.h b/engine/src/Utils/Spawning/SpawnSpot.h new file mode 100644 index 00000000..84804855 --- /dev/null +++ b/engine/src/Utils/Spawning/SpawnSpot.h @@ -0,0 +1,39 @@ +/******************************************************************************** + * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#pragma once + +#include "Interval.h" +#include "Obstacle.h" +#include "VelocityRange.h" + +namespace OpenScenarioEngine::v1_3 +{ +/// A SpawnSpot is an interval that holds necessary information for a SpawnSpace to determine whether an +/// entity can and should be spawned within it. This includes the velocities of the obstacle before and after it +/// and a pointer to the spot's zone so that the SpawnSpace can look for the further spots within that zone. +struct SpawnSpot : Interval +{ + constexpr SpawnSpot(Interval &&, VelocityRange &&); + + constexpr SpawnSpot(const Obstacle &before, const Obstacle &after); + + VelocityRange velocity; +}; +} // namespace OpenScenarioEngine::v1_3 + +namespace OpenScenarioEngine::v1_3 +{ +constexpr SpawnSpot::SpawnSpot(Interval &&interval, VelocityRange &&velocity) + : Interval{std::move(interval)}, velocity{std::move(velocity)} {} + +constexpr SpawnSpot::SpawnSpot(const Obstacle &before, const Obstacle &after) + : Interval{before.max, after.min}, velocity{before.velocity, after.velocity} {} +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/SpawnZone.cpp b/engine/src/Utils/Spawning/SpawnZone.cpp index 88a4e93e..d682f779 100644 --- a/engine/src/Utils/Spawning/SpawnZone.cpp +++ b/engine/src/Utils/Spawning/SpawnZone.cpp @@ -17,86 +17,34 @@ namespace OpenScenarioEngine::v1_3 { -SpawnZone::SpawnZone(std::vector<Interval> input) - : Iterable<std::vector<Interval>>{std::move(input)}, lengths{units::length::meter_t{}} +SpawnZone::SpawnZone(Interval&& interval, std::set<Obstacle, Less<ToMin>>&& obstacles) + : Interval{std::move(interval)}, obstacles{std::move(obstacles)} {} + +SpawnZone::SpawnZone(const mantle_api::ITrafficAreaStream& stream) + : SpawnZone{Interval{{}, ToLength{}(stream)}, GetObstacles(stream)} { - Transform(GetContainer(), lengths, ToAccumulatedLength{}); } -void SpawnZone::UpdateIndexToLongest() +SpawnZones::SpawnZones(std::vector<SpawnZone> input) + : Iterable<std::vector<SpawnZone>>{std::move(input)} { - indexToLongest = std::distance(begin(), std::max_element(begin(), end(), [](const auto& a, const auto& b) - { return a.GetLength() < b.GetLength(); })); } -void SpawnZone::RemoveInterval(Interval interval) +SpawnZone ToSpawnZone::operator()(const mantle_api::ITrafficAreaStream& stream) const { - auto candidate{std::upper_bound(begin(), end(), interval.max, [](units::length::meter_t threshold, const Interval& entry) - { return threshold <= entry.max; })}; - assert(candidate != end()); - assert(candidate->min <= interval.min); - assert(candidate->max >= interval.max); - const auto offset{std::distance(begin(), candidate)}; - if (candidate->min == interval.min) - { - if (candidate->max == interval.max) - { - lengths.erase(std::next(lengths.begin(), offset + 1)); - GetContainer().erase(candidate); - if (indexToLongest == offset) - { - UpdateIndexToLongest(); - } - else if (indexToLongest > offset + 1) - { - --indexToLongest; - } - } - else - { - candidate->min = interval.max; - if (indexToLongest == offset) - { - UpdateIndexToLongest(); - } - } - std::transform(std::next(begin(), offset), - end(), - std::next(lengths.begin(), offset), - ToAccumulatedLength{offset ? *std::next(lengths.begin(), offset - 1) : units::length::meter_t{}}); - return; - } - else if (candidate->max == interval.max) - { - candidate->max = interval.min; - if (indexToLongest == offset) - { - indexToLongest = std::distance(lengths.begin(), std::max_element(lengths.begin(), lengths.end())); - } - std::transform(std::next(begin(), offset), - end(), - std::next(lengths.begin(), offset), - ToAccumulatedLength{offset ? *std::next(lengths.begin(), offset - 1) : units::length::meter_t{}}); - return; - } - GetContainer().emplace(std::next(candidate), interval.max, candidate->max); - lengths.push_back(units::length::meter_t{}); - std::next(begin(), offset)->max = interval.min; - std::transform(std::next(begin(), offset), - end(), - std::next(lengths.begin(), offset), - ToAccumulatedLength{offset ? *std::next(lengths.begin(), offset - 1) : units::length::meter_t{}}); + return {stream}; } -SpawnZones::SpawnZones(std::vector<SpawnZone> input) - : Iterable<std::vector<SpawnZone>>{std::move(input)}, lengths{units::length::meter_t{}} +SpawnZone ToSpawnZone::operator()(const std::shared_ptr<mantle_api::ITrafficAreaStream>& stream) const { - Transform(GetContainer(), lengths, ToAccumulatedLength{}); + return (*this)(*stream); } -} // namespace OpenScenarioEngine::v1_3 -namespace OpenScenarioEngine::v1_3 +SpawnZones::SpawnZones(const mantle_api::TrafficArea& area) + : SpawnZones{Transform(area, ToSpawnZone{})} { +} + SpawnZones ToSpawnZones::operator()(const mantle_api::TrafficArea& area) const { return {area}; diff --git a/engine/src/Utils/Spawning/SpawnZone.h b/engine/src/Utils/Spawning/SpawnZone.h index 92389b28..d3c182c4 100644 --- a/engine/src/Utils/Spawning/SpawnZone.h +++ b/engine/src/Utils/Spawning/SpawnZone.h @@ -14,53 +14,44 @@ #include <MantleAPI/Traffic/i_traffic_area_stream.h> #include <units.h> +#include <optional> +#include <set> #include <vector> #include "Common.h" #include "Interval.h" #include "Iterable.h" +#include "SpawnSpot.h" namespace OpenScenarioEngine::v1_3 { -struct SpawnZone : Iterable<std::vector<Interval>> +struct SpawnZone : Interval { - SpawnZone(std::vector<Interval>); + SpawnZone(Interval&&, std::set<Obstacle, Less<ToMin>>&&); - template <typename Filter = Return<true>> - SpawnZone(const mantle_api::ITrafficAreaStream&, Filter&&); + SpawnZone(const mantle_api::ITrafficAreaStream&); - /// Removes the given interval from this spawn zone and updates the interval lengths and index to the longest interval if necessary - /// - /// \param Interval Interval guaranteed to be a subset of an interval of this zone - void RemoveInterval(Interval); + const Interval& GetLongestInterval() const; + Interval& GetLongestInterval(); - void UpdateIndexToLongest(); + template <typename Predicate> + std::optional<SpawnSpot> GetLastSpawnSpot(Predicate&&) const; - std::vector<units::length::meter_t> lengths; - std::ptrdiff_t indexToLongest; + std::set<Obstacle, Less<ToMin>> obstacles; }; -template <typename Filter = Return<true>> struct ToSpawnZone { SpawnZone operator()(const mantle_api::ITrafficAreaStream&) const; SpawnZone operator()(const std::shared_ptr<mantle_api::ITrafficAreaStream>&) const; - - Filter filter; }; -template <typename Filter> -ToSpawnZone(Filter&&) -> ToSpawnZone<Filter>; - struct SpawnZones : Iterable<std::vector<SpawnZone>> { SpawnZones(std::vector<SpawnZone>); - template <typename Filter = Return<true>> - SpawnZones(const mantle_api::TrafficArea&, Filter&& = {}); - - std::vector<units::length::meter_t> lengths; + SpawnZones(const mantle_api::TrafficArea&); }; struct ToSpawnZones @@ -71,27 +62,31 @@ struct ToSpawnZones namespace OpenScenarioEngine::v1_3 { -template <typename Filter> -SpawnZone ToSpawnZone<Filter>::operator()(const mantle_api::ITrafficAreaStream& stream) const -{ - return {stream, filter}; -} - -template <typename Filter> -SpawnZone ToSpawnZone<Filter>::operator()(const std::shared_ptr<mantle_api::ITrafficAreaStream>& stream) const -{ - return (*this)(*stream); -} - -template <typename Filter> -SpawnZone::SpawnZone(const mantle_api::ITrafficAreaStream& stream, Filter&& filter) - : SpawnZone{GetFreeIntervals(stream, std::forward<Filter>(filter))} -{ -} - -template <typename Filter> -SpawnZones::SpawnZones(const mantle_api::TrafficArea& area, Filter&& filter) - : SpawnZones{Transform(area, ToSpawnZone{std::forward<Filter>(filter)})} +template <typename Predicate> +std::optional<SpawnSpot> SpawnZone::GetLastSpawnSpot(Predicate&& pred) const { + auto obstacle_before{std::find_if(obstacles.rbegin(), + obstacles.rend(), + [this](const Obstacle& obstacle) + { return obstacle.min < max; })}; + if (obstacle_before == obstacles.rend()) + { // There are no obstacles + return pred(*this) ? std::optional<SpawnSpot>{*this, {}} : std::nullopt; + } + auto obstacle_after{obstacle_before++}; + while (obstacle_before != obstacles.rend()) + { + if (SpawnSpot candidate{*obstacle_before, *obstacle_after}; pred(candidate)) + { // Obstacle before and after the spawn spot + return std::move(candidate); + } + ++obstacle_before; + ++obstacle_after; + } + if (SpawnSpot candidate{Obstacle::GetStartingSentinel(min), *obstacle_after}; obstacle_after->min > min && pred(candidate)) + { // No obstacle before, but an obstacle after the spawn spot + return std::move(candidate); + } + return std::nullopt; } } // namespace OpenScenarioEngine::v1_3 \ No newline at end of file diff --git a/engine/src/Utils/Spawning/VelocityRange.cpp b/engine/src/Utils/Spawning/VelocityRange.cpp new file mode 100644 index 00000000..ba65e1a1 --- /dev/null +++ b/engine/src/Utils/Spawning/VelocityRange.cpp @@ -0,0 +1,54 @@ +/******************************************************************************** + * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#include "VelocityRange.h" + +#include <exception> +#include <string> + +namespace OpenScenarioEngine::v1_3 +{ +VelocityRange ToSpawnVelocityRange::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject &object) const +{ + return object.GetVehicle() ? (*this)(object.GetVehicle()) : (*this)(object.GetPedestrian()); +} + +VelocityRange ToSpawnVelocityRange::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IVehicle &vehicle) const +{ + return (*this)(vehicle.GetProperties()); +} + +VelocityRange ToSpawnVelocityRange::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IPedestrian &pedestrian) const +{ + return (*this)(pedestrian.GetProperties()); +} + +VelocityRange ToSpawnVelocityRange::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IProperties &properties) const +{ + const auto &entries{properties.GetProperties()}; + const auto min_match{std::find_if(entries.begin(), + entries.end(), + [](const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IProperty> &entry) + { return entry->GetName() == "MinVelocity"; })}; + if (min_match == entries.end()) + { + throw std::range_error("ToSpawnVelocityRange: 'MinVelocity' not found in properties"); + } + const auto max_match{std::find_if(entries.begin(), + entries.end(), + [](const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IProperty> &entry) + { return entry->GetName() == "MaxVelocity"; })}; + if (max_match == entries.end()) + { + throw std::range_error("ToSpawnVelocityRange: 'MaxVelocity' not found in properties"); + } + return {units::length::meter_t{std::stod((*min_match)->GetValue())}, units::length::meter_t{std::stod((*max_match)->GetValue())}}; +} +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/VelocityRange.h b/engine/src/Utils/Spawning/VelocityRange.h new file mode 100644 index 00000000..e851efc0 --- /dev/null +++ b/engine/src/Utils/Spawning/VelocityRange.h @@ -0,0 +1,78 @@ +/******************************************************************************** + * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ +#pragma once + +#include <units.h> + +#include <memory> + +#include "openScenarioLib/generated/v1_3/api/ApiClassInterfacesV1_3.h" + +namespace OpenScenarioEngine::v1_3 +{ +struct SpawnZone; + +struct VelocityRange +{ + units::velocity::meters_per_second_t min{std::numeric_limits<double>::lowest()}, max{std::numeric_limits<double>::max()}; + + constexpr bool Contains(const VelocityRange &) const; + + constexpr units::velocity::meters_per_second_t GetDifference() const; +}; + +struct ToSpawnVelocityRange +{ + constexpr VelocityRange operator()(VelocityRange) const; + + template <typename Type> + VelocityRange operator()(const std::shared_ptr<Type> &) const; + + VelocityRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject &) const; + VelocityRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IMiscObject &) const; + VelocityRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IVehicle &) const; + VelocityRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IPedestrian &) const; + VelocityRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IProperties &) const; +}; + +template <typename Input> +VelocityRange GetSpawnVelocityRange(Input &&); + +} // namespace OpenScenarioEngine::v1_3 + +namespace OpenScenarioEngine::v1_3 +{ +constexpr bool VelocityRange::Contains(const VelocityRange &other) const +{ + return min <= other.min && other.max <= max; +} + +constexpr units::velocity::meters_per_second_t VelocityRange::GetDifference() const +{ + return max - min; +} + +constexpr VelocityRange ToSpawnVelocityRange::operator()(VelocityRange input) const +{ + return input; +} + +template <typename Type> +VelocityRange ToSpawnVelocityRange::operator()(const std::shared_ptr<Type> &input) const +{ + return ToSpawnVelocityRange{}(*input); +} + +template <typename Input> +VelocityRange GetSpawnVelocityRange(Input &&input) +{ + return ToSpawnVelocityRange{}(std::forward<Input>(input)); +} +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Weight.h b/engine/src/Utils/Spawning/Weight.h new file mode 100644 index 00000000..374efefa --- /dev/null +++ b/engine/src/Utils/Spawning/Weight.h @@ -0,0 +1,92 @@ +/******************************************************************************** + * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#pragma once + +#include <openScenarioLib/generated/v1_3/api/ApiClassInterfacesV1_3.h> +#include <units.h> + +#include <memory> + +namespace OpenScenarioEngine::v1_3 +{ +struct ToWeight +{ + constexpr units::dimensionless::scalar_t operator()(units::dimensionless::scalar_t) const; + + template <typename Type> + units::dimensionless::scalar_t operator()(const std::shared_ptr<Type>&) const; + + units::dimensionless::scalar_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IScenarioObjectTemplate&) const; + + units::dimensionless::scalar_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject&) const; + + units::dimensionless::scalar_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IPedestrian&) const; + + units::dimensionless::scalar_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IVehicle&) const; + + units::dimensionless::scalar_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IProperties&) const; +}; + +template <typename Input> +units::dimensionless::scalar_t GetWeight(Input&&); +} // namespace OpenScenarioEngine::v1_3 + +namespace OpenScenarioEngine::v1_3 +{ +constexpr units::dimensionless::scalar_t ToWeight::operator()(units::dimensionless::scalar_t value) const +{ + return value; +} + +template <typename Type> +units::dimensionless::scalar_t ToWeight::operator()(const std::shared_ptr<Type>& input) const +{ + return ToWeight{}(*input); +} + +template <typename Input> +units::dimensionless::scalar_t GetWeight(Input&& input) +{ + return ToWeight{}(std::forward<Input>(input)); +} + +units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IScenarioObjectTemplate& object) const +{ + return ToWeight{}(object.GetEntityObject()); +} + +units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject& object) const +{ + return object.GetVehicle() ? ToWeight{}(object.GetVehicle()) : ToWeight{}(object.GetPedestrian()); +} + +units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IPedestrian& pedestrian) const +{ + return ToWeight{}(pedestrian.GetProperties()); +} + +units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IVehicle& vehicle) const +{ + return ToWeight{}(vehicle.GetProperties()); +} + +units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IProperties& properties) const +{ + const auto& entries{properties.GetProperties()}; + auto match{std::find_if(entries.begin(), entries.end(), [](const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IProperty>& entry) + { return entry->GetName() == "Weight"; })}; + if (match != entries.end()) + { + return units::dimensionless::scalar_t{std::stod((*match)->GetValue())}; + } + throw std::runtime_error("ToWeight: 'Weight' not found in properties"); +} +} // namespace OpenScenarioEngine::v1_3 -- GitLab From 7e6be64ef0db0d9c2006ab529d6340bebb5d8f18 Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Fri, 17 Jan 2025 10:58:40 +0100 Subject: [PATCH 08/26] Apply various spawning adjustments --- engine/CMakeLists.txt | 8 +---- engine/cmake/generated_files.cmake | 1 - .../ConvertScenarioTrafficDistribution.cpp | 2 +- .../GenericAction/TrafficAreaAction.cpp | 3 -- .../GenericAction/TrafficAreaAction.h | 4 ++- engine/src/Utils/Spawning/Common.h | 1 + engine/src/Utils/Spawning/Length.cpp | 22 +++++++++--- engine/src/Utils/Spawning/Length.h | 27 +++++--------- engine/src/Utils/Spawning/LinkedInterval.cpp | 34 ------------------ engine/src/Utils/Spawning/LinkedInterval.h | 36 ------------------- engine/src/Utils/Spawning/SpawnSpace.cpp | 34 +++++++++--------- engine/src/Utils/Spawning/SpawnSpace.h | 2 ++ engine/src/Utils/Spawning/SpawnZone.h | 3 +- engine/src/Utils/Spawning/Weight.h | 24 +++++++++++++ utils/ci/conan/conanfile.txt | 4 +-- .../conan/recipe/mantleapi/all/conandata.yml | 4 --- .../openscenario_engine/all/conanfile.py | 2 +- 17 files changed, 80 insertions(+), 131 deletions(-) delete mode 100644 engine/src/Utils/Spawning/LinkedInterval.cpp delete mode 100644 engine/src/Utils/Spawning/LinkedInterval.h diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index fed70383..b339fbfe 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -55,12 +55,6 @@ target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) find_package(OpenScenarioAPI REQUIRED MODULE) find_package(Antlr4Runtime REQUIRED MODULE) -include(FetchContent) -FetchContent_Declare(Stochastics - URL https://gitlab.eclipse.org/eclipse/openpass/stochastics-library/-/archive/main/stochastics-library-main.zip - EXCLUDE_FROM_ALL) -FetchContent_MakeAvailable(Stochastics) - include(cmake/generated_files.cmake) target_sources( @@ -81,7 +75,7 @@ target_link_libraries(${PROJECT_NAME} PUBLIC antlr4_runtime::shared openscenario_api::shared units::units Yase::agnostic_behavior_tree - PRIVATE Stochastics) + PRIVATE Stochastics::Stochastics) if(USE_CCACHE) find_program(CCACHE_FOUND ccache) diff --git a/engine/cmake/generated_files.cmake b/engine/cmake/generated_files.cmake index 42b52e31..3700be3b 100644 --- a/engine/cmake/generated_files.cmake +++ b/engine/cmake/generated_files.cmake @@ -234,7 +234,6 @@ list(APPEND ${PROJECT_NAME}_SOURCES src/Storyboard/MotionControlAction/SpeedAction_impl.cpp src/Utils/Spawning/Interval.cpp src/Utils/Spawning/Length.cpp - src/Utils/Spawning/LinkedInterval.cpp src/Utils/Spawning/Obstacle.cpp src/Utils/Spawning/SpawnSpace.cpp src/Utils/Spawning/SpawnZone.cpp diff --git a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp index ae0eb73c..28cfdb42 100644 --- a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp +++ b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp @@ -10,8 +10,8 @@ #include "ConvertScenarioTrafficDistribution.h" -#include "Utils/Spawning/Length.h" #include "Utils/Spawning/Transform.h" +#include "Utils/Spawning/Weight.h" namespace OpenScenarioEngine::v1_3 { diff --git a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp index 6777a273..5a1bfbf5 100644 --- a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp +++ b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp @@ -10,8 +10,6 @@ #include "Storyboard/GenericAction/TrafficAreaAction.h" -#include <Stochastics/Stochastics.h> - #include "Conversion/OscToMantle/ConvertScenarioTrafficArea.h" #include "Utils/EntityCreator.h" #include "Utils/Logger.h" @@ -66,7 +64,6 @@ bool TrafficAreaAction::Step() EntityCreator spawner{mantle.environment}; std::vector<SpawnZones> spawnZones{Transform(trafficAreas, ToSpawnZones{})}; SpawnSpace spawnSpace{values.trafficDistributions, spawnZones}; - Stochastics rng; while (values.numberOfEntities) { if (!spawnSpace.Spawn(spawner, rng)) diff --git a/engine/src/Storyboard/GenericAction/TrafficAreaAction.h b/engine/src/Storyboard/GenericAction/TrafficAreaAction.h index 1e63a341..22cd970d 100644 --- a/engine/src/Storyboard/GenericAction/TrafficAreaAction.h +++ b/engine/src/Storyboard/GenericAction/TrafficAreaAction.h @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2021-2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2021-2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -12,6 +12,7 @@ #include <MantleAPI/Execution/i_environment.h> #include <MantleAPI/Traffic/i_traffic_area_service.h> +#include <Stochastics/Stochastics.h> #include <Stochastics/StochasticsInterface.h> #include <agnostic_behavior_tree/action_node.h> #include <openScenarioLib/generated/v1_3/api/ApiClassInterfacesV1_3.h> @@ -52,6 +53,7 @@ private: /// The accumulated size of all traffic areas up to and including the index of the respective element. /// Does not account for overlapping areas. std::vector<units::length::meter_t> lengths; + Stochastics rng; }; namespace Node diff --git a/engine/src/Utils/Spawning/Common.h b/engine/src/Utils/Spawning/Common.h index 3d16791a..eff3b449 100644 --- a/engine/src/Utils/Spawning/Common.h +++ b/engine/src/Utils/Spawning/Common.h @@ -13,6 +13,7 @@ #include <units.h> #include <algorithm> +#include <cassert> #include <functional> #include <memory> #include <type_traits> diff --git a/engine/src/Utils/Spawning/Length.cpp b/engine/src/Utils/Spawning/Length.cpp index cd6310c9..8643a25f 100644 --- a/engine/src/Utils/Spawning/Length.cpp +++ b/engine/src/Utils/Spawning/Length.cpp @@ -13,6 +13,8 @@ #include <cassert> #include <numeric> +#include "SpawnSpace.h" + namespace OpenScenarioEngine::v1_3 { units::length::meter_t ToLength::operator()(const mantle_api::ITrafficAreaStream& stream) const @@ -25,6 +27,21 @@ units::length::meter_t ToLength::operator()(const mantle_api::TrafficArea& area) return std::transform_reduce(area.begin(), area.end(), units::length::meter_t{}, std::plus<>{}, *this); } +units::length::meter_t ToLength::operator()(const WeightedEntity& entity) const +{ + return (*this)(entity.handle); +} + +units::length::meter_t ToLength::operator()(const mantle_api::IEntity& entity) const +{ + return (*this)(entity.GetProperties()); +} + +units::length::meter_t ToLength::operator()(const mantle_api::EntityProperties& properties) const +{ + return properties.bounding_box.dimension.length; +} + units::length::meter_t ToLength::operator()(const SpawnZone& zone) const { return zone.GetLength() - std::transform_reduce(zone.obstacles.begin(), zone.obstacles.end(), units::length::meter_t{}, std::plus<>{}, *this); @@ -35,11 +52,6 @@ units::length::meter_t ToLength::operator()(const SpawnZones& zones) const return std::transform_reduce(zones.begin(), zones.end(), units::length::meter_t{}, std::plus<>{}, *this); } -units::length::meter_t ToLength::operator()(const LinkedInterval& interval) const -{ - return ToLength{}(*interval.interval); -} - units::length::meter_t ToLength::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject& object) const { if (decltype(auto) vehicle{object.GetVehicle()}; vehicle != nullptr) diff --git a/engine/src/Utils/Spawning/Length.h b/engine/src/Utils/Spawning/Length.h index 01a96046..7c81c36f 100644 --- a/engine/src/Utils/Spawning/Length.h +++ b/engine/src/Utils/Spawning/Length.h @@ -10,6 +10,7 @@ #pragma once +#include <MantleAPI/Traffic/i_entity.h> #include <MantleAPI/Traffic/i_traffic_area_service.h> #include <MantleAPI/Traffic/i_traffic_area_stream.h> #include <openScenarioLib/generated/v1_3/api/ApiClassInterfacesV1_3.h> @@ -19,25 +20,30 @@ #include "Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h" #include "Interval.h" -#include "LinkedInterval.h" #include "SpawnZone.h" #include "Transform.h" namespace OpenScenarioEngine::v1_3 { +struct WeightedEntity; + struct ToLength { constexpr units::length::meter_t operator()(units::length::meter_t) const; constexpr units::length::meter_t operator()(const Interval&) const; - units::length::meter_t operator()(const LinkedInterval&) const; - units::length::meter_t operator()(const mantle_api::ITrafficAreaStream&) const; template <typename Type> units::length::meter_t operator()(const std::shared_ptr<Type>&) const; + units::length::meter_t operator()(const WeightedEntity&) const; + + units::length::meter_t operator()(const mantle_api::IEntity&) const; + + units::length::meter_t operator()(const mantle_api::EntityProperties&) const; + template <typename Type> units::length::meter_t operator()(const Type*) const; @@ -71,14 +77,6 @@ struct ToAccumulatedLength units::length::meter_t totalLength; }; - -struct ToAccumulatedWeight -{ - template <typename Entry> - units::dimensionless::scalar_t operator()(const Entry&); - - units::dimensionless::scalar_t totalWeight; -}; } // namespace OpenScenarioEngine::v1_3 namespace OpenScenarioEngine::v1_3 @@ -134,11 +132,4 @@ units::length::meter_t ToAccumulatedLength::operator()(const Type& input) totalLength += GetLength(input); return totalLength; } - -template <typename Entry> -units::dimensionless::scalar_t ToAccumulatedWeight::operator()(const Entry& entry) -{ - totalWeight += units::dimensionless::scalar_t{entry.GetWeight()}; - return totalWeight; -} } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/LinkedInterval.cpp b/engine/src/Utils/Spawning/LinkedInterval.cpp deleted file mode 100644 index 6556f22d..00000000 --- a/engine/src/Utils/Spawning/LinkedInterval.cpp +++ /dev/null @@ -1,34 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ - -#include "LinkedInterval.h" - -#include "SpawnZone.h" - -namespace OpenScenarioEngine::v1_3 -{ -VelocityRange LinkedInterval::GetVelocityRange() const -{ - const size_t index{static_cast<size_t>(std::distance(zone->begin(), interval))}; - return {zone->velocities[index], zone->velocities[index + 1]}; -} - -units::velocity::meters_per_second_t LinkedInterval::GetMinVelocity() const -{ - const size_t index{static_cast<size_t>(std::distance(zone->begin(), interval))}; - return zone->velocities[index]; -} - -units::velocity::meters_per_second_t LinkedInterval::GetMaxVelocity() const -{ - const size_t index{static_cast<size_t>(std::distance(zone->begin(), interval))}; - return zone->velocities[index + 1]; -} -} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/LinkedInterval.h b/engine/src/Utils/Spawning/LinkedInterval.h deleted file mode 100644 index cb3780ec..00000000 --- a/engine/src/Utils/Spawning/LinkedInterval.h +++ /dev/null @@ -1,36 +0,0 @@ -/******************************************************************************** - * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) - * - * This program and the accompanying materials are made available under the - * terms of the Eclipse Public License 2.0 which is available at - * http://www.eclipse.org/legal/epl-2.0. - * - * SPDX-License-Identifier: EPL-2.0 - ********************************************************************************/ -#pragma once - -#include <vector> - -#include "Interval.h" -#include "VelocityRange.h" - -namespace OpenScenarioEngine::v1_3 -{ -struct SpawnZone; - -// A LinkedInterval holds an iterator to an Interval and a pointer to the container of that interval (its SpawnZone). -// This is done so that there can be a list of all Intervals regardless of their SpawnZone while still being able to refer -// back to the zone they are a part of. That way, an interval can be sampled with a fair distribution efficiently while -// also reducing the amount of length recomputations needed when an interval is changed/removed. (See SpawnSpace) -struct LinkedInterval -{ - units::velocity::meters_per_second_t GetMinVelocity() const; - - units::velocity::meters_per_second_t GetMaxVelocity() const; - - VelocityRange GetVelocityRange() const; - - std::vector<Interval>::iterator interval; - SpawnZone* zone; -}; -} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/SpawnSpace.cpp b/engine/src/Utils/Spawning/SpawnSpace.cpp index 1b1a5761..7bf14331 100644 --- a/engine/src/Utils/Spawning/SpawnSpace.cpp +++ b/engine/src/Utils/Spawning/SpawnSpace.cpp @@ -23,8 +23,8 @@ SpawnSpace::SpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &dis { // Add entities { - const size_t number_of_entities{std::transform_reduce(distributions.begin(), distributions.end(), size_t{}, std::plus<>{}, ToSize{})}; - entities.reserve(number_of_entities); + const size_t numberOfEntities{std::transform_reduce(distributions.begin(), distributions.end(), size_t{}, std::plus<>{}, ToSize{})}; + entities.reserve(numberOfEntities); } for (auto &distribution : distributions) { @@ -53,20 +53,20 @@ units::length::meter_t SpawnSpace::GetMaxIntervalLength() const return GetLength(*spots.rbegin()); } -std::vector<WeightedEntity>::const_iterator SpawnSpace::FindSpawnableEntityEnd(units::length::meter_t upper_bound) const +std::vector<WeightedEntity>::const_iterator SpawnSpace::FindSpawnableEntityEnd(units::length::meter_t upperBound) const { - return std::upper_bound(entities.begin(), entities.end(), [upper_bound](const Entity &entity) - { return GetLength(entity) < upper_bound; }); + return std::upper_bound(entities.begin(), entities.end(), upperBound, [](units::length::meter_t upperBound, const WeightedEntity &entity) + { return GetLength(entity) < upperBound; }); } -std::vector<WeightedEntity>::iterator SpawnSpace::FindSpawnableEntityEnd(units::length::meter_t upper_bound) +std::vector<WeightedEntity>::iterator SpawnSpace::FindSpawnableEntityEnd(units::length::meter_t upperBound) { - return std::upper_bound(entities.begin(), entities.end(), [upper_bound](const Entity &entity) - { return GetLength(entity) < upper_bound; }); + return std::upper_bound(entities.begin(), entities.end(), upperBound, [](units::length::meter_t upperBound, const WeightedEntity &entity) + { return GetLength(entity) < upperBound; }); } -std::set<SpawnSpot, Less<ToLength>>::const_iterator SpawnSpace::SampleInterval(units::length::meter_t min_length, VelocityRange velocity, StochasticsInterface &rng) const +std::set<SpawnSpot, Less<ToLength>>::const_iterator SpawnSpace::SampleSpot(units::length::meter_t minLength, VelocityRange velocity, StochasticsInterface &rng) const { - auto spot{spots.lower_bound(min_length)}; + auto spot{spots.lower_bound(minLength)}; if (spot == spots.end()) { return spots.end(); @@ -74,22 +74,22 @@ std::set<SpawnSpot, Less<ToLength>>::const_iterator SpawnSpace::SampleInterval(u std::vector<decltype(spot)> candidates; for (; spot != spots.end(); ++spot) { - const bool can_keep_up{spot->velocity.Contains(velocity)}; - const bool is_enough_room_after_2_seconds{GetLength(*spot) - spot->velocity.GetDifference() * units::time::second_t{2.0} > min_length}; - if (can_keep_up && is_enough_room_after_2_seconds) + const bool canKeepUp{spot->velocity.Contains(velocity)}; + const bool isEnoughRoomAfter2Seconds{GetLength(*spot) - spot->velocity.GetDifference() * units::time::second_t{2.0} > minLength}; + if (canKeepUp && isEnoughRoomAfter2Seconds) { candidates.push_back(spot); } } - if (candidates.empty()) + if (candidates.size() <= 1) { - return spots.end(); + return candidates.empty() ? spots.end() : candidiates.begin(); } ToAccumulatedLength accumulator; std::vector<units::length::meter_t> lengths{Transform(candidates, [&accumulator](const auto iterator) { return accumulator(*iterator); })}; - const units::length::meter_t sampled_length{rng.GetUniformDistributed(.0, lengths.back().value())}; - return Sample(candidates, lengths, sampled_length); + const units::length::meter_t sampledLength{rng.GetUniformDistributed(.0, lengths.back().value())}; + return Sample(candidates, lengths, sampledLength); } } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/SpawnSpace.h b/engine/src/Utils/Spawning/SpawnSpace.h index d277b4b0..8b257a53 100644 --- a/engine/src/Utils/Spawning/SpawnSpace.h +++ b/engine/src/Utils/Spawning/SpawnSpace.h @@ -11,8 +11,10 @@ #pragma once #include <Stochastics/StochasticsInterface.h> +#include <units.h> #include <cassert> +#include <memory> #include <set> #include <utility> #include <vector> diff --git a/engine/src/Utils/Spawning/SpawnZone.h b/engine/src/Utils/Spawning/SpawnZone.h index d3c182c4..4f8336f9 100644 --- a/engine/src/Utils/Spawning/SpawnZone.h +++ b/engine/src/Utils/Spawning/SpawnZone.h @@ -25,8 +25,9 @@ namespace OpenScenarioEngine::v1_3 { -struct SpawnZone : Interval +class SpawnZone : public Interval { +public: SpawnZone(Interval&&, std::set<Obstacle, Less<ToMin>>&&); SpawnZone(const mantle_api::ITrafficAreaStream&); diff --git a/engine/src/Utils/Spawning/Weight.h b/engine/src/Utils/Spawning/Weight.h index 374efefa..944cb6c2 100644 --- a/engine/src/Utils/Spawning/Weight.h +++ b/engine/src/Utils/Spawning/Weight.h @@ -26,6 +26,8 @@ struct ToWeight units::dimensionless::scalar_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IScenarioObjectTemplate&) const; + units::dimensionless::scalar_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::ITrafficDistributionEntry&) const; + units::dimensionless::scalar_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject&) const; units::dimensionless::scalar_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IPedestrian&) const; @@ -33,10 +35,20 @@ struct ToWeight units::dimensionless::scalar_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IVehicle&) const; units::dimensionless::scalar_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IProperties&) const; + + units::dimensionless::scalar_t operator()(const WeightedEntity&) const; }; template <typename Input> units::dimensionless::scalar_t GetWeight(Input&&); + +struct ToAccumulatedWeight +{ + template <typename Entry> + units::dimensionless::scalar_t operator()(const Entry&); + + units::dimensionless::scalar_t totalWeight; +}; } // namespace OpenScenarioEngine::v1_3 namespace OpenScenarioEngine::v1_3 @@ -63,6 +75,11 @@ units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO: return ToWeight{}(object.GetEntityObject()); } +units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::ITrafficDistributionEntry& entry) const +{ + return units::dimensionless::scalar_t{entry.GetWeight()}; +} + units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject& object) const { return object.GetVehicle() ? ToWeight{}(object.GetVehicle()) : ToWeight{}(object.GetPedestrian()); @@ -89,4 +106,11 @@ units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO: } throw std::runtime_error("ToWeight: 'Weight' not found in properties"); } + +template <typename Entry> +units::dimensionless::scalar_t ToAccumulatedWeight::operator()(const Entry& entry) +{ + totalWeight += GetWeight(entry); + return totalWeight; +} } // namespace OpenScenarioEngine::v1_3 diff --git a/utils/ci/conan/conanfile.txt b/utils/ci/conan/conanfile.txt index 849dff88..7e405f78 100644 --- a/utils/ci/conan/conanfile.txt +++ b/utils/ci/conan/conanfile.txt @@ -1,9 +1,9 @@ [requires] -units/2.3.4@openscenarioengine/testing mantleapi/v11.0.0@openscenarioengine/testing -yase/d0c0e58d17358044cc9018c74308b45f6097ecfb@openscenarioengine/testing openscenario_api/v1.4.0@openscenarioengine/testing stochastics/0.11.0@openscenarioengine/testing +units/2.3.4@openscenarioengine/testing +yase/d0c0e58d17358044cc9018c74308b45f6097ecfb@openscenarioengine/testing [options] diff --git a/utils/ci/conan/recipe/mantleapi/all/conandata.yml b/utils/ci/conan/recipe/mantleapi/all/conandata.yml index 56d0b1a5..30e9ad2a 100644 --- a/utils/ci/conan/recipe/mantleapi/all/conandata.yml +++ b/utils/ci/conan/recipe/mantleapi/all/conandata.yml @@ -41,10 +41,6 @@ sources: url: https://gitlab.eclipse.org/eclipse/openpass/mantle-api.git sha256: "a5530013edcd2028ce48d1dcd7e90a512c158f27" - "6.0.1": - url: https://gitlab.eclipse.org/eclipse/openpass/mantle-api.git - sha256: "4c04afb373d26b9e4ddcae3fd920f6c5543a19f2" - "8.0.0": url: https://gitlab.eclipse.org/eclipse/openpass/mantle-api.git sha256: "18eadd8ad4f3ddd78c955ce3d4d637a8ff912091" diff --git a/utils/ci/conan/recipe/openscenario_engine/all/conanfile.py b/utils/ci/conan/recipe/openscenario_engine/all/conanfile.py index b79fb3fa..43f2201c 100644 --- a/utils/ci/conan/recipe/openscenario_engine/all/conanfile.py +++ b/utils/ci/conan/recipe/openscenario_engine/all/conanfile.py @@ -25,7 +25,7 @@ class OpenScenarioEngineConan(ConanFile): settings = "os", "compiler", "build_type", "arch" options = {"shared": [True, False], "fPIC": [True, False], - "MantleAPI_version":["6.0.1"], + "MantleAPI_version":["ANY"], "openscenario_api_version":["ANY"], "units_version":["ANY"], "Yase_version":["ANY"] -- GitLab From 72b7da93b865e1f022e226679a87ee9d24c3d2d1 Mon Sep 17 00:00:00 2001 From: Naida Goro <naida.goro@in-tech.com> Date: Thu, 30 Jan 2025 11:12:36 +0000 Subject: [PATCH 09/26] Fix some build errors in the branch traffic-area-action --- engine/CMakeLists.txt | 2 ++ engine/cmake/generated_files.cmake | 1 + .../ConvertScenarioTrafficDistribution.cpp | 6 +++--- engine/src/Utils/Spawning/SpawnSpace.cpp | 18 ++++++++++++++++-- engine/src/Utils/Spawning/SpawnSpace.h | 13 ------------- engine/src/Utils/Spawning/VelocityRange.cpp | 2 +- engine/src/Utils/Spawning/Weight.h | 4 +++- 7 files changed, 26 insertions(+), 20 deletions(-) diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index b339fbfe..d81c00c2 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -48,6 +48,8 @@ find_package(Stochastics REQUIRED) # see https://stackoverflow.com/a/58495612 set(CMAKE_INSTALL_RPATH $ORIGIN) +find_package(Stochastics REQUIRED CONFIG) + add_library(${PROJECT_NAME} SHARED "") target_compile_features(${PROJECT_NAME} PRIVATE cxx_std_17) diff --git a/engine/cmake/generated_files.cmake b/engine/cmake/generated_files.cmake index 3700be3b..5e4d2eb2 100644 --- a/engine/cmake/generated_files.cmake +++ b/engine/cmake/generated_files.cmake @@ -598,6 +598,7 @@ list(APPEND ${PROJECT_NAME}_HEADERS src/Utils/Spawning/Length.h src/Utils/Spawning/SpawnZone.h src/Utils/Spawning/Transform.h + src/Utils/Spawning/Weight.h src/Utils/ConditionEdgeEvaluator.h src/Utils/Constants.h src/Utils/ControllerCreator.h diff --git a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp index 28cfdb42..bba5d9a4 100644 --- a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp +++ b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024-2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -19,7 +19,7 @@ struct ToEntities { std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject> operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityDistributionEntry& entry) const { - return entry.GetScenarioObjectTemplate()->GetEntityObject(); + return entry.GetScenarioObjectTemplate()->GetEntitiyObject(); // https://github.com/RA-Consulting-GmbH/openscenario.api.test/issues/222 } std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject>> operator()(const NET_ASAM_OPENSCENARIO::v1_3::ITrafficDistributionEntry& entry) const @@ -33,7 +33,7 @@ struct ToTrafficDistribution TrafficDistribution operator()(const NET_ASAM_OPENSCENARIO::v1_3::ITrafficDistributionEntry& entry) const { return {TransformSharedPointers(entry.GetEntityDistribution()->GetEntityDistributionEntry(), [](const NET_ASAM_OPENSCENARIO::v1_3::IEntityDistributionEntry& entry) - { return entry.GetScenarioObjectTemplate()->GetEntityObject(); }), + { return entry.GetScenarioObjectTemplate()->GetEntitiyObject(); }), // https://github.com/RA-Consulting-GmbH/openscenario.api.test/issues/222 entry.GetWeight()}; } }; diff --git a/engine/src/Utils/Spawning/SpawnSpace.cpp b/engine/src/Utils/Spawning/SpawnSpace.cpp index 7bf14331..a1090f7b 100644 --- a/engine/src/Utils/Spawning/SpawnSpace.cpp +++ b/engine/src/Utils/Spawning/SpawnSpace.cpp @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2024-2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -48,6 +48,20 @@ SpawnSpace::SpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &dis } } +Entity SpawnSpace::SampleEntity(StochasticsInterface &rng) const +{ + const units::length::meter_t max_length{GetMaxIntervalLength()}; + const auto entity_end{FindSpawnableEntityEnd(max_length)}; + if (entity_end == entities.begin()) + { + return nullptr; + } + assert(weights.size() == entities.size() + 1); + const units::dimensionless::scalar_t max_weight{weights[static_cast<size_t>(std::distance(entities.begin(), entity_end))]}; + const units::dimensionless::scalar_t sampled_weight{rng.GetUniformDistributed(.0, max_weight.value())}; + return Sample(entities, weights, sampled_weight).handle; +} + units::length::meter_t SpawnSpace::GetMaxIntervalLength() const { return GetLength(*spots.rbegin()); @@ -83,7 +97,7 @@ std::set<SpawnSpot, Less<ToLength>>::const_iterator SpawnSpace::SampleSpot(units } if (candidates.size() <= 1) { - return candidates.empty() ? spots.end() : candidiates.begin(); + return candidates.empty() ? spots.end() : *candidates.begin(); } ToAccumulatedLength accumulator; std::vector<units::length::meter_t> lengths{Transform(candidates, diff --git a/engine/src/Utils/Spawning/SpawnSpace.h b/engine/src/Utils/Spawning/SpawnSpace.h index 8b257a53..2b07296f 100644 --- a/engine/src/Utils/Spawning/SpawnSpace.h +++ b/engine/src/Utils/Spawning/SpawnSpace.h @@ -75,19 +75,6 @@ struct SpawnSpace namespace OpenScenarioEngine::v1_3 { -Entity SpawnSpace::SampleEntity(StochasticsInterface &rng) const -{ - const units::length::meter_t max_length{GetMaxIntervalLength()}; - const auto entity_end{FindSpawnableEntityEnd(max_length)}; - if (entity_end == entities.begin()) - { - return nullptr; - } - assert(weights.size() == entities.size() + 1); - const units::dimensionless::scalar_t max_weight{weights[static_cast<size_t>(std::distance(entities.begin(), entity_end))]}; - const units::dimensionless::scalar_t sampled_weight{rng.GetUniformDistributed(.0, max_weight.value())}; - return Sample(entities, weights, sampled_weight).handle; -} template <typename EntityCreator> bool SpawnSpace::Spawn(EntityCreator &&spawner, StochasticsInterface &rng) diff --git a/engine/src/Utils/Spawning/VelocityRange.cpp b/engine/src/Utils/Spawning/VelocityRange.cpp index ba65e1a1..a1326dac 100644 --- a/engine/src/Utils/Spawning/VelocityRange.cpp +++ b/engine/src/Utils/Spawning/VelocityRange.cpp @@ -49,6 +49,6 @@ VelocityRange ToSpawnVelocityRange::operator()(const NET_ASAM_OPENSCENARIO::v1_3 { throw std::range_error("ToSpawnVelocityRange: 'MaxVelocity' not found in properties"); } - return {units::length::meter_t{std::stod((*min_match)->GetValue())}, units::length::meter_t{std::stod((*max_match)->GetValue())}}; + return {units::velocity::meters_per_second_t{std::stod((*min_match)->GetValue())}, units::velocity::meters_per_second_t{std::stod((*max_match)->GetValue())}}; } } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Weight.h b/engine/src/Utils/Spawning/Weight.h index 944cb6c2..b068658b 100644 --- a/engine/src/Utils/Spawning/Weight.h +++ b/engine/src/Utils/Spawning/Weight.h @@ -17,6 +17,8 @@ namespace OpenScenarioEngine::v1_3 { +struct WeightedEntity; + struct ToWeight { constexpr units::dimensionless::scalar_t operator()(units::dimensionless::scalar_t) const; @@ -72,7 +74,7 @@ units::dimensionless::scalar_t GetWeight(Input&& input) units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IScenarioObjectTemplate& object) const { - return ToWeight{}(object.GetEntityObject()); + return ToWeight{}(object.GetEntitiyObject()); // https://github.com/RA-Consulting-GmbH/openscenario.api.test/issues/222 } units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::ITrafficDistributionEntry& entry) const -- GitLab From e920427eb1d1a1370820d2b58540a836ef521c36 Mon Sep 17 00:00:00 2001 From: Naida Goro <naida.goro@in-tech.com> Date: Fri, 31 Jan 2025 05:53:05 +0000 Subject: [PATCH 10/26] Add TrafficAreaActionTest --- engine/CMakeLists.txt | 1 + .../GenericAction/TrafficAreaActionTest.cpp | 53 ++++++++ .../scenario_with_traffic_area_action.xosc | 118 ++++++++++++++++++ 3 files changed, 172 insertions(+) create mode 100644 engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp create mode 100644 engine/tests/data/Scenarios/scenario_with_traffic_area_action.xosc diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index d81c00c2..2b61c49a 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -207,6 +207,7 @@ target_sources( ${CMAKE_CURRENT_LIST_DIR}/tests/Storyboard/GenericAction/EnvironmentActionTest.cpp ${CMAKE_CURRENT_LIST_DIR}/tests/Storyboard/GenericAction/LightStateActionTests.cpp ${CMAKE_CURRENT_LIST_DIR}/tests/Storyboard/GenericAction/TeleportActionTest.cpp + ${CMAKE_CURRENT_LIST_DIR}/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp ${CMAKE_CURRENT_LIST_DIR}/tests/Storyboard/GenericAction/TrafficSignalStateActionTest.cpp ${CMAKE_CURRENT_LIST_DIR}/tests/Storyboard/GenericAction/TrafficSinkActionTest.cpp ${CMAKE_CURRENT_LIST_DIR}/tests/Storyboard/GenericAction/TrafficSwarmActionTest.cpp diff --git a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp new file mode 100644 index 00000000..33317b2c --- /dev/null +++ b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp @@ -0,0 +1,53 @@ +/******************************************************************************** + * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ + +#include <MantleAPI/Test/test_utils.h> +#include <gtest/gtest.h> + +#include "OpenScenarioEngine/OpenScenarioEngine.h" +#include "TestUtils.h" +#include "TestUtils/TestLogger.h" + +using testing::_; +using testing::Return; +using testing::ReturnRef; +using testing::HasSubstr; +using testing::OpenScenarioEngine::v1_3::OpenScenarioEngineTestBase; +using testing::OpenScenarioEngine::v1_3::GetScenariosPath; + +class TrafficAreaActionTestFixture : public OpenScenarioEngineTestBase +{ +protected: + void SetUp() override + { + //LOGGER = std::make_unique<testing::OpenScenarioEngine::v1_3::TestLogger>(); + //mock_environment_ = std::make_shared<mantle_api::MockEnvironment>(); + auto& mockTrafficAreaService{dynamic_cast<mantle_api::MockTrafficAreaService&>(env_->GetTrafficAreaService())}; + ON_CALL(mockTrafficAreaService, CreateTrafficArea(_)).WillByDefault([this]() -> std::vector<std::shared_ptr<mantle_api::ITrafficAreaStream>> + { + auto mock_traffic_area_stream{std::make_shared<mantle_api::MockTrafficAreaStream>()}; + traffic_area_stream_vec_.emplace_back(std::move(mock_traffic_area_stream)); + return traffic_area_stream_vec_; }); + } + + //std::shared_ptr<mantle_api::MockEnvironment> mock_environment_; + std::vector<std::shared_ptr<mantle_api::ITrafficAreaStream>> traffic_area_stream_vec_; + //std::unique_ptr<testing::OpenScenarioEngine::v1_3::TestLogger> LOGGER; +}; + +TEST_F(TrafficAreaActionTestFixture, GivenScenarioWithTrafficAreaAction_WhenInitScenario_Then) +{ + std::string xosc_file_path{GetScenariosPath(test_info_->file()) + "scenario_with_traffic_area_action.xosc"}; + OpenScenarioEngine::v1_3::OpenScenarioEngine engine(xosc_file_path, env_); + engine.Init(); + + //EXPECT_THAT(LOGGER->LastLogLevel(), mantle_api::LogLevel::kError); + //EXPECT_THAT(LOGGER->LastLogMessage(), HasSubstr("Method TrafficAreaAction::Step() not implemented yet (returning \"true\" by default)")); +} diff --git a/engine/tests/data/Scenarios/scenario_with_traffic_area_action.xosc b/engine/tests/data/Scenarios/scenario_with_traffic_area_action.xosc new file mode 100644 index 00000000..caa81f9a --- /dev/null +++ b/engine/tests/data/Scenarios/scenario_with_traffic_area_action.xosc @@ -0,0 +1,118 @@ +<?xml version='1.0' encoding='UTF-8'?> +<OpenSCENARIO> + <FileHeader revMajor="1" revMinor="3" date="2024-12-06T10:00:00" + description="TrafficAreaAction Test" author="BMW AG" /> + <CatalogLocations> + <VehicleCatalog> + <Directory path="Vehicles" /> + </VehicleCatalog> + <PedestrianCatalog> + <Directory path="Vehicles" /> + </PedestrianCatalog> + </CatalogLocations> + <RoadNetwork> + <LogicFile filepath="SceneryConfiguration.xodr" /> + <SceneGraphFile filepath="" /> + </RoadNetwork> + <Entities> + <ScenarioObject name="Ego"> + <CatalogReference catalogName="VehicleCatalog" entryName="car_mini_cooper" /> + <ObjectController> + <Controller name="Ego"> + <Properties> + <Property name="AgentProfile" value="MiddleClassCarAgent" /> + </Properties> + </Controller> + </ObjectController> + </ScenarioObject> + </Entities> + <Storyboard> + <Init> + <Actions> + <GlobalAction> + <TrafficAction> + <!-- + https://publications.pages.asam.net/standards/ASAM_OpenSCENARIO/ASAM_OpenSCENARIO_XML/latest/generated/content/TrafficAreaAction.html --> + <TrafficAreaAction continuous="false" numberOfEntities="100"> + <TrafficDistribution> + <TrafficDistributionEntiry weight="1.0"> + <EntityDistribution> + <EntityDistributionEntry weight="1.0"> + <ScenarioObjectTemplate> + <CatalogReference catalogName="VehicleCatalog" entryName="car_mini_cooper" /> + <ObjectController> + <Controller name="Common"> + <Properties> + <Property name="AgentProfile" value="MiddleClassCarAgent" /> + </Properties> + </Controller> + </ObjectController> + </ScenarioObjectTemplate> + </EntityDistributionEntry> + </EntityDistribution> + </TrafficDistributionEntiry> + </TrafficDistribution> + <TrafficArea> + <RoadRange> + <RoadCursor roadId="road00" s="100" /> + <RoadCursor roadId="road00" s="500" /> + </RoadRange> + </TrafficArea> + </TrafficAreaAction> + </TrafficAction> + </GlobalAction> + <Private entityRef="Ego"> + <PrivateAction> + <TeleportAction> + <Position> + <LanePosition roadId="1" laneId="-1" offset="0.0" s="0.0"> + <Orientation type="relative" /> + </LanePosition> + </Position> + </TeleportAction> + </PrivateAction> + <PrivateAction> + <LongitudinalAction> + <SpeedAction> + <SpeedActionDynamics dynamicsShape="step" value="0.0" dynamicsDimension="rate" /> + <SpeedActionTarget> + <AbsoluteTargetSpeed value="43.5" /> + </SpeedActionTarget> + </SpeedAction> + </LongitudinalAction> + </PrivateAction> + <PrivateAction> + <ControllerAction> + <ActivateControllerAction controllerRef="Ego" lateral="true" longitudinal="true" /> + </ControllerAction> + </PrivateAction> + </Private> + </Actions> + </Init> + <Story name=""> + <Act name=""> + <ManeuverGroup name="" maximumExecutionCount="1"> + <Actors selectTriggeringEntities="false" /> + </ManeuverGroup> + <StartTrigger> + <ConditionGroup> + <Condition name="StartTime" delay="0" conditionEdge="rising"> + <ByValueCondition> + <SimulationTimeCondition value="0.0" rule="greaterOrEqual" /> + </ByValueCondition> + </Condition> + </ConditionGroup> + </StartTrigger> + </Act> + </Story> + <StopTrigger> + <ConditionGroup> + <Condition name="EndTime" delay="0" conditionEdge="rising"> + <ByValueCondition> + <SimulationTimeCondition value="30.0" rule="greaterThan" /> + </ByValueCondition> + </Condition> + </ConditionGroup> + </StopTrigger> + </Storyboard> +</OpenSCENARIO> \ No newline at end of file -- GitLab From 626ae71c8b6531bc81c65d7458b706f18543e579 Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Wed, 12 Feb 2025 16:25:45 +0100 Subject: [PATCH 11/26] Fix build errors --- engine/cmake/generated_files.cmake | 1 + engine/src/Utils/Spawning/Interval.h | 6 +- engine/src/Utils/Spawning/SpawnSpace.h | 9 +-- engine/src/Utils/Spawning/Weight.cpp | 57 +++++++++++++++++++ engine/src/Utils/Spawning/Weight.h | 37 ------------ engine/src/Utils/Spawning/WeightedEntity.h | 26 +++++++++ .../GenericAction/TrafficAreaActionTest.cpp | 19 ++++--- 7 files changed, 98 insertions(+), 57 deletions(-) create mode 100644 engine/src/Utils/Spawning/Weight.cpp create mode 100644 engine/src/Utils/Spawning/WeightedEntity.h diff --git a/engine/cmake/generated_files.cmake b/engine/cmake/generated_files.cmake index 5e4d2eb2..1c4c3ab5 100644 --- a/engine/cmake/generated_files.cmake +++ b/engine/cmake/generated_files.cmake @@ -238,6 +238,7 @@ list(APPEND ${PROJECT_NAME}_SOURCES src/Utils/Spawning/SpawnSpace.cpp src/Utils/Spawning/SpawnZone.cpp src/Utils/Spawning/VelocityRange.cpp + src/Utils/Spawning/Weight.cpp src/Utils/ConditionEdgeEvaluator.cpp src/Utils/ControllerCreator.cpp src/Utils/ControllerService.cpp diff --git a/engine/src/Utils/Spawning/Interval.h b/engine/src/Utils/Spawning/Interval.h index 1f16640c..f1378417 100644 --- a/engine/src/Utils/Spawning/Interval.h +++ b/engine/src/Utils/Spawning/Interval.h @@ -127,12 +127,12 @@ std::vector<Interval> GetFreeIntervals(const mantle_api::ITrafficAreaStream& str auto entity{pointer.lock()}; const auto length{entity->GetProperties()->bounding_box.dimension.length}; const auto offset{entity->GetProperties()->bounding_box.geometric_center.x}; - auto [s, t]{stream.Convert(entity->GetPosition()).value()}; - if (const auto entityStartDistance{s + offset - length / units::dimensionless::scalar_t{2.0}}; entityStartDistance > min) + auto stream_pose{stream.Convert(mantle_api::Pose{entity->GetPosition(), entity->GetOrientation()}).value()}; + if (const auto entityStartDistance{stream_pose.s + offset - length / units::dimensionless::scalar_t{2.0}}; entityStartDistance > min) { EmplaceInterval(result, {min, entityStartDistance}, filter); } - min = s + offset + length / units::dimensionless::scalar_t{2.0}; + min = stream_pose.s + offset + length / units::dimensionless::scalar_t{2.0}; // TODO: This would loop endlessly if the object has a length of 0 and MIGHT loop endlessly if its offset lies outside of its bounds. } return result; diff --git a/engine/src/Utils/Spawning/SpawnSpace.h b/engine/src/Utils/Spawning/SpawnSpace.h index 2b07296f..9d1404d3 100644 --- a/engine/src/Utils/Spawning/SpawnSpace.h +++ b/engine/src/Utils/Spawning/SpawnSpace.h @@ -25,17 +25,10 @@ #include "SpawnSpot.h" #include "SpawnZone.h" #include "VelocityRange.h" +#include "WeightedEntity.h" namespace OpenScenarioEngine::v1_3 { -using Entity = typename std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject>; - -struct WeightedEntity -{ - Entity handle; - units::dimensionless::scalar_t weight; // Product of the distribution's weight and the weight of this entity's handle -}; - /// A SpawnSpace is essentially a self-managing, depleting list of TrafficAreas and weighted entities optimized /// for the use-case of spawning entities at the rear end of a TrafficArea (with offsets). It restructures the /// data to be sorted by interval and entity length, allowing efficient filtering of remaining spawnable entities diff --git a/engine/src/Utils/Spawning/Weight.cpp b/engine/src/Utils/Spawning/Weight.cpp new file mode 100644 index 00000000..5d45041a --- /dev/null +++ b/engine/src/Utils/Spawning/Weight.cpp @@ -0,0 +1,57 @@ +/******************************************************************************** + * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ +#include "Weight.h" + +#include "WeightedEntity.h" + +namespace OpenScenarioEngine::v1_3 +{ +units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IScenarioObjectTemplate& object) const +{ + return ToWeight{}(object.GetEntitiyObject()); // https://github.com/RA-Consulting-GmbH/openscenario.api.test/issues/222 +} + +units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::ITrafficDistributionEntry& entry) const +{ + return units::dimensionless::scalar_t{entry.GetWeight()}; +} + +units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject& object) const +{ + return object.GetVehicle() ? ToWeight{}(object.GetVehicle()) : ToWeight{}(object.GetPedestrian()); +} + +units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IPedestrian& pedestrian) const +{ + return ToWeight{}(pedestrian.GetProperties()); +} + +units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IVehicle& vehicle) const +{ + return ToWeight{}(vehicle.GetProperties()); +} + +units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IProperties& properties) const +{ + const auto& entries{properties.GetProperties()}; + auto match{std::find_if(entries.begin(), entries.end(), [](const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IProperty>& entry) + { return entry->GetName() == "Weight"; })}; + if (match != entries.end()) + { + return units::dimensionless::scalar_t{std::stod((*match)->GetValue())}; + } + throw std::runtime_error("ToWeight: 'Weight' not found in properties"); +} + +units::dimensionless::scalar_t ToWeight::operator()(const WeightedEntity& entity) const +{ + return ToWeight{}(entity.handle); +} +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Weight.h b/engine/src/Utils/Spawning/Weight.h index b068658b..2e003f12 100644 --- a/engine/src/Utils/Spawning/Weight.h +++ b/engine/src/Utils/Spawning/Weight.h @@ -72,43 +72,6 @@ units::dimensionless::scalar_t GetWeight(Input&& input) return ToWeight{}(std::forward<Input>(input)); } -units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IScenarioObjectTemplate& object) const -{ - return ToWeight{}(object.GetEntitiyObject()); // https://github.com/RA-Consulting-GmbH/openscenario.api.test/issues/222 -} - -units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::ITrafficDistributionEntry& entry) const -{ - return units::dimensionless::scalar_t{entry.GetWeight()}; -} - -units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject& object) const -{ - return object.GetVehicle() ? ToWeight{}(object.GetVehicle()) : ToWeight{}(object.GetPedestrian()); -} - -units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IPedestrian& pedestrian) const -{ - return ToWeight{}(pedestrian.GetProperties()); -} - -units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IVehicle& vehicle) const -{ - return ToWeight{}(vehicle.GetProperties()); -} - -units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IProperties& properties) const -{ - const auto& entries{properties.GetProperties()}; - auto match{std::find_if(entries.begin(), entries.end(), [](const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IProperty>& entry) - { return entry->GetName() == "Weight"; })}; - if (match != entries.end()) - { - return units::dimensionless::scalar_t{std::stod((*match)->GetValue())}; - } - throw std::runtime_error("ToWeight: 'Weight' not found in properties"); -} - template <typename Entry> units::dimensionless::scalar_t ToAccumulatedWeight::operator()(const Entry& entry) { diff --git a/engine/src/Utils/Spawning/WeightedEntity.h b/engine/src/Utils/Spawning/WeightedEntity.h new file mode 100644 index 00000000..e17b8183 --- /dev/null +++ b/engine/src/Utils/Spawning/WeightedEntity.h @@ -0,0 +1,26 @@ +/******************************************************************************** + * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ +#pragma once + +#include <openScenarioLib/generated/v1_3/api/ApiClassInterfacesV1_3.h> +#include <units.h> + +#include <memory> + +namespace OpenScenarioEngine::v1_3 +{ +using Entity = typename std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject>; + +struct WeightedEntity +{ + Entity handle; + units::dimensionless::scalar_t weight; // Product of the distribution's weight and the weight of this entity's handle +}; +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp index 33317b2c..defa65af 100644 --- a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp +++ b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp @@ -16,19 +16,20 @@ #include "TestUtils/TestLogger.h" using testing::_; +using testing::HasSubstr; using testing::Return; using testing::ReturnRef; -using testing::HasSubstr; -using testing::OpenScenarioEngine::v1_3::OpenScenarioEngineTestBase; using testing::OpenScenarioEngine::v1_3::GetScenariosPath; +using testing::OpenScenarioEngine::v1_3::OpenScenarioEngineTestBase; class TrafficAreaActionTestFixture : public OpenScenarioEngineTestBase { protected: void SetUp() override - { - //LOGGER = std::make_unique<testing::OpenScenarioEngine::v1_3::TestLogger>(); - //mock_environment_ = std::make_shared<mantle_api::MockEnvironment>(); + { + OpenScenarioEngineTestBase::SetUp(); + // LOGGER = std::make_unique<testing::OpenScenarioEngine::v1_3::TestLogger>(); + // mock_environment_ = std::make_shared<mantle_api::MockEnvironment>(); auto& mockTrafficAreaService{dynamic_cast<mantle_api::MockTrafficAreaService&>(env_->GetTrafficAreaService())}; ON_CALL(mockTrafficAreaService, CreateTrafficArea(_)).WillByDefault([this]() -> std::vector<std::shared_ptr<mantle_api::ITrafficAreaStream>> { @@ -37,9 +38,9 @@ protected: return traffic_area_stream_vec_; }); } - //std::shared_ptr<mantle_api::MockEnvironment> mock_environment_; + // std::shared_ptr<mantle_api::MockEnvironment> mock_environment_; std::vector<std::shared_ptr<mantle_api::ITrafficAreaStream>> traffic_area_stream_vec_; - //std::unique_ptr<testing::OpenScenarioEngine::v1_3::TestLogger> LOGGER; + // std::unique_ptr<testing::OpenScenarioEngine::v1_3::TestLogger> LOGGER; }; TEST_F(TrafficAreaActionTestFixture, GivenScenarioWithTrafficAreaAction_WhenInitScenario_Then) @@ -48,6 +49,6 @@ TEST_F(TrafficAreaActionTestFixture, GivenScenarioWithTrafficAreaAction_WhenInit OpenScenarioEngine::v1_3::OpenScenarioEngine engine(xosc_file_path, env_); engine.Init(); - //EXPECT_THAT(LOGGER->LastLogLevel(), mantle_api::LogLevel::kError); - //EXPECT_THAT(LOGGER->LastLogMessage(), HasSubstr("Method TrafficAreaAction::Step() not implemented yet (returning \"true\" by default)")); + // EXPECT_THAT(LOGGER->LastLogLevel(), mantle_api::LogLevel::kError); + // EXPECT_THAT(LOGGER->LastLogMessage(), HasSubstr("Method TrafficAreaAction::Step() not implemented yet (returning \"true\" by default)")); } -- GitLab From 57b90bbbbb2b5506936445982da85a0d006f967c Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Fri, 14 Feb 2025 15:56:33 +0100 Subject: [PATCH 12/26] Fix errors in TrafficAreaAction test configuration file --- .../GenericAction/TrafficAreaActionTest.cpp | 4 +- .../scenario_with_traffic_area_action.xosc | 117 ++++++++++++++++++ .../scenario_with_traffic_area_action.xosc | 11 +- 3 files changed, 124 insertions(+), 8 deletions(-) create mode 100644 engine/tests/Storyboard/GenericAction/data/Scenarios/scenario_with_traffic_area_action.xosc diff --git a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp index defa65af..59bfdd93 100644 --- a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp +++ b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp @@ -28,7 +28,7 @@ protected: void SetUp() override { OpenScenarioEngineTestBase::SetUp(); - // LOGGER = std::make_unique<testing::OpenScenarioEngine::v1_3::TestLogger>(); + LOGGER = std::make_unique<testing::OpenScenarioEngine::v1_3::TestLogger>(); // mock_environment_ = std::make_shared<mantle_api::MockEnvironment>(); auto& mockTrafficAreaService{dynamic_cast<mantle_api::MockTrafficAreaService&>(env_->GetTrafficAreaService())}; ON_CALL(mockTrafficAreaService, CreateTrafficArea(_)).WillByDefault([this]() -> std::vector<std::shared_ptr<mantle_api::ITrafficAreaStream>> @@ -40,7 +40,7 @@ protected: // std::shared_ptr<mantle_api::MockEnvironment> mock_environment_; std::vector<std::shared_ptr<mantle_api::ITrafficAreaStream>> traffic_area_stream_vec_; - // std::unique_ptr<testing::OpenScenarioEngine::v1_3::TestLogger> LOGGER; + std::shared_ptr<testing::OpenScenarioEngine::v1_3::TestLogger> LOGGER; }; TEST_F(TrafficAreaActionTestFixture, GivenScenarioWithTrafficAreaAction_WhenInitScenario_Then) diff --git a/engine/tests/Storyboard/GenericAction/data/Scenarios/scenario_with_traffic_area_action.xosc b/engine/tests/Storyboard/GenericAction/data/Scenarios/scenario_with_traffic_area_action.xosc new file mode 100644 index 00000000..f83d832a --- /dev/null +++ b/engine/tests/Storyboard/GenericAction/data/Scenarios/scenario_with_traffic_area_action.xosc @@ -0,0 +1,117 @@ +<?xml version='1.0' encoding='UTF-8'?> +<OpenSCENARIO> + <FileHeader revMajor="1" revMinor="3" date="2024-12-06T10:00:00" description="TrafficAreaAction Test" author="BMW AG" /> + <CatalogLocations> + <VehicleCatalog> + <Directory path="Vehicles" /> + </VehicleCatalog> + <PedestrianCatalog> + <Directory path="Vehicles" /> + </PedestrianCatalog> + </CatalogLocations> + <RoadNetwork> + <LogicFile filepath="SceneryConfiguration.xodr" /> + <SceneGraphFile filepath="" /> + </RoadNetwork> + <Entities> + <ScenarioObject name="Ego"> + <CatalogReference catalogName="VehicleCatalog" entryName="car_mini_cooper" /> + <ObjectController name="Ego"> + <Controller name="Ego"> + <Properties> + <Property name="AgentProfile" value="MiddleClassCarAgent" /> + </Properties> + </Controller> + </ObjectController> + </ScenarioObject> + </Entities> + <Storyboard> + <Init> + <Actions> + <GlobalAction> + <TrafficAction> + <!-- + https://publications.pages.asam.net/standards/ASAM_OpenSCENARIO/ASAM_OpenSCENARIO_XML/latest/generated/content/TrafficAreaAction.html --> + <TrafficAreaAction continuous="false" numberOfEntities="100"> + <TrafficDistribution> + <TrafficDistributionEntry weight="1.0"> + <EntityDistribution> + <EntityDistributionEntry weight="1.0"> + <ScenarioObjectTemplate> + <CatalogReference catalogName="VehicleCatalog" entryName="car_mini_cooper" /> + <ObjectController> + <Controller name="Common"> + <Properties> + <Property name="AgentProfile" value="MiddleClassCarAgent" /> + </Properties> + </Controller> + </ObjectController> + </ScenarioObjectTemplate> + </EntityDistributionEntry> + </EntityDistribution> + </TrafficDistributionEntry> + </TrafficDistribution> + <TrafficArea> + <RoadRange> + <RoadCursor roadId="road00" s="100" /> + <RoadCursor roadId="road00" s="500" /> + </RoadRange> + </TrafficArea> + </TrafficAreaAction> + </TrafficAction> + </GlobalAction> + <Private entityRef="Ego"> + <PrivateAction> + <TeleportAction> + <Position> + <LanePosition roadId="1" laneId="-1" offset="0.0" s="0.0"> + <Orientation type="relative" /> + </LanePosition> + </Position> + </TeleportAction> + </PrivateAction> + <PrivateAction> + <LongitudinalAction> + <SpeedAction> + <SpeedActionDynamics dynamicsShape="step" value="0.0" dynamicsDimension="rate" /> + <SpeedActionTarget> + <AbsoluteTargetSpeed value="43.5" /> + </SpeedActionTarget> + </SpeedAction> + </LongitudinalAction> + </PrivateAction> + <PrivateAction> + <ControllerAction> + <ActivateControllerAction objectControllerRef="Ego" lateral="true" longitudinal="true" /> + </ControllerAction> + </PrivateAction> + </Private> + </Actions> + </Init> + <Story name=""> + <Act name=""> + <ManeuverGroup name="" maximumExecutionCount="1"> + <Actors selectTriggeringEntities="false" /> + </ManeuverGroup> + <StartTrigger> + <ConditionGroup> + <Condition name="StartTime" delay="0" conditionEdge="rising"> + <ByValueCondition> + <SimulationTimeCondition value="0.0" rule="greaterOrEqual" /> + </ByValueCondition> + </Condition> + </ConditionGroup> + </StartTrigger> + </Act> + </Story> + <StopTrigger> + <ConditionGroup> + <Condition name="EndTime" delay="0" conditionEdge="rising"> + <ByValueCondition> + <SimulationTimeCondition value="30.0" rule="greaterThan" /> + </ByValueCondition> + </Condition> + </ConditionGroup> + </StopTrigger> + </Storyboard> +</OpenSCENARIO> \ No newline at end of file diff --git a/engine/tests/data/Scenarios/scenario_with_traffic_area_action.xosc b/engine/tests/data/Scenarios/scenario_with_traffic_area_action.xosc index caa81f9a..f83d832a 100644 --- a/engine/tests/data/Scenarios/scenario_with_traffic_area_action.xosc +++ b/engine/tests/data/Scenarios/scenario_with_traffic_area_action.xosc @@ -1,7 +1,6 @@ <?xml version='1.0' encoding='UTF-8'?> <OpenSCENARIO> - <FileHeader revMajor="1" revMinor="3" date="2024-12-06T10:00:00" - description="TrafficAreaAction Test" author="BMW AG" /> + <FileHeader revMajor="1" revMinor="3" date="2024-12-06T10:00:00" description="TrafficAreaAction Test" author="BMW AG" /> <CatalogLocations> <VehicleCatalog> <Directory path="Vehicles" /> @@ -17,7 +16,7 @@ <Entities> <ScenarioObject name="Ego"> <CatalogReference catalogName="VehicleCatalog" entryName="car_mini_cooper" /> - <ObjectController> + <ObjectController name="Ego"> <Controller name="Ego"> <Properties> <Property name="AgentProfile" value="MiddleClassCarAgent" /> @@ -35,7 +34,7 @@ https://publications.pages.asam.net/standards/ASAM_OpenSCENARIO/ASAM_OpenSCENARIO_XML/latest/generated/content/TrafficAreaAction.html --> <TrafficAreaAction continuous="false" numberOfEntities="100"> <TrafficDistribution> - <TrafficDistributionEntiry weight="1.0"> + <TrafficDistributionEntry weight="1.0"> <EntityDistribution> <EntityDistributionEntry weight="1.0"> <ScenarioObjectTemplate> @@ -50,7 +49,7 @@ </ScenarioObjectTemplate> </EntityDistributionEntry> </EntityDistribution> - </TrafficDistributionEntiry> + </TrafficDistributionEntry> </TrafficDistribution> <TrafficArea> <RoadRange> @@ -83,7 +82,7 @@ </PrivateAction> <PrivateAction> <ControllerAction> - <ActivateControllerAction controllerRef="Ego" lateral="true" longitudinal="true" /> + <ActivateControllerAction objectControllerRef="Ego" lateral="true" longitudinal="true" /> </ControllerAction> </PrivateAction> </Private> -- GitLab From f4a32a34594a8629257eea15857a0533233c976a Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Tue, 4 Mar 2025 17:21:35 +0100 Subject: [PATCH 13/26] Advance TrafficAreaAction test --- engine/CMakeLists.txt | 4 +- .../ConvertScenarioTrafficArea.cpp | 6 +- engine/src/Utils/Spawning/Weight.cpp | 19 ++++- engine/src/Utils/Spawning/Weight.h | 2 + .../GenericAction/TrafficAreaActionTest.cpp | 13 +++- .../data/Scenarios/VehicleCatalog.xosc | 20 +++++ .../scenario_with_traffic_area_action.xosc | 78 ++----------------- 7 files changed, 64 insertions(+), 78 deletions(-) create mode 100644 engine/tests/Storyboard/GenericAction/data/Scenarios/VehicleCatalog.xosc diff --git a/engine/CMakeLists.txt b/engine/CMakeLists.txt index 2b61c49a..c54e100d 100644 --- a/engine/CMakeLists.txt +++ b/engine/CMakeLists.txt @@ -75,9 +75,9 @@ target_include_directories( target_link_libraries(${PROJECT_NAME} PUBLIC antlr4_runtime::shared MantleAPI::MantleAPI openscenario_api::shared + Stochastics::Stochastics units::units - Yase::agnostic_behavior_tree - PRIVATE Stochastics::Stochastics) + Yase::agnostic_behavior_tree) if(USE_CCACHE) find_program(CCACHE_FOUND ccache) diff --git a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficArea.cpp b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficArea.cpp index 6a22f0ef..1c13fcfd 100644 --- a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficArea.cpp +++ b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficArea.cpp @@ -44,10 +44,10 @@ struct ToRoadRange TrafficArea ConvertScenarioTrafficArea(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ITrafficArea>& trafficArea) { - if (trafficArea->GetPolygon() != nullptr) + if (!trafficArea->GetRoadRange().empty()) { - throw std::runtime_error("ConvertScenarioTrafficArea: Conversion of polygon not yet supported"); + return TransformSharedPointers(trafficArea->GetRoadRange(), ToRoadRange{}); } - return TransformSharedPointers(trafficArea->GetRoadRange(), ToRoadRange{}); + throw std::runtime_error("ConvertScenarioTrafficArea: Conversion of polygon not yet supported"); } } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Weight.cpp b/engine/src/Utils/Spawning/Weight.cpp index 5d45041a..fdb7e4ec 100644 --- a/engine/src/Utils/Spawning/Weight.cpp +++ b/engine/src/Utils/Spawning/Weight.cpp @@ -25,7 +25,19 @@ units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO: units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject& object) const { - return object.GetVehicle() ? ToWeight{}(object.GetVehicle()) : ToWeight{}(object.GetPedestrian()); + if (object.IsSetVehicle()) + { + return ToWeight{}(object.GetVehicle()); + } + else if (object.IsSetPedestrian()) + { + return ToWeight{}(object.GetPedestrian()); + } + else if (object.IsSetMiscObject()) + { + return ToWeight{}(object.GetMiscObject()); + } + throw std::runtime_error("ToWeight: Invalid entity. Is neither a vehicle, pedestrian nor a misc. object"); } units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IPedestrian& pedestrian) const @@ -38,6 +50,11 @@ units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO: return ToWeight{}(vehicle.GetProperties()); } +units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IMiscObject& object) const +{ + return ToWeight{}(object.GetProperties()); +} + units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IProperties& properties) const { const auto& entries{properties.GetProperties()}; diff --git a/engine/src/Utils/Spawning/Weight.h b/engine/src/Utils/Spawning/Weight.h index 2e003f12..e5d83854 100644 --- a/engine/src/Utils/Spawning/Weight.h +++ b/engine/src/Utils/Spawning/Weight.h @@ -36,6 +36,8 @@ struct ToWeight units::dimensionless::scalar_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IVehicle&) const; + units::dimensionless::scalar_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IMiscObject&) const; + units::dimensionless::scalar_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IProperties&) const; units::dimensionless::scalar_t operator()(const WeightedEntity&) const; diff --git a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp index 59bfdd93..3212d377 100644 --- a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp +++ b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp @@ -9,8 +9,11 @@ ********************************************************************************/ #include <MantleAPI/Test/test_utils.h> +#include <Storyboard/GenericAction/TrafficAreaAction.h> #include <gtest/gtest.h> +#include <string> + #include "OpenScenarioEngine/OpenScenarioEngine.h" #include "TestUtils.h" #include "TestUtils/TestLogger.h" @@ -28,8 +31,9 @@ protected: void SetUp() override { OpenScenarioEngineTestBase::SetUp(); + ON_CALL(controller_, GetName()).WillByDefault(ReturnRef(controller_name)); + ON_CALL(controller_, GetUniqueId()).WillByDefault(Return(1234)); LOGGER = std::make_unique<testing::OpenScenarioEngine::v1_3::TestLogger>(); - // mock_environment_ = std::make_shared<mantle_api::MockEnvironment>(); auto& mockTrafficAreaService{dynamic_cast<mantle_api::MockTrafficAreaService&>(env_->GetTrafficAreaService())}; ON_CALL(mockTrafficAreaService, CreateTrafficArea(_)).WillByDefault([this]() -> std::vector<std::shared_ptr<mantle_api::ITrafficAreaStream>> { @@ -38,9 +42,10 @@ protected: return traffic_area_stream_vec_; }); } - // std::shared_ptr<mantle_api::MockEnvironment> mock_environment_; std::vector<std::shared_ptr<mantle_api::ITrafficAreaStream>> traffic_area_stream_vec_; std::shared_ptr<testing::OpenScenarioEngine::v1_3::TestLogger> LOGGER; + + std::string controller_name{"TestController"}; }; TEST_F(TrafficAreaActionTestFixture, GivenScenarioWithTrafficAreaAction_WhenInitScenario_Then) @@ -48,6 +53,10 @@ TEST_F(TrafficAreaActionTestFixture, GivenScenarioWithTrafficAreaAction_WhenInit std::string xosc_file_path{GetScenariosPath(test_info_->file()) + "scenario_with_traffic_area_action.xosc"}; OpenScenarioEngine::v1_3::OpenScenarioEngine engine(xosc_file_path, env_); engine.Init(); + engine.SetupDynamicContent(); + engine.Step({}); + + // OpenScenarioEngine::v1_3::TrafficAreaAction action; // EXPECT_THAT(LOGGER->LastLogLevel(), mantle_api::LogLevel::kError); // EXPECT_THAT(LOGGER->LastLogMessage(), HasSubstr("Method TrafficAreaAction::Step() not implemented yet (returning \"true\" by default)")); diff --git a/engine/tests/Storyboard/GenericAction/data/Scenarios/VehicleCatalog.xosc b/engine/tests/Storyboard/GenericAction/data/Scenarios/VehicleCatalog.xosc new file mode 100644 index 00000000..c1510b1e --- /dev/null +++ b/engine/tests/Storyboard/GenericAction/data/Scenarios/VehicleCatalog.xosc @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<OpenSCENARIO> + <FileHeader revMajor="1" revMinor="1" date="2025-02-26T00:00:00" author="BMW AG" description="Vehicle catalog" /> + <Catalog name="VehicleCatalog"> + <Vehicle name="my_car" vehicleCategory="car" model3d="medium_car"> + <BoundingBox> + <Center x="1.4" y="0.0" z="0.9" /> + <Dimensions width="2.0" length="5.0" height="1.8" /> + </BoundingBox> + <Performance maxSpeed="70" maxDeceleration="10" maxAcceleration="10" /> + <Axles> + <FrontAxle maxSteering="0.5" wheelDiameter="0.8" trackWidth="1.68" positionX="2.98" positionZ="0.4" /> + <RearAxle maxSteering="0" wheelDiameter="0.8" trackWidth="1.68" positionX="0" positionZ="0.4" /> + </Axles> + <Properties> + <Property name="custom_property" value="5" /> + </Properties> + </Vehicle> + </Catalog> +</OpenSCENARIO> diff --git a/engine/tests/Storyboard/GenericAction/data/Scenarios/scenario_with_traffic_area_action.xosc b/engine/tests/Storyboard/GenericAction/data/Scenarios/scenario_with_traffic_area_action.xosc index f83d832a..ef8d43c5 100644 --- a/engine/tests/Storyboard/GenericAction/data/Scenarios/scenario_with_traffic_area_action.xosc +++ b/engine/tests/Storyboard/GenericAction/data/Scenarios/scenario_with_traffic_area_action.xosc @@ -1,21 +1,18 @@ <?xml version='1.0' encoding='UTF-8'?> <OpenSCENARIO> - <FileHeader revMajor="1" revMinor="3" date="2024-12-06T10:00:00" description="TrafficAreaAction Test" author="BMW AG" /> + <FileHeader revMajor="1" revMinor="3" date="2025-02-26T00:00:00" description="TrafficAreaAction Test" author="BMW AG" /> <CatalogLocations> <VehicleCatalog> - <Directory path="Vehicles" /> + <Directory path="." /> </VehicleCatalog> - <PedestrianCatalog> - <Directory path="Vehicles" /> - </PedestrianCatalog> </CatalogLocations> <RoadNetwork> - <LogicFile filepath="SceneryConfiguration.xodr" /> + <LogicFile filepath="RoadNetwork.xodr" /> <SceneGraphFile filepath="" /> </RoadNetwork> <Entities> <ScenarioObject name="Ego"> - <CatalogReference catalogName="VehicleCatalog" entryName="car_mini_cooper" /> + <CatalogReference catalogName="VehicleCatalog" entryName="my_car" /> <ObjectController name="Ego"> <Controller name="Ego"> <Properties> @@ -30,22 +27,14 @@ <Actions> <GlobalAction> <TrafficAction> - <!-- - https://publications.pages.asam.net/standards/ASAM_OpenSCENARIO/ASAM_OpenSCENARIO_XML/latest/generated/content/TrafficAreaAction.html --> + <!-- https://publications.pages.asam.net/standards/ASAM_OpenSCENARIO/ASAM_OpenSCENARIO_XML/latest/generated/content/TrafficAreaAction.html --> <TrafficAreaAction continuous="false" numberOfEntities="100"> <TrafficDistribution> <TrafficDistributionEntry weight="1.0"> <EntityDistribution> <EntityDistributionEntry weight="1.0"> <ScenarioObjectTemplate> - <CatalogReference catalogName="VehicleCatalog" entryName="car_mini_cooper" /> - <ObjectController> - <Controller name="Common"> - <Properties> - <Property name="AgentProfile" value="MiddleClassCarAgent" /> - </Properties> - </Controller> - </ObjectController> + <CatalogReference catalogName="VehicleCatalog" entryName="my_car" /> </ScenarioObjectTemplate> </EntityDistributionEntry> </EntityDistribution> @@ -53,65 +42,14 @@ </TrafficDistribution> <TrafficArea> <RoadRange> - <RoadCursor roadId="road00" s="100" /> - <RoadCursor roadId="road00" s="500" /> + <RoadCursor roadId="DOES_NOT_EXIST" s="100" /> + <RoadCursor roadId="DOES_NOT_EXIST" s="500" /> </RoadRange> </TrafficArea> </TrafficAreaAction> </TrafficAction> </GlobalAction> - <Private entityRef="Ego"> - <PrivateAction> - <TeleportAction> - <Position> - <LanePosition roadId="1" laneId="-1" offset="0.0" s="0.0"> - <Orientation type="relative" /> - </LanePosition> - </Position> - </TeleportAction> - </PrivateAction> - <PrivateAction> - <LongitudinalAction> - <SpeedAction> - <SpeedActionDynamics dynamicsShape="step" value="0.0" dynamicsDimension="rate" /> - <SpeedActionTarget> - <AbsoluteTargetSpeed value="43.5" /> - </SpeedActionTarget> - </SpeedAction> - </LongitudinalAction> - </PrivateAction> - <PrivateAction> - <ControllerAction> - <ActivateControllerAction objectControllerRef="Ego" lateral="true" longitudinal="true" /> - </ControllerAction> - </PrivateAction> - </Private> </Actions> </Init> - <Story name=""> - <Act name=""> - <ManeuverGroup name="" maximumExecutionCount="1"> - <Actors selectTriggeringEntities="false" /> - </ManeuverGroup> - <StartTrigger> - <ConditionGroup> - <Condition name="StartTime" delay="0" conditionEdge="rising"> - <ByValueCondition> - <SimulationTimeCondition value="0.0" rule="greaterOrEqual" /> - </ByValueCondition> - </Condition> - </ConditionGroup> - </StartTrigger> - </Act> - </Story> - <StopTrigger> - <ConditionGroup> - <Condition name="EndTime" delay="0" conditionEdge="rising"> - <ByValueCondition> - <SimulationTimeCondition value="30.0" rule="greaterThan" /> - </ByValueCondition> - </Condition> - </ConditionGroup> - </StopTrigger> </Storyboard> </OpenSCENARIO> \ No newline at end of file -- GitLab From 537c4ad06b0de090c6eff92e7cde2da2cee677a2 Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Tue, 4 Mar 2025 17:21:59 +0100 Subject: [PATCH 14/26] Hotfix parser for RoadRange --- utils/ci/conan/recipe/openscenario_api/all/conanfile.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/utils/ci/conan/recipe/openscenario_api/all/conanfile.py b/utils/ci/conan/recipe/openscenario_api/all/conanfile.py index dbe00a8f..4e4482b5 100644 --- a/utils/ci/conan/recipe/openscenario_api/all/conanfile.py +++ b/utils/ci/conan/recipe/openscenario_api/all/conanfile.py @@ -1,5 +1,5 @@ ################################################################################ -# Copyright (c) 2023 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) +# Copyright (c) 2023-2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) # 2024 Volkswagen AG # # This program and the accompanying materials are made available under the @@ -15,7 +15,7 @@ import os from conan import ConanFile -from conan.tools.files import copy, export_conandata_patches, apply_conandata_patches +from conan.tools.files import apply_conandata_patches, copy, export_conandata_patches, replace_in_file from conan.tools.scm import Git required_conan_version = ">=1.53.0" @@ -55,6 +55,10 @@ class OpenScenarioApiConan(ConanFile): self._repo_source = os.path.join(self.source_folder, self.name) self._artifact_path = os.path.join(self._repo_source, "cpp", "buildArtifact") apply_conandata_patches(self) + replace_in_file(self, + os.path.join(self._repo_source, "cpp/openScenarioLib/src/parser/modelgroup/XmlChoiceParser.cpp"), + "if (currentOccurs < parser->GetMaxOccur())", + "if (currentOccurs < parser->GetMaxOccur() || parser->GetMaxOccur() == -1)") def build(self): -- GitLab From dda49c1ebfd7262bcf4dc46249b9703afb80c3a4 Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Wed, 5 Mar 2025 13:55:15 +0100 Subject: [PATCH 15/26] Fix issues in TrafficAreaAction implementation --- engine/src/Utils/Spawning/Common.h | 15 +++-------- engine/src/Utils/Spawning/SpawnSpace.cpp | 27 +++++++++++-------- engine/src/Utils/Spawning/VelocityRange.cpp | 19 ++++++++++++- engine/src/Utils/Spawning/VelocityRange.h | 2 +- engine/src/Utils/Spawning/Weight.cpp | 2 +- .../GenericAction/TrafficAreaActionTest.cpp | 5 ---- .../scenario_with_traffic_area_action.xosc | 2 +- 7 files changed, 41 insertions(+), 31 deletions(-) diff --git a/engine/src/Utils/Spawning/Common.h b/engine/src/Utils/Spawning/Common.h index eff3b449..4814e145 100644 --- a/engine/src/Utils/Spawning/Common.h +++ b/engine/src/Utils/Spawning/Common.h @@ -76,17 +76,10 @@ constexpr decltype(auto) BinaryOperation<Operation, Transformer>::operator()(A&& template <typename Elements, typename Thresholds> typename Elements::const_reference Sample(const Elements& elements, const Thresholds& thresholds, typename Thresholds::const_reference value) { - assert(elements.size() == thresholds.size()); - const auto threshold{std::upper_bound(std::next(thresholds.begin()), thresholds.end(), value)}; - const size_t index{static_cast<size_t>(std::distance(thresholds.begin(), threshold) - 1)}; + assert(elements.size() <= thresholds.size()); + const size_t index_offset{thresholds.size() - elements.size()}; + const auto threshold{std::upper_bound(std::next(thresholds.begin(), index_offset), thresholds.end(), value)}; + const size_t index{static_cast<size_t>(std::distance(thresholds.begin(), threshold) - index_offset)}; return elements[index]; } -// template <typename Type> -// std::pair<Type, size_t> Sample(const std::vector<Type>& range) -// { -// const units::length::meter_t sample{range.back()}; // TODO: Use Stochastics -// const auto upperBound{std::upper_bound(range.begin(), std::prev(range.end()), sample)}; -// const auto index{static_cast<size_t>(std::distance(range.begin(), upperBound))}; -// return {sample, index}; -// } } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/SpawnSpace.cpp b/engine/src/Utils/Spawning/SpawnSpace.cpp index a1090f7b..35640587 100644 --- a/engine/src/Utils/Spawning/SpawnSpace.cpp +++ b/engine/src/Utils/Spawning/SpawnSpace.cpp @@ -20,6 +20,7 @@ namespace OpenScenarioEngine::v1_3 { SpawnSpace::SpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &distributions, std::vector<SpawnZones> &zones) + : weights{{}} { // Add entities { @@ -29,20 +30,23 @@ SpawnSpace::SpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &dis for (auto &distribution : distributions) { std::transform(distribution.begin(), distribution.end(), std::back_inserter(entities), // - [multiplier = distribution.weight](const Entity &entity) -> WeightedEntity - { return {entity, GetWeight(entity) * multiplier}; }); + [weight = distribution.weight](const Entity &entity) -> WeightedEntity + { return {entity, weight}; }); } std::sort(entities.begin(), entities.end(), Less<ToLength>{}); - weights = Transform(entities, ToAccumulatedWeight{}); + Transform(entities, weights, ToAccumulatedWeight{}); // Add intervals for (const SpawnZones ®ions : zones) { for (const SpawnZone &zone : regions) { - for (auto obstacle{std::next(zone.obstacles.begin())}; obstacle != zone.obstacles.end(); ++obstacle) + if (zone.obstacles.size() > 1) { - spots.emplace(*std::prev(obstacle), *obstacle); + for (auto obstacle{std::next(zone.obstacles.begin())}; obstacle != zone.obstacles.end(); ++obstacle) + { + spots.emplace(*std::prev(obstacle), *obstacle); + } } } } @@ -52,9 +56,9 @@ Entity SpawnSpace::SampleEntity(StochasticsInterface &rng) const { const units::length::meter_t max_length{GetMaxIntervalLength()}; const auto entity_end{FindSpawnableEntityEnd(max_length)}; - if (entity_end == entities.begin()) + if (std::distance(entities.begin(), entity_end) <= 1) { - return nullptr; + return entity_end == entities.begin() ? Entity{} : entities.front().handle; } assert(weights.size() == entities.size() + 1); const units::dimensionless::scalar_t max_weight{weights[static_cast<size_t>(std::distance(entities.begin(), entity_end))]}; @@ -64,7 +68,7 @@ Entity SpawnSpace::SampleEntity(StochasticsInterface &rng) const units::length::meter_t SpawnSpace::GetMaxIntervalLength() const { - return GetLength(*spots.rbegin()); + return spots.empty() ? units::length::meter_t{} : GetLength(*spots.rbegin()); } std::vector<WeightedEntity>::const_iterator SpawnSpace::FindSpawnableEntityEnd(units::length::meter_t upperBound) const @@ -100,9 +104,10 @@ std::set<SpawnSpot, Less<ToLength>>::const_iterator SpawnSpace::SampleSpot(units return candidates.empty() ? spots.end() : *candidates.begin(); } ToAccumulatedLength accumulator; - std::vector<units::length::meter_t> lengths{Transform(candidates, - [&accumulator](const auto iterator) - { return accumulator(*iterator); })}; + std::vector<units::length::meter_t> lengths{{}}; + Transform(candidates, lengths, [&accumulator](const auto iterator) { // + return accumulator(*iterator); + }); const units::length::meter_t sampledLength{rng.GetUniformDistributed(.0, lengths.back().value())}; return Sample(candidates, lengths, sampledLength); } diff --git a/engine/src/Utils/Spawning/VelocityRange.cpp b/engine/src/Utils/Spawning/VelocityRange.cpp index a1326dac..d6bbd78c 100644 --- a/engine/src/Utils/Spawning/VelocityRange.cpp +++ b/engine/src/Utils/Spawning/VelocityRange.cpp @@ -17,7 +17,19 @@ namespace OpenScenarioEngine::v1_3 { VelocityRange ToSpawnVelocityRange::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject &object) const { - return object.GetVehicle() ? (*this)(object.GetVehicle()) : (*this)(object.GetPedestrian()); + if (object.IsSetVehicle()) + { + return (*this)(object.GetVehicle()); + } + else if (object.IsSetPedestrian()) + { + return (*this)(object.GetPedestrian()); + } + else if (object.IsSetMiscObject()) + { + return (*this)(object.GetMiscObject()); + } + throw std::runtime_error("ToSpawmVelocityRange: Invalid entity. Is neither a vehicle, pedestrian or misc. object"); } VelocityRange ToSpawnVelocityRange::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IVehicle &vehicle) const @@ -30,6 +42,11 @@ VelocityRange ToSpawnVelocityRange::operator()(const NET_ASAM_OPENSCENARIO::v1_3 return (*this)(pedestrian.GetProperties()); } +VelocityRange ToSpawnVelocityRange::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IMiscObject &object) const +{ + return (*this)(object.GetProperties()); +} + VelocityRange ToSpawnVelocityRange::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IProperties &properties) const { const auto &entries{properties.GetProperties()}; diff --git a/engine/src/Utils/Spawning/VelocityRange.h b/engine/src/Utils/Spawning/VelocityRange.h index e851efc0..180019f2 100644 --- a/engine/src/Utils/Spawning/VelocityRange.h +++ b/engine/src/Utils/Spawning/VelocityRange.h @@ -36,9 +36,9 @@ struct ToSpawnVelocityRange VelocityRange operator()(const std::shared_ptr<Type> &) const; VelocityRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject &) const; - VelocityRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IMiscObject &) const; VelocityRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IVehicle &) const; VelocityRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IPedestrian &) const; + VelocityRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IMiscObject &) const; VelocityRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IProperties &) const; }; diff --git a/engine/src/Utils/Spawning/Weight.cpp b/engine/src/Utils/Spawning/Weight.cpp index fdb7e4ec..96774fbc 100644 --- a/engine/src/Utils/Spawning/Weight.cpp +++ b/engine/src/Utils/Spawning/Weight.cpp @@ -69,6 +69,6 @@ units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO: units::dimensionless::scalar_t ToWeight::operator()(const WeightedEntity& entity) const { - return ToWeight{}(entity.handle); + return entity.weight; } } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp index 3212d377..c9a2de01 100644 --- a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp +++ b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp @@ -55,9 +55,4 @@ TEST_F(TrafficAreaActionTestFixture, GivenScenarioWithTrafficAreaAction_WhenInit engine.Init(); engine.SetupDynamicContent(); engine.Step({}); - - // OpenScenarioEngine::v1_3::TrafficAreaAction action; - - // EXPECT_THAT(LOGGER->LastLogLevel(), mantle_api::LogLevel::kError); - // EXPECT_THAT(LOGGER->LastLogMessage(), HasSubstr("Method TrafficAreaAction::Step() not implemented yet (returning \"true\" by default)")); } diff --git a/engine/tests/Storyboard/GenericAction/data/Scenarios/scenario_with_traffic_area_action.xosc b/engine/tests/Storyboard/GenericAction/data/Scenarios/scenario_with_traffic_area_action.xosc index ef8d43c5..191987e1 100644 --- a/engine/tests/Storyboard/GenericAction/data/Scenarios/scenario_with_traffic_area_action.xosc +++ b/engine/tests/Storyboard/GenericAction/data/Scenarios/scenario_with_traffic_area_action.xosc @@ -13,7 +13,7 @@ <Entities> <ScenarioObject name="Ego"> <CatalogReference catalogName="VehicleCatalog" entryName="my_car" /> - <ObjectController name="Ego"> + <ObjectController> <Controller name="Ego"> <Properties> <Property name="AgentProfile" value="MiddleClassCarAgent" /> -- GitLab From 0250f04e90edbc4934234d85e71bfed4fa0a422b Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Wed, 5 Mar 2025 15:04:42 +0100 Subject: [PATCH 16/26] Support catalog references in TrafficAreaAction --- engine/src/Utils/Spawning/Length.cpp | 53 +++++++++++++++++-- engine/src/Utils/Spawning/Length.h | 8 +++ engine/src/Utils/Spawning/VelocityRange.cpp | 36 ++++++++++--- engine/src/Utils/Spawning/VelocityRange.h | 1 + .../data/Scenarios/VehicleCatalog.xosc | 2 + 5 files changed, 89 insertions(+), 11 deletions(-) diff --git a/engine/src/Utils/Spawning/Length.cpp b/engine/src/Utils/Spawning/Length.cpp index 8643a25f..03706dd3 100644 --- a/engine/src/Utils/Spawning/Length.cpp +++ b/engine/src/Utils/Spawning/Length.cpp @@ -10,6 +10,8 @@ #include "Length.h" +#include <openScenarioLib/generated/v1_3/catalog/CatalogHelperV1_3.h> + #include <cassert> #include <numeric> @@ -52,16 +54,57 @@ units::length::meter_t ToLength::operator()(const SpawnZones& zones) const return std::transform_reduce(zones.begin(), zones.end(), units::length::meter_t{}, std::plus<>{}, *this); } +units::length::meter_t ToLength::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IBoundingBox& bounding_box) const +{ + return units::length::meter_t{bounding_box.GetDimensions()->GetLength()}; +} + +units::length::meter_t ToLength::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IVehicle& vehicle) const +{ + return (*this)(vehicle.GetBoundingBox()); +} + +units::length::meter_t ToLength::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IPedestrian& pedestrian) const +{ + return (*this)(pedestrian.GetBoundingBox()); +} + +units::length::meter_t ToLength::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IMiscObject& misc_object) const +{ + return (*this)(misc_object.GetBoundingBox()); +} + units::length::meter_t ToLength::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject& object) const { - if (decltype(auto) vehicle{object.GetVehicle()}; vehicle != nullptr) + if (object.IsSetCatalogReference()) + { + const auto& catalog_reference{object.GetCatalogReference()->GetRef()}; + auto& element{const_cast<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ICatalogElement>&>(catalog_reference)}; + if (NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::IsVehicle(element)) + { + return (*this)(NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::AsVehicle(element)); + } + if (NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::IsPedestrian(element)) + { + return (*this)(NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::AsPedestrian(element)); + } + if (NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::IsMiscObject(element)) + { + return (*this)(NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::AsMiscObject(element)); + } + } + if (object.IsSetVehicle()) + { + return (*this)(object.GetVehicle()); + } + if (object.IsSetPedestrian()) { - return units::length::meter_t{vehicle->GetBoundingBox()->GetDimensions()->GetLength()}; + return (*this)(object.GetPedestrian()); } - if (decltype(auto) pedestrian{object.GetPedestrian()}; pedestrian != nullptr) + if (object.IsSetMiscObject()) { - return units::length::meter_t{pedestrian->GetBoundingBox()->GetDimensions()->GetLength()}; + return (*this)(object.GetMiscObject()); } - return units::length::meter_t{std::numeric_limits<double>::quiet_NaN()}; + throw std::runtime_error("ToLength: Entity is not a catalog reference, vehicle, pedestrian or misc. object."); } } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Length.h b/engine/src/Utils/Spawning/Length.h index 7c81c36f..e5c9702d 100644 --- a/engine/src/Utils/Spawning/Length.h +++ b/engine/src/Utils/Spawning/Length.h @@ -44,6 +44,14 @@ struct ToLength units::length::meter_t operator()(const mantle_api::EntityProperties&) const; + units::length::meter_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IBoundingBox&) const; + + units::length::meter_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IVehicle&) const; + + units::length::meter_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IPedestrian&) const; + + units::length::meter_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IMiscObject&) const; + template <typename Type> units::length::meter_t operator()(const Type*) const; diff --git a/engine/src/Utils/Spawning/VelocityRange.cpp b/engine/src/Utils/Spawning/VelocityRange.cpp index d6bbd78c..d3f03f50 100644 --- a/engine/src/Utils/Spawning/VelocityRange.cpp +++ b/engine/src/Utils/Spawning/VelocityRange.cpp @@ -10,6 +10,8 @@ #include "VelocityRange.h" +#include <openScenarioLib/generated/v1_3/catalog/CatalogHelperV1_3.h> + #include <exception> #include <string> @@ -17,21 +19,43 @@ namespace OpenScenarioEngine::v1_3 { VelocityRange ToSpawnVelocityRange::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject &object) const { + if (object.IsSetCatalogReference()) + { + return (*this)(object.GetCatalogReference()->GetRef()); + } if (object.IsSetVehicle()) { return (*this)(object.GetVehicle()); } - else if (object.IsSetPedestrian()) + if (object.IsSetPedestrian()) { return (*this)(object.GetPedestrian()); } - else if (object.IsSetMiscObject()) + if (object.IsSetMiscObject()) { return (*this)(object.GetMiscObject()); } throw std::runtime_error("ToSpawmVelocityRange: Invalid entity. Is neither a vehicle, pedestrian or misc. object"); } +VelocityRange ToSpawnVelocityRange::operator()(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ICatalogElement> &input) const +{ + auto &element{const_cast<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ICatalogElement> &>(input)}; + if (NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::IsVehicle(element)) + { + return (*this)(NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::AsVehicle(element)); + } + if (NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::IsPedestrian(element)) + { + return (*this)(NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::AsPedestrian(element)); + } + if (NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::IsMiscObject(element)) + { + return (*this)(NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::AsMiscObject(element)); + } + throw std::runtime_error("ToSpawnVelocityRange: Invalid catalog reference. Contains no vehicle, pedestrian or misc. object."); +} + VelocityRange ToSpawnVelocityRange::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IVehicle &vehicle) const { return (*this)(vehicle.GetProperties()); @@ -53,18 +77,18 @@ VelocityRange ToSpawnVelocityRange::operator()(const NET_ASAM_OPENSCENARIO::v1_3 const auto min_match{std::find_if(entries.begin(), entries.end(), [](const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IProperty> &entry) - { return entry->GetName() == "MinVelocity"; })}; + { return entry->GetName() == "min_spawn_velocity"; })}; if (min_match == entries.end()) { - throw std::range_error("ToSpawnVelocityRange: 'MinVelocity' not found in properties"); + throw std::range_error("ToSpawnVelocityRange: 'min_spawn_velocity' not found in properties"); } const auto max_match{std::find_if(entries.begin(), entries.end(), [](const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IProperty> &entry) - { return entry->GetName() == "MaxVelocity"; })}; + { return entry->GetName() == "max_spawn_velocity"; })}; if (max_match == entries.end()) { - throw std::range_error("ToSpawnVelocityRange: 'MaxVelocity' not found in properties"); + throw std::range_error("ToSpawnVelocityRange: 'max_spawn_velocity' not found in properties"); } return {units::velocity::meters_per_second_t{std::stod((*min_match)->GetValue())}, units::velocity::meters_per_second_t{std::stod((*max_match)->GetValue())}}; } diff --git a/engine/src/Utils/Spawning/VelocityRange.h b/engine/src/Utils/Spawning/VelocityRange.h index 180019f2..75d60e15 100644 --- a/engine/src/Utils/Spawning/VelocityRange.h +++ b/engine/src/Utils/Spawning/VelocityRange.h @@ -35,6 +35,7 @@ struct ToSpawnVelocityRange template <typename Type> VelocityRange operator()(const std::shared_ptr<Type> &) const; + VelocityRange operator()(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ICatalogElement> &) const; VelocityRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject &) const; VelocityRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IVehicle &) const; VelocityRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IPedestrian &) const; diff --git a/engine/tests/Storyboard/GenericAction/data/Scenarios/VehicleCatalog.xosc b/engine/tests/Storyboard/GenericAction/data/Scenarios/VehicleCatalog.xosc index c1510b1e..1314dc68 100644 --- a/engine/tests/Storyboard/GenericAction/data/Scenarios/VehicleCatalog.xosc +++ b/engine/tests/Storyboard/GenericAction/data/Scenarios/VehicleCatalog.xosc @@ -14,6 +14,8 @@ </Axles> <Properties> <Property name="custom_property" value="5" /> + <Property name="min_spawn_velocity" value="20" /> + <Property name="max_spawn_velocity" value="80" /> </Properties> </Vehicle> </Catalog> -- GitLab From bba1a777d04881a9038d6a7c965cbe3818b6e505 Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Thu, 6 Mar 2025 15:14:43 +0100 Subject: [PATCH 17/26] Add SequentialSpawnSpace --- .../GenericAction/TrafficAreaAction.cpp | 2 +- engine/src/Utils/Spawning/Length.h | 1 + engine/src/Utils/Spawning/SpawnSpace.cpp | 62 ++++++++++++++----- engine/src/Utils/Spawning/SpawnSpace.h | 61 +++++++++++++++--- engine/src/Utils/Spawning/SpawnSpot.h | 4 +- engine/src/Utils/Spawning/SpawnZone.cpp | 28 ++++++++- engine/src/Utils/Spawning/SpawnZone.h | 39 ++---------- engine/src/Utils/Spawning/VelocityRange.cpp | 5 ++ engine/src/Utils/Spawning/VelocityRange.h | 14 ++--- .../GenericAction/TrafficAreaActionTest.cpp | 7 ++- 10 files changed, 150 insertions(+), 73 deletions(-) diff --git a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp index 5a1bfbf5..fe3abb5e 100644 --- a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp +++ b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp @@ -63,7 +63,7 @@ bool TrafficAreaAction::Step() } EntityCreator spawner{mantle.environment}; std::vector<SpawnZones> spawnZones{Transform(trafficAreas, ToSpawnZones{})}; - SpawnSpace spawnSpace{values.trafficDistributions, spawnZones}; + SequentialSpawnSpace spawnSpace{values.trafficDistributions, spawnZones}; while (values.numberOfEntities) { if (!spawnSpace.Spawn(spawner, rng)) diff --git a/engine/src/Utils/Spawning/Length.h b/engine/src/Utils/Spawning/Length.h index e5c9702d..20650dda 100644 --- a/engine/src/Utils/Spawning/Length.h +++ b/engine/src/Utils/Spawning/Length.h @@ -131,6 +131,7 @@ constexpr size_t ToSize::operator()(const std::vector<Type>& container) const template <typename Type> units::length::meter_t GetLength(const Type& item) { + std::cout << "Length: " << ToLength{}(item) << '\n'; return ToLength{}(item); } diff --git a/engine/src/Utils/Spawning/SpawnSpace.cpp b/engine/src/Utils/Spawning/SpawnSpace.cpp index 35640587..95ff4952 100644 --- a/engine/src/Utils/Spawning/SpawnSpace.cpp +++ b/engine/src/Utils/Spawning/SpawnSpace.cpp @@ -35,24 +35,51 @@ SpawnSpace::SpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &dis } std::sort(entities.begin(), entities.end(), Less<ToLength>{}); Transform(entities, weights, ToAccumulatedWeight{}); +} - // Add intervals +SequentialSpawnSpace::SequentialSpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &distributions, std::vector<SpawnZones> &zones) + : SpawnSpace{distributions, zones} +{ // clang-format off + std::transform(zones.begin(), zones.end(), std::back_inserter(spots), [](const SpawnZones &zones) { + std::vector<SpawnSpot> spots; + for(const SpawnZone& zone : zones) { + auto next_spots{zone.GetSpawnSpots()}; + spots.insert(spots.end(), std::make_move_iterator(next_spots.begin()), std::make_move_iterator(next_spots.end())); + } + return spots; + }); + spots.erase(std::remove_if(spots.begin(), spots.end(), [](const auto& container) { return container.empty(); }), spots.end()); + // clang-format on +} + +Entity SequentialSpawnSpace::SampleEntity(StochasticsInterface &rng) const +{ + if (entities.size() <= 1) + { + return entities.empty() ? Entity{} : entities.front().handle; + } + assert(weights.size() == entities.size() + 1); + const units::dimensionless::scalar_t sampled_weight{rng.GetUniformDistributed(.0, weights.back().value())}; + return Sample(entities, weights, sampled_weight).handle; +} + +DistributedSpawnSpace::DistributedSpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &distributions, std::vector<SpawnZones> &zones) + : SpawnSpace{distributions, zones} +{ for (const SpawnZones ®ions : zones) { for (const SpawnZone &zone : regions) { - if (zone.obstacles.size() > 1) + assert(zone.obstacles.size() > 1); + for (auto obstacle{std::next(zone.obstacles.begin())}; obstacle != zone.obstacles.end(); ++obstacle) { - for (auto obstacle{std::next(zone.obstacles.begin())}; obstacle != zone.obstacles.end(); ++obstacle) - { - spots.emplace(*std::prev(obstacle), *obstacle); - } + spots.emplace(*std::prev(obstacle), *obstacle); } } } } -Entity SpawnSpace::SampleEntity(StochasticsInterface &rng) const +Entity DistributedSpawnSpace::SampleEntity(StochasticsInterface &rng) const { const units::length::meter_t max_length{GetMaxIntervalLength()}; const auto entity_end{FindSpawnableEntityEnd(max_length)}; @@ -66,23 +93,23 @@ Entity SpawnSpace::SampleEntity(StochasticsInterface &rng) const return Sample(entities, weights, sampled_weight).handle; } -units::length::meter_t SpawnSpace::GetMaxIntervalLength() const +units::length::meter_t DistributedSpawnSpace::GetMaxIntervalLength() const { return spots.empty() ? units::length::meter_t{} : GetLength(*spots.rbegin()); } -std::vector<WeightedEntity>::const_iterator SpawnSpace::FindSpawnableEntityEnd(units::length::meter_t upperBound) const +std::vector<WeightedEntity>::const_iterator DistributedSpawnSpace::FindSpawnableEntityEnd(units::length::meter_t threshold) const { - return std::upper_bound(entities.begin(), entities.end(), upperBound, [](units::length::meter_t upperBound, const WeightedEntity &entity) - { return GetLength(entity) < upperBound; }); + return std::upper_bound(entities.begin(), entities.end(), threshold, [](units::length::meter_t threshold, const WeightedEntity &entity) + { return threshold < GetLength(entity); }); } -std::vector<WeightedEntity>::iterator SpawnSpace::FindSpawnableEntityEnd(units::length::meter_t upperBound) +std::vector<WeightedEntity>::iterator DistributedSpawnSpace::FindSpawnableEntityEnd(units::length::meter_t threshold) { - return std::upper_bound(entities.begin(), entities.end(), upperBound, [](units::length::meter_t upperBound, const WeightedEntity &entity) - { return GetLength(entity) < upperBound; }); + return std::upper_bound(entities.begin(), entities.end(), threshold, [](units::length::meter_t threshold, const WeightedEntity &entity) + { return threshold < GetLength(entity); }); } -std::set<SpawnSpot, Less<ToLength>>::const_iterator SpawnSpace::SampleSpot(units::length::meter_t minLength, VelocityRange velocity, StochasticsInterface &rng) const +std::set<SpawnSpot, Less<ToLength>>::const_iterator DistributedSpawnSpace::SampleSpot(units::length::meter_t minLength, VelocityRange velocity, StochasticsInterface &rng) const { auto spot{spots.lower_bound(minLength)}; if (spot == spots.end()) @@ -93,8 +120,9 @@ std::set<SpawnSpot, Less<ToLength>>::const_iterator SpawnSpace::SampleSpot(units for (; spot != spots.end(); ++spot) { const bool canKeepUp{spot->velocity.Contains(velocity)}; - const bool isEnoughRoomAfter2Seconds{GetLength(*spot) - spot->velocity.GetDifference() * units::time::second_t{2.0} > minLength}; - if (canKeepUp && isEnoughRoomAfter2Seconds) + const bool isEnoughRoomNow{GetLength(*spot) > minLength}; + const bool isEnoughRoomAfter2Seconds{GetLength(*spot) + spot->velocity.GetDifference() * units::time::second_t{2.0} > minLength}; + if (canKeepUp && isEnoughRoomNow && isEnoughRoomAfter2Seconds) { candidates.push_back(spot); } diff --git a/engine/src/Utils/Spawning/SpawnSpace.h b/engine/src/Utils/Spawning/SpawnSpace.h index 9d1404d3..28593c85 100644 --- a/engine/src/Utils/Spawning/SpawnSpace.h +++ b/engine/src/Utils/Spawning/SpawnSpace.h @@ -29,13 +29,46 @@ namespace OpenScenarioEngine::v1_3 { -/// A SpawnSpace is essentially a self-managing, depleting list of TrafficAreas and weighted entities optimized +struct SpawnSpace +{ + SpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &, std::vector<SpawnZones> &); + + std::vector<WeightedEntity> entities; // Entities sorted by ascending length + std::vector<units::dimensionless::scalar_t> weights; // Used to sample an entity +}; + +/// Space that spawns entities naively & greedily by design as defined here: +/// https://eclipse.dev/openpass/content/html/user_guide/sim_user_guide/components/spawner.html#spawner +/// The algorithm is greedy because it does not restrict what entities it samples from based on the available space. Instead, it +/// samples a vehicle & velocity, then finds the first area it can be spawned in. If no such area exists, spawning is aborted. The +/// algorithm is naive because no memoization of area sizes occurs, meaning all areas are tested anew when another entity is being spawned. +struct SequentialSpawnSpace : SpawnSpace +{ + SequentialSpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &, std::vector<SpawnZones> &); + + /// Samples and returns a random entity without considering the remaining space. + /// + /// \return Entity Random entity out of this space's entities. If it has none, a nullptr is returned. + Entity SampleEntity(StochasticsInterface &) const; + + /// Tries to spawn an entity and returns whether spawning was successful. + /// If successful, also adjusts the spawn space's intervals. + /// + /// \tparam EntityCreator Provides CreateEntity(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IScenarioObject> &) + /// \param StochasticsInterface Used to sample the entity and its velocity + template <typename EntityCreator> + bool Spawn(EntityCreator &&, StochasticsInterface &); + + std::vector<std::vector<SpawnSpot>> spots; +}; + +/// A DistributedSpawnSpace is essentially a self-managing, depleting list of TrafficAreas and weighted entities optimized /// for the use-case of spawning entities at the rear end of a TrafficArea (with offsets). It restructures the /// data to be sorted by interval and entity length, allowing efficient filtering of remaining spawnable entities /// and intervals as the space is filled. -struct SpawnSpace +struct DistributedSpawnSpace : SpawnSpace { - SpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &, std::vector<SpawnZones> &); + DistributedSpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &, std::vector<SpawnZones> &); /// Returns the entity out of the set of entities in this space based on the given sample /// @@ -59,18 +92,28 @@ struct SpawnSpace std::vector<WeightedEntity>::iterator FindSpawnableEntityEnd(units::length::meter_t upper_bound); std::set<SpawnSpot, Less<ToLength>> spots; - - std::vector<WeightedEntity> entities; // Entities sorted by ascending length - std::vector<units::dimensionless::scalar_t> weights; // Used to sample an entity }; } // namespace OpenScenarioEngine::v1_3 namespace OpenScenarioEngine::v1_3 { +template <typename EntityCreator> +bool SequentialSpawnSpace::Spawn(EntityCreator &&, StochasticsInterface &rng) +{ + Entity entity{SampleEntity(rng)}; + if (entity == nullptr) + { + return false; + } + const auto velocity_range{GetSpawnVelocityRange(entity)}; + auto spawn_velocity{SampleVelocity(velocity_range, rng)}; + // TODO + return true; +} template <typename EntityCreator> -bool SpawnSpace::Spawn(EntityCreator &&spawner, StochasticsInterface &rng) +bool DistributedSpawnSpace::Spawn(EntityCreator &&spawner, StochasticsInterface &rng) { Entity entity{SampleEntity(rng)}; if (entity == nullptr) @@ -84,10 +127,12 @@ bool SpawnSpace::Spawn(EntityCreator &&spawner, StochasticsInterface &rng) return false; } + // TODO: Sample Velocity + // // TODO: Find last spawnable spot on the zone of the sampled interval // spawner.CreateEntity(*entity, place.zone->GetPosition(distance)); // TODO: Remove/replace interval - // interval->zone->RemoveInterval(*interval->interval, sampler(restricted_velocity.min, restricted_velocity.max)); + // spot->zone->RemoveInterval(*interval->interval, sampler(restricted_velocity.min, restricted_velocity.max)); return true; } diff --git a/engine/src/Utils/Spawning/SpawnSpot.h b/engine/src/Utils/Spawning/SpawnSpot.h index 84804855..b476ba56 100644 --- a/engine/src/Utils/Spawning/SpawnSpot.h +++ b/engine/src/Utils/Spawning/SpawnSpot.h @@ -21,7 +21,7 @@ namespace OpenScenarioEngine::v1_3 /// and a pointer to the spot's zone so that the SpawnSpace can look for the further spots within that zone. struct SpawnSpot : Interval { - constexpr SpawnSpot(Interval &&, VelocityRange &&); + constexpr SpawnSpot(Interval, VelocityRange); constexpr SpawnSpot(const Obstacle &before, const Obstacle &after); @@ -31,7 +31,7 @@ struct SpawnSpot : Interval namespace OpenScenarioEngine::v1_3 { -constexpr SpawnSpot::SpawnSpot(Interval &&interval, VelocityRange &&velocity) +constexpr SpawnSpot::SpawnSpot(Interval interval, VelocityRange velocity) : Interval{std::move(interval)}, velocity{std::move(velocity)} {} constexpr SpawnSpot::SpawnSpot(const Obstacle &before, const Obstacle &after) diff --git a/engine/src/Utils/Spawning/SpawnZone.cpp b/engine/src/Utils/Spawning/SpawnZone.cpp index d682f779..cba3f7b8 100644 --- a/engine/src/Utils/Spawning/SpawnZone.cpp +++ b/engine/src/Utils/Spawning/SpawnZone.cpp @@ -17,14 +17,33 @@ namespace OpenScenarioEngine::v1_3 { -SpawnZone::SpawnZone(Interval&& interval, std::set<Obstacle, Less<ToMin>>&& obstacles) - : Interval{std::move(interval)}, obstacles{std::move(obstacles)} {} +SpawnZone::SpawnZone(Interval interval, std::set<Obstacle, Less<ToMin>> obstacles) + : Interval{std::move(interval)}, obstacles{std::move(obstacles)} +{ + this->obstacles.emplace_hint(this->obstacles.begin(), Obstacle::GetStartingSentinel(min)); + this->obstacles.emplace_hint(this->obstacles.end(), Obstacle::GetEndingSentinel(max)); +} SpawnZone::SpawnZone(const mantle_api::ITrafficAreaStream& stream) : SpawnZone{Interval{{}, ToLength{}(stream)}, GetObstacles(stream)} { } +std::vector<SpawnSpot> SpawnZone::GetSpawnSpots() const +{ + std::vector<SpawnSpot> result; + assert(obstacles.size() > 1); + for (auto obstacle_after{std::next(obstacles.begin())}; obstacle_after != obstacles.end(); ++obstacle_after) + { + const auto obstacle_before{std::prev(obstacle_after)}; + if (obstacle_before->max < obstacle_after->min) + { + result.emplace_back(*obstacle_before, *obstacle_after); + } + } + return result; +} + SpawnZones::SpawnZones(std::vector<SpawnZone> input) : Iterable<std::vector<SpawnZone>>{std::move(input)} { @@ -49,4 +68,9 @@ SpawnZones ToSpawnZones::operator()(const mantle_api::TrafficArea& area) const { return {area}; } + +std::vector<SpawnSpot> ToSpawnSpots::operator()(const SpawnZone& zone) const +{ + return zone.GetSpawnSpots(); +} } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/SpawnZone.h b/engine/src/Utils/Spawning/SpawnZone.h index 4f8336f9..0ae88e46 100644 --- a/engine/src/Utils/Spawning/SpawnZone.h +++ b/engine/src/Utils/Spawning/SpawnZone.h @@ -28,15 +28,14 @@ namespace OpenScenarioEngine::v1_3 class SpawnZone : public Interval { public: - SpawnZone(Interval&&, std::set<Obstacle, Less<ToMin>>&&); + SpawnZone(Interval, std::set<Obstacle, Less<ToMin>>); SpawnZone(const mantle_api::ITrafficAreaStream&); const Interval& GetLongestInterval() const; Interval& GetLongestInterval(); - template <typename Predicate> - std::optional<SpawnSpot> GetLastSpawnSpot(Predicate&&) const; + std::vector<SpawnSpot> GetSpawnSpots() const; std::set<Obstacle, Less<ToMin>> obstacles; }; @@ -59,35 +58,9 @@ struct ToSpawnZones { SpawnZones operator()(const mantle_api::TrafficArea&) const; }; -} // namespace OpenScenarioEngine::v1_3 -namespace OpenScenarioEngine::v1_3 -{ -template <typename Predicate> -std::optional<SpawnSpot> SpawnZone::GetLastSpawnSpot(Predicate&& pred) const +struct ToSpawnSpots { - auto obstacle_before{std::find_if(obstacles.rbegin(), - obstacles.rend(), - [this](const Obstacle& obstacle) - { return obstacle.min < max; })}; - if (obstacle_before == obstacles.rend()) - { // There are no obstacles - return pred(*this) ? std::optional<SpawnSpot>{*this, {}} : std::nullopt; - } - auto obstacle_after{obstacle_before++}; - while (obstacle_before != obstacles.rend()) - { - if (SpawnSpot candidate{*obstacle_before, *obstacle_after}; pred(candidate)) - { // Obstacle before and after the spawn spot - return std::move(candidate); - } - ++obstacle_before; - ++obstacle_after; - } - if (SpawnSpot candidate{Obstacle::GetStartingSentinel(min), *obstacle_after}; obstacle_after->min > min && pred(candidate)) - { // No obstacle before, but an obstacle after the spawn spot - return std::move(candidate); - } - return std::nullopt; -} -} // namespace OpenScenarioEngine::v1_3 \ No newline at end of file + std::vector<SpawnSpot> operator()(const SpawnZone&) const; +}; +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/VelocityRange.cpp b/engine/src/Utils/Spawning/VelocityRange.cpp index d3f03f50..0190e588 100644 --- a/engine/src/Utils/Spawning/VelocityRange.cpp +++ b/engine/src/Utils/Spawning/VelocityRange.cpp @@ -92,4 +92,9 @@ VelocityRange ToSpawnVelocityRange::operator()(const NET_ASAM_OPENSCENARIO::v1_3 } return {units::velocity::meters_per_second_t{std::stod((*min_match)->GetValue())}, units::velocity::meters_per_second_t{std::stod((*max_match)->GetValue())}}; } + +units::velocity::meters_per_second_t SampleVelocity(const VelocityRange &range, StochasticsInterface &rng) +{ + return units::velocity::meters_per_second_t{rng.GetUniformDistributed(range.min.value(), range.max.value())}; +} } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/VelocityRange.h b/engine/src/Utils/Spawning/VelocityRange.h index 75d60e15..348f7d8b 100644 --- a/engine/src/Utils/Spawning/VelocityRange.h +++ b/engine/src/Utils/Spawning/VelocityRange.h @@ -9,6 +9,7 @@ ********************************************************************************/ #pragma once +#include <Stochastics/StochasticsInterface.h> #include <units.h> #include <memory> @@ -21,17 +22,16 @@ struct SpawnZone; struct VelocityRange { - units::velocity::meters_per_second_t min{std::numeric_limits<double>::lowest()}, max{std::numeric_limits<double>::max()}; - constexpr bool Contains(const VelocityRange &) const; constexpr units::velocity::meters_per_second_t GetDifference() const; + + units::velocity::meters_per_second_t min{std::numeric_limits<double>::lowest()}; + units::velocity::meters_per_second_t max{std::numeric_limits<double>::max()}; }; struct ToSpawnVelocityRange { - constexpr VelocityRange operator()(VelocityRange) const; - template <typename Type> VelocityRange operator()(const std::shared_ptr<Type> &) const; @@ -46,6 +46,7 @@ struct ToSpawnVelocityRange template <typename Input> VelocityRange GetSpawnVelocityRange(Input &&); +units::velocity::meters_per_second_t SampleVelocity(const VelocityRange &, StochasticsInterface &); } // namespace OpenScenarioEngine::v1_3 namespace OpenScenarioEngine::v1_3 @@ -60,11 +61,6 @@ constexpr units::velocity::meters_per_second_t VelocityRange::GetDifference() co return max - min; } -constexpr VelocityRange ToSpawnVelocityRange::operator()(VelocityRange input) const -{ - return input; -} - template <typename Type> VelocityRange ToSpawnVelocityRange::operator()(const std::shared_ptr<Type> &input) const { diff --git a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp index c9a2de01..2c98045f 100644 --- a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp +++ b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp @@ -11,7 +11,9 @@ #include <MantleAPI/Test/test_utils.h> #include <Storyboard/GenericAction/TrafficAreaAction.h> #include <gtest/gtest.h> +#include <units.h> +#include <memory> #include <string> #include "OpenScenarioEngine/OpenScenarioEngine.h" @@ -24,6 +26,7 @@ using testing::Return; using testing::ReturnRef; using testing::OpenScenarioEngine::v1_3::GetScenariosPath; using testing::OpenScenarioEngine::v1_3::OpenScenarioEngineTestBase; +using namespace units::literals; class TrafficAreaActionTestFixture : public OpenScenarioEngineTestBase { @@ -35,15 +38,17 @@ protected: ON_CALL(controller_, GetUniqueId()).WillByDefault(Return(1234)); LOGGER = std::make_unique<testing::OpenScenarioEngine::v1_3::TestLogger>(); auto& mockTrafficAreaService{dynamic_cast<mantle_api::MockTrafficAreaService&>(env_->GetTrafficAreaService())}; + + ON_CALL(*mock_traffic_area_stream, GetLength()).WillByDefault(Return(500_m)); ON_CALL(mockTrafficAreaService, CreateTrafficArea(_)).WillByDefault([this]() -> std::vector<std::shared_ptr<mantle_api::ITrafficAreaStream>> { - auto mock_traffic_area_stream{std::make_shared<mantle_api::MockTrafficAreaStream>()}; traffic_area_stream_vec_.emplace_back(std::move(mock_traffic_area_stream)); return traffic_area_stream_vec_; }); } std::vector<std::shared_ptr<mantle_api::ITrafficAreaStream>> traffic_area_stream_vec_; std::shared_ptr<testing::OpenScenarioEngine::v1_3::TestLogger> LOGGER; + std::shared_ptr<mantle_api::MockTrafficAreaStream> mock_traffic_area_stream{std::make_shared<mantle_api::MockTrafficAreaStream>()}; std::string controller_name{"TestController"}; }; -- GitLab From 600cf83bab609e145750e1ec17f5e8ab5906330e Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Fri, 7 Mar 2025 10:48:33 +0100 Subject: [PATCH 18/26] Implement spawning logic --- .../GenericAction/TrafficAreaAction.cpp | 3 +- engine/src/Utils/Spawning/Interval.h | 76 ++++++++++++++++--- engine/src/Utils/Spawning/Length.h | 1 - engine/src/Utils/Spawning/SpawnSpace.cpp | 67 ++++++++++++---- engine/src/Utils/Spawning/SpawnSpace.h | 57 +++++++++++--- engine/src/Utils/Spawning/SpawnZone.h | 4 + engine/src/Utils/Spawning/Transform.h | 28 +++++-- engine/src/Utils/Spawning/VelocityRange.h | 21 +++++ .../GenericAction/TrafficAreaActionTest.cpp | 2 + 9 files changed, 214 insertions(+), 45 deletions(-) diff --git a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp index fe3abb5e..b3518912 100644 --- a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp +++ b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp @@ -62,8 +62,7 @@ bool TrafficAreaAction::Step() } } EntityCreator spawner{mantle.environment}; - std::vector<SpawnZones> spawnZones{Transform(trafficAreas, ToSpawnZones{})}; - SequentialSpawnSpace spawnSpace{values.trafficDistributions, spawnZones}; + SequentialSpawnSpace spawnSpace{trafficAreas, values.trafficDistributions}; while (values.numberOfEntities) { if (!spawnSpace.Spawn(spawner, rng)) diff --git a/engine/src/Utils/Spawning/Interval.h b/engine/src/Utils/Spawning/Interval.h index f1378417..1beb8aed 100644 --- a/engine/src/Utils/Spawning/Interval.h +++ b/engine/src/Utils/Spawning/Interval.h @@ -20,6 +20,16 @@ namespace OpenScenarioEngine::v1_3 { +struct Padding +{ + constexpr units::length::meter_t GetLength() const; + + constexpr Padding operator-() const; + + units::length::meter_t rear; + units::length::meter_t front; +}; + struct Interval { constexpr Interval(units::length::meter_t min, units::length::meter_t max) @@ -30,11 +40,24 @@ struct Interval /// \return Difference between min and max constexpr units::length::meter_t GetLength() const; - /// Moves the min and max of this interval by the given offsets + /// Returns an interval that matches this interval moved by the given offsets. + /// + /// \param Padding Rear and front padding to be added to this interval + /// \return Modified copy of this interval + constexpr Interval OffsetBy(Padding) const; + + /// Returns an interval that matches this interval moved by the given offsets. + /// + /// \param Interval Min and max offset to be added to this interval's respective limits + /// \return Modified copy of this interval + constexpr Interval OffsetBy(Interval) const; + + /// Returns an interval that matches this interval moved by the given offsets. /// - /// \param frontOffset The amount to be added to min - /// \param rearOffset The amount to be added to max - constexpr void OffsetBy(units::length::meter_t frontOffset, units::length::meter_t rearOffset); + /// \param min_offset The amount to be added to min + /// \param max_offset The amount to be added to max + /// \return Modified copy of this interval + constexpr Interval OffsetBy(units::length::meter_t min_offset, units::length::meter_t max_offset) const; /// Returns the resulting list of intervals if the given interval were cut out of this interval. /// @@ -42,14 +65,21 @@ struct Interval /// \return 0, 1 or 2 elements sorted by ascending (min/max) value. std::vector<Interval> SplitBy(Interval) const; + /// Returns the interval representing the intersection of this interval and the given interval. + /// If the intervals do not intersect, the inverted negative space between the intervals is returned instead. + /// + /// \param other Other interval + /// \return The intersection interval of this and the given other interval + constexpr Interval GetIntersection(const Interval& other) const; + units::length::meter_t min, max; }; struct OffsetBy { - constexpr void operator()(Interval&) const; + constexpr Interval operator()(const Interval&) const; - units::length::meter_t frontOffset, rearOffset; + Padding offset; }; void OffsetIntervals(std::vector<Interval>& intervals, units::length::meter_t frontOffset, units::length::meter_t rearOffset); @@ -74,20 +104,44 @@ struct ToMax namespace OpenScenarioEngine::v1_3 { +constexpr units::length::meter_t Padding::GetLength() const +{ + return rear + front; +} + +constexpr Padding Padding::operator-() const +{ + return {-rear, -front}; +} + constexpr units::length::meter_t Interval::GetLength() const { return max - min; } -constexpr void Interval::OffsetBy(units::length::meter_t frontOffset, units::length::meter_t rearOffset) +constexpr Interval Interval::GetIntersection(const Interval& other) const +{ + return {std::max(min, other.min), std::min(max, other.max)}; +} + +constexpr Interval Interval::OffsetBy(Padding padding) const +{ + return OffsetBy(padding.rear, padding.front); +} + +constexpr Interval Interval::OffsetBy(Interval other) const +{ + return OffsetBy(other.min, other.max); +} + +constexpr Interval Interval::OffsetBy(units::length::meter_t min_offset, units::length::meter_t max_offset) const { - min += frontOffset; - max += rearOffset; + return {min + min_offset, max + max_offset}; } -constexpr void OffsetBy::operator()(Interval& interval) const +constexpr Interval OffsetBy::operator()(const Interval& interval) const { - interval.OffsetBy(frontOffset, rearOffset); + return interval.OffsetBy(offset); } template <typename Filter = Return<true>> diff --git a/engine/src/Utils/Spawning/Length.h b/engine/src/Utils/Spawning/Length.h index 20650dda..e5c9702d 100644 --- a/engine/src/Utils/Spawning/Length.h +++ b/engine/src/Utils/Spawning/Length.h @@ -131,7 +131,6 @@ constexpr size_t ToSize::operator()(const std::vector<Type>& container) const template <typename Type> units::length::meter_t GetLength(const Type& item) { - std::cout << "Length: " << ToLength{}(item) << '\n'; return ToLength{}(item); } diff --git a/engine/src/Utils/Spawning/SpawnSpace.cpp b/engine/src/Utils/Spawning/SpawnSpace.cpp index 95ff4952..98ee93e6 100644 --- a/engine/src/Utils/Spawning/SpawnSpace.cpp +++ b/engine/src/Utils/Spawning/SpawnSpace.cpp @@ -19,7 +19,7 @@ namespace OpenScenarioEngine::v1_3 { -SpawnSpace::SpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &distributions, std::vector<SpawnZones> &zones) +SpawnSpace::SpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &distributions) : weights{{}} { // Add entities @@ -37,19 +37,14 @@ SpawnSpace::SpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &dis Transform(entities, weights, ToAccumulatedWeight{}); } -SequentialSpawnSpace::SequentialSpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &distributions, std::vector<SpawnZones> &zones) - : SpawnSpace{distributions, zones} -{ // clang-format off - std::transform(zones.begin(), zones.end(), std::back_inserter(spots), [](const SpawnZones &zones) { - std::vector<SpawnSpot> spots; - for(const SpawnZone& zone : zones) { - auto next_spots{zone.GetSpawnSpots()}; - spots.insert(spots.end(), std::make_move_iterator(next_spots.begin()), std::make_move_iterator(next_spots.end())); - } - return spots; - }); - spots.erase(std::remove_if(spots.begin(), spots.end(), [](const auto& container) { return container.empty(); }), spots.end()); - // clang-format on +SequentialSpawnSpace::SequentialSpawnSpace(const std::vector<mantle_api::TrafficArea> &areas, const OpenScenarioEngine::v1_3::TrafficDistributions &distributions) + : SpawnSpace{distributions}, areas{areas} +{ + std::vector<SpawnZones> zones{Transform(areas, ToSpawnZones{})}; // clang-format off + Transform(zones, spots, [](const SpawnZones &zones) { + assert(!zones.empty()); + return Transform(zones, ToSpawnSpots{}); + }); // clang-format on } Entity SequentialSpawnSpace::SampleEntity(StochasticsInterface &rng) const @@ -63,8 +58,44 @@ Entity SequentialSpawnSpace::SampleEntity(StochasticsInterface &rng) const return Sample(entities, weights, sampled_weight).handle; } +std::tuple<std::vector<std::vector<std::vector<SpawnSpot>>>::iterator, + std::vector<std::vector<SpawnSpot>>::iterator, + std::vector<SpawnSpot>::iterator, + Interval> +SequentialSpawnSpace::FindSpot(const Padding &padding, units::velocity::meters_per_second_t velocity, units::time::second_t timespan) +{ + assert(!spots.empty()); + for (auto area{spots.begin()}; area != spots.end(); ++area) + { + for (auto stream{area->begin()}; stream != area->end(); ++stream) + { + for (auto spot{stream->rbegin()}; spot != stream->rend(); ++spot) + { + Interval range{spot->OffsetBy(-padding)}; + if (range.min <= range.max) + { + range = range.GetIntersection(spot->OffsetBy(spot->velocity * timespan)); + if (range.min <= range.max) + { + return {area, stream, std::next(spot).base(), std::move(range)}; + } + } + } + } + } + return { + spots.end(), + spots.back().end(), + spots.back().back().end(), + Interval{ + units::length::meter_t{std::numeric_limits<double>::quiet_NaN()}, + units::length::meter_t{std::numeric_limits<double>::quiet_NaN()} // + } // + }; +} + DistributedSpawnSpace::DistributedSpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &distributions, std::vector<SpawnZones> &zones) - : SpawnSpace{distributions, zones} + : SpawnSpace{distributions} { for (const SpawnZones ®ions : zones) { @@ -139,4 +170,10 @@ std::set<SpawnSpot, Less<ToLength>>::const_iterator DistributedSpawnSpace::Sampl const units::length::meter_t sampledLength{rng.GetUniformDistributed(.0, lengths.back().value())}; return Sample(candidates, lengths, sampledLength); } + +Padding GetLengthPadding(const Entity &entity) +{ // TODO: Consider the center offset of the entity's bounds + const units::length::meter_t half_length{GetLength(entity) * units::dimensionless::scalar_t{.5}}; + return {half_length, half_length}; +} } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/SpawnSpace.h b/engine/src/Utils/Spawning/SpawnSpace.h index 28593c85..5315c305 100644 --- a/engine/src/Utils/Spawning/SpawnSpace.h +++ b/engine/src/Utils/Spawning/SpawnSpace.h @@ -31,7 +31,7 @@ namespace OpenScenarioEngine::v1_3 { struct SpawnSpace { - SpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &, std::vector<SpawnZones> &); + SpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &); std::vector<WeightedEntity> entities; // Entities sorted by ascending length std::vector<units::dimensionless::scalar_t> weights; // Used to sample an entity @@ -44,7 +44,7 @@ struct SpawnSpace /// algorithm is naive because no memoization of area sizes occurs, meaning all areas are tested anew when another entity is being spawned. struct SequentialSpawnSpace : SpawnSpace { - SequentialSpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &, std::vector<SpawnZones> &); + SequentialSpawnSpace(const std::vector<mantle_api::TrafficArea> &, const OpenScenarioEngine::v1_3::TrafficDistributions &); /// Samples and returns a random entity without considering the remaining space. /// @@ -59,7 +59,15 @@ struct SequentialSpawnSpace : SpawnSpace template <typename EntityCreator> bool Spawn(EntityCreator &&, StochasticsInterface &); - std::vector<std::vector<SpawnSpot>> spots; + std::tuple<std::vector<std::vector<std::vector<SpawnSpot>>>::iterator, + std::vector<std::vector<SpawnSpot>>::iterator, + std::vector<SpawnSpot>::iterator, + Interval> + FindSpot(const Padding &, units::velocity::meters_per_second_t velocity, units::time::second_t timespan); + + // Stream -> vector<SpawnSpot>, thus Area = vector<vector<SpawnSpot>>, thus vector<Area> = vector<vector<vector<SpawnSpot>>> + std::vector<std::vector<std::vector<SpawnSpot>>> spots; + const std::vector<mantle_api::TrafficArea> &areas; }; /// A DistributedSpawnSpace is essentially a self-managing, depleting list of TrafficAreas and weighted entities optimized @@ -94,13 +102,21 @@ struct DistributedSpawnSpace : SpawnSpace std::set<SpawnSpot, Less<ToLength>> spots; }; +/// Returns the positive distance from the entity's center to its rear and the positive distance from the entity's center to its front. +Padding GetLengthPadding(const Entity &); } // namespace OpenScenarioEngine::v1_3 namespace OpenScenarioEngine::v1_3 { template <typename EntityCreator> -bool SequentialSpawnSpace::Spawn(EntityCreator &&, StochasticsInterface &rng) +bool SequentialSpawnSpace::Spawn(EntityCreator &&spawner, StochasticsInterface &rng) { + using namespace units::literals; + + if (spots.empty()) + { + return false; + } Entity entity{SampleEntity(rng)}; if (entity == nullptr) { @@ -108,8 +124,33 @@ bool SequentialSpawnSpace::Spawn(EntityCreator &&, StochasticsInterface &rng) } const auto velocity_range{GetSpawnVelocityRange(entity)}; auto spawn_velocity{SampleVelocity(velocity_range, rng)}; - // TODO - return true; + const units::time::second_t minimum_time_to_collision{2_s}; + const Padding padding{GetLengthPadding(entity)}; + if (auto [spot_area, spot_stream, spot, interval]{FindSpot(padding, spawn_velocity, minimum_time_to_collision)}; spot_area != spots.end()) + { + const auto area{std::next(areas.begin(), std::distance(spots.begin(), spot_area))}; + const auto &stream{*std::next(area->begin(), std::distance(spot_area->begin(), spot_stream))}; + const mantle_api::Pose pose{stream->Convert(mantle_api::ITrafficAreaStream::StreamPose{{interval.max, {}}, {}}).value()}; + + // TODO: Spawn entity + + std::vector<OpenScenarioEngine::v1_3::Interval> new_intervals{spot->SplitBy(Interval{interval.max, interval.max}.OffsetBy(-padding.rear, padding.front))}; + if (new_intervals.empty()) + { + spot_stream->erase(spot); + } + else + { + std::vector<SpawnSpot> new_spots{Transform(std::move(new_intervals), [spot](Interval &&interval) -> SpawnSpot { // + return {interval, spot->velocity}; + })}; + new_spots.front().velocity.max = spawn_velocity; + new_spots.back().velocity.min = spawn_velocity; + spot_stream->insert(spot_stream->erase(spot), std::make_move_iterator(new_spots.begin()), std::make_move_iterator(new_spots.end())); + } + return true; + } + return false; } template <typename EntityCreator> @@ -126,9 +167,7 @@ bool DistributedSpawnSpace::Spawn(EntityCreator &&spawner, StochasticsInterface { return false; } - - // TODO: Sample Velocity - // + auto spawn_velocity{SampleVelocity(velocity_range, rng)}; // TODO: Find last spawnable spot on the zone of the sampled interval // spawner.CreateEntity(*entity, place.zone->GetPosition(distance)); // TODO: Remove/replace interval diff --git a/engine/src/Utils/Spawning/SpawnZone.h b/engine/src/Utils/Spawning/SpawnZone.h index 0ae88e46..f82ebf82 100644 --- a/engine/src/Utils/Spawning/SpawnZone.h +++ b/engine/src/Utils/Spawning/SpawnZone.h @@ -25,6 +25,10 @@ namespace OpenScenarioEngine::v1_3 { +/// \brief A SpawnZone is a representation of a mantle_api::ITrafficAreaStream with the assumption +/// that said stream consists of connected lanes. It reduces the stream to its length and a full +/// list of obstacles within it. The SpawnZone is used to generate SpawnSpots, which is a list of +/// empty intervals where entities can be spawned. class SpawnZone : public Interval { public: diff --git a/engine/src/Utils/Spawning/Transform.h b/engine/src/Utils/Spawning/Transform.h index 072c6733..975b4316 100644 --- a/engine/src/Utils/Spawning/Transform.h +++ b/engine/src/Utils/Spawning/Transform.h @@ -17,20 +17,34 @@ namespace OpenScenarioEngine::v1_3 { -template <typename Invocable, typename Type> -std::vector<std::invoke_result_t<Invocable, const Type&>> Transform(const std::vector<Type>& input, Invocable&& invocable) +template <typename Invocable, typename Container> +std::vector<std::invoke_result_t<Invocable, typename std::remove_reference_t<Container>::value_type>> Transform(Container&& input, Invocable&& invocable) { - std::vector<std::invoke_result_t<Invocable, const Type&>> result; + std::vector<std::invoke_result_t<Invocable, typename std::remove_reference_t<Container>::value_type>> result; result.reserve(input.size()); - std::transform(input.begin(), input.end(), std::back_inserter(result), std::forward<Invocable>(invocable)); + if constexpr (std::is_lvalue_reference_v<decltype(input)>) + { + std::transform(input.begin(), input.end(), std::back_inserter(result), std::forward<Invocable>(invocable)); + } + else + { + std::transform(std::make_move_iterator(input.begin()), std::make_move_iterator(input.end()), std::back_inserter(result), std::forward<Invocable>(invocable)); + } return result; } -template <typename Invocable, typename Type> -void Transform(const std::vector<Type>& input, std::vector<std::invoke_result_t<Invocable, const Type&>>& output, Invocable&& invocable) +template <typename Invocable, typename Container> +void Transform(Container&& input, std::vector<std::invoke_result_t<Invocable, typename std::remove_reference_t<Container>::value_type>>& output, Invocable&& invocable) { output.reserve(output.size() + input.size()); - std::transform(input.begin(), input.end(), std::back_inserter(output), std::forward<Invocable>(invocable)); + if constexpr (std::is_lvalue_reference_v<decltype(input)>) + { + std::transform(input.begin(), input.end(), std::back_inserter(output), std::forward<Invocable>(invocable)); + } + else + { + std::transform(std::make_move_iterator(input.begin()), std::make_move_iterator(input.end()), std::back_inserter(output), std::forward<Invocable>(invocable)); + } } template <typename Type, typename Invocable> diff --git a/engine/src/Utils/Spawning/VelocityRange.h b/engine/src/Utils/Spawning/VelocityRange.h index 348f7d8b..a4a9eeb7 100644 --- a/engine/src/Utils/Spawning/VelocityRange.h +++ b/engine/src/Utils/Spawning/VelocityRange.h @@ -14,6 +14,7 @@ #include <memory> +#include "Interval.h" #include "openScenarioLib/generated/v1_3/api/ApiClassInterfacesV1_3.h" namespace OpenScenarioEngine::v1_3 @@ -24,12 +25,17 @@ struct VelocityRange { constexpr bool Contains(const VelocityRange &) const; + constexpr bool Contains(units::velocity::meters_per_second_t) const; + constexpr units::velocity::meters_per_second_t GetDifference() const; units::velocity::meters_per_second_t min{std::numeric_limits<double>::lowest()}; units::velocity::meters_per_second_t max{std::numeric_limits<double>::max()}; }; +constexpr Interval operator*(const VelocityRange &, units::time::second_t); +constexpr Interval operator*(units::time::second_t, const VelocityRange &); + struct ToSpawnVelocityRange { template <typename Type> @@ -56,11 +62,26 @@ constexpr bool VelocityRange::Contains(const VelocityRange &other) const return min <= other.min && other.max <= max; } +constexpr bool VelocityRange::Contains(units::velocity::meters_per_second_t velocity) const +{ + return min <= velocity && velocity <= max; +} + constexpr units::velocity::meters_per_second_t VelocityRange::GetDifference() const { return max - min; } +constexpr Interval operator*(const VelocityRange &range, units::time::second_t time) +{ + return {range.min * time, range.max * time}; +} + +constexpr Interval operator*(units::time::second_t time, const VelocityRange &range) +{ + return range * time; +} + template <typename Type> VelocityRange ToSpawnVelocityRange::operator()(const std::shared_ptr<Type> &input) const { diff --git a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp index 2c98045f..d4fd0dad 100644 --- a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp +++ b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp @@ -15,6 +15,7 @@ #include <memory> #include <string> +#include <utility> #include "OpenScenarioEngine/OpenScenarioEngine.h" #include "TestUtils.h" @@ -40,6 +41,7 @@ protected: auto& mockTrafficAreaService{dynamic_cast<mantle_api::MockTrafficAreaService&>(env_->GetTrafficAreaService())}; ON_CALL(*mock_traffic_area_stream, GetLength()).WillByDefault(Return(500_m)); + // ON_CALL(*mock_traffic_area_stream, Convert(_)).WillByDefault(Return(std::optional<mantle_api::Pose>{std::in_place, mantle_api::ITrafficAreaStream::StreamPosition{100_m, 0_m}, units::angle::radian_t{}})); ON_CALL(mockTrafficAreaService, CreateTrafficArea(_)).WillByDefault([this]() -> std::vector<std::shared_ptr<mantle_api::ITrafficAreaStream>> { traffic_area_stream_vec_.emplace_back(std::move(mock_traffic_area_stream)); -- GitLab From c28cda2958e622cd41bd9e9ea9de7138ab5576ae Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Tue, 11 Mar 2025 17:27:19 +0100 Subject: [PATCH 19/26] Expant mocking for TrafficAreaAction --- .../ConvertScenarioTrafficDistribution.cpp | 4 +- .../GenericAction/TrafficAreaAction.cpp | 1 + engine/src/Utils/EntityCreator.cpp | 121 ++++++++++-------- engine/src/Utils/EntityCreator.h | 32 +++-- engine/src/Utils/Spawning/Interval.h | 2 +- engine/src/Utils/Spawning/SpawnSpace.h | 4 +- .../GenericAction/TrafficAreaActionTest.cpp | 65 ++++++++-- .../data/Scenarios/VehicleCatalog.xosc | 4 +- 8 files changed, 161 insertions(+), 72 deletions(-) diff --git a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp index bba5d9a4..1fd3d7a3 100644 --- a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp +++ b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp @@ -19,7 +19,7 @@ struct ToEntities { std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject> operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityDistributionEntry& entry) const { - return entry.GetScenarioObjectTemplate()->GetEntitiyObject(); // https://github.com/RA-Consulting-GmbH/openscenario.api.test/issues/222 + return entry.GetScenarioObjectTemplate()->GetEntitiyObject(); // https://github.com/RA-Consulting-GmbH/openscenario.api.test/issues/222 } std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject>> operator()(const NET_ASAM_OPENSCENARIO::v1_3::ITrafficDistributionEntry& entry) const @@ -33,7 +33,7 @@ struct ToTrafficDistribution TrafficDistribution operator()(const NET_ASAM_OPENSCENARIO::v1_3::ITrafficDistributionEntry& entry) const { return {TransformSharedPointers(entry.GetEntityDistribution()->GetEntityDistributionEntry(), [](const NET_ASAM_OPENSCENARIO::v1_3::IEntityDistributionEntry& entry) - { return entry.GetScenarioObjectTemplate()->GetEntitiyObject(); }), // https://github.com/RA-Consulting-GmbH/openscenario.api.test/issues/222 + { return entry.GetScenarioObjectTemplate()->GetEntitiyObject(); }), // https://github.com/RA-Consulting-GmbH/openscenario.api.test/issues/222 entry.GetWeight()}; } }; diff --git a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp index b3518912..3c7d47a7 100644 --- a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp +++ b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp @@ -11,6 +11,7 @@ #include "Storyboard/GenericAction/TrafficAreaAction.h" #include "Conversion/OscToMantle/ConvertScenarioTrafficArea.h" +#include "Utils/ControllerCreator.h" #include "Utils/EntityCreator.h" #include "Utils/Logger.h" #include "Utils/Spawning/Length.h" diff --git a/engine/src/Utils/EntityCreator.cpp b/engine/src/Utils/EntityCreator.cpp index 0042a031..0176c046 100644 --- a/engine/src/Utils/EntityCreator.cpp +++ b/engine/src/Utils/EntityCreator.cpp @@ -63,35 +63,41 @@ std::map<NET_ASAM_OPENSCENARIO::v1_3::MiscObjectCategory::MiscObjectCategoryEnum EntityCreator::EntityCreator(std::shared_ptr<mantle_api::IEnvironment> environment) : environment_(environment) {} -void EntityCreator::CreateEntity(std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IScenarioObject> scenario_object) +mantle_api::IEntity& EntityCreator::CreateEntity(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject>& entity_object, const std::string& name) { - auto entity_object = scenario_object->GetEntityObject(); - - if (!entity_object) + if (entity_object->IsSetVehicle()) { - throw std::runtime_error(std::string("entityObject missing in ScenarioObject " + scenario_object->GetName())); + return CreateVehicle(entity_object->GetVehicle(), name); } - - if (entity_object->GetVehicle() != nullptr) + else if (entity_object->IsSetPedestrian()) { - CreateVehicle(entity_object->GetVehicle(), scenario_object->GetName()); + return CreatePedestrian(entity_object->GetPedestrian(), name); } - else if (entity_object->GetPedestrian() != nullptr) + else if (entity_object->IsSetMiscObject()) { - CreatePedestrian(entity_object->GetPedestrian(), scenario_object->GetName()); + return CreateMiscObject(entity_object->GetMiscObject(), name); } - else if (entity_object->GetMiscObject() != nullptr) + else if (entity_object->IsSetCatalogReference()) + { + return CreateCatalogReferenceEntity(entity_object->GetCatalogReference(), name); + } + else { - CreateMiscObject(entity_object->GetMiscObject(), scenario_object->GetName()); + throw std::runtime_error("EntityCreator::CreateEntity: Entity has no valid object type (neither vehicle, pedestrian, miscellaneous nor catalog reference)"); } - else if (entity_object->GetCatalogReference() != nullptr) +} + +mantle_api::IEntity& EntityCreator::CreateEntity(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IScenarioObject>& scenario_object) +{ + if (!scenario_object->IsSetEntityObject()) { - CreateCatalogReferenceEntity(entity_object->GetCatalogReference(), scenario_object->GetName()); + throw std::runtime_error("EntityObject missing in ScenarioObject " + scenario_object->GetName()); } + return CreateEntity(scenario_object->GetEntityObject(), scenario_object->GetName()); } -void EntityCreator::CreateVehicle(std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IVehicle> vehicle, - const std::string& name) +mantle_api::IVehicle& EntityCreator::CreateVehicle(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IVehicle>& vehicle, + const std::string& name) { mantle_api::VehicleProperties properties; properties.type = mantle_api::EntityType::kVehicle; @@ -129,30 +135,46 @@ void EntityCreator::CreateVehicle(std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::I properties.is_host = true; } - environment_->GetEntityRepository().Create(name, properties); + return environment_->GetEntityRepository().Create(name, properties); +} + +inline bool IsInWheelchair(const NET_ASAM_OPENSCENARIO::v1_3::IPedestrian& pedestrian) +{ + return GetEnumValue(pedestrian.GetPedestrianCategory()) == NET_ASAM_OPENSCENARIO::v1_3::PedestrianCategory::PedestrianCategoryEnum::WHEELCHAIR; } -void EntityCreator::CreatePedestrian(std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IPedestrian> pedestrian, - const std::string& name) +mantle_api::IEntity& EntityCreator::CreatePedestrian(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IPedestrian>& pedestrian, + const std::string& name) { - if (pedestrian->GetPedestrianCategory().GetFromLiteral(pedestrian->GetPedestrianCategory().GetLiteral()) == - NET_ASAM_OPENSCENARIO::v1_3::PedestrianCategory::PedestrianCategoryEnum::WHEELCHAIR) + if (IsInWheelchair(*pedestrian)) { - mantle_api::VehicleProperties vehicle_properties; - vehicle_properties.classification = mantle_api::VehicleClass::kWheelchair; - FillEntityPropertiesForPedestrian(pedestrian, vehicle_properties); - environment_->GetEntityRepository().Create(name, vehicle_properties); + return CreateWheelchairPedestrian(pedestrian, name); } else { - mantle_api::PedestrianProperties pedestrian_properties; - FillEntityPropertiesForPedestrian(pedestrian, pedestrian_properties); - environment_->GetEntityRepository().Create(name, pedestrian_properties); + return CreateRegularPedestrian(pedestrian, name); } } -void EntityCreator::CreateMiscObject(std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IMiscObject> misc_object, - const std::string& name) +mantle_api::IVehicle& EntityCreator::CreateWheelchairPedestrian(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IPedestrian>& pedestrian, + const std::string& name) +{ + mantle_api::VehicleProperties vehicle_properties; + vehicle_properties.classification = mantle_api::VehicleClass::kWheelchair; + FillEntityPropertiesForPedestrian(pedestrian, vehicle_properties); + return environment_->GetEntityRepository().Create(name, vehicle_properties); +} + +mantle_api::IPedestrian& EntityCreator::CreateRegularPedestrian(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IPedestrian>& pedestrian, + const std::string& name) +{ + mantle_api::PedestrianProperties pedestrian_properties; + FillEntityPropertiesForPedestrian(pedestrian, pedestrian_properties); + return environment_->GetEntityRepository().Create(name, pedestrian_properties); +} + +mantle_api::IStaticObject& EntityCreator::CreateMiscObject(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IMiscObject>& misc_object, + const std::string& name) { mantle_api::StaticObjectProperties properties; properties.type = mantle_api::EntityType::kStatic; @@ -161,32 +183,31 @@ void EntityCreator::CreateMiscObject(std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3 FillBoundingBoxProperties(misc_object->GetBoundingBox(), misc_object->GetName(), properties); FillGenericProperties(*misc_object, properties); SetVerticalOffset(misc_object, properties); - environment_->GetEntityRepository().Create(name, properties); + return environment_->GetEntityRepository().Create(name, properties); } -void EntityCreator::CreateCatalogReferenceEntity( - std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ICatalogReference> catalog_reference, +mantle_api::IEntity& EntityCreator::CreateCatalogReferenceEntity( + const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ICatalogReference>& catalog_reference, const std::string& name) { - auto catalog_element = catalog_reference->GetRef(); - if (catalog_element == nullptr) + if (!catalog_reference->IsSetRef()) { - throw std::runtime_error( - std::string("CatalogReference " + catalog_reference->GetEntryName() + " without ref.")); + throw std::runtime_error("CatalogReference " + catalog_reference->GetEntryName() + " without ref."); } - + auto catalog_element = catalog_reference->GetRef(); if (NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::IsVehicle(catalog_element)) { - CreateVehicle(NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::AsVehicle(catalog_element), name); + return CreateVehicle(NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::AsVehicle(catalog_element), name); } - else if (NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::IsPedestrian(catalog_element)) + if (NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::IsPedestrian(catalog_element)) { - CreatePedestrian(NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::AsPedestrian(catalog_element), name); + return CreatePedestrian(NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::AsPedestrian(catalog_element), name); } - else if (NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::IsMiscObject(catalog_element)) + if (NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::IsMiscObject(catalog_element)) { - CreateMiscObject(NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::AsMiscObject(catalog_element), name); + return CreateMiscObject(NET_ASAM_OPENSCENARIO::v1_3::CatalogHelper::AsMiscObject(catalog_element), name); } + throw std::runtime_error("Catalog reference does not describe a valid entity"); } void EntityCreator::FillBoundingBoxProperties(std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IBoundingBox> bounding_box, @@ -195,12 +216,12 @@ void EntityCreator::FillBoundingBoxProperties(std::shared_ptr<NET_ASAM_OPENSCENA { if (!bounding_box) { - throw std::runtime_error(std::string("Entity " + name + " without bounding box.")); + throw std::runtime_error("Entity " + name + " without bounding box."); } auto dimensions = bounding_box->GetDimensions(); if (!dimensions) { - throw std::runtime_error(std::string("Bounding box of entity " + name + " without Dimensions.")); + throw std::runtime_error("Bounding box of entity " + name + " without Dimensions."); } properties.bounding_box.dimension.length = units::length::meter_t(dimensions->GetLength()); @@ -210,7 +231,7 @@ void EntityCreator::FillBoundingBoxProperties(std::shared_ptr<NET_ASAM_OPENSCENA auto center = bounding_box->GetCenter(); if (!center) { - throw std::runtime_error(std::string("Bounding box of entity " + name + " without Center.")); + throw std::runtime_error("Bounding box of entity " + name + " without Center."); } properties.bounding_box.geometric_center.x = units::length::meter_t(center->GetX()); @@ -225,7 +246,7 @@ void EntityCreator::FillAxleProperties(std::shared_ptr<NET_ASAM_OPENSCENARIO::v1 { if (open_scenario_axle == nullptr) { - throw std::runtime_error(std::string("Entity " + name + " is missing axle information.")); + throw std::runtime_error("Entity " + name + " is missing axle information."); } axle.bb_center_to_axle_center = { @@ -255,7 +276,7 @@ mantle_api::VehicleClass EntityCreator::GetVehicleClass(NET_ASAM_OPENSCENARIO::v { return map_vehicle_class[vehicle_category.GetFromLiteral(vehicle_category.GetLiteral())]; } - throw std::runtime_error(std::string("No vehicle class for vehicle category " + vehicle_category.GetLiteral())); + throw std::runtime_error("No vehicle class for vehicle category " + vehicle_category.GetLiteral()); } mantle_api::StaticObjectType EntityCreator::GetStaticObjectType(NET_ASAM_OPENSCENARIO::v1_3::MiscObjectCategory misc_object_category) @@ -265,7 +286,7 @@ mantle_api::StaticObjectType EntityCreator::GetStaticObjectType(NET_ASAM_OPENSCE { return map_static_object_type[misc_object_category.GetFromLiteral(misc_object_category.GetLiteral())]; } - throw std::runtime_error(std::string("No static object type for misc object category " + misc_object_category.GetLiteral())); + throw std::runtime_error("No static object type for misc object category " + misc_object_category.GetLiteral()); } mantle_api::EntityType EntityCreator::GetEntityTypeFromPedestrianCategory( @@ -276,7 +297,7 @@ mantle_api::EntityType EntityCreator::GetEntityTypeFromPedestrianCategory( { return map_entity_type[pedestrian_category.GetFromLiteral(pedestrian_category.GetLiteral())]; } - throw std::runtime_error(std::string("No entity type for pedestrian category " + pedestrian_category.GetLiteral())); + throw std::runtime_error("No entity type for pedestrian category " + pedestrian_category.GetLiteral()); } void EntityCreator::SetVerticalOffset(std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IMiscObject> misc_object, mantle_api::StaticObjectProperties& entity_properties) diff --git a/engine/src/Utils/EntityCreator.h b/engine/src/Utils/EntityCreator.h index 8dc19ed7..03f976d8 100644 --- a/engine/src/Utils/EntityCreator.h +++ b/engine/src/Utils/EntityCreator.h @@ -21,16 +21,27 @@ class EntityCreator public: explicit EntityCreator(std::shared_ptr<mantle_api::IEnvironment> environment); - void CreateEntity(std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IScenarioObject> scenario_object); + mantle_api::IEntity& CreateEntity(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IScenarioObject>& scenario_object); + + mantle_api::IEntity& CreateEntity(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject>& entity_object, const std::string& name); private: - void CreateVehicle(std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IVehicle> vehicle, const std::string& name); - void CreatePedestrian(std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IPedestrian> pedestrian, - const std::string& name); - void CreateMiscObject(std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IMiscObject> misc_object, - const std::string& name); - void CreateCatalogReferenceEntity(std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ICatalogReference> catalog_reference, - const std::string& name); + mantle_api::IVehicle& CreateVehicle(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IVehicle>& vehicle, const std::string& name); + + mantle_api::IEntity& CreatePedestrian(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IPedestrian>& pedestrian, + const std::string& name); + + mantle_api::IVehicle& CreateWheelchairPedestrian(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IPedestrian>& pedestrian, + const std::string& name); + + mantle_api::IPedestrian& CreateRegularPedestrian(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IPedestrian>& pedestrian, + const std::string& name); + + mantle_api::IStaticObject& CreateMiscObject(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IMiscObject>& misc_object, + const std::string& name); + + mantle_api::IEntity& CreateCatalogReferenceEntity(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ICatalogReference>& catalog_reference, + const std::string& name); /// Fills the bounding box properties of an entity /// @param[in] bounding_box The bounding box information to copy from @@ -86,4 +97,9 @@ private: std::shared_ptr<mantle_api::IEnvironment> environment_; }; +template <typename Category> +inline auto GetEnumValue(const Category& category) +{ + return category.GetFromLiteral(category.GetLiteral()); +} } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Interval.h b/engine/src/Utils/Spawning/Interval.h index 1beb8aed..1748e0e1 100644 --- a/engine/src/Utils/Spawning/Interval.h +++ b/engine/src/Utils/Spawning/Interval.h @@ -126,7 +126,7 @@ constexpr Interval Interval::GetIntersection(const Interval& other) const constexpr Interval Interval::OffsetBy(Padding padding) const { - return OffsetBy(padding.rear, padding.front); + return OffsetBy(-padding.rear, padding.front); } constexpr Interval Interval::OffsetBy(Interval other) const diff --git a/engine/src/Utils/Spawning/SpawnSpace.h b/engine/src/Utils/Spawning/SpawnSpace.h index 5315c305..9b838397 100644 --- a/engine/src/Utils/Spawning/SpawnSpace.h +++ b/engine/src/Utils/Spawning/SpawnSpace.h @@ -132,7 +132,9 @@ bool SequentialSpawnSpace::Spawn(EntityCreator &&spawner, StochasticsInterface & const auto &stream{*std::next(area->begin(), std::distance(spot_area->begin(), spot_stream))}; const mantle_api::Pose pose{stream->Convert(mantle_api::ITrafficAreaStream::StreamPose{{interval.max, {}}, {}}).value()}; - // TODO: Spawn entity + mantle_api::IEntity &object{spawner.CreateEntity(entity, "NAME_DISCARDED_BY_TRAFFIC_AREA_ACTION")}; + object.SetPosition(pose.position); + object.SetOrientation(pose.orientation); std::vector<OpenScenarioEngine::v1_3::Interval> new_intervals{spot->SplitBy(Interval{interval.max, interval.max}.OffsetBy(-padding.rear, padding.front))}; if (new_intervals.empty()) diff --git a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp index d4fd0dad..b78a52d9 100644 --- a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp +++ b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp @@ -29,28 +29,76 @@ using testing::OpenScenarioEngine::v1_3::GetScenariosPath; using testing::OpenScenarioEngine::v1_3::OpenScenarioEngineTestBase; using namespace units::literals; +struct MockVehicle : mantle_api::IVehicle +{ + mantle_api::UniqueId GetUniqueId() const override { return {}; } + + void SetName(const std::string& name) override {} + const std::string& GetName() const override + { + static std::string s; + return s; + } + + void SetPosition(const mantle_api::Vec3<units::length::meter_t>& xyz) override { this->xyz = xyz; }; + mantle_api::Vec3<units::length::meter_t> GetPosition() const override { return {}; } + + void SetVelocity(const mantle_api::Vec3<units::velocity::meters_per_second_t>&) override {} + mantle_api::Vec3<units::velocity::meters_per_second_t> GetVelocity() const override { return {}; } + + void SetAcceleration(const mantle_api::Vec3<units::acceleration::meters_per_second_squared_t>&) override {} + mantle_api::Vec3<units::acceleration::meters_per_second_squared_t> GetAcceleration() const override { return {}; } + + void SetOrientation(const mantle_api::Orientation3<units::angle::radian_t>& orientation) override { this->orientation = orientation; } + mantle_api::Orientation3<units::angle::radian_t> GetOrientation() const override { return {}; } + + void SetOrientationRate(const mantle_api::Orientation3<units::angular_velocity::radians_per_second_t>&) override {} + mantle_api::Orientation3<units::angular_velocity::radians_per_second_t> GetOrientationRate() const override { return {}; } + + void SetOrientationAcceleration(const mantle_api::Orientation3<units::angular_acceleration::radians_per_second_squared_t>&) override {} + mantle_api::Orientation3<units::angular_acceleration::radians_per_second_squared_t> GetOrientationAcceleration() const override { return {}; } + + void SetProperties(std::unique_ptr<mantle_api::EntityProperties>) override {} + mantle_api::VehicleProperties* GetProperties() const override { return {}; } + + void SetAssignedLaneIds(const std::vector<std::uint64_t>&) override {} + std::vector<std::uint64_t> GetAssignedLaneIds() const override { return {}; } + + void SetVisibility(const mantle_api::EntityVisibilityConfig& visibility) override {} + mantle_api::EntityVisibilityConfig GetVisibility() const override { return {}; } + + void SetIndicatorState(mantle_api::IndicatorState state) override {} + mantle_api::IndicatorState GetIndicatorState() const override { return {}; } + + mantle_api::Vec3<units::length::meter_t> xyz; + mantle_api::Orientation3<units::angle::radian_t> orientation; +}; + class TrafficAreaActionTestFixture : public OpenScenarioEngineTestBase { protected: void SetUp() override { + auto& stream{dynamic_cast<mantle_api::MockTrafficAreaStream&>(*streams.emplace_back(std::make_shared<mantle_api::MockTrafficAreaStream>()))}; OpenScenarioEngineTestBase::SetUp(); ON_CALL(controller_, GetName()).WillByDefault(ReturnRef(controller_name)); ON_CALL(controller_, GetUniqueId()).WillByDefault(Return(1234)); LOGGER = std::make_unique<testing::OpenScenarioEngine::v1_3::TestLogger>(); auto& mockTrafficAreaService{dynamic_cast<mantle_api::MockTrafficAreaService&>(env_->GetTrafficAreaService())}; - ON_CALL(*mock_traffic_area_stream, GetLength()).WillByDefault(Return(500_m)); - // ON_CALL(*mock_traffic_area_stream, Convert(_)).WillByDefault(Return(std::optional<mantle_api::Pose>{std::in_place, mantle_api::ITrafficAreaStream::StreamPosition{100_m, 0_m}, units::angle::radian_t{}})); - ON_CALL(mockTrafficAreaService, CreateTrafficArea(_)).WillByDefault([this]() -> std::vector<std::shared_ptr<mantle_api::ITrafficAreaStream>> - { - traffic_area_stream_vec_.emplace_back(std::move(mock_traffic_area_stream)); - return traffic_area_stream_vec_; }); + ON_CALL(stream, GetLength()).WillByDefault(Return(500_m)); + ON_CALL(stream, Convert(testing::A<const mantle_api::ITrafficAreaStream::StreamPose&>())).WillByDefault(Return(mantle_api::Pose{mantle_api::Vec3<units::length::meter_t>{100_m, 0_m, 0_m}, mantle_api::Orientation3<units::angle::radian_t>{}})); // clang-format off + ON_CALL(mockTrafficAreaService, CreateTrafficArea(_)).WillByDefault([this]() -> std::vector<std::shared_ptr<mantle_api::ITrafficAreaStream>> { // + return streams; + }); // clang-format on + auto& entity_repository{static_cast<mantle_api::MockEntityRepository&>(env_->GetEntityRepository())}; + ON_CALL(entity_repository, Create(testing::A<const std::string&>(), testing::A<const mantle_api::VehicleProperties&>())).WillByDefault(ReturnRef(mock_vehicle)); } - std::vector<std::shared_ptr<mantle_api::ITrafficAreaStream>> traffic_area_stream_vec_; + std::vector<std::shared_ptr<mantle_api::ITrafficAreaStream>> streams; std::shared_ptr<testing::OpenScenarioEngine::v1_3::TestLogger> LOGGER; - std::shared_ptr<mantle_api::MockTrafficAreaStream> mock_traffic_area_stream{std::make_shared<mantle_api::MockTrafficAreaStream>()}; + + MockVehicle mock_vehicle; std::string controller_name{"TestController"}; }; @@ -62,4 +110,5 @@ TEST_F(TrafficAreaActionTestFixture, GivenScenarioWithTrafficAreaAction_WhenInit engine.Init(); engine.SetupDynamicContent(); engine.Step({}); + EXPECT_EQ(mock_vehicle.xyz.x, 100_m); } diff --git a/engine/tests/Storyboard/GenericAction/data/Scenarios/VehicleCatalog.xosc b/engine/tests/Storyboard/GenericAction/data/Scenarios/VehicleCatalog.xosc index 1314dc68..bc7c5a51 100644 --- a/engine/tests/Storyboard/GenericAction/data/Scenarios/VehicleCatalog.xosc +++ b/engine/tests/Storyboard/GenericAction/data/Scenarios/VehicleCatalog.xosc @@ -14,8 +14,8 @@ </Axles> <Properties> <Property name="custom_property" value="5" /> - <Property name="min_spawn_velocity" value="20" /> - <Property name="max_spawn_velocity" value="80" /> + <Property name="min_spawn_velocity" value="2" /> + <Property name="max_spawn_velocity" value="8" /> </Properties> </Vehicle> </Catalog> -- GitLab From d16d5a85a2280dd302732f697fe377bc508bebf8 Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Wed, 12 Mar 2025 10:21:45 +0100 Subject: [PATCH 20/26] Fix TTC calculation in TrafficAreaAction --- engine/src/Utils/Spawning/Interval.cpp | 10 ++++++ engine/src/Utils/Spawning/Interval.h | 18 ++++++++++ engine/src/Utils/Spawning/SpawnSpace.cpp | 30 +++++++++++++++- engine/src/Utils/Spawning/SpawnSpace.h | 35 ++++++++++--------- engine/src/Utils/Spawning/VelocityRange.cpp | 5 +++ engine/src/Utils/Spawning/VelocityRange.h | 3 ++ .../GenericAction/TrafficAreaActionTest.cpp | 30 ++++++++++++---- 7 files changed, 108 insertions(+), 23 deletions(-) diff --git a/engine/src/Utils/Spawning/Interval.cpp b/engine/src/Utils/Spawning/Interval.cpp index d35e5077..9946ebe0 100644 --- a/engine/src/Utils/Spawning/Interval.cpp +++ b/engine/src/Utils/Spawning/Interval.cpp @@ -43,4 +43,14 @@ std::vector<Interval> Interval::SplitBy(Interval input) const } return {}; } + +std::ostream& operator<<(std::ostream& os, const Padding& padding) +{ + return os << '[' << padding.rear.value() << ", " << padding.front.value() << ']'; +} + +std::ostream& operator<<(std::ostream& os, const Interval& interval) +{ + return os << '[' << interval.min.value() << ", " << interval.max.value() << ']'; +} } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Interval.h b/engine/src/Utils/Spawning/Interval.h index 1748e0e1..762a5f83 100644 --- a/engine/src/Utils/Spawning/Interval.h +++ b/engine/src/Utils/Spawning/Interval.h @@ -13,6 +13,7 @@ #include <MantleAPI/Traffic/i_traffic_area_stream.h> #include <units.h> +#include <iostream> #include <set> #include <vector> @@ -30,6 +31,8 @@ struct Padding units::length::meter_t front; }; +std::ostream& operator<<(std::ostream&, const Padding&); + struct Interval { constexpr Interval(units::length::meter_t min, units::length::meter_t max) @@ -75,6 +78,11 @@ struct Interval units::length::meter_t min, max; }; +constexpr Interval operator+(const Interval&, units::length::meter_t); +constexpr Interval operator-(const Interval&, units::length::meter_t); + +std::ostream& operator<<(std::ostream&, const Interval&); + struct OffsetBy { constexpr Interval operator()(const Interval&) const; @@ -139,6 +147,16 @@ constexpr Interval Interval::OffsetBy(units::length::meter_t min_offset, units:: return {min + min_offset, max + max_offset}; } +constexpr Interval operator+(const Interval& interval, units::length::meter_t offset) +{ + return {interval.min + offset, interval.max + offset}; +} + +constexpr Interval operator-(const Interval& interval, units::length::meter_t offset) +{ + return {interval.min - offset, interval.max - offset}; +} + constexpr Interval OffsetBy::operator()(const Interval& interval) const { return interval.OffsetBy(offset); diff --git a/engine/src/Utils/Spawning/SpawnSpace.cpp b/engine/src/Utils/Spawning/SpawnSpace.cpp index 98ee93e6..b8e77eed 100644 --- a/engine/src/Utils/Spawning/SpawnSpace.cpp +++ b/engine/src/Utils/Spawning/SpawnSpace.cpp @@ -74,7 +74,7 @@ SequentialSpawnSpace::FindSpot(const Padding &padding, units::velocity::meters_p Interval range{spot->OffsetBy(-padding)}; if (range.min <= range.max) { - range = range.GetIntersection(spot->OffsetBy(spot->velocity * timespan)); + range = range.GetIntersection(spot->OffsetBy(spot->velocity * timespan - velocity * timespan)); if (range.min <= range.max) { return {area, stream, std::next(spot).base(), std::move(range)}; @@ -176,4 +176,32 @@ Padding GetLengthPadding(const Entity &entity) const units::length::meter_t half_length{GetLength(entity) * units::dimensionless::scalar_t{.5}}; return {half_length, half_length}; } + +void UpdateSpot(std::vector<OpenScenarioEngine::v1_3::SpawnSpot> &stream, + std::vector<OpenScenarioEngine::v1_3::SpawnSpot>::iterator spot, + const Interval &occupied_space, + units::velocity::meters_per_second_t velocity) +{ + std::vector<OpenScenarioEngine::v1_3::Interval> new_intervals{spot->SplitBy(occupied_space)}; + if (new_intervals.empty()) + { + stream.erase(spot); + } + else + { + std::vector<SpawnSpot> new_spots{Transform(std::move(new_intervals), [spot](Interval &&interval) -> SpawnSpot { // + return {interval, spot->velocity}; + })}; + if (new_spots.front().min < occupied_space.min) + { + new_spots.front().velocity.max = velocity; + } + if (new_spots.back().max > occupied_space.max) + { + new_spots.back().velocity.min = velocity; + } + stream.insert(stream.erase(spot), std::make_move_iterator(new_spots.begin()), std::make_move_iterator(new_spots.end())); + } +} + } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/SpawnSpace.h b/engine/src/Utils/Spawning/SpawnSpace.h index 9b838397..faef6325 100644 --- a/engine/src/Utils/Spawning/SpawnSpace.h +++ b/engine/src/Utils/Spawning/SpawnSpace.h @@ -104,6 +104,19 @@ struct DistributedSpawnSpace : SpawnSpace /// Returns the positive distance from the entity's center to its rear and the positive distance from the entity's center to its front. Padding GetLengthPadding(const Entity &); + +//! Receives a vector of spawn spots, an iterator to a spot within that vector, an interval contained by that spot and a velocity. +//! Replaces the spot pointed to by the iterator with the disjunction of the spot's interval and the given interval. +//! The velocities of the new spots match the given velocity at the end where they touch the given interval. +//! +//! @param stream Container of spots in which a specific spot will be erased and possibly others inserted +//! @param spot Iterator to a spot that will be replaced +//! @param occupied_space Interval to be cut out of the given spot +//! @param velocity Velocity of the ends of the new inserted spots at the given interval +void UpdateSpot(std::vector<OpenScenarioEngine::v1_3::SpawnSpot> &stream, + std::vector<OpenScenarioEngine::v1_3::SpawnSpot>::iterator spot, + const Interval &occupied_space, + units::velocity::meters_per_second_t velocity); } // namespace OpenScenarioEngine::v1_3 namespace OpenScenarioEngine::v1_3 @@ -131,25 +144,15 @@ bool SequentialSpawnSpace::Spawn(EntityCreator &&spawner, StochasticsInterface & const auto area{std::next(areas.begin(), std::distance(spots.begin(), spot_area))}; const auto &stream{*std::next(area->begin(), std::distance(spot_area->begin(), spot_stream))}; const mantle_api::Pose pose{stream->Convert(mantle_api::ITrafficAreaStream::StreamPose{{interval.max, {}}, {}}).value()}; - mantle_api::IEntity &object{spawner.CreateEntity(entity, "NAME_DISCARDED_BY_TRAFFIC_AREA_ACTION")}; object.SetPosition(pose.position); object.SetOrientation(pose.orientation); - - std::vector<OpenScenarioEngine::v1_3::Interval> new_intervals{spot->SplitBy(Interval{interval.max, interval.max}.OffsetBy(-padding.rear, padding.front))}; - if (new_intervals.empty()) - { - spot_stream->erase(spot); - } - else - { - std::vector<SpawnSpot> new_spots{Transform(std::move(new_intervals), [spot](Interval &&interval) -> SpawnSpot { // - return {interval, spot->velocity}; - })}; - new_spots.front().velocity.max = spawn_velocity; - new_spots.back().velocity.min = spawn_velocity; - spot_stream->insert(spot_stream->erase(spot), std::make_move_iterator(new_spots.begin()), std::make_move_iterator(new_spots.end())); - } + const auto occupied_space{Interval{interval.max, interval.max}.OffsetBy(-padding.rear, padding.front)}; + // object.SetAssignedLaneIds(areas[static_cast<size_t>(std::distance(spots.begin(), spot_area))] // clang-format off + // [static_cast<size_t>(std::distance(spot_area->begin(), spot_stream))]->GetLanesIds( + // occupied_space.min, occupied_space.max + // )); // clang-format on + UpdateSpot(*spot_stream, spot, occupied_space, spawn_velocity); return true; } return false; diff --git a/engine/src/Utils/Spawning/VelocityRange.cpp b/engine/src/Utils/Spawning/VelocityRange.cpp index 0190e588..7e028a6a 100644 --- a/engine/src/Utils/Spawning/VelocityRange.cpp +++ b/engine/src/Utils/Spawning/VelocityRange.cpp @@ -97,4 +97,9 @@ units::velocity::meters_per_second_t SampleVelocity(const VelocityRange &range, { return units::velocity::meters_per_second_t{rng.GetUniformDistributed(range.min.value(), range.max.value())}; } + +std::ostream &operator<<(std::ostream &os, const VelocityRange &range) +{ + return os << '[' << range.min.value() << " - " << range.max.value() << ']'; +} } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/VelocityRange.h b/engine/src/Utils/Spawning/VelocityRange.h index a4a9eeb7..65c40a55 100644 --- a/engine/src/Utils/Spawning/VelocityRange.h +++ b/engine/src/Utils/Spawning/VelocityRange.h @@ -12,6 +12,7 @@ #include <Stochastics/StochasticsInterface.h> #include <units.h> +#include <iostream> #include <memory> #include "Interval.h" @@ -53,6 +54,8 @@ template <typename Input> VelocityRange GetSpawnVelocityRange(Input &&); units::velocity::meters_per_second_t SampleVelocity(const VelocityRange &, StochasticsInterface &); + +std::ostream &operator<<(std::ostream &, const VelocityRange &); } // namespace OpenScenarioEngine::v1_3 namespace OpenScenarioEngine::v1_3 diff --git a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp index b78a52d9..594ce5c7 100644 --- a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp +++ b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp @@ -10,12 +10,14 @@ #include <MantleAPI/Test/test_utils.h> #include <Storyboard/GenericAction/TrafficAreaAction.h> +#include <gmock/gmock.h> #include <gtest/gtest.h> #include <units.h> #include <memory> #include <string> #include <utility> +#include <vector> #include "OpenScenarioEngine/OpenScenarioEngine.h" #include "TestUtils.h" @@ -23,6 +25,7 @@ using testing::_; using testing::HasSubstr; +using testing::NiceMock; using testing::Return; using testing::ReturnRef; using testing::OpenScenarioEngine::v1_3::GetScenariosPath; @@ -87,18 +90,22 @@ protected: auto& mockTrafficAreaService{dynamic_cast<mantle_api::MockTrafficAreaService&>(env_->GetTrafficAreaService())}; ON_CALL(stream, GetLength()).WillByDefault(Return(500_m)); - ON_CALL(stream, Convert(testing::A<const mantle_api::ITrafficAreaStream::StreamPose&>())).WillByDefault(Return(mantle_api::Pose{mantle_api::Vec3<units::length::meter_t>{100_m, 0_m, 0_m}, mantle_api::Orientation3<units::angle::radian_t>{}})); // clang-format off - ON_CALL(mockTrafficAreaService, CreateTrafficArea(_)).WillByDefault([this]() -> std::vector<std::shared_ptr<mantle_api::ITrafficAreaStream>> { // + ON_CALL(stream, Convert(testing::A<const mantle_api::ITrafficAreaStream::StreamPose&>())).WillByDefault([](const mantle_api::ITrafficAreaStream::StreamPose& pose) { // clang-format off + return mantle_api::Pose{mantle_api::Vec3<units::length::meter_t>{pose.s, pose.t, 0_m}, mantle_api::Orientation3<units::angle::radian_t>{}}; + }); + ON_CALL(mockTrafficAreaService, CreateTrafficArea(_)).WillByDefault([this]() -> std::vector<std::shared_ptr<mantle_api::ITrafficAreaStream>> { // return streams; + }); // clang-format on + auto& entity_repository{static_cast<mantle_api::MockEntityRepository&>(env_->GetEntityRepository())}; // clang-format off + ON_CALL(entity_repository, Create(testing::A<const std::string&>(), testing::A<const mantle_api::VehicleProperties&>())).WillByDefault([this](const std::string&, const mantle_api::VehicleProperties&) -> NiceMock<MockVehicle>& { + return mock_vehicles.emplace_back(); }); // clang-format on - auto& entity_repository{static_cast<mantle_api::MockEntityRepository&>(env_->GetEntityRepository())}; - ON_CALL(entity_repository, Create(testing::A<const std::string&>(), testing::A<const mantle_api::VehicleProperties&>())).WillByDefault(ReturnRef(mock_vehicle)); } std::vector<std::shared_ptr<mantle_api::ITrafficAreaStream>> streams; std::shared_ptr<testing::OpenScenarioEngine::v1_3::TestLogger> LOGGER; - MockVehicle mock_vehicle; + std::vector<NiceMock<MockVehicle>> mock_vehicles; std::string controller_name{"TestController"}; }; @@ -110,5 +117,16 @@ TEST_F(TrafficAreaActionTestFixture, GivenScenarioWithTrafficAreaAction_WhenInit engine.Init(); engine.SetupDynamicContent(); engine.Step({}); - EXPECT_EQ(mock_vehicle.xyz.x, 100_m); + const size_t offset{1}; // SetupDynamicContent spawns one entity, which we want to ignore (env_->GetEntityRepository().GetEntities().size() does not work) + // The initial area is 500m long. The vehicle is 5_m long. + // The last place its center can be placed at is 500 - 5/2 = 497.5 + EXPECT_EQ(mock_vehicles[0 + offset].xyz.x, 497.5_m); + // The prior vehicle starts at 497.5 - 5/2 = 495 + // The last place the next vehicle's center can be placed at is 495 - 5/2 = 492.5 + // If its randomly sampled velocity is greater than that of the prior vehicle, it may be placed even further back + EXPECT_LE(mock_vehicles[1 + offset].xyz.x, 492.5_m); + for (auto vehicle{std::next(mock_vehicles.begin(), offset + 1)}; vehicle < mock_vehicles.end(); ++vehicle) + { + EXPECT_LE(vehicle->xyz.x, std::prev(vehicle)->xyz.x - 5_m); + } } -- GitLab From f2fa931eb7ebef0f254141a67df3bb56c86003cd Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Wed, 19 Mar 2025 17:55:48 +0100 Subject: [PATCH 21/26] Pass ScenarioObjectTemplate through TrafficAreaAction Doing so makes the list of controllers available when spawning --- engine/cmake/generated_files.cmake | 1 + .../ConvertScenarioTrafficDistribution.cpp | 2 +- .../ConvertScenarioTrafficDistribution.h | 2 +- .../GenericAction/TrafficAreaAction.cpp | 22 +------ .../GenericAction/TrafficAreaAction.h | 10 +++ engine/src/Utils/Spawning/Length.cpp | 10 +-- engine/src/Utils/Spawning/Length.h | 28 +++++++- engine/src/Utils/Spawning/SpawnSpace.cpp | 24 +++---- engine/src/Utils/Spawning/SpawnSpace.h | 49 +++++++------- engine/src/Utils/Spawning/VelocityRange.cpp | 5 ++ engine/src/Utils/Spawning/VelocityRange.h | 1 + engine/src/Utils/Spawning/Weight.cpp | 7 -- engine/src/Utils/Spawning/Weight.h | 13 +++- .../{WeightedEntity.h => Weighted.cpp} | 19 +++--- engine/src/Utils/Spawning/Weighted.h | 65 +++++++++++++++++++ 15 files changed, 169 insertions(+), 89 deletions(-) rename engine/src/Utils/Spawning/{WeightedEntity.h => Weighted.cpp} (57%) create mode 100644 engine/src/Utils/Spawning/Weighted.h diff --git a/engine/cmake/generated_files.cmake b/engine/cmake/generated_files.cmake index 1c4c3ab5..4750e194 100644 --- a/engine/cmake/generated_files.cmake +++ b/engine/cmake/generated_files.cmake @@ -239,6 +239,7 @@ list(APPEND ${PROJECT_NAME}_SOURCES src/Utils/Spawning/SpawnZone.cpp src/Utils/Spawning/VelocityRange.cpp src/Utils/Spawning/Weight.cpp + src/Utils/Spawning/Weighted.cpp src/Utils/ConditionEdgeEvaluator.cpp src/Utils/ControllerCreator.cpp src/Utils/ControllerService.cpp diff --git a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp index 1fd3d7a3..8f2cdb17 100644 --- a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp +++ b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.cpp @@ -33,7 +33,7 @@ struct ToTrafficDistribution TrafficDistribution operator()(const NET_ASAM_OPENSCENARIO::v1_3::ITrafficDistributionEntry& entry) const { return {TransformSharedPointers(entry.GetEntityDistribution()->GetEntityDistributionEntry(), [](const NET_ASAM_OPENSCENARIO::v1_3::IEntityDistributionEntry& entry) - { return entry.GetScenarioObjectTemplate()->GetEntitiyObject(); }), // https://github.com/RA-Consulting-GmbH/openscenario.api.test/issues/222 + { return entry.GetScenarioObjectTemplate(); }), // https://github.com/RA-Consulting-GmbH/openscenario.api.test/issues/222 entry.GetWeight()}; } }; diff --git a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h index 5442081e..2149edf1 100644 --- a/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h +++ b/engine/src/Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h @@ -20,7 +20,7 @@ namespace OpenScenarioEngine::v1_3 { -struct TrafficDistribution : Iterable<std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject>>> +struct TrafficDistribution : Iterable<std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IScenarioObjectTemplate>>> { units::dimensionless::scalar_t weight; }; diff --git a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp index 3c7d47a7..af2ab558 100644 --- a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp +++ b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp @@ -29,23 +29,6 @@ void TrafficAreaAction::UpdateTrafficAreas() bool TrafficAreaAction::Step() { - // Spawn entities in a randomly selected area among this action's defined traffic areas. - // Selection is weighted based on the length of each area's remaining spawnable area (SpawnZone). - - // Traffic distributions determine which vehicles can spawn and their likelihood of spawning. The distributions are sorted by - // entity length for efficient filtering in case the largest interval grows smaller than some spawnable entities. Similary, a - // SpawnZone tracks its largest interval (without sorting, as it would have to be reordered each time an entity is spawned). - - // The general process of the spawn loop must go as follows: - // - Filter out the entities of all traffic distributions longer than the largest interval of all areas. - // (can be skipped if the last spawned entity was not on the largest interval) - // - Sample the entity to be spawned based on the weights of all entity distributions - // - Filter out the intervals of all areas shorter than the entity to be spawned - // - Sample the spawn position from the total length of all remaining intervals - // - Shrink the sampled interval interval by the front and rear offset from the entity's center to its - // front and rear bounds, scaling the sampled value accordingly. - // - Spawn the entity and update the computed area intervals and lengths. - if (values.numberOfEntities == 0) // No entities left to spawn { return true; @@ -88,7 +71,8 @@ yase::NodeStatus TrafficAreaAction::tick() void TrafficAreaAction::lookupAndRegisterData(yase::Blackboard& blackboard) { - std::shared_ptr<mantle_api::IEnvironment> environment = blackboard.get<std::shared_ptr<mantle_api::IEnvironment>>("Environment"); + auto environment = blackboard.get<std::shared_ptr<mantle_api::IEnvironment>>("Environment"); + std::shared_ptr<IControllerService> controller_service = blackboard.get<std::shared_ptr<IControllerService>>("ControllerService"); impl_ = std::make_unique<OpenScenarioEngine::v1_3::TrafficAreaAction>( Values<OpenScenarioEngine::v1_3::TrafficAreaAction>{ @@ -96,7 +80,7 @@ void TrafficAreaAction::lookupAndRegisterData(yase::Blackboard& blackboard) action_->GetNumberOfEntities(), ConvertScenarioTrafficDistribution(action_->GetTrafficDistribution()), ConvertScenarioTrafficArea(action_->GetTrafficArea())}, - Interfaces<OpenScenarioEngine::v1_3::TrafficAreaAction>{environment}); + Interfaces<OpenScenarioEngine::v1_3::TrafficAreaAction>{environment, controller_service}); } } // namespace Node } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Storyboard/GenericAction/TrafficAreaAction.h b/engine/src/Storyboard/GenericAction/TrafficAreaAction.h index 22cd970d..13633df5 100644 --- a/engine/src/Storyboard/GenericAction/TrafficAreaAction.h +++ b/engine/src/Storyboard/GenericAction/TrafficAreaAction.h @@ -24,6 +24,7 @@ #include "Action.h" #include "Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h" +#include "Utils/IControllerService.h" namespace OpenScenarioEngine::v1_3 { @@ -38,6 +39,15 @@ struct Values<TrafficAreaAction> std::vector<mantle_api::RoadRange> trafficArea; }; +template <> +struct Interfaces<TrafficAreaAction> +{ + std::shared_ptr<mantle_api::IEnvironment> environment; + std::shared_ptr<IControllerService> controller_service; + + // void CreateController(mantle_api::IEntity& entity); +}; + class TrafficAreaAction : public Action<TrafficAreaAction> { public: diff --git a/engine/src/Utils/Spawning/Length.cpp b/engine/src/Utils/Spawning/Length.cpp index 03706dd3..1d2a41e6 100644 --- a/engine/src/Utils/Spawning/Length.cpp +++ b/engine/src/Utils/Spawning/Length.cpp @@ -29,11 +29,6 @@ units::length::meter_t ToLength::operator()(const mantle_api::TrafficArea& area) return std::transform_reduce(area.begin(), area.end(), units::length::meter_t{}, std::plus<>{}, *this); } -units::length::meter_t ToLength::operator()(const WeightedEntity& entity) const -{ - return (*this)(entity.handle); -} - units::length::meter_t ToLength::operator()(const mantle_api::IEntity& entity) const { return (*this)(entity.GetProperties()); @@ -107,4 +102,9 @@ units::length::meter_t ToLength::operator()(const NET_ASAM_OPENSCENARIO::v1_3::I } throw std::runtime_error("ToLength: Entity is not a catalog reference, vehicle, pedestrian or misc. object."); } + +units::length::meter_t ToLength::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IScenarioObjectTemplate& input) const +{ + return (*this)(input.GetEntitiyObject()); +} } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Length.h b/engine/src/Utils/Spawning/Length.h index e5c9702d..47d7de9c 100644 --- a/engine/src/Utils/Spawning/Length.h +++ b/engine/src/Utils/Spawning/Length.h @@ -22,11 +22,10 @@ #include "Interval.h" #include "SpawnZone.h" #include "Transform.h" +#include "Weighted.h" namespace OpenScenarioEngine::v1_3 { -struct WeightedEntity; - struct ToLength { constexpr units::length::meter_t operator()(units::length::meter_t) const; @@ -38,7 +37,8 @@ struct ToLength template <typename Type> units::length::meter_t operator()(const std::shared_ptr<Type>&) const; - units::length::meter_t operator()(const WeightedEntity&) const; + template <typename Type> + units::length::meter_t operator()(const Weighted<Type>&) const; units::length::meter_t operator()(const mantle_api::IEntity&) const; @@ -62,6 +62,8 @@ struct ToLength units::length::meter_t operator()(const SpawnZones&) const; units::length::meter_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject&) const; + + units::length::meter_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IScenarioObjectTemplate&) const; }; struct ToSize @@ -85,6 +87,13 @@ struct ToAccumulatedLength units::length::meter_t totalLength; }; + +/// Returns the positive distance from the entity's center to its rear and the positive distance from the entity's center to its front. +/// +/// \tparam Type Type for which GetLength(const Type&) is implemented +/// \return Positive distance from the entity's center to its rear and the positive distance from the entity's center to its front +template <typename Type> +Padding GetLengthPadding(const Type&); } // namespace OpenScenarioEngine::v1_3 namespace OpenScenarioEngine::v1_3 @@ -105,6 +114,12 @@ units::length::meter_t ToLength::operator()(const std::shared_ptr<Type>& input) return ToLength{}(*input); } +template <typename Type> +units::length::meter_t ToLength::operator()(const Weighted<Type>& input) const +{ + return ToLength{}(input.handle); +} + template <typename Type> units::length::meter_t ToLength::operator()(const Type* input) const { @@ -140,4 +155,11 @@ units::length::meter_t ToAccumulatedLength::operator()(const Type& input) totalLength += GetLength(input); return totalLength; } + +template <typename Input> +Padding GetLengthPadding(const Input& input) +{ // TODO: Consider the center offset of the entity's bounds + const units::length::meter_t half_length{GetLength(input) * units::dimensionless::scalar_t{.5}}; + return {half_length, half_length}; +} } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/SpawnSpace.cpp b/engine/src/Utils/Spawning/SpawnSpace.cpp index b8e77eed..889f8f44 100644 --- a/engine/src/Utils/Spawning/SpawnSpace.cpp +++ b/engine/src/Utils/Spawning/SpawnSpace.cpp @@ -30,7 +30,7 @@ SpawnSpace::SpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &dis for (auto &distribution : distributions) { std::transform(distribution.begin(), distribution.end(), std::back_inserter(entities), // - [weight = distribution.weight](const Entity &entity) -> WeightedEntity + [weight = distribution.weight](const ObjectTemplate &entity) -> Weighted<ObjectTemplate> { return {entity, weight}; }); } std::sort(entities.begin(), entities.end(), Less<ToLength>{}); @@ -47,11 +47,11 @@ SequentialSpawnSpace::SequentialSpawnSpace(const std::vector<mantle_api::Traffic }); // clang-format on } -Entity SequentialSpawnSpace::SampleEntity(StochasticsInterface &rng) const +ObjectTemplate SequentialSpawnSpace::SampleObjectTemplate(StochasticsInterface &rng) const { if (entities.size() <= 1) { - return entities.empty() ? Entity{} : entities.front().handle; + return entities.empty() ? ObjectTemplate{} : entities.front().handle; } assert(weights.size() == entities.size() + 1); const units::dimensionless::scalar_t sampled_weight{rng.GetUniformDistributed(.0, weights.back().value())}; @@ -110,13 +110,13 @@ DistributedSpawnSpace::DistributedSpawnSpace(const OpenScenarioEngine::v1_3::Tra } } -Entity DistributedSpawnSpace::SampleEntity(StochasticsInterface &rng) const +ObjectTemplate DistributedSpawnSpace::SampleObjectTemplate(StochasticsInterface &rng) const { const units::length::meter_t max_length{GetMaxIntervalLength()}; const auto entity_end{FindSpawnableEntityEnd(max_length)}; if (std::distance(entities.begin(), entity_end) <= 1) { - return entity_end == entities.begin() ? Entity{} : entities.front().handle; + return entity_end == entities.begin() ? ObjectTemplate{} : entities.front().handle; } assert(weights.size() == entities.size() + 1); const units::dimensionless::scalar_t max_weight{weights[static_cast<size_t>(std::distance(entities.begin(), entity_end))]}; @@ -129,14 +129,14 @@ units::length::meter_t DistributedSpawnSpace::GetMaxIntervalLength() const return spots.empty() ? units::length::meter_t{} : GetLength(*spots.rbegin()); } -std::vector<WeightedEntity>::const_iterator DistributedSpawnSpace::FindSpawnableEntityEnd(units::length::meter_t threshold) const +std::vector<Weighted<ObjectTemplate>>::const_iterator DistributedSpawnSpace::FindSpawnableEntityEnd(units::length::meter_t threshold) const { - return std::upper_bound(entities.begin(), entities.end(), threshold, [](units::length::meter_t threshold, const WeightedEntity &entity) + return std::upper_bound(entities.begin(), entities.end(), threshold, [](units::length::meter_t threshold, const Weighted<ObjectTemplate> &entity) { return threshold < GetLength(entity); }); } -std::vector<WeightedEntity>::iterator DistributedSpawnSpace::FindSpawnableEntityEnd(units::length::meter_t threshold) +std::vector<Weighted<ObjectTemplate>>::iterator DistributedSpawnSpace::FindSpawnableEntityEnd(units::length::meter_t threshold) { - return std::upper_bound(entities.begin(), entities.end(), threshold, [](units::length::meter_t threshold, const WeightedEntity &entity) + return std::upper_bound(entities.begin(), entities.end(), threshold, [](units::length::meter_t threshold, const Weighted<ObjectTemplate> &entity) { return threshold < GetLength(entity); }); } @@ -171,12 +171,6 @@ std::set<SpawnSpot, Less<ToLength>>::const_iterator DistributedSpawnSpace::Sampl return Sample(candidates, lengths, sampledLength); } -Padding GetLengthPadding(const Entity &entity) -{ // TODO: Consider the center offset of the entity's bounds - const units::length::meter_t half_length{GetLength(entity) * units::dimensionless::scalar_t{.5}}; - return {half_length, half_length}; -} - void UpdateSpot(std::vector<OpenScenarioEngine::v1_3::SpawnSpot> &stream, std::vector<OpenScenarioEngine::v1_3::SpawnSpot>::iterator spot, const Interval &occupied_space, diff --git a/engine/src/Utils/Spawning/SpawnSpace.h b/engine/src/Utils/Spawning/SpawnSpace.h index faef6325..0c485089 100644 --- a/engine/src/Utils/Spawning/SpawnSpace.h +++ b/engine/src/Utils/Spawning/SpawnSpace.h @@ -25,7 +25,7 @@ #include "SpawnSpot.h" #include "SpawnZone.h" #include "VelocityRange.h" -#include "WeightedEntity.h" +#include "Weighted.h" namespace OpenScenarioEngine::v1_3 { @@ -33,7 +33,7 @@ struct SpawnSpace { SpawnSpace(const OpenScenarioEngine::v1_3::TrafficDistributions &); - std::vector<WeightedEntity> entities; // Entities sorted by ascending length + std::vector<Weighted<ObjectTemplate>> entities; // Object templates (entity + controllers) sorted by ascending length std::vector<units::dimensionless::scalar_t> weights; // Used to sample an entity }; @@ -48,8 +48,8 @@ struct SequentialSpawnSpace : SpawnSpace /// Samples and returns a random entity without considering the remaining space. /// - /// \return Entity Random entity out of this space's entities. If it has none, a nullptr is returned. - Entity SampleEntity(StochasticsInterface &) const; + /// \return Random entity out of this space's entities. If it has none, a nullptr is returned. + ObjectTemplate SampleObjectTemplate(StochasticsInterface &) const; /// Tries to spawn an entity and returns whether spawning was successful. /// If successful, also adjusts the spawn space's intervals. @@ -80,9 +80,15 @@ struct DistributedSpawnSpace : SpawnSpace /// Returns the entity out of the set of entities in this space based on the given sample /// - Entity GetEntity(units::dimensionless::scalar_t sample); + /// \param sample Value within the accumulated weights of each entity of this space + /// \return Blueprint for spawning an entity alongside controllers for that entity + ObjectTemplate GetObjectTemplate(units::dimensionless::scalar_t sample); - Entity SampleEntity(StochasticsInterface &) const; + //! Returns a random object template (entity + controllers) using the given number generator + //! + //! \param StochasticsInterface Random number generator used to sample a weight + //! \return Randomly selected object template + ObjectTemplate SampleObjectTemplate(StochasticsInterface &) const; std::set<SpawnSpot, Less<ToLength>>::const_iterator SampleSpot(units::length::meter_t min_length, VelocityRange, StochasticsInterface &) const; @@ -96,15 +102,12 @@ struct DistributedSpawnSpace : SpawnSpace units::length::meter_t GetMaxIntervalLength() const; - std::vector<WeightedEntity>::const_iterator FindSpawnableEntityEnd(units::length::meter_t upper_bound) const; - std::vector<WeightedEntity>::iterator FindSpawnableEntityEnd(units::length::meter_t upper_bound); + typename std::vector<Weighted<ObjectTemplate>>::const_iterator FindSpawnableEntityEnd(units::length::meter_t upper_bound) const; + typename std::vector<Weighted<ObjectTemplate>>::iterator FindSpawnableEntityEnd(units::length::meter_t upper_bound); std::set<SpawnSpot, Less<ToLength>> spots; }; -/// Returns the positive distance from the entity's center to its rear and the positive distance from the entity's center to its front. -Padding GetLengthPadding(const Entity &); - //! Receives a vector of spawn spots, an iterator to a spot within that vector, an interval contained by that spot and a velocity. //! Replaces the spot pointed to by the iterator with the disjunction of the spot's interval and the given interval. //! The velocities of the new spots match the given velocity at the end where they touch the given interval. @@ -126,32 +129,30 @@ bool SequentialSpawnSpace::Spawn(EntityCreator &&spawner, StochasticsInterface & { using namespace units::literals; + static size_t i{0}; + if (spots.empty()) { return false; } - Entity entity{SampleEntity(rng)}; - if (entity == nullptr) + ObjectTemplate blueprint{SampleObjectTemplate(rng)}; + if (blueprint == nullptr) { return false; } - const auto velocity_range{GetSpawnVelocityRange(entity)}; + const auto velocity_range{GetSpawnVelocityRange(blueprint)}; auto spawn_velocity{SampleVelocity(velocity_range, rng)}; const units::time::second_t minimum_time_to_collision{2_s}; - const Padding padding{GetLengthPadding(entity)}; + const Padding padding{GetLengthPadding(blueprint)}; if (auto [spot_area, spot_stream, spot, interval]{FindSpot(padding, spawn_velocity, minimum_time_to_collision)}; spot_area != spots.end()) { const auto area{std::next(areas.begin(), std::distance(spots.begin(), spot_area))}; const auto &stream{*std::next(area->begin(), std::distance(spot_area->begin(), spot_stream))}; const mantle_api::Pose pose{stream->Convert(mantle_api::ITrafficAreaStream::StreamPose{{interval.max, {}}, {}}).value()}; - mantle_api::IEntity &object{spawner.CreateEntity(entity, "NAME_DISCARDED_BY_TRAFFIC_AREA_ACTION")}; + mantle_api::IEntity &object{spawner.CreateEntity(GetEntity(blueprint), "Common" + std::to_string(++i))}; object.SetPosition(pose.position); object.SetOrientation(pose.orientation); const auto occupied_space{Interval{interval.max, interval.max}.OffsetBy(-padding.rear, padding.front)}; - // object.SetAssignedLaneIds(areas[static_cast<size_t>(std::distance(spots.begin(), spot_area))] // clang-format off - // [static_cast<size_t>(std::distance(spot_area->begin(), spot_stream))]->GetLanesIds( - // occupied_space.min, occupied_space.max - // )); // clang-format on UpdateSpot(*spot_stream, spot, occupied_space, spawn_velocity); return true; } @@ -161,13 +162,13 @@ bool SequentialSpawnSpace::Spawn(EntityCreator &&spawner, StochasticsInterface & template <typename EntityCreator> bool DistributedSpawnSpace::Spawn(EntityCreator &&spawner, StochasticsInterface &rng) { - Entity entity{SampleEntity(rng)}; - if (entity == nullptr) + ObjectTemplate blueprint{SampleObjectTemplate(rng)}; + if (blueprint == nullptr) { return false; } - const auto velocity_range{GetSpawnVelocityRange(entity)}; - const auto spot{SampleSpot(GetLength(entity), velocity_range, rng)}; + const auto velocity_range{GetSpawnVelocityRange(blueprint)}; + const auto spot{SampleSpot(GetLength(blueprint), velocity_range, rng)}; if (spot == spots.end()) { return false; diff --git a/engine/src/Utils/Spawning/VelocityRange.cpp b/engine/src/Utils/Spawning/VelocityRange.cpp index 7e028a6a..5fc6f685 100644 --- a/engine/src/Utils/Spawning/VelocityRange.cpp +++ b/engine/src/Utils/Spawning/VelocityRange.cpp @@ -56,6 +56,11 @@ VelocityRange ToSpawnVelocityRange::operator()(const std::shared_ptr<NET_ASAM_OP throw std::runtime_error("ToSpawnVelocityRange: Invalid catalog reference. Contains no vehicle, pedestrian or misc. object."); } +VelocityRange ToSpawnVelocityRange::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IScenarioObjectTemplate &input) const +{ + return (*this)(input.GetEntitiyObject()); +} + VelocityRange ToSpawnVelocityRange::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IVehicle &vehicle) const { return (*this)(vehicle.GetProperties()); diff --git a/engine/src/Utils/Spawning/VelocityRange.h b/engine/src/Utils/Spawning/VelocityRange.h index 65c40a55..0ac14b0b 100644 --- a/engine/src/Utils/Spawning/VelocityRange.h +++ b/engine/src/Utils/Spawning/VelocityRange.h @@ -43,6 +43,7 @@ struct ToSpawnVelocityRange VelocityRange operator()(const std::shared_ptr<Type> &) const; VelocityRange operator()(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::ICatalogElement> &) const; + VelocityRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IScenarioObjectTemplate &) const; VelocityRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IEntityObject &) const; VelocityRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IVehicle &) const; VelocityRange operator()(const NET_ASAM_OPENSCENARIO::v1_3::IPedestrian &) const; diff --git a/engine/src/Utils/Spawning/Weight.cpp b/engine/src/Utils/Spawning/Weight.cpp index 96774fbc..571157b6 100644 --- a/engine/src/Utils/Spawning/Weight.cpp +++ b/engine/src/Utils/Spawning/Weight.cpp @@ -9,8 +9,6 @@ ********************************************************************************/ #include "Weight.h" -#include "WeightedEntity.h" - namespace OpenScenarioEngine::v1_3 { units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO::v1_3::IScenarioObjectTemplate& object) const @@ -66,9 +64,4 @@ units::dimensionless::scalar_t ToWeight::operator()(const NET_ASAM_OPENSCENARIO: } throw std::runtime_error("ToWeight: 'Weight' not found in properties"); } - -units::dimensionless::scalar_t ToWeight::operator()(const WeightedEntity& entity) const -{ - return entity.weight; -} } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Weight.h b/engine/src/Utils/Spawning/Weight.h index e5d83854..f2ff9cdd 100644 --- a/engine/src/Utils/Spawning/Weight.h +++ b/engine/src/Utils/Spawning/Weight.h @@ -15,10 +15,10 @@ #include <memory> +#include "Weighted.h" + namespace OpenScenarioEngine::v1_3 { -struct WeightedEntity; - struct ToWeight { constexpr units::dimensionless::scalar_t operator()(units::dimensionless::scalar_t) const; @@ -40,7 +40,8 @@ struct ToWeight units::dimensionless::scalar_t operator()(const NET_ASAM_OPENSCENARIO::v1_3::IProperties&) const; - units::dimensionless::scalar_t operator()(const WeightedEntity&) const; + template <typename Type> + units::dimensionless::scalar_t operator()(const Weighted<Type>&) const; }; template <typename Input> @@ -68,6 +69,12 @@ units::dimensionless::scalar_t ToWeight::operator()(const std::shared_ptr<Type>& return ToWeight{}(*input); } +template <typename Type> +units::dimensionless::scalar_t ToWeight::operator()(const Weighted<Type>& input) const +{ + return input.weight; +} + template <typename Input> units::dimensionless::scalar_t GetWeight(Input&& input) { diff --git a/engine/src/Utils/Spawning/WeightedEntity.h b/engine/src/Utils/Spawning/Weighted.cpp similarity index 57% rename from engine/src/Utils/Spawning/WeightedEntity.h rename to engine/src/Utils/Spawning/Weighted.cpp index e17b8183..2702aecb 100644 --- a/engine/src/Utils/Spawning/WeightedEntity.h +++ b/engine/src/Utils/Spawning/Weighted.cpp @@ -7,20 +7,17 @@ * * SPDX-License-Identifier: EPL-2.0 ********************************************************************************/ -#pragma once - -#include <openScenarioLib/generated/v1_3/api/ApiClassInterfacesV1_3.h> -#include <units.h> - -#include <memory> +#include "Weighted.h" namespace OpenScenarioEngine::v1_3 { -using Entity = typename std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject>; +std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IObjectController>> GetControllers(const NET_ASAM_OPENSCENARIO::v1_3::IScenarioObjectTemplate& handle) +{ + return handle.GetObjectController(); +} -struct WeightedEntity +std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject> GetEntity(const NET_ASAM_OPENSCENARIO::v1_3::IScenarioObjectTemplate& handle) { - Entity handle; - units::dimensionless::scalar_t weight; // Product of the distribution's weight and the weight of this entity's handle -}; + return handle.GetEntitiyObject(); +} } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/Weighted.h b/engine/src/Utils/Spawning/Weighted.h new file mode 100644 index 00000000..b9288b4d --- /dev/null +++ b/engine/src/Utils/Spawning/Weighted.h @@ -0,0 +1,65 @@ +/******************************************************************************** + * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ +#pragma once + +#include <openScenarioLib/generated/v1_3/api/ApiClassInterfacesV1_3.h> +#include <units.h> + +#include <memory> + +namespace OpenScenarioEngine::v1_3 +{ +//! \brief Shared point of a scenario object template, which is an entity paired with an arbitrary number of controllers +using ObjectTemplate = typename std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IScenarioObjectTemplate>; + +template <typename Type> +struct Weighted +{ + constexpr operator const Type&() const; + constexpr operator Type&(); + + Type handle; + units::dimensionless::scalar_t weight; +}; + +template <typename Type> +std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IObjectController>> GetControllers(const std::shared_ptr<Type>&); + +std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IObjectController>> GetControllers(const NET_ASAM_OPENSCENARIO::v1_3::IScenarioObjectTemplate&); + +template <typename Type> +std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject> GetEntity(const std::shared_ptr<Type>&); + +std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject> GetEntity(const NET_ASAM_OPENSCENARIO::v1_3::IScenarioObjectTemplate&); + +template <typename Type> +constexpr Weighted<Type>::operator const Type&() const +{ + return (handle); +} + +template <typename Type> +constexpr Weighted<Type>::operator Type&() +{ + return (handle); +} + +template <typename Type> +std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IObjectController>> GetControllers(const std::shared_ptr<Type>& input) +{ + return GetControllers(*input); +} + +template <typename Type> +std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IEntityObject> GetEntity(const std::shared_ptr<Type>& input) +{ + return GetEntity(*input); +} +} // namespace OpenScenarioEngine::v1_3 -- GitLab From 37dd227d9efbae65cff11d88ce3fe3e94e735d11 Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Fri, 21 Mar 2025 10:43:48 +0100 Subject: [PATCH 22/26] TrafficAreaAction: Add controllers --- .../GenericAction/TrafficAreaAction.cpp | 18 +++++++++-- .../GenericAction/TrafficAreaAction.h | 6 ++-- .../GenericAction/TrafficSwarmAction_impl.cpp | 14 ++------ engine/src/Utils/ControllerCreator.cpp | 7 ++-- engine/src/Utils/ControllerCreator.h | 8 ++--- engine/src/Utils/Geometry.h | 28 ++++++++++++++++ engine/src/Utils/Spawning/SpawnSpace.h | 32 ++++++++----------- 7 files changed, 69 insertions(+), 44 deletions(-) create mode 100644 engine/src/Utils/Geometry.h diff --git a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp index af2ab558..797aa3eb 100644 --- a/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp +++ b/engine/src/Storyboard/GenericAction/TrafficAreaAction.cpp @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2021-2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2021-2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -20,6 +20,14 @@ namespace OpenScenarioEngine::v1_3 { +mantle_api::IController& Interfaces<TrafficAreaAction>::operator()(mantle_api::IEntity& entity, const std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IObjectController>>& controllers) +{ + ControllerRegistrar registrar{entity, entity.GetName(), controllers, *environment, environment->GetControllerRepository(), controller_service}; + auto& controller{registrar.CreateDefaultController()}; + registrar.CreateUserDefinedControllers(false); + return controller; +} + void TrafficAreaAction::UpdateTrafficAreas() { trafficAreas = Transform(values.trafficArea, [this](const mantle_api::RoadRange& range) @@ -49,7 +57,7 @@ bool TrafficAreaAction::Step() SequentialSpawnSpace spawnSpace{trafficAreas, values.trafficDistributions}; while (values.numberOfEntities) { - if (!spawnSpace.Spawn(spawner, rng)) + if (!spawnSpace.Spawn(spawner, mantle, rng)) { return false; } @@ -72,7 +80,11 @@ yase::NodeStatus TrafficAreaAction::tick() void TrafficAreaAction::lookupAndRegisterData(yase::Blackboard& blackboard) { auto environment = blackboard.get<std::shared_ptr<mantle_api::IEnvironment>>("Environment"); - std::shared_ptr<IControllerService> controller_service = blackboard.get<std::shared_ptr<IControllerService>>("ControllerService"); + auto controller_service = std::dynamic_pointer_cast<ControllerService>(blackboard.get<std::shared_ptr<IControllerService>>("ControllerService")); + if (!controller_service) + { + throw std::runtime_error("TrafficAreaAction::lookupAndRegisterData: IControllerService not supported"); + } impl_ = std::make_unique<OpenScenarioEngine::v1_3::TrafficAreaAction>( Values<OpenScenarioEngine::v1_3::TrafficAreaAction>{ diff --git a/engine/src/Storyboard/GenericAction/TrafficAreaAction.h b/engine/src/Storyboard/GenericAction/TrafficAreaAction.h index 13633df5..ddf6db21 100644 --- a/engine/src/Storyboard/GenericAction/TrafficAreaAction.h +++ b/engine/src/Storyboard/GenericAction/TrafficAreaAction.h @@ -24,7 +24,7 @@ #include "Action.h" #include "Conversion/OscToMantle/ConvertScenarioTrafficDistribution.h" -#include "Utils/IControllerService.h" +#include "Utils/ControllerService.h" namespace OpenScenarioEngine::v1_3 { @@ -43,9 +43,9 @@ template <> struct Interfaces<TrafficAreaAction> { std::shared_ptr<mantle_api::IEnvironment> environment; - std::shared_ptr<IControllerService> controller_service; + std::shared_ptr<ControllerService> controller_service; - // void CreateController(mantle_api::IEntity& entity); + mantle_api::IController& operator()(mantle_api::IEntity& entity, const std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IObjectController>>& controllers); }; class TrafficAreaAction : public Action<TrafficAreaAction> diff --git a/engine/src/Storyboard/GenericAction/TrafficSwarmAction_impl.cpp b/engine/src/Storyboard/GenericAction/TrafficSwarmAction_impl.cpp index 16deb16a..228cb5a3 100644 --- a/engine/src/Storyboard/GenericAction/TrafficSwarmAction_impl.cpp +++ b/engine/src/Storyboard/GenericAction/TrafficSwarmAction_impl.cpp @@ -15,21 +15,11 @@ #include "MantleAPI/Traffic/i_traffic_swarm_service.h" #include "Utils/Ellipse.h" #include "Utils/EntityUtils.h" +#include "Utils/Geometry.h" namespace detail { constexpr units::length::meter_t ELLIPSE_DETECTION_TOLERANCE{1.0}; - -mantle_api::Vec3<units::velocity::meters_per_second_t> GetVelocityVector( - units::velocity::meters_per_second_t speed, - const mantle_api::Orientation3<units::angle::radian_t>& orientation) -{ - auto cos_elevation{units::math::cos(orientation.pitch)}; - return {speed * units::math::cos(orientation.yaw) * cos_elevation, - speed * units::math::sin(orientation.yaw) * cos_elevation, - speed * -units::math::sin(orientation.pitch)}; -} - } // namespace detail namespace OpenScenarioEngine::v1_3 @@ -238,7 +228,7 @@ void TrafficSwarmAction::SpawnEntities() auto& entity{mantle_.environment->GetEntityRepository().Create("traffic-swarm-entity-" + std::to_string(spawned_entities_count_ + 1), vehicle_properties)}; entity.SetPosition(spawning_pose.position); entity.SetOrientation(spawning_pose.orientation); - entity.SetVelocity(detail::GetVelocityVector(vehicle_speed, spawning_pose.orientation)); + entity.SetVelocity(util::Rotate(vehicle_speed, spawning_pose.orientation)); const auto controller_configuration{GetControllerConfiguration()}; auto external_controller_config{std::make_unique<mantle_api::ExternalControllerConfig>(controller_configuration)}; diff --git a/engine/src/Utils/ControllerCreator.cpp b/engine/src/Utils/ControllerCreator.cpp index 669a8875..96de78e2 100644 --- a/engine/src/Utils/ControllerCreator.cpp +++ b/engine/src/Utils/ControllerCreator.cpp @@ -1,5 +1,5 @@ /******************************************************************************* - * Copyright (c) 2021-2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2021-2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * This program and the accompanying materials are made * available under the terms of the Eclipse Public License 2.0 @@ -162,7 +162,7 @@ void ControllerCreator::CreateControllers(const std::vector<std::shared_ptr<NET_ ControllerRegistrar::ControllerRegistrar( mantle_api::IEntity& entity, const std::string& entity_name, - std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IObjectController>>&& object_controllers, + std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IObjectController>> object_controllers, mantle_api::IEnvironment& environment, mantle_api::IControllerRepository& controller_repository, std::shared_ptr<ControllerService>& controller_service) @@ -175,7 +175,7 @@ ControllerRegistrar::ControllerRegistrar( { } -void ControllerRegistrar::CreateDefaultController() +mantle_api::IController& ControllerRegistrar::CreateDefaultController() { Logger::Info("ControllerCreator: Setting up internal controller for entity \"" + entity_name_ + "\""); @@ -186,6 +186,7 @@ void ControllerRegistrar::CreateDefaultController() auto& controller = controller_repository_.Create(std::move(default_config)); controller.ChangeState(mantle_api::IController::LateralState::kActivate, mantle_api::IController::LongitudinalState::kActivate); RegisterDefaultController(controller); + return controller; } void ControllerRegistrar::CreateUserDefinedControllers(bool control_override) diff --git a/engine/src/Utils/ControllerCreator.h b/engine/src/Utils/ControllerCreator.h index abdbff2a..4542cadd 100644 --- a/engine/src/Utils/ControllerCreator.h +++ b/engine/src/Utils/ControllerCreator.h @@ -1,5 +1,5 @@ /******************************************************************************** - * Copyright (c) 2021-2024 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * Copyright (c) 2021-2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) * * This program and the accompanying materials are made available under the * terms of the Eclipse Public License 2.0 which is available at @@ -50,12 +50,12 @@ public: explicit ControllerRegistrar( mantle_api::IEntity& entity, const std::string& entity_name, - std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IObjectController>>&& object_controllers, + std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IObjectController>> object_controllers, mantle_api::IEnvironment& environment, mantle_api::IControllerRepository& controller_repository, std::shared_ptr<ControllerService>& controller_service); - void CreateDefaultController(); + mantle_api::IController& CreateDefaultController(); void CreateUserDefinedControllers(bool control_override); private: @@ -68,7 +68,7 @@ private: const std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IObjectController>> object_controllers_; mantle_api::IEnvironment& environment_; mantle_api::IControllerRepository& controller_repository_; - std::shared_ptr<ControllerService>& controller_service_; + std::shared_ptr<ControllerService> controller_service_; }; } // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Geometry.h b/engine/src/Utils/Geometry.h new file mode 100644 index 00000000..5e3fd697 --- /dev/null +++ b/engine/src/Utils/Geometry.h @@ -0,0 +1,28 @@ +/******************************************************************************** + * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ +#pragma once + +#include <MantleAPI/Common/orientation.h> +#include <MantleAPI/Common/vector.h> +#include <units.h> + +namespace util +{ +template <typename Unit> +constexpr mantle_api::Vec3<Unit> Rotate( + Unit magnitude, + const mantle_api::Orientation3<units::angle::radian_t>& orientation) +{ + auto cos_elevation{units::math::cos(orientation.pitch)}; + return {magnitude * units::math::cos(orientation.yaw) * cos_elevation, + magnitude * units::math::sin(orientation.yaw) * cos_elevation, + magnitude * -units::math::sin(orientation.pitch)}; +} +} // namespace util diff --git a/engine/src/Utils/Spawning/SpawnSpace.h b/engine/src/Utils/Spawning/SpawnSpace.h index 0c485089..ade5d282 100644 --- a/engine/src/Utils/Spawning/SpawnSpace.h +++ b/engine/src/Utils/Spawning/SpawnSpace.h @@ -13,7 +13,6 @@ #include <Stochastics/StochasticsInterface.h> #include <units.h> -#include <cassert> #include <memory> #include <set> #include <utility> @@ -24,6 +23,7 @@ #include "Length.h" #include "SpawnSpot.h" #include "SpawnZone.h" +#include "Utils/Geometry.h" #include "VelocityRange.h" #include "Weighted.h" @@ -55,9 +55,11 @@ struct SequentialSpawnSpace : SpawnSpace /// If successful, also adjusts the spawn space's intervals. /// /// \tparam EntityCreator Provides CreateEntity(const std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IScenarioObject> &) + /// \tparam AttachControllers Invocable taking (mantle_api::IEntity&, std::vector<std::shared_ptr<NET_ASAM_OPENSCENARIO::v1_3::IObjectController>>) + /// used to attach controllers to the spawned entity /// \param StochasticsInterface Used to sample the entity and its velocity - template <typename EntityCreator> - bool Spawn(EntityCreator &&, StochasticsInterface &); + template <typename EntityCreator, typename AttachControllers> + bool Spawn(EntityCreator &&, AttachControllers &&, StochasticsInterface &); std::tuple<std::vector<std::vector<std::vector<SpawnSpot>>>::iterator, std::vector<std::vector<SpawnSpot>>::iterator, @@ -65,7 +67,7 @@ struct SequentialSpawnSpace : SpawnSpace Interval> FindSpot(const Padding &, units::velocity::meters_per_second_t velocity, units::time::second_t timespan); - // Stream -> vector<SpawnSpot>, thus Area = vector<vector<SpawnSpot>>, thus vector<Area> = vector<vector<vector<SpawnSpot>>> + // Stream = vector<SpawnSpot>, thus Area = vector<vector<SpawnSpot>>, thus vector<Area> = vector<vector<vector<SpawnSpot>>> std::vector<std::vector<std::vector<SpawnSpot>>> spots; const std::vector<mantle_api::TrafficArea> &areas; }; @@ -124,8 +126,8 @@ void UpdateSpot(std::vector<OpenScenarioEngine::v1_3::SpawnSpot> &stream, namespace OpenScenarioEngine::v1_3 { -template <typename EntityCreator> -bool SequentialSpawnSpace::Spawn(EntityCreator &&spawner, StochasticsInterface &rng) +template <typename EntityCreator, typename AttachControllers> +bool SequentialSpawnSpace::Spawn(EntityCreator &&spawner, AttachControllers &&attach_controllers, StochasticsInterface &rng) { using namespace units::literals; @@ -149,9 +151,13 @@ bool SequentialSpawnSpace::Spawn(EntityCreator &&spawner, StochasticsInterface & const auto area{std::next(areas.begin(), std::distance(spots.begin(), spot_area))}; const auto &stream{*std::next(area->begin(), std::distance(spot_area->begin(), spot_stream))}; const mantle_api::Pose pose{stream->Convert(mantle_api::ITrafficAreaStream::StreamPose{{interval.max, {}}, {}}).value()}; - mantle_api::IEntity &object{spawner.CreateEntity(GetEntity(blueprint), "Common" + std::to_string(++i))}; + std::string name{"Common" + std::to_string(++i)}; + mantle_api::IEntity &object{spawner.CreateEntity(GetEntity(blueprint), name)}; + object.SetName(name); object.SetPosition(pose.position); object.SetOrientation(pose.orientation); + object.SetVelocity(util::Rotate(spawn_velocity, pose.orientation)); + attach_controllers(object, blueprint->GetObjectController()); const auto occupied_space{Interval{interval.max, interval.max}.OffsetBy(-padding.rear, padding.front)}; UpdateSpot(*spot_stream, spot, occupied_space, spawn_velocity); return true; @@ -180,16 +186,4 @@ bool DistributedSpawnSpace::Spawn(EntityCreator &&spawner, StochasticsInterface // spot->zone->RemoveInterval(*interval->interval, sampler(restricted_velocity.min, restricted_velocity.max)); return true; } - -// std::pair<const SpawnZones &, const SpawnZone &> SpawnSpace::SampleZone(units::dimensionless::scalar_t sample) const -// { -// assert(units::dimensionless::scalar_t{} <= sample && sample < units::dimensionless::scalar_t{1.0}); -// const units::length::meter_t weight{sample * weights.back()}; -// auto index{static_cast<size_t>(std::distance(weights.begin(), std::prev(std::upper_bound(std::next(weights.begin()), weights.end(), weight))))}; -// assert(index > 1); -// const SpawnZones &zones{(*this)[index]}; -// const units::length::meter_t zone_weight{weight - weights[index]}; -// auto zone_index{static_cast<size_t>(std::distance(zones.lengths.begin(), std::prev(std::upper_bound(std::next(zones.lengths.begin()), zones.lengths.end(), zone_weight))))}; -// return {zones, zones[zone_index]}; -// } } // namespace OpenScenarioEngine::v1_3 -- GitLab From a4de93c092d359150378f5b471b0f51af59a2840 Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Thu, 27 Mar 2025 09:15:12 +0100 Subject: [PATCH 23/26] Update MockVehicle to satisfy upadted mantle_api --- .../tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp index 594ce5c7..32713524 100644 --- a/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp +++ b/engine/tests/Storyboard/GenericAction/TrafficAreaActionTest.cpp @@ -73,6 +73,9 @@ struct MockVehicle : mantle_api::IVehicle void SetIndicatorState(mantle_api::IndicatorState state) override {} mantle_api::IndicatorState GetIndicatorState() const override { return {}; } + void SetSteeringWheelAngle(units::angle::radian_t) override{}; + units::angle::radian_t GetSteeringWheelAngle() const override { return {}; } + mantle_api::Vec3<units::length::meter_t> xyz; mantle_api::Orientation3<units::angle::radian_t> orientation; }; -- GitLab From 23b6d3459187042ce25fee67beddd75ef6bbf983 Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Thu, 27 Mar 2025 15:36:31 +0100 Subject: [PATCH 24/26] Bump openscenario_api to v1.4.1 --- .../Storyboard/GenericAction/data/Scenarios/RoadNetwork.xodr | 0 utils/ci/conan/conanfile.txt | 2 +- utils/ci/conan/recipe/openscenario_api/all/conandata.yml | 3 +++ utils/ci/conan/recipe/openscenario_api/all/conanfile.py | 5 ----- utils/ci/conan/recipe/openscenario_api/config.yml | 3 +++ 5 files changed, 7 insertions(+), 6 deletions(-) create mode 100644 engine/tests/Storyboard/GenericAction/data/Scenarios/RoadNetwork.xodr diff --git a/engine/tests/Storyboard/GenericAction/data/Scenarios/RoadNetwork.xodr b/engine/tests/Storyboard/GenericAction/data/Scenarios/RoadNetwork.xodr new file mode 100644 index 00000000..e69de29b diff --git a/utils/ci/conan/conanfile.txt b/utils/ci/conan/conanfile.txt index 7e405f78..f1302c20 100644 --- a/utils/ci/conan/conanfile.txt +++ b/utils/ci/conan/conanfile.txt @@ -1,6 +1,6 @@ [requires] mantleapi/v11.0.0@openscenarioengine/testing -openscenario_api/v1.4.0@openscenarioengine/testing +openscenario_api/v1.4.1@openscenarioengine/testing stochastics/0.11.0@openscenarioengine/testing units/2.3.4@openscenarioengine/testing yase/d0c0e58d17358044cc9018c74308b45f6097ecfb@openscenarioengine/testing diff --git a/utils/ci/conan/recipe/openscenario_api/all/conandata.yml b/utils/ci/conan/recipe/openscenario_api/all/conandata.yml index bc5c5031..7a09463a 100644 --- a/utils/ci/conan/recipe/openscenario_api/all/conandata.yml +++ b/utils/ci/conan/recipe/openscenario_api/all/conandata.yml @@ -13,6 +13,9 @@ ################################################################################ sources: + "v1.4.1": + url: https://github.com/RA-Consulting-GmbH/openscenario.api.test.git + sha256: "a72820b00445714071d0e457afcc2112d6015c94" "v1.4.0": url: https://github.com/RA-Consulting-GmbH/openscenario.api.test.git sha256: "5980e8806216351fc2d749c4bfa8669367531688" diff --git a/utils/ci/conan/recipe/openscenario_api/all/conanfile.py b/utils/ci/conan/recipe/openscenario_api/all/conanfile.py index 4e4482b5..258f1a89 100644 --- a/utils/ci/conan/recipe/openscenario_api/all/conanfile.py +++ b/utils/ci/conan/recipe/openscenario_api/all/conanfile.py @@ -55,11 +55,6 @@ class OpenScenarioApiConan(ConanFile): self._repo_source = os.path.join(self.source_folder, self.name) self._artifact_path = os.path.join(self._repo_source, "cpp", "buildArtifact") apply_conandata_patches(self) - replace_in_file(self, - os.path.join(self._repo_source, "cpp/openScenarioLib/src/parser/modelgroup/XmlChoiceParser.cpp"), - "if (currentOccurs < parser->GetMaxOccur())", - "if (currentOccurs < parser->GetMaxOccur() || parser->GetMaxOccur() == -1)") - def build(self): if self.settings.os == "Windows": diff --git a/utils/ci/conan/recipe/openscenario_api/config.yml b/utils/ci/conan/recipe/openscenario_api/config.yml index 2e807a23..1dbb91dd 100644 --- a/utils/ci/conan/recipe/openscenario_api/config.yml +++ b/utils/ci/conan/recipe/openscenario_api/config.yml @@ -21,3 +21,6 @@ versions: "v1.4.0": folder: "all" + + "v1.4.1": + folder: "all" -- GitLab From 8e0f9dc6e76f485036cba284cf9e69b388aa4d13 Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Tue, 1 Apr 2025 17:19:42 +0200 Subject: [PATCH 25/26] Negation workaround for units v.2.3.1 compatibility --- engine/src/Utils/Spawning/Interval.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/engine/src/Utils/Spawning/Interval.h b/engine/src/Utils/Spawning/Interval.h index 762a5f83..ca4494d2 100644 --- a/engine/src/Utils/Spawning/Interval.h +++ b/engine/src/Utils/Spawning/Interval.h @@ -119,7 +119,7 @@ constexpr units::length::meter_t Padding::GetLength() const constexpr Padding Padding::operator-() const { - return {-rear, -front}; + return {units::length::meter_t{-rear()}, units::length::meter_t{-front()}}; } constexpr units::length::meter_t Interval::GetLength() const @@ -134,7 +134,7 @@ constexpr Interval Interval::GetIntersection(const Interval& other) const constexpr Interval Interval::OffsetBy(Padding padding) const { - return OffsetBy(-padding.rear, padding.front); + return OffsetBy(-units::length::meter_t{padding.rear()}, padding.front); } constexpr Interval Interval::OffsetBy(Interval other) const -- GitLab From 6defbf83bde8e0ced3e0b0f8b344175d82554c5b Mon Sep 17 00:00:00 2001 From: Noah Schick <noah.schick@in-tech.com> Date: Wed, 9 Apr 2025 18:04:10 +0200 Subject: [PATCH 26/26] Adapt to TrafficAreaStream change --- .gitignore | 3 +- engine/cmake/generated_files.cmake | 1 + engine/src/Utils/EntityCreator.cpp | 1 + engine/src/Utils/Spawning/Common.h | 20 +++++++++++++- engine/src/Utils/Spawning/Obstacle.cpp | 17 ++++++++---- engine/src/Utils/Spawning/Obstacle.h | 5 ++-- engine/src/Utils/Spawning/ToEntity.cpp | 28 +++++++++++++++++++ engine/src/Utils/Spawning/ToEntity.h | 38 ++++++++++++++++++++++++++ 8 files changed, 103 insertions(+), 10 deletions(-) create mode 100644 engine/src/Utils/Spawning/ToEntity.cpp create mode 100644 engine/src/Utils/Spawning/ToEntity.h diff --git a/.gitignore b/.gitignore index 0ef100c2..a31023a5 100644 --- a/.gitignore +++ b/.gitignore @@ -7,5 +7,6 @@ generator.log *.orig *.bkp .devcontainer +compile_commands.json build -deps \ No newline at end of file +deps diff --git a/engine/cmake/generated_files.cmake b/engine/cmake/generated_files.cmake index 4750e194..fe7d9855 100644 --- a/engine/cmake/generated_files.cmake +++ b/engine/cmake/generated_files.cmake @@ -237,6 +237,7 @@ list(APPEND ${PROJECT_NAME}_SOURCES src/Utils/Spawning/Obstacle.cpp src/Utils/Spawning/SpawnSpace.cpp src/Utils/Spawning/SpawnZone.cpp + src/Utils/Spawning/ToEntity.cpp src/Utils/Spawning/VelocityRange.cpp src/Utils/Spawning/Weight.cpp src/Utils/Spawning/Weighted.cpp diff --git a/engine/src/Utils/EntityCreator.cpp b/engine/src/Utils/EntityCreator.cpp index 0176c046..52bf1447 100644 --- a/engine/src/Utils/EntityCreator.cpp +++ b/engine/src/Utils/EntityCreator.cpp @@ -13,6 +13,7 @@ #include <MantleAPI/Traffic/entity_properties.h> #include <openScenarioLib/generated/v1_3/catalog/CatalogHelperV1_3.h> +#include <units.h> #include "Utils/Constants.h" diff --git a/engine/src/Utils/Spawning/Common.h b/engine/src/Utils/Spawning/Common.h index 4814e145..f424bc8a 100644 --- a/engine/src/Utils/Spawning/Common.h +++ b/engine/src/Utils/Spawning/Common.h @@ -23,7 +23,25 @@ namespace OpenScenarioEngine::v1_3 template <typename T> bool is_uninitialized(const std::weak_ptr<T>& pointer) { - return !pointer.owner_before(std::weak_ptr<T>{}) && !std::weak_ptr<T>{}.owner_before(pointer); + return pointer.expired(); +} + +template <typename T> +bool is_uninitialized(const std::shared_ptr<T>& pointer) +{ + return pointer == nullptr; +} + +template <typename T> +bool is_uninitialized(const std::unique_ptr<T>& pointer) +{ + return pointer == nullptr; +} + +template <typename T> +constexpr bool is_uninitialized(const T* pointer) +{ + return pointer == nullptr; } /// Functor returning the specified value when invoked. diff --git a/engine/src/Utils/Spawning/Obstacle.cpp b/engine/src/Utils/Spawning/Obstacle.cpp index fc2c97bd..84d750da 100644 --- a/engine/src/Utils/Spawning/Obstacle.cpp +++ b/engine/src/Utils/Spawning/Obstacle.cpp @@ -10,10 +10,15 @@ #include "Obstacle.h" +#include "ToEntity.h" + namespace OpenScenarioEngine::v1_3 { -Obstacle::Obstacle(Interval interval, const std::shared_ptr<mantle_api::IEntity>& entity, units::velocity::meters_per_second_t velocity) - : Interval{std::move(interval)}, entity{entity}, velocity{velocity} {} +template <typename Entity> +Obstacle::Obstacle(Interval interval, Entity&& entity, units::velocity::meters_per_second_t velocity) + : Interval{std::move(interval)}, entity{&to_entity(std::forward<Entity>(entity))}, velocity{velocity} +{ +} Obstacle Obstacle::GetStartingSentinel(units::length::meter_t value) { @@ -25,7 +30,7 @@ Obstacle Obstacle::GetEndingSentinel(units::length::meter_t value) return {{value, value}, nullptr, units::velocity::meters_per_second_t{std::numeric_limits<double>::max()}}; } -Obstacle ToObstacle(const std::shared_ptr<mantle_api::IEntity>& entity, const mantle_api::ITrafficAreaStream& stream) +Obstacle ToObstacle(const mantle_api::IEntity* entity, const mantle_api::ITrafficAreaStream& stream) { const auto halfLength{entity->GetProperties()->bounding_box.dimension.length / units::dimensionless::scalar_t{2.0}}; const auto offset{entity->GetProperties()->bounding_box.geometric_center.x}; @@ -42,12 +47,12 @@ std::set<Obstacle, Less<ToMin>> GetObstacles(const mantle_api::ITrafficAreaStrea const units::length::meter_t max{stream.GetLength()}; while (min < max) { - std::weak_ptr<mantle_api::IEntity> pointer{stream.GetEntity(mantle_api::ITrafficAreaStream::SearchDirection::kDownstream, min, max - min)}; - if (is_uninitialized(pointer)) + const mantle_api::IEntity* entity{stream.GetEntity(mantle_api::ITrafficAreaStream::SearchDirection::kDownstream, min, max - min)}; + if (is_uninitialized(entity)) { break; } - min = result.emplace(ToObstacle(pointer.lock(), stream)).first->max; + min = result.emplace(ToObstacle(entity, stream)).first->max; } return result; } diff --git a/engine/src/Utils/Spawning/Obstacle.h b/engine/src/Utils/Spawning/Obstacle.h index 79dae2d1..df661464 100644 --- a/engine/src/Utils/Spawning/Obstacle.h +++ b/engine/src/Utils/Spawning/Obstacle.h @@ -18,13 +18,14 @@ namespace OpenScenarioEngine::v1_3 { struct Obstacle : Interval { - Obstacle(Interval, const std::shared_ptr<mantle_api::IEntity>&, units::velocity::meters_per_second_t); + template <typename Entity> + Obstacle(Interval, Entity&&, units::velocity::meters_per_second_t); static Obstacle GetStartingSentinel(units::length::meter_t); static Obstacle GetEndingSentinel(units::length::meter_t); - std::shared_ptr<mantle_api::IEntity> entity; + const mantle_api::IEntity* entity; units::velocity::meters_per_second_t velocity; }; diff --git a/engine/src/Utils/Spawning/ToEntity.cpp b/engine/src/Utils/Spawning/ToEntity.cpp new file mode 100644 index 00000000..6b4a6349 --- /dev/null +++ b/engine/src/Utils/Spawning/ToEntity.cpp @@ -0,0 +1,28 @@ +/******************************************************************************** + * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ +#include "ToEntity.h" + +namespace OpenScenarioEngine::v1_3 +{ +const mantle_api::IEntity& ToEntity::operator()(const std::unique_ptr<mantle_api::IEntity>& input) const +{ + return (*this)(*input); +} + +const mantle_api::IEntity& ToEntity::operator()(const std::shared_ptr<mantle_api::IEntity>& input) const +{ + return (*this)(*input); +} + +const mantle_api::IEntity& ToEntity::operator()(const std::weak_ptr<mantle_api::IEntity>& input) const +{ + return (*this)(input.lock()); +} +} // namespace OpenScenarioEngine::v1_3 diff --git a/engine/src/Utils/Spawning/ToEntity.h b/engine/src/Utils/Spawning/ToEntity.h new file mode 100644 index 00000000..e6def0ba --- /dev/null +++ b/engine/src/Utils/Spawning/ToEntity.h @@ -0,0 +1,38 @@ +/******************************************************************************** + * Copyright (c) 2025 Bayerische Motoren Werke Aktiengesellschaft (BMW AG) + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * http://www.eclipse.org/legal/epl-2.0. + * + * SPDX-License-Identifier: EPL-2.0 + ********************************************************************************/ +#include <MantleAPI/Traffic/i_entity.h> + +#include <memory> + +namespace OpenScenarioEngine::v1_3 +{ +struct ToEntity +{ + constexpr const mantle_api::IEntity& operator()(const mantle_api::IEntity&) const; + constexpr const mantle_api::IEntity& operator()(const mantle_api::IEntity*) const; + const mantle_api::IEntity& operator()(const std::unique_ptr<mantle_api::IEntity>&) const; + const mantle_api::IEntity& operator()(const std::shared_ptr<mantle_api::IEntity>&) const; + const mantle_api::IEntity& operator()(const std::weak_ptr<mantle_api::IEntity>&) const; +}; +const ToEntity to_entity; // NOLINT(readability-identifier-naming) +} // namespace OpenScenarioEngine::v1_3 + +namespace OpenScenarioEngine::v1_3 +{ +constexpr const mantle_api::IEntity& ToEntity::operator()(const mantle_api::IEntity& input) const +{ + return input; +} + +constexpr const mantle_api::IEntity& ToEntity::operator()(const mantle_api::IEntity* input) const +{ + return *input; +} +} // namespace OpenScenarioEngine::v1_3 -- GitLab