void WebSocketSinkManager::addSink(libwebsocket* socket, VehicleProperty::Property property,string uuid) { PropertyList foo = VehicleProperty::capabilities(); if (!ListPlusPlus<VehicleProperty::Property>(&foo).contains(property)) { DebugOut(DebugOut::Warning)<<"Invalid property requested: "<<property<<endl; return; } QVariantMap reply; reply["type"] = "methodReply"; reply["name"] = "subscribe"; reply["data"] = property.c_str(); reply["transactionid"] = uuid.c_str(); QByteArray replystr; if(doBinary) replystr = QJsonDocument::fromVariant(reply).toBinaryData(); else { replystr = QJsonDocument::fromVariant(reply).toJson(); cleanJson(replystr); } lwsWrite(socket, replystr, replystr.length()); WebSocketSink *sink = new WebSocketSink(m_engine,socket,uuid,property,property); m_sinkMap[property].push_back(sink); }
void WebSocketSinkManager::removeSink(libwebsocket* socket,VehicleProperty::Property property, string uuid) { if (m_sinkMap.find(property) != m_sinkMap.end()) { list<WebSocketSink*> sinks = m_sinkMap[property]; for(auto i = sinks.begin(); i != sinks.end(); i++) { delete *i; } m_sinkMap.erase(property); QVariantMap reply; reply["type"]="methodReply"; reply["name"]="unsubscribe"; reply["data"]=property.c_str(); reply["transactionid"]= uuid.c_str(); QByteArray replystr; if(doBinary) replystr = QJsonDocument::fromVariant(reply).toBinaryData(); else { replystr = QJsonDocument::fromVariant(reply).toJson(); cleanJson(replystr); } lwsWrite(socket, replystr, replystr.length()); } }
void WebSocketSinkManager::addSingleShotSink(libwebsocket* socket, VehicleProperty::Property property, Zone::Type zone, string id) { AsyncPropertyRequest request; PropertyList foo = VehicleProperty::capabilities(); if (ListPlusPlus<VehicleProperty::Property>(&foo).contains(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->success || !reply->value) { DebugOut()<<"Property value is null"<<endl; delete reply; return; } QVariantMap data; data["property"] = property.c_str(); data["zone"] = reply->value->zone; data["value"] = reply->value->toString().c_str(); data["timestamp"] = reply->value->timestamp; data["sequence"] = reply->value->sequence; QVariantMap replyvar; replyvar["type"]="methodReply"; replyvar["name"]="get"; replyvar["data"]= data; replyvar["transactionid"]=id.c_str(); QByteArray replystr; if(doBinary) replystr = QJsonDocument::fromVariant(replyvar).toBinaryData(); else { replystr = QJsonDocument::fromVariant(replyvar).toJson(); cleanJson(replystr); } lwsWrite(socket, replystr, replystr.length()); delete reply; }; AsyncPropertyReply* reply = routingEngine->getPropertyAsync(request); }
int lwsWriteVariant(libwebsocket *lws, QVariant d) { QByteArray replystr; if(doBinary) replystr = QJsonDocument::fromVariant(d).toBinaryData(); else { replystr = QJsonDocument::fromVariant(d).toJson(); cleanJson(replystr); } lwsWrite(lws, replystr); }
void WebSocketSinkManager::addSingleShotRangedSink(libwebsocket* socket, PropertyList properties, double start, double end, double seqstart,double seqend, string id) { AsyncRangePropertyRequest rangedRequest; rangedRequest.timeBegin = start; rangedRequest.timeEnd = end; rangedRequest.sequenceBegin = seqstart; rangedRequest.sequenceEnd = seqend; rangedRequest.completed = [socket,id](AsyncRangePropertyReply* reply) { QVariantMap replyvar; QVariantList list; std::list<AbstractPropertyType*> values = reply->values; for(auto itr = values.begin(); itr != values.end(); itr++) { QVariantMap obj; obj["value"]= (*itr)->toString().c_str(); obj["timestamp"] = (*itr)->timestamp; obj["sequence"] = (*itr)->sequence; list.append(obj); } replyvar["type"]="methodReply"; replyvar["name"]="getRanged"; replyvar["data"]=list; replyvar["transactionid"]=id.c_str(); QByteArray replystr; if(doBinary) replystr = QJsonDocument::fromVariant(replyvar).toBinaryData(); else { replystr = QJsonDocument::fromVariant(replyvar).toJson(); cleanJson(replystr); } lwsWrite(socket, replystr, replystr.length()); delete reply; }; AsyncRangePropertyReply* reply = routingEngine->getRangePropertyAsync(rangedRequest); }
void WebSocketSinkManager::setValue(libwebsocket* socket,VehicleProperty::Property property,string value,Zone::Type zone,string uuid) { AbstractPropertyType* type = VehicleProperty::getPropertyTypeForPropertyNameValue(property,value); AsyncSetPropertyRequest request; request.property = property; request.value = type; request.zoneFilter = zone; request.completed = [&](AsyncPropertyReply* reply) { QVariantMap data; data["property"] = property.c_str(); data["zone"] = zone; data["source"] = reply->value->sourceUuid.c_str(); QVariantMap replyvar; replyvar["type"]="methodReply"; replyvar["name"]="set"; replyvar["data"]= data; replyvar["transactionid"]=uuid.c_str(); QByteArray replystr; if(doBinary) replystr = QJsonDocument::fromVariant(replyvar).toBinaryData(); else { replystr = QJsonDocument::fromVariant(replyvar).toJson(); cleanJson(replystr); } lwsWrite(socket, replystr, replystr.length()); delete reply; }; m_engine->setProperty(request); DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "AbstractRoutingEngine::setProperty called with arguments:" << property << value << "\n"; delete type; }
int lwsWrite(libwebsocket *lws, QByteArray d, int len) { if(!lws) { DebugOut(DebugOut::Error)<<__FUNCTION__<<": libwebsockets is not valid. Perhaps it has not been initialized?"<<endl; return -1; } int retval = -1; QByteArray temp = d; int numframes = 1; int framesize = 122; if(d.length() > framesize) { numframes = qCeil((double)d.length() / 122.0); QVariantMap multiFrameMessage; multiFrameMessage["type"] = "multiframe"; multiFrameMessage["frames"] = numframes; QByteArray msg; if(doBinary) msg = QJsonDocument::fromVariant(multiFrameMessage).toBinaryData(); else { msg = QJsonDocument::fromVariant(multiFrameMessage).toJson(); cleanJson(msg); } lwsWrite(lws, msg, msg.length()); } while(numframes--) { int range = 0; if(temp.length() > framesize) range = framesize; else range = temp.length(); QByteArray toWrite = temp.mid(0,range); const char* strToWrite = toWrite.data(); temp = temp.mid(range); if(doBinary) { retval = libwebsocket_write(lws, (unsigned char*)strToWrite, toWrite.length(), LWS_WRITE_BINARY); } else { std::unique_ptr<char[]> buffer(new char[LWS_SEND_BUFFER_PRE_PADDING + len + LWS_SEND_BUFFER_POST_PADDING]); char *buf = buffer.get() + LWS_SEND_BUFFER_PRE_PADDING; memcpy(buf, strToWrite, toWrite.length()); retval = libwebsocket_write(lws, (unsigned char*)strToWrite, toWrite.length(), LWS_WRITE_TEXT); } } return retval; }
static int websocket_callback(struct libwebsocket_context *context,struct libwebsocket *wsi,enum libwebsocket_callback_reasons reason, void *user,void *in, size_t len) { //printf("Switch: %i\n",reason); DebugOut(5) << __SMALLFILE__ << ":" << __LINE__ << "websocket_callback:" << reason << endl; switch (reason) { case LWS_CALLBACK_CLIENT_WRITEABLE: { break; } case LWS_CALLBACK_CLOSED: { sinkManager->disconnectAll(wsi); break; } case LWS_CALLBACK_CLIENT_RECEIVE: { break; } case LWS_CALLBACK_SERVER_WRITEABLE: { break; } case LWS_CALLBACK_RECEIVE: { } case LWS_CALLBACK_HTTP: { //TODO: Verify that ALL requests get sent via LWS_CALLBACK_HTTP, so we can use that instead of LWS_CALLBACK_RECIEVE //TODO: Do we want exceptions, or just to return an invalid json reply? Probably an invalid json reply. DebugOut() << __SMALLFILE__ << ":" << __LINE__ << " Requested: " << (char*)in << "\n"; QByteArray d((char*)in,len); WebSocketSinkManager * manager = sinkManager; if(manager->expectedMessageFrames && manager->partialMessageIndex < manager->expectedMessageFrames) { manager->incompleteMessage += d; manager->partialMessageIndex++; break; } else if(manager->expectedMessageFrames && manager->partialMessageIndex == manager->expectedMessageFrames) { d = manager->incompleteMessage + d; manager->expectedMessageFrames = 0; } QJsonDocument doc; if(doBinary) doc = QJsonDocument::fromBinaryData(d); else doc = QJsonDocument::fromJson(d); if(doc.isNull()) { DebugOut(DebugOut::Error)<<"Invalid message"<<endl; return 0; } QVariantMap call = doc.toVariant().toMap(); string type = call["type"].toString().toStdString(); string name = call["name"].toString().toStdString(); string id = call["transactionid"].toString().toStdString(); if (type == "multiframe") { manager->expectedMessageFrames = call["frames"].toInt(); manager->partialMessageIndex = 1; manager->incompleteMessage = ""; } else if (type == "method") { if(name == "getRanged") { QVariantMap data = call["data"].toMap(); PropertyList propertyList; propertyList.push_back(data["property"].toString().toStdString()); double timeBegin = data["timeBegin"].toDouble(); double timeEnd = data["timeEnd"].toDouble(); double sequenceBegin = data["sequenceBegin"].toInt(); double sequenceEnd = data["sequenceEnd"].toInt(); if ((timeBegin < 0 && timeEnd > 0) || (timeBegin > 0 && timeEnd < 0)) { DebugOut(DebugOut::Warning)<<"Invalid time begin/end pair"<<endl; } else if ((sequenceBegin < 0 && sequenceEnd > 0) || (sequenceBegin > 0 && sequenceEnd < 0)) { DebugOut(DebugOut::Warning)<<"Invalid sequence begin/end pair"<<endl; } else { sinkManager->addSingleShotRangedSink(wsi,propertyList,timeBegin,timeEnd,sequenceBegin,sequenceEnd,id); } } else if (name == "get") { QVariantMap data = call["data"].toMap(); Zone::Type zone = Zone::None; if(data.contains("zone")) { zone = data["zone"].toInt(); } sinkManager->addSingleShotSink(wsi,data["property"].toString().toStdString(),zone,id); } else if (name == "set") { QVariantMap data = call["data"].toMap(); Zone::Type zone(Zone::None); if(data.contains("zone")) { zone = data["zone"].toInt(); } sinkManager->setValue(wsi,data["property"].toString().toStdString(), data["value"].toString().toStdString(), zone, id); } else if (name == "subscribe") { std::string data = call["data"].toString().toStdString(); sinkManager->addSink(wsi, data, id); } else if (name == "unsubscribe") { std::string data = call["data"].toString().toStdString(); sinkManager->removeSink(wsi,data,id); } else if (name == "getSupportedEventTypes") { QVariantMap reply; QStringList list; PropertyList supported = sinkManager->getSupportedProperties(); for(VehicleProperty::Property i : supported) { list.append(i.c_str()); } reply["type"] = "methodReply"; reply["name"] = "getSupportedEventTypes"; reply["transactionid"] = id.c_str(); reply["data"] = list; QByteArray replystr; if(doBinary) replystr = QJsonDocument::fromVariant(reply).toBinaryData(); else { replystr = QJsonDocument::fromVariant(reply).toJson(); cleanJson(replystr); } lwsWrite(wsi, replystr, replystr.length()); } else { DebugOut(0)<<"Unknown method called."<<endl; } } break; } case LWS_CALLBACK_ADD_POLL_FD: { //printf("Adding poll %i\n",sinkManager); DebugOut(5) << __SMALLFILE__ <<":"<< __LINE__ << "Adding poll" << endl; if (sinkManager != 0) { //sinkManager->addPoll((int)(long)user); sinkManager->addPoll(libwebsocket_get_socket_fd(wsi)); } else { DebugOut(5) << "Error, invalid sink manager!!" << endl; } break; } case LWS_CALLBACK_DEL_POLL_FD: { sinkManager->removePoll(libwebsocket_get_socket_fd(wsi)); break; } case LWS_CALLBACK_SET_MODE_POLL_FD: { //Set the poll mode break; } case LWS_CALLBACK_CLEAR_MODE_POLL_FD: { //Don't handle this yet. break; } default: { //printf("Unhandled callback: %i\n",reason); DebugOut() << __SMALLFILE__ <<":"<< __LINE__ << "Unhandled callback:" << reason << "\n"; break; } } return 0; }