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