-
Notifications
You must be signed in to change notification settings - Fork 255
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
Use enum values for offered_qos_profiles in code and string names in serialized metadata #1476
Conversation
@MichaelOrlov I see one issue here: |
@roncapat The proper way to handle such a situation with backward compatibility support is to increment rosbag2/rosbag2_storage/include/rosbag2_storage/bag_metadata.hpp Lines 43 to 45 in a80adfd
field in data structure. And will need to keep old tests for the old *8 version and create new tests for a newer version where offered_qos_profiles encoded as string names.
|
@MichaelOrlov where is the piece of code that, based on this metadata version tag, handles different metadata formats? Edit: it seems to me there is no centralized handling for this. Instead, around the repo, there are switches like: if (metadata_.version == 4) {
...
} when needed. Am I right? |
The problem is that: /// Pass metadata version to the sub-structs of BagMetadata for deserializing.
/**
* Encoding should always use the current metadata version, so it does not need this value.
* We cannot extend the YAML::Node class to include this, so we must call it
* as a function with the node as an argument.
*/
template<typename T>
T decode_for_version(const Node & node, int version)
{
static_assert(
std::is_default_constructible<T>::value,
"Type passed to decode_for_version that has is not default constructible.");
if (!node.IsDefined()) {
throw TypedBadConversion<T>(node.Mark());
}
T value{};
if (convert<T>::decode(node, value, version)) {
return value;
}
throw TypedBadConversion<T>(node.Mark());
}
template<>
struct convert<rosbag2_storage::TopicMetadata>
{
static Node encode(const rosbag2_storage::TopicMetadata & topic)
{
Node node;
node["name"] = topic.name;
node["type"] = topic.type;
node["serialization_format"] = topic.serialization_format;
node["offered_qos_profiles"] = topic.offered_qos_profiles;
node["type_description_hash"] = topic.type_description_hash;
return node;
}
static bool decode(const Node & node, rosbag2_storage::TopicMetadata & topic, int version)
{
topic.name = node["name"].as<std::string>();
topic.type = node["type"].as<std::string>();
topic.serialization_format = node["serialization_format"].as<std::string>();
if (version >= 4) {
topic.offered_qos_profiles = node["offered_qos_profiles"].as<std::string>();
} else {
topic.offered_qos_profiles = "";
}
if (version >= 7) {
topic.type_description_hash = node["type_description_hash"].as<std::string>();
} else {
topic.type_description_hash = "";
}
return true;
}
}; keeps the So why don't we change |
@roncapat I see the problem. rosbag2/rosbag2_storage_mcap/src/mcap_storage.cpp Lines 813 to 820 in a80adfd
and rosbag2/rosbag2_storage_sqlite3/src/rosbag2_storage_sqlite3/sqlite_storage.cpp Lines 461 to 473 in a80adfd
However, we can reconsider this approach and serialize it one more time when we need to save it. There shouldn't be a concern regarding performance since we are saving the topic's metadata not so often and usually at the beginning of the recording. Another alternative approach would be handling Metadata version on the upper levels such as Player and Recorder classes where we are currently deserializing and serializing TopicMetadata.offered_qos_profile . That will likely involve API changes as well.
@roncapat If you would address all required changes when changing |
@roncapat There are one caveate. |
I've just pushed some -ugly but may work- code. As I'm working from an old laptop, I need some help from the GH CI, since the build & test jobs are heavy. I did some sub-optimal stuff with templates to manage the version, but I would like then to refactor everythin to use |
@MichaelOrlov I think I avoided the problem via adding one version field to the TopicMetadata. This field is filled when parsing a Care to review? |
@roncapat I would like to review, but I am afraid that I will not have the capacity in the next 3-5 days. |
Don't worry :) I'm also a bit overloaded by work! |
This pull request has been mentioned on ROS Discourse. There might be relevant details there: https://discourse.ros.org/t/ros-2-tsc-meeting-minutes-2023-10-12/34178/1 |
@MichaelOrlov may I ask you if you have any update on this? |
@roncapat Sorry, I was a bit busy with other tasks than it was a ROSCon time... |
@roncapat I briefly reviewed your latest changes with inserting bag metadata version to the The better and more efficient way would be to adhere to my first suggestion when we would change data type for There are no advantages in the currently implemented approach since adding the |
No problem, as soon as I have some more time I'll try to follow the proposed approach. Thanks for the review. |
Ok, I'm trying to reimplement as requested. It seems to me that May we move the |
@roncapat I am thinking about if we would have two data members for the |
I don't understand what we would solve then. If I understood correctly, the desired approach is to deserialize/decode everything in one place. To deserialize, we need parse code currently found in |
@roncapat As regards:
Yes, I agree. It would be better to move it down somewhere to the |
Ahhh undestood now, sorry but I lost the match between problem/solution comments. Here you were trying to solve the re-serialization need for storage plugins, nothing related to QoS parsing code location. |
Just to recap what's going on in the last WIP commits:
|
Hit a problem in // NOTE: it is non-trivial to add a constructor for PlayOptions and RecordOptions
// because the rclcpp::QoS <-> rclpy.qos.QoS Profile conversion cannot be done by builtins.
// It is possible, but the code is much longer and harder to maintain, requiring duplicating
// the names of the members multiple times, as well as the default values from the struct
// definitions. EDIT: I failed to understand the comment. I think to have implemented the binding correctly... |
@MichaelOrlov in order to make some tests work, like During the mentioned test, a temporary file is generated like this one: rosbag2_bagfile_information:
version: 4
storage_identifier: ""
duration:
nanoseconds: 0
starting_time:
nanoseconds_since_epoch: 0
message_count: 0
topics_with_message_count:
- topic_metadata:
name: topic
type: type
serialization_format: rmw
offered_qos_profiles: "- history: keep_last\n depth: 1\n reliability: reliable\n durability: volatile\n deadline:\n sec: 0\n nsec: 0\n lifespan:\n sec: 0\n nsec: 0\n liveliness: system_default\n liveliness_lease_duration:\n sec: 0\n nsec: 0\n avoid_ros_namespace_conventions: false"
type_description_hash: ""
message_count: 100
compression_format: ""
compression_mode: ""
relative_file_paths:
[]
files:
[]
custom_data: ~
ros_distro: "" as you see, it is a V4 metadata, but I'm currently encoding disregarding version, hence, the "keep last" string and alike. |
I'm checking both serialization & deserialization code paths. For serialization, I may be wrong but: std::string serialize_rclcpp_qos_vector(const std::vector<rclcpp::QoS> & in, int version)
{
auto node = YAML::convert<std::vector<rclcpp::QoS>>::encode(in, version);
return YAML::Dump(node);
} isn't already serializing as inline YAML like @emersonknapp did in his commit? Edit: well, maybe it's a bit different since here we don't have And for deserialization, we should not have any particular problem, am I right? |
I'm disabling temp folder destruction in |
Sorry, I went to read #609, and maybe now I catched the request here... Having: offered_qos_profiles:
- history: 3
depth: 0
reliability: 1
durability: 2
deadline:
sec: 2147483647
nsec: 4294967295
lifespan:
sec: 2147483647
nsec: 4294967295
liveliness: 1
liveliness_lease_duration:
sec: 2147483647
nsec: 4294967295
avoid_ros_namespace_conventions: false instead of offered_qos_profiles: "- history: 3\n depth: 0\n reliability: 1\n durability: 2\n deadline:\n sec: 2147483647\n nsec: 4294967295\n lifespan:\n sec: 2147483647\n nsec: 4294967295\n liveliness: 1\n liveliness_lease_duration:\n sec: 2147483647\n nsec: 4294967295\n avoid_ros_namespace_conventions: false" so proper YAML instead of std::string. Please let me know if this is the desired approach or something way simpler and I'm just misunderstanding :) |
If we change: std::string serialize_rclcpp_qos_vector(const std::vector<rclcpp::QoS> & in, int version)
{
auto node = YAML::convert<std::vector<rclcpp::QoS>>::encode(in, version);
return YAML::Dump(node);
}
to std::string serialize_rclcpp_qos_vector(const std::vector<rclcpp::QoS> & in, int version)
{
YAML::Emitter emitter;
auto node = YAML::convert<std::vector<rclcpp::QoS>>::encode(in, version);
emitter << YAML::Literal << node;
return emitter.c_str();
} it will keep newlines in the string. But we have still serialization. Example: offered_qos_profiles: " - history: 3
depth: 0
reliability: 1
durability: 2
deadline:
sec: 2147483647
nsec: 4294967295
lifespan:
sec: 2147483647
nsec: 4294967295
liveliness: 1
liveliness_lease_duration:
sec: 2147483647
nsec: 4294967295
avoid_ros_namespace_conventions: false" |
Signed-off-by: Patrick Roncagliolo <[email protected]>
Waaay better now IMHO. The last commit implements the desired behaviour of #609 and frankly speaking this is very user friendly, I mean, now everything in |
@roncapat Thanks for taking care of the proper serialization formatting and sorry that I didn't explain what those fixes intended to do.
Can we add it too? perhaps we can use it only for |
Of course :) Let me just add it locally and run test on my workstation first. When everything is good I push! |
Test report... this may not be as straightforward as it seems unfortunately. |
@roncapat Did you put it under the I made it as
And all tests from the |
I was just telling you the same, yep, I forgot! :) |
Actually emmiter output is not as we would expect
it is serilzes as |
Strange... adding a doc reference here to start investigating from: |
@roncapat I've tried to change emmiter config to the YAML::Newline
And it seems tests passing without errors. |
I fear it will just add a newline at the start of the string, but let me know if you find out. |
@roncapat I realized that what we are trying to achieve is not doable because we a trying to convert in one string. I double checked in MCAP binary file in current implementation it is already looks like multiple strings with descent formating with new lines if open mcap file in text editor you can see something like this:
I think we don't need to do anything more. It is already good enough. I am going to re-run full CI and merge this PR if it will be no test failures. |
ci_launcher ran: https://ci.ros2.org/job/ci_launcher/12879 |
Fantastic! :) |
@roncapat I changed the title for this PR to the |
No problem at all! Thanks |
This pull request has been mentioned on ROS Discourse. There might be relevant details there: https://discourse.ros.org/t/ros-2-tsc-meeting-minutes-2023-11-16/34757/1 |
Fixes: #1475