osrf / dynamic_message_introspection

Apache License 2.0
33 stars 19 forks source link

ROS message<-->YAML conversion is not symmetric #14

Open christophebedard opened 3 years ago

christophebedard commented 3 years ago

I'm trying to use dynmsg_demo as a library to convert ROS messages to YAML and then convert that YAML back to a ROS message. However, it seems that the conversion is not symmetric: we can't convert a ROS message to YAML using message_to_yaml() and then convert that same YAML (string) back to a ROS message using yaml_to_rosmsg().

Example:

#include <iostream>

#include "dynmsg_demo/message_reading.hpp"
#include "dynmsg_demo/msg_parser.hpp"
#include "dynmsg_demo/typesupport_utils.hpp"
#include "std_msgs/msg/string.h"

#include <yaml-cpp/yaml.h>

int main()
{
  // Start with a ROS message, like a std_msgs/String
  std_msgs__msg__String msg{
    {
      "hello world",
      11,
      12
    },
  };

  // Convert it to a YAML representation
  InterfaceTypeName interface{"std_msgs", "String"};
  RosMessage ros_msg;
  ros_msg.type_info = get_type_info(interface);
  ros_msg.data = reinterpret_cast<uint8_t *>(&msg);
  YAML::Node yaml_msg = message_to_yaml(ros_msg);
  std::cout << "message to YAML:" << std::endl;
  std::cout << yaml_msg << std::endl << std::endl;

  // Convert the YAML representation back to a ROS message
  YAML::Emitter emitter;
  emitter << YAML::DoubleQuoted << YAML::Flow << yaml_msg;
  std::cout << "YAML:" << std::endl;
  std::cout << emitter.c_str() << std::endl << std::endl;
  // What we want
  //    RosMessage ros_msg_from_yaml = yaml_to_rosmsg(interface, emitter.c_str());
  // ..which is
  //    RosMessage ros_msg_from_yaml = yaml_to_rosmsg(interface, "{\"data\": {\"type\": \"string\", \"value\": \"hello world\"}}");
  // What yaml_to_rosmsg() expects
  RosMessage ros_msg_from_yaml = yaml_to_rosmsg(interface, "{\"data\": \"hello world\"}");

  // Show ROS message
  std_msgs__msg__String * msg_from_yaml = reinterpret_cast<std_msgs__msg__String *>(ros_msg_from_yaml.data);
  rosidl_runtime_c__String msg_from_yaml_str = msg_from_yaml->data;
  std::cout << msg_from_yaml_str.data << std::endl;

  return 0;
}

The output is:

message to YAML:
data:
  type: string
  value: hello world

YAML:
{"data": {"type": "string", "value": "hello world"}}

hello world

Using RosMessage ros_msg_from_yaml = yaml_to_rosmsg(interface, emitter.c_str()); results in a parsing exception.

message_to_yaml() converts the message into a YAML representation that includes type information, but yaml_to_rosmsg() converts a different kind of YAML representation (something similar to what ros2 topic echo or rosidl_generator_traits::to_yaml() would output) to a message.

Is this correct, or am I missing something? It's not really what I expected (after reading "It can go both directions" here https://discourse.ros.org/t/ros2-c-based-dynamic-typesupport-example/19079/3), but looking at the tests, this is the correct behaviour.

I modified message_to_yaml() so that the generated YAML matches what yaml_to_rosmsg() expects, but I'm just wondering if there is a reason for this.