From 72a8e9b33cec3cf30f328a97b8888e2b19e298c3 Mon Sep 17 00:00:00 2001 From: Alexey Rybalchenko Date: Mon, 7 Oct 2019 14:59:35 +0200 Subject: [PATCH] Commands: support JSON in addition to binary --- fairmq/sdk/commands/CMakeLists.txt | 7 ++- fairmq/sdk/commands/Commands.cxx | 36 +++++++++-- fairmq/sdk/commands/Commands.h | 41 ++++++------ fairmq/sdk/commands/CommandsFormatDef.h.in | 21 +++++++ test/commands/_commands.cxx | 73 +++++++++++++--------- 5 files changed, 127 insertions(+), 51 deletions(-) create mode 100644 fairmq/sdk/commands/CommandsFormatDef.h.in diff --git a/fairmq/sdk/commands/CMakeLists.txt b/fairmq/sdk/commands/CMakeLists.txt index 6f44170c..b7e2a84c 100644 --- a/fairmq/sdk/commands/CMakeLists.txt +++ b/fairmq/sdk/commands/CMakeLists.txt @@ -15,7 +15,12 @@ add_custom_command( WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} ) -add_library(${target} Commands.cxx Commands.h ${CMAKE_CURRENT_BINARY_DIR}/CommandsFormat.h) +# JSON serialization needs to see the .fbs file at run time, save it as constexpr string instead of locating/opening it every time +file(STRINGS CommandsFormat.fbs tmp) +list(JOIN tmp "\n" commands_format_def_fbs) +configure_file(${CMAKE_CURRENT_SOURCE_DIR}/CommandsFormatDef.h.in ${CMAKE_CURRENT_BINARY_DIR}/CommandsFormatDef.h) + +add_library(${target} Commands.cxx Commands.h ${CMAKE_CURRENT_BINARY_DIR}/CommandsFormat.h ${CMAKE_CURRENT_BINARY_DIR}/CommandsFormatDef.h) add_library(FairMQ::${target} ALIAS ${target}) target_link_libraries(${target} diff --git a/fairmq/sdk/commands/Commands.cxx b/fairmq/sdk/commands/Commands.cxx index 8094ce7c..d72a0aec 100644 --- a/fairmq/sdk/commands/Commands.cxx +++ b/fairmq/sdk/commands/Commands.cxx @@ -8,8 +8,11 @@ #include "Commands.h" +#include #include +#include + #include using namespace std; @@ -203,7 +206,7 @@ string GetTypeName(const Type type) { return typeNames.at(static_cast(type) FBCmd GetFBCmd(const Type type) { return typeToFBCmd.at(static_cast(type)); } -string Cmds::Serialize() const +string Cmds::Serialize(const Format type) const { flatbuffers::FlatBufferBuilder fbb; vector> commandOffsets; @@ -327,14 +330,39 @@ string Cmds::Serialize() const auto cmds = CreateFBCommands(fbb, commands); fbb.Finish(cmds); - return string(reinterpret_cast(fbb.GetBufferPointer()), fbb.GetSize()); + if (type == Format::Binary) { + return string(reinterpret_cast(fbb.GetBufferPointer()), fbb.GetSize()); + } else { // Type == Format::JSON + flatbuffers::Parser parser; + if (!parser.Parse(commandsFormatDefFbs)) { + throw CommandFormatError("Serialize couldn't parse commands format"); + } + std::string json; + if (!flatbuffers::GenerateText(parser, fbb.GetBufferPointer(), &json)) { + throw CommandFormatError("Serialize couldn't serialize parsed data to JSON!"); + } + return json; + } } -void Cmds::Deserialize(const string& str) +void Cmds::Deserialize(const string& str, const Format type) { fCmds.clear(); - auto cmds = cmd::GetFBCommands(const_cast(str.c_str()))->commands(); + const flatbuffers::Vector>* cmds; + + if (type == Format::Binary) { + cmds = cmd::GetFBCommands(const_cast(str.c_str()))->commands(); + } else { // Type == Format::JSON + flatbuffers::Parser parser; + if (!parser.Parse(commandsFormatDefFbs)) { + throw CommandFormatError("Deserialize couldn't parse commands format"); + } + if (!parser.Parse(str.c_str())) { + throw CommandFormatError("Deserialize couldn't parse incoming JSON string"); + } + cmds = cmd::GetFBCommands(parser.builder_.GetBufferPointer())->commands(); + } for (unsigned int i = 0; i < cmds->size(); ++i) { const fair::mq::sdk::cmd::FBCommand& cmdPtr = *(cmds->Get(i)); diff --git a/fairmq/sdk/commands/Commands.h b/fairmq/sdk/commands/Commands.h index 7ed27f2d..7c9fe919 100644 --- a/fairmq/sdk/commands/Commands.h +++ b/fairmq/sdk/commands/Commands.h @@ -27,6 +27,11 @@ namespace sdk namespace cmd { +enum class Format : int { + Binary, + JSON +}; + enum class Result : int { Ok, Failure @@ -133,7 +138,7 @@ struct CurrentState : Cmd struct TransitionStatus : Cmd { - explicit TransitionStatus(const std::string& id, Result result, Transition transition) + explicit TransitionStatus(const std::string& id, const Result result, const Transition transition) : Cmd(Type::transition_status) , fDeviceId(id) , fResult(result) @@ -143,9 +148,9 @@ struct TransitionStatus : Cmd std::string GetDeviceId() const { return fDeviceId; } void SetDeviceId(const std::string& deviceId) { fDeviceId = deviceId; } Result GetResult() const { return fResult; } - void SetResult(Result result) { fResult = result; } + void SetResult(const Result result) { fResult = result; } Transition GetTransition() const { return fTransition; } - void SetTransition(Transition transition) { fTransition = transition; } + void SetTransition(const Transition transition) { fTransition = transition; } private: std::string fDeviceId; @@ -173,7 +178,7 @@ struct Config : Cmd struct HeartbeatSubscription : Cmd { - explicit HeartbeatSubscription(const std::string& id, Result result) + explicit HeartbeatSubscription(const std::string& id, const Result result) : Cmd(Type::heartbeat_subscription) , fDeviceId(id) , fResult(result) @@ -182,7 +187,7 @@ struct HeartbeatSubscription : Cmd std::string GetDeviceId() const { return fDeviceId; } void SetDeviceId(const std::string& deviceId) { fDeviceId = deviceId; } Result GetResult() const { return fResult; } - void SetResult(Result result) { fResult = result; } + void SetResult(const Result result) { fResult = result; } private: std::string fDeviceId; @@ -191,7 +196,7 @@ struct HeartbeatSubscription : Cmd struct HeartbeatUnsubscription : Cmd { - explicit HeartbeatUnsubscription(const std::string& id, Result result) + explicit HeartbeatUnsubscription(const std::string& id, const Result result) : Cmd(Type::heartbeat_unsubscription) , fDeviceId(id) , fResult(result) @@ -200,7 +205,7 @@ struct HeartbeatUnsubscription : Cmd std::string GetDeviceId() const { return fDeviceId; } void SetDeviceId(const std::string& deviceId) { fDeviceId = deviceId; } Result GetResult() const { return fResult; } - void SetResult(Result result) { fResult = result; } + void SetResult(const Result result) { fResult = result; } private: std::string fDeviceId; @@ -223,7 +228,7 @@ struct Heartbeat : Cmd struct StateChangeSubscription : Cmd { - explicit StateChangeSubscription(const std::string& id, Result result) + explicit StateChangeSubscription(const std::string& id, const Result result) : Cmd(Type::state_change_subscription) , fDeviceId(id) , fResult(result) @@ -232,7 +237,7 @@ struct StateChangeSubscription : Cmd std::string GetDeviceId() const { return fDeviceId; } void SetDeviceId(const std::string& deviceId) { fDeviceId = deviceId; } Result GetResult() const { return fResult; } - void SetResult(Result result) { fResult = result; } + void SetResult(const Result result) { fResult = result; } private: std::string fDeviceId; @@ -241,7 +246,7 @@ struct StateChangeSubscription : Cmd struct StateChangeUnsubscription : Cmd { - explicit StateChangeUnsubscription(const std::string& id, Result result) + explicit StateChangeUnsubscription(const std::string& id, const Result result) : Cmd(Type::state_change_unsubscription) , fDeviceId(id) , fResult(result) @@ -250,7 +255,7 @@ struct StateChangeUnsubscription : Cmd std::string GetDeviceId() const { return fDeviceId; } void SetDeviceId(const std::string& deviceId) { fDeviceId = deviceId; } Result GetResult() const { return fResult; } - void SetResult(Result result) { fResult = result; } + void SetResult(const Result result) { fResult = result; } private: std::string fDeviceId; @@ -259,7 +264,7 @@ struct StateChangeUnsubscription : Cmd struct StateChange : Cmd { - explicit StateChange(const std::string& deviceId, uint64_t taskId, State lastState, State currentState) + explicit StateChange(const std::string& deviceId, const uint64_t taskId, const State lastState, const State currentState) : Cmd(Type::state_change) , fDeviceId(deviceId) , fTaskId(taskId) @@ -270,11 +275,11 @@ struct StateChange : Cmd std::string GetDeviceId() const { return fDeviceId; } void SetDeviceId(const std::string& deviceId) { fDeviceId = deviceId; } uint64_t GetTaskId() const { return fTaskId; } - void SetTaskId(uint64_t taskId) { fTaskId = taskId; } + void SetTaskId(const uint64_t taskId) { fTaskId = taskId; } fair::mq::State GetLastState() const { return fLastState; } - void SetLastState(fair::mq::State state) { fLastState = state; } + void SetLastState(const fair::mq::State state) { fLastState = state; } fair::mq::State GetCurrentState() const { return fCurrentState; } - void SetCurrentState(fair::mq::State state) { fCurrentState = state; } + void SetCurrentState(const fair::mq::State state) { fCurrentState = state; } private: std::string fDeviceId; @@ -314,11 +319,11 @@ struct Cmds Cmd& At(size_t i) { return *(fCmds.at(i)); } - size_t Size() { return fCmds.size(); } + size_t Size() const { return fCmds.size(); } void Reset() { fCmds.clear(); } - std::string Serialize() const; - void Deserialize(const std::string&); + std::string Serialize(const Format type = Format::Binary) const; + void Deserialize(const std::string&, const Format type = Format::Binary); private: container fCmds; diff --git a/fairmq/sdk/commands/CommandsFormatDef.h.in b/fairmq/sdk/commands/CommandsFormatDef.h.in new file mode 100644 index 00000000..a04967d1 --- /dev/null +++ b/fairmq/sdk/commands/CommandsFormatDef.h.in @@ -0,0 +1,21 @@ +/******************************************************************************** + * Copyright (C) 2019 GSI Helmholtzzentrum fuer Schwerionenforschung GmbH * + * * + * This software is distributed under the terms of the * + * GNU Lesser General Public Licence (LGPL) version 3, * + * copied verbatim in the file "LICENSE" * + ********************************************************************************/ + +#include + +namespace fair { +namespace mq { +namespace sdk { +namespace cmd { + +constexpr auto commandsFormatDefFbs = R"(@commands_format_def_fbs@)"; + +} // namespace cmd +} // namespace sdk +} // namespace mq +} // namespace fair diff --git a/test/commands/_commands.cxx b/test/commands/_commands.cxx index 79ba30d6..31f5f5ac 100644 --- a/test/commands/_commands.cxx +++ b/test/commands/_commands.cxx @@ -77,39 +77,34 @@ TEST(Format, Construction) ASSERT_EQ(static_cast(stateChangeCmds.At(0)).GetCurrentState(), State::Ready); } -TEST(Format, Serialization) +void fillCommands(Cmds& cmds) { - Cmds outCmds; + cmds.Add(); + cmds.Add(Transition::Stop); + cmds.Add(); + cmds.Add(); + cmds.Add(); + cmds.Add(); + cmds.Add(); + cmds.Add(); + cmds.Add("somedeviceid", State::Running); + cmds.Add("somedeviceid", Result::Ok, Transition::Stop); + cmds.Add("somedeviceid", "someconfig"); + cmds.Add("somedeviceid", Result::Ok); + cmds.Add("somedeviceid", Result::Ok); + cmds.Add("somedeviceid"); + cmds.Add("somedeviceid", Result::Ok); + cmds.Add("somedeviceid", Result::Ok); + cmds.Add("somedeviceid", 123456, State::Running, State::Ready); +} - outCmds.Add(); - outCmds.Add(Transition::Stop); - outCmds.Add(); - outCmds.Add(); - outCmds.Add(); - outCmds.Add(); - outCmds.Add(); - outCmds.Add(); - outCmds.Add("somedeviceid", State::Running); - outCmds.Add("somedeviceid", Result::Ok, Transition::Stop); - outCmds.Add("somedeviceid", "someconfig"); - outCmds.Add("somedeviceid", Result::Ok); - outCmds.Add("somedeviceid", Result::Ok); - outCmds.Add("somedeviceid"); - outCmds.Add("somedeviceid", Result::Ok); - outCmds.Add("somedeviceid", Result::Ok); - outCmds.Add("somedeviceid", 123456, State::Running, State::Ready); - - std::string buffer(outCmds.Serialize()); - - Cmds inCmds; - - inCmds.Deserialize(buffer); - - ASSERT_EQ(inCmds.Size(), 17); +void checkCommands(Cmds& cmds) +{ + ASSERT_EQ(cmds.Size(), 17); int count = 0; - for (const auto& cmd : inCmds) { + for (const auto& cmd : cmds) { switch (cmd->GetType()) { case Type::check_state: ++count; @@ -192,4 +187,26 @@ TEST(Format, Serialization) ASSERT_EQ(count, 17); } +TEST(Format, SerializationBinary) +{ + Cmds outCmds; + fillCommands(outCmds); + std::string buffer(outCmds.Serialize()); + + Cmds inCmds; + inCmds.Deserialize(buffer); + checkCommands(inCmds); +} + +TEST(Format, SerializationJSON) +{ + Cmds outCmds; + fillCommands(outCmds); + std::string buffer(outCmds.Serialize(Format::JSON)); + + Cmds inCmds; + inCmds.Deserialize(buffer, Format::JSON); + checkCommands(inCmds); +} + } // namespace