Subversion Repositories HomeAutomation

Rev

Details | Last modification | View Log | SVN | RSS feed

Rev Author Line No. Line
969 runge 1
/***************************************************************************
2
 *   Copyright (C) December 10, 2008 by Mattias Runge                             *
3
 *   mattias@runge.se                                                      *
4
 *   virtualmachine.cpp                                            *
5
 *                                                                         *
6
 *   This program is free software; you can redistribute it and/or modify  *
7
 *   it under the terms of the GNU General Public License as published by  *
8
 *   the Free Software Foundation; either version 2 of the License, or     *
9
 *   (at your option) any later version.                                   *
10
 *                                                                         *
11
 *   This program is distributed in the hope that it will be useful,       *
12
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
13
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
14
 *   GNU General Public License for more details.                          *
15
 *                                                                         *
16
 *   You should have received a copy of the GNU General Public License     *
17
 *   along with this program; if not, write to the                         *
18
 *   Free Software Foundation, Inc.,                                       *
19
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
20
 ***************************************************************************/
21
 
22
#include "virtualmachine.h"
23
 
24
VirtualMachine* VirtualMachine::myInstance = NULL;
25
 
26
VirtualMachine& VirtualMachine::getInstance()
27
{
28
    if (myInstance == NULL)
29
    {
30
        myInstance = new VirtualMachine();
31
    }
32
 
33
    return *myInstance;
34
}
35
 
36
void VirtualMachine::deleteInstance()
37
{
38
    if (myInstance != NULL)
39
    {
40
        delete myInstance;
41
        myInstance = NULL;
42
    }
43
}
44
 
45
VirtualMachine::VirtualMachine()
46
{
981 runge 47
    Thread<VirtualMachine>();
969 runge 48
}
49
 
50
VirtualMachine::~VirtualMachine()
51
{
981 runge 52
    stop();
969 runge 53
}
54
 
55
void VirtualMachine::run()
56
{
57
    SyslogStream &slog = SyslogStream::getInstance();
58
 
971 runge 59
    string basePath = Settings::get("BasePath") + "Services/";
969 runge 60
    string scriptName = "Base.js";
971 runge 61
    string scriptFileName = basePath + "System/" + scriptName;
969 runge 62
 
63
    if (!file_exists(scriptFileName))
64
    {
976 runge 65
        slog << "Failed to load :" + scriptFileName + "\n";
969 runge 66
    }
67
 
68
    string scriptSource = file_get_contents(scriptFileName);
69
 
70
    Handle<String> source =  String::New(scriptSource.c_str(), scriptSource.length());
71
    //each script name must be unique , for this demo I just run one embedded script, so the name can be fixed
72
    Handle<String> name = String::New(scriptName.c_str(), scriptName.length());
73
 
74
    // Create a template for the global object.
75
    myGlobal = ObjectTemplate::New();
76
 
77
    //associates "print" on script to the Print function
78
    myGlobal->Set(String::New("log"), FunctionTemplate::New(VirtualMachine_log));
79
    myGlobal->Set(String::New("sendCanMessage"), FunctionTemplate::New(VirtualMachine_sendCanMessage));
986 runge 80
    myGlobal->Set(String::New("sendCanNMTMessage"), FunctionTemplate::New(VirtualMachine_sendCanNMTMessage));
969 runge 81
    myGlobal->Set(String::New("loadScript"), FunctionTemplate::New(VirtualMachine_loadScript));
971 runge 82
    myGlobal->Set(String::New("startIntervalThread"), FunctionTemplate::New(VirtualMachine_startIntervalThread));
83
    myGlobal->Set(String::New("stopIntervalThread"), FunctionTemplate::New(VirtualMachine_stopIntervalThread));
976 runge 84
    myGlobal->Set(String::New("startSocketThread"), FunctionTemplate::New(VirtualMachine_startSocketThread));
85
    myGlobal->Set(String::New("stopSocketThread"), FunctionTemplate::New(VirtualMachine_stopSocketThread));
86
    myGlobal->Set(String::New("sendToSocketThread"), FunctionTemplate::New(VirtualMachine_sendToSocketThread));
981 runge 87
    myGlobal->Set(String::New("uint2hex"), FunctionTemplate::New(VirtualMachine_uint2hex));
969 runge 88
 
89
    //create context for the script
90
    myContext = Context::New(NULL, myGlobal);
91
 
92
 
93
    //access global context within this scope
94
    Context::Scope context_scope(myContext);
95
    //exception handler
995 runge 96
    TryCatch tryCatch;
969 runge 97
    //compile script to binary code - JIT
98
    Handle<Script> script = Script::Compile(source, name);
99
 
100
    //check if we got problems on compilation
101
    if (script.IsEmpty())
102
    {
995 runge 103
        printException(&tryCatch);
104
        slog << "\n";
105
        return;
969 runge 106
    }
107
    else
108
    {
109
        //no errors , let's continue
110
        Handle<Value> result = script->Run();
111
 
112
        //check if execution ended with errors
113
        if (result.IsEmpty())
114
        {
995 runge 115
            printException(&tryCatch);
116
            slog << "\n";
117
            return;
969 runge 118
        }
119
        else
120
        {
121
            loadScript("System/Startup.js");
122
 
970 runge 123
            if (file_exists(basePath + "Autostart.js"))
124
            {
125
                loadScript("Autostart.js");
126
            }
127
 
986 runge 128
            myFunctionHandleNMTMessage = Handle<Function>::Cast(myContext->Global()->Get(String::New("handleNMTMessage")));
969 runge 129
            myFunctionOfflineCheck = Handle<Function>::Cast(myContext->Global()->Get(String::New("offlineCheck")));
130
            myFunctionHandleMessage = Handle<Function>::Cast(myContext->Global()->Get(String::New("handleMessage")));
131
            myFunctionStartup = Handle<Function>::Cast(myContext->Global()->Get(String::New("startup")));
132
        }
133
    }
134
 
135
    const int argc = 0;
136
    Handle<Value> argv[argc] = { };
137
 
138
    Handle<Value> result = myFunctionStartup->Call(myFunctionStartup, argc, argv); // argc and argv are your standard arguments to a function
139
 
140
    mySemaphore.lock();
141
 
142
    while (1)
143
    {
144
        string expression;
145
 
146
        while (myExpressions.size() > 0)
147
        {
148
            expression = myExpressions.pop();
149
 
150
            runExpression(expression);
151
        }
152
 
153
        CanMessage canMessage;
154
 
155
        while (myCanMessages.size() > 0)
156
        {
157
            canMessage = myCanMessages.pop();
158
 
986 runge 159
            if (canMessage.getClassName() == "nmt")
969 runge 160
            {
986 runge 161
                callHandleNMTMessage(canMessage);
969 runge 162
            }
163
            else
164
            {
165
                callHandleMessage(canMessage);
166
            }
167
        }
168
 
169
        mySemaphore.wait();
170
    }
171
 
172
    mySemaphore.unlock();
173
}
174
 
175
void VirtualMachine::queueCanMessage(CanMessage canMessage)
176
{
177
    myCanMessages.push(canMessage);
178
    mySemaphore.broadcast();
179
}
180
 
181
void VirtualMachine::queueExpression(string expression)
182
{
183
    myExpressions.push(expression);
184
    mySemaphore.broadcast();
185
}
186
 
187
bool VirtualMachine::loadScript(string scriptName)
188
{
189
    SyslogStream &slog = SyslogStream::getInstance();
190
 
981 runge 191
    string basePath = Settings::get("BasePath") + "Services/";
969 runge 192
    string scriptFileName = basePath + scriptName;
193
 
194
    if (!file_exists(scriptFileName))
195
    {
981 runge 196
        slog << "Failed to load " + scriptFileName + "\n";
969 runge 197
        return false;
198
    }
199
 
200
    string scriptSource = file_get_contents(scriptFileName);
201
 
202
    Handle<String> source =  String::New(scriptSource.c_str(), scriptSource.length());
203
    //each script name must be unique , for this demo I just run one embedded script, so the name can be fixed
204
    Handle<String> name = String::New(scriptName.c_str(), scriptName.length());
205
 
206
    //access global context within this scope
207
    Context::Scope context_scope(myContext);
208
    //exception handler
995 runge 209
    TryCatch tryCatch;
969 runge 210
    //compile script to binary code - JIT
211
    Handle<Script> script = Script::Compile(source, name);
212
 
213
    //check if we got problems on compilation
214
    if (script.IsEmpty())
215
    {
995 runge 216
        printException(&tryCatch);
217
        slog << "\n";
969 runge 218
        return false;
219
    }
220
    else
221
    {
222
        //no errors , let's continue
223
        Handle<Value> result = script->Run();
224
 
225
        //check if execution ended with errors
226
        if (result.IsEmpty())
227
        {
995 runge 228
            printException(&tryCatch);
229
            slog << "\n";
969 runge 230
            return false;
231
        }
977 runge 232
 
233
        slog << "Loaded " + scriptName + "\n";
969 runge 234
    }
235
 
236
    return true;
237
}
238
 
986 runge 239
void VirtualMachine::callHandleNMTMessage(CanMessage canMessage)
969 runge 240
{
986 runge 241
    const int argc = 3;
969 runge 242
 
986 runge 243
    string jsonData = canMessage.getJSONData();
244
 
245
    Handle<Value> argv[argc] = {String::New(canMessage.getClassName().c_str()),
246
                                String::New(canMessage.getCommandName().c_str()),
247
                                String::New(jsonData.c_str()) };
248
 
249
    Handle<Value> result = myFunctionHandleNMTMessage->Call(myFunctionHandleNMTMessage, argc, argv); // argc and argv are your standard arguments to a function
969 runge 250
}
251
 
252
void VirtualMachine::callHandleMessage(CanMessage canMessage)
253
{
254
    const int argc = 6;
981 runge 255
 
256
    string jsonData = canMessage.getJSONData();
257
 
969 runge 258
    Handle<Value> argv[argc] = {String::New(canMessage.getClassName().c_str()),
259
                                String::New(canMessage.getDirectionFlag().c_str()),
260
                                String::New(canMessage.getModuleName().c_str()),
261
                                Integer::New(canMessage.getModuleId()),
262
                                String::New(canMessage.getCommandName().c_str()),
981 runge 263
                                String::New(jsonData.c_str()) };
969 runge 264
 
265
    Handle<Value> result = myFunctionHandleMessage->Call(myFunctionHandleMessage, argc, argv); // argc and argv are your standard arguments to a function
266
}
267
 
971 runge 268
unsigned int VirtualMachine::startIntervalThread(unsigned int timeout)
969 runge 269
{
971 runge 270
    IntervalThread intervalThread(timeout);
969 runge 271
 
971 runge 272
    myIntervalThreads[intervalThread.getId()] = intervalThread;
273
    myIntervalThreads[intervalThread.getId()].start();
969 runge 274
 
971 runge 275
    return intervalThread.getId();
969 runge 276
}
277
 
971 runge 278
bool VirtualMachine::stopIntervalThread(unsigned int id)
969 runge 279
{
971 runge 280
    if (myIntervalThreads.find(id) != myIntervalThreads.end())
969 runge 281
    {
971 runge 282
        myIntervalThreads.erase(id);
969 runge 283
        return true;
284
    }
285
 
286
    return false;
287
}
288
 
976 runge 289
unsigned int VirtualMachine::startSocketThread(string address, int port, unsigned int reconnectTimeout)
290
{
983 runge 291
    SocketThread *socketThread = new SocketThread(address, port, reconnectTimeout);
292
    mySocketThreads[socketThread->getId()] = socketThread;
293
    mySocketThreads[socketThread->getId()]->start();
976 runge 294
 
983 runge 295
    return socketThread->getId();
976 runge 296
}
297
 
298
bool VirtualMachine::stopSocketThread(unsigned int id)
299
{
983 runge 300
 
301
 
976 runge 302
    if (mySocketThreads.find(id) != mySocketThreads.end())
303
    {
983 runge 304
        //mySocketThreads[id]->stop();
305
        delete mySocketThreads[id];
306
 
976 runge 307
        mySocketThreads.erase(id);
308
        return true;
309
    }
310
 
311
    return false;
312
}
313
 
314
void VirtualMachine::sendToSocketThread(unsigned int id, string data)
315
{
316
    if (mySocketThreads.find(id) != mySocketThreads.end())
317
    {
983 runge 318
        mySocketThreads[id]->send(data);
976 runge 319
    }
320
}
321
 
969 runge 322
bool VirtualMachine::runExpression(string expression)
323
{
324
    SyslogStream &slog = SyslogStream::getInstance();
325
 
326
    string scriptName = "runExpression";
327
 
328
    Handle<String> source =  String::New(expression.c_str(), expression.length());
329
    //each script name must be unique , for this demo I just run one embedded script, so the name can be fixed
330
    Handle<String> name = String::New(scriptName.c_str(), scriptName.length());
331
 
332
    //access global context within this scope
333
    Context::Scope context_scope(myContext);
334
    //exception handler
995 runge 335
    TryCatch tryCatch;
969 runge 336
    //compile script to binary code - JIT
337
    Handle<Script> script = Script::Compile(source, name);
338
 
339
    //check if we got problems on compilation
340
    if (script.IsEmpty())
341
    {
995 runge 342
        printException(&tryCatch);
343
        slog << "\n";
969 runge 344
        return false;
345
    }
346
    else
347
    {
348
        //no errors , let's continue
349
        Handle<Value> result = script->Run();
350
 
351
        //check if execution ended with errors
352
        if (result.IsEmpty())
353
        {
995 runge 354
            printException(&tryCatch);
355
            slog << "\n";
969 runge 356
            return false;
357
        }
358
    }
359
 
360
    return true;
361
}
362
 
995 runge 363
void VirtualMachine::printException(TryCatch* tryCatch)
364
{
365
    SyslogStream &slog = SyslogStream::getInstance();
366
 
367
    HandleScope handle_scope;
368
    String::Utf8Value exception(tryCatch->Exception());
369
    Handle<v8::Message> message = tryCatch->Message();
370
 
371
    string strException = *exception;
372
 
373
    if (message.IsEmpty())
374
    {
375
        // V8 didn't provide any extra information about this error; just print the exception.
376
        slog << strException + "\n";
377
    }
378
    else
379
    {
380
        // Print (filename):(line number): (message).
381
        String::Utf8Value filename(message->GetScriptResourceName());
382
        int linenum = message->GetLineNumber();
383
 
384
        string strFilename = *filename;
385
 
386
        slog << strFilename + ":" + itos(linenum) + ": " + strException + "\n";
387
 
388
        // Print line of source code.
389
        String::Utf8Value sourceline(message->GetSourceLine());
390
 
391
        string strSourceline = *sourceline;
392
 
393
        slog << strSourceline + "\n";
394
 
395
        string underline;
396
        // Print wavy underline (GetUnderline is deprecated).
397
        int start = message->GetStartColumn();
398
        for (int i = 0; i < start; i++)
399
        {
400
            underline += " ";
401
        }
402
 
403
        int end = message->GetEndColumn();
404
        for (int i = start; i < end; i++)
405
        {
406
            underline += "^";
407
        }
408
 
409
        slog << underline + "\n";
410
    }
411
}
412
 
413
 
969 runge 414
Handle<Value> VirtualMachine_log(const Arguments& args)
415
{
416
    SyslogStream &slog = SyslogStream::getInstance();
417
 
418
    String::AsciiValue str(args[0]);
419
 
420
    slog << *str;
421
 
422
    return Undefined();
423
}
424
 
425
Handle<Value> VirtualMachine_sendCanMessage(const Arguments& args)
426
{
976 runge 427
    //SyslogStream &slog = SyslogStream::getInstance();
969 runge 428
    CanNetManager &canMan = CanNetManager::getInstance();
429
 
430
    CanMessage canMessage;
431
 
432
    String::AsciiValue className(args[0]);
433
    canMessage.setClassName(*className);
434
    String::AsciiValue directionFlag(args[1]);
435
    canMessage.setDirectionFlag(*directionFlag);
436
    String::AsciiValue moduleName(args[2]);
437
    canMessage.setModuleName(*moduleName);
438
    canMessage.setModuleId(args[3]->Uint32Value());
439
    String::AsciiValue commandName(args[4]);
440
    canMessage.setCommandName(*commandName);
441
 
442
    String::AsciiValue dataString(args[5]);
443
 
444
    vector<string> parts = explode(",", trim(*dataString, ','));
445
    map<string, CanVariable> data;
446
 
447
    for (int n = 0; n < parts.size(); n++)
448
    {
449
        vector<string> keyAndValue = explode(":", parts[n]);
450
 
451
        //string key = trim(keyAndValue[0], ' ');
452
        //string value = trim(keyAndValue[1], ' ');
453
        data[keyAndValue[0]] = CanVariable(keyAndValue[0], keyAndValue[1]);
454
    }
455
 
456
    canMessage.setData(data);
457
 
458
    canMan.sendMessage(canMessage);
459
 
460
    return Undefined();
461
}
462
 
986 runge 463
Handle<Value> VirtualMachine_sendCanNMTMessage(const Arguments& args)
464
{
465
    //SyslogStream &slog = SyslogStream::getInstance();
466
    CanNetManager &canMan = CanNetManager::getInstance();
467
 
468
    CanMessage canMessage;
469
 
470
    String::AsciiValue className(args[0]);
471
    canMessage.setClassName(*className);
472
    String::AsciiValue commandName(args[1]);
473
    canMessage.setCommandName(*commandName);
474
 
475
    String::AsciiValue dataString(args[2]);
476
 
477
    vector<string> parts = explode(",", trim(*dataString, ','));
478
    map<string, CanVariable> data;
479
 
480
    for (int n = 0; n < parts.size(); n++)
481
    {
482
        vector<string> keyAndValue = explode(":", parts[n]);
483
 
484
        //string key = trim(keyAndValue[0], ' ');
485
        //string value = trim(keyAndValue[1], ' ');
486
        data[keyAndValue[0]] = CanVariable(keyAndValue[0], keyAndValue[1]);
487
    }
488
 
489
    canMessage.setData(data);
490
 
491
    canMan.sendMessage(canMessage);
492
 
493
    return Undefined();
494
}
495
 
969 runge 496
Handle<Value> VirtualMachine_loadScript(const Arguments& args)
497
{
498
    VirtualMachine &vm = VirtualMachine::getInstance();
499
 
500
    String::AsciiValue str(args[0]);
501
 
502
    return Boolean::New(vm.loadScript(*str));
503
}
504
 
971 runge 505
Handle<Value> VirtualMachine_startIntervalThread(const Arguments& args)
969 runge 506
{
507
    VirtualMachine &vm = VirtualMachine::getInstance();
508
 
971 runge 509
    unsigned int timeout = args[0]->Uint32Value();
969 runge 510
 
971 runge 511
    return Integer::New(vm.startIntervalThread(timeout));
969 runge 512
}
513
 
971 runge 514
Handle<Value> VirtualMachine_stopIntervalThread(const Arguments& args)
969 runge 515
{
516
    VirtualMachine &vm = VirtualMachine::getInstance();
517
 
973 runge 518
    unsigned int id = args[0]->Uint32Value();
969 runge 519
 
976 runge 520
    return Boolean::New(vm.stopIntervalThread(id));
969 runge 521
}
976 runge 522
 
523
Handle<Value> VirtualMachine_startSocketThread(const Arguments& args)
524
{
525
    VirtualMachine &vm = VirtualMachine::getInstance();
526
 
527
    String::AsciiValue address(args[0]);
528
    int port = args[1]->Uint32Value();
529
    unsigned int reconnectTimeout = args[2]->Uint32Value();
530
 
531
    return Integer::New(vm.startSocketThread(*address, port, reconnectTimeout));
532
}
533
 
534
Handle<Value> VirtualMachine_stopSocketThread(const Arguments& args)
535
{
536
    VirtualMachine &vm = VirtualMachine::getInstance();
537
 
538
    unsigned int id = args[0]->Uint32Value();
539
 
540
    return Boolean::New(vm.stopSocketThread(id));
541
}
542
 
543
Handle<Value> VirtualMachine_sendToSocketThread(const Arguments& args)
544
{
545
    VirtualMachine &vm = VirtualMachine::getInstance();
546
 
547
    unsigned int id = args[0]->Uint32Value();
548
    String::AsciiValue data(args[1]);
549
 
550
    vm.sendToSocketThread(id, *data);
551
 
552
    return Undefined();
553
}
554
 
981 runge 555
Handle<Value> VirtualMachine_uint2hex(const Arguments& args)
556
{
557
    VirtualMachine &vm = VirtualMachine::getInstance();
558
 
559
    unsigned int id = args[0]->Uint32Value();
560
    unsigned int length = args[1]->Uint32Value();
561
    string hexId = uint2hex(id, length);
562
 
563
    return String::New(hexId.c_str());
564
}
565