Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
30 changes: 30 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,36 @@ 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};
EXPECT_EQ(buf, expected);
// Discriminator byte: classic CDR_LE is 0x01; any XCDRv2 representation
// (PLAIN_CDR2 / DELIMIT_CDR2 / PL_CDR2) would change byte 1.
ASSERT_GE(buf.size(), 2u);
Comment thread
JArmandoAnaya marked this conversation as resolved.
Outdated
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