std::string json(table);
std::replace(json.begin(), json.end(), '\'', '"');// replace all ' to "
- std::unique_ptr<json_object, decltype(&json_object_put)> rootobject(json_tokener_parse(json.c_str()), &json_object_put);
- if(!rootobject)
+ picojson::value rootobject;
+
+ picojson::parse(rootobject, json);
+
+ if(!rootobject.is<picojson::object>())
{
LOG_ERROR("Failed to parse json: " << json);
return;
// Success, use json_obj here.
mappingTable.clear();
- json_object *sources = json_object_object_get(rootobject.get(),"sources");
- if(!sources)
- return;
- array_list* arraySources = json_object_get_array(sources);
- if(!arraySources)
+ picojson::array sources = rootobject.get("sources").get<picojson::array>();
+ if(!sources.size())
return;
- for(int i=0; i < array_list_length(arraySources); ++i)
+ for(auto rootsource : sources)
{
- json_object *rootsource = static_cast<json_object*>(array_list_get_idx(arraySources,i));
- if(!rootsource)
+ picojson::value source = rootsource.get("source");
+ const std::string guidstr = source.get("guid").to_str();
+ picojson::array signals = rootsource.get("signals").get<picojson::array>();
+ if(!signals.size())
continue;
- json_object* source = json_object_object_get(rootsource, "source");
- if(!source)
- continue;
- json_object* guid = json_object_object_get(source, "guid");
- const std::string guidstr(guid ? json_object_get_string(guid) : "");
- json_object* signals = json_object_object_get(rootsource, "signals");
- if(!signals)
- continue;
- array_list* arraySignals = json_object_get_array(signals);
- for(int j = 0; j < array_list_length(arraySignals); ++j)
+ for(auto signal : signals)
{
- json_object *signal = static_cast<json_object*>(array_list_get_idx(arraySignals,j));
- if(!signal)
- continue;
mappingTable.addProperty(guidstr, signal);
}// signals array loop
}// sources array loop
if(!data || len == 0)
return;
- //TODO: refactor ? copied from websocketsink
- std::unique_ptr<json_object, decltype(&json_object_put)> rootobject(nullptr, &json_object_put);
- std::unique_ptr<json_tokener, decltype(&json_tokener_free)> tokener(json_tokener_new(), &json_tokener_free);
- enum json_tokener_error err;
- do
- { std::unique_ptr<json_object, decltype(&json_object_put)> tmpobject(json_tokener_parse_ex(tokener.get(), data, len), &json_object_put);
- rootobject.swap(tmpobject);
- } while ((err = json_tokener_get_error(tokener.get())) == json_tokener_continue);
- if (err != json_tokener_success)
- {
- LOG_ERROR("Error: " << json_tokener_error_desc(err) << std::endl);
- return;
- }
- if(!rootobject)
+ picojson::value rootobject;
+
+ picojson::parse(rootobject, data);
+
+ if(!rootobject.is<picojson::object>())
{
LOG_ERROR("Failed to parse json: " << data << std::endl);
return;
}
- if (tokener->char_offset < len) // XXX shouldn't access internal fields
- {
- // Handle extra characters after parsed object as desired.
- // e.g. issue an error, parse another object from that point, etc...
- }
- // Success, use jobj here.
- json_object *typeobject = json_object_object_get(rootobject.get(),"type");
- json_object *nameobject = json_object_object_get(rootobject.get(),"name");
- json_object *transidobject = json_object_object_get(rootobject.get(),"transactionid");
+ std::string type = rootobject.get("type").to_str();
+ std::string name = rootobject.get("name").to_str();
+ std::string id = rootobject.get("transactionid").to_str();
- if(!typeobject || !nameobject || !transidobject)
+ if(typeobject.empty() || nameobject.empty() || transidobject.empty())
{
DebugOut(DebugOut::Warning)<<"Malformed json. aborting"<<endl;
return;
}
- string type = string(json_object_get_string(typeobject));
- string name = string(json_object_get_string(nameobject));
- string id;
- if (json_object_get_type(transidobject) == json_type_string)
- {
- id = string(json_object_get_string(transidobject));
- }
- else
- {
- stringstream strstr;
- strstr << json_object_get_int(transidobject);
- id = strstr.str();
- }
if (type == "method") {
vector<string> propertyNames;
list< std::tuple<string, string, string, Zone::Type, string> > propertyData;
- json_object *dataobject = json_object_object_get(rootobject.get(),"data");
- if (json_object_get_type(dataobject) == json_type_array)
+ picojson::value dataobject = rootobject.get("data");
+ if(dataobject.is<picojson::array)
{
- array_list *arraylist = json_object_get_array(dataobject);
- for (int i=0;i<array_list_length(arraylist);i++)
+ for (auto arrayobject : dataobject)
{
- json_object *arrayobject = (json_object*)array_list_get_idx(arraylist,i);
if (json_object_get_type(arrayobject) == json_type_object)
{
json_object *interfaceobject = json_object_object_get(arrayobject,"interface");
propertyNames.push_back(propertyName);
}
}
- //array_list_free(arraylist);
}
else
{
- string path = json_object_get_string(dataobject);
- if (path != "")
+ propertyNames.push_back();
+ }
+
+ if (name == "get")
+ {
+ if (!propertyNames.empty())
{
- propertyNames.push_back(path);
+ //GetProperty is going to be a singleshot sink.
+ getValue(socket,propertyNames.front(), Zone::None,id);
+ }
+ else if (!propertyData.empty())
+ {
+ //GetProperty is going to be a singleshot sink.
+ auto prop = propertyData.front();
+ getValue(socket,std::get<1>(prop),std::get<3>(prop),id);
+ }
+ else
+ {
+ LOG_WARNING(" \"get\" method called with no data! Transaction ID:" << id);
}
}
- if (type == "method")
+ else if (name == "set")
{
- if (name == "get")
+ LOG_MESSAGE("set called");
+ if (!propertyNames.empty())
{
- if (!propertyNames.empty())
- {
- //GetProperty is going to be a singleshot sink.
- getValue(socket,propertyNames.front(),Zone::None,id);
- }
- else if (!propertyData.empty())
- {
- //GetProperty is going to be a singleshot sink.
- auto prop = propertyData.front();
- getValue(socket,std::get<1>(prop),std::get<3>(prop),id);
- }
- else
- {
- LOG_WARNING(" \"get\" method called with no data! Transaction ID:" << id);
- }
+ //Should not happen
}
- else if (name == "set")
+ else if (!propertyData.empty())
{
- LOG_MESSAGE("set called");
- if (!propertyNames.empty())
+ for (auto prop = propertyData.begin(); prop != propertyData.end(); ++prop)
{
- //Should not happen
- }
- else if (!propertyData.empty())
- {
- for (auto prop = propertyData.begin(); prop != propertyData.end(); ++prop)
- {
- LOG_MESSAGE("websocketsinkmanager setting " << std::get<1>(*prop) << " to " << std::get<2>(*prop) << " in zone " << std::get<3>(*prop));
- setValue(socket,std::get<1>(*prop),std::get<2>(*prop),std::get<3>(*prop),std::get<0>(*prop), id);
- }
+ LOG_MESSAGE("websocketsinkmanager setting " << std::get<1>(*prop) << " to " << std::get<2>(*prop) << " in zone " << std::get<3>(*prop));
+ setValue(socket,std::get<1>(*prop),std::get<2>(*prop),std::get<3>(*prop),std::get<0>(*prop), id);
}
}
- else if (name == "getSupportedEventTypes")
+ }
+ else if (name == "getSupportedEventTypes")
+ {
+ //If data.front() dosen't contain a property name, return a list of properties supported.
+ //if it does, then return the event types that particular property supports.
+ string typessupported = "";
+ if (propertyNames.empty())
{
- //If data.front() dosen't contain a property name, return a list of properties supported.
- //if it does, then return the event types that particular property supports.
- string typessupported = "";
- if (propertyNames.empty())
- {
- //Send what properties we support
- PropertyList foo(routingEngine->supported());
- PropertyList::const_iterator i=foo.cbegin();
- while (i != foo.cend())
- {
- if(i==foo.cbegin())
- typessupported.append("\"").append((*i)).append("\"");
- else
- typessupported.append(",\"").append((*i)).append("\"");
- ++i;
- }
- }
- else
+ //Send what properties we support
+ PropertyList foo(routingEngine->supported());
+ PropertyList::const_iterator i=foo.cbegin();
+ while (i != foo.cend())
{
- //Send what events a particular property supports
- PropertyList foo(routingEngine->supported());
- if (contains(foo,propertyNames.front()))
- {
- //sinkManager->addSingleShotSink(wsi,data.front(),id);
- typessupported = "\"get\",\"getSupportedEventTypes\"";
- }
+ if(i==foo.cbegin())
+ typessupported.append("\"").append((*i)).append("\"");
+ else
+ typessupported.append(",\"").append((*i)).append("\"");
+ ++i;
}
- stringstream s;
- string s2;
- s << "{\"type\":\"methodReply\",\"name\":\"getSupportedEventTypes\",\"data\":[" << typessupported << "],\"transactionid\":\"" << id << "\"}";
- string replystr = s.str();
- LOG_INFO(" JSON Reply: " << replystr);
- WebSockets::Write(socket, replystr);
}
else
{
- DebugOut(0)<<"Unknown method called."<<endl;
+ //Send what events a particular property supports
+ PropertyList foo(routingEngine->supported());
+ if (contains(foo,propertyNames.front()))
+ {
+ //sinkManager->addSingleShotSink(wsi,data.front(),id);
+ typessupported = "\"get\",\"getSupportedEventTypes\"";
+ }
}
+ stringstream s;
+ string s2;
+ s << "{\"type\":\"methodReply\",\"name\":\"getSupportedEventTypes\",\"data\":[" << typessupported << "],\"transactionid\":\"" << id << "\"}";
+ string replystr = s.str();
+ LOG_INFO(" JSON Reply: " << replystr);
+ WebSockets::Write(socket, replystr);
+ }
+ else
+ {
+ DebugOut(0)<<"Unknown method called."<<endl;
}
}
}
#include <canobserver.h>
#include <ambpluginimpl.h>
+#include <picojson.h>
#include "websockets.h"
/*!
public:
- /*!
- * \param re AbstractRoutingEngine
- * \param config Map of the configuration string values loaded on startup from AMB configuration file
- * \param parent AmbPlugin instance
- */
+ /*!
+ * \param re AbstractRoutingEngine
+ * \param config Map of the configuration string values loaded on startup from AMB configuration file
+ * \param parent AmbPlugin instance
+ */
CANGenPlugin(AbstractRoutingEngine* re, const std::map<std::string, std::string>& config, AbstractSource &parent);
- virtual ~CANGenPlugin(); // has to be virtual because of unit tests
+ virtual ~CANGenPlugin(); // has to be virtual because of unit tests
- // from AbstractSink
+ // from AbstractSink
public:
- /*! uuid() is a unique identifier of the plugin
- * @return a guid-style unique identifier
- */
- const std::string uuid() const { return "becbbef9-6cc8-4b9e-8cd7-2fbe37b9b52a"; }
+ /*! uuid() is a unique identifier of the plugin
+ * @return a guid-style unique identifier
+ */
+ const std::string uuid() const { return "becbbef9-6cc8-4b9e-8cd7-2fbe37b9b52a"; }
- /*! propertyChanged is called when a subscribed to property changes.
- * @see AbstractRoutingEngine::subscribeToPropertyChanges()
- * \param value value of the property that changed. this is a temporary pointer that will be destroyed.
- * Do not destroy it. If you need to store the value use value.anyValue(), value.value<T>() or
- * value->copy() to copy.
- */
- void propertyChanged(AbstractPropertyType* value);
+ /*! propertyChanged is called when a subscribed to property changes.
+ * @see AbstractRoutingEngine::subscribeToPropertyChanges()
+ * \param value value of the property that changed. this is a temporary pointer that will be destroyed.
+ * Do not destroy it. If you need to store the value use value.anyValue(), value.value<T>() or
+ * value->copy() to copy.
+ */
+ void propertyChanged(AbstractPropertyType* value);
AsyncPropertyReply* setProperty(const AsyncSetPropertyRequest &request);
- // from CANObserver
+ // from CANObserver
public:
- /*!
- * Called when error occurred on the bus.
- * \fn errorOccured
- * \param error \link CANObserver#CANError Bus error code \endlink
- */
- virtual void errorOccured(CANObserver::CANError error);/* socket error */
- /*!
- * Called when standard frame was is received from the bus.
- * \fn standardFrameReceived
- * \param frame Received frame
- */
- virtual void standardFrameReceived(const can_frame& frame);/* SFF was present */
- /*!
- * Called when extended frame was is received from the bus.
- * \fn extendedFrameReceived
- * \param frame Received frame
- */
- virtual void extendedFrameReceived(const can_frame& frame);/* EFF was present */
- /*!
- * Called when error frame was received from the bus.
- * \fn errorFrameReceived
- * \param frame Error frame
- */
- virtual void errorFrameReceived(const can_frame& frame);/* error frame */
- /*!
- * Called when remote transmission frame was received from the bus.
- * \fn remoteTransmissionRequest
- * \param frame RTR frame
- */
- virtual void remoteTransmissionRequest(const can_frame& frame);/* remote transmission request (SFF/EFF is still present)*/
-
- /*!
- * Second phase of the plugin initialization.
- * \fn init
- */
- virtual void init();
-
- // from WebSocketsObserver
-
- /*!
- * Called when data received from libwebsockets
- * \fn dataReceived
- * \param socket libwebsocket* to be used to send any reply.
- * \param data Received data pointer.
- * \param len Length of the data.
- * \return None
- */
- void dataReceived(libwebsocket* socket, const char* data, size_t len);
+ /*!
+ * Called when error occurred on the bus.
+ * \fn errorOccured
+ * \param error \link CANObserver#CANError Bus error code \endlink
+ */
+ virtual void errorOccured(CANObserver::CANError error);/* socket error */
+ /*!
+ * Called when standard frame was is received from the bus.
+ * \fn standardFrameReceived
+ * \param frame Received frame
+ */
+ virtual void standardFrameReceived(const can_frame& frame);/* SFF was present */
+ /*!
+ * Called when extended frame was is received from the bus.
+ * \fn extendedFrameReceived
+ * \param frame Received frame
+ */
+ virtual void extendedFrameReceived(const can_frame& frame);/* EFF was present */
+ /*!
+ * Called when error frame was received from the bus.
+ * \fn errorFrameReceived
+ * \param frame Error frame
+ */
+ virtual void errorFrameReceived(const can_frame& frame);/* error frame */
+ /*!
+ * Called when remote transmission frame was received from the bus.
+ * \fn remoteTransmissionRequest
+ * \param frame RTR frame
+ */
+ virtual void remoteTransmissionRequest(const can_frame& frame);/* remote transmission request (SFF/EFF is still present)*/
+
+ /*!
+ * Second phase of the plugin initialization.
+ * \fn init
+ */
+ virtual void init();
+
+ // from WebSocketsObserver
+
+ /*!
+ * Called when data received from libwebsockets
+ * \fn dataReceived
+ * \param socket libwebsocket* to be used to send any reply.
+ * \param data Received data pointer.
+ * \param len Length of the data.
+ * \return None
+ */
+ void dataReceived(libwebsocket* socket, const char* data, size_t len);
//
// Internal methods:
//
private:
- /*!
- * \brief Prints received CAN frame
- * \param frame Received CAN frame.
- * \internal
- * \private
- */
- void printFrame(const can_frame& frame) const;
-
- /*!
- * Parses 'MappingTable' property from CANSimPlugin.
- * Result is stored in internal MappingTable class(AMB property and Zone to CAN Id map) which contains all properties that can be simulated.
- * \param json Content of the 'MappingTable' property in JSON format.
- */
- void parseMappingTable(const std::string& json);
-
- /*!
- * \brief Simulator.get request handler function.
- * Builds and sends reply with the property value, timestamp and sequence number in JSON format.
- * \param socket libwebsocket handle to be used to send reply.
- * \param property Name of the property.
- * \param zone Property's zone.
- * \param uuid Request's transaction id.
- * \private
- */
- void getValue(libwebsocket* socket, const std::string& property, int zone, const std::string& uuid);
-
- /*!
- * \brief Simulator.set request handler function.
- * Formats property's value as a AMB's AbstractPropertyValue and passes it to sendValue. Reply to the Simulator with reply string in JSON format.
- * \param socket libwebsocket handle to be used to send reply.
- * \param property Name of the property.
- * \param value Property's new value to be simulated.
- * \param zone Property's zone.
- * \param interface CAN interface to be used to send CAN frame.
- * \param transactionId Request's transaction id.
- * \private
- */
- void setValue(libwebsocket* socket, const std::string& property, const std::string& value, int zone, const std::string& interface, const std::string& transactionId);
-
- /*!
- * \brief Build and sends CAN frame to CANSimPlugin.
- * Finds CAN Id using mappingTable for requested property name and zone, builds CAN frame with the property's new value and tries to send it via requested CAN interface.
- * \param interface CAN interface to be used to send CAN frame.
- * \param value AMB's AbstractPropertyValue which encapsulates property name, zone and value.
- * \return true if CAN frame was successfully sent, otherwise false.
- * \private
- */
- bool sendValue(const std::string& interface, AbstractPropertyType* value);
-
- /*!
- * Internal helper class
- * AMB property and property's zone to CAN Id map
- * \class MappingTable
- * \private
- * \internal
- *
- */
- class MappingTable{
- public:
- MappingTable()
- {
- }
-
- MappingTable(const MappingTable& other) = delete;
- MappingTable& operator=(const MappingTable& other) = delete;
- MappingTable(MappingTable&& other) = default;
- MappingTable& operator=(MappingTable&& other) = default;
-
- void addProperty(const std::string& source, json_object* signal)
- {
- json_object* canIdObj = json_object_object_get(signal, "can_id");
- json_object* nameObj = json_object_object_get(signal, "name");
- if(!canIdObj || !nameObj) // mandatory
- return;
- Zone::Type zone(Zone::None);
- json_object* zoneObj = json_object_object_get(signal, "zone");
- if(zoneObj)
- zone = json_object_get_int(zoneObj);
-
- auto& zp = mapping[source];
- auto& prop = zp[Zone::Type(zone)];
- std::string name(json_object_get_string(nameObj));
- int can_id = json_object_get_int(canIdObj);
- prop[name] = can_id; // update an existing value
- }
-
- int getCanId(const std::string& source, const Zone::Type& zone, const VehicleProperty::Property& name) const
- {
- //return mapping[source][zone][name]; // caution! this will insert if not found. I don't want it.
- auto sourceIt = mapping.find(source);
- if(sourceIt == mapping.end())
- return 0;
- auto zoneIt = sourceIt->second.find(zone);
- if(zoneIt == sourceIt->second.end())
- return 0;
- auto propIt = zoneIt->second.find(name);
- if(propIt == zoneIt->second.end())
- return 0;
- else
- return propIt->second;
- }
-
- void clear()
- {
- mapping.clear();
- }
-
- private:
- typedef std::map< Zone::Type, std::map<VehicleProperty::Property, canid_t> > ZonedProperty;
- std::map<std::string, ZonedProperty> mapping;
- };
+ /*!
+ * \brief Prints received CAN frame
+ * \param frame Received CAN frame.
+ * \internal
+ * \private
+ */
+ void printFrame(const can_frame& frame) const;
+
+ /*!
+ * Parses 'MappingTable' property from CANSimPlugin.
+ * Result is stored in internal MappingTable class(AMB property and Zone to CAN Id map) which contains all properties that can be simulated.
+ * \param json Content of the 'MappingTable' property in JSON format.
+ */
+ void parseMappingTable(const std::string& json);
+
+ /*!
+ * \brief Simulator.get request handler function.
+ * Builds and sends reply with the property value, timestamp and sequence number in JSON format.
+ * \param socket libwebsocket handle to be used to send reply.
+ * \param property Name of the property.
+ * \param zone Property's zone.
+ * \param uuid Request's transaction id.
+ * \private
+ */
+ void getValue(libwebsocket* socket, const std::string& property, int zone, const std::string& uuid);
+
+ /*!
+ * \brief Simulator.set request handler function.
+ * Formats property's value as a AMB's AbstractPropertyValue and passes it to sendValue. Reply to the Simulator with reply string in JSON format.
+ * \param socket libwebsocket handle to be used to send reply.
+ * \param property Name of the property.
+ * \param value Property's new value to be simulated.
+ * \param zone Property's zone.
+ * \param interface CAN interface to be used to send CAN frame.
+ * \param transactionId Request's transaction id.
+ * \private
+ */
+ void setValue(libwebsocket* socket, const std::string& property, const std::string& value, int zone, const std::string& interface, const std::string& transactionId);
+
+ /*!
+ * \brief Build and sends CAN frame to CANSimPlugin.
+ * Finds CAN Id using mappingTable for requested property name and zone, builds CAN frame with the property's new value and tries to send it via requested CAN interface.
+ * \param interface CAN interface to be used to send CAN frame.
+ * \param value AMB's AbstractPropertyValue which encapsulates property name, zone and value.
+ * \return true if CAN frame was successfully sent, otherwise false.
+ * \private
+ */
+ bool sendValue(const std::string& interface, AbstractPropertyType* value);
+
+ /*!
+ * Internal helper class
+ * AMB property and property's zone to CAN Id map
+ * \class MappingTable
+ * \private
+ * \internal
+ *
+ */
+ class MappingTable{
+ public:
+ MappingTable()
+ {
+ }
+
+ MappingTable(const MappingTable& other) = delete;
+ MappingTable& operator=(const MappingTable& other) = delete;
+ MappingTable(MappingTable&& other) = default;
+ MappingTable& operator=(MappingTable&& other) = default;
+
+ void addProperty(const std::string& source, picojson::value signal)
+ {
+
+ if(!signal.contains("can_id") || !signal.contains("name")) // mandatory
+ return;
+ picojson::value canIdObj = signal.get("can_id");
+ picojson::value nameObj = signal.get("name");
+ Zone::Type zone(Zone::None);
+
+ if(signal.contains("zone"))
+ {
+ zone = signal.get("zone").get<double>();
+ }
+
+ auto& zp = mapping[source];
+ auto& prop = zp[Zone::Type(zone)];
+ std::string name(nameObj.to_str());
+ int can_id = canIdObj.get<double>();
+ prop[name] = can_id; // update an existing value
+ }
+
+ int getCanId(const std::string& source, const Zone::Type& zone, const VehicleProperty::Property& name) const
+ {
+ //return mapping[source][zone][name]; // caution! this will insert if not found. I don't want it.
+ auto sourceIt = mapping.find(source);
+ if(sourceIt == mapping.end())
+ return 0;
+ auto zoneIt = sourceIt->second.find(zone);
+ if(zoneIt == sourceIt->second.end())
+ return 0;
+ auto propIt = zoneIt->second.find(name);
+ if(propIt == zoneIt->second.end())
+ return 0;
+ else
+ return propIt->second;
+ }
+
+ void clear()
+ {
+ mapping.clear();
+ }
+
+ private:
+ typedef std::map< Zone::Type, std::map<VehicleProperty::Property, canid_t> > ZonedProperty;
+ std::map<std::string, ZonedProperty> mapping;
+ };
//
// data:
//
- /*!
- * AMB property and property's zone to CAN Id map
- * \private
- */
- MappingTable mappingTable;
-
- /*!
- * Opened CAN interfaces used to send CAN frames
- * \private
- */
- std::map<std::string, std::shared_ptr<CANBus> > interfaces;
-
- /*!
- * Encapsulated libwebsocket library
- * \private
- */
- std::unique_ptr<WebSockets> ws;
-
- /*!
- * Mutex to protect mappingTable container during property 'MappingTable' parsing on change notification
- * \private
- */
- interprocess_recursive_mutex mutex;
+ /*!
+ * AMB property and property's zone to CAN Id map
+ * \private
+ */
+ MappingTable mappingTable;
+
+ /*!
+ * Opened CAN interfaces used to send CAN frames
+ * \private
+ */
+ std::map<std::string, std::shared_ptr<CANBus> > interfaces;
+
+ /*!
+ * Encapsulated libwebsocket library
+ * \private
+ */
+ std::unique_ptr<WebSockets> ws;
+
+ /*!
+ * Mutex to protect mappingTable container during property 'MappingTable' parsing on change notification
+ * \private
+ */
+ interprocess_recursive_mutex mutex;
};
class SimCommand: public StringPropertyType