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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
## Latest Changes
* Upgraded the bundled eProsima Fast-DDS used by the native ROS 2 integration from v2.11.2 to the v2.14.6 LTS line, moving serialization onto Fast-CDR 2.x while keeping the classic XCDRv1 wire format so published topics stay compatible with every ROS 2 distribution.
Comment thread
JArmandoAnaya marked this conversation as resolved.
* Added Zenoh as a third ROS 2 middleware backend, selectable at runtime via `--rmw=zenoh` alongside `--rmw=fastdds` and `--rmw=cyclonedds`.
* Renamed the ROS2 abstraction layer from dds/DDS* to generic middleware/Middleware* naming to support future non-DDS backends like Zenoh. FastDDS and CycloneDDS vendor classes are unchanged.
* Added NumPy 2 compatibility to the PythonAPI: replaced removed aliases (`np.bool`, `np.matrix`) in example scripts and upgraded Boost to 1.90.0, which ships the upstream NumPy 2 C ABI fix (boostorg/python#432) so the C extension builds against both NumPy 1.x (>=1.18.4) and NumPy 2.x
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class GenericCdrPubSubType : public eprosima::fastdds::dds::TopicDataType {
eprosima::fastcdr::Cdr ser(
fb,
eprosima::fastcdr::Cdr::LITTLE_ENDIANNESS,
eprosima::fastcdr::Cdr::DDS_CDR);
eprosima::fastcdr::CdrVersion::XCDRv1);
payload->encapsulation = CDR_LE;

try {
Expand All @@ -77,7 +77,7 @@ class GenericCdrPubSubType : public eprosima::fastdds::dds::TopicDataType {
return false;
}

const uint32_t len = static_cast<uint32_t>(ser.getSerializedDataLength());
const uint32_t len = static_cast<uint32_t>(ser.get_serialized_data_length());
if (len > payload->max_size) {
return false;
}
Expand All @@ -86,6 +86,19 @@ class GenericCdrPubSubType : public eprosima::fastdds::dds::TopicDataType {
return true;
}

/// FastDDS 2.13+ data-representation-aware serialize overload. CARLA always
/// emits classic CDR (XCDRv1, little-endian) regardless of the requested
/// representation, so this simply delegates to the 2-argument override. It
/// guarantees the writer path reaches our CDR serializer whichever overload
/// FastDDS calls internally.
bool serialize(
void* data,
SerializedPayload_t* payload,
eprosima::fastdds::dds::DataRepresentationId_t /*data_representation*/)
override {
return serialize(data, payload);
}
Comment thread
JArmandoAnaya marked this conversation as resolved.

/// Deserialize a FastDDS payload buffer into a MsgType instance.
/// Called by FastDDS DataReader after receiving data from the wire.
bool deserialize(
Expand All @@ -103,7 +116,7 @@ class GenericCdrPubSubType : public eprosima::fastdds::dds::TopicDataType {
eprosima::fastcdr::Cdr deser(
fastbuffer,
eprosima::fastcdr::Cdr::LITTLE_ENDIANNESS,
eprosima::fastcdr::Cdr::DDS_CDR);
eprosima::fastcdr::CdrVersion::XCDRv1);

try {
deser.read_encapsulation();
Expand All @@ -128,6 +141,16 @@ class GenericCdrPubSubType : public eprosima::fastdds::dds::TopicDataType {
};
}

/// FastDDS 2.13+ data-representation-aware size-provider overload. CARLA
/// serializes the same classic-CDR bytes regardless of the requested
/// representation, so this delegates to the 1-argument override.
std::function<uint32_t()> getSerializedSizeProvider(
void* data,
eprosima::fastdds::dds::DataRepresentationId_t /*data_representation*/)
override {
return getSerializedSizeProvider(data);
}
Comment thread
JArmandoAnaya marked this conversation as resolved.

/// Allocate a new default-initialized MsgType on the heap.
void* createData() override {
return static_cast<void*>(new MsgType());
Expand Down
10 changes: 5 additions & 5 deletions LibCarla/source/carla/ros2/types/CdrSerialization.h
Original file line number Diff line number Diff line change
Expand Up @@ -655,15 +655,15 @@ std::vector<uint8_t> serialize_to_cdr(const T& msg) {
eprosima::fastcdr::Cdr cdr{
fb,
eprosima::fastcdr::Cdr::LITTLE_ENDIANNESS,
eprosima::fastcdr::Cdr::DDS_CDR};
eprosima::fastcdr::CdrVersion::XCDRv1};
try {
cdr.serialize_encapsulation();
serialize_cdr(cdr, msg);
} catch (const eprosima::fastcdr::exception::Exception&) {
return std::vector<uint8_t>{};
}
const char* buf{fb.getBuffer()};
const size_t len{cdr.getSerializedDataLength()};
const size_t len{cdr.get_serialized_data_length()};
return std::vector<uint8_t>{
reinterpret_cast<const uint8_t*>(buf),
reinterpret_cast<const uint8_t*>(buf) + len};
Expand All @@ -680,10 +680,10 @@ uint32_t cdr_serialized_size(const T& msg) {
eprosima::fastcdr::Cdr cdr{
fb,
eprosima::fastcdr::Cdr::LITTLE_ENDIANNESS,
eprosima::fastcdr::Cdr::DDS_CDR};
eprosima::fastcdr::CdrVersion::XCDRv1};
cdr.serialize_encapsulation();
serialize_cdr(cdr, msg);
return static_cast<uint32_t>(cdr.getSerializedDataLength());
return static_cast<uint32_t>(cdr.get_serialized_data_length());
}

/// Deserialize a msg::X from a CDR byte buffer that was produced by
Expand All @@ -701,7 +701,7 @@ bool deserialize_from_cdr(
eprosima::fastcdr::Cdr cdr{
fb,
eprosima::fastcdr::Cdr::LITTLE_ENDIANNESS,
eprosima::fastcdr::Cdr::DDS_CDR};
eprosima::fastcdr::CdrVersion::XCDRv1};
try {
cdr.read_encapsulation();
deserialize_cdr(cdr, msg);
Expand Down
32 changes: 32 additions & 0 deletions LibCarla/source/test/server/test_ros2_middleware.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -505,6 +505,38 @@ TEST(cdr_serialization, time_round_trip) {
EXPECT_EQ(recovered.nanosec, 123456789u);
}

// Byte-exact wire-format guard. The symmetric round-trip tests above cannot
// detect an encoding drift (e.g. Fast-CDR defaulting to XCDRv2, which inserts
// DHEADERs) because deserialize_from_cdr would consume whatever encoding
// serialize_to_cdr emitted. This test pins the literal bytes so a regression
// to XCDRv2 or big-endian is caught deterministically. These exact bytes are
// what every backend puts on the wire: FastDDS via write_serialized_payload,
// CycloneDDS via dds_writecdr (raw passthrough of serialize_to_cdr), and Zenoh.
// Layout (classic CDR, encoding version 1, little-endian):
// 00 01 00 00 encapsulation header (PLAIN_CDR little-endian + 2 option bytes)
// 2A 00 00 00 sec = int32 42 (0x0000002A, LE)
// 15 CD 5B 07 nanosec= uint32 123456789 (0x075BCD15, LE)
TEST(cdr_serialization, time_golden_xcdrv1_bytes) {
carla::ros2::msg::Time original{};
original.sec = 42;
original.nanosec = 123456789u;

const auto buf = carla::ros2::serialize_to_cdr(original);

const std::vector<uint8_t> expected{
0x00u, 0x01u, 0x00u, 0x00u,
0x2Au, 0x00u, 0x00u, 0x00u,
0x15u, 0xCDu, 0x5Bu, 0x07u};
// Assert the size first: a failed or short serialization then yields a
// targeted size mismatch instead of a noisy full-vector diff.
ASSERT_EQ(buf.size(), expected.size());
EXPECT_EQ(buf, expected);
// Discriminator byte: classic CDR_LE is 0x01; any XCDRv2 representation
// (PLAIN_CDR2 / DELIMIT_CDR2 / PL_CDR2) would change byte 1.
EXPECT_EQ(buf[0], 0x00u);
EXPECT_EQ(buf[1], 0x01u);
}

TEST(cdr_serialization, header_round_trip) {
carla::ros2::msg::Header original{};
original.stamp.sec = 10;
Expand Down
2 changes: 1 addition & 1 deletion Util/BuildTools/Setup.sh
Original file line number Diff line number Diff line change
Expand Up @@ -972,7 +972,7 @@ if ${USE_ROS2} ; then
FAST_DDS_LIB_BASENAME=fast-dds-lib
FAST_DDS_LIB_SOURCE_DIR=${PWD}/${FAST_DDS_LIB_BASENAME}-source
FAST_DDS_LIB_REPO="https://github.com/eProsima/Fast-DDS.git"
FAST_DDS_LIB_BRANCH=v2.11.2
FAST_DDS_LIB_BRANCH=v2.14.6

git clone --recurse-submodules --depth 1 --branch ${FAST_DDS_LIB_BRANCH} ${FAST_DDS_LIB_REPO} ${FAST_DDS_LIB_SOURCE_DIR}

Expand Down
Loading