source: trunk/src/nrEngine/Script.cpp @ 46

Revision 46, 17.1 KB checked in by art, 12 years ago (diff)
  • Include changes in *.cpp due to the directory structure change
  • Plugin build fixes (now plugins are build without preinstalling of the engine)
  • ToDo?: Uninstall do not remove the lib*.so files
Line 
1/***************************************************************************
2 *                                                                         *
3 *   (c) Art Tevs, MPI Informatik Saarbruecken                             *
4 *       mailto: <tevs@mpi-sb.mpg.de>                                      *
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 ***************************************************************************/
12
13
14//----------------------------------------------------------------------------------
15// Includes
16//----------------------------------------------------------------------------------
17#include <nrEngine/Script.h>
18#include <nrEngine/ScriptEngine.h>
19#include <nrEngine/Clock.h>
20#include <nrEngine/Log.h>
21#include <nrEngine/Kernel.h>
22#include <boost/algorithm/string/trim.hpp>
23
24namespace nrEngine {
25
26        //----------------------------------------------------------------------------------
27        Script::Script() : IScript("Script")
28        {
29                mLastLine = 0;
30                mLoop = NO_LOOP;
31                mTime = 0;
32                bRunStepwise = true;
33                mStopTime = 0;
34                mRunningTimeLength = 0;
35                mFirstRunTimed = true;
36                mScriptStartTime = 0;
37        }
38
39        //----------------------------------------------------------------------------------
40        Script::~Script()
41        {
42                unloadResource();
43        }
44
45
46        //----------------------------------------------------------------------------------
47        Result Script::loadFromString(const std::string& str)
48        {
49                // set task name as resource name
50                setTaskName(getResourceName() + "_ScriptTask");
51
52                // setup content
53                mContent = str;
54
55                // parse it
56                Result ret = parse(mContent);
57                if (ret == OK) notifyLoaded();
58
59                return ret;
60        }
61
62        //------------------------------------------------------------------------------
63        std::string Script::cleanScript(const std::string& script)
64        {
65                // check the lenght of the script
66                std::stringstream str(script);
67                if (script.length() == 0 || !str.good()) return script;
68                std::string resultScript;
69
70                // get linewise
71                char buf[2048];
72                std::string buffer;
73                std::string::size_type pos;
74                std::string line;
75
76                // for the whole stream do following
77                while (!str.eof())
78                {
79                        // get line
80                        str.getline(buf, 2048);
81                        buffer = std::string(buf);
82
83                        // remove comments
84                        pos = buffer.find("//");
85                        if (pos == std::string::npos)
86                                line = buffer;
87                        else
88                                line = buffer.substr(0, pos);
89
90                        // trim the line
91                        boost::algorithm::trim(line);
92
93                        // if the line is empty, so get the next one
94                        if (line.length() == 0) continue;
95
96                        // if we are here, so we do not have any comments or empty lines
97                        resultScript += line;
98                        if (!str.eof()) resultScript += std::string("\n");
99                }
100
101                return resultScript;
102        }
103
104        //------------------------------------------------------------------------------
105        std::string Script::parseSubscripts(const std::string& script)
106        {
107                // vars
108                int depth = 0, count = 0, curdepth = 0;
109
110                // for parsing we are using stack-algorithm
111                //std::vector<std::string> stack;
112                //stack.push_back(std::string(""));
113                //stack.push_back(std::string(""));
114
115                std::string main;// = &stack[0];
116                std::string sub;//  = &stack[1];
117                std::string* cur  = &main;
118               
119                for (unsigned int i=0; i < script.length(); i++)
120                {
121                        if (script.at(i) == '{') curdepth ++;
122                       
123                        if (script.at(i) == '{' && cur == &main && curdepth == 1)
124                        {
125                                cur = &sub;
126                                depth ++;
127                        }
128                        else if (script.at(i) == '}' && cur == &sub && curdepth == 1)
129                        {
130                                count ++;
131                                depth --;
132                               
133                                // create name for the new subscript
134                                std::stringstream subscriptName;
135                                subscriptName << getResourceName() << std::string("_sub_") << count ;
136
137                                // current top element is a new script, so create it
138                                ResourcePtr<IScript> scr = Engine::sResourceManager()->createResource(subscriptName.str(), getResourceName(), "Script");
139
140                                // if script could be created, so fill it and store, to execute later
141                                if (scr.valid())
142                                {
143                                        scr.lockResource();
144                                                scr->loadFromString(sub);
145                                        scr.unlockResource();
146                                        mSubscripts.push_back(scr);
147                                }else{
148                                        NR_Log(Log::LOG_ENGINE, Log::LL_ERROR, "Script (%s): Subscript could not be created. Maybe no proper loader exists. Script will not run.\n", getResourceName().c_str());
149                                        return std::string("");
150                                }
151                               
152                                // reset pointer back to main script and empty the subscript string
153                                cur = &main;
154                                sub = std::string("");
155                        }
156                        else
157                        {
158                                // write character into valid script
159                                *cur += script.at(i);
160                        }
161                       
162                        if (script.at(i) == '}') curdepth --;
163                }
164               
165                // check if closing brackets were complete
166                if (depth != 0 || curdepth != 0)
167                {
168                        NR_Log(Log::LOG_ENGINE, Log::LL_ERROR, "Script (%s): Subscript-brackets were not used properly. Depth is %d/%d, must be 0/0. Script disabled!", getResourceName().c_str(), depth, curdepth);
169                        return std::string("");
170                }
171
172                //printf("\n%s\n%s\n\n", getResourceName().c_str(), main.c_str());
173               
174                return main;
175               
176                /*std::string* top = &stack[stack.size()-1];
177
178                // go through the given script
179                for (unsigned int i=0; i < script.length(); i++)
180                {
181                        top = &(stack[stack.size()-1]);
182                        if (script.at(i) == '{')
183                        {
184                                depth ++;
185                                stack.push_back(std::string(""));
186                        }
187                        else if (script.at(i) == '}')
188                        {
189                                depth --;
190                                count ++;
191
192                                if (depth >= 0)
193                                {
194                                        // create name for the new subscript
195                                        std::stringstream subscriptName;
196                                        subscriptName << getResourceName() << std::string("_sub_") << count << std::string("_") << depth;
197
198                                        // current top element is a new script, so create it
199                                        ResourcePtr<IScript> scr = Engine::sResourceManager()->createResource(subscriptName.str(), getResourceName(), "Script");
200
201                                        // if script could be created, so fill it and store, to execute later
202                                        if (scr.isNull())
203                                        {
204                                                NR_Log(Log::LOG_ENGINE, Log::LL_ERROR, "Script (%s): Subscript could not be created. Maybe no proper loader exists. Script will not run.\n", getResourceName().c_str());
205                                                return std::string("");
206                                        }else{
207                                                scr.lockResource();
208                                                        scr->loadFromString(*top);
209                                                scr.unlockResource();
210                                                mSubscripts.push_back(scr);
211                                        }
212
213                                        // remove top element
214                                        stack.pop_back();
215                                }
216                        }else{
217                                *top += script.at(i);
218                        }
219                }
220
221
222                // check if closing brackets were complete
223                if (depth != 0)
224                {
225                        NR_Log(Log::LOG_ENGINE, Log::LL_WARNING, "Script (%s): Subscript-brackets were not used properly. Depth is %d, must be 0. Script could not run as expected!", getResourceName().c_str(), depth);
226                }
227
228                // return mainscript back
229                return cleanScript(stack[0]);*/
230        }
231
232        //------------------------------------------------------------------------------
233        Result Script::parse(const std::string& script)
234        {
235                // remove all non-commands from the script
236                // check for subscripts
237                std::stringstream str(parseSubscripts(cleanScript(script)));
238                if (script.length() == 0 || !str.good()) return OK;
239
240                //printf("%s: \n%s\n---\n\n", getResourceName().c_str(),str.str().c_str());
241                // get linewise
242                char buf[1024];
243                std::string::size_type pos;
244                std::string line;
245
246                while (!str.eof())
247                {
248                        // get line
249                        str.getline(buf, 1024);
250                        line = std::string(buf);
251
252                        // check for script parameters
253                        pos = line.find('^');
254                        if (pos != std::string::npos)
255                        {
256                                // get parameter
257                                std::string sparam = line.substr(pos+1);
258                                boost::algorithm::trim(sparam);
259                               
260                                // now tokenize parameter, to get special argument values for the parameters
261                                std::vector<std::string> args;
262                                std::string param;
263                                tokenize(sparam, param, args); 
264                               
265                                // set parameters
266                                if (!setParameter(param, args))
267                                {
268                                        NR_Log(Log::LOG_ENGINE, Log::LL_ERROR, "Script: Unknown local parameter %s in line \"%s\"", param.c_str(), line.c_str());
269                                        return SCRIPT_PARSE_ERROR;
270                                }
271                                continue;
272                        }
273
274                        // check for the timestamp
275                        pos = line.find('|');
276                        Command cmd;
277                        cmd.timestamp = 0;
278                        cmd.estimatedStart = 0;
279
280                        // timestamp found, so do extra stuff with this kind of commands
281                        if(pos != std::string::npos )
282                        {
283                                // retrieve the timestamp and the command and store it into the command buffer
284                                std::string time = line.substr(0,pos);
285                                boost::algorithm::trim(time);
286                                std::string command = line.substr(pos+1);
287
288                                // try to convert the time and add the command to the list
289                                try{
290                                        cmd.timestamp = boost::lexical_cast<float32>(time);
291                                        tokenize(command, cmd.cmd, cmd.args);
292                                        if (cmd.args.size() > 0 && cmd.args[0].length() > 0)
293                                        {
294                                                if (cmd.cmd == std::string("_stop_"))
295                                                {
296                                                        mRunningTimeLength = cmd.timestamp;
297                                                }else
298                                                        mTimedCommand.push_back(cmd);
299                                        }
300                                }catch(...){
301                                        NR_Log(Log::LOG_ENGINE, Log::LL_ERROR, "Script: Unknown syntax in \"%s\"\n", line.c_str());
302                                        return SCRIPT_PARSE_ERROR;
303                                }
304
305                        // timestamp not found, so it is a simple command
306                        }else{
307                                tokenize(line, cmd.cmd, cmd.args);
308                                if (cmd.args.size() > 0 && cmd.args[0].length() > 0)
309                                {
310                                        mCommand.push_back(cmd);
311                                }
312                        }
313                }
314
315                resetCommandFifo();
316
317                // if we got any command so we are valid
318                return OK;
319        }
320
321        //------------------------------------------------------------------------------
322        void Script::tokenize(const std::string& str, std::string& cmd, std::vector<std::string>& tokens)
323        {
324                // do not tokenize if we do not get any token
325                if (str.length() == 0) return;
326
327                using namespace std;
328
329                // tokenize on space characters
330                const string delimiters = " ";
331
332                // Skip delimiters at beginning.
333                string::size_type lastPos = str.find_first_not_of(delimiters, 0);
334
335                // Find first "non-delimiter".
336                string::size_type pos     = str.find_first_of(delimiters, lastPos);
337
338                while (pos != string::npos || lastPos != string::npos)
339                {
340                        // Found a token, add it to the vector.
341                        if (pos  - lastPos > 0)
342                                tokens.push_back(str.substr(lastPos, pos - lastPos));
343
344                        // Skip delimiters.  Note the "not_of"
345                        lastPos = str.find_first_not_of(delimiters, pos);
346
347                        // Find next "non-delimiter"
348                        pos = str.find_first_of(delimiters, lastPos);
349                }
350
351                // now the first element in the token list is the command name
352                cmd = tokens[0];
353        }
354
355        //------------------------------------------------------------------------------
356        bool Script::setParameter(const std::string& param, const std::vector<std::string>& args){
357
358                if (param == "loop")
359                        mLoop |= LOOP;
360                else if (param == "loop_seq")
361                        mLoop |= LOOP_COMMAND;
362                else if (param == "loop_timed")
363                        mLoop |= LOOP_TIMED_COMMAND;
364                else if (param == "runcomplete")
365                        bRunStepwise = false;
366                //else if (param == "runonce")
367                //      setRunOnce(true);
368                else if (param == "start_at")
369                        try{
370                                if (args.size() > 1) mScriptStartTime = boost::lexical_cast<float32>(args[1]);
371                                else throw boost::bad_lexical_cast();
372                        }catch(boost::bad_lexical_cast &){
373                                NR_Log(Log::LOG_ENGINE, Log::LL_WARNING, "Script: %s - not a valid value for \"start_at\" parameter specified", getResourceName().c_str());
374                        }
375                else
376                        return false;
377
378                return true;
379        }
380
381        //------------------------------------------------------------------------------
382        void Script::resetCommandFifo()
383        {
384                // the untimed command fifo is very simple
385                // the commands are executed in the way they were found in the file
386                mCommandFifo.clear();
387                for (uint32 i=0; i < mCommand.size(); i++){
388                        mCommandFifo.push_front(i);
389                }
390        }
391
392        //------------------------------------------------------------------------------
393        void Script::resetTimedCommandFifo(bool firstReset)
394        {
395                if (firstReset) mTimedCommandFifo.clear();
396
397                // timed commands could only be resetted when they
398                // are already executed
399               
400                // the timed commands are also setted in the fifo
401                // the estimated end time is also computed
402                for (int32 i=0; i < (int32)mTimedCommand.size(); i++)
403                {
404                        // check whenever fifo does not contain this command
405                        std::list< int32 >::iterator it = std::find(mTimedCommandFifo.begin(), mTimedCommandFifo.end(), i);
406                       
407                        // if such command not exists, then reset it
408                        if (it == mTimedCommandFifo.end() || firstReset)
409                        {
410                                mTimedCommandFifo.push_front(i);
411                                mTimedCommand[i].estimatedStart = mTimer->getTime() + mTimedCommand[i].timestamp;
412                        }
413                }
414        }
415
416        //----------------------------------------------------------------------------------
417        Result Script::step()
418        {
419                // update time
420                mTimer->setPause(false);
421                mTime = mTimer->getTime();
422               
423                //printf("%s (%f - %f)\n", getResourceName().c_str(), mTime, mScriptStartTime);
424
425                // do execute the script only if start time was passed
426                if (mScriptStartTime > mTime)
427                {
428                        // if the script is waiting for the start, then it runs in a loop mode
429                        setRunOnce(false);
430                       
431                        // do nothing at now
432                        return OK;
433                }
434               
435                // if the script running first time, then do reset on timed commands
436                if (mFirstRunTimed)
437                {
438                        // initialize timed commands
439                        mFirstRunTimed = false;
440                        resetTimedCommandFifo(true);
441                        setRunOnce(true);
442                }
443               
444                // compute stop time, when to stop the script if such defined
445                if (mStopTime == 0 && mRunningTimeLength > 0) mStopTime = mTime + mRunningTimeLength;
446               
447                // ----- timed commands (quasi parallel execution) -----
448                // scan through the command fifo
449                std::list<int32>::iterator it;
450                for (it = mTimedCommandFifo.begin(); it != mTimedCommandFifo.end(); )
451                {
452                        int32 id = *it;
453                        mTimedCommand[id].time = mTime;
454                       
455                        //std::string msg;
456                        //for (unsigned int i=1; i < mTimedCommand[id].args.size(); i++) msg += std::string(" ") + mTimedCommand[id].args[i];
457                        //printf("%s (%s): %f %f\n", mTimedCommand[id].cmd.c_str(), msg.c_str(), mTimedCommand[id].estimatedStart, mTimedCommand[id].time);
458
459                        // if the estimated start time is reeached, so start the command and remove it from the queue
460                        if (mTimedCommand[id].estimatedStart < mTimedCommand[id].time)
461                        {
462                                // remove the command from the fifo
463                                it = mTimedCommandFifo.erase(it);
464
465                                // call the commando
466                                Engine::sScriptEngine()->call(mTimedCommand[id].cmd, mTimedCommand[id].args);
467                        }else
468                                it ++;
469                }
470
471                // ----- sequentiall commands -----
472                if (mCommandFifo.size())
473                {
474                        // get the id of the command
475                        int32 id = mCommandFifo.back();
476                        mCommandFifo.pop_back();
477
478                        // check if we have a stop commando
479                        if (mCommand[id].cmd != std::string("_stop_"))
480                        {
481                                Engine::sScriptEngine()->call(mCommand[id].cmd, mCommand[id].args);
482                        }else{
483                                Engine::sKernel()->RemoveTask(this->getTaskID());
484                        }
485                }
486
487                // reset the command list
488                reset();
489
490                // check whenever execution time of the script exceeds
491                if (mTime > mStopTime && mStopTime > 0)
492                {
493                        Engine::sKernel()->RemoveTask(this->getTaskID());
494                }
495
496                //printf("\n\n");
497               
498                return OK;
499        }
500
501        //----------------------------------------------------------------------------------
502        bool Script::hasCommands()
503        {
504                if (mCommandFifo.size() == 0 && mTimedCommandFifo.size() == 0 && !(mLoop & LOOP_COMMAND) && !(mLoop & LOOP_TIMED_COMMAND))
505                        return false;
506
507                return true;
508        }
509
510        //----------------------------------------------------------------------------------
511        Result Script::run()
512        {
513                // check whenver this script is running stepwise
514                if (bRunStepwise) return step();
515
516                // make full run of the script
517                return fullRun();
518        }
519
520
521        //----------------------------------------------------------------------------------
522        Result Script::fullRun()
523        {
524
525                // check for looped script
526                static bool warned = false;
527                if (!warned && mLoop & LOOP_COMMAND){
528                        warned = true;
529                        NR_Log(Log::LOG_ENGINE, Log::LL_WARNING, "Script: %s - you are executing a looped script!!! Check this!!!", getResourceName().c_str());
530                }
531
532                // loop the content of the script if script should be looped
533                bool bLoop = false;
534                do {
535                        // we go through the whole command list and execute it.
536                        while (mCommandFifo.size() > 0)
537                        {
538                                // get the id of the command
539                                int32 id = mCommandFifo.back();
540                                mCommandFifo.pop_back();
541
542                                // execute it
543                                Engine::sScriptEngine()->call(mCommand[id].cmd, mCommand[id].args);
544                        }
545
546                        // reset the list and check if the task is removed from the kernel
547                        reset();
548                        bLoop = hasCommands();
549
550                } while(bLoop && (mLoop & LOOP_COMMAND));
551
552                return OK;
553        }
554
555        //--------------------------------------------------------------------
556        void Script::onStartScript()
557        {
558                // do run all subscripts specified in this script
559                for (unsigned int i = 0; i < mSubscripts.size(); i++)
560                {
561                        // cast subscripts to script objects
562                        Script* subscript = dynamic_cast<Script*>(mSubscripts[i].get());
563                        if (subscript)
564                        {
565                                // first set starting time of subscripts relative to the main script
566                                subscript->mScriptStartTime += mScriptStartTime;
567                               
568                                // now execute the script
569                                subscript->execute();
570                               
571                                // setup dependencies
572                                addTaskDependency(subscript->getTaskID());
573                        }
574                       
575                }
576               
577                // intialize the timer and pause it
578                mTimer = Engine::sClock()->createTimer();
579                mTimer->setPause(true);
580               
581                // reset the script, so it can run
582                resetCommandFifo();
583                resetTimedCommandFifo(true);
584                mFirstRunTimed = true;
585        }
586
587        //----------------------------------------------------------------------------------
588        bool Script::reset()
589        {
590                if (mCommandFifo.size() == 0 && (mLoop & LOOP_COMMAND)){
591                        resetCommandFifo();
592                }else if (mTimedCommandFifo.size() == 0 && (mLoop & LOOP_TIMED_COMMAND)){
593                        resetTimedCommandFifo(false);
594                }
595                return true;
596        }
597
598};
599
Note: See TracBrowser for help on using the repository browser.