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
 
62
	string basePath = Settings::get("BasePath") + "/Services/System/";
63
	string scriptName = "Base.js";
64
	string scriptFileName = basePath + scriptName;
65
 
66
	if (!file_exists(scriptFileName))
67
	{
68
		slog << "Failed to load :" << scriptFileName << "\n";
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));
84
	myGlobal->Set(String::New("setTimeout"), FunctionTemplate::New(VirtualMachine_setTimeout));
85
	myGlobal->Set(String::New("setInterval"), FunctionTemplate::New(VirtualMachine_setInterval));
86
	myGlobal->Set(String::New("clearTimeout"), FunctionTemplate::New(VirtualMachine_clearInterval));
87
	myGlobal->Set(String::New("clearInterval"), FunctionTemplate::New(VirtualMachine_clearInterval));
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
96
	TryCatch try_catch;
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
	{
103
 
104
		// Print errors that happened during compilation.
105
		String::AsciiValue error(try_catch.Exception());
106
		slog << "Failed to compile script: " << *error << "\n";
107
	}
108
	else
109
	{
110
		//no errors , let's continue
111
		Handle<Value> result = script->Run();
112
 
113
		//check if execution ended with errors
114
		if (result.IsEmpty())
115
		{
116
			// Print errors that happened during execution.
117
			String::AsciiValue error(try_catch.Exception());
118
			slog << "Constructor: Failed to run script: " << *error << "\n";
119
		}
120
		else
121
		{
122
			loadScript("System/Service.js");
123
			loadScript("System/ServiceManager.js");
124
			loadScript("System/CanMessage.js");
125
			loadScript("System/CanManager.js");
126
			loadScript("System/CanService.js");
127
			loadScript("System/Startup.js");
128
 
970 runge 129
			if (file_exists(basePath + "Autostart.js"))
130
			{
131
				loadScript("Autostart.js");
132
			}
133
 
969 runge 134
			myFunctionHandleHeartbeat = Handle<Function>::Cast(myContext->Global()->Get(String::New("handleHeartbeat")));
135
			myFunctionOfflineCheck = Handle<Function>::Cast(myContext->Global()->Get(String::New("offlineCheck")));
136
			myFunctionHandleMessage = Handle<Function>::Cast(myContext->Global()->Get(String::New("handleMessage")));
137
			myFunctionStartup = Handle<Function>::Cast(myContext->Global()->Get(String::New("startup")));
138
		}
139
	}
140
 
141
	const int argc = 0;
142
	Handle<Value> argv[argc] = { };
143
 
144
	Handle<Value> result = myFunctionStartup->Call(myFunctionStartup, argc, argv); // argc and argv are your standard arguments to a function
145
 
146
	mySemaphore.lock();
147
 
148
	while (1)
149
	{
150
		string expression;
151
 
152
		while (myExpressions.size() > 0)
153
		{
154
			expression = myExpressions.pop();
155
 
156
			runExpression(expression);
157
		}
158
 
159
		CanMessage canMessage;
160
 
161
		while (myCanMessages.size() > 0)
162
		{
163
			canMessage = myCanMessages.pop();
164
 
165
			if (canMessage.isHeartbeat())
166
			{
167
				callHandleHeartbeat(stoi(canMessage.getData()["HardwareId"].getValue()));
168
			}
169
			else
170
			{
171
				callHandleMessage(canMessage);
172
			}
173
		}
174
 
175
		mySemaphore.wait();
176
	}
177
 
178
	mySemaphore.unlock();
179
}
180
 
181
void VirtualMachine::queueCanMessage(CanMessage canMessage)
182
{
183
	myCanMessages.push(canMessage);
184
	mySemaphore.broadcast();
185
}
186
 
187
void VirtualMachine::queueExpression(string expression)
188
{
189
	myExpressions.push(expression);
190
	mySemaphore.broadcast();
191
}
192
 
193
bool VirtualMachine::loadScript(string scriptName)
194
{
195
	SyslogStream &slog = SyslogStream::getInstance();
196
 
197
	string basePath = Settings::get("BasePath") + "/Services/";
198
	string scriptFileName = basePath + scriptName;
199
 
200
	if (!file_exists(scriptFileName))
201
	{
202
		slog << "Failed to load :" << scriptFileName << "\n";
203
		return false;
204
	}
205
 
206
	string scriptSource = file_get_contents(scriptFileName);
207
 
208
	Handle<String> source =  String::New(scriptSource.c_str(), scriptSource.length());
209
	//each script name must be unique , for this demo I just run one embedded script, so the name can be fixed
210
	Handle<String> name = String::New(scriptName.c_str(), scriptName.length());
211
 
212
	//access global context within this scope
213
	Context::Scope context_scope(myContext);
214
	//exception handler
215
	TryCatch try_catch;
216
	//compile script to binary code - JIT
217
	Handle<Script> script = Script::Compile(source, name);
218
 
219
	//check if we got problems on compilation
220
	if (script.IsEmpty())
221
	{
222
		// Print errors that happened during compilation.
223
		String::AsciiValue error(try_catch.Exception());
224
		slog << "Failed to compile script: " << *error << "\n";
225
		return false;
226
	}
227
	else
228
	{
229
		//no errors , let's continue
230
		Handle<Value> result = script->Run();
231
 
232
		//check if execution ended with errors
233
		if (result.IsEmpty())
234
		{
235
			// Print errors that happened during execution.
236
			String::AsciiValue error(try_catch.Exception());
237
			slog << "loadScript: Failed to run script: " << *error << "\n";
238
			return false;
239
		}
240
	}
241
 
242
	return true;
243
}
244
 
245
void VirtualMachine::callHandleHeartbeat(int hardwareId)
246
{
247
	const int argc = 1;
248
	Handle<Value> argv[argc] = { Integer::New(hardwareId) };
249
 
250
	Handle<Value> result = myFunctionHandleHeartbeat->Call(myFunctionHandleHeartbeat, argc, argv); // argc and argv are your standard arguments to a function
251
}
252
 
253
void VirtualMachine::callHandleMessage(CanMessage canMessage)
254
{
255
	const int argc = 6;
256
	Handle<Value> argv[argc] = {String::New(canMessage.getClassName().c_str()),
257
								String::New(canMessage.getDirectionFlag().c_str()),
258
								String::New(canMessage.getModuleName().c_str()),
259
								Integer::New(canMessage.getModuleId()),
260
								String::New(canMessage.getCommandName().c_str()),
261
								String::New(canMessage.getJSONData().c_str()) };
262
 
263
	Handle<Value> result = myFunctionHandleMessage->Call(myFunctionHandleMessage, argc, argv); // argc and argv are your standard arguments to a function
264
}
265
 
266
int VirtualMachine::setInterval(string expression, int interval, bool single)
267
{
268
	IntervalThread intervalThread(expression, interval, single);
269
	intervalThread.start();
270
 
271
	int timeoutId = myTimers.size();
272
	myTimers[timeoutId] = intervalThread;
273
	myTimers[timeoutId].start();
274
 
275
	return timeoutId;
276
}
277
 
278
bool VirtualMachine::clearInterval(int timeoutId)
279
{
280
	if (myTimers.find(timeoutId) != myTimers.end())
281
	{
282
		myTimers.erase(timeoutId);
283
		return true;
284
	}
285
 
286
	return false;
287
}
288
 
289
bool VirtualMachine::runExpression(string expression)
290
{
291
	SyslogStream &slog = SyslogStream::getInstance();
292
 
293
	string scriptName = "runExpression";
294
 
295
	Handle<String> source =  String::New(expression.c_str(), expression.length());
296
	//each script name must be unique , for this demo I just run one embedded script, so the name can be fixed
297
	Handle<String> name = String::New(scriptName.c_str(), scriptName.length());
298
 
299
	//access global context within this scope
300
	Context::Scope context_scope(myContext);
301
	//exception handler
302
	TryCatch try_catch;
303
	//compile script to binary code - JIT
304
	Handle<Script> script = Script::Compile(source, name);
305
 
306
	//check if we got problems on compilation
307
	if (script.IsEmpty())
308
	{
309
		// Print errors that happened during compilation.
310
		String::AsciiValue error(try_catch.Exception());
311
		slog << "Failed to compile script: " << *error << "\n";
312
 
313
		return false;
314
	}
315
	else
316
	{
317
		//no errors , let's continue
318
		Handle<Value> result = script->Run();
319
 
320
		//check if execution ended with errors
321
		if (result.IsEmpty())
322
		{
323
			// Print errors that happened during execution.
324
			String::AsciiValue error(try_catch.Exception());
325
			slog << "runExpression: Failed to run script: " << *error << "\n";
326
			return false;
327
		}
328
	}
329
 
330
	return true;
331
}
332
 
333
Handle<Value> VirtualMachine_log(const Arguments& args)
334
{
335
	SyslogStream &slog = SyslogStream::getInstance();
336
 
337
	String::AsciiValue str(args[0]);
338
 
339
	slog << *str;
340
 
341
	return Undefined();
342
}
343
 
344
Handle<Value> VirtualMachine_sendCanMessage(const Arguments& args)
345
{
346
	SyslogStream &slog = SyslogStream::getInstance();
347
	CanNetManager &canMan = CanNetManager::getInstance();
348
 
349
	CanMessage canMessage;
350
 
351
	String::AsciiValue className(args[0]);
352
	canMessage.setClassName(*className);
353
	String::AsciiValue directionFlag(args[1]);
354
	canMessage.setDirectionFlag(*directionFlag);
355
	String::AsciiValue moduleName(args[2]);
356
	canMessage.setModuleName(*moduleName);
357
	canMessage.setModuleId(args[3]->Uint32Value());
358
	String::AsciiValue commandName(args[4]);
359
	canMessage.setCommandName(*commandName);
360
 
361
	String::AsciiValue dataString(args[5]);
362
 
363
	vector<string> parts = explode(",", trim(*dataString, ','));
364
	map<string, CanVariable> data;
365
 
366
	for (int n = 0; n < parts.size(); n++)
367
	{
368
		vector<string> keyAndValue = explode(":", parts[n]);
369
 
370
		//string key = trim(keyAndValue[0], ' ');
371
		//string value = trim(keyAndValue[1], ' ');
372
		data[keyAndValue[0]] = CanVariable(keyAndValue[0], keyAndValue[1]);
373
	}
374
 
375
	canMessage.setData(data);
376
 
377
	canMan.sendMessage(canMessage);
378
 
379
	return Undefined();
380
}
381
 
382
Handle<Value> VirtualMachine_loadScript(const Arguments& args)
383
{
384
	VirtualMachine &vm = VirtualMachine::getInstance();
385
 
386
	String::AsciiValue str(args[0]);
387
 
388
	return Boolean::New(vm.loadScript(*str));
389
}
390
 
391
Handle<Value> VirtualMachine_setInterval(const Arguments& args)
392
{
393
	VirtualMachine &vm = VirtualMachine::getInstance();
394
 
395
	String::AsciiValue expression(args[0]);
396
	unsigned int interval = args[1]->Uint32Value();
397
 
398
	return Integer::New(vm.setInterval(*expression, interval, false));
399
}
400
 
401
Handle<Value> VirtualMachine_setTimeout(const Arguments& args)
402
{
403
	VirtualMachine &vm = VirtualMachine::getInstance();
404
 
405
	String::AsciiValue expression(args[0]);
406
	unsigned int interval = args[1]->Uint32Value();
407
 
408
	return Integer::New(vm.setInterval(*expression, interval, true));
409
}
410
 
411
Handle<Value> VirtualMachine_clearInterval(const Arguments& args)
412
{
413
	VirtualMachine &vm = VirtualMachine::getInstance();
414
 
415
	return Boolean::New(vm.clearInterval(args[0]->Uint32Value()));
416
}