From 6e9b972908f8a1e5a03923aa3db531b661fa5f9a Mon Sep 17 00:00:00 2001 From: "Addisu Z. Taddese" Date: Mon, 31 Jan 2022 11:02:29 -0600 Subject: [PATCH 1/3] Add option to output messages in JSON format Signed-off-by: Addisu Z. Taddese --- src/cmd/ign.cc | 14 ++++++++++++-- src/cmd/ign.hh | 7 ++++--- src/cmd/ign_src_TEST.cc | 4 ++-- src/cmd/topic_main.cc | 9 ++++++++- 4 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/cmd/ign.cc b/src/cmd/ign.cc index 61acc900e..c577f9a92 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, bool jsonOutput) { if (!_topic || std::string(_topic).empty()) { @@ -266,7 +267,16 @@ extern "C" void cmdTopicEcho(const char *_topic, std::function cb = [&](const ProtoMsg &_msg) { std::lock_guard lock(mutex); - std::cout << _msg.DebugString() << std::endl; + if (jsonOutput) + { + std::string jsonStr; + google::protobuf::util::MessageToJsonString(_msg, &jsonStr); + std::cout << jsonStr << std::endl; + } + else + { + std::cout << _msg.DebugString() << std::endl; + } ++count; condition.notify_one(); }; diff --git a/src/cmd/ign.hh b/src/cmd/ign.hh index 00817c43d..316a1ae04 100644 --- a/src/cmd/ign.hh +++ b/src/cmd/ign.hh @@ -71,9 +71,10 @@ 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] _jsonOutput If true, message output will be in JSON format. +/// Otherwise, Protobuf's default DebugString format will be used. +extern "C" void cmdTopicEcho(const char *_topic, const double _duration, + int _count, bool _jsonOutput); /// \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..f9035c2e8 100644 --- a/src/cmd/ign_src_TEST.cc +++ b/src/cmd/ign_src_TEST.cc @@ -230,11 +230,11 @@ 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, false); 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, false); EXPECT_EQ(stdErrBuffer.str(), "Topic [/] is not valid.\n"); clearIOStreams(stdOutBuffer, stdErrBuffer); diff --git a/src/cmd/topic_main.cc b/src/cmd/topic_main.cc index 6e1c7ab8b..5a2e41283 100644 --- a/src/cmd/topic_main.cc +++ b/src/cmd/topic_main.cc @@ -53,6 +53,8 @@ struct TopicOptions /// \brief Number of messages to echo int count{-1}; + + bool jsonOutput{false}; }; ////////////////////////////////////////////////// @@ -73,7 +75,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.jsonOutput); break; case TopicCommand::kNone: default: @@ -119,6 +122,10 @@ void addTopicFlags(CLI::App &_app) opt->command = TopicCommand::kTopicEcho; }); + command->add_flag_callback("--json-output", + [opt]() { opt->jsonOutput = true; }, + "Output messages in JSON format"); + command->add_option_function("-p,--pub", [opt](const std::string &_msgData){ opt->command = TopicCommand::kTopicPub; From f80254afda5f0c18589fb1e3ba243fb27ffafc9f Mon Sep 17 00:00:00 2001 From: "Addisu Z. Taddese" Date: Mon, 28 Feb 2022 12:14:20 -0600 Subject: [PATCH 2/3] Address reviewer feedback: Change parameter to enum Signed-off-by: Addisu Z. Taddese --- src/cmd/ign.cc | 25 ++++++++++++++++--------- src/cmd/ign.hh | 20 +++++++++++++++++--- src/cmd/ign_src_TEST.cc | 4 ++-- src/cmd/topic_main.cc | 7 ++++--- 4 files changed, 39 insertions(+), 17 deletions(-) diff --git a/src/cmd/ign.cc b/src/cmd/ign.cc index c577f9a92..a894789fa 100644 --- a/src/cmd/ign.cc +++ b/src/cmd/ign.cc @@ -252,7 +252,7 @@ extern "C" void cmdServiceReq(const char *_service, ////////////////////////////////////////////////// extern "C" void cmdTopicEcho(const char *_topic, - const double _duration, int _count, bool jsonOutput) + const double _duration, int _count, MsgOutputFormat _outputFormat) { if (!_topic || std::string(_topic).empty()) { @@ -267,15 +267,22 @@ extern "C" void cmdTopicEcho(const char *_topic, std::function cb = [&](const ProtoMsg &_msg) { std::lock_guard lock(mutex); - if (jsonOutput) + switch (_outputFormat) { - std::string jsonStr; - google::protobuf::util::MessageToJsonString(_msg, &jsonStr); - std::cout << jsonStr << std::endl; - } - else - { - std::cout << _msg.DebugString() << std::endl; + 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 316a1ae04..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,10 +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. -/// \param[in] _jsonOutput If true, message output will be in JSON format. -/// Otherwise, Protobuf's default DebugString format will be used. +/// \param[in] _outputFormat Message output format. extern "C" void cmdTopicEcho(const char *_topic, const double _duration, - int _count, bool _jsonOutput); + 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 f9035c2e8..8da50dde9 100644 --- a/src/cmd/ign_src_TEST.cc +++ b/src/cmd/ign_src_TEST.cc @@ -230,11 +230,11 @@ TEST(ignTest, cmdTopicEcho) transport::Node node; // Requesting a null topic should trigger an error message. - cmdTopicEcho(nullptr, 10.00, 0, false); + 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, false); + cmdTopicEcho(kInvalidTopic.c_str(), 5.00, 0, MsgOutputFormat::kDefault); EXPECT_EQ(stdErrBuffer.str(), "Topic [/] is not valid.\n"); clearIOStreams(stdOutBuffer, stdErrBuffer); diff --git a/src/cmd/topic_main.cc b/src/cmd/topic_main.cc index 5a2e41283..03deba1e3 100644 --- a/src/cmd/topic_main.cc +++ b/src/cmd/topic_main.cc @@ -54,7 +54,8 @@ struct TopicOptions /// \brief Number of messages to echo int count{-1}; - bool jsonOutput{false}; + /// \brief Message output format + MsgOutputFormat msgOutputFormat {MsgOutputFormat::kDefault}; }; ////////////////////////////////////////////////// @@ -76,7 +77,7 @@ void runTopicCommand(const TopicOptions &_opt) break; case TopicCommand::kTopicEcho: cmdTopicEcho(_opt.topic.c_str(), _opt.duration, _opt.count, - _opt.jsonOutput); + _opt.msgOutputFormat); break; case TopicCommand::kNone: default: @@ -123,7 +124,7 @@ void addTopicFlags(CLI::App &_app) }); command->add_flag_callback("--json-output", - [opt]() { opt->jsonOutput = true; }, + [opt]() { opt->msgOutputFormat = MsgOutputFormat::kJSON; }, "Output messages in JSON format"); command->add_option_function("-p,--pub", From 05d7f69757d09f941864a2dd5e441a1ae891c482 Mon Sep 17 00:00:00 2001 From: "Addisu Z. Taddese" Date: Mon, 28 Feb 2022 13:20:55 -0600 Subject: [PATCH 3/3] Add test Signed-off-by: Addisu Z. Taddese --- src/cmd/ign_src_TEST.cc | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/cmd/ign_src_TEST.cc b/src/cmd/ign_src_TEST.cc index 8da50dde9..9f0a46ed3 100644 --- a/src/cmd/ign_src_TEST.cc +++ b/src/cmd/ign_src_TEST.cc @@ -15,6 +15,7 @@ * */ +#include #include #include #include @@ -241,6 +242,45 @@ TEST(ignTest, cmdTopicEcho) 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)