-
Notifications
You must be signed in to change notification settings - Fork 260
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
Display bag summary using ros2 bag info
(reduced size)
#45
Display bag summary using ros2 bag info
(reduced size)
#45
Conversation
6a5479e
to
2083676
Compare
9078de5
to
efa75d6
Compare
efa75d6
to
dbc8c58
Compare
dbc8c58
to
09516b6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There is one bug IMO:
When running ros2 bag record -o MY_TEST_BAG
the execution fails correctly because no topics were specified (-a | <topic1>
). However, the folder MY_TEST_BAG
is already created.
That means when running the command a second time correctly with -a
, the execution fails again because the folder already exists. This behavior doesn't happen when no explicit bag name is specified.
Then further, I believe it would be nice to put that exception in a nicer format:
➭ ros2 bag record -a -s my_plugin
DISCLAIMER
ros2 bag is currently under development and not ready to use yet
[ERROR] [rosbag2_storage]: Could not load/open plugin with storage id 'my_plugin'.
libc++abi.dylib: terminating with uncaught exception of type std::runtime_error: No storage could be initialized. Abort
Abort trap: 6
|
||
struct BagMetadata | ||
{ | ||
int version = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is version 1 meaning here? How is the user supposed to change this version?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This version field was introduced to allow easy transition to new formats if necessary (e.g. we want to add information about service calls, parameters, etc.). Having a version field then simplifies backwards compatibility with old yaml files.
Therefore, it should never be changed by the user.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
where is this field currently used in the code? Can we extract that info somehow from incoming messages? Maybe store the MD5 sum of the messages instead a running version number?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It is not used yet and its sole purpose is to distinguish the yaml format in case of changes. It must be independent of the content so a md5 sum (or something similar) is not useful.
An example use would be the case of a format change (after the first release). For that case rosbag can distinguish the to formats and provide an automatic conversion (or at least read also the old format correctly).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As you can see, the yaml file has a specific format. This is basically specified by the metadata struct. If you add a field to it (e.g. "number of service calls"), you won't be able to parse old yaml files. The version field is introduced to help us with this in the future. We can just ask "what metadata.yaml version is this?" and then choose the parsing algorithm accordingly.
Think of it like the version field of the package.xml
. It's there to allow the schema to change in the future and simplify without any hassle.
It is currently not used in the code, because until the first official release, there are no "old" bagfiles for rosbag2 and there is only one version of yaml files. Since it will always be hidden inside rosbag2_storage
, there is no need to write the logic just yet.
@@ -12,25 +12,30 @@ | |||
// See the License for the specific language governing permissions and | |||
// limitations under the License. | |||
|
|||
#ifndef ROSBAG2_STORAGE__STORAGE_INTERFACES__BASE_INFO_INTERFACE_HPP_ | |||
#define ROSBAG2_STORAGE__STORAGE_INTERFACES__BASE_INFO_INTERFACE_HPP_ | |||
#ifndef ROSBAG2_STORAGE__METADATA_IO_HPP_ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why this change? What's the problem with base_info_interface
? Remember that these interfaces were designed to be composable for many specific interfaces.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
See other comment referring to this change.
namespace rosbag2_storage | ||
{ | ||
|
||
void MetadataIo::write_metadata(const std::string & uri, BagMetadata metadata) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
void MetadataIo::write_metadata(const std::string & uri, BagMetadata metadata) | |
void MetadataIo::write_metadata(const std::string & uri, const BagMetadata & metadata) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point.
rosbag2_storage::MetadataIo metadata_io; | ||
return std::make_unique<rosbag2_storage::BagMetadata>(metadata_io.read_metadata(uri)); | ||
} catch (std::exception & e) { | ||
(void) e; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Shouldn't there at least be some kind of logging?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right, we'll do so.
@@ -28,7 +27,7 @@ namespace storage_interfaces | |||
{ | |||
|
|||
class ROSBAG2_STORAGE_PUBLIC ReadOnlyInterface | |||
: public BaseInfoInterface, public BaseIOInterface, public BaseReadInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand this change, or maybe just don't agree with it.
When I open a bag file read only, I shall still have the opportunity to read the meta data info, right? How would I do this with this current change?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here is our rationale:
- Reading the metadata yaml file should always be possible, even without opening the storage (e.g. when the storage is not known in advance) - and indeed, the storage should not be opened if it is not necessary.
- The logic to read the yaml file is the same for every plugin hence a storage plugin author does not need to implement it
- Therefore, it seems logical to expose this functionality outside of the storage_interface hierarchy.
Note that the information we get from the storage plugin itself is necessarily incomplete - it must be enriched with additional information from the filesystem.
It is of course possible to expose a non-abstract method inside the storage interface which just calls the the outside method. I'm just not sure I see the benefit.
In other words, with the current implementation, you can open your storage read-only (or read-write) and you can (in a separate call) query the information from the metadata file without having a storage handle.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That assumption only holds when bag files are written with rosbag2. When, however, no yaml file is specified for let's say ros1 bags, then how do I get this type of info?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, from an interface point of view. If I get a ReadWriteInterface
, I would like to be able to call get_info()
or get_metadata()
on that instance. I don't see how I would do that right now, because with this change I have to rely on the plugin that this function is available.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I see your first point. That's a part where we do indeed need a function to read the information from the storage directly (for read write, this is possible).
The problem with the folder still persists:
Even tho the plugin couldn't be found, and thus the execution is aborted, the folder gets created. |
* \param uri path to the metadata yaml file. | ||
*/ | ||
ROSBAG2_TRANSPORT_PUBLIC | ||
void print_bag_info(const std::string & uri); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that the transport layer should print anything to console. I know this will most likely go away in future PRs, but at least this function should return a BagMetaData
struct and let the calling side handle the printing.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's impossible: Since the BagMetaData struct is not a C type, there is no convenient way to pass it to the calling side (read: the python layer). So I think this will have to stay for now until moved.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This will be done separately -> #51.
static constexpr const char * const metadata_filename = "metadata.yaml"; | ||
|
||
ROSBAG2_STORAGE_PUBLIC | ||
void write_metadata(const std::string & uri, const BagMetadata & metadata); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
void write_metadata(const std::string & uri, const BagMetadata & metadata); | |
void write_metadata(const std::string & uri, const BagMetadata & metadata); | |
@@ -28,7 +27,7 @@ namespace storage_interfaces | |||
{ | |||
|
|||
class ROSBAG2_STORAGE_PUBLIC ReadOnlyInterface | |||
: public BaseInfoInterface, public BaseIOInterface, public BaseReadInterface | |||
: public BaseIOInterface, public BaseReadInterface | |||
{ | |||
public: | |||
virtual ~ReadOnlyInterface() = default; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
virtual ~ReadOnlyInterface() = default; | |
virtual ~ReadOnlyInterface() = default; | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The same change should be done in read_write_interface.hpp
and base_io_interface.hpp
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I hopefully found all occurences
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The change above is outdated. The interface is back in, so the only line changing in that file is the addition of a newline.
@@ -28,7 +27,7 @@ namespace storage_interfaces | |||
{ | |||
|
|||
class ROSBAG2_STORAGE_PUBLIC ReadOnlyInterface | |||
: public BaseInfoInterface, public BaseIOInterface, public BaseReadInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, from an interface point of view. If I get a ReadWriteInterface
, I would like to be able to call get_info()
or get_metadata()
on that instance. I don't see how I would do that right now, because with this change I have to rely on the plugin that this function is available.
8bb296e
to
873026b
Compare
|
|
||
if args.all: | ||
rosbag2_transport_py.record(uri=uri, storage_id=args.storage, all=True) | ||
elif args.topics and len(args.topics) > 0: | ||
rosbag2_transport_py.record(uri=uri, storage_id=args.storage, topics=args.topics) | ||
else: | ||
self._subparser.print_help() | ||
|
||
if os.path.isdir(uri) and not os.listdir(uri): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It seems to be there is a design flaw here. I believe there should be a function (exposed) which can check whether the specified plugin is correct. That is, before calling straight up record
, I think we should be able to initialize and setup the recorder at first and then call record. Having the recorder setup first allows for error checking.
Then second, the code for removing the folder only works right now because record has to be cancelled by ctrl-c
(because of the spin inside). I believe this should be handled by a signint handler, to cancel the record deliberately.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IMHO the "design flaw" is that the directory creation happens already in the python layer before instead of having the storage taking care of that. The reason for this is that cross platform file handling is hard to do in C++ but much easier in Python.
Creating a separate initialize
method would only solidify this workaround and would still leave some scenarios uncovered that cannot be detected beforehand.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Regarding the second point I agree that we will need to do our own signal handling for that and then we also need to change this "failure detection". I would prefer to do this with another pull request.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As discussed offline this relies on the current capabilities of the rosbag2
interface and needs to be adopted when the #50 is implemented.
has to be rebased. |
- On Windows, the process is killed hard and thus does not write its metadata file - Since this is an issue with the test setup that seems very hard to fix, for now we just write the metadata file on our own
- For example is neither --all nor topics are specified or if a non exsisting storage plugin is specified
- For example if the storage plugin specified by the user at record does not exist
Currently only supported on rosbag2 side: - Allow passing a storage identifier to rosbag2::Info() - If a yaml file exists, read info from yaml - If no yaml file exists and a storage identifier was passed open storage and read info directly
…ing with ReadWrite io_flag - This avoids the logging of a 'failed to read metadata' error when recording a new bag
-In this way it is not confused with the storage id (e.g. sqlite3)
9d18dd0
to
9356463
Compare
|
||
std::cout << info_stream.str() << std::endl; | ||
try { | ||
auto metadata = info_->read_metadata(uri); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
wouldn't the exception come out here? I believe it makes more sense to catch the exception right after this method.
This PR adds a new ros2 bag CLI verb
info
which prints a summary of a bag content.The printout looks like this:
These infos are stored in a metadata file at the end of the record process to allow getting infos about a bag without inspecting its content.
This is necessary since the user might not have access to the storage backend used to record the bagfile.
As a result,
rviz_yaml_cpp_vendor
is required as a dependency.This PR is based on the changes introduced in #43 and contains these changes.
Therefore, this PR should be merged after #43.
CI: