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

Display bag summary using ros2 bag info (reduced size) #45

Merged
merged 14 commits into from
Oct 30, 2018

Conversation

greimela-si
Copy link
Contributor

@greimela-si greimela-si commented Oct 11, 2018

This PR adds a new ros2 bag CLI verb info which prints a summary of a bag content.

The printout looks like this:

$ ros2 bag info 2018_10_04-11_08_21

Files:            2018_10_04-11_08_21.db3
Bag size:         44.4 KiB
Storage id:       sqlite3
Storage format:   cdr
Duration:         21.3s
Start:            Oct  4 2018 11:08:21.483 (1538644101.483)
End               Oct  4 2018 11:08:42.486 (1538644122.486)
Messages:         22
Topics with Type: /string_topic; std_msgs/String; 22 msgs

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:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

@greimela-si greimela-si force-pushed the feature/ros2_bag_info_small branch from 6a5479e to 2083676 Compare October 11, 2018 14:49
@greimela-si greimela-si force-pushed the feature/ros2_bag_info_small branch 2 times, most recently from 9078de5 to efa75d6 Compare October 12, 2018 14:39
@anhosi anhosi force-pushed the feature/ros2_bag_info_small branch from efa75d6 to dbc8c58 Compare October 17, 2018 14:03
@Martin-Idel-SI
Copy link
Contributor

New CI after rebase and fixes:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

@Martin-Idel-SI Martin-Idel-SI force-pushed the feature/ros2_bag_info_small branch from dbc8c58 to 09516b6 Compare October 18, 2018 07:58
@Martin-Idel-SI
Copy link
Contributor

New CI after rebase:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

Copy link
Collaborator

@Karsten1987 Karsten1987 left a 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;
Copy link
Collaborator

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?

Copy link
Contributor

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.

Copy link
Collaborator

@Karsten1987 Karsten1987 Oct 22, 2018

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?

Copy link
Contributor

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).

Copy link
Contributor

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_
Copy link
Collaborator

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.

Copy link
Contributor

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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
void MetadataIo::write_metadata(const std::string & uri, BagMetadata metadata)
void MetadataIo::write_metadata(const std::string & uri, const BagMetadata & metadata)

Copy link
Contributor

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;
Copy link
Collaborator

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?

Copy link
Contributor

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
Copy link
Collaborator

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?

Copy link
Contributor

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.

Copy link
Collaborator

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?

Copy link
Collaborator

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.

Copy link
Contributor

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).

@botteroa-si
Copy link
Contributor

botteroa-si commented Oct 22, 2018

New CI after implementing requested changes:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

@Karsten1987
Copy link
Collaborator

The problem with the folder still persists:

✔  11:45:06  ~/workspace/osrf/rosbag2_ws
 ➭ 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'.
[ERROR] [rosbag2_transport]: Failed to record: No storage could be initialized. Abort

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);
Copy link
Collaborator

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.

Copy link
Contributor

@Martin-Idel-SI Martin-Idel-SI Oct 23, 2018

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.

Copy link
Contributor

@anhosi anhosi Oct 25, 2018

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);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
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;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
virtual ~ReadOnlyInterface() = default;
virtual ~ReadOnlyInterface() = default;

Copy link
Collaborator

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

Copy link
Contributor

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

Copy link
Contributor

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
Copy link
Collaborator

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.

@Martin-Idel-SI Martin-Idel-SI force-pushed the feature/ros2_bag_info_small branch 2 times, most recently from 8bb296e to 873026b Compare October 23, 2018 13:51
@Karsten1987
Copy link
Collaborator

 ➭ ros2 bag record -a
DISCLAIMER
ros2 bag is currently under development and not ready to use yet
[ERROR] [rosbag2_storage]: Failed to load metadata: Exception on parsing info file: bad file


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):
Copy link
Collaborator

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.

Copy link
Contributor

@anhosi anhosi Oct 24, 2018

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.

Copy link
Contributor

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.

Copy link
Contributor

@anhosi anhosi left a 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.

@Karsten1987
Copy link
Collaborator

has to be rebased.

greimela-si and others added 10 commits October 29, 2018 08:32
- 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)
@Martin-Idel-SI Martin-Idel-SI force-pushed the feature/ros2_bag_info_small branch from 9d18dd0 to 9356463 Compare October 29, 2018 07:43
@Martin-Idel-SI
Copy link
Contributor

Done. Add new CI:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status


std::cout << info_stream.str() << std::endl;
try {
auto metadata = info_->read_metadata(uri);
Copy link
Collaborator

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.

@Martin-Idel-SI
Copy link
Contributor

Done. New CI:

  • Linux Build Status
  • Linux-aarch64 Build Status
  • macOS Build Status
  • Windows Build Status

@Karsten1987 Karsten1987 merged commit f37ffa3 into ros2:master Oct 30, 2018
@Martin-Idel-SI Martin-Idel-SI deleted the feature/ros2_bag_info_small branch November 7, 2018 15:10
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

Successfully merging this pull request may close these issues.

6 participants