632f26f7e0c8240bd960cbe96b24e5e71e1cb2ee
[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 BluemonkeySink::BluemonkeySink(AbstractRoutingEngine* e, map<string, string> config, AbstractSource &parent): QObject(0), AmbPluginImpl(e, config, parent), engine(nullptr), mSilentMode(false)
99 {
100         QTimer::singleShot(1,this,SLOT(reloadEngine()));
101
102         auth = new Authenticate(config, this);
103
104         qmlRegisterType<QTimer>("", 1, 0, "QTimer");
105 }
106
107
108 PropertyList BluemonkeySink::subscriptions()
109 {
110
111 }
112
113 void BluemonkeySink::supportedChanged(const PropertyList & supportedProperties)
114 {
115         DebugOut()<<"supported changed"<<endl;
116 }
117
118 void BluemonkeySink::propertyChanged(AbstractPropertyType * value)
119 {
120
121 }
122
123 const string BluemonkeySink::uuid() const
124 {
125         return "bluemonkey";
126 }
127
128 int BluemonkeySink::supportedOperations()
129 {
130         return AbstractSource::Get | AbstractSource::Set;
131 }
132
133 QObject *BluemonkeySink::subscribeTo(QString str)
134 {
135         return new Property(str.toStdString(), "", routingEngine, Zone::None, this);
136 }
137
138 QObject *BluemonkeySink::subscribeToSource(QString str, QString srcFilter)
139 {
140         return new Property(str.toStdString(), srcFilter, routingEngine, Zone::None, this);
141 }
142
143 QObject* BluemonkeySink::subscribeToZone(QString str, int zone)
144 {
145         return new Property(str.toStdString(), "", routingEngine, zone, this);
146 }
147
148
149 QStringList BluemonkeySink::sourcesForProperty(QString property)
150 {
151         std::list<std::string> list = routingEngine->sourcesForProperty(property.toStdString());
152         QStringList strList;
153         for(auto itr = list.begin(); itr != list.end(); itr++)
154         {
155                 strList<<(*itr).c_str();
156         }
157
158         return strList;
159 }
160
161 QStringList BluemonkeySink::supportedProperties()
162 {
163         PropertyList props = routingEngine->supported();
164         QStringList strList;
165         for(auto p : props)
166         {
167                 strList<<p.c_str();
168         }
169
170         return strList;
171 }
172
173
174 bool BluemonkeySink::authenticate(QString pass)
175 {
176
177 }
178
179 void BluemonkeySink::loadConfig(QString str)
180 {
181         QFile file(str);
182         if(!file.open(QIODevice::ReadOnly))
183         {
184                 DebugOut(DebugOut::Error)<<"failed to open config file: "<<str.toStdString()<<endl;
185                 return;
186         }
187
188         QString script = file.readAll();
189
190         file.close();
191
192         DebugOut()<<"evaluating script: "<<script.toStdString()<<endl;
193
194         QJSValue val = engine->evaluate(script);
195
196         DebugOut()<<val.toString().toStdString()<<endl;
197 }
198
199 bool BluemonkeySink::loadModule(QString path)
200 {
201         void* handle = dlopen(path.toUtf8().data(), RTLD_LAZY);
202
203         if(!handle)
204         {
205                 DebugOut(DebugOut::Warning) << "bluemonkey load module failed: " << dlerror() << endl;
206                 return false;
207         }
208
209         void* c = dlsym(handle, "create");
210
211         if(!c)
212         {
213                 DebugOut(DebugOut::Warning) << "bluemonkey load module failed: " << path.toStdString() << " " << dlerror() << endl;
214                 return false;
215         }
216
217         create_bluemonkey_module_t* create = (create_bluemonkey_module_t*)(c);
218
219         std::map<std::string, QObject*> exports = create(configuration, this);
220
221         for(auto i : exports)
222         {
223                 QJSValue val = engine->newQObject(i.second);
224                 engine->globalObject().setProperty(i.first.c_str(), val);
225         }
226
227         return true;
228 }
229
230 void BluemonkeySink::reloadEngine()
231 {
232         if(engine)
233                 engine->deleteLater();
234
235         engine = new QJSEngine(this);
236
237         QJSValue value = engine->newQObject(this);
238         engine->globalObject().setProperty("bluemonkey", value);
239
240         loadConfig(configuration["config"].c_str());
241 }
242
243 void BluemonkeySink::writeProgram(QString program)
244 {
245
246         QJSEngine temp;
247         QJSValue result = temp.evaluate(program);
248         if(result.isError())
249         {
250                 DebugOut(DebugOut::Error)<<"Syntax error in program: "<<result.toString().toStdString()<<endl;
251                 return;
252         }
253
254         QFile file(configuration["customPrograms"].c_str());
255
256         if(!file.open(QIODevice::ReadWrite | QIODevice::Append))
257         {
258                 DebugOut(DebugOut::Error)<<"failed to open file: "<<file.fileName().toStdString()<<endl;
259                 return;
260         }
261
262         file.write(program.toUtf8());
263         file.write("\n");
264
265         file.close();
266 }
267
268 void BluemonkeySink::log(QString str)
269 {
270         DebugOut()<<str.toStdString()<<endl;
271 }
272
273 QObject *BluemonkeySink::createTimer()
274 {
275         return new QTimer(this);
276 }
277
278 void BluemonkeySink::getHistory(QStringList properties, QDateTime begin, QDateTime end, QJSValue cbFunction)
279 {
280         double b = (double)begin.toMSecsSinceEpoch() / 1000.0;
281         double e = (double)end.toMSecsSinceEpoch() / 1000.0;
282         AsyncRangePropertyRequest request;
283         request.timeBegin = b;
284         request.timeEnd = e;
285
286         PropertyList reqlist;
287
288         foreach(QString prop, properties)
289         {
290                 reqlist.push_back(prop.toStdString());
291         }
292
293         request.properties = reqlist;
294         request.completed = [&cbFunction](AsyncRangePropertyReply* reply)
295         {
296                 if(!reply->success)
297                 {
298                         DebugOut(DebugOut::Error)<<"bluemoney get history call failed"<<endl;
299                         return;
300                 }
301
302                 if(cbFunction.isCallable())
303                 {
304                         QVariantList list;
305
306                         for(auto itr = reply->values.begin(); itr != reply->values.end(); itr++)
307                         {
308                                 AbstractPropertyType *val = *itr;
309
310                                 list.append(gvariantToQVariant(val->toVariant()));
311                         }
312
313                         QJSValue val = cbFunction.engine()->toScriptValue<QVariantList>(list);
314
315                         cbFunction.call(QJSValueList()<<val);
316
317                 }
318
319                 delete reply;
320         };
321
322         routingEngine->getRangePropertyAsync(request);
323 }
324
325 void BluemonkeySink::createCustomProperty(QString name, QJSValue defaultValue, int zone)
326 {
327         QVariant var = defaultValue.toVariant();
328
329         auto create = [defaultValue, name, var]() -> AbstractPropertyType*
330         {
331                 if(!var.isValid())
332                         return nullptr;
333
334                 if(var.type() == QVariant::UInt)
335                         return new BasicPropertyType<uint>(name.toStdString(), var.toUInt());
336                 else if(var.type() == QVariant::Double)
337                         return new BasicPropertyType<double>(name.toStdString(), var.toDouble());
338                 else if(var.type() == QVariant::Bool)
339                         return new BasicPropertyType<bool>(name.toStdString(), var.toBool());
340                 else if(var.type() == QVariant::Int)
341                         return new BasicPropertyType<int>(name.toStdString(), var.toInt());
342                 else if(var.type() == QVariant::String)
343                         return new StringPropertyType(name.toStdString(), var.toString().toStdString());
344
345                 return nullptr;
346         };
347
348         addPropertySupport(zone, create);
349
350         AsyncSetPropertyRequest request;
351         request.property = name.toStdString();
352         request.zoneFilter = zone;
353         request.value = VehicleProperty::getPropertyTypeForPropertyNameValue(name.toStdString(), var.toString().toStdString());
354
355         routingEngine->updateSupported(supported(), PropertyList(), &source);
356         routingEngine->setProperty(request);
357 }
358
359
360 QVariant Property::value()
361 {
362         return mValue ? gvariantToQVariant(mValue->toVariant()) : QVariant::Invalid;
363 }
364
365 void Property::setValue(QVariant v)
366 {
367         if(v.type() == QVariant::List || v.type() == QVariant::Map)
368         {
369
370                 QJsonDocument doc = QJsonDocument::fromVariant(v);
371
372                 QString json = doc.toJson();
373
374                 mValue->fromString(json.toStdString());
375         }
376         else
377         {
378                 QString tempVal = v.toString();
379                 mValue->fromString(tempVal.toStdString());
380         }
381
382         AsyncSetPropertyRequest request;
383         request.property = mValue->name;
384         request.value = mValue->copy();
385         request.completed = [&](AsyncPropertyReply* reply)
386         {
387                 if(reply->success)
388                 {
389                         propertyChanged(reply->value);
390                 }
391                 else
392                 {
393                         DebugOut(DebugOut::Warning) << "Error, trying to set value: " << reply->error << endl;
394                 }
395                 delete reply;
396         };
397         routingEngine->setProperty(request);
398 }
399
400 void Property::getHistory(QDateTime begin, QDateTime end, QJSValue cbFunction)
401 {
402         double b = (double)begin.toMSecsSinceEpoch() / 1000.0;
403         double e = (double)end.toMSecsSinceEpoch() / 1000.0;
404         AsyncRangePropertyRequest request;
405         request.timeBegin = b;
406         request.timeEnd = e;
407
408         PropertyList reqlist;
409         reqlist.push_back(mValue->name);
410
411         request.properties = reqlist;
412         request.completed = [&cbFunction](AsyncRangePropertyReply* reply)
413         {
414                 if(!reply->success)
415                 {
416                         DebugOut(DebugOut::Error)<<"bluemoney get history call failed"<<endl;
417                         return;
418                 }
419
420                 if(cbFunction.isCallable())
421                 {
422                         QVariantList list;
423
424                         for(auto itr = reply->values.begin(); itr != reply->values.end(); itr++)
425                         {
426                                 AbstractPropertyType *val = *itr;
427
428                                 list.append(gvariantToQVariant(val->toVariant()));
429                         }
430                         QJSValue val = cbFunction.engine()->toScriptValue<QVariantList>(list);
431                         cbFunction.call(QJSValueList()<<val);
432
433                 }
434
435                 delete reply;
436         };
437
438         routingEngine->getRangePropertyAsync(request);
439 }
440
441 Property::Property(VehicleProperty::Property prop, QString srcFilter, AbstractRoutingEngine* re, Zone::Type zone, QObject *parent)
442         :QObject(parent), AbstractSink(re, std::map<std::string,std::string>()),mValue(nullptr), mUuid(amb::createUuid()), mZone(zone)
443 {
444         setType(prop.c_str());
445 }
446
447 QString Property::type()
448 {
449         return mValue->name.c_str();
450 }
451
452 void Property::setType(QString t)
453 {
454         if(mValue && type() != "")
455                 routingEngine->unsubscribeToProperty(type().toStdString(), this);
456
457         routingEngine->subscribeToProperty(t.toStdString(), this);
458
459         mValue = VehicleProperty::getPropertyTypeForPropertyNameValue(t.toStdString());
460
461         if(!mValue)
462                 return;
463
464         AsyncPropertyRequest request;
465         request.property = mValue->name;
466         request.completed = [this](AsyncPropertyReply* reply)
467         {
468                 if(reply->success)
469                         propertyChanged(reply->value);
470
471                 delete reply;
472         };
473
474         routingEngine->getPropertyAsync(request);
475 }
476
477 void Property::propertyChanged(AbstractPropertyType *value)
478 {
479         if(value->zone != mZone)
480                 return;
481
482         if(mValue)
483         {
484                 delete mValue;
485         }
486         mValue = value->copy();
487
488         changed(gvariantToQVariant(mValue->toVariant()));
489 }
490
491
492 QVariant BluemonkeySink::zonesForProperty(QString prop, QString src)
493 {
494         PropertyInfo info = routingEngine->getPropertyInfo(prop.toStdString(), src.toStdString());
495
496         QVariantList list;
497
498         for(auto i : info.zones())
499         {
500                 list << i;
501         }
502
503         return list;
504 }