diff --git a/src/cmd/ign.cc b/src/cmd/ign.cc index 61acc900e..a894789fa 100644 --- a/src/cmd/ign.cc +++ b/src/cmd/ign.cc @@ -17,6 +17,7 @@ #include #include +#include #include #include #include @@ -251,7 +252,7 @@ extern "C" void cmdServiceReq(const char *_service, ////////////////////////////////////////////////// extern "C" void cmdTopicEcho(const char *_topic, - const double _duration, int _count) + const double _duration, int _count, MsgOutputFormat _outputFormat) { if (!_topic || std::string(_topic).empty()) { @@ -266,7 +267,23 @@ extern "C" void cmdTopicEcho(const char *_topic, std::function cb = [&](const ProtoMsg &_msg) { std::lock_guard lock(mutex); - std::cout << _msg.DebugString() << std::endl; + switch (_outputFormat) + { + case MsgOutputFormat::kDefault: + case MsgOutputFormat::kDebugString: + std::cout << _msg.DebugString() << std::endl; + break; + case MsgOutputFormat::kJSON: + { + std::string jsonStr; + google::protobuf::util::MessageToJsonString(_msg, &jsonStr); + std::cout << jsonStr << std::endl; + } + break; + default: + std::cerr << "Invalid output format selected.\n"; + return; + } ++count; condition.notify_one(); }; diff --git a/src/cmd/ign.hh b/src/cmd/ign.hh index 00817c43d..5a5e1482b 100644 --- a/src/cmd/ign.hh +++ b/src/cmd/ign.hh @@ -63,6 +63,21 @@ extern "C" void cmdServiceReq(const char *_service, const int _timeout, const char *_reqData); +extern "C" { + /// \brief Enum used for specifing the message output format for functions + /// like cmdTopicEcho. + enum class MsgOutputFormat { + // Default. Currently, this is Protobuf's DebugString output format. + kDefault, + + // Output format used in Protobuf's Message::DebugString. + kDebugString, + + // JSON output. + kJSON + }; +} + /// \brief External hook to execute 'ign topic -e' from the command line. /// The _duration parameter overrides the _count parameter. /// \param[in] _topic Topic name. @@ -71,9 +86,9 @@ extern "C" void cmdServiceReq(const char *_service, /// \param[in] _count Number of messages to echo and then stop. A value <= 0 /// indicates no limit. The _duration parameter overrides the _count /// parameter. -extern "C" void cmdTopicEcho(const char *_topic, - const double _duration, - int _count); +/// \param[in] _outputFormat Message output format. +extern "C" void cmdTopicEcho(const char *_topic, const double _duration, + int _count, MsgOutputFormat _outputFormat); /// \brief External hook to read the library version. /// \return C-string representing the version. Ex.: 0.1.2 diff --git a/src/cmd/ign_src_TEST.cc b/src/cmd/ign_src_TEST.cc index 91ea58d56..9f0a46ed3 100644 --- a/src/cmd/ign_src_TEST.cc +++ b/src/cmd/ign_src_TEST.cc @@ -15,6 +15,7 @@ * */ +#include #include #include #include @@ -230,17 +231,56 @@ TEST(ignTest, cmdTopicEcho) transport::Node node; // Requesting a null topic should trigger an error message. - cmdTopicEcho(nullptr, 10.00, 0); + cmdTopicEcho(nullptr, 10.00, 0, MsgOutputFormat::kDefault); EXPECT_EQ(stdErrBuffer.str(), "Invalid topic. Topic must not be empty.\n"); clearIOStreams(stdOutBuffer, stdErrBuffer); - cmdTopicEcho(kInvalidTopic.c_str(), 5.00, 0); + cmdTopicEcho(kInvalidTopic.c_str(), 5.00, 0, MsgOutputFormat::kDefault); EXPECT_EQ(stdErrBuffer.str(), "Topic [/] is not valid.\n"); clearIOStreams(stdOutBuffer, stdErrBuffer); restoreIO(); } +///////////////////////////////////////////////// +TEST(ignTest, cmdTopicEchoOutputFormats) +{ + std::stringstream stdOutBuffer; + std::stringstream stdErrBuffer; + redirectIO(stdOutBuffer, stdErrBuffer); + + transport::Node node; + ignition::msgs::Int32 msg; + msg.set_data(5); + + clearIOStreams(stdOutBuffer, stdErrBuffer); + + auto getSubscriberOutput = [&](MsgOutputFormat _outputFormat) + { + cmdTopicEcho(g_topic.c_str(), 3.00, 1, _outputFormat); + return stdOutBuffer.str(); + }; + + auto defaultOutput = std::async(std::launch::async, getSubscriberOutput, + MsgOutputFormat::kDefault); + + cmdTopicPub(g_topic.c_str(), g_intType.c_str(), msg.DebugString().c_str()); + EXPECT_EQ("data: 5\n\n", defaultOutput.get()); + + clearIOStreams(stdOutBuffer, stdErrBuffer); + + auto jsonOutput = std::async(std::launch::async, getSubscriberOutput, + MsgOutputFormat::kJSON); + + msg.set_data(10); + cmdTopicPub(g_topic.c_str(), g_intType.c_str(), msg.DebugString().c_str()); + EXPECT_EQ("{\"data\":10}\n", jsonOutput.get()); + + clearIOStreams(stdOutBuffer, stdErrBuffer); + + restoreIO(); +} + ///////////////////////////////////////////////// /// Main int main(int argc, char **argv) diff --git a/src/cmd/topic_main.cc b/src/cmd/topic_main.cc index 6e1c7ab8b..03deba1e3 100644 --- a/src/cmd/topic_main.cc +++ b/src/cmd/topic_main.cc @@ -53,6 +53,9 @@ struct TopicOptions /// \brief Number of messages to echo int count{-1}; + + /// \brief Message output format + MsgOutputFormat msgOutputFormat {MsgOutputFormat::kDefault}; }; ////////////////////////////////////////////////// @@ -73,7 +76,8 @@ void runTopicCommand(const TopicOptions &_opt) _opt.msgData.c_str()); break; case TopicCommand::kTopicEcho: - cmdTopicEcho(_opt.topic.c_str(), _opt.duration, _opt.count); + cmdTopicEcho(_opt.topic.c_str(), _opt.duration, _opt.count, + _opt.msgOutputFormat); break; case TopicCommand::kNone: default: @@ -119,6 +123,10 @@ void addTopicFlags(CLI::App &_app) opt->command = TopicCommand::kTopicEcho; }); + command->add_flag_callback("--json-output", + [opt]() { opt->msgOutputFormat = MsgOutputFormat::kJSON; }, + "Output messages in JSON format"); + command->add_option_function("-p,--pub", [opt](const std::string &_msgData){ opt->command = TopicCommand::kTopicPub;