Skip to content
Snippets Groups Projects
Commit b2c3511a authored by Reinhard Biegel's avatar Reinhard Biegel
Browse files

Merge branch 'master' into 'master'

Initial

See merge request eclipse/simopenpass/scenario_api!1
parents 9b10369f f589980d
No related branches found
No related tags found
1 merge request!1Initial
/*******************************************************************************
* Copyright (c) 2021, 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 https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
//-----------------------------------------------------------------------------
/** @file i_route.h */
//-----------------------------------------------------------------------------
#ifndef MANTLEAPI_MAP_IROUTE_H
#define MANTLEAPI_MAP_IROUTE_H
#include <MantleAPI/Common/i_identifiable.h>
#include <MantleAPI/Common/vector.h>
namespace mantle_api
{
enum class LaneId
{
kLeft9,
kLeft8,
kLeft7,
kLeft6,
kLeft5,
kLeft4,
kLeft3,
kLeft2,
kLeft1,
kRef,
kRight1,
kRight2,
kRight3,
kRight4,
kRight5,
kRight6,
kRight7,
kRight8,
kRight9
};
class IRoute : public virtual IIdentifiable
{
public:
virtual IRoute& AddWaypoint(const Vec3d& inert_pos) = 0;
virtual IRoute& AddWaypoint(Vec3d&& inert_pos) = 0;
virtual Vec3d GetInertPos(double route_pos, LaneId lane_id, double lane_offset = 0) const = 0;
virtual double GetLaneWidth(double route_pos, LaneId lane_id) const = 0;
virtual LaneId GetLaneId(const Vec3d& inert_pos) const = 0;
virtual double GetDistanceFromStartTo(const Vec3d& inert_pos) const = 0;
virtual double GetLength() const = 0;
};
} // namespace mantle_api
#endif // MANTLEAPI_MAP_IROUTE_H
/*******************************************************************************
* Copyright (c) 2021, 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 https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
//-----------------------------------------------------------------------------
/** @file entity_properties.h */
//-----------------------------------------------------------------------------
#ifndef MANTLEAPI_TRAFFIC_ENTITYPROPERTIES_H
#define MANTLEAPI_TRAFFIC_ENTITYPROPERTIES_H
#include <MantleAPI/Common/dimension.h>
#include <MantleAPI/Common/floating_point_helper.h>
#include <MantleAPI/Common/vector.h>
#include <string>
namespace mantle_api
{
enum class EntityType
{
// Other (unspecified but known) type of moving object.
kOther = 1,
// Object is a vehicle.
kVehicle = 2,
// Object is a pedestrian.
kPedestrian = 3,
// Object is an animal.
kAnimal = 4
};
/// Basic properties that describe scenario entities.
struct EntityProperties
{
virtual ~EntityProperties() = default;
Dimension3d dimension{0.0, 0.0, 0.0};
EntityType type{EntityType::kOther};
std::string model{};
};
inline bool operator==(const EntityProperties& lhs, const EntityProperties& rhs) noexcept
{
return lhs.dimension == rhs.dimension && lhs.type == rhs.type && lhs.model == rhs.model;
}
enum class VehicleClass
{
kOther = 1, // Other (unspecified but known) type of vehicle.
// Vehicle is a small car.
// Definition: Hatchback car with maximum length 4 m.
kSmall_car = 2,
// Vehicle is a compact car.
// Definition: Hatchback car with length between 4 and 4.5 m.
kCompact_car = 3,
// Vehicle is a medium car.
// Definition: Hatchback or sedan with length between 4.5 and 5 m.
kMedium_car = 4,
// Vehicle is a luxury car.
// Definition: Sedan or coupe that is longer then 5 m.
kLuxury_car = 5,
// Vehicle is a delivery van.
// Definition: A delivery van.
kDelivery_van = 6,
// Vehicle is a heavy truck.
kHeavy_truck = 7,
// Vehicle is a truck with semitrailer.
kSemitrailer = 8,
// Vehicle is a trailer (possibly attached to another vehicle).
kTrailer = 9,
// Vehicle is a motorbike or moped.
kMotorbike = 10,
// Vehicle is a bicycle (without motor and specific lights).
kBicycle = 11,
// Vehicle is a bus.
kBus = 12,
// Vehicle is a tram.
kTram = 13,
// Vehicle is a train.
kTrain = 14,
// Vehicle is a wheelchair.
kWheelchair = 15,
// Vehicle type not specified properly.
kInvalid = -1
};
enum class IndicatorState
{
// Indicator state is unknown (must not be used in ground truth).
kUnknown = 0,
// Other (unspecified but known) state of indicator.
kOther = 1,
// Indicators are off.
kOff = 2,
// Left indicator is on.
kLeft = 3,
// Right indicator is on.
kRight = 4,
// Hazard/warning light, i.e. both indicators, are on.
kWarning = 5
};
struct VehicleProperties : public EntityProperties
{
VehicleClass classification{VehicleClass::kOther};
Vec3d bb_center_to_front{};
Vec3d bb_center_to_rear{};
double front_wheel_diameter{0.0};
double rear_wheel_diameter{0.0};
bool is_host{false};
};
inline bool operator==(const VehicleProperties& lhs, const VehicleProperties& rhs) noexcept
{
return lhs.dimension == rhs.dimension && lhs.type == rhs.type && lhs.model == rhs.model &&
lhs.classification == rhs.classification && lhs.bb_center_to_front == rhs.bb_center_to_front &&
lhs.bb_center_to_rear == rhs.bb_center_to_rear &&
IsEqual(lhs.front_wheel_diameter, rhs.front_wheel_diameter) &&
IsEqual(lhs.rear_wheel_diameter, rhs.rear_wheel_diameter) && lhs.is_host == rhs.is_host;
}
struct PedestrianProperties : public EntityProperties
{
};
struct StaticObjectProperties : public EntityProperties
{
};
} // namespace mantle_api
#endif // MANTLEAPI_TRAFFIC_ENTITYPROPERTIES_H
/*******************************************************************************
* Copyright (c) 2021, 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 https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
//-----------------------------------------------------------------------------
/** @file i_controller_config.h */
//-----------------------------------------------------------------------------
#ifndef MANTLEAPI_TRAFFIC_ICONTROLLERCONFIG_H
#define MANTLEAPI_TRAFFIC_ICONTROLLERCONFIG_H
#include <MantleAPI/Common/spline.h>
#include <MantleAPI/Map/i_lane_location_query_service.h>
#include <vector>
namespace mantle_api
{
struct IControllerConfig
{
virtual ~IControllerConfig() = default;
ILaneLocationQueryService* map_query_service{nullptr};
std::uint64_t id{0};
};
struct NoOpControllerConfig : public IControllerConfig
{
};
struct PathControllerConfig : public IControllerConfig
{
std::vector<mantle_api::Vec3d> waypoints{};
};
struct TrajectoryControllerConfig : public IControllerConfig
{
std::vector<mantle_api::SplineSection> heading_splines;
std::vector<mantle_api::SplineSection> velocity_splines;
};
} // namespace mantle_api
#endif // MANTLEAPI_TRAFFIC_ICONTROLLERCONFIG_H
/*******************************************************************************
* Copyright (c) 2021, 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 https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
//-----------------------------------------------------------------------------
/** @file i_entity.h */
//-----------------------------------------------------------------------------
#ifndef MANTLEAPI_TRAFFIC_IENTITY_H
#define MANTLEAPI_TRAFFIC_IENTITY_H
#include <MantleAPI/Common/bounding_box.h>
#include <MantleAPI/Common/i_identifiable.h>
#include <MantleAPI/Common/pose.h>
#include <MantleAPI/Traffic/entity_properties.h>
#include <MantleAPI/Traffic/i_controller_config.h>
#include <memory>
#include <vector>
namespace mantle_api
{
/// Base interface for all static and dynamic scenario entities.
class IEntity : public IIdentifiable
{
public:
virtual void SetPosition(const Vec3d& inert_pos) = 0;
virtual Vec3d GetPosition() const = 0;
virtual void SetOrientation(const Orientation3d& orientation) = 0;
virtual Orientation3d GetOrientation() const = 0;
virtual void SetBoundingBox(const BoundingBox& bounding_box) = 0;
virtual BoundingBox GetBoundingBox() const = 0;
virtual void SetVelocity(const Vec3d& velocity) = 0;
virtual Vec3d GetVelocity() const = 0;
virtual void SetAcceleration(const Vec3d& acceleration) = 0;
virtual Vec3d GetAcceleration() const = 0;
virtual void SetProperties(std::unique_ptr<mantle_api::EntityProperties> properties) = 0;
virtual EntityProperties* GetProperties() const = 0;
// TODO: evaluate if this will be part of the final interface or if this for simulator only (= defined in Vehicle entity)
virtual void SetAssignedLaneIds(const std::vector<std::uint64_t>& assigned_lane_ids) = 0;
virtual std::vector<std::uint64_t> GetAssignedLaneIds() const = 0;
};
class IVehicle : public virtual IEntity
{
public:
virtual VehicleProperties* GetProperties() const = 0;
virtual void SetIndicatorState(IndicatorState state) = 0;
virtual IndicatorState GetIndicatorState() const = 0;
// virtual bool IsHost() const = 0;
// virtual void SetHost() = 0;
};
class IPedestrian : public virtual IEntity
{
public:
virtual PedestrianProperties* GetProperties() const = 0;
};
class IStaticObject : public virtual IEntity
{
public:
virtual StaticObjectProperties* GetProperties() const = 0;
};
} // namespace mantle_api
#endif // MANTLEAPI_TRAFFIC_IENTITY_H
/*******************************************************************************
* Copyright (c) 2021, 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 https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
//-----------------------------------------------------------------------------
/** @file i_entity_repository.h */
//-----------------------------------------------------------------------------
#ifndef MANTLEAPI_TRAFFIC_IENTITYREPOSITORY_H
#define MANTLEAPI_TRAFFIC_IENTITYREPOSITORY_H
#include <MantleAPI/Traffic/entity_properties.h>
#include <MantleAPI/Traffic/i_entity.h>
#include <string>
#include <vector>
namespace mantle_api
{
/// This interface provides CRUD functionality for scenario entities.
class IEntityRepository
{
public:
virtual IVehicle& Create(const std::string& name, const VehicleProperties& properties) = 0;
virtual IVehicle& Create(UniqueId id, const std::string& name, const VehicleProperties& properties) = 0;
virtual IPedestrian& Create(const std::string& name, const PedestrianProperties& properties) = 0;
virtual IPedestrian& Create(UniqueId id, const std::string& name, const PedestrianProperties& properties) = 0;
virtual IStaticObject& Create(const std::string& name, const StaticObjectProperties& properties) = 0;
virtual IStaticObject& Create(UniqueId id, const std::string& name, const StaticObjectProperties& properties) = 0;
virtual IVehicle& GetHost() = 0;
virtual IEntity& Get(const std::string& name) = 0;
virtual IEntity& Get(UniqueId id) = 0;
virtual bool Contains(UniqueId id) const = 0;
virtual void Delete(const std::string& name) = 0;
virtual void Delete(UniqueId id) = 0;
virtual const std::vector<std::unique_ptr<mantle_api::IEntity>>& GetEntities() const = 0;
};
} // namespace mantle_api
#endif // MANTLEAPI_TRAFFIC_IENTITYREPOSITORY_H
/*******************************************************************************
* Copyright (c) 2021, 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 https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
//-----------------------------------------------------------------------------
/** @file test_utils.h */
//-----------------------------------------------------------------------------
#pragma once
#include <MantleAPI/Common/i_identifiable.h>
#include <MantleAPI/Common/position.h>
#include <MantleAPI/EnvironmentalConditions/road_condition.h>
#include <MantleAPI/EnvironmentalConditions/weather.h>
#include <MantleAPI/Execution/i_environment.h>
#include <MantleAPI/Map/i_coord_converter.h>
#include <MantleAPI/Map/i_lane_location_query_service.h>
#include <MantleAPI/Traffic/entity_properties.h>
#include <MantleAPI/Traffic/i_controller_config.h>
#include <MantleAPI/Traffic/i_entity.h>
#include <MantleAPI/Traffic/i_entity_repository.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
namespace mantle_api
{
class MockConverter : public mantle_api::ICoordConverter
{
public:
mantle_api::Vec3d Convert(mantle_api::Position position) const override
{
std::ignore = position;
return mantle_api::Vec3d();
}
mantle_api::Position Convert(mantle_api::Vec3d vec) const override
{
std::ignore = vec;
return mantle_api::Position{};
}
};
class MockVehicle : public mantle_api::IVehicle
{
public:
mantle_api::UniqueId GetUniqueId() const override { return 0; }
void SetName(const std::string& name) override { name_ = name; }
const std::string& GetName() const override { return name_; }
void SetPosition(const mantle_api::Vec3d& inert_pos) override { std::ignore = inert_pos; }
mantle_api::Vec3d GetPosition() const override { return mantle_api::Vec3d(); }
void SetOrientation(const mantle_api::Orientation3d& orientation) override { std::ignore = orientation; }
mantle_api::Orientation3d GetOrientation() const override { return mantle_api::Orientation3d(); }
void SetBoundingBox(const mantle_api::BoundingBox& bounding_box) override { std::ignore = bounding_box; }
mantle_api::BoundingBox GetBoundingBox() const override { return mantle_api::BoundingBox(); }
void SetVelocity(const mantle_api::Vec3d& velocity) override { std::ignore = velocity; }
mantle_api::Vec3d GetVelocity() const override { return {}; }
void SetAcceleration(const mantle_api::Vec3d& acceleration) override { std::ignore = acceleration; }
mantle_api::Vec3d GetAcceleration() const override { return {}; }
void SetAssignedLaneIds(const std::vector<std::uint64_t>& ids) override { std::ignore = ids; }
std::vector<std::uint64_t> GetAssignedLaneIds() const override { return {}; }
void SetProperties(std::unique_ptr<mantle_api::EntityProperties> properties) override { std::ignore = properties; }
mantle_api::VehicleProperties* GetProperties() const override
{
return static_cast<mantle_api::VehicleProperties*>(properties_.get());
}
void SetIndicatorState(mantle_api::IndicatorState state) override { std::ignore = state; }
mantle_api::IndicatorState GetIndicatorState() const override { return mantle_api::IndicatorState::kUnknown; }
private:
std::string name_{};
std::unique_ptr<mantle_api::EntityProperties> properties_{nullptr};
};
class MockQueryService : public mantle_api::ILaneLocationQueryService
{
public:
/// TODO: cleanup once the IQueryService interface is properly defined
const mantle_api::IIdentifiable& GetMapObjectById(mantle_api::UniqueId id)
{
std::ignore = id;
return test_vehicle_;
}
private:
MockVehicle test_vehicle_{};
};
class MockPedestrian : public mantle_api::IPedestrian
{
public:
mantle_api::UniqueId GetUniqueId() const override { return 0; }
void SetName(const std::string& name) override { name_ = name; }
const std::string& GetName() const override { return name_; }
void SetPosition(const mantle_api::Vec3d& inert_pos) override { std::ignore = inert_pos; }
mantle_api::Vec3d GetPosition() const override { return mantle_api::Vec3d(); }
void SetOrientation(const mantle_api::Orientation3d& orientation) override { std::ignore = orientation; }
mantle_api::Orientation3d GetOrientation() const override { return mantle_api::Orientation3d(); }
void SetBoundingBox(const mantle_api::BoundingBox& bounding_box) override { std::ignore = bounding_box; }
mantle_api::BoundingBox GetBoundingBox() const override { return mantle_api::BoundingBox(); }
void SetVelocity(const mantle_api::Vec3d& velocity) override { std::ignore = velocity; }
mantle_api::Vec3d GetVelocity() const override { return {}; }
void SetAcceleration(const mantle_api::Vec3d& acceleration) override { std::ignore = acceleration; }
mantle_api::Vec3d GetAcceleration() const override { return {}; }
void SetAssignedLaneIds(const std::vector<std::uint64_t>& ids) override { std::ignore = ids; }
std::vector<std::uint64_t> GetAssignedLaneIds() const override { return {}; }
void SetProperties(std::unique_ptr<mantle_api::EntityProperties> properties) override { std::ignore = properties; }
mantle_api::PedestrianProperties* GetProperties() const override
{
return static_cast<mantle_api::PedestrianProperties*>(properties_.get());
}
private:
std::string name_{};
std::unique_ptr<mantle_api::EntityProperties> properties_{nullptr};
};
class MockStaticObject : public mantle_api::IStaticObject
{
public:
mantle_api::UniqueId GetUniqueId() const override { return 0; }
void SetName(const std::string& name) override { name_ = name; }
const std::string& GetName() const override { return name_; }
void SetPosition(const mantle_api::Vec3d& inert_pos) override { std::ignore = inert_pos; }
mantle_api::Vec3d GetPosition() const override { return mantle_api::Vec3d(); }
void SetOrientation(const mantle_api::Orientation3d& orientation) override { std::ignore = orientation; }
mantle_api::Orientation3d GetOrientation() const override { return mantle_api::Orientation3d(); }
void SetBoundingBox(const mantle_api::BoundingBox& bounding_box) override { std::ignore = bounding_box; }
mantle_api::BoundingBox GetBoundingBox() const override { return mantle_api::BoundingBox(); }
void SetVelocity(const mantle_api::Vec3d& velocity) override { std::ignore = velocity; }
mantle_api::Vec3d GetVelocity() const override { return {}; }
void SetAcceleration(const mantle_api::Vec3d& acceleration) override { std::ignore = acceleration; }
mantle_api::Vec3d GetAcceleration() const override { return {}; }
void SetAssignedLaneIds(const std::vector<std::uint64_t>& ids) override { std::ignore = ids; }
std::vector<std::uint64_t> GetAssignedLaneIds() const override { return {}; }
void SetProperties(std::unique_ptr<mantle_api::EntityProperties> properties) override { std::ignore = properties; }
mantle_api::StaticObjectProperties* GetProperties() const override
{
return static_cast<mantle_api::StaticObjectProperties*>(properties_.get());
}
private:
std::string name_{};
std::unique_ptr<mantle_api::EntityProperties> properties_{nullptr};
};
class MockEntityRepository : public mantle_api::IEntityRepository
{
public:
MOCK_METHOD(mantle_api::IVehicle&,
Create,
(const std::string& name, const mantle_api::VehicleProperties& properties),
(override));
mantle_api::IVehicle& Create(mantle_api::UniqueId id,
const std::string& name,
const mantle_api::VehicleProperties& properties) override
{
std::ignore = id;
std::ignore = name;
std::ignore = properties;
return test_vehicle_;
}
MOCK_METHOD(mantle_api::IPedestrian&,
Create,
(const std::string& name, const mantle_api::PedestrianProperties& properties),
(override));
mantle_api::IPedestrian& Create(mantle_api::UniqueId id,
const std::string& name,
const mantle_api::PedestrianProperties& properties) override
{
std::ignore = id;
std::ignore = name;
std::ignore = properties;
return test_pedestrian_;
}
MOCK_METHOD(mantle_api::IStaticObject&,
Create,
(const std::string& name, const mantle_api::StaticObjectProperties& properties),
(override));
mantle_api::IStaticObject& Create(mantle_api::UniqueId id,
const std::string& name,
const mantle_api::StaticObjectProperties& properties) override
{
std::ignore = id;
std::ignore = name;
std::ignore = properties;
return test_static_object_;
}
mantle_api::IEntity& Get(const std::string& name) override
{
std::ignore = name;
return test_vehicle_;
}
mantle_api::IEntity& Get(mantle_api::UniqueId id) override
{
std::ignore = id;
return test_vehicle_;
}
mantle_api::IVehicle& GetHost() override { return test_vehicle_; }
const std::vector<std::unique_ptr<mantle_api::IEntity>>& GetEntities() const override { return entities_; }
void Delete(const std::string& name) override { std::ignore = name; }
bool Contains(UniqueId id) const override { return false; }
void Delete(UniqueId id) override { std::ignore = id; }
// const std::vector<mantle_api::IEntity>& GetEntities() const override { return <#initializer #>{}; }
// std::vector<mantle_api::IEntity>& GetEntities() override { return <#initializer #>; }
private:
MockVehicle test_vehicle_{};
MockPedestrian test_pedestrian_{};
MockStaticObject test_static_object_{};
std::vector<std::unique_ptr<mantle_api::IEntity>> entities_{};
};
class MockEnvironment : public mantle_api::IEnvironment
{
public:
virtual void Init() override {}
virtual void Step() override {}
MOCK_METHOD(void,
CreateMap,
(const std::string& file_path, const std::vector<mantle_api::Position>& map_region),
(override)
);
MOCK_METHOD(void, CreateController, (std::unique_ptr<IControllerConfig> config), (override)
);
MOCK_METHOD(void, AddEntityToController, (mantle_api::IEntity & entity, std::uint64_t controller_id), (override)
);
const mantle_api::ILaneLocationQueryService& GetQueryService() const override { return query_service_; }
const mantle_api::ICoordConverter* GetConverter() const override { return &converter_; }
mantle_api::IEntityRepository& GetEntityRepository() override { return entity_repository_; }
const mantle_api::IEntityRepository& GetEntityRepository() const override { return entity_repository_; }
void SetWeather(mantle_api::Weather weather) override { std::ignore = weather; }
void SetRoadCondition(std::vector<mantle_api::FrictionPatch> friction_patches) override
{
std::ignore = friction_patches;
}
void SetDateTime(std::chrono::duration<std::int64_t, std::milli> date_time) override { std::ignore = date_time; }
std::chrono::duration<std::int64_t, std::milli> GetDateTime() override
{
return std::chrono::duration<std::int64_t, std::milli>();
}
private:
MockQueryService query_service_{};
MockEntityRepository entity_repository_{};
MockConverter converter_{};
};
} // namespace mantle_api
/*******************************************************************************
* Copyright (c) 2021, 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 https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
//-----------------------------------------------------------------------------
/** @file interface_test.cpp */
//-----------------------------------------------------------------------------
#include <MantleAPI/Test/test_utils.h>
TEST(InterfaceTest, GivenTeleportAction_When_ThenHostVehicleIsPlaced)
{
mantle_api::Position inert_pos{};
inert_pos = mantle_api::OpenDrivePosition{{0, 0}, 0, 0};
mantle_api::MockEnvironment env{};
env.CreateMap("dummy_map_path", {});
mantle_api::VehicleProperties vehicle_properties;
vehicle_properties.is_host = true;
vehicle_properties.model = "G12";
auto& repo = env.GetEntityRepository();
auto& host_vehicle = repo.Create(0, "host", vehicle_properties);
const auto* const converter = env.GetConverter();
auto world_pos = converter->Convert(inert_pos);
host_vehicle.SetPosition(world_pos);
}
# Scenario API
A collection of interfaces for abstraction between a scenario engine and an environment simulator.
It is intended to be usable with a wide variety of scenario description languages by implementing according scenario engines.
Remark: This is currently work in progress and no stable state is reached yet.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment