summaryrefslogtreecommitdiff
path: root/plugins/cangenplugin/cangenplugin.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/cangenplugin/cangenplugin.cpp')
-rw-r--r--plugins/cangenplugin/cangenplugin.cpp466
1 files changed, 466 insertions, 0 deletions
diff --git a/plugins/cangenplugin/cangenplugin.cpp b/plugins/cangenplugin/cangenplugin.cpp
new file mode 100644
index 00000000..73d32538
--- /dev/null
+++ b/plugins/cangenplugin/cangenplugin.cpp
@@ -0,0 +1,466 @@
+/*
+Copyright (C) 2012 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
+*/
+
+#include <boost/assert.hpp>
+#include <glib.h>
+#include <deque>
+
+#include <vehicleproperty.h>
+#include <listplusplus.h>
+
+#include <logger.h>
+#include <ambplugin.h>
+
+#include "cangenplugin.h"
+
+//----------------------------------------------------------------------------
+// CANGenPlugin
+//----------------------------------------------------------------------------
+
+// library exported function for plugin loader
+extern "C" void create(AbstractRoutingEngine* routingengine, std::map<std::string, std::string> config)
+{
+#ifndef UNIT_TESTS
+ DEBUG_CONF("cangenplugin",
+ CUtil::Logger::file_off | CUtil::Logger::screen_on,
+ CUtil::Logger::EInfo, CUtil::Logger::EInfo
+ );
+#endif
+
+ AmbPlugin<CANGenPlugin> * plugin = new AmbPlugin<CANGenPlugin>(routingengine, config);
+ plugin->init();
+}
+
+//----------------------------------------------------------------------------
+// CANGenPlugin
+//----------------------------------------------------------------------------
+
+CANGenPlugin::CANGenPlugin(AbstractRoutingEngine* re, const map<string, string>& config, AbstractSource& parent) :
+ AmbPluginImpl(re, config, parent),
+ ws(new WebSockets(*this))
+{
+ addPropertySupport(Zone::None,[]()
+ {
+ return new SimCommand();
+ });
+}
+
+CANGenPlugin::~CANGenPlugin()
+{
+ for(auto it = interfaces.begin(); it != interfaces.end(); ++it){
+ it->second->stop();
+ }
+}
+
+void CANGenPlugin::init()
+{
+ routingEngine->subscribeToProperty("MappingTable", &source);
+}
+
+void CANGenPlugin::propertyChanged(AbstractPropertyType* value)
+{
+ if(!value)
+ return;
+ if(!value->name.compare("MappingTable")) {
+ parseMappingTable(value->toString());
+ }
+}
+
+AsyncPropertyReply *CANGenPlugin::setProperty(const AsyncSetPropertyRequest &request)
+{
+ if(request.property == "SimCommand")
+ {
+ std::string v = request.value->toString();
+
+ dataReceived(nullptr, v.c_str(), v.length());
+ }
+
+ return AmbPluginImpl::setProperty(request);
+}
+
+void CANGenPlugin::parseMappingTable(const std::string& table)
+{
+ scoped_lock<interprocess_recursive_mutex> lock(mutex);
+
+ 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)
+ {
+ 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)
+ return;
+ for(int i=0; i < array_list_length(arraySources); ++i)
+ {
+ json_object *rootsource = static_cast<json_object*>(array_list_get_idx(arraySources,i));
+ if(!rootsource)
+ 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)
+ {
+ 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
+}
+
+// from CANObserver
+void CANGenPlugin::errorOccured(CANObserver::CANError error)
+{
+ (void) error;
+ LOG_INFO( "CANPlugin::errorOccured() not implemented "<< std::endl );
+}
+
+void CANGenPlugin::standardFrameReceived(const can_frame& frame)
+{
+ (void) frame;
+ LOG_INFO( "CANPlugin::standardFrameReceived() not implemented "<< std::endl );
+}
+
+void CANGenPlugin::extendedFrameReceived(const can_frame& frame)
+{
+ LOG_INFO("CANPlugin::extendedFrameReceived()");
+ printFrame(frame);
+}
+
+void CANGenPlugin::errorFrameReceived(const can_frame& frame)
+{
+ LOG_INFO("CANPlugin::errorFrameReceived()");
+ printFrame(frame);
+}
+
+void CANGenPlugin::remoteTransmissionRequest(const can_frame& frame)
+{
+ (void) frame;
+ LOG_INFO( "CANPlugin::remoteTransmissionRequest() not implemented "<< std::endl );
+}
+
+void CANGenPlugin::printFrame(const can_frame& frame) const
+{
+ LOG_INFO( "CANPlugin::printFrame can_id: " << std::hex << frame.can_id << std::dec << endl );
+ LOG_INFO( "CANPlugin::printFrame can_dlc: " << int(frame.can_dlc) << endl );
+
+ std::stringstream ss;
+ for(int i=0; i<frame.can_dlc; ++i){
+ ss << " " << std::hex << (int)(frame.data[i]);
+ }
+ ss << std::dec;
+
+ LOG_INFO( "CANPlugin::printFrame can data" << ss.str() << endl );
+}
+
+bool CANGenPlugin::sendValue(const std::string& interface, AbstractPropertyType* value)
+{
+ scoped_lock<interprocess_recursive_mutex> lock(mutex);
+
+ if(!value)
+ return false;
+ int can_id = mappingTable.getCanId(value->sourceUuid, value->zone, value->name);
+ if(can_id == 0)
+ return false;
+
+ can_frame frame;
+ frame.can_id = can_id;
+ std::unique_ptr<GVariant, decltype(&g_variant_unref)> v(value->toVariant(), &g_variant_unref);
+ gsize vs = g_variant_get_size(v.get());
+ assert(vs <= sizeof(frame.data));// Has to be <= 8
+ frame.can_dlc = vs > sizeof(frame.data) ? sizeof(frame.data) : vs;
+ memcpy(frame.data, g_variant_get_data(v.get()), frame.can_dlc);
+
+ auto& canBus = interfaces[interface];
+ if(!canBus){
+ canBus = std::shared_ptr<CANBus>(new CANBus(*static_cast<CANObserver*>(this)));
+ bool started(canBus->start(interface.c_str()));
+ if(!started)
+ return false;
+ }
+ return canBus->sendExtendedFrame(frame);
+}
+
+void CANGenPlugin::getValue(libwebsocket* socket, const std::string& property, int zone, const std::string& id)
+{
+ AsyncPropertyRequest request;
+ PropertyList foo = VehicleProperty::capabilities();
+ if(contains(foo, property))
+ {
+ request.property = property;
+ }
+ else
+ {
+ DebugOut(0)<<"websocketsink: Invalid property requested: "<<property;
+ return;
+ }
+
+ request.zoneFilter = zone;
+ request.completed = [socket,id,property](AsyncPropertyReply* reply)
+ {
+ DebugOut()<<"Got property: "<<reply->property.c_str()<<endl;
+ if(!reply->value){
+ DebugOut()<<"Property value is null"<<endl;
+ delete reply;
+ return;
+ }
+
+ stringstream s;
+ s.precision(15);
+
+ s << "{\"type\":\"methodReply\",\"name\":\"get\",\"data\":{";
+ s << "\"property\":\"" << property << "\",\"zone\":\"" << reply->value->zone << "\",\"value\":\"" << reply->value->toString() << "\",\"timestamp\":\""<<reply->value->timestamp<<"\",";
+ s <<"\"sequence\": \""<<reply->value->sequence<<"\"}";
+ s << ",\"transactionid\":\"" << id << "\"}";
+
+ string replystr = s.str();
+ //printf("Reply: %s\n",replystr.c_str());
+ LOG_MESSAGE("Reply:" << replystr << endl);
+
+ //if(replystr.length() > 4096){
+ // WebSockets::Write(socket, replystr.substr(0,4096));
+ // WebSockets::Write(socket, replystr.substr(4096, 4096));
+ //}
+ //else WebSockets::Write(socket, replystr);
+ WebSockets::Write(socket, replystr);
+
+ delete reply;
+ };
+
+ routingEngine->getPropertyAsync(request);
+}
+
+void CANGenPlugin::setValue(libwebsocket* socket, const std::string& property, const std::string& value, int zone, const std::string& interface, const std::string& transactionId)
+{
+ LOG_MESSAGE( "CANGenPlugin::setValue called with arguments:" << property << ", " << value << endl);
+
+ bool sent(false);
+ std::unique_ptr<AbstractPropertyType> type(VehicleProperty::getPropertyTypeForPropertyNameValue(property,value));
+ if(type) {
+ type->zone = zone;
+ type->sourceUuid = CANSimPluginUUID;
+
+ sent = sendValue(interface, type.get());
+ }
+
+ stringstream ss;
+ ss << "{\"type\":\"methodReply\",\"name\":\"set\",\"data\":[{\"property\":\"" << property << "\"}],\"transactionid\":\"" << transactionId << "\"";
+ if(!sent)
+ ss << ",\"error\":\"method call failed\"";
+ ss << "}";
+
+ string replystr = ss.str();
+ LOG_MESSAGE( "Reply:" << replystr << endl);
+
+ WebSockets::Write(socket, replystr);
+}
+
+void CANGenPlugin::dataReceived(libwebsocket* socket, const char* data, size_t len)
+{
+ 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)
+ {
+ 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");
+
+ if(!typeobject || !nameobject || !transidobject)
+ {
+ 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)
+ {
+ array_list *arraylist = json_object_get_array(dataobject);
+ for (int i=0;i<array_list_length(arraylist);i++)
+ {
+ 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");
+ json_object *propobject = json_object_object_get(arrayobject,"property");
+ json_object *valueobject = json_object_object_get(arrayobject,"value");
+ json_object *zoneobject = json_object_object_get(arrayobject,"zone");
+ json_object *sourceobject = json_object_object_get(arrayobject,"source");
+ string interfacestr = string(interfaceobject ? json_object_get_string(interfaceobject) : "vcan0");
+ string keystr = string(propobject ? json_object_get_string(propobject) : "");
+ string valuestr = string(valueobject ? json_object_get_string(valueobject): "");
+ string sourcestr = string(sourceobject ? json_object_get_string(sourceobject): "");
+ Zone::Type z(Zone::None);
+ if(zoneobject){
+ try {
+ z = static_cast<Zone::Type>(boost::lexical_cast<int,std::string>(json_object_get_string(zoneobject)));
+ } catch (...) { }
+ }
+ propertyData.push_back(make_tuple(interfacestr, keystr, valuestr, z, sourcestr));
+ }
+ else if (json_object_get_type(arrayobject) == json_type_string)
+ {
+ string propertyName = string(json_object_get_string(arrayobject));
+ propertyNames.push_back(propertyName);
+ }
+ }
+ //array_list_free(arraylist);
+ }
+ else
+ {
+ string path = json_object_get_string(dataobject);
+ if (path != "")
+ {
+ propertyNames.push_back(path);
+ }
+ }
+ if (type == "method")
+ {
+ if (name == "get")
+ {
+ 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);
+ }
+ }
+ else if (name == "set")
+ {
+ LOG_MESSAGE("set called");
+ if (!propertyNames.empty())
+ {
+ //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);
+ }
+ }
+ }
+ 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())
+ {
+ //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 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;
+ }
+ }
+ }
+}