Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 13 additions & 1 deletion LibCarla/source/carla/ros2/dds/DDSMiddleware.h
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,17 @@ namespace ros2 {
/// Passed to ROS2::Enable() to select the middleware at startup.
/// Once set, the middleware cannot be changed without restarting.
enum class DDSMiddleware {
FastDDS
FastDDS,
CycloneDDS
};

/// Convert a DDSMiddleware enum value to a readable string.
inline const char* DDSMiddlewareToString(DDSMiddleware middleware) {
switch (middleware) {
case DDSMiddleware::FastDDS:
return "FastDDS";
case DDSMiddleware::CycloneDDS:
return "CycloneDDS";
}
return "Unknown";
}
Expand All @@ -37,6 +40,9 @@ inline DDSMiddlewareParseResult DDSMiddlewareFromString(const std::string& name)
if (name == "fastdds") {
return {true, DDSMiddleware::FastDDS};
}
if (name == "cyclonedds") {
return {true, DDSMiddleware::CycloneDDS};
}
return {false, DDSMiddleware::FastDDS};
}

Expand All @@ -45,6 +51,12 @@ inline std::string GetAvailableMiddlewareString() {
std::string result;
#if defined(CARLA_ROS2_DDS_FASTDDS)
result += "FastDDS";
#endif
#if defined(CARLA_ROS2_DDS_CYCLONEDDS)
if (!result.empty()) {
result += ", ";
}
result += "CycloneDDS";
#endif
if (result.empty()) {
result = "none";
Expand Down
27 changes: 27 additions & 0 deletions LibCarla/source/carla/ros2/dds/DDSMiddlewareFactory.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@
# include "carla/ros2/dds/fastdds/FastDDSSubscriberMiddleware.h"
#endif

#if defined(CARLA_ROS2_DDS_CYCLONEDDS) && !defined(CARLA_ROS2_DDS_TESTING)
# include "carla/ros2/dds/cyclonedds/CycloneDDSPublisherMiddleware.h"
# include "carla/ros2/dds/cyclonedds/CycloneDDSSubscriberMiddleware.h"
#endif

namespace carla {
namespace ros2 {

Expand Down Expand Up @@ -44,6 +49,12 @@ class DDSMiddlewareFactory {
return true;
#else
return false;
#endif
case DDSMiddleware::CycloneDDS:
#if defined(CARLA_ROS2_DDS_CYCLONEDDS)
return true;
#else
return false;
#endif
}
return false;
Expand Down Expand Up @@ -83,6 +94,14 @@ class DDSMiddlewareFactory {
#else
log_error("DDSMiddlewareFactory: FastDDS not compiled in");
return nullptr;
#endif
case DDSMiddleware::CycloneDDS:
#if defined(CARLA_ROS2_DDS_CYCLONEDDS) && !defined(CARLA_ROS2_DDS_TESTING)
return std::unique_ptr<IDDSPublisherMiddleware>(
new CycloneDDSPublisherMiddleware<T>());
#else
log_error("DDSMiddlewareFactory: CycloneDDS not compiled in");
return nullptr;
#endif
}
return nullptr;
Expand All @@ -101,6 +120,14 @@ class DDSMiddlewareFactory {
#else
log_error("DDSMiddlewareFactory: FastDDS not compiled in");
return nullptr;
#endif
case DDSMiddleware::CycloneDDS:
#if defined(CARLA_ROS2_DDS_CYCLONEDDS) && !defined(CARLA_ROS2_DDS_TESTING)
return std::unique_ptr<IDDSSubscriberMiddleware>(
new CycloneDDSSubscriberMiddleware<S>());
#else
log_error("DDSMiddlewareFactory: CycloneDDS not compiled in");
return nullptr;
#endif
}
return nullptr;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// Copyright (c) 2025 Computer Vision Center (CVC) at the Universitat Autonoma de Barcelona (UAB).
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.

#pragma once

#include "carla/ros2/dds/IDDSPublisherMiddleware.h"
#include "carla/Logging.h"

namespace carla {
namespace ros2 {

/// CycloneDDS implementation of IDDSPublisherMiddleware (stub).
///
/// This is a placeholder that compiles and satisfies the factory interface
/// but does not contain a real CycloneDDS implementation. All operations
/// log an error and return failure. The real implementation will replace
/// this file once CycloneDDS types and the C API are available.
///
/// Parameterized on a traits type T that provides:
/// T::msg_type — middleware-neutral POD message struct
template<typename T>
class CycloneDDSPublisherMiddleware : public IDDSPublisherMiddleware {
public:
bool Init(const std::string& topic_name) override {
log_error("CycloneDDSPublisherMiddleware: stub — not yet implemented "
"(topic '", topic_name, "')");
return false;
}

bool Publish(void* /*message_data*/) override {
return false;
}

bool IsAlive() const override {
return false;
}

std::string GetTopicName() const override {
return {};
}
};

} // namespace ros2
} // namespace carla
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
// Copyright (c) 2025 Computer Vision Center (CVC) at the Universitat Autonoma de Barcelona (UAB).
// This work is licensed under the terms of the MIT license.
// For a copy, see <https://opensource.org/licenses/MIT>.

#pragma once

#include "carla/ros2/dds/IDDSSubscriberMiddleware.h"
#include "carla/Logging.h"

namespace carla {
namespace ros2 {

/// CycloneDDS implementation of IDDSSubscriberMiddleware (stub).
///
/// This is a placeholder that compiles and satisfies the factory interface
/// but does not contain a real CycloneDDS implementation. All operations
/// log an error and return failure. The real implementation will replace
/// this file once CycloneDDS types and the C API are available.
///
/// Parameterized on a traits type S that provides:
/// S::msg_type — middleware-neutral POD message struct
template<typename S>
class CycloneDDSSubscriberMiddleware : public IDDSSubscriberMiddleware {
public:
bool Init(
const std::string& topic_name,
void* /*message_ptr*/,
bool* /*new_message_flag*/) override {
log_error("CycloneDDSSubscriberMiddleware: stub — not yet implemented "
"(topic '", topic_name, "')");
return false;
}

bool IsAlive() const override {
return false;
}

std::string GetTopicName() const override {
return {};
}
};

} // namespace ros2
} // namespace carla
70 changes: 60 additions & 10 deletions LibCarla/source/test/server/test_dds_middleware.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,19 +107,26 @@ class DDSMiddlewareFactoryFixture : public ::testing::Test {
// ==========================================================================

TEST(dds_middleware_enum, values_exist) {
DDSMiddleware mw = DDSMiddleware::FastDDS;
EXPECT_EQ(static_cast<int>(mw), 0);
DDSMiddleware mw_fast = DDSMiddleware::FastDDS;
EXPECT_EQ(static_cast<int>(mw_fast), 0);
DDSMiddleware mw_cyclone = DDSMiddleware::CycloneDDS;
EXPECT_EQ(static_cast<int>(mw_cyclone), 1);
}

TEST(dds_middleware_enum, switch_covers_all) {
DDSMiddleware mw = DDSMiddleware::FastDDS;
bool covered = false;
switch (mw) {
case DDSMiddleware::FastDDS:
covered = true;
break;
DDSMiddleware values[] = {DDSMiddleware::FastDDS, DDSMiddleware::CycloneDDS};
for (auto mw : values) {
bool covered = false;
switch (mw) {
case DDSMiddleware::FastDDS:
covered = true;
break;
case DDSMiddleware::CycloneDDS:
covered = true;
break;
}
EXPECT_TRUE(covered);
}
EXPECT_TRUE(covered);
}

// ==========================================================================
Expand All @@ -140,6 +147,10 @@ TEST(dds_middleware_to_string, result_is_not_empty) {
EXPECT_STRNE(result, "");
}

TEST(dds_middleware_to_string, cyclonedds_returns_correct_string) {
EXPECT_STREQ(DDSMiddlewareToString(DDSMiddleware::CycloneDDS), "CycloneDDS");
}

// ==========================================================================
// Group 3: dds_middleware_from_string (5 tests)
// ==========================================================================
Expand All @@ -151,10 +162,16 @@ TEST(dds_middleware_from_string, fastdds_lowercase_valid) {
}

TEST(dds_middleware_from_string, unknown_string_invalid) {
auto result = DDSMiddlewareFromString("cyclonedds");
auto result = DDSMiddlewareFromString("unknowndds");
EXPECT_FALSE(result.valid);
}

TEST(dds_middleware_from_string, cyclonedds_lowercase_valid) {
auto result = DDSMiddlewareFromString("cyclonedds");
EXPECT_TRUE(result.valid);
EXPECT_EQ(result.middleware, DDSMiddleware::CycloneDDS);
}

TEST(dds_middleware_from_string, empty_string_invalid) {
auto result = DDSMiddlewareFromString("");
EXPECT_FALSE(result.valid);
Expand Down Expand Up @@ -184,6 +201,11 @@ TEST(dds_middleware_available, available_string_contains_fastdds) {
EXPECT_NE(available.find("FastDDS"), std::string::npos);
}

TEST(dds_middleware_available, cyclonedds_not_available_without_macro) {
EXPECT_FALSE(
DDSMiddlewareFactory::IsMiddlewareAvailable(DDSMiddleware::CycloneDDS));
}

// ==========================================================================
// Group 5: dds_middleware_type_name (4 tests)
// ==========================================================================
Expand Down Expand Up @@ -236,6 +258,34 @@ TEST_F(DDSMiddlewareFactoryFixture, factory_available_string) {
EXPECT_NE(available.find("FastDDS"), std::string::npos);
}

TEST_F(DDSMiddlewareFactoryFixture, set_and_get_cyclonedds) {
DDSMiddlewareFactory::SetMiddleware(DDSMiddleware::CycloneDDS);
EXPECT_EQ(DDSMiddlewareFactory::GetMiddleware(), DDSMiddleware::CycloneDDS);
}

TEST_F(DDSMiddlewareFactoryFixture, resolve_unavailable_cyclonedds) {
auto resolution =
DDSMiddlewareFactory::ResolveMiddleware(DDSMiddleware::CycloneDDS);
EXPECT_FALSE(resolution.success);
EXPECT_EQ(resolution.middleware, DDSMiddleware::CycloneDDS);
}

TEST_F(DDSMiddlewareFactoryFixture, create_publisher_cyclonedds_unavailable) {
DDSMiddlewareFactory::SetMiddleware(DDSMiddleware::CycloneDDS);
::testing::internal::CaptureStderr();
auto pub = DDSMiddlewareFactory::CreatePublisher<TestPubTraits>();
::testing::internal::GetCapturedStderr();
EXPECT_EQ(pub, nullptr);
}

TEST_F(DDSMiddlewareFactoryFixture, create_subscriber_cyclonedds_unavailable) {
DDSMiddlewareFactory::SetMiddleware(DDSMiddleware::CycloneDDS);
::testing::internal::CaptureStderr();
auto sub = DDSMiddlewareFactory::CreateSubscriber<TestSubTraits>();
::testing::internal::GetCapturedStderr();
EXPECT_EQ(sub, nullptr);
}

// ==========================================================================
// Group 7: dds_publisher_interface (5 tests)
// ==========================================================================
Expand Down