From 70dd6279a742030271b5b0927501f59bc9aecb98 Mon Sep 17 00:00:00 2001 From: Mathias Froehlich Date: Sun, 27 Jun 2010 09:04:10 +0200 Subject: [PATCH] Add an initial implementation of a hla module. --- configure.ac | 55 +- src/AIModel/AIMultiplayer.hxx | 3 + src/Main/Makefile.am | 9 +- src/Main/fg_io.cxx | 13 +- src/Main/options.cxx | 27 +- src/Network/HLA/Makefile.am | 8 + src/Network/HLA/hla.cxx | 1267 +++++++++++++++++++++++++++++++++ src/Network/HLA/hla.hxx | 62 ++ src/Network/Makefile.am | 4 + 9 files changed, 1425 insertions(+), 23 deletions(-) create mode 100644 src/Network/HLA/Makefile.am create mode 100644 src/Network/HLA/hla.cxx create mode 100644 src/Network/HLA/hla.hxx diff --git a/configure.ac b/configure.ac index c4ee12d68..d7f7a65a6 100644 --- a/configure.ac +++ b/configure.ac @@ -29,15 +29,15 @@ else fi AC_CHECK_PROG([HAVE_GIT], git, 1) -if test "x$HAVE_GIT" != "x" ; then +if test "x$HAVE_GIT" != "x" ; then # git might be installed, but we might be building from a tarball if git rev-parse ; then AC_SUBST([REVISION], `git rev-parse HEAD`) else - AC_SUBST([REVISION], [none]) + AC_SUBST([REVISION], [none]) fi else - AC_SUBST([REVISION], [none]) + AC_SUBST([REVISION], [none]) fi dnl Checks for programs. @@ -90,7 +90,7 @@ case "${host}" in dnl Thank you Christian Bauer from SheepSaver dnl Modified by Tatsuhiro Nishioka for accepting a given framework path - dnl AC_CHECK_FRAMEWORK($1=NAME, $2=INCLUDES, $3=FRAMEWORK_PATH, $4=ACTION_IF_FOUND) ; + dnl AC_CHECK_FRAMEWORK($1=NAME, $2=INCLUDES, $3=FRAMEWORK_PATH, $4=ACTION_IF_FOUND) ; AC_DEFUN([AC_CHECK_FRAMEWORK], [ AS_VAR_PUSHDEF([ac_Framework], [ac_cv_framework_$1])dnl AC_CACHE_CHECK([whether compiler supports framework $1], @@ -251,13 +251,55 @@ if test "x$with_threads" = "xyes"; then fi AC_CHECK_HEADER(pthread.h) +dnl Configure HLA support +AC_ARG_WITH(rti13, [ --with-rti13[[=PATH]] Enable the HLA/RTI 1.3 interface, set a path to the RTI13 RTI libraries (default=no)]) +if test "x$with_rti13" != "xno"; then + if test "x$with_rti13" != "xyes"; then + AC_SUBST(HLA_CPPFLAGS, "-I$with_rti13/include") + AC_SUBST(HLA_LDFLAGS, "-L$with_rti13/lib") + fi + + dnl Configure HLA support + AC_MSG_CHECKING([for simgear HLA/RTI support]) + AC_LANG_PUSH(C++) + AC_TRY_COMPILE([ + #include + ],[ + new simgear::HLA13Federate; + ], simgear_hla=yes, simgear_hla=no) + AC_MSG_RESULT($simgear_hla) + if test "x$simgear_hla" = "xyes" ; then + AC_MSG_CHECKING([for hla libraries]) + saved_LIBS="$LIBS" + for rti13libs in "-lRTI-NG -lFedTime" "-lRTI-NGd -lFedTimed" ; do + if test "x$hla_libs" = "x" ; then + LIBS="-lsghla13 -lsghla -lsgxml -lsgstructure -lsgmath -lsgdebug -lsgtiming $rti13libs -lrt $saved_LIBS" + AC_TRY_LINK([ + #include + ],[ + new simgear::HLA13Federate; + ], [hla_libs="$rti13libs"; have_rti13=yes],) + fi + done + LIBS="$saved_LIBS" + AC_MSG_RESULT($hla_libs) + fi + AC_LANG_POP() +fi +dnl Currently only the rti13 variant, but in the future also rti1516 +AM_CONDITIONAL(WITH_HLA, test "x$have_rti13" = "xyes") +if test "x$have_rti13" = "xyes" ; then + AC_DEFINE([FG_HAVE_HLA], 1, [Define if HLA/RTI is available.]) + AC_SUBST(HLA_LIBS, "-lsghla13 -lsghla $hla_libs") +fi + dnl Used by JSBSim to conditionally compile in fgfs interface code AC_DEFINE([FGFS], 1, [Define so that JSBSim compiles in 'library' mode]) # Check for MS Windows environment AC_CHECK_HEADER(windows.h) -dnl Using AM_CONDITIONAL is a step out of the protected little +dnl Using AM_CONDITIONAL is a step out of the protected little dnl automake fold so it is potentially dangerous. But, we are dnl beginning to run into cases where the standard checks are not dnl enough. AM_CONDITIONALS are then referenced to conditionally @@ -453,7 +495,7 @@ case "${host}" in # Mac OS X has OpenAL.framework, but no ALUT, by default, so we # require use of a non-Apple ALUT.framework which we provide openal_LIBS="-framework IOKit -framework OpenAL" - + AC_CHECK_FRAMEWORK(OpenAL, [#include ], "", [OPENAL_OK="yes"]) AC_CHECK_FRAMEWORK(ALUT, [#include ], $with_alut_framework, [ ALUT_OK="yes" @@ -878,6 +920,7 @@ AC_CONFIG_FILES([ \ src/MultiPlayer/Makefile \ src/Navaids/Makefile \ src/Network/Makefile \ + src/Network/HLA/Makefile \ src/Scenery/Makefile \ src/Scripting/Makefile \ src/Sound/Makefile \ diff --git a/src/AIModel/AIMultiplayer.hxx b/src/AIModel/AIMultiplayer.hxx index 6b30685aa..e484dacb0 100644 --- a/src/AIModel/AIMultiplayer.hxx +++ b/src/AIModel/AIMultiplayer.hxx @@ -59,6 +59,9 @@ public: void addPropertyId(unsigned id, const char* name) { mPropertyMap[id] = props->getNode(name, true); } + SGPropertyNode* getPropertyRoot() + { return props; } + virtual const char* getTypeString(void) const { return "multiplayer"; } private: diff --git a/src/Main/Makefile.am b/src/Main/Makefile.am index 7cfe0fd91..c4525ed8b 100644 --- a/src/Main/Makefile.am +++ b/src/Main/Makefile.am @@ -6,7 +6,7 @@ MPLAYER_LIBS = $(top_builddir)/src/MultiPlayer/libMultiPlayer.a if ENABLE_SP_FDM SP_FDM_LIBS = $(top_builddir)/src/FDM/SP/libSPFDM.a else -SP_FDM_LIBS = +SP_FDM_LIBS = endif if WITH_EVENTINPUT @@ -26,6 +26,10 @@ if HAVE_FRAMEWORK_OSG fgfs_OSG_FW = $(osg_FRAMEWORKS) $(openthreads_FRAMEWORK) endif +if WITH_HLA +HLA_LDADD=$(HLA_LDFLAGS) $(top_builddir)/src/Network/HLA/libFGHLA.a $(HLA_LIBS) +endif + GFX_CODE = fg_os_osgviewer.cxx fg_os_common.cxx fg_os.hxx JSBSIM_LIBS = \ @@ -105,6 +109,7 @@ fgfs_LDADD = \ $(top_builddir)/src/Time/libTime.a \ $(top_builddir)/src/Traffic/libTraffic.a \ $(top_builddir)/src/Environment/libEnvironment.a \ + $(HLA_LDADD) \ -lsgroute -lsgsky -lsgsound -lsgephem -lsgtgdb -lsgmodel -lsgbvh \ -lsgmaterial -lsgutil -lsgtiming -lsgio -lsgscreen -lsgmath -lsgbucket \ -lsgprops -lsgdebug -lsgmagvar -lsgmisc -lsgnasal -lsgxml -lsgsound \ @@ -128,4 +133,4 @@ metar_LDADD = \ -lz $(base_LIBS) -INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src -I$(top_builddir)/src +INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src -I$(top_builddir)/src $(HLA_CPPFLAGS) diff --git a/src/Main/fg_io.cxx b/src/Main/fg_io.cxx index 75ec5c978..47c302f87 100644 --- a/src/Main/fg_io.cxx +++ b/src/Main/fg_io.cxx @@ -65,6 +65,9 @@ #include #include #include +#ifdef FG_HAVE_HLA +#include +#endif #include "globals.hxx" #include "fg_io.hxx" @@ -213,6 +216,10 @@ FGIO::parse_port_config( const string& config ) string host = tokens[3]; string port = tokens[4]; return new FGMultiplay(dir, atoi(rate.c_str()), host, atoi(port.c_str())); +#ifdef FG_HAVE_HLA + } else if ( protocol == "hla" ) { + return new FGHLA(tokens); +#endif } else { return NULL; } @@ -223,7 +230,7 @@ FGIO::parse_port_config( const string& config ) delete io; return 0; } - + if (tokens.size() < 3) { SG_LOG( SG_IO, SG_ALERT, "Incompatible number of network arguments."); return NULL; @@ -253,7 +260,7 @@ FGIO::parse_port_config( const string& config ) string baud = tokens[5]; SG_LOG( SG_IO, SG_INFO, " baud = " << baud ); - + SGSerial *ch = new SGSerial( device, baud ); io->set_io_channel( ch ); } else if ( medium == "file" ) { @@ -262,7 +269,7 @@ FGIO::parse_port_config( const string& config ) SG_LOG( SG_IO, SG_ALERT, "Incompatible number of arguments for file I/O."); return NULL; } - + string file = tokens[4]; SG_LOG( SG_IO, SG_INFO, " file name = " << file ); int repeat = 1; diff --git a/src/Main/options.cxx b/src/Main/options.cxx index b2b960dbf..d0662793f 100644 --- a/src/Main/options.cxx +++ b/src/Main/options.cxx @@ -118,7 +118,7 @@ fgSetDefaults () // Otherwise, default to Scenery being in $FG_ROOT/Scenery globals->set_fg_scenery(""); } - + // Position (deliberately out of range) fgSetDouble("/position/longitude-deg", 9999.0); fgSetDouble("/position/latitude-deg", 9999.0); @@ -220,7 +220,7 @@ fgSetDefaults () // HUD options fgSetString("/sim/startup/units", "feet"); fgSetString("/sim/hud/frame-stat-type", "tris"); - + // Time options fgSetInt("/sim/startup/time-offset", 0); fgSetString("/sim/startup/time-offset-type", "system-offset"); @@ -355,7 +355,7 @@ parse_time(const string& time_in) { // parse a date string (yyyy:mm:dd:hh:mm:ss) into a time_t (seconds) -static long int +static long int parse_date( const string& date) { struct tm gmt; @@ -493,7 +493,7 @@ parse_time_offset( const string& time_str) { } -// Parse --fov=x.xx type option +// Parse --fov=x.xx type option static double parse_fov( const string& arg ) { double fov = atof(arg); @@ -520,7 +520,7 @@ parse_fov( const string& arg ) { // point values are ok. // // Serial example "--nmea=serial,dir,hz,device,baud" where -// +// // device = OS device name of serial line to be open()'ed // baud = {300, 1200, 2400, ..., 230400} // @@ -900,7 +900,7 @@ fgSetupProxy( const char *arg ) string::size_type pos; host = port = auth = ""; - if ((pos = options.find("@")) != string::npos) + if ((pos = options.find("@")) != string::npos) auth = options.substr(0, pos++); else pos = 0; @@ -981,7 +981,7 @@ fgOptViewOffset( const char *arg ) } else { default_view_offset = atof( woffset.c_str() ) * SGD_DEGREES_TO_RADIANS; } - /* apparently not used (CLO, 11 Jun 2002) + /* apparently not used (CLO, 11 Jun 2002) FGViewer *pilot_view = (FGViewer *)globals->get_viewmgr()->get_view( 0 ); */ // this will work without calls to the viewer... @@ -1317,7 +1317,7 @@ struct OptionDesc { const char *s_param; int (*func)( const char * ); } fgOptionArray[] = { - + {"language", true, OPTION_FUNC, "", false, "", fgOptLanguage }, {"disable-game-mode", false, OPTION_BOOL, "/sim/startup/game-mode", false, "", 0 }, {"enable-game-mode", false, OPTION_BOOL, "/sim/startup/game-mode", true, "", 0 }, @@ -1464,6 +1464,9 @@ struct OptionDesc { {"proxy", true, OPTION_FUNC, "", false, "", fgSetupProxy }, {"callsign", true, OPTION_FUNC, "", false, "", fgOptCallSign}, {"multiplay", true, OPTION_CHANNEL, "", false, "", 0 }, +#ifdef FG_HAVE_HLA + {"hla", true, OPTION_CHANNEL, "", false, "", 0 }, +#endif {"trace-read", true, OPTION_FUNC, "", false, "", fgOptTraceRead }, {"trace-write", true, OPTION_FUNC, "", false, "", fgOptTraceWrite }, {"log-level", true, OPTION_FUNC, "", false, "", fgOptLogLevel }, @@ -1756,7 +1759,7 @@ fgParseOptions (const string& path) { line = line.substr( 0, i ); if ( parse_option( line ) == FG_OPTIONS_ERROR ) { - cerr << endl << "Config file parse error: " << path << " '" + cerr << endl << "Config file parse error: " << path << " '" << line << "'" << endl; fgUsage(); exit(-1); @@ -1767,7 +1770,7 @@ fgParseOptions (const string& path) { // Print usage message -void +void fgUsage (bool verbose) { SGPropertyNode *locale = globals->get_locale(); @@ -1784,7 +1787,7 @@ fgUsage (bool verbose) cout << "Make sure the file options.xml is located in the FlightGear base directory," << endl; cout << "and the location of the base directory is specified by setting $FG_ROOT or" << endl; cout << "by adding --fg-root=path as a program argument." << endl; - + exit(-1); } @@ -1828,7 +1831,7 @@ fgUsage (bool verbose) tmp.append(", -"); tmp.append(short_name->getStringValue()); } - + if (tmp.size() <= 25) { msg+= " --"; msg += tmp; diff --git a/src/Network/HLA/Makefile.am b/src/Network/HLA/Makefile.am new file mode 100644 index 000000000..792ae4418 --- /dev/null +++ b/src/Network/HLA/Makefile.am @@ -0,0 +1,8 @@ +noinst_LIBRARIES = libFGHLA.a + +libFGHLA_a_SOURCES = \ + hla.cxx hla.hxx + +libFGHLA_a_CPPFLAGS = $(HLA_CPPFLAGS) + +INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src diff --git a/src/Network/HLA/hla.cxx b/src/Network/HLA/hla.cxx new file mode 100644 index 000000000..de12ae2f1 --- /dev/null +++ b/src/Network/HLA/hla.cxx @@ -0,0 +1,1267 @@ +// +// Copyright (C) 2009 - 2010 Mathias Fröhlich +// +// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "hla.hxx" + +#include + +#include + +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include
+#include
+ +// FIXME rename HLAMP +// FIXME make classes here private ... + +namespace sg = simgear; + +enum HLAVersion { + RTI13, + RTI1516, + RTI1516E +}; + +class FGHLA::XMLConfigReader : public XMLVisitor { +public: + XMLConfigReader() : + _rtiVersion(RTI13) + { } + + const std::string& getFederateObjectModel() const + { return _federateObjectModel; } + + HLAVersion getRTIVersion() const + { return _rtiVersion; } + const std::vector& getRTIArguments() const + { return _rtiArguments; } + + struct DataElement { + std::string _type; + std::string _name; + std::string _inProperty; + std::string _outProperty; + }; + typedef std::list DataElementList; + struct ModelConfig { + std::string _type; + DataElementList _dataElementList; + std::list > _modelMap; + }; + struct SimTimeConfig { + std::string _type; + DataElementList _dataElementList; + }; + struct PositionConfig { + std::string _type; + DataElementList _dataElementList; + }; + struct MPPropertiesConfig { + std::string _name; + }; + struct ObjectClassConfig { + std::string _name; + std::string _type; + ModelConfig _modelConfig; + SimTimeConfig _simTimeConfig; + PositionConfig _positionConfig; + MPPropertiesConfig _mpPropertiesConfig; + DataElementList _dataElementList; + }; + typedef std::list ObjectClassConfigList; + + const ObjectClassConfigList& getObjectClassConfigList() const + { return _objectClassConfigList; } + +protected: + virtual void startXML() + { + _modeStack.push(TopLevelMode); + } + virtual void endXML() + { + _modeStack.pop(); + } + virtual void startElement(const char* name, const XMLAttributes& atts) + { + if (strcmp(name, "hlaConfiguration") == 0) { + if (!isModeStackTop(TopLevelMode)) + throw sg_exception("hlaConfiguration tag not at top level"); + + _modeStack.push(HLAConfigurationMode); + } + + else if (strcmp(name, "rti") == 0) { + if (!isModeStackTop(HLAConfigurationMode)) + throw sg_exception("rti tag not at top level"); + + std::string rtiVersion = getAttribute("version", atts); + if (rtiVersion == "RTI13") + _rtiVersion = RTI13; + else if (rtiVersion == "RTI1516") + _rtiVersion = RTI1516; + else if (rtiVersion == "RTI1516E") + _rtiVersion = RTI1516E; + else + throw sg_exception("invalid version attribute in rti tag"); + + _modeStack.push(RTIMode); + } + else if (strcmp(name, "argument") == 0) { + if (!isModeStackTop(RTIMode)) + throw sg_exception("argument tag not inside an rti tag"); + + _modeStack.push(RTIArgumentMode); + _rtiArguments.push_back(std::string()); + } + + else if (strcmp(name, "objects") == 0) { + if (!isModeStackTop(HLAConfigurationMode)) + throw sg_exception("objects tag not at top level"); + + _modeStack.push(ObjectsMode); + } + else if (strcmp(name, "objectClass") == 0) { + // Currently this is flat and only allows one class to be configured + if (!isModeStackTop(ObjectsMode)) + throw sg_exception("objects tag not inside an objects tag"); + if (!_objectClassConfigList.empty()) + throw sg_exception("currently only one objectClass is allowed"); + // if (!isModeStackTop(ObjectsMode) && !isModeStackTop(ObjectClassMode)) + // throw sg_exception("objects tag not inside an objects or objectClass tag"); + + ObjectClassConfig objectClassConfig; + objectClassConfig._type = getAttribute("type", atts); + objectClassConfig._name = getAttribute("name", atts); + _objectClassConfigList.push_back(objectClassConfig); + + _modeStack.push(ObjectClassMode); + } + else if (strcmp(name, "model") == 0) { + if (!isModeStackTop(ObjectClassMode)) + throw sg_exception("position tag not inside an objectClass tag"); + + _objectClassConfigList.back()._modelConfig._type = getAttribute("type", atts); + + _modeStack.push(ModelMode); + } + else if (strcmp(name, "key") == 0) { + if (!isModeStackTop(ModelMode)) + throw sg_exception("key tag not inside an model tag"); + + std::pair p; + p.first = getAttribute("external", atts); + p.second = getAttribute("modelPath", atts); + _objectClassConfigList.back()._modelConfig._modelMap.push_back(p); + + _modeStack.push(KeyMode); + } + else if (strcmp(name, "simTime") == 0) { + if (!isModeStackTop(ObjectClassMode)) + throw sg_exception("simTime tag not inside an objectClass tag"); + + _objectClassConfigList.back()._simTimeConfig._type = getAttribute("type", atts); + + _modeStack.push(SimTimeMode); + } + else if (strcmp(name, "position") == 0) { + if (!isModeStackTop(ObjectClassMode)) + throw sg_exception("position tag not inside an objectClass tag"); + + _objectClassConfigList.back()._positionConfig._type = getAttribute("type", atts); + + _modeStack.push(PositionMode); + } + else if (strcmp(name, "mpProperties") == 0) { + if (!isModeStackTop(ObjectClassMode)) + throw sg_exception("mpProperties tag not inside an objectClass tag"); + + _objectClassConfigList.back()._mpPropertiesConfig._name = getAttribute("name", atts); + + _modeStack.push(MPPropertiesMode); + } + else if (strcmp(name, "dataElement") == 0) { + + DataElement dataElement; + dataElement._type = getAttribute("type", atts); + dataElement._name = getAttribute("name", atts); + if (isModeStackTop(ModelMode)) { + _objectClassConfigList.back()._modelConfig._dataElementList.push_back(dataElement); + } else if (isModeStackTop(SimTimeMode)) { + _objectClassConfigList.back()._simTimeConfig._dataElementList.push_back(dataElement); + } else if (isModeStackTop(PositionMode)) { + _objectClassConfigList.back()._positionConfig._dataElementList.push_back(dataElement); + } else if (isModeStackTop(ObjectClassMode)) { + std::string io = getAttribute("io", atts); + dataElement._inProperty = getAttribute("in", atts); + if (dataElement._inProperty.empty()) + dataElement._inProperty = io; + dataElement._outProperty = getAttribute("out", atts); + if (dataElement._outProperty.empty()) + dataElement._outProperty = io; + _objectClassConfigList.back()._dataElementList.push_back(dataElement); + } else { + throw sg_exception("dataElement tag not inside a position, model or objectClass tag"); + } + + _modeStack.push(DataElementMode); + } + + else if (strcmp(name, "federateObjectModel") == 0) { + if (!isModeStackTop(HLAConfigurationMode)) + throw sg_exception("federateObjectModel tag not at top level"); + + _federateObjectModel = getAttribute("name", atts); + _modeStack.push(FederateObjectModelMode); + } + + else { + throw sg_exception(std::string("Unknown tag ") + name); + } + } + + virtual void data(const char * s, int length) + { + if (isModeStackTop(RTIArgumentMode)) { + _rtiArguments.back().append(s, length); + } + } + + virtual void endElement(const char* name) + { + _modeStack.pop(); + } + + std::string getAttribute(const char* name, const XMLAttributes& atts) + { + int index = atts.findAttribute(name); + if (index < 0 || atts.size() <= index) + return std::string(); + return std::string(atts.getValue(index)); + } + std::string getAttribute(const std::string& name, const XMLAttributes& atts) + { + int index = atts.findAttribute(name.c_str()); + if (index < 0 || atts.size() <= index) + return std::string(); + return std::string(atts.getValue(index)); + } + +private: + // poor peoples xml validation ... + enum Mode { + TopLevelMode, + HLAConfigurationMode, + RTIMode, + RTIArgumentMode, + ObjectsMode, + ObjectClassMode, + ModelMode, + KeyMode, + SimTimeMode, + PositionMode, + MPPropertiesMode, + DataElementMode, + FederateObjectModelMode + }; + bool isModeStackTop(Mode mode) + { + if (_modeStack.empty()) + return false; + return _modeStack.top() == mode; + } + std::stack _modeStack; + + std::string _federateObjectModel; + HLAVersion _rtiVersion; + std::vector _rtiArguments; + + ObjectClassConfigList _objectClassConfigList; +}; + +class AbstractSimTime : public SGReferenced { +public: + virtual ~AbstractSimTime() {} + + virtual double getTimeStamp() const = 0; + virtual void setTimeStamp(double) = 0; +}; + +class SimTimeFactory : public SGReferenced { +public: + virtual ~SimTimeFactory() {} + virtual AbstractSimTime* createSimTime(sg::HLAAttributePathElementMap&) const = 0; +}; + +// A SimTime implementation that works with the simulation +// time in an attribute. Used when we cannot do real time management. +class AttributeSimTime : public AbstractSimTime { +public: + + virtual double getTimeStamp() const + { return _simTime; } + virtual void setTimeStamp(double simTime) + { _simTime = simTime; } + + sg::HLADataElement* getDataElement() + { return _simTime.getDataElement(); } + +private: + simgear::HLADoubleData _simTime; +}; + +class AttributeSimTimeFactory : public SimTimeFactory { +public: + virtual AttributeSimTime* createSimTime(sg::HLAAttributePathElementMap& attributePathElementMap) const + { + AttributeSimTime* attributeSimTime = new AttributeSimTime; + attributePathElementMap[_simTimeIndexPathPair.first][_simTimeIndexPathPair.second] = attributeSimTime->getDataElement(); + return attributeSimTime; + } + + void setSimTimeIndexPathPair(const sg::HLADataElement::IndexPathPair& indexPathPair) + { _simTimeIndexPathPair = indexPathPair; } + +private: + sg::HLADataElement::IndexPathPair _simTimeIndexPathPair; +}; + +// A SimTime implementation that works with the simulation +// time in two attributes like the avation sim net fom works +class MSecAttributeSimTime : public AbstractSimTime { +public: + virtual double getTimeStamp() const + { + return _secSimTime + 1e-3*_msecSimTime; + } + virtual void setTimeStamp(double simTime) + { + double sec = floor(simTime); + _secSimTime = sec; + _msecSimTime = 1e3*(simTime - sec); + } + + sg::HLADataElement* getSecDataElement() + { return _secSimTime.getDataElement(); } + sg::HLADataElement* getMSecDataElement() + { return _msecSimTime.getDataElement(); } + +private: + simgear::HLADoubleData _secSimTime; + simgear::HLADoubleData _msecSimTime; +}; + +class MSecAttributeSimTimeFactory : public SimTimeFactory { +public: + virtual MSecAttributeSimTime* createSimTime(sg::HLAAttributePathElementMap& attributePathElementMap) const + { + MSecAttributeSimTime* attributeSimTime = new MSecAttributeSimTime; + attributePathElementMap[_secIndexPathPair.first][_secIndexPathPair.second] = attributeSimTime->getSecDataElement(); + attributePathElementMap[_msecIndexPathPair.first][_msecIndexPathPair.second] = attributeSimTime->getMSecDataElement(); + return attributeSimTime; + } + + void setSecIndexPathPair(const sg::HLADataElement::IndexPathPair& indexPathPair) + { _secIndexPathPair = indexPathPair; } + void setMSecIndexPathPair(const sg::HLADataElement::IndexPathPair& indexPathPair) + { _msecIndexPathPair = indexPathPair; } + +private: + sg::HLADataElement::IndexPathPair _secIndexPathPair; + sg::HLADataElement::IndexPathPair _msecIndexPathPair; +}; + +class AbstractModel : public SGReferenced { +public: + virtual ~AbstractModel() {} + + virtual std::string getModelPath() const = 0; + virtual void setModelPath(const std::string&) = 0; +}; + +class ModelFactory : public SGReferenced { +public: + virtual ~ModelFactory() {} + virtual AbstractModel* createModel(sg::HLAAttributePathElementMap&, bool) const = 0; +}; + +class AttributeModel : public AbstractModel { +public: + virtual std::string getModelPath() const + { return _modelPath; } + virtual void setModelPath(const std::string& modelPath) + { _modelPath = modelPath; } + + sg::HLADataElement* getDataElement() + { return _modelPath.getDataElement(); } + +private: + simgear::HLAStringData _modelPath; +}; + +class AttributeModelFactory : public ModelFactory { +public: + virtual AttributeModel* createModel(sg::HLAAttributePathElementMap& attributePathElementMap, bool outgoing) const + { + AttributeModel* attributeModel = new AttributeModel; + attributePathElementMap[_modelIndexPathPair.first][_modelIndexPathPair.second] = attributeModel->getDataElement(); + if (outgoing) + attributeModel->setModelPath(fgGetString("/sim/model/path", "default")); + return attributeModel; + } + + void setModelIndexPathPair(const sg::HLADataElement::IndexPathPair& indexPathPair) + { _modelIndexPathPair = indexPathPair; } + +private: + sg::HLADataElement::IndexPathPair _modelIndexPathPair; +}; + +class AttributeMapModelFactory : public ModelFactory { +public: + class Model : public AbstractModel { + public: + Model(const AttributeMapModelFactory* mapModelFactory) : + _mapModelFactory(mapModelFactory) + { } + + virtual std::string getModelPath() const + { + if (_modelPath.getValue().empty()) + return _modelPath.getValue(); + return _mapModelFactory->mapToFlightgear(_modelPath); + } + virtual void setModelPath(const std::string& modelPath) + { _modelPath = _mapModelFactory->mapToExternal(modelPath); } + + sg::HLADataElement* getDataElement() + { return _modelPath.getDataElement(); } + + private: + simgear::HLAStringData _modelPath; + SGSharedPtr _mapModelFactory; + }; + + virtual AbstractModel* createModel(sg::HLAAttributePathElementMap& attributePathElementMap, bool outgoing) const + { + Model* attributeModel = new Model(this); + attributePathElementMap[_modelIndexPathPair.first][_modelIndexPathPair.second] = attributeModel->getDataElement(); + if (outgoing) + attributeModel->setModelPath(fgGetString("/sim/model/path", "default")); + return attributeModel; + } + + void setModelIndexPathPair(const sg::HLADataElement::IndexPathPair& indexPathPair) + { _modelIndexPathPair = indexPathPair; } + + std::string mapToFlightgear(const std::string& externalModel) const + { + std::map::const_iterator i; + i = _externalToModelPathMap.find(externalModel); + if (i != _externalToModelPathMap.end()) + return i->second; + return "default"; + } + std::string mapToExternal(const std::string& modelPath) const + { + std::map::const_iterator i; + i = _modelPathToExternalMap.find(modelPath); + if (i != _modelPathToExternalMap.end()) + return i->second; + return _externalDefault; + } + + void setExternalDefault(const std::string& externalDefault) + { _externalDefault = externalDefault; } + void addExternalModelPathPair(const std::string& external, const std::string& modelPath) + { + _externalToModelPathMap[external] = modelPath; + _modelPathToExternalMap[modelPath] = external; + } + +private: + sg::HLADataElement::IndexPathPair _modelIndexPathPair; + std::map _externalToModelPathMap; + std::map _modelPathToExternalMap; + std::string _externalDefault; +}; + +// Factory class that is used to create an apternative data element for the multiplayer property attribute +class MPPropertyVariantDataElementFactory : public sg::HLAVariantArrayDataElement::AlternativeDataElementFactory { +public: + MPPropertyVariantDataElementFactory(sg::HLAPropertyReferenceSet* propertyReferenceSet) : + _propertyReferenceSet(propertyReferenceSet) + { } + + virtual sg::HLADataElement* createElement(const sg::HLAVariantDataElement& variantDataElement, unsigned index) + { + const sg::HLAVariantDataType* dataType = variantDataElement.getDataType(); + if (!dataType) + return 0; + const sg::HLAEnumeratedDataType* enumDataType = dataType->getEnumeratedDataType(); + if (!enumDataType) + return 0; + const sg::HLADataType* alternativeDataType = dataType->getAlternativeDataType(index); + if (!alternativeDataType) + return 0; + + // The relative property path should be in the semantics field name + std::string relativePath = dataType->getAlternativeSemantics(index); + sg::HLAPropertyReference* propertyReference = new sg::HLAPropertyReference(relativePath); + _propertyReferenceSet->insert(propertyReference); + return new sg::HLAPropertyDataElement(alternativeDataType, propertyReference); + } + +private: + SGSharedPtr _propertyReferenceSet; +}; + +class MPAttributeCallback : public sg::HLAObjectInstance::AttributeCallback { +public: + MPAttributeCallback() : + _propertyReferenceSet(new sg::HLAPropertyReferenceSet), + _mpProperties(new sg::HLAVariantArrayDataElement) + { + _mpProperties->setAlternativeDataElementFactory(new MPPropertyVariantDataElementFactory(_propertyReferenceSet.get())); + } + virtual ~MPAttributeCallback() + { } + + void setLocation(sg::HLAAbstractLocation* location) + { _location = location; } + sg::HLAAbstractLocation* getLocation() + { return _location.get(); } + const sg::HLAAbstractLocation* getLocation() const + { return _location.get(); } + + void setModel(AbstractModel* model) + { _model = model; } + AbstractModel* getModel() + { return _model.get(); } + const AbstractModel* getModel() const + { return _model.get(); } + + void setSimTime(AbstractSimTime* simTime) + { _simTime = simTime; } + AbstractSimTime* getSimTime() + { return _simTime.get(); } + const AbstractSimTime* getSimTime() const + { return _simTime.get(); } + + sg::HLAVariantArrayDataElement* getMPProperties() const + { return _mpProperties.get(); } + + SGSharedPtr _propertyReferenceSet; + +protected: + SGSharedPtr _location; + SGSharedPtr _simTime; + SGSharedPtr _model; + SGSharedPtr _mpProperties; +}; + +class MPOutAttributeCallback : public MPAttributeCallback { +public: + virtual void updateAttributeValues(sg::HLAObjectInstance&, const sg::RTIData&) + { + _simTime->setTimeStamp(globals->get_sim_time_sec()); + + SGGeod position = ifce.getPosition(); + // The quaternion rotating from the earth centered frame to the + // horizontal local frame + SGQuatd qEc2Hl = SGQuatd::fromLonLat(position); + SGQuatd hlOr = SGQuatd::fromYawPitchRoll(ifce.get_Psi(), ifce.get_Theta(), ifce.get_Phi()); + _location->setCartPosition(SGVec3d::fromGeod(position)); + _location->setCartOrientation(qEc2Hl*hlOr); + // The angular velocitied in the body frame + double p = ifce.get_P_body(); + double q = ifce.get_Q_body(); + double r = ifce.get_R_body(); + _location->setAngularBodyVelocity(SGVec3d(p, q, r)); + // The body uvw velocities in the interface are wrt the wind instead + // of wrt the ec frame + double n = ifce.get_V_north(); + double e = ifce.get_V_east(); + double d = ifce.get_V_down(); + _location->setLinearBodyVelocity(hlOr.transform(SGVec3d(n, e, d))); + + if (_mpProperties.valid() && _mpProperties->getNumElements() == 0) { + if (_propertyReferenceSet.valid() && _propertyReferenceSet->getRootNode()) { + const sg::HLADataType* elementDataType = _mpProperties->getElementDataType(); + const sg::HLAVariantDataType* variantDataType = elementDataType->toVariantDataType(); + for (unsigned i = 0, count = 0; i < variantDataType->getNumAlternatives(); ++i) { + std::string name = variantDataType->getAlternativeSemantics(i); + SGPropertyNode* node = _propertyReferenceSet->getRootNode()->getNode(name); + if (!node) + continue; + _mpProperties->getOrCreateElement(count++)->setAlternativeIndex(i); + } + } + } + } + +private: + FlightProperties ifce; +}; + +class MPInAttributeCallback : public MPAttributeCallback { +public: + virtual void reflectAttributeValues(sg::HLAObjectInstance& objectInstance, + const sg::RTIIndexDataPairList&, const sg::RTIData&) + { + // Puh, damn ordering problems with properties startup and so on + if (_aiMultiplayer.valid()) { + FGExternalMotionData motionInfo; + motionInfo.time = _simTime->getTimeStamp(); + motionInfo.lag = 0; + + motionInfo.position = _location->getCartPosition(); + motionInfo.orientation = toQuatf(_location->getCartOrientation()); + motionInfo.linearVel = toVec3f(_location->getLinearBodyVelocity()); + motionInfo.angularVel = toVec3f(_location->getAngularBodyVelocity()); + motionInfo.linearAccel = SGVec3f::zeros(); + motionInfo.angularAccel = SGVec3f::zeros(); + + _aiMultiplayer->addMotionInfo(motionInfo, SGTimeStamp::now().getSeconds()); + + } else { + std::string modelPath = _model->getModelPath(); + if (modelPath.empty()) + return; + FGAIManager *aiMgr; + aiMgr = static_cast(globals->get_subsystem("ai_model")); + if (!aiMgr) + return; + + _aiMultiplayer = new FGAIMultiplayer; + _aiMultiplayer->setPath(modelPath.c_str()); + aiMgr->attach(_aiMultiplayer.get()); + + _propertyReferenceSet->setRootNode(_aiMultiplayer->getPropertyRoot()); + objectInstance.requestAttributeUpdate(); + } + } + + void setDie() + { + if (!_aiMultiplayer.valid()) + return; + _aiMultiplayer->setDie(true); + _aiMultiplayer = 0; + } + +private: + SGSharedPtr _aiMultiplayer; +}; + + +class MpClassCallback : public sg::HLAObjectClass::InstanceCallback { +public: + MpClassCallback() + { } + virtual ~MpClassCallback() + { } + + virtual void discoverInstance(const sg::HLAObjectClass& objectClass, sg::HLAObjectInstance& objectInstance, const sg::RTIData& tag) + { + MPInAttributeCallback* attributeCallback = new MPInAttributeCallback; + objectInstance.setAttributeCallback(attributeCallback); + attachDataElements(objectInstance, *attributeCallback, false); + objectInstance.requestAttributeUpdate(); + } + + virtual void removeInstance(const sg::HLAObjectClass& objectClass, sg::HLAObjectInstance& objectInstance, const sg::RTIData& tag) + { + MPInAttributeCallback* attributeCallback; + attributeCallback = dynamic_cast(objectInstance.getAttributeCallback().get()); + if (!attributeCallback) { + SG_LOG(SG_IO, SG_WARN, "HLA: expected to have a different attribute callback in remove instance."); + return; + } + attributeCallback->setDie(); + } + + virtual void registerInstance(const sg::HLAObjectClass& objectClass, sg::HLAObjectInstance& objectInstance) + { + MPOutAttributeCallback* attributeCallback = new MPOutAttributeCallback; + objectInstance.setAttributeCallback(attributeCallback); + attachDataElements(objectInstance, *attributeCallback, true); + attributeCallback->_propertyReferenceSet->setRootNode(fgGetNode("/", true)); + } + + virtual void deleteInstance(const sg::HLAObjectClass& objectClass, sg::HLAObjectInstance& objectInstance) + { + } + + void setLocationFactory(sg::HLALocationFactory* locationFactory) + { _locationFactory = locationFactory; } + sg::HLALocationFactory* getLocationFactory() + { return _locationFactory.get(); } + + void setSimTimeFactory(SimTimeFactory* simTimeFactory) + { _simTimeFactory = simTimeFactory; } + SimTimeFactory* getSimTimeFactory() + { return _simTimeFactory.get(); } + + void setModelFactory(ModelFactory* modelFactory) + { _modelFactory = modelFactory; } + ModelFactory* getModelFactory() + { return _modelFactory.get(); } + + void setMPPropertiesIndexPathPair(const sg::HLADataElement::IndexPathPair& indexPathPair) + { _mpPropertiesIndexPathPair = indexPathPair; } + const sg::HLADataElement::IndexPathPair& getMPPropertiesIndexPathPair() const + { return _mpPropertiesIndexPathPair; } + + typedef std::map PathPropertyMap; + typedef std::map AttributePathPropertyMap; + + void setInputProperty(const sg::HLADataElement::IndexPathPair& indexPathPair, const std::string& property) + { + _inputProperties[indexPathPair.first][indexPathPair.second] = property; + } + void setOutputProperty(const sg::HLADataElement::IndexPathPair& indexPathPair, const std::string& property) + { + _outputProperties[indexPathPair.first][indexPathPair.second] = property; + } + +private: + void attachDataElements(sg::HLAObjectInstance& objectInstance, MPAttributeCallback& attributeCallback, bool outgoing) + { + sg::HLAAttributePathElementMap attributePathElementMap; + + if (_locationFactory.valid()) + attributeCallback.setLocation(_locationFactory->createLocation(attributePathElementMap)); + if (_modelFactory.valid()) + attributeCallback.setModel(_modelFactory->createModel(attributePathElementMap, outgoing)); + if (_simTimeFactory.valid()) + attributeCallback.setSimTime(_simTimeFactory->createSimTime(attributePathElementMap)); + + attributePathElementMap[_mpPropertiesIndexPathPair.first][_mpPropertiesIndexPathPair.second] = attributeCallback.getMPProperties(); + + if (outgoing) + attachPropertyDataElements(*attributeCallback._propertyReferenceSet, + attributePathElementMap, _outputProperties); + else + attachPropertyDataElements(*attributeCallback._propertyReferenceSet, + attributePathElementMap, _inputProperties); + + objectInstance.setAttributes(attributePathElementMap); + } + void attachPropertyDataElements(sg::HLAPropertyReferenceSet& propertyReferenceSet, + sg::HLAAttributePathElementMap& attributePathElementMap, + const AttributePathPropertyMap& attributePathPropertyMap) + { + for (AttributePathPropertyMap::const_iterator i = attributePathPropertyMap.begin(); + i != attributePathPropertyMap.end(); ++i) { + for (PathPropertyMap::const_iterator j = i->second.begin(); + j != i->second.end(); ++j) { + SGSharedPtr propertyReference; + propertyReference = new sg::HLAPropertyReference(j->second); + propertyReferenceSet.insert(propertyReference); + attributePathElementMap[i->first][j->first] = new sg::HLAPropertyDataElement(propertyReference); + } + } + } + + AttributePathPropertyMap _inputProperties; + AttributePathPropertyMap _outputProperties; + + SGSharedPtr _locationFactory; + SGSharedPtr _simTimeFactory; + SGSharedPtr _modelFactory; + + sg::HLADataElement::IndexPathPair _mpPropertiesIndexPathPair; +}; + +FGHLA::FGHLA(const std::vector& tokens) +{ + if (1 < tokens.size() && !tokens[1].empty()) + set_direction(tokens[1]); + else + set_direction("bi"); + + int rate = 60; + if (2 < tokens.size() && !tokens[2].empty()) { + std::stringstream ss(tokens[2]); + ss >> rate; + } + set_hz(rate); + + if (3 < tokens.size() && !tokens[3].empty()) + _federate = tokens[3]; + else + _federate = fgGetString("/sim/user/callsign"); + + if (4 < tokens.size() && !tokens[4].empty()) + _federation = tokens[4]; + else + _federation = "FlightGear"; + + if (5 < tokens.size() && !tokens[5].empty()) + _objectModelConfig = tokens[5]; + else + _objectModelConfig = "mp-aircraft.xml"; +} + +FGHLA::~FGHLA() +{ +} + +bool +FGHLA::open() +{ + if (is_enabled()) { + SG_LOG(SG_IO, SG_ALERT, "This shouldn't happen, but the channel " + "is already in use, ignoring"); + return false; + } + + if (_federation.empty()) { + SG_LOG(SG_IO, SG_ALERT, "No federation to join given to " + "the HLA module"); + return false; + } + + if (_federate.empty()) { + SG_LOG(SG_IO, SG_ALERT, "No federate name given to the HLA module"); + return false; + } + + if (!SGPath(_objectModelConfig).exists()) { + SGPath path(globals->get_fg_root()); + path.append("HLA"); + path.append(_objectModelConfig); + if (path.exists()) + _objectModelConfig = path.str(); + else { + SG_LOG(SG_IO, SG_ALERT, "Federate object model configuration \"" + << _objectModelConfig << "\" does not exist."); + return false; + } + } + + XMLConfigReader configReader; + try { + readXML(_objectModelConfig, configReader); + } catch (const sg_throwable& e) { + SG_LOG(SG_IO, SG_ALERT, "Could not open HLA configuration file: " + << e.getMessage()); + return false; + } catch (...) { + SG_LOG(SG_IO, SG_ALERT, "Could not open HLA configuration file"); + return false; + } + + // Flightgears hla configuration + std::string objectModel = configReader.getFederateObjectModel(); + if (objectModel.empty()) { + SG_LOG(SG_IO, SG_ALERT, "No federate object model given to " + "the HLA module"); + return false; + } + if (!SGPath(objectModel).exists()) { + SGPath path(SGPath(_objectModelConfig).dir()); + path.append(objectModel); + if (path.exists()) + objectModel = path.str(); + else { + SG_LOG(SG_IO, SG_ALERT, "Federate object model file \"" + << objectModel << "\" does not exist."); + return false; + } + } + + // We need that to communicate to the rti + switch (configReader.getRTIVersion()) { + case RTI13: + _hlaFederate = new simgear::HLA13Federate; + break; + case RTI1516: + SG_LOG(SG_IO, SG_ALERT, "HLA version RTI1516 not yet(!?) supported."); + return false; + case RTI1516E: + SG_LOG(SG_IO, SG_ALERT, "HLA version RTI1516E not yet(!?) supported."); + return false; + } + + // Try to create a new federation execution + _hlaFederate->createFederationExecution(_federation, objectModel); + + // Try to join + if (!_hlaFederate->join(_federate, _federation)) { + SG_LOG(SG_IO, SG_ALERT, "Could not join federation"); + return false; + } + + // bool publish = get_direction() & SG_IO_OUT; + // bool subscribe = get_direction() & SG_IO_IN; + + sg::HLAFederate::ObjectModelFactory objectModelFactory; + if (!_hlaFederate->readObjectModelTemplate(objectModel, objectModelFactory)) { + SG_LOG(SG_IO, SG_ALERT, "Could not read omt file \"" << objectModel << "\"!"); + return false; + } + + // This should be configured form a file + XMLConfigReader::ObjectClassConfigList::const_iterator i; + for (i = configReader.getObjectClassConfigList().begin(); + i != configReader.getObjectClassConfigList().end(); ++i) { + + if (i->_type != "Multiplayer") { + SG_LOG(SG_IO, SG_ALERT, "Ignoring unknown object class type \"" << i->_type << "\"!"); + continue; + } + + /// The object class for HLA aircraft + SGSharedPtr objectClass; + + // Register the object class that we need for this simple hla implementation + std::string aircraftClassName = i->_name; + objectClass = _hlaFederate->getObjectClass(aircraftClassName); + if (!objectClass.valid()) { + SG_LOG(SG_IO, SG_ALERT, "Could not find " << aircraftClassName << " object class!"); + continue; + } + + SGSharedPtr mpClassCallback = new MpClassCallback; + + if (i->_positionConfig._type == "cartesian") { + SGSharedPtr locationFactory; + locationFactory = new sg::HLACartesianLocationFactory; + XMLConfigReader::DataElementList::const_iterator j; + for (j = i->_positionConfig._dataElementList.begin(); + j != i->_positionConfig._dataElementList.end(); ++j) { + + if (j->_type == "position-x") + locationFactory->setPositionIndexPathPair(0, objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "position-y") + locationFactory->setPositionIndexPathPair(1, objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "position-z") + locationFactory->setPositionIndexPathPair(2, objectClass->getIndexPathPair(j->_name)); + + else if (j->_type == "orientation-sin-angle-axis-x") + locationFactory->setOrientationIndexPathPair(0, objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "orientation-sin-angle-axis-y") + locationFactory->setOrientationIndexPathPair(1, objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "orientation-sin-angle-axis-z") + locationFactory->setOrientationIndexPathPair(2, objectClass->getIndexPathPair(j->_name)); + + else if (j->_type == "angular-velocity-x") + locationFactory->setAngularVelocityIndexPathPair(0, objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "angular-velocity-y") + locationFactory->setAngularVelocityIndexPathPair(1, objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "angular-velocity-z") + locationFactory->setAngularVelocityIndexPathPair(2, objectClass->getIndexPathPair(j->_name)); + + else if (j->_type == "linear-velocity-x") + locationFactory->setLinearVelocityIndexPathPair(0, objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "linear-velocity-y") + locationFactory->setLinearVelocityIndexPathPair(1, objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "linear-velocity-z") + locationFactory->setLinearVelocityIndexPathPair(2, objectClass->getIndexPathPair(j->_name)); + + else { + SG_LOG(SG_IO, SG_ALERT, "HLA: Unknown position configuration type \"" + << j->_type << "\"for object class \"" << aircraftClassName << "\". Ignoring!"); + } + } + + mpClassCallback->setLocationFactory(locationFactory.get()); + } else if (i->_positionConfig._type == "geodetic") { + SGSharedPtr locationFactory; + locationFactory = new sg::HLAGeodeticLocationFactory; + + XMLConfigReader::DataElementList::const_iterator j; + for (j = i->_positionConfig._dataElementList.begin(); + j != i->_positionConfig._dataElementList.end(); ++j) { + + if (j->_type == "latitude-deg") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::LatitudeDeg, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "latitude-rad") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::LatitudeRad, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "longitude-deg") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::LongitudeDeg, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "longitude-rad") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::LongitudeRad, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "elevation-m") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::ElevationM, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "elevation-m") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::ElevationFt, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "heading-deg") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::HeadingDeg, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "heading-rad") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::HeadingRad, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "pitch-deg") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::PitchDeg, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "pitch-rad") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::PitchRad, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "roll-deg") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::RollDeg, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "roll-rad") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::RollRad, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "ground-track-deg") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::GroundTrackDeg, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "ground-track-rad") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::GroundTrackRad, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "ground-speed-kt") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::GroundSpeedKnots, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "ground-speed-ft-per-sec") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::GroundSpeedFtPerSec, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "ground-speed-m-per-sec") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::GroundSpeedMPerSec, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "vertical-speed-ft-per-sec") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::VerticalSpeedFtPerSec, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "vertical-speed-ft-per-min") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::VerticalSpeedFtPerMin, + objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "vertical-speed-m-per-sec") + locationFactory->setIndexPathPair(sg::HLAGeodeticLocationFactory::VerticalSpeedMPerSec, + objectClass->getIndexPathPair(j->_name)); + else { + SG_LOG(SG_IO, SG_ALERT, "HLA: Unknown position configuration type \"" + << j->_type << "\" for object class \"" << aircraftClassName << "\". Ignoring!"); + } + } + + mpClassCallback->setLocationFactory(locationFactory.get()); + } + + if (i->_modelConfig._type == "native") { + SGSharedPtr attributeModelFactory; + attributeModelFactory = new AttributeModelFactory; + + XMLConfigReader::DataElementList::const_iterator j; + for (j = i->_modelConfig._dataElementList.begin(); + j != i->_modelConfig._dataElementList.end(); ++j) { + + if (j->_type == "model-path") + attributeModelFactory->setModelIndexPathPair(objectClass->getIndexPathPair(j->_name)); + + else { + SG_LOG(SG_IO, SG_ALERT, "HLA: Unknown model configuration type \"" + << j->_type << "\" for object class \"" << aircraftClassName << "\". Ignoring!"); + } + } + + mpClassCallback->setModelFactory(attributeModelFactory.get()); + } else if (i->_modelConfig._type == "map") { + + SGSharedPtr attributeModelFactory; + attributeModelFactory = new AttributeMapModelFactory; + + XMLConfigReader::DataElementList::const_iterator j; + for (j = i->_modelConfig._dataElementList.begin(); + j != i->_modelConfig._dataElementList.end(); ++j) { + + if (j->_type == "external") + attributeModelFactory->setModelIndexPathPair(objectClass->getIndexPathPair(j->_name)); + + else { + SG_LOG(SG_IO, SG_ALERT, "HLA: Unknown model configuration type \"" + << j->_type << "\" for object class \"" << aircraftClassName << "\". Ignoring!"); + } + } + + std::list >::const_iterator k; + for (k = i->_modelConfig._modelMap.begin(); + k != i->_modelConfig._modelMap.end(); ++k) { + + if (k->second.empty()) + attributeModelFactory->setExternalDefault(k->first); + else + attributeModelFactory->addExternalModelPathPair(k->first, k->second); + } + + mpClassCallback->setModelFactory(attributeModelFactory.get()); + + } else { + SG_LOG(SG_IO, SG_ALERT, "HLA: Unknown model configuration type \"" + << i->_modelConfig._type << "\" for object class \"" << aircraftClassName << "\". Ignoring!"); + } + + if (i->_simTimeConfig._type == "attribute") { + SGSharedPtr attributeSimTimeFactory; + attributeSimTimeFactory = new AttributeSimTimeFactory; + + XMLConfigReader::DataElementList::const_iterator j; + for (j = i->_simTimeConfig._dataElementList.begin(); + j != i->_simTimeConfig._dataElementList.end(); ++j) { + + if (j->_type == "local-simtime") + attributeSimTimeFactory->setSimTimeIndexPathPair(objectClass->getIndexPathPair(j->_name)); + else { + SG_LOG(SG_IO, SG_ALERT, "HLA: Unknown simtime configuration type \"" + << j->_type << "\" for object class \"" << aircraftClassName << "\". Ignoring!"); + } + } + + mpClassCallback->setSimTimeFactory(attributeSimTimeFactory.get()); + } else if (i->_simTimeConfig._type == "attribute-sec-msec") { + SGSharedPtr attributeSimTimeFactory; + attributeSimTimeFactory = new MSecAttributeSimTimeFactory; + + XMLConfigReader::DataElementList::const_iterator j; + for (j = i->_simTimeConfig._dataElementList.begin(); + j != i->_simTimeConfig._dataElementList.end(); ++j) { + + if (j->_type == "local-simtime-sec") + attributeSimTimeFactory->setSecIndexPathPair(objectClass->getIndexPathPair(j->_name)); + else if (j->_type == "local-simtime-msec") + attributeSimTimeFactory->setMSecIndexPathPair(objectClass->getIndexPathPair(j->_name)); + else { + SG_LOG(SG_IO, SG_ALERT, "HLA: Unknown simtime configuration type \"" + << j->_type << "\" for object class \"" << aircraftClassName << "\". Ignoring!"); + } + } + + mpClassCallback->setSimTimeFactory(attributeSimTimeFactory.get()); + } else { + SG_LOG(SG_IO, SG_ALERT, "HLA: Unknown simtime configuration type \"" + << i->_simTimeConfig._type << "\" for object class \"" << aircraftClassName << "\". Ignoring!"); + } + + if (!i->_mpPropertiesConfig._name.empty()) { + mpClassCallback->setMPPropertiesIndexPathPair(objectClass->getIndexPathPair(i->_mpPropertiesConfig._name)); + } + + // The free configurabel property - dataElement mapping + XMLConfigReader::DataElementList::const_iterator j; + for (j = i->_dataElementList.begin(); + j != i->_dataElementList.end(); ++j) { + + if (j->_type == "property") { + if (!j->_inProperty.empty()) + mpClassCallback->setInputProperty(objectClass->getIndexPathPair(j->_name), j->_inProperty); + if (!j->_outProperty.empty()) + mpClassCallback->setOutputProperty(objectClass->getIndexPathPair(j->_name), j->_outProperty); + } else { + SG_LOG(SG_IO, SG_ALERT, "HLA: Unknown dataElement configuration type \"" + << j->_type << "\" for object class \"" << aircraftClassName << "\". Ignoring!"); + } + } + + objectClass->setInstanceCallback(mpClassCallback); + + if (i->_type == "Multiplayer") + _localAircraftClass = objectClass; + } + + set_enabled(true); + return is_enabled(); +} + +bool +FGHLA::process() +{ + // First push our own data so that others can recieve ... + if (get_direction() & SG_IO_OUT) { + if (fgGetBool("/sim/fdm-initialized", false) && _localAircraftClass.valid()) { + if (!_localAircraftInstance.valid()) { + _localAircraftInstance = new sg::HLAObjectInstance(_localAircraftClass.get()); + _localAircraftInstance->registerInstance(); + } + _localAircraftInstance->updateAttributeValues(sg::RTIData("tag")); + } + } + + // Then get news from others ... + if (get_direction() & SG_IO_IN) { + + // I hoped that the tick call itself would do that job with the timestamps, but this way it works + SGTimeStamp timestamp = SGTimeStamp::now(); + timestamp += SGTimeStamp::fromSec(0.01); + do { + if (!_hlaFederate->tick(0.0, 0.0)) + break; + } while (SGTimeStamp::now() <= timestamp); + } + + return true; +} + +bool +FGHLA::close() +{ + if (get_direction() & SG_IO_OUT) { + // Remove the local object from the rti + _localAircraftInstance->deleteInstance(simgear::RTIData("gone")); + _localAircraftInstance = 0; + } + + // Leave the federation + _hlaFederate->resign(); + + // Try to destroy the federation execution. Only works if no federate is joined + _hlaFederate->destroyFederationExecution(_federation); + + // throw away the HLAFederate + _hlaFederate = 0; + + set_enabled(false); + + return true; +} + diff --git a/src/Network/HLA/hla.hxx b/src/Network/HLA/hla.hxx new file mode 100644 index 000000000..b1f6d4269 --- /dev/null +++ b/src/Network/HLA/hla.hxx @@ -0,0 +1,62 @@ +// +// Copyright (C) 2009 - 2010 Mathias Fröhlich +// +// 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +// + +#ifndef _FG_HLA_HXX +#define _FG_HLA_HXX + +#include +#include +#include + +#include +#include +#include + +namespace simgear { +class HLAFederate; +class HLAObjectClass; +class HLAObjectInstance; +} + +class FGHLA : public FGProtocol { +public: + FGHLA(const std::vector& tokens); + virtual ~FGHLA(); + + virtual bool open(); + virtual bool process(); + virtual bool close(); + +private: + /// All the utility classes we need currently + class XMLConfigReader; + + /// The configuration parameters extracted from the tokens in the constructor + std::string _objectModelConfig; + std::string _federation; + std::string _federate; + + /// The toplevel rti class + SGSharedPtr _hlaFederate; + /// This class that is used to send register the local instance + SGSharedPtr _localAircraftClass; + /// The local aircraft instance + SGSharedPtr _localAircraftInstance; +}; + +#endif // _FG_HLA_HXX diff --git a/src/Network/Makefile.am b/src/Network/Makefile.am index 8356ca98a..1f9b207c3 100644 --- a/src/Network/Makefile.am +++ b/src/Network/Makefile.am @@ -40,3 +40,7 @@ libNetwork_a_SOURCES = \ generic.hxx generic.cxx INCLUDES = -I$(top_srcdir) -I$(top_srcdir)/src + +if WITH_HLA +SUBDIRS = HLA +endif \ No newline at end of file