>xabsl | The Extensible Agent Behavior Specification Language | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
start | team | language reference | tools | xabsl engine | download | licence | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
The xabsl::Engine Class LibraryThe xabsl::Engine is the XABSL runtime system. It is platform and application independent and written in plain ANSI C++. Due to that, XABSL can be easily employed on any robotic platform. To run the engine in a specific software environment only two classes (for file access and error handling) have to be derived from abstract classes. The engine parses and executes the intermediate code that was generated from XABSL documents. It links the symbols of a XABSL specification to variables and functions of the agent platform. Therefore, for each used symbol an entity in the software environment has to be registered to the engine. While options and their states are represented in XABSL, basic behaviors are written in C++. They have to be derived from a common base class and registered to the engine. The engine provides extensive debugging interfaces for monitoring the option and state activations, the values of the symbols and the parameters of options and basic behaviors. Instead of executing the engine from the root option, single options or basic behaviors can be tested separately. Files of the xabsl::Engine
See the Doxygen-generated source code documentation of the class Engine for more details. Running the xabsl::Engine on a specific Target PlatformFirst, one has to declare a message and error handling class that is derived from ErrorHandler. This class has to implement the printMessage() and printError() function. E.g.: class MyErrorHandler : public xabsl::ErrorHandler { public: MyErrorHandler(); virtual void printError(const char* text) {cout << "error: " << text << endl;} virtual void printMessage(const char* text){cout << text << endl;} }; The Boolean variable "errorsOccurred" can be used to determine if there occurred errors during the creation or execution of the engine. Then, a class that gives the engine a read access to the intermediate code has to be derived from InputSource. These pure virtual functions have to be implemented:
An example: class MyFileInputSource : public xabsl::InputSource { public: MyFileInputSource(const char* fileName) : file(0), theChar(' ') { strcpy(filename,fileName); } ~MyFileInputSource() {if (file!=0) delete file;} virtual bool open() {file = new std::ifstream(filename); return(file!=0);} virtual void close() {if (file!=0) delete file; file = 0;} virtual double readValue() { char buf[20]; readFromFile(buf); return atof(buf); } virtual bool readString(char* destination, int maxLength) { readFromFile(destination); return true; } private: char filename[200]; std::ifstream* file; char theChar; void readFromFile(char* value) { while(!file->eof() && isWhitespace()) { if (theChar == '/') while(!file->eof() && theChar != '\n') file->read(&theChar,1); file->read(&theChar,1); } while(!file->eof() && !isWhitespace()) { *value++ = theChar; if(!file->eof()) file->read(&theChar,1); } *value = 0; } bool isWhitespace() { return theChar == ' ' || theChar == '/' || theChar == '\n' || theChar == '\r' || theChar == '\t'; } }; Please note that the file contains comments (//...) that have to be skipped by the read functions: // divide (7) 7 // multiply (6) 6 // decimal value (0): 52.5 0 52.5 // reference to decimal symbol (1) ball.y: 1 13 The comments have to be treated as in C++ files. (New line ends a comment.) In the example only "7 6 0 52.5 1 13" should be read from the file. At last, a static function that returns the system time in milliseconds has to be defined, e.g.: static unsigned long getSystemTime() { timeb sysTime; ftime(&sysTime); return (sysTime.time * 1000 + sysTime.millitm); } Creating a New EngineFirst, an instance of the adapted ErrorHandler has to be created: MyErrorHandler errorHandler; Then, the engine can be created, passing a reference to the error handler and a pointer to the time function as parameters: xabsl::Engine* pMyEngine = new xabsl::Engine(errorHandler,&getSystemTime); Now all the symbols and basic behaviors have to be registered to the engine. Note that this has to be done before the option graph is created. Registering SymbolsAs the behaviors written in XABSL use symbols to interact with the agent's software environment, for each of these symbols the corresponding variable or function have to be registered to the engine. The following example registers the variables aDoubleInputVariable and aDoubleOutputVariable to the symbols "a-decimal-input-symbol" and "a-decimal-output-symbol" which were defined in the XABSL agent: pMyEngine->registerDecimalInputSymbol("a-decimal-input-symbol",&aDoubleInputVariable); pMyEngine->registerDecimalOutputSymbol("a-decimal-output-symbol",&aDoubleOutputVariable); If the value for the symbol is not represented by a variable but by a function, this function has to be registered to the engine. Note that this function has do be static: class MySymbols { public: static double doubleReturningFunction(); static void doubleAcceptingFunction(double value); }; ... MySymbols mySymbols; pMyEngine->registerDecimalInputSymbol("a-decimal-input-symbol", &MySymbols::doubleReturningFunction); pMyEngine->registerDecimalOutputSymbol("a-decimal-output-symbol", &MySymbols::doubleAcceptingFunction);
pMyEngine->registerBooleanInputSymbol("a-boolean-input-symbol",&aBooleanInputVariable); pMyEngine->registerBooleanOutputSymbol("a-boolean-output-symbol",&aBooleanOutputVariable); Or: class MySymbols { public: static bool booleanReturningFunction(); static void booleanAcceptingFunction(bool value); }; ... MySymbols mySymbols; pMyEngine->registerBooleanInputSymbol("a-boolean-input-symbol", &MySymbols::booleanReturningFunction); pMyEngine->registerBooleanOutputSymbol("a-boolean-output-symbol", &MySymbols::booleanAcceptingFunction, &MySymbols::booleanReturningFunction);
class MySymbols { public: enum MyEnum { element1, element2, element3 } anEnumInputVariable, anEnumOutputVariable; static MyEnum enumReturningFunction(); static void enumAcceptingFunction(MyEnum value); }; ... MySymbols mySymbols; pMyEngine->registerEnumeratedInputSymbol("an-enumerated-input-symbol", "my-enum", (int*)&mySymbols.anEnumInputVariable); pMyEngine->registerEnumeratedOutputSymbol("an-enumerated-output-symbol", "my-enum", (int*)&mySymbols.anEnumOutputVariable); Or: pMyEngine->registerEnumeratedInputSymbol("an-enumerated-input-symbol", "my-enum", &MySymbols::enumReturningFunction); pMyEngine->registerEnumeratedOutputSymbol("an-enumerated-symbol", "my-enum", &MySymbols::enumAcceptingFunction, &MySymbols::enumReturningFunction); For the enumerations used in enumerated symbols, each enum element that was defined in the XABSL agent has to be registered to its corresponding value: pMyEngine->registerEnumElement("my-enum", "my-enum.element1", MySymbols::element1); pMyEngine->registerEnumElement("my-enum", "my-enum.element2", MySymbols::element2); pMyEngine->registerEnumElement("my-enum", "my-enum.element3", MySymbols::element3);
class MySymbols { public: static double parameter1, parameter2; static double myFunction() { return (parameter1 + parameter2) / 2; } }; ... MySymbols mySymbols; pMyEngine->registerDecimalInputSymbol("a-decimal-input-symbol", &MySymbols::myFunction); pMyEngine->registerDecimalInputSymbolDecimalParameter("a-decimal-input-symbol", "a-decimal-input-symbol.parameter1", mySymbols.parameter1); pMyEngine->registerDecimalInputSymbolDecimalParameter("a-decimal-input-symbol", "a-decimal-input-symbol.parameter2", mySymbols.parameter2); For different symbol and parameter types there are different functions for registering parameters. These functions are: registerDecimalInputSymbolDecimalParameter, registerDecimalInputSymbolBooleanParameter, registerDecimalInputSymbolEnumeratedParameter, registerBooleanInputSymbolDecimalParameter, registerBooleanInputSymbolBooleanParameter, registerBooleanInputSymbolEnumeratedParameter, registerEnumeratedInputSymbolDecimalParameter, registerEnumeratedInputSymbolBooleanParameter, and registerEnumeratedInputSymbolEnumeratedParameter. Registering Basic BehaviorsAll basic behaviors have to be derived from the class xabsl::BasicBehavior and have to implement the pure virtual function execute(). The name of the basic behavior has to be passed to the constructor of the base class. The decimal, boolean, or enumerated parameters of the basic behavior have to be declared as members of the class and registered using parameters->registerDecimal/registerBoolean/registerEnumerated inside the implementation of the function registerParameters(): class MyBasicBehavior : public xabsl::BasicBehavior { public: double parameter1; bool parameter2; MyEnum parameter3; MyBasicBehavior(XabslErrorHandler& errorHandler) : xabsl::BasicBehavior("a-basic-behavior",errorHandler) {} virtual void registerParameters() { parameters->registerDecimal("a-basic-behavior.parameter1", parameter1); parameters->registerBoolean("a-basic-behavior.parameter2", parameter2); parameters->registerEnumerated("a-basic-behavior.parameter3", "my-enum", parameter3); } virtual void execute() { // do the requested action using parameter1, parameter2, and parameter3 } }; Then, for each basic behavior class an instance has to be registered to the engine: MyBasicBehavior myBasicBehavior(errorHandler); pMyEngine->registerBasicBehavior(myBasicBehavior); Creating the Option GraphAfter the registration of all symbols and basic behaviors, the intermediate code can be parsed: MyFileInputSource input("path_to_the_intermediate_code.dat"); pMyEngine->createOptionGraph(input); If the engine detects an error during the execution of the option graph, the error handler is invoked. This can happen when the intermediate code contains a symbol or a basic behavior that was not registered before. Whether the option graph was created successfully or not can be checked like this: if (errorHandler.errorsOccurred) { // do some backup behavior delete pMyEngine; } Executing the EngineIf no errors occurred during the creation, the engine can be executed this way: pMyEngine->execute(); This function executes the option graph only a single time. Starting from the selected root option, the state machine of each option is carried out to determine the next active state. Then for the subsequent option of this state the state machine becomes carried out and so on until the subsequent behavior is a basic behavior, which is executed then, too. After that the output symbols that were set during the execution of the option graph become applied to the software environment. In the execute() function the execution starts from the selected root option, which is in the beginning the root option of the first agent. The agent can be switched using this function: pMyEngine->setSelectedAgent("name-of-the-agent"); Debugging InterfacesInstead of executing the option graph beginning with the root option of the currently selected agent, the function
pMyEngine->setRootAction("name-of-an-option-or-basic-behavior", isOption); can be called to select a different root action. This is useful to test a single option or basic behavior. With pMyEngine->getRootAction(0)->getParameters()->setDecimalParameter("name-of-the-decimal-parameter", 42); pMyEngine->getRootAction(0)->getParameters()->setBooleanParameter("name-of-the-boolean-parameter", true); pMyEngine->getRootAction(0)->getParameters()->setEnumeratedParameter("name-of-the-enumerated-parameter", element2); the parameters of the executed option or basic behavior can be set.
const xabsl::Action* getRootAction (int index) const; const xabsl::Array <xabsl::Action*> getRootActions () const; const char* getSelectedAgentName (); The member functions of the xabsl::Action object returned can be used to retrieve this information.
xabsl::DecimalInputSymbol* decimalInputSymbol = pMyEngine->decimalInputSymbols["name-of-a-symbol"]; xabsl::BooleanInputSymbol* booleanInputSymbol = pMyEngine->booleanInputSymbols["name-of-a-symbol"]; xabsl::EnumeratedInputSymbol* enumeratedInputSymbol = pMyEngine->enumeratedInputSymbols["name-of-a-symbol"]; xabsl::DecimalOutputSymbol* decimalOutputSymbol = pMyEngine->decimalOutputSymbols["name-of-a-symbol"]; xabsl::BooleanOutputSymbol* booleanOutputSymbol = pMyEngine->booleanOutputSymbols["name-of-a-symbol"]; xabsl::EnumeratedOutputSymbol* enumeratedOutputSymbol = pMyEngine->enumeratedOutputSymbols["name-of-a-symbol"]; Note that these operators crash if the requested symbol does not exist. The existence of symbols can be checked using the exists method: pMyEngine->decimalInputSymbols.exists("name-of-a-symbol"); pMyEngine->booleanInputSymbols.exists("name-of-a-symbol"); pMyEngine->enumeratedInputSymbols.exists("name-of-a-symbol"); pMyEngine->decimalOutputSymbols.exists("name-of-a-symbol"); pMyEngine->booleanOutputSymbols.exists("name-of-a-symbol"); pMyEngine->enumeratedOutputSymbols.exists("name-of-a-symbol"); |
|||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Copyright 2002 - 2009 by the XABSL developer team. See the licence for details. |