Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deserialize bytes into ros message? #158

Closed
lucasw opened this issue Jun 23, 2024 · 6 comments
Closed

Deserialize bytes into ros message? #158

lucasw opened this issue Jun 23, 2024 · 6 comments

Comments

@lucasw
Copy link
Contributor

lucasw commented Jun 23, 2024

Given I have raw bytes of a ros message (which happen to come from mcap) and that I have a matching ros message struct from the macro, what do I do to decode?

https://github.com/lucasw/ros_one2z/blob/38ce91d5976d3f362f5ec9f7e92b3237290bfff0/mcap_to_rerun/src/main.rs#L44

I was thinking roslibrust must have an example within it in subscriber code but I haven't seen it so far- also I'm not that familiar with rust,maybe this is a basic serde questionm.

@Carter12s
Copy link
Collaborator

Yup you got it.

We don't have too much of that in our Repo because we rely on serde_rosmsg for it.

The types that our codegen generates are directly compatible with serde_rosmsg, and ::from_slice() should be what you want.

However, the fact that you called out MCAP has me a bit worried. serde_rosmsg is for ROS1 native messages and very likely will not work with a binary encoded ROS2 message.

If you are working with native ROS2 data you probably want to checkout https://github.com/ros2-rust/ros2_rust

Sorry the ecosystem is so piecemeal at the moment.

@lucasw
Copy link
Contributor Author

lucasw commented Jun 23, 2024

No I'm using ros1msg in mcap which works fine after doing a mcap convert my_ros1.bag my_ros1.mcap (I was looking at https://docs.rs/rosbag/latest/rosbag/ for using bags directly, maybe that could still work but the output looks too raw).

There's an annoying 4 byte length header that appears in some places and not others - serde_rosmsg wants the header, but mcap doesn't deliver an array that contains it. (It would be nice to chase down some of these and add an option not to require it if it already knows the length from elsewhere)

This code is working:
lucasw/ros_one2z#10 (comment)

@Carter12s
Copy link
Collaborator

Dope! I hadn't seen ros1 w/ mcap yet. Cool that exists.

I've definitely tripped over the various 4-byte length fields before. Note that deserializing to Vec needs the 4 byte header to be there, but [T; #] needs the header to not be there. Note that v0.9.0 of this crate fixed a bug with our message generation where we we're incorrectly generating Vec for fixed length array types which was causing them to fail deserialization with serde_rosmsg.

I "think" the answer to your "it would be nice to chase down some of these and not require the 4-byte length field if already knows the length" comes down to using [T; #] vs Vec, but not entirely sure that addresses what you need.

@Carter12s
Copy link
Collaborator

Ahh never mind. Just read your linked thread more.

You are talking about the 4-byte overall message length field documented here:
https://wiki.ros.org/ROS/Connection%20Header

Which is technically part of TCPROS/UDPROS specification, but I can see why mcap would omit it.

Let me jump over to serde_rosmsg and see if I can make and PR to add the feature you are talking about. I agree it is reasonable.

@Carter12s
Copy link
Collaborator

Okay I reviewed the serde_rosmsg code, and their implementation of a Deserializer is fully dependent on being able to read the correct expected message length up front, and relies on this message length to track deserialization progress, and to know when to stop.

I think it is probably a significant re-write to implement skipping the message length field in a "good" way.

There is an easy "work around" that looks like:

pub fn from_slice_no_length<'de, T>(bytes: &[u8]) -> Result<T>
    where T: de::Deserialize<'de>
{
    let mut data_copy = Vec::new();
    data_copy.reserve(bytes.len() + 4);
    data_copy.extend_from_slice(&(bytes.len() as u32).to_le_bytes());
    data_copy.extend_from_slice(&bytes);
    from_slice(&data_copy)
}

I'm not sure folks will want that to be the canonical solution, but I think you could use this as a pretty reasonable workaround if you're willing to deal with a silly copy.

@lucasw lucasw closed this as completed Jun 28, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants