Script.cpp

00001 /***************************************************************************
00002  *                                                                         *
00003  *   (c) Art Tevs, MPI Informatik Saarbruecken                             *
00004  *       mailto: <tevs@mpi-sb.mpg.de>                                      *
00005  *                                                                         *
00006  *   This program is free software; you can redistribute it and/or modify  *
00007  *   it under the terms of the GNU General Public License as published by  *
00008  *   the Free Software Foundation; either version 2 of the License, or     *
00009  *   (at your option) any later version.                                   *
00010  *                                                                         *
00011  ***************************************************************************/
00012 
00013 
00014 //----------------------------------------------------------------------------------
00015 // Includes
00016 //----------------------------------------------------------------------------------
00017 #include "Script.h"
00018 #include "ScriptEngine.h"
00019 #include "Clock.h"
00020 #include "Log.h"
00021 #include "Kernel.h"
00022 #include <boost/algorithm/string/trim.hpp>
00023 
00024 namespace nrEngine {
00025 
00026         //----------------------------------------------------------------------------------
00027         Script::Script() : IScript("Script")
00028         {
00029                 mLastLine = 0;
00030                 mLoop = NO_LOOP;
00031                 mTime = 0;
00032                 bRunStepwise = true;
00033                 mStopTime = 0;
00034                 mRunningTimeLength = 0;
00035                 mFirstRunTimed = true;
00036                 mScriptStartTime = 0;
00037         }
00038 
00039         //----------------------------------------------------------------------------------
00040         Script::~Script()
00041         {
00042                 unloadResource();
00043         }
00044 
00045 
00046         //----------------------------------------------------------------------------------
00047         Result Script::loadFromString(const std::string& str)
00048         {
00049                 // set task name as resource name
00050                 setTaskName(getResourceName() + "_ScriptTask");
00051 
00052                 // setup content
00053                 mContent = str;
00054 
00055                 // parse it
00056                 Result ret = parse(mContent);
00057                 if (ret == OK) notifyLoaded();
00058 
00059                 return ret;
00060         }
00061 
00062         //------------------------------------------------------------------------------
00063         std::string Script::cleanScript(const std::string& script)
00064         {
00065                 // check the lenght of the script
00066                 std::stringstream str(script);
00067                 if (script.length() == 0 || !str.good()) return script;
00068                 std::string resultScript;
00069 
00070                 // get linewise
00071                 char buf[2048];
00072                 std::string buffer;
00073                 std::string::size_type pos;
00074                 std::string line;
00075 
00076                 // for the whole stream do following
00077                 while (!str.eof())
00078                 {
00079                         // get line
00080                         str.getline(buf, 2048);
00081                         buffer = std::string(buf);
00082 
00083                         // remove comments
00084                         pos = buffer.find("//");
00085                         if (pos == std::string::npos)
00086                                 line = buffer;
00087                         else
00088                                 line = buffer.substr(0, pos);
00089 
00090                         // trim the line
00091                         boost::algorithm::trim(line);
00092 
00093                         // if the line is empty, so get the next one
00094                         if (line.length() == 0) continue;
00095 
00096                         // if we are here, so we do not have any comments or empty lines
00097                         resultScript += line;
00098                         if (!str.eof()) resultScript += std::string("\n");
00099                 }
00100 
00101                 return resultScript;
00102         }
00103 
00104         //------------------------------------------------------------------------------
00105         std::string Script::parseSubscripts(const std::string& script)
00106         {
00107                 // vars
00108                 int depth = 0, count = 0, curdepth = 0;
00109 
00110                 // for parsing we are using stack-algorithm
00111                 //std::vector<std::string> stack;
00112                 //stack.push_back(std::string(""));
00113                 //stack.push_back(std::string(""));
00114 
00115                 std::string main;// = &stack[0];
00116                 std::string sub;//  = &stack[1];
00117                 std::string* cur  = &main;
00118                 
00119                 for (unsigned int i=0; i < script.length(); i++)
00120                 {
00121                         if (script.at(i) == '{') curdepth ++;
00122                         
00123                         if (script.at(i) == '{' && cur == &main && curdepth == 1)
00124                         {
00125                                 cur = &sub;
00126                                 depth ++;
00127                         }
00128                         else if (script.at(i) == '}' && cur == &sub && curdepth == 1)
00129                         {
00130                                 count ++;
00131                                 depth --;
00132                                 
00133                                 // create name for the new subscript
00134                                 std::stringstream subscriptName;
00135                                 subscriptName << getResourceName() << std::string("_sub_") << count ;
00136 
00137                                 // current top element is a new script, so create it
00138                                 ResourcePtr<IScript> scr = Engine::sResourceManager()->createResource(subscriptName.str(), getResourceName(), "Script");
00139 
00140                                 // if script could be created, so fill it and store, to execute later
00141                                 if (scr.valid())
00142                                 {
00143                                         scr.lockResource();
00144                                                 scr->loadFromString(sub);
00145                                         scr.unlockResource();
00146                                         mSubscripts.push_back(scr);
00147                                 }else{
00148                                         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());
00149                                         return std::string("");
00150                                 }
00151                                 
00152                                 // reset pointer back to main script and empty the subscript string
00153                                 cur = &main;
00154                                 sub = std::string("");
00155                         }
00156                         else
00157                         {
00158                                 // write character into valid script
00159                                 *cur += script.at(i);
00160                         }
00161                         
00162                         if (script.at(i) == '}') curdepth --;
00163                 }
00164                 
00165                 // check if closing brackets were complete
00166                 if (depth != 0 || curdepth != 0)
00167                 {
00168                         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);
00169                         return std::string("");
00170                 }
00171 
00172                 //printf("\n%s\n%s\n\n", getResourceName().c_str(), main.c_str());
00173                 
00174                 return main;
00175                 
00176                 /*std::string* top = &stack[stack.size()-1];
00177 
00178                 // go through the given script
00179                 for (unsigned int i=0; i < script.length(); i++)
00180                 {
00181                         top = &(stack[stack.size()-1]);
00182                         if (script.at(i) == '{')
00183                         {
00184                                 depth ++;
00185                                 stack.push_back(std::string(""));
00186                         }
00187                         else if (script.at(i) == '}')
00188                         {
00189                                 depth --;
00190                                 count ++;
00191 
00192                                 if (depth >= 0)
00193                                 {
00194                                         // create name for the new subscript
00195                                         std::stringstream subscriptName;
00196                                         subscriptName << getResourceName() << std::string("_sub_") << count << std::string("_") << depth;
00197 
00198                                         // current top element is a new script, so create it
00199                                         ResourcePtr<IScript> scr = Engine::sResourceManager()->createResource(subscriptName.str(), getResourceName(), "Script");
00200 
00201                                         // if script could be created, so fill it and store, to execute later
00202                                         if (scr.isNull())
00203                                         {
00204                                                 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());
00205                                                 return std::string("");
00206                                         }else{
00207                                                 scr.lockResource();
00208                                                         scr->loadFromString(*top);
00209                                                 scr.unlockResource();
00210                                                 mSubscripts.push_back(scr);
00211                                         }
00212 
00213                                         // remove top element
00214                                         stack.pop_back();
00215                                 }
00216                         }else{
00217                                 *top += script.at(i);
00218                         }
00219                 }
00220 
00221 
00222                 // check if closing brackets were complete
00223                 if (depth != 0)
00224                 {
00225                         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);
00226                 }
00227 
00228                 // return mainscript back
00229                 return cleanScript(stack[0]);*/
00230         }
00231 
00232         //------------------------------------------------------------------------------
00233         Result Script::parse(const std::string& script)
00234         {
00235                 // remove all non-commands from the script
00236                 // check for subscripts
00237                 std::stringstream str(parseSubscripts(cleanScript(script)));
00238                 if (script.length() == 0 || !str.good()) return OK;
00239 
00240                 //printf("%s: \n%s\n---\n\n", getResourceName().c_str(),str.str().c_str());
00241                 // get linewise
00242                 char buf[1024];
00243                 std::string::size_type pos;
00244                 std::string line;
00245 
00246                 while (!str.eof())
00247                 {
00248                         // get line
00249                         str.getline(buf, 1024);
00250                         line = std::string(buf);
00251 
00252                         // check for script parameters
00253                         pos = line.find('^');
00254                         if (pos != std::string::npos)
00255                         {
00256                                 // get parameter
00257                                 std::string sparam = line.substr(pos+1);
00258                                 boost::algorithm::trim(sparam);
00259                                 
00260                                 // now tokenize parameter, to get special argument values for the parameters
00261                                 std::vector<std::string> args;
00262                                 std::string param;
00263                                 tokenize(sparam, param, args); 
00264                                 
00265                                 // set parameters
00266                                 if (!setParameter(param, args))
00267                                 {
00268                                         NR_Log(Log::LOG_ENGINE, Log::LL_ERROR, "Script: Unknown local parameter %s in line \"%s\"", param.c_str(), line.c_str());
00269                                         return SCRIPT_PARSE_ERROR;
00270                                 }
00271                                 continue;
00272                         }
00273 
00274                         // check for the timestamp
00275                         pos = line.find('|');
00276                         Command cmd;
00277                         cmd.timestamp = 0;
00278                         cmd.estimatedStart = 0;
00279 
00280                         // timestamp found, so do extra stuff with this kind of commands
00281                         if(pos != std::string::npos )
00282                         {
00283                                 // retrieve the timestamp and the command and store it into the command buffer
00284                                 std::string time = line.substr(0,pos);
00285                                 boost::algorithm::trim(time);
00286                                 std::string command = line.substr(pos+1);
00287 
00288                                 // try to convert the time and add the command to the list
00289                                 try{
00290                                         cmd.timestamp = boost::lexical_cast<float32>(time);
00291                                         tokenize(command, cmd.cmd, cmd.args);
00292                                         if (cmd.args.size() > 0 && cmd.args[0].length() > 0)
00293                                         {
00294                                                 if (cmd.cmd == std::string("_stop_"))
00295                                                 {
00296                                                         mRunningTimeLength = cmd.timestamp;
00297                                                 }else
00298                                                         mTimedCommand.push_back(cmd);
00299                                         }
00300                                 }catch(...){
00301                                         NR_Log(Log::LOG_ENGINE, Log::LL_ERROR, "Script: Unknown syntax in \"%s\"\n", line.c_str());
00302                                         return SCRIPT_PARSE_ERROR;
00303                                 }
00304 
00305                         // timestamp not found, so it is a simple command
00306                         }else{
00307                                 tokenize(line, cmd.cmd, cmd.args);
00308                                 if (cmd.args.size() > 0 && cmd.args[0].length() > 0)
00309                                 {
00310                                         mCommand.push_back(cmd);
00311                                 }
00312                         }
00313                 }
00314 
00315                 resetCommandFifo();
00316 
00317                 // if we got any command so we are valid
00318                 return OK;
00319         }
00320 
00321         //------------------------------------------------------------------------------
00322         void Script::tokenize(const std::string& str, std::string& cmd, std::vector<std::string>& tokens)
00323         {
00324                 // do not tokenize if we do not get any token
00325                 if (str.length() == 0) return;
00326 
00327                 using namespace std;
00328 
00329                 // tokenize on space characters
00330                 const string delimiters = " ";
00331 
00332                 // Skip delimiters at beginning.
00333                 string::size_type lastPos = str.find_first_not_of(delimiters, 0);
00334 
00335                 // Find first "non-delimiter".
00336                 string::size_type pos     = str.find_first_of(delimiters, lastPos);
00337 
00338                 while (pos != string::npos || lastPos != string::npos)
00339                 {
00340                         // Found a token, add it to the vector.
00341                         if (pos  - lastPos > 0)
00342                                 tokens.push_back(str.substr(lastPos, pos - lastPos));
00343 
00344                         // Skip delimiters.  Note the "not_of"
00345                         lastPos = str.find_first_not_of(delimiters, pos);
00346 
00347                         // Find next "non-delimiter"
00348                         pos = str.find_first_of(delimiters, lastPos);
00349                 }
00350 
00351                 // now the first element in the token list is the command name
00352                 cmd = tokens[0];
00353         }
00354 
00355         //------------------------------------------------------------------------------
00356         bool Script::setParameter(const std::string& param, const std::vector<std::string>& args){
00357 
00358                 if (param == "loop")
00359                         mLoop |= LOOP;
00360                 else if (param == "loop_seq")
00361                         mLoop |= LOOP_COMMAND;
00362                 else if (param == "loop_timed")
00363                         mLoop |= LOOP_TIMED_COMMAND;
00364                 else if (param == "runcomplete")
00365                         bRunStepwise = false;
00366                 //else if (param == "runonce")
00367                 //      setRunOnce(true);
00368                 else if (param == "start_at")
00369                         try{
00370                                 if (args.size() > 1) mScriptStartTime = boost::lexical_cast<float32>(args[1]);
00371                                 else throw boost::bad_lexical_cast();
00372                         }catch(boost::bad_lexical_cast &){
00373                                 NR_Log(Log::LOG_ENGINE, Log::LL_WARNING, "Script: %s - not a valid value for \"start_at\" parameter specified", getResourceName().c_str());
00374                         }
00375                 else
00376                         return false;
00377 
00378                 return true;
00379         }
00380 
00381         //------------------------------------------------------------------------------
00382         void Script::resetCommandFifo()
00383         {
00384                 // the untimed command fifo is very simple
00385                 // the commands are executed in the way they were found in the file
00386                 mCommandFifo.clear();
00387                 for (uint32 i=0; i < mCommand.size(); i++){
00388                         mCommandFifo.push_front(i);
00389                 }
00390         }
00391 
00392         //------------------------------------------------------------------------------
00393         void Script::resetTimedCommandFifo(bool firstReset)
00394         {
00395                 if (firstReset) mTimedCommandFifo.clear();
00396 
00397                 // timed commands could only be resetted when they
00398                 // are already executed
00399                 
00400                 // the timed commands are also setted in the fifo
00401                 // the estimated end time is also computed
00402                 for (int32 i=0; i < (int32)mTimedCommand.size(); i++)
00403                 {
00404                         // check whenever fifo does not contain this command 
00405                         std::list< int32 >::iterator it = std::find(mTimedCommandFifo.begin(), mTimedCommandFifo.end(), i);
00406                         
00407                         // if such command not exists, then reset it
00408                         if (it == mTimedCommandFifo.end() || firstReset)
00409                         {
00410                                 mTimedCommandFifo.push_front(i);
00411                                 mTimedCommand[i].estimatedStart = mTimer->getTime() + mTimedCommand[i].timestamp;
00412                         }
00413                 }
00414         }
00415 
00416         //----------------------------------------------------------------------------------
00417         Result Script::step()
00418         {
00419                 // update time
00420                 mTimer->setPause(false);
00421                 mTime = mTimer->getTime();
00422                 
00423                 //printf("%s (%f - %f)\n", getResourceName().c_str(), mTime, mScriptStartTime);
00424 
00425                 // do execute the script only if start time was passed
00426                 if (mScriptStartTime > mTime)
00427                 {
00428                         // if the script is waiting for the start, then it runs in a loop mode 
00429                         setRunOnce(false);
00430                         
00431                         // do nothing at now
00432                         return OK;
00433                 }
00434                 
00435                 // if the script running first time, then do reset on timed commands
00436                 if (mFirstRunTimed)
00437                 {
00438                         // initialize timed commands
00439                         mFirstRunTimed = false;
00440                         resetTimedCommandFifo(true);
00441                         setRunOnce(true);
00442                 }
00443                 
00444                 // compute stop time, when to stop the script if such defined
00445                 if (mStopTime == 0 && mRunningTimeLength > 0) mStopTime = mTime + mRunningTimeLength;
00446                 
00447                 // ----- timed commands (quasi parallel execution) -----
00448                 // scan through the command fifo
00449                 std::list<int32>::iterator it;
00450                 for (it = mTimedCommandFifo.begin(); it != mTimedCommandFifo.end(); )
00451                 {
00452                         int32 id = *it;
00453                         mTimedCommand[id].time = mTime;
00454                         
00455                         //std::string msg;
00456                         //for (unsigned int i=1; i < mTimedCommand[id].args.size(); i++) msg += std::string(" ") + mTimedCommand[id].args[i];
00457                         //printf("%s (%s): %f %f\n", mTimedCommand[id].cmd.c_str(), msg.c_str(), mTimedCommand[id].estimatedStart, mTimedCommand[id].time);
00458 
00459                         // if the estimated start time is reeached, so start the command and remove it from the queue
00460                         if (mTimedCommand[id].estimatedStart < mTimedCommand[id].time)
00461                         {
00462                                 // remove the command from the fifo
00463                                 it = mTimedCommandFifo.erase(it);
00464 
00465                                 // call the commando
00466                                 Engine::sScriptEngine()->call(mTimedCommand[id].cmd, mTimedCommand[id].args);
00467                         }else
00468                                 it ++;
00469                 }
00470 
00471                 // ----- sequentiall commands -----
00472                 if (mCommandFifo.size())
00473                 {
00474                         // get the id of the command
00475                         int32 id = mCommandFifo.back();
00476                         mCommandFifo.pop_back();
00477 
00478                         // check if we have a stop commando
00479                         if (mCommand[id].cmd != std::string("_stop_"))
00480                         {
00481                                 Engine::sScriptEngine()->call(mCommand[id].cmd, mCommand[id].args);
00482                         }else{
00483                                 Engine::sKernel()->RemoveTask(this->getTaskID());
00484                         }
00485                 }
00486 
00487                 // reset the command list
00488                 reset();
00489 
00490                 // check whenever execution time of the script exceeds
00491                 if (mTime > mStopTime && mStopTime > 0)
00492                 {
00493                         Engine::sKernel()->RemoveTask(this->getTaskID());
00494                 }
00495 
00496                 //printf("\n\n");
00497                 
00498                 return OK;
00499         }
00500 
00501         //----------------------------------------------------------------------------------
00502         bool Script::hasCommands()
00503         {
00504                 if (mCommandFifo.size() == 0 && mTimedCommandFifo.size() == 0 && !(mLoop & LOOP_COMMAND) && !(mLoop & LOOP_TIMED_COMMAND))
00505                         return false;
00506 
00507                 return true;
00508         }
00509 
00510         //----------------------------------------------------------------------------------
00511         Result Script::run()
00512         {
00513                 // check whenver this script is running stepwise
00514                 if (bRunStepwise) return step();
00515 
00516                 // make full run of the script
00517                 return fullRun();
00518         }
00519 
00520 
00521         //----------------------------------------------------------------------------------
00522         Result Script::fullRun()
00523         {
00524 
00525                 // check for looped script
00526                 static bool warned = false;
00527                 if (!warned && mLoop & LOOP_COMMAND){
00528                         warned = true;
00529                         NR_Log(Log::LOG_ENGINE, Log::LL_WARNING, "Script: %s - you are executing a looped script!!! Check this!!!", getResourceName().c_str());
00530                 }
00531 
00532                 // loop the content of the script if script should be looped
00533                 bool bLoop = false;
00534                 do {
00535                         // we go through the whole command list and execute it.
00536                         while (mCommandFifo.size() > 0)
00537                         {
00538                                 // get the id of the command
00539                                 int32 id = mCommandFifo.back();
00540                                 mCommandFifo.pop_back();
00541 
00542                                 // execute it
00543                                 Engine::sScriptEngine()->call(mCommand[id].cmd, mCommand[id].args);
00544                         }
00545 
00546                         // reset the list and check if the task is removed from the kernel
00547                         reset();
00548                         bLoop = hasCommands();
00549 
00550                 } while(bLoop && (mLoop & LOOP_COMMAND));
00551 
00552                 return OK;
00553         }
00554 
00555         //--------------------------------------------------------------------
00556         void Script::onStartScript()
00557         {
00558                 // do run all subscripts specified in this script
00559                 for (unsigned int i = 0; i < mSubscripts.size(); i++)
00560                 {
00561                         // cast subscripts to script objects
00562                         Script* subscript = dynamic_cast<Script*>(mSubscripts[i].get());
00563                         if (subscript)
00564                         {
00565                                 // first set starting time of subscripts relative to the main script 
00566                                 subscript->mScriptStartTime += mScriptStartTime;
00567                                 
00568                                 // now execute the script 
00569                                 subscript->execute();
00570                                 
00571                                 // setup dependencies
00572                                 addTaskDependency(subscript->getTaskID());
00573                         }
00574                         
00575                 }
00576                 
00577                 // intialize the timer and pause it
00578                 mTimer = Engine::sClock()->createTimer();
00579                 mTimer->setPause(true);
00580                 
00581                 // reset the script, so it can run
00582                 resetCommandFifo();
00583                 resetTimedCommandFifo(true);
00584                 mFirstRunTimed = true;
00585         }
00586 
00587         //----------------------------------------------------------------------------------
00588         bool Script::reset()
00589         {
00590                 if (mCommandFifo.size() == 0 && (mLoop & LOOP_COMMAND)){
00591                         resetCommandFifo();
00592                 }else if (mTimedCommandFifo.size() == 0 && (mLoop & LOOP_TIMED_COMMAND)){
00593                         resetTimedCommandFifo(false);
00594                 }
00595                 return true;
00596         }
00597 
00598 };
00599 

Generated on Wed Sep 12 23:19:42 2007 for nrEngine by  doxygen 1.5.1