reverted varianttype
[profile/ivi/automotive-message-broker.git] / plugins / bluemonkey / bluemonkey.cpp
1 /*
2   Copyright (C) 2012  Intel Corporation
3
4   This library is free software; you can redistribute it and/or
5   modify it under the terms of the GNU Lesser General Public
6   License as published by the Free Software Foundation; either
7   version 2.1 of the License, or (at your option) any later version.
8
9   This library is distributed in the hope that it will be useful,
10   but WITHOUT ANY WARRANTY; without even the implied warranty of
11   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12   Lesser General Public License for more details.
13
14   You should have received a copy of the GNU Lesser General Public
15   License along with this library; if not, write to the Free Software
16   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17 */
18
19
20 #include "bluemonkey.h"
21 #include "abstractroutingengine.h"
22 #include "ambplugin.h"
23 #include "debugout.h"
24
25 #include <QJsonDocument>
26 #include <QJSEngine>
27 #include <QDateTime>
28 #include <QString>
29 #include <QFile>
30 #include <QTimer>
31 #include <QtQml>
32
33 #include <dlfcn.h>
34
35 #define foreach Q_FOREACH
36
37 typedef std::map<std::string, QObject*> create_bluemonkey_module_t(std::map<std::string, std::string> config, QObject* parent);
38
39 extern "C" void create(AbstractRoutingEngine* routingengine, map<string, string> config)
40 {
41         auto plugin = new AmbPlugin<BluemonkeySink>(routingengine, config);
42         plugin->init();
43 }
44
45 QVariant gvariantToQVariant(GVariant *value)
46 {
47         GVariantClass c = g_variant_classify(value);
48         if(c == G_VARIANT_CLASS_BOOLEAN)
49                 return QVariant((bool) g_variant_get_boolean(value));
50
51         else if(c == G_VARIANT_CLASS_BYTE)
52                 return QVariant((char) g_variant_get_byte(value));
53
54         else if(c == G_VARIANT_CLASS_INT16)
55                 return QVariant((int) g_variant_get_int16(value));
56
57         else if(c == G_VARIANT_CLASS_UINT16)
58                 return QVariant((unsigned int) g_variant_get_uint16(value));
59
60         else if(c == G_VARIANT_CLASS_INT32)
61                 return QVariant((int) g_variant_get_int32(value));
62
63         else if(c ==  G_VARIANT_CLASS_UINT32)
64                 return QVariant((unsigned int) g_variant_get_uint32(value));
65
66         else if(c == G_VARIANT_CLASS_INT64)
67                 return QVariant((long long) g_variant_get_int64(value));
68
69         else if(c == G_VARIANT_CLASS_UINT64)
70                 return QVariant((unsigned long long) g_variant_get_uint64(value));
71
72         else if(c == G_VARIANT_CLASS_DOUBLE)
73                 return QVariant(g_variant_get_double(value));
74
75         else if(c == G_VARIANT_CLASS_STRING)
76                 return QVariant(g_variant_get_string(value, NULL));
77
78         else if(c == G_VARIANT_CLASS_ARRAY)
79         {
80                 gsize dictsize = g_variant_n_children(value);
81                 QVariantList list;
82                 for (int i=0; i<dictsize; i++)
83                 {
84                         GVariant *childvariant = g_variant_get_child_value(value, i);
85                         GVariant *innervariant = g_variant_get_variant(childvariant);
86                         list.append(gvariantToQVariant(innervariant));
87                 }
88                 return list;
89         }
90
91         else
92                 return QVariant::Invalid;
93
94 }
95
96 AbstractPropertyType* qVariantToAbstractPropertyType(QString name, QVariant var)
97 {
98         if(!var.isValid())
99                 return nullptr;
100
101         if(var.type() == QVariant::UInt)
102                 return new BasicPropertyType<uint>(name.toStdString(), var.toUInt());
103         else if(var.type() == QVariant::Double)
104                 return new BasicPropertyType<double>(name.toStdString(), var.toDouble());
105         else if(var.type() == QVariant::Bool)
106                 return new BasicPropertyType<bool>(name.toStdString(), var.toBool());
107         else if(var.type() == QVariant::Int)
108                 return new BasicPropertyType<int>(name.toStdString(), var.toInt());
109         else if(var.type() == QVariant::String)
110                 return new StringPropertyType(name.toStdString(), var.toString().toStdString());
111         else if(var.type() == QVariant::List && var.toList().count())
112         {
113                 QVariant subVariant = var.toList().at(0);
114                 if(subVariant.type() == QVariant::UInt)
115                         return new ListPropertyType<BasicPropertyType<uint>>(name.toStdString(), subVariant.toUInt());
116                 else if(subVariant.type() == QVariant::Double)
117                         return new ListPropertyType<BasicPropertyType<double>>(name.toStdString(), subVariant.toDouble());
118                 else if(subVariant.type() == QVariant::Bool)
119                         return new ListPropertyType<BasicPropertyType<bool>>(name.toStdString(), subVariant.toBool());
120                 else if(subVariant.type() == QVariant::Int)
121                         return new ListPropertyType<BasicPropertyType<int>>(name.toStdString(), subVariant.toInt());
122                 else if(subVariant.type() == QVariant::String)
123                         return new ListPropertyType<StringPropertyType>(name.toStdString(), subVariant.toString().toStdString());
124         }
125         return nullptr;
126 }
127
128 QVariant toQVariant(AbstractPropertyType* val)
129 {
130         QVariantMap value;
131
132         value["name"] = val->name.c_str();
133         value["zone"] = val->zone;
134         value["source"] = val->sourceUuid.c_str();
135         value["timestamp"] = val->timestamp;
136         value["sequence"] = val->sequence;
137         value["value"] = gvariantToQVariant(val->toVariant());
138
139         return value;
140 }
141
142 BluemonkeySink::BluemonkeySink(AbstractRoutingEngine* e, map<string, string> config, AbstractSource &parent)
143         : QObject(0), AmbPluginImpl(e, config, parent), engine(nullptr), mSilentMode(false)
144 {
145         QTimer::singleShot(1,this,SLOT(reloadEngine()));
146
147         auth = new Authenticate(config, this);
148 }
149
150 BluemonkeySink::~BluemonkeySink()
151 {
152         Q_FOREACH(void* module, modules)
153         {
154                 dlclose(module);
155         }
156 }
157
158
159 PropertyList BluemonkeySink::subscriptions()
160 {
161
162 }
163
164 void BluemonkeySink::supportedChanged(const PropertyList & supportedProperties)
165 {
166         DebugOut()<<"supported changed"<<endl;
167 }
168
169 void BluemonkeySink::propertyChanged(AbstractPropertyType * value)
170 {
171
172 }
173
174 const string BluemonkeySink::uuid() const
175 {
176         return "bluemonkey";
177 }
178
179 int BluemonkeySink::supportedOperations()
180 {
181         return AbstractSource::Get | AbstractSource::Set;
182 }
183
184 QObject *BluemonkeySink::subscribeTo(QString str)
185 {
186         return new Property(str.toStdString(), "", routingEngine, Zone::None, this);
187 }
188
189 QObject *BluemonkeySink::subscribeTo(QString str, int zone, QString srcFilter)
190 {
191         return new Property(str.toStdString(), srcFilter, routingEngine, zone, this);
192 }
193
194 QObject* BluemonkeySink::subscribeTo(QString str, int zone)
195 {
196         return new Property(str.toStdString(), "", routingEngine, zone, this);
197 }
198
199
200 QStringList BluemonkeySink::sourcesForProperty(QString property)
201 {
202         std::vector<std::string> list = routingEngine->sourcesForProperty(property.toStdString());
203         QStringList strList;
204         for(auto itr : list)
205         {
206                 strList<<itr.c_str();
207         }
208
209         return strList;
210 }
211
212 QStringList BluemonkeySink::supportedProperties()
213 {
214         PropertyList props = routingEngine->supported();
215         QStringList strList;
216         for(auto p : props)
217         {
218                 strList<<p.c_str();
219         }
220
221         return strList;
222 }
223
224
225 bool BluemonkeySink::authenticate(QString pass)
226 {
227
228 }
229
230 void BluemonkeySink::loadConfig(QString str)
231 {
232         QFile file(str);
233         if(!file.open(QIODevice::ReadOnly))
234         {
235                 DebugOut(DebugOut::Error)<<"failed to open config file: "<<str.toStdString()<<endl;
236                 return;
237         }
238
239         QString script = file.readAll();
240
241         file.close();
242
243         DebugOut()<<"evaluating script: "<<script.toStdString()<<endl;
244
245         QJSValue val = engine->evaluate(script);
246
247         DebugOut()<<val.toString().toStdString()<<endl;
248
249         if(val.isError())
250         {
251                 DebugOut(DebugOut::Error) << val.property("name").toString().toStdString() << endl;
252                 DebugOut(DebugOut::Error) << val.property("message").toString().toStdString() << endl;
253                 DebugOut(DebugOut::Error) << str.toStdString() << ":" <<val.property("lineNumber").toString().toStdString() << endl;
254         }
255 }
256
257 bool BluemonkeySink::loadModule(QString path)
258 {
259         void* handle = dlopen(path.toUtf8().data(), RTLD_LAZY);
260
261         if(!handle)
262         {
263                 DebugOut(DebugOut::Warning) << "bluemonkey load module failed: " << dlerror() << endl;
264                 return false;
265         }
266
267         if(modules.contains(handle))
268                 return false;
269
270         modules.push_back(handle);
271
272         void* c = dlsym(handle, "create");
273
274         if(!c)
275         {
276                 DebugOut(DebugOut::Warning) << "bluemonkey load module failed: " << path.toStdString() << " " << dlerror() << endl;
277                 return false;
278         }
279
280         create_bluemonkey_module_t* create = (create_bluemonkey_module_t*)(c);
281
282         std::map<std::string, QObject*> exports = create(configuration, this);
283
284         for(auto i : exports)
285         {
286                 std::string obj = i.first;
287                 if(!engine->globalObject().hasProperty(obj.c_str()))
288                 {
289                         QJSValue val = engine->newQObject(i.second);
290                         engine->globalObject().setProperty(obj.c_str(), val);
291                 }
292         }
293
294         return true;
295 }
296
297 void BluemonkeySink::reloadEngine()
298 {
299         if(engine)
300                 engine->deleteLater();
301
302         engine = new QJSEngine(this);
303
304         QJSValue value = engine->newQObject(this);
305         engine->globalObject().setProperty("bluemonkey", value);
306
307         loadConfig(configuration["config"].c_str());
308 }
309
310 void BluemonkeySink::writeProgram(QString program)
311 {
312
313         QJSEngine temp;
314         QJSValue result = temp.evaluate(program);
315         if(result.isError())
316         {
317                 DebugOut(DebugOut::Error)<<"Syntax error in program: "<<result.toString().toStdString()<<endl;
318                 return;
319         }
320
321         QFile file(configuration["customPrograms"].c_str());
322
323         if(!file.open(QIODevice::ReadWrite | QIODevice::Append))
324         {
325                 DebugOut(DebugOut::Error)<<"failed to open file: "<<file.fileName().toStdString()<<endl;
326                 return;
327         }
328
329         file.write(program.toUtf8());
330         file.write("\n");
331
332         file.close();
333 }
334
335 void BluemonkeySink::log(QString str)
336 {
337         DebugOut()<<str.toStdString()<<endl;
338 }
339
340 QObject *BluemonkeySink::createTimer()
341 {
342         return new QTimer(this);
343 }
344
345 QObject *BluemonkeySink::createQObject()
346 {
347         return new QObject(this);
348 }
349
350 void BluemonkeySink::getHistory(QStringList properties, QDateTime begin, QDateTime end, QJSValue cbFunction)
351 {
352         double b = (double)begin.toMSecsSinceEpoch() / 1000.0;
353         double e = (double)end.toMSecsSinceEpoch() / 1000.0;
354         AsyncRangePropertyRequest request;
355         request.timeBegin = b;
356         request.timeEnd = e;
357
358         PropertyList reqlist;
359
360         foreach(QString prop, properties)
361         {
362                 reqlist.push_back(prop.toStdString());
363         }
364
365         request.properties = reqlist;
366         request.completed = [&cbFunction](AsyncRangePropertyReply* reply)
367         {
368                 if(!reply->success)
369                 {
370                         DebugOut(DebugOut::Error)<<"bluemoney get history call failed"<<endl;
371                         return;
372                 }
373
374                 if(cbFunction.isCallable())
375                 {
376                         QVariantList list;
377
378                         for(auto val : reply->values)
379                         {
380                                 list.append(toQVariant(val));
381                         }
382
383                         QJSValue val = cbFunction.engine()->toScriptValue<QVariantList>(list);
384
385                         cbFunction.call(QJSValueList()<<val);
386
387                 }
388
389                 delete reply;
390         };
391
392         routingEngine->getRangePropertyAsync(request);
393 }
394
395 void BluemonkeySink::createCustomProperty(QString name, QJSValue defaultValue, int zone)
396 {
397         QVariant var = defaultValue.toVariant();
398
399         DebugOut() << "Variant value for: " << name.toStdString() << " is " << defaultValue.toString().toStdString() << endl;
400
401         auto create = [name, var]() -> AbstractPropertyType*
402         {
403                 return qVariantToAbstractPropertyType(name, var);
404         };
405
406         addPropertySupport(zone, create);
407
408         AsyncSetPropertyRequest request;
409         request.property = name.toStdString();
410         request.zoneFilter = zone;
411         request.value = VehicleProperty::getPropertyTypeForPropertyNameValue(name.toStdString(), defaultValue.toString().toStdString());
412
413         routingEngine->updateSupported(supported(), PropertyList(), &source);
414         routingEngine->setProperty(request);
415 }
416
417
418 QVariant Property::value()
419 {
420         return mValue ? gvariantToQVariant(mValue->toVariant()) : QVariant::Invalid;
421 }
422
423 void Property::setValue(QVariant v)
424 {
425         if(v.type() == QVariant::List || v.type() == QVariant::Map)
426         {
427
428                 QJsonDocument doc = QJsonDocument::fromVariant(v);
429
430                 QString json = doc.toJson();
431
432                 mValue->fromString(json.toStdString());
433         }
434         else
435         {
436                 QString tempVal = v.toString();
437                 mValue->fromString(tempVal.toStdString());
438         }
439
440         AsyncSetPropertyRequest request;
441         request.property = mValue->name;
442         request.value = mValue->copy();
443         request.completed = [&](AsyncPropertyReply* reply)
444         {
445                 if(reply->success)
446                 {
447                         propertyChanged(reply->value);
448                 }
449                 else
450                 {
451                         DebugOut(DebugOut::Warning) << "Error, trying to set value: " << reply->error << endl;
452                 }
453                 delete reply;
454         };
455         routingEngine->setProperty(request);
456 }
457
458 void Property::getHistory(QDateTime begin, QDateTime end, QJSValue cbFunction)
459 {
460         double b = (double)begin.toMSecsSinceEpoch() / 1000.0;
461         double e = (double)end.toMSecsSinceEpoch() / 1000.0;
462         AsyncRangePropertyRequest request;
463         request.timeBegin = b;
464         request.timeEnd = e;
465
466         PropertyList reqlist;
467         reqlist.push_back(mValue->name);
468
469         request.properties = reqlist;
470         request.completed = [&cbFunction](AsyncRangePropertyReply* reply)
471         {
472                 if(!reply->success)
473                 {
474                         DebugOut(DebugOut::Error)<<"bluemoney get history call failed"<<endl;
475                         return;
476                 }
477
478                 if(cbFunction.isCallable())
479                 {
480                         QVariantList list;
481
482                         for(auto val : reply->values)
483                         {
484                                 list.append(toQVariant(val));
485                         }
486
487                         QJSValue val = cbFunction.engine()->toScriptValue<QVariantList>(list);
488                         cbFunction.call(QJSValueList()<<val);
489
490                 }
491
492                 delete reply;
493         };
494
495         routingEngine->getRangePropertyAsync(request);
496 }
497
498 Property::Property(VehicleProperty::Property prop, QString srcFilter, AbstractRoutingEngine* re, Zone::Type zone, QObject *parent)
499         :QObject(parent), AbstractSink(re, std::map<std::string,std::string>()),mValue(nullptr), mUuid(amb::createUuid()), mZone(zone)
500 {
501         setType(prop.c_str());
502 }
503
504 QString Property::name()
505 {
506         return mValue->name.c_str();
507 }
508
509 void Property::setType(QString t)
510 {
511         if(mValue && name() != "")
512                 routingEngine->unsubscribeToProperty(name().toStdString(), this);
513
514         routingEngine->subscribeToProperty(t.toStdString(), this);
515
516         mValue = VehicleProperty::getPropertyTypeForPropertyNameValue(t.toStdString());
517
518         if(!mValue)
519                 return;
520
521         AsyncPropertyRequest request;
522         request.property = mValue->name;
523         request.completed = [this](AsyncPropertyReply* reply)
524         {
525                 if(reply->success)
526                         propertyChanged(reply->value);
527
528                 delete reply;
529         };
530
531         routingEngine->getPropertyAsync(request);
532 }
533
534 void Property::propertyChanged(AbstractPropertyType *value)
535 {
536         if(value->zone != mZone)
537                 return;
538
539         if(mValue)
540         {
541                 delete mValue;
542         }
543         mValue = value->copy();
544
545         changed(gvariantToQVariant(mValue->toVariant()));
546 }
547
548
549 QVariant BluemonkeySink::zonesForProperty(QString prop, QString src)
550 {
551         PropertyInfo info = routingEngine->getPropertyInfo(prop.toStdString(), src.toStdString());
552
553         QVariantList list;
554
555         for(auto i : info.zones())
556         {
557                 list << i;
558         }
559
560         return list;
561 }