[Plugins Common] - json protocol helper class
authorKevron Rees <kevron.m.rees@intel.com>
Sat, 31 Jan 2015 00:25:34 +0000 (16:25 -0800)
committerKevron Rees <kevron.m.rees@intel.com>
Sat, 31 Jan 2015 00:25:34 +0000 (16:25 -0800)
lib/CMakeLists.txt
lib/mappropertytype.hpp
plugins/common/CMakeLists.txt
plugins/common/jsonprotocol.cpp [new file with mode: 0644]
plugins/common/jsonprotocol.h [new file with mode: 0644]
plugins/gpsnmea/gpsnmea.cpp

index b479aaf..ce7fc7f 100644 (file)
@@ -23,6 +23,6 @@ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/automotive-message-broker.pc DESTINAT
 
 install (FILES ${amb_headers_install} DESTINATION ${INCLUDE_INSTALL_DIR}/amb COMPONENT Devel)
 
-install (TARGETS amb LIBRARY DESTINATION ${LIB_INSTALL_DIR} RUNTIME DESTINATION bin ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
+install (TARGETS amb LIBRARY DESTINATION ${LIB_INSTALL_DIR})
 
 
index c9e3d98..6cdcc6e 100644 (file)
@@ -8,7 +8,7 @@
 #include <debugout.h>
 #include "picojson.h"
 
-template <class N>
+template <class N = AbstractPropertyType*>
 class MapPropertyType: public AbstractPropertyType
 {
 public:
@@ -33,6 +33,28 @@ public:
                return t;
        }
 
+       bool contains(std::string key)
+       {
+               return mMap.find(key) != mMap.end();
+       }
+
+       N operator[](std::string key)
+       {
+               return mMap[key];
+       }
+
+       std::vector<std::string> keys()
+       {
+               std::vector<std::string> list;
+
+               for(auto itr : mMap)
+               {
+                       list.push_back(itr.first);
+               }
+
+               return list;
+       }
+
        std::string toString() const
        {
                std::stringstream str;
index c57c8b3..052c2c5 100644 (file)
@@ -21,4 +21,12 @@ install (FILES ${plugins_common_headers_install} DESTINATION ${INCLUDE_INSTALL_D
 
 install (TARGETS amb-plugins-common LIBRARY DESTINATION ${LIB_INSTALL_DIR} RUNTIME DESTINATION bin ARCHIVE DESTINATION ${LIB_INSTALL_DIR})
 
+set(amb_json_protocol_sources jsonprotocol.cpp)
+set(amb_json_protocol_headers jsonprotocol.cpp)
+
+add_library(amb-json-protocol SHARED ${amb_json_protocol_sources})
+
+target_link_libraries(amb-json-protocol amb -L${CMAKE_BINARY_DIR}/lib ${link_libraries} -L${CMAKE_CURRENT_BINARY_DIR} amb-plugins-common)
+
+install (TARGETS amb-json-protocol LIBRARY DESTINATION ${LIB_INSTALL_DIR})
 
diff --git a/plugins/common/jsonprotocol.cpp b/plugins/common/jsonprotocol.cpp
new file mode 100644 (file)
index 0000000..84e79c9
--- /dev/null
@@ -0,0 +1,438 @@
+#include "jsonprotocol.h"
+
+#include <glib.h>
+
+const char * amb::BasicTypes::UInt16Str = "UInt16";
+const char * amb::BasicTypes::UInt32Str = "UInt32";
+const char * amb::BasicTypes::Int16Str = "Int16";
+const char * amb::BasicTypes::Int32Str = "Int32";
+const char * amb::BasicTypes::StringStr = "String";
+const char * amb::BasicTypes::DoubleStr = "Double";
+const char * amb::BasicTypes::BooleanStr = "Boolean";
+
+const std::string amb::BasicTypes::fromSignature(const string &sig)
+{
+       if(sig.empty()) return "";
+
+       char c = sig[0];
+
+       if(c == G_VARIANT_CLASS_BOOLEAN)
+               return BooleanStr;
+
+       else if(c == G_VARIANT_CLASS_BYTE)
+               return "";
+
+       else if(c == G_VARIANT_CLASS_INT16)
+               return Int16Str;
+
+       else if(c == G_VARIANT_CLASS_UINT16)
+               return UInt16Str;
+
+       else if(c == G_VARIANT_CLASS_INT32)
+               return Int32Str;
+
+       else if(c ==  G_VARIANT_CLASS_UINT32)
+               return UInt32Str;
+
+       else if(c == G_VARIANT_CLASS_INT64)
+               return "";
+
+       else if(c == G_VARIANT_CLASS_UINT64)
+               return "";
+
+       else if(c == G_VARIANT_CLASS_DOUBLE)
+               return DoubleStr;
+
+       else if(c == G_VARIANT_CLASS_STRING)
+               return StringStr;
+
+       else if(c == G_VARIANT_CLASS_ARRAY)
+       {
+               ///TODO support array and map
+               return "";
+       }
+       return "";
+}
+
+bool readCallback(GIOChannel *source, GIOCondition condition, gpointer data)
+{
+       if(condition & G_IO_ERR)
+       {
+               DebugOut(DebugOut::Error)<<"BaseJsonReader polling error."<<endl;
+       }
+
+       if (condition & G_IO_HUP)
+       {
+               //Hang up. Returning false closes out the GIOChannel.
+               DebugOut(DebugOut::Warning)<<"socket hangup event..."<<endl;
+               return false;
+       }
+
+       amb::BaseJsonMessageReader * p = static_cast<amb::BaseJsonMessageReader*>(data);
+
+       p->canHasData();
+
+       return true;
+}
+
+amb::AmbRemoteClient::AmbRemoteClient(AbstractIo *io)
+       :BaseJsonMessageReader(io)
+{
+
+}
+
+void amb::AmbRemoteClient::list(amb::AmbRemoteClient::ListCallback cb)
+{
+       ListMethodCall methodCall;
+
+       mListCalls[methodCall.messageId] = cb;
+
+       mIo->write(methodCall.toJson().serialize());
+}
+
+void amb::AmbRemoteClient::get(const string &objectName, amb::AmbRemoteClient::ObjectCallback cb)
+{
+
+}
+
+void amb::AmbRemoteClient::get(const string &objectName, const string &sourceUuid, amb::AmbRemoteClient::ObjectCallback cb)
+{
+
+}
+
+void amb::AmbRemoteClient::get(const string &objectName, Zone::Type zone, amb::AmbRemoteClient::ObjectCallback cb)
+{
+
+}
+
+void amb::AmbRemoteClient::get(const string &objectName, const string &sourceUuid, Zone::Type zone, amb::AmbRemoteClient::ObjectCallback cb)
+{
+
+}
+
+void amb::AmbRemoteClient::set(const string &objectName, MapPropertyType<> *value, amb::AmbRemoteClient::ObjectCallback cb)
+{
+
+}
+
+void amb::AmbRemoteClient::set(const string &objectName, MapPropertyType<> *value, const string &sourceUuid, Zone::Type zone, amb::AmbRemoteClient::ObjectCallback cb)
+{
+
+}
+
+void amb::AmbRemoteClient::listen(const string &objectName, const string &sourceUuid, Zone::Type zone, amb::AmbRemoteClient::ObjectCallback cb)
+{
+
+}
+
+void amb::AmbRemoteClient::listen(const string &objectName, amb::AmbRemoteClient::ObjectCallback cb)
+{
+
+}
+
+void amb::AmbRemoteClient::hasJsonMessage(const picojson::value &json)
+{
+       BaseMessage message;
+
+       message.fromJson(json);
+
+       if(message.is<MethodCall>())
+       {
+               MethodCall methodCall(message);
+               methodCall.fromJson(json);
+
+               if(message.is<ListMethodCall>())
+               {
+                       ListMethodCall listMethodCall(methodCall);
+
+                       listMethodCall.fromJson(json);
+
+                       if(mListCalls.find(listMethodCall.messageId) != mListCalls.end())
+                       {
+                               auto cb = mListCalls[listMethodCall.messageId];
+
+                               try
+                               {
+                                       cb(listMethodCall.objectNames);
+                               }
+                               catch(...)
+                               {
+                                       DebugOut(DebugOut::Warning) << "callback for 'list' is not valid" << endl;
+                               }
+
+                               mListCalls.erase(listMethodCall.messageId);
+                       }
+               }
+       }
+}
+
+string amb::AmbRemoteClient::createSubscriptionId(const string & objectName, const string & sourceUuid, Zone::Type zone)
+{
+       std::string str = std::string(objectName + sourceUuid + std::to_string(zone));
+       return g_compute_checksum_for_string(G_CHECKSUM_MD5, str.c_str(), str.length());
+}
+
+picojson::value amb::BaseMessage::toJson()
+{
+       picojson::object val;
+
+       val["name"] = picojson::value(name);
+       val["type"] = picojson::value(type);
+       val["messageId"] = picojson::value(messageId);
+       val["data"] = picojson::value(data);
+
+       return picojson::value(val);
+}
+
+bool amb::BaseMessage::fromJson(const picojson::value &json)
+{
+       if(!json.is<picojson::object>() || !json.contains("type") || !json.contains("name") || !json.contains("messageId"))
+       {
+               DebugOut(DebugOut::Error) << "malformed message: is not json object or does not contain keys 'type', 'name' or 'messageId'." << endl;
+               return false;
+       }
+
+       picojson::object obj = json.get<picojson::object>();
+
+       type = obj["type"].to_str();
+       name = obj["name"].to_str();
+       messageId = obj["messageId"].to_str();
+
+       if(data.contains("data"))
+       {
+               data = json.get("data");
+       }
+
+       return true;
+}
+
+
+picojson::value amb::ListMethodCall::toJson()
+{
+       picojson::object v = MethodCall::toJson().get<picojson::object>();
+
+       picojson::array list;
+
+       for(auto i : objectNames)
+       {
+               list.push_back(property2Json(i));
+       }
+
+       v["data"] = picojson::value(list);
+
+       return picojson::value(v);
+}
+
+bool amb::ListMethodCall::fromJson(const picojson::value &json)
+{
+       if(!MethodCall::fromJson(json) || json.get("type").to_str() != "list" || !data.is<picojson::array>())
+       {
+               DebugOut(DebugOut::Error) << "type not 'list' or data not type json array";
+               return false;
+       }
+
+       objectNames.clear();
+
+       for(auto i : data.get<picojson::array>())
+       {
+               objectNames.push_back(json2Property(i));
+       }
+
+       return true;
+}
+
+
+amb::BaseJsonMessageReader::BaseJsonMessageReader(AbstractIo *io)
+       :mIo(io)
+{
+       GIOChannel *chan = g_io_channel_unix_new(mIo->fileDescriptor());
+       g_io_add_watch(chan, GIOCondition(G_IO_IN | G_IO_HUP | G_IO_ERR),(GIOFunc)readCallback, this);
+       g_io_channel_set_close_on_unref(chan, true);
+       g_io_channel_unref(chan);
+}
+
+void amb::BaseJsonMessageReader::canHasData()
+{
+       std::string d = mIo->read();
+
+       std::string::size_type start = d.find("{");
+
+       if(start == std::string::npos)
+       {
+               incompleteMessage += d;
+               return;
+       }
+
+       if(incompleteMessage.empty() && start > 0)
+       {
+               DebugOut(7) << "We have an incomplete message at the beginning.  Toss it away." << endl;
+               d = d.substr(start-1);
+       }
+
+
+       std::string::size_type end = d.find_last_of("}");
+
+       if(end == std::string::npos)
+       {
+               incompleteMessage += d;
+               return;
+       }
+
+       std::string tryMessage = incompleteMessage + d.substr(0, end+1);
+
+       DebugOut(6) << "Trying to parse message: " << tryMessage << endl;
+
+       picojson::value doc;
+
+       picojson::parse(doc, tryMessage);
+
+       std::string parseError = picojson::get_last_error();
+
+       if(!parseError.empty())
+       {
+               DebugOut(7) << "Invalid or incomplete message" << endl;
+               DebugOut(7) << parseError << endl;
+               incompleteMessage += d;
+               return;
+       }
+
+       incompleteMessage = end == d.length()-1 ? "" : d.substr(end);
+
+       hasJsonMessage(doc);
+}
+
+
+bool amb::MethodCall::fromJson(const picojson::value &json)
+{
+
+       return true;
+}
+
+
+std::shared_ptr<AbstractPropertyType> amb::json2Property(const picojson::value &json)
+{
+       Zone::Type zone = json.get("zone").get<double>();
+       std::string name = json.get("property").to_str();
+       std::string type = json.get("type").to_str();
+       std::string source = json.get("source").to_str();
+
+       auto t = VehicleProperty::getPropertyTypeForPropertyNameValue(name);
+
+       if(!t)
+       {
+               bool ret = VehicleProperty::registerProperty(name, [name, type]() -> AbstractPropertyType* {
+                       if(type == amb::BasicTypes::UInt16Str)
+                       {
+                               return new BasicPropertyType<uint16_t>(name, 0);
+                       }
+                       else if(type == amb::BasicTypes::Int16Str)
+                       {
+                               return new BasicPropertyType<int16_t>(name, 0);
+                       }
+                       else if(type == amb::BasicTypes::UInt32Str)
+                       {
+                               return new BasicPropertyType<uint32_t>(name, 0);
+                       }
+                       else if(type == amb::BasicTypes::Int32Str)
+                       {
+                               return new BasicPropertyType<int32_t>(name, 0);
+                       }
+                       else if(type == amb::BasicTypes::StringStr)
+                       {
+                               return new StringPropertyType(name);
+                       }
+                       else if(type == amb::BasicTypes::DoubleStr)
+                       {
+                               return new BasicPropertyType<double>(name, 0);
+                       }
+                       else if(type == amb::BasicTypes::BooleanStr)
+                       {
+                               return new BasicPropertyType<bool>(name, false);
+                       }
+                       DebugOut(DebugOut::Warning) << "Unknown or unsupported type: " << type << endl;
+                       return nullptr;
+               });
+
+               if(!ret)
+               {
+                       DebugOut(DebugOut::Error) << "failed to register property: " << name << endl;
+                       return nullptr;
+               }
+
+               t = VehicleProperty::getPropertyTypeForPropertyNameValue(name);
+       }
+
+       t->sourceUuid = source;
+       t->zone = zone;
+
+       return std::shared_ptr<AbstractPropertyType>(t);
+}
+
+
+picojson::value amb::property2Json(std::shared_ptr<AbstractPropertyType> property)
+{
+       std::string signature = property->signature();
+       const std::string basicType = amb::BasicTypes::fromSignature(signature);
+
+       picojson::object map;
+       map["zone"] = picojson::value((double)property->zone);
+       map["property"] = picojson::value(property->name);
+       map["type"] = picojson::value(basicType);
+       map["source"] = picojson::value(property->sourceUuid);
+
+       return picojson::value(map);
+}
+
+
+amb::AmbRemoteServer::AmbRemoteServer(AbstractIo *io)
+       :BaseJsonMessageReader(io)
+{
+
+}
+
+void amb::AmbRemoteServer::list(ListMethodCall &call)
+{
+
+}
+
+void amb::AmbRemoteServer::get()
+{
+
+}
+
+void amb::AmbRemoteServer::set()
+{
+
+}
+
+void amb::AmbRemoteServer::listen()
+{
+
+}
+
+void amb::AmbRemoteServer::hasJsonMessage(const picojson::value &json)
+{
+       if(!BaseMessage::validate(json))
+       {
+               DebugOut(DebugOut::Warning) << "not a valid message" << endl;
+       }
+
+       if(BaseMessage::is<MethodCall>(json))
+       {
+               if(BaseMessage::is<ListMethodCall>(json))
+               {
+                       ListMethodCall listCall;
+
+                       listCall.fromJson(json);
+
+                       list(listCall);
+
+                       send(&listCall);
+               }
+       }
+}
+
+void amb::AmbRemoteServer::send(amb::BaseMessage *msg)
+{
+       mIo->write(msg->toJson().serialize());
+}
diff --git a/plugins/common/jsonprotocol.h b/plugins/common/jsonprotocol.h
new file mode 100644 (file)
index 0000000..83b5613
--- /dev/null
@@ -0,0 +1,279 @@
+/*
+Copyright (C) 2015 Intel Corporation
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+#ifndef AMB_JSON_PROTOCOL_H_
+#define AMB_JSON_PROTOCOL_H_
+
+#include <functional>
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <abstractroutingengine.h>
+#include <mappropertytype.hpp>
+#include <picojson.h>
+#include <uuidhelper.h>
+#include <vehicleproperty.h>
+
+#include "abstractio.hpp"
+
+
+namespace amb
+{
+
+namespace BasicTypes
+{
+enum BasicTypeEnum
+{
+       UInt16,
+       UInt32,
+       Int16,
+       Int32,
+       String,
+       Double,
+       Boolean
+};
+
+extern const char * UInt16Str;
+extern const char * UInt32Str;
+extern const char * Int16Str;
+extern const char * Int32Str;
+extern const char * StringStr;
+extern const char * DoubleStr;
+extern const char * BooleanStr;
+
+const std::string fromSignature(std::string const & sig);
+
+} // BasicTypes
+
+std::shared_ptr<AbstractPropertyType> json2Property(const picojson::value& json);
+
+picojson::value property2Json(std::shared_ptr<AbstractPropertyType> property );
+
+class BaseMessage
+{
+public:
+       BaseMessage() { }
+
+       BaseMessage(std::string n, std::string t)
+               : name(n), type(t)
+       {
+               messageId = amb::createUuid();
+       }
+
+       BaseMessage(const BaseMessage & other)
+               : name(other.name), type(other.type), data(other.data)
+       {
+
+       }
+
+       std::string name;
+       std::string type;
+
+       std::string messageId;
+
+       virtual picojson::value toJson();
+       virtual bool fromJson(const picojson::value & json);
+
+       template <typename T>
+       bool is()
+       {
+               return T::is(this);
+       }
+
+       static bool validate(const picojson::value & json)
+       {
+               return json.is<picojson::object>() && json.contains("type") && !json.contains("name") && json.contains("messageId");
+       }
+
+       template <typename T>
+       static bool is(const picojson::value & json)
+       {
+               return T::is(json);
+       }
+
+protected:
+
+       picojson::value data;
+
+private:
+};
+
+class MethodCall : public BaseMessage
+{
+public:
+       MethodCall(std::string name)
+               :BaseMessage(name, "method"), zone(Zone::None)
+       {
+
+       }
+
+       MethodCall(const BaseMessage & other)
+               :BaseMessage(other), zone(Zone::None)
+       {
+               name = other.name;
+       }
+
+       MethodCall(const MethodCall & other)
+               :MethodCall(other.name)
+       {
+               sourceUuid = other.sourceUuid;
+       }
+
+       static bool is(const BaseMessage * msg)
+       {
+               return (msg->type == "method");
+       }
+
+       /*!
+        * \brief is checks if json message is of this message type
+        * Assumes that json is already a valid \ref BaseMessage
+        * \param json
+        * \return
+        */
+       static bool is(const picojson::value & json)
+       {
+               return json.contains("sourceUuid") && json.contains("zone");
+       }
+
+       virtual bool fromJson(const picojson::value &json);
+
+       std::string sourceUuid;
+       Zone::Type zone;
+};
+
+class ListMethodCall : public MethodCall
+{
+public:
+       ListMethodCall(): MethodCall("list") {}
+       ListMethodCall(const MethodCall & other)
+               :MethodCall(other)
+       {
+               if(!is(&other))
+                       throw std::runtime_error("type not list");
+       }
+
+       picojson::value toJson();
+       bool fromJson(const picojson::value &json);
+
+       std::vector<std::shared_ptr<AbstractPropertyType>> objectNames;
+
+       static bool is(const BaseMessage * msg)
+       {
+               return msg->name == "list";
+       }
+
+       static bool is(const picojson::value & json)
+       {
+               return json.get("type").to_str() != "list" && json.is<picojson::array>();
+       }
+};
+
+class BaseJsonMessageReader
+{
+public:
+       BaseJsonMessageReader(AbstractIo* io);
+
+       void canHasData();
+
+protected:
+
+       virtual void hasJsonMessage(const picojson::value & message) = 0;
+
+       std::shared_ptr<AbstractIo> mIo;
+
+private:
+
+       std::string incompleteMessage;
+
+};
+
+class AmbRemoteClient: public BaseJsonMessageReader
+{
+public:
+       typedef std::function<void (std::vector<std::shared_ptr<AbstractPropertyType>>)> ListCallback;
+       typedef std::function<void (MapPropertyType<> *)> ObjectCallback;
+       typedef std::function<void (bool)> SetCallback;
+
+       AmbRemoteClient(AbstractIo* io);
+
+       void list(ListCallback cb);
+
+       void get(const std::string & objectName, ObjectCallback cb);
+
+       void get(const std::string & objectName, const std::string & sourceUuid, ObjectCallback cb);
+
+       void get(const std::string & objectName, Zone::Type zone, ObjectCallback cb);
+
+       void get(const std::string & objectName, const std::string & sourceUuid, Zone::Type zone, ObjectCallback cb);
+
+       void set(const std::string & objectName, MapPropertyType<>* value, ObjectCallback cb);
+
+       void set(const std::string & objectName, MapPropertyType<>* value, const std::string & sourceUuid, Zone::Type zone, ObjectCallback cb);
+
+       void listen(const std::string & objectName, const std::string & sourceUuid, Zone::Type zone, ObjectCallback cb);
+
+       void listen(const std::string & objectName, ObjectCallback cb);
+
+protected:
+
+private:
+
+       void hasJsonMessage(const picojson::value & message);
+
+       std::string createSubscriptionId(const std::string & objectName,  const std::string & sourceUuid, Zone::Type zone);
+       std::unordered_map<std::string, ListCallback> mListCalls;
+       std::unordered_map<std::string, AsyncPropertyReply*> mMethodCalls;
+       std::unordered_map<std::string, std::vector<ObjectCallback>> mSubsriptions;
+};
+
+class AmbRemoteServer : public BaseJsonMessageReader
+{
+public:
+       AmbRemoteServer(AbstractIo* io);
+
+protected:
+
+       /*!
+        * \brief list called when a ListMessageCall was received
+        */
+       virtual void list(ListMethodCall & call);
+
+       /*!
+        * \brief get called when a GetMessageCall was received
+        */
+       virtual void get();
+
+       /*!
+        * \brief set called when SetMessageCall was received
+        */
+       virtual void set();
+       /*!
+        * \brief listen called when ListenMessageCall was received
+        */
+       virtual void listen();
+
+       void hasJsonMessage(const picojson::value & json);
+
+       void send(BaseMessage* msg);
+};
+
+} //namespace amb
+
+#endif
index 9bcf0a4..8ef1462 100644 (file)
@@ -469,7 +469,7 @@ GpsNmeaSource::GpsNmeaSource(AbstractRoutingEngine *re, map<string, string> conf
        auto lon = addPropertySupport<VehicleProperty::LongitudeType>(Zone::None);
        auto alt = addPropertySupport<VehicleProperty::AltitudeType>(Zone::None);
        auto spd = addPropertySupport(Zone::None, [](){ return new BasicPropertyType<uint16_t>(GPSSPEED, 0); });
-       auto vspd = addPropertySupport<VehicleProperty::VehicleSpeed>(Zone::None);
+       auto vspd = addPropertySupport<VehicleProperty::VehicleSpeedType>(Zone::None);
        auto dir = addPropertySupport<VehicleProperty::DirectionType>(Zone::None);
        auto time = addPropertySupport(Zone::None, [](){ return new BasicPropertyType<double>(GPSTIME, 0); });
        auto fix = addPropertySupport(Zone::None, []() { return new BasicPropertyType<Location::FixType>(GpsFix, Location::NoFix); });