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