Subversion Repositories HomeAutomation

Rev

Rev 976 | Go to most recent revision | Blame | Last modification | View Log | SVN | RSS feed

/***************************************************************************
 *   Copyright (C) December 10, 2008 by Mattias Runge                             *
 *   mattias@runge.se                                                      *
 *   virtualmachine.cpp                                            *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

#include <map>


#include "virtualmachine.h"

VirtualMachine* VirtualMachine::myInstance = NULL;

VirtualMachine& VirtualMachine::getInstance()
{
    if (myInstance == NULL)
    {
        myInstance = new VirtualMachine();
    }

    return *myInstance;
}

void VirtualMachine::deleteInstance()
{
    if (myInstance != NULL)
    {
        delete myInstance;
        myInstance = NULL;
    }
}

VirtualMachine::VirtualMachine()
{
    
}

VirtualMachine::~VirtualMachine()
{
    
}

void VirtualMachine::run()
{
    SyslogStream &slog = SyslogStream::getInstance();

    string basePath = Settings::get("BasePath") + "Services/";
    string scriptName = "Base.js";
    string scriptFileName = basePath + "System/" + scriptName;

    if (!file_exists(scriptFileName))
    {
        slog << "Failed to load :" + scriptFileName + "\n";
    }

    string scriptSource = file_get_contents(scriptFileName);

    Handle<String> source =  String::New(scriptSource.c_str(), scriptSource.length());
    //each script name must be unique , for this demo I just run one embedded script, so the name can be fixed
    Handle<String> name = String::New(scriptName.c_str(), scriptName.length());

    // Create a template for the global object.
    myGlobal = ObjectTemplate::New();

    //associates "print" on script to the Print function
    myGlobal->Set(String::New("log"), FunctionTemplate::New(VirtualMachine_log));
    myGlobal->Set(String::New("sendCanMessage"), FunctionTemplate::New(VirtualMachine_sendCanMessage));
    myGlobal->Set(String::New("loadScript"), FunctionTemplate::New(VirtualMachine_loadScript));
    myGlobal->Set(String::New("startIntervalThread"), FunctionTemplate::New(VirtualMachine_startIntervalThread));
    myGlobal->Set(String::New("stopIntervalThread"), FunctionTemplate::New(VirtualMachine_stopIntervalThread));
    myGlobal->Set(String::New("startSocketThread"), FunctionTemplate::New(VirtualMachine_startSocketThread));
    myGlobal->Set(String::New("stopSocketThread"), FunctionTemplate::New(VirtualMachine_stopSocketThread));
    myGlobal->Set(String::New("sendToSocketThread"), FunctionTemplate::New(VirtualMachine_sendToSocketThread));

    //create context for the script
    myContext = Context::New(NULL, myGlobal);


    //access global context within this scope
    Context::Scope context_scope(myContext);
    //exception handler
    TryCatch try_catch;
    //compile script to binary code - JIT
    Handle<Script> script = Script::Compile(source, name);

    //check if we got problems on compilation
    if (script.IsEmpty())
    {

        // Print errors that happened during compilation.
        String::AsciiValue error(try_catch.Exception());
        string sError = *error;
        slog << "Failed to compile script: " + sError + "\n";
    }
    else
    {
        //no errors , let's continue
        Handle<Value> result = script->Run();

        //check if execution ended with errors
        if (result.IsEmpty())
        {
            // Print errors that happened during execution.
            String::AsciiValue error(try_catch.Exception());
            string sError = *error;
            slog << "Constructor: Failed to run script: " << sError << "\n";
        }
        else
        {
            loadScript("System/Startup.js");

            if (file_exists(basePath + "Autostart.js"))
            {
                loadScript("Autostart.js");
            }
            
            myFunctionHandleHeartbeat = Handle<Function>::Cast(myContext->Global()->Get(String::New("handleHeartbeat")));
            myFunctionOfflineCheck = Handle<Function>::Cast(myContext->Global()->Get(String::New("offlineCheck")));
            myFunctionHandleMessage = Handle<Function>::Cast(myContext->Global()->Get(String::New("handleMessage")));
            myFunctionStartup = Handle<Function>::Cast(myContext->Global()->Get(String::New("startup")));
        }
    }

    const int argc = 0;
    Handle<Value> argv[argc] = { };

    Handle<Value> result = myFunctionStartup->Call(myFunctionStartup, argc, argv); // argc and argv are your standard arguments to a function

    mySemaphore.lock();

    while (1)
    {
        string expression;

        while (myExpressions.size() > 0)
        {
            expression = myExpressions.pop();

            runExpression(expression);
        }

        CanMessage canMessage;

        while (myCanMessages.size() > 0)
        {
            canMessage = myCanMessages.pop();

            if (canMessage.isHeartbeat())
            {
                callHandleHeartbeat(stoi(canMessage.getData()["HardwareId"].getValue()));
            }
            else
            {
                callHandleMessage(canMessage);
            }
        }

        mySemaphore.wait();
    }

    mySemaphore.unlock();
}

void VirtualMachine::queueCanMessage(CanMessage canMessage)
{
    myCanMessages.push(canMessage);
    mySemaphore.broadcast();
}

void VirtualMachine::queueExpression(string expression)
{
    myExpressions.push(expression);
    mySemaphore.broadcast();
}

bool VirtualMachine::loadScript(string scriptName)
{
    SyslogStream &slog = SyslogStream::getInstance();

    string basePath = Settings::get("BasePath") + "/Services/";
    string scriptFileName = basePath + scriptName;

    if (!file_exists(scriptFileName))
    {
        slog << "Failed to load :" + scriptFileName + "\n";
        return false;
    }

    string scriptSource = file_get_contents(scriptFileName);

    Handle<String> source =  String::New(scriptSource.c_str(), scriptSource.length());
    //each script name must be unique , for this demo I just run one embedded script, so the name can be fixed
    Handle<String> name = String::New(scriptName.c_str(), scriptName.length());

    //access global context within this scope
    Context::Scope context_scope(myContext);
    //exception handler
    TryCatch try_catch;
    //compile script to binary code - JIT
    Handle<Script> script = Script::Compile(source, name);

    //check if we got problems on compilation
    if (script.IsEmpty())
    {
        // Print errors that happened during compilation.
        String::AsciiValue error(try_catch.Exception());
        string sError = *error;
        slog << "loadScript: Failed to compile script: " + sError + "\n";
        return false;
    }
    else
    {
        //no errors , let's continue
        Handle<Value> result = script->Run();

        //check if execution ended with errors
        if (result.IsEmpty())
        {
            // Print errors that happened during execution.
            String::AsciiValue error(try_catch.Exception());
            string sError = *error;
            slog << "loadScript: Failed to run script: " + sError + "\n";
            return false;
        }

        slog << "Loaded " + scriptName + "\n";
    }

    return true;
}

void VirtualMachine::callHandleHeartbeat(int hardwareId)
{
    const int argc = 1;
    Handle<Value> argv[argc] = { Integer::New(hardwareId) };

    Handle<Value> result = myFunctionHandleHeartbeat->Call(myFunctionHandleHeartbeat, argc, argv); // argc and argv are your standard arguments to a function
}

void VirtualMachine::callHandleMessage(CanMessage canMessage)
{
    const int argc = 6;
    Handle<Value> argv[argc] = {String::New(canMessage.getClassName().c_str()),
                                String::New(canMessage.getDirectionFlag().c_str()),
                                String::New(canMessage.getModuleName().c_str()),
                                Integer::New(canMessage.getModuleId()),
                                String::New(canMessage.getCommandName().c_str()),
                                String::New(canMessage.getJSONData().c_str()) };

    Handle<Value> result = myFunctionHandleMessage->Call(myFunctionHandleMessage, argc, argv); // argc and argv are your standard arguments to a function
}

unsigned int VirtualMachine::startIntervalThread(unsigned int timeout)
{
    IntervalThread intervalThread(timeout);

    myIntervalThreads[intervalThread.getId()] = intervalThread;
    myIntervalThreads[intervalThread.getId()].start();

    return intervalThread.getId();
}

bool VirtualMachine::stopIntervalThread(unsigned int id)
{
    if (myIntervalThreads.find(id) != myIntervalThreads.end())
    {
        myIntervalThreads.erase(id);
        return true;
    }

    return false;
}

unsigned int VirtualMachine::startSocketThread(string address, int port, unsigned int reconnectTimeout)
{
    SocketThread socketThread(address, port, reconnectTimeout);
    mySocketThreads[socketThread.getId()] = socketThread;
    mySocketThreads[socketThread.getId()].start();

    return socketThread.getId();
}

bool VirtualMachine::stopSocketThread(unsigned int id)
{
    if (mySocketThreads.find(id) != mySocketThreads.end())
    {
        mySocketThreads.erase(id);
        return true;
    }

    return false;
}

void VirtualMachine::sendToSocketThread(unsigned int id, string data)
{
    if (mySocketThreads.find(id) != mySocketThreads.end())
    {
        mySocketThreads[id].send(data);
    }
}

bool VirtualMachine::runExpression(string expression)
{
    SyslogStream &slog = SyslogStream::getInstance();

    string scriptName = "runExpression";

    Handle<String> source =  String::New(expression.c_str(), expression.length());
    //each script name must be unique , for this demo I just run one embedded script, so the name can be fixed
    Handle<String> name = String::New(scriptName.c_str(), scriptName.length());

    //access global context within this scope
    Context::Scope context_scope(myContext);
    //exception handler
    TryCatch try_catch;
    //compile script to binary code - JIT
    Handle<Script> script = Script::Compile(source, name);

    //check if we got problems on compilation
    if (script.IsEmpty())
    {
        // Print errors that happened during compilation.
        String::AsciiValue error(try_catch.Exception());
        string sError = *error;
        slog << "runExpression: Failed to compile script: " + sError + "\n";
        slog << expression << "\n\n";
        return false;
    }
    else
    {
        //no errors , let's continue
        Handle<Value> result = script->Run();

        //check if execution ended with errors
        if (result.IsEmpty())
        {
            // Print errors that happened during execution.
            String::AsciiValue error(try_catch.Exception());
            string sError = *error;
            slog << "runExpression: Failed to run script: " + sError + "\n";
            return false;
        }
    }

    return true;
}

Handle<Value> VirtualMachine_log(const Arguments& args)
{
    SyslogStream &slog = SyslogStream::getInstance();

    String::AsciiValue str(args[0]);

    slog << *str;

    return Undefined();
}

Handle<Value> VirtualMachine_sendCanMessage(const Arguments& args)
{
    //SyslogStream &slog = SyslogStream::getInstance();
    CanNetManager &canMan = CanNetManager::getInstance();

    CanMessage canMessage;

    String::AsciiValue className(args[0]);
    canMessage.setClassName(*className);
    String::AsciiValue directionFlag(args[1]);
    canMessage.setDirectionFlag(*directionFlag);
    String::AsciiValue moduleName(args[2]);
    canMessage.setModuleName(*moduleName);
    canMessage.setModuleId(args[3]->Uint32Value());
    String::AsciiValue commandName(args[4]);
    canMessage.setCommandName(*commandName);

    String::AsciiValue dataString(args[5]);

    vector<string> parts = explode(",", trim(*dataString, ','));
    map<string, CanVariable> data;

    for (int n = 0; n < parts.size(); n++)
    {
        vector<string> keyAndValue = explode(":", parts[n]);

        //string key = trim(keyAndValue[0], ' ');
        //string value = trim(keyAndValue[1], ' ');
        data[keyAndValue[0]] = CanVariable(keyAndValue[0], keyAndValue[1]);
    }

    canMessage.setData(data);
    
    canMan.sendMessage(canMessage);

    return Undefined();
}

Handle<Value> VirtualMachine_loadScript(const Arguments& args)
{
    VirtualMachine &vm = VirtualMachine::getInstance();

    String::AsciiValue str(args[0]);

    return Boolean::New(vm.loadScript(*str));
}

Handle<Value> VirtualMachine_startIntervalThread(const Arguments& args)
{
    VirtualMachine &vm = VirtualMachine::getInstance();

    unsigned int timeout = args[0]->Uint32Value();

    return Integer::New(vm.startIntervalThread(timeout));
}

Handle<Value> VirtualMachine_stopIntervalThread(const Arguments& args)
{
    VirtualMachine &vm = VirtualMachine::getInstance();

    unsigned int id = args[0]->Uint32Value();

    return Boolean::New(vm.stopIntervalThread(id));
}

Handle<Value> VirtualMachine_startSocketThread(const Arguments& args)
{
    VirtualMachine &vm = VirtualMachine::getInstance();

    String::AsciiValue address(args[0]);
    int port = args[1]->Uint32Value();
    unsigned int reconnectTimeout = args[2]->Uint32Value();

    return Integer::New(vm.startSocketThread(*address, port, reconnectTimeout));
}

Handle<Value> VirtualMachine_stopSocketThread(const Arguments& args)
{
    VirtualMachine &vm = VirtualMachine::getInstance();

    unsigned int id = args[0]->Uint32Value();

    return Boolean::New(vm.stopSocketThread(id));
}

Handle<Value> VirtualMachine_sendToSocketThread(const Arguments& args)
{
    VirtualMachine &vm = VirtualMachine::getInstance();

    unsigned int id = args[0]->Uint32Value();
    String::AsciiValue data(args[1]);

    vm.sendToSocketThread(id, *data);

    return Undefined();
}