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