Developing with HLA

From FlightGear wiki
Jump to navigation Jump to search
This article is a stub. You can help the wiki by expanding it.
  • Last updated: 06/2013
  • Contributors: Callahanp, Hooray

Objective

The HLA article only really discusses the overall picture and lists some general tutorials - it doesn't go into FG specifics, which we however need to cover to get more people up to scratch with HLA, i.e. 1) building SG/FG with HLA and 2) actually developing with it to partition FG into separate processes. Like I mentioned previously, we do have a bunch of C++ code that can serve as an example, i.e. to reverse-engineer things a little and provide a more programmer-centric tutorial

Work through a simple use-case of using HLA to support a Nasal HLA standalone interpreter that provides "live" property tree access using setprop()/getprop() and then generalize the info and present it here 30}% completed.

Intro

OpenRTI

Operation

OpenRTI can operate in several ways:

The most important one is probably the client/server scenario: Have one server process and some federates in several processes on some machines in a network.

Often it would be also good to exploit some locality in an RTI. To get this, an OpenRTI server can operate as slave servers under a parent server. That means that every server in that tree seed all the federations in that server tree but messages are only routed to servers that need these messages. So having a sub server on a local subnet, traffict just interresting for federates connected to that slave server originating from federates connected to that slave server will never be sent to the parent server.

But OpenRTI also can work without any external server running. When all federates are living in one program - may be in different threads, messages are just exchanged in that program. While this is probably the fastest operation mode it is also the one that is the easiest to set up.

Protocols

To select between the different modes of operation above, OpenRTI knows some protocols. This is the basic way of the transport used to communicate.

  • thread: Just use the in memory communication between threads. This is the default as it does not require much setup.
  • rti: This is the preferred tcp/ip protocol variant for networked communication.
  • pipe: Uses the rti binary protocol on a named pipe. Can be used for machine local communication in presence of tight packet filters.

The implementation is prepared to some degree to move protocols into a user provided shared library that could be loaded at runtime.

Connecting to an rti

For rti1516 we have that string vector that is given to the ambassador when it is created. This vector can be used to configure the protocol that is used to communicate. The strings might contain key value pairs like <key>=<value>. Supported keys are:

  • protocol=

    • 'thread' (default) use the smp, in memory connect.
    • 'rti' use the tcp/ip binary protocol on some custom port
    • 'pipe' use the binary protocol on a named pipe
    • 'trace' chain the communication to an other protocol and dump messages
  • address=<a> - The interpretation depends on the protocol:
    • For 'thread' this is ignored.
    • For 'rti' this contains the host:port pair.
    • port can be omitted to use the apropriate default port.
    • For 'pipe' this should contain the path to the named pipe.
  • timeout=<t>
  • Sets initial packet timeouts for messages that happen syncronous. That is mostly for create, destroy, join and resign.

Federation execution names and URLs

OpenRTI interprets federation execution names as urls:

<protocol>://<address>/<path>/<name>

Where the protocol overriedes the protocol field from the string list. The address is the network address of the server. The path contains the path to the named pipe if protocol is pipe and the name is just the plain name of the federation execution.

Learn more at: openrti/OpenRTI/master/README

SimGear

FlightGear

Base Package

  • Federation Object Model (FOM) - An identification of the essential classes of objects, object attributes, and object interactions. The FOM does not contain information about the actual objects in the simulation (the federates), but only about the possible object classes in the simulation.

Examples

All examples will be made available through Gitorious, i.e. see: gitorious/fg/hoorays-simgear/topics/hla-tutorial.

To track the simgear branch:

git remote add hla-tut https://gitorious.org/fg/hoorays-simgear.git
git fetch hla-tut
git checkout --track -b hla-tut/topics/hla-tutorial

Adding a new HLA app to the build system

Add this to your toplevel CMakeLists.txt:

##
# use this function to build HLA federates
#
# add_federate(hla-demo hla-demo.cxx dep1.cxx dep2.cxx)
#
function( add_federate EXECUTABLE SOURCE_FILE)
  if(RTI_FOUND)
   set(HLALibs SimGearCore "${CMAKE_THREAD_LIBS_INIT}" ${RTI_LIBRARIES} ${ZLIB_LIBRARY}  ${WINSOCK_LIBRARY} ${RT_LIBRARY} )
   # add additional files 
   set(FILES ${FILES} ${SOURCE_FILE} ) # using set() here is slower than list(APPEND) but more portable
   foreach(additional_file ${ARGN} )
        set( FILES ${FILES} ${additional_file} )
   endforeach()
   add_executable( ${EXECUTABLE} ${FILES})
   target_link_libraries( ${EXECUTABLE} ${HLALibs} )
  else(RTI_FOUND)
  message(STATUS "RTI not found, not building ${EXECUTABLE}")
 endif(RTI_FOUND)
endfunction(add_federate) 


option(ENABLE_RTI       "Set to ON to build with RTI support" OFF)

if(ENABLE_RTI)
    # See if we have any rti library variant installed
    message(STATUS "RTI: ENABLED")
    find_package(RTI)
    if(RTI_FOUND)
     set(SG_HAVE_HLA 1)
    endif(RTI_FOUND)
else()
    message(STATUS "RTI: DISABLED")
endif(ENABLE_RTI)

set(RT_LIBRARY "")
if(HAVE_CLOCK_GETTIME)
    check_library_exists(rt clock_gettime "" HAVE_RT)
    if(HAVE_RT)
        set(RT_LIBRARY rt)
    endif(HAVE_RT)
endif(HAVE_CLOCK_GETTIME)


And this to your CMakeLists.txt in the source folder:

 add_federate(hla-demo hla-demo.cxx)

Creating a new Federate

Create a new class and implement the interface of simgear::HLAFederate

#ifndef __MY_FEDERATE_HXX_
#define __MY_FEDERATE_HXX_
#include <simgear/hla/HLAFederate.hxx>

class MyFederate : public simgear::HLAFederate {
public:
    MyFederate();
    virtual ~MyFederate();
    virtual simgear::HLAObjectClass* createObjectClass(const std::string& name);
    virtual bool init();
    virtual bool update();
    virtual bool shutdown();
private:
};
#endif

Creating a Federate instance

Use the SGSharedPtr<> template to create a shared pointer of your new federate:

#include "MyFederate.hxx"

int main() {
 SGSharedPtr<MyFederate> manager = new MyFederate;
return 0;
}


Initializing a Federate

Call the following federate methods, to initialize your federate:

  • setVersion( simgear::HLAFederate::RTI13 )
  • setFederateType(type);
  • setFederationExecutionName("rti:///FlightGear")
  • setCreateFederationExecution(true)
  • setFederationObjectModel(OMT)
  • init() + explicit update() calls or exec()
#include <simgear/misc/sg_path.hxx> 
#include "MyFederate.hxx"

int main() {
 SGSharedPtr<MyFederate> manager = new MyFederate;
 manager.setVersion( simgear::HLAFederate::RTI13 ) 
 manager.setFederateType("MyFederate");
 manager.setFederationExecutionName("rti:///FlightGear");

 std::string fg_root;

// get FG_ROOT for FOM (in $FG_ROOT/HLA)
if (fg_root.empty()) {
       if (const char *fg_root_env = std::getenv("FG_ROOT")) {
            fg_root = fg_root_env;
        } else {
            std::cerr << "Error: path to FG_ROOT must be specified as env var" << std::endl;
            exit(-1);
        }
 
// set up the default fg-local-fom.xml
if (manager->getFederationObjectModel().empty()) {
        SGPath path(fg_root);
        path.append("HLA");
        path.append("fg-local-fom.xml");
        manager->setFederationObjectModel(path.str());
        std::cout << "OMT is:" << path << std::endl;
    }


return manager.exec(); //fires off init() and update()
}

Hello World - HLA

Here's a more contrived but self-contained example:

#include <simgear/misc/sg_path.hxx> 
#include <simgear/hla/HLAFederate.hxx>

#include <cstdlib>
#include <iostream>

using namespace std;

static void hello_from(std::string source) {
 std::cerr << "Hello from:" << source << std::endl;
}

static void cancel(std::string error) {
 std::cerr << error << std::endl;
 exit(-1);
} 

class MyFederate : public simgear::HLAFederate {
public:
    MyFederate() {}
    virtual ~MyFederate() {}
    virtual simgear::HLAObjectClass* createObjectClass(const std::string& name) {return NULL; }
    virtual bool init() { hello_from("init"); return true;}
    virtual bool update() { hello_from("update"); return true;}
    virtual bool shutdown() {hello_from("shutdown"); return true;}
private:
};
 
SGSharedPtr<MyFederate> manager = new MyFederate; // our global federate instance

static 
void setup_fom(const char* filename="fg-local-fom.xml") {
std::string fg_root;
 
// get FG_ROOT for FOM (in $FG_ROOT/HLA)
if (fg_root.empty()) {
       if (const char *fg_root_env = std::getenv("FG_ROOT")) {
            fg_root = fg_root_env;
        } else {
            cancel("Error: path to FG_ROOT must be specified as env var");
        }
 
// set up the default fg-local-fom.xml
if (manager->getFederationObjectModel().empty()) {
        SGPath path(fg_root);
        path.append("HLA");
        path.append( filename );
        manager->setFederationObjectModel(path.str());
        std::cout << "OMT is:" << path << std::endl;
    }
 
 } 
if ( !manager.valid() )
 cancel("manager not valid!");

}

// entry point

int main() {
 manager->setVersion( simgear::HLAFederate::RTI13 );
 manager->setFederateType("MyFederate");
 manager->setFederationExecutionName("rti:///FlightGear");
 setup_fom(); 

 // federate is now valid
return manager->exec(); //fires off init() and update()
}

Resources

Introductions (Youtube)

Tutorials