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 <map>
23
 
24
 
25
#include "virtualmachine.h"
26
 
27
VirtualMachine* VirtualMachine::myInstance = NULL;
28
 
29
VirtualMachine& VirtualMachine::getInstance()
30
{
31
	if (myInstance == NULL)
32
	{
33
		myInstance = new VirtualMachine();
34
	}
35
 
36
	return *myInstance;
37
}
38
 
39
void VirtualMachine::deleteInstance()
40
{
41
	if (myInstance != NULL)
42
	{
43
		delete myInstance;
44
		myInstance = NULL;
45
	}
46
}
47
 
48
VirtualMachine::VirtualMachine()
49
{
50
 
51
}
52
 
53
VirtualMachine::~VirtualMachine()
54
{
55
 
56
}
57
 
58
void VirtualMachine::run()
59
{
60
	SyslogStream &slog = SyslogStream::getInstance();
61
 
971 runge 62
	string basePath = Settings::get("BasePath") + "Services/";
969 runge 63
	string scriptName = "Base.js";
971 runge 64
	string scriptFileName = basePath + "System/" + scriptName;
969 runge 65
 
66
	if (!file_exists(scriptFileName))
67
	{
976 runge 68
		slog << "Failed to load :" + scriptFileName + "\n";
969 runge 69
	}
70
 
71
	string scriptSource = file_get_contents(scriptFileName);
72
 
73
	Handle<String> source =  String::New(scriptSource.c_str(), scriptSource.length());
74
	//each script name must be unique , for this demo I just run one embedded script, so the name can be fixed
75
	Handle<String> name = String::New(scriptName.c_str(), scriptName.length());
76
 
77
	// Create a template for the global object.
78
	myGlobal = ObjectTemplate::New();
79
 
80
	//associates "print" on script to the Print function
81
	myGlobal->Set(String::New("log"), FunctionTemplate::New(VirtualMachine_log));
82
	myGlobal->Set(String::New("sendCanMessage"), FunctionTemplate::New(VirtualMachine_sendCanMessage));
83
	myGlobal->Set(String::New("loadScript"), FunctionTemplate::New(VirtualMachine_loadScript));
971 runge 84
	myGlobal->Set(String::New("startIntervalThread"), FunctionTemplate::New(VirtualMachine_startIntervalThread));
85
	myGlobal->Set(String::New("stopIntervalThread"), FunctionTemplate::New(VirtualMachine_stopIntervalThread));
976 runge 86
	myGlobal->Set(String::New("startSocketThread"), FunctionTemplate::New(VirtualMachine_startSocketThread));
87
	myGlobal->Set(String::New("stopSocketThread"), FunctionTemplate::New(VirtualMachine_stopSocketThread));
88
	myGlobal->Set(String::New("sendToSocketThread"), FunctionTemplate::New(VirtualMachine_sendToSocketThread));
969 runge 89
 
90
	//create context for the script
91
	myContext = Context::New(NULL, myGlobal);
92
 
93
 
94
	//access global context within this scope
95
	Context::Scope context_scope(myContext);
96
	//exception handler
97
	TryCatch try_catch;
98
	//compile script to binary code - JIT
99
	Handle<Script> script = Script::Compile(source, name);
100
 
101
	//check if we got problems on compilation
102
	if (script.IsEmpty())
103
	{
104
 
105
		// Print errors that happened during compilation.
106
		String::AsciiValue error(try_catch.Exception());
976 runge 107
		string sError = *error;
108
		slog << "Failed to compile script: " + sError + "\n";
969 runge 109
	}
110
	else
111
	{
112
		//no errors , let's continue
113
		Handle<Value> result = script->Run();
114
 
115
		//check if execution ended with errors
116
		if (result.IsEmpty())
117
		{
118
			// Print errors that happened during execution.
119
			String::AsciiValue error(try_catch.Exception());
976 runge 120
			string sError = *error;
121
			slog << "Constructor: Failed to run script: " << sError << "\n";
969 runge 122
		}
123
		else
124
		{
125
			loadScript("System/Startup.js");
126
 
970 runge 127
			if (file_exists(basePath + "Autostart.js"))
128
			{
129
				loadScript("Autostart.js");
130
			}
131
 
969 runge 132
			myFunctionHandleHeartbeat = Handle<Function>::Cast(myContext->Global()->Get(String::New("handleHeartbeat")));
133
			myFunctionOfflineCheck = Handle<Function>::Cast(myContext->Global()->Get(String::New("offlineCheck")));
134
			myFunctionHandleMessage = Handle<Function>::Cast(myContext->Global()->Get(String::New("handleMessage")));
135
			myFunctionStartup = Handle<Function>::Cast(myContext->Global()->Get(String::New("startup")));
136
		}
137
	}
138
 
139
	const int argc = 0;
140
	Handle<Value> argv[argc] = { };
141
 
142
	Handle<Value> result = myFunctionStartup->Call(myFunctionStartup, argc, argv); // argc and argv are your standard arguments to a function
143
 
144
	mySemaphore.lock();
145
 
146
	while (1)
147
	{
148
		string expression;
149
 
150
		while (myExpressions.size() > 0)
151
		{
152
			expression = myExpressions.pop();
153
 
154
			runExpression(expression);
155
		}
156
 
157
		CanMessage canMessage;
158
 
159
		while (myCanMessages.size() > 0)
160
		{
161
			canMessage = myCanMessages.pop();
162
 
163
			if (canMessage.isHeartbeat())
164
			{
165
				callHandleHeartbeat(stoi(canMessage.getData()["HardwareId"].getValue()));
166
			}
167
			else
168
			{
169
				callHandleMessage(canMessage);
170
			}
171
		}
172
 
173
		mySemaphore.wait();
174
	}
175
 
176
	mySemaphore.unlock();
177
}
178
 
179
void VirtualMachine::queueCanMessage(CanMessage canMessage)
180
{
181
	myCanMessages.push(canMessage);
182
	mySemaphore.broadcast();
183
}
184
 
185
void VirtualMachine::queueExpression(string expression)
186
{
187
	myExpressions.push(expression);
188
	mySemaphore.broadcast();
189
}
190
 
191
bool VirtualMachine::loadScript(string scriptName)
192
{
193
	SyslogStream &slog = SyslogStream::getInstance();
194
 
195
	string basePath = Settings::get("BasePath") + "/Services/";
196
	string scriptFileName = basePath + scriptName;
197
 
198
	if (!file_exists(scriptFileName))
199
	{
976 runge 200
		slog << "Failed to load :" + scriptFileName + "\n";
969 runge 201
		return false;
202
	}
203
 
204
	string scriptSource = file_get_contents(scriptFileName);
205
 
206
	Handle<String> source =  String::New(scriptSource.c_str(), scriptSource.length());
207
	//each script name must be unique , for this demo I just run one embedded script, so the name can be fixed
208
	Handle<String> name = String::New(scriptName.c_str(), scriptName.length());
209
 
210
	//access global context within this scope
211
	Context::Scope context_scope(myContext);
212
	//exception handler
213
	TryCatch try_catch;
214
	//compile script to binary code - JIT
215
	Handle<Script> script = Script::Compile(source, name);
216
 
217
	//check if we got problems on compilation
218
	if (script.IsEmpty())
219
	{
220
		// Print errors that happened during compilation.
221
		String::AsciiValue error(try_catch.Exception());
976 runge 222
		string sError = *error;
223
		slog << "loadScript: Failed to compile script: " + sError + "\n";
969 runge 224
		return false;
225
	}
226
	else
227
	{
228
		//no errors , let's continue
229
		Handle<Value> result = script->Run();
230
 
231
		//check if execution ended with errors
232
		if (result.IsEmpty())
233
		{
234
			// Print errors that happened during execution.
235
			String::AsciiValue error(try_catch.Exception());
976 runge 236
			string sError = *error;
237
			slog << "loadScript: Failed to run script: " + sError + "\n";
969 runge 238
			return false;
239
		}
977 runge 240
 
241
		slog << "Loaded " + scriptName + "\n";
969 runge 242
	}
243
 
244
	return true;
245
}
246
 
247
void VirtualMachine::callHandleHeartbeat(int hardwareId)
248
{
249
	const int argc = 1;
250
	Handle<Value> argv[argc] = { Integer::New(hardwareId) };
251
 
252
	Handle<Value> result = myFunctionHandleHeartbeat->Call(myFunctionHandleHeartbeat, argc, argv); // argc and argv are your standard arguments to a function
253
}
254
 
255
void VirtualMachine::callHandleMessage(CanMessage canMessage)
256
{
257
	const int argc = 6;
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()),
263
								String::New(canMessage.getJSONData().c_str()) };
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
{
291
	SocketThread socketThread(address, port, reconnectTimeout);
292
	mySocketThreads[socketThread.getId()] = socketThread;
293
	mySocketThreads[socketThread.getId()].start();
294
 
295
	return socketThread.getId();
296
}
297
 
298
bool VirtualMachine::stopSocketThread(unsigned int id)
299
{
300
	if (mySocketThreads.find(id) != mySocketThreads.end())
301
	{
302
		mySocketThreads.erase(id);
303
		return true;
304
	}
305
 
306
	return false;
307
}
308
 
309
void VirtualMachine::sendToSocketThread(unsigned int id, string data)
310
{
311
	if (mySocketThreads.find(id) != mySocketThreads.end())
312
	{
313
		mySocketThreads[id].send(data);
314
	}
315
}
316
 
969 runge 317
bool VirtualMachine::runExpression(string expression)
318
{
319
	SyslogStream &slog = SyslogStream::getInstance();
320
 
321
	string scriptName = "runExpression";
322
 
323
	Handle<String> source =  String::New(expression.c_str(), expression.length());
324
	//each script name must be unique , for this demo I just run one embedded script, so the name can be fixed
325
	Handle<String> name = String::New(scriptName.c_str(), scriptName.length());
326
 
327
	//access global context within this scope
328
	Context::Scope context_scope(myContext);
329
	//exception handler
330
	TryCatch try_catch;
331
	//compile script to binary code - JIT
332
	Handle<Script> script = Script::Compile(source, name);
333
 
334
	//check if we got problems on compilation
335
	if (script.IsEmpty())
336
	{
337
		// Print errors that happened during compilation.
338
		String::AsciiValue error(try_catch.Exception());
976 runge 339
		string sError = *error;
340
		slog << "runExpression: Failed to compile script: " + sError + "\n";
341
		slog << expression << "\n\n";
969 runge 342
		return false;
343
	}
344
	else
345
	{
346
		//no errors , let's continue
347
		Handle<Value> result = script->Run();
348
 
349
		//check if execution ended with errors
350
		if (result.IsEmpty())
351
		{
352
			// Print errors that happened during execution.
353
			String::AsciiValue error(try_catch.Exception());
976 runge 354
			string sError = *error;
355
			slog << "runExpression: Failed to run script: " + sError + "\n";
969 runge 356
			return false;
357
		}
358
	}
359
 
360
	return true;
361
}
362
 
363
Handle<Value> VirtualMachine_log(const Arguments& args)
364
{
365
	SyslogStream &slog = SyslogStream::getInstance();
366
 
367
	String::AsciiValue str(args[0]);
368
 
369
	slog << *str;
370
 
371
	return Undefined();
372
}
373
 
374
Handle<Value> VirtualMachine_sendCanMessage(const Arguments& args)
375
{
976 runge 376
	//SyslogStream &slog = SyslogStream::getInstance();
969 runge 377
	CanNetManager &canMan = CanNetManager::getInstance();
378
 
379
	CanMessage canMessage;
380
 
381
	String::AsciiValue className(args[0]);
382
	canMessage.setClassName(*className);
383
	String::AsciiValue directionFlag(args[1]);
384
	canMessage.setDirectionFlag(*directionFlag);
385
	String::AsciiValue moduleName(args[2]);
386
	canMessage.setModuleName(*moduleName);
387
	canMessage.setModuleId(args[3]->Uint32Value());
388
	String::AsciiValue commandName(args[4]);
389
	canMessage.setCommandName(*commandName);
390
 
391
	String::AsciiValue dataString(args[5]);
392
 
393
	vector<string> parts = explode(",", trim(*dataString, ','));
394
	map<string, CanVariable> data;
395
 
396
	for (int n = 0; n < parts.size(); n++)
397
	{
398
		vector<string> keyAndValue = explode(":", parts[n]);
399
 
400
		//string key = trim(keyAndValue[0], ' ');
401
		//string value = trim(keyAndValue[1], ' ');
402
		data[keyAndValue[0]] = CanVariable(keyAndValue[0], keyAndValue[1]);
403
	}
404
 
405
	canMessage.setData(data);
406
 
407
	canMan.sendMessage(canMessage);
408
 
409
	return Undefined();
410
}
411
 
412
Handle<Value> VirtualMachine_loadScript(const Arguments& args)
413
{
414
	VirtualMachine &vm = VirtualMachine::getInstance();
415
 
416
	String::AsciiValue str(args[0]);
417
 
418
	return Boolean::New(vm.loadScript(*str));
419
}
420
 
971 runge 421
Handle<Value> VirtualMachine_startIntervalThread(const Arguments& args)
969 runge 422
{
423
	VirtualMachine &vm = VirtualMachine::getInstance();
424
 
971 runge 425
	unsigned int timeout = args[0]->Uint32Value();
969 runge 426
 
971 runge 427
	return Integer::New(vm.startIntervalThread(timeout));
969 runge 428
}
429
 
971 runge 430
Handle<Value> VirtualMachine_stopIntervalThread(const Arguments& args)
969 runge 431
{
432
	VirtualMachine &vm = VirtualMachine::getInstance();
433
 
973 runge 434
	unsigned int id = args[0]->Uint32Value();
969 runge 435
 
976 runge 436
	return Boolean::New(vm.stopIntervalThread(id));
969 runge 437
}
976 runge 438
 
439
Handle<Value> VirtualMachine_startSocketThread(const Arguments& args)
440
{
441
	VirtualMachine &vm = VirtualMachine::getInstance();
442
 
443
	String::AsciiValue address(args[0]);
444
	int port = args[1]->Uint32Value();
445
	unsigned int reconnectTimeout = args[2]->Uint32Value();
446
 
447
	return Integer::New(vm.startSocketThread(*address, port, reconnectTimeout));
448
}
449
 
450
Handle<Value> VirtualMachine_stopSocketThread(const Arguments& args)
451
{
452
	VirtualMachine &vm = VirtualMachine::getInstance();
453
 
454
	unsigned int id = args[0]->Uint32Value();
455
 
456
	return Boolean::New(vm.stopSocketThread(id));
457
}
458
 
459
Handle<Value> VirtualMachine_sendToSocketThread(const Arguments& args)
460
{
461
	VirtualMachine &vm = VirtualMachine::getInstance();
462
 
463
	unsigned int id = args[0]->Uint32Value();
464
	String::AsciiValue data(args[1]);
465
 
466
	vm.sendToSocketThread(id, *data);
467
 
468
	return Undefined();
469
}
470