VR-Link API Documentation for HLA 1.3
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Extended RPR FOM Example

Table of Contents

This example shows how to extend the RPR FOM from an existing RPR FOM Class Object (aircraft).

To extend the RPR FOM, you specify new objects and their attributes in a special FOM or FED file and add classes to your application to implement the new objects and attributes.

This example encompasses 2 separate example applications, an extendedRprFomTalk for publishing and an extendedRprFomListen for subscribing.

Note
Extending a FOM is not the same thing as creating a FOM Mapper. See the discussion at the end of this file.

Extending the RPR FOM

This example extends the Aircraft class in the RPR FOM. It adds a new class called F18, with an attribute called SpecialF18Attribute. This attribute is a variable length byte array that represents a string. The talk and listen applications are modified to implement the new object and attribute.

This example uses a special FOM called “example-extendF18”. There is an HLA1516 Evolved “_evolved.xml” version if the FOM, a 1516 .xml version, as well as a .fed version for HLA13.

The VR-Link.fed file is edited to add the new object as follows:

(class Aircraft (class F18 (attribute SpecialF18Attribute) ) )

To support this new attribute, we do the following things: 1) Extend the Entity State Repository by subclassing it. 2) Create Encoders/Decoders for this attribute by subclassing from existing encoders/decoders. 3) Tell the RPR FOM Mapper to use these attributes.

Note
Since the new class is subclassed from an existing class, we can take advantage of the existing classes for our state repository, publishers, and so on. If we were creating a new class from the base object level we would have to write additional code. See the Test Object Example which adds a new class from the baseObject level.

Why Extending a FOM is Not the Same Thing as Creating a FOM Mapper

Some VR-Link developers think that to add an object to the RPR FOM (extend the FOM) they have to create a FOM Mapper. This is not correct.

The purpose of a FOM Mapper is to map a FOM that VR-Link does not know about to the internal VR-Link object model. VR-Link already knows about the RPR FOM. The RPR FOM is mapped to the VR-Link object model through code internal to the VR-Link DLLs. There is no need to create a new FOM Mapper DLL, which would have to replace all of the internal FOM mapping, just to extend the RPR FOM.

To extend the RPR FOM, all you have to do is add your new class to the FED file and extend the VR-Link applications to work with the new object or attribute, as done in this example.

How to Run the Example

  1. Launch a extendedRprFomListen example from your VR-Link install's bin64 directory using the desired HLA protocol.
  2. Launch a extendedRprFomTalk example from your VR-Link install's bin64 directory using the same HLA protocol.
  3. Note that the talk will create an F18 using the new class, encoder, and decoder.
  4. Note that the listen will report the special F18 Attribute, a string "This is an F18".
  5. Enter 'q' in the Listen console to quit the listen application.

Example Code

Extended RPR FOM Talk Application

/****************************************************************************
* Copyright (c) 2014 MAK Technologies, Inc
* All rights reserved.
****************************************************************************/
#include <vl/topoView.h>
#include "patch.h"
int main( int argc, char* argv[] )
{
// Used for error handling
DtINIT_MINIDUMPER( "ExtendedFOM-Talk" );
try
{
// Create an DtExerciseConn initializer. This will parse the comand
// line, as well as parse an mtl file with the same name as this application.
DtVrlApplicationInitializer appInit(argc, argv, "VR-Link Talk");
#if DtHLA_1516
#if DtHLA_1516_EVOLVED
// HLA 1516 Evolved
appInit.setFederateType("Extended RPR FOM F18 publisher");
std::vector<DtString> fomModules;
fomModules.push_back("example-extendF18_evolved.xml");
appInit.setFomModules(fomModules);
#else
// HLA 1516
appInit.setFedFileName("example-extendF18.xml");
#endif
#else
// HLA 1.3
appInit.setFedFileName("example-extendF18.fed");
#endif
appInit.parseCmdLine();
DtExerciseConn exConn(appInit);
DtInfo << "adding new Encoders/Decoders for DtF18Entity" << std::endl;
DtADD_ENCODER(BaseEntity.PhysicalEntity.Platform.Aircraft.F18, DtF18EntityEncoder);
DtADD_DECODER(BaseEntity.PhysicalEntity.Platform.Aircraft.F18, DtF18EntityDecoder);
// This tells the FOM Mapper which hlaClass to publish for this entity.
// we need to install our own here because the default one doesn't know
// anything about the f18 type
DtInfo << "Registering the EntityClass Chooser" << std::endl;
exConn.fomMapper()->setObjectClassChooser("DtEntityPublisher", DtF18EntityChooser);
// Time for a design decision. We can create a new reflectedList class, and a new
// publisher class for our new entity type. That involves writing 3 or
// so more classes. I chose not to do that. Instead, we will just use a
// new DtF18EntityStateRep to store info for all Entity State objects. The only
// difference is that the SpecialF18Attribute will be ignored for all non F18
// Classes. So, in this next line i tell my EntityPublisher to always create a
// F18EntityStateRep, we don't need to use the F18 attributes if we don't want to
// and nothing else will change.
// Create an F18 Type
// Create an entity publisher for the entity we are simulating.
DtEntityPublisher entityPub(f18Type, &exConn,
// Lets start with a regular EntityState rep to show that nothing
// changes when we install a new state rep creator.
DtEntityStateRepository *esr = entityPub.entityStateRep();
esr->setMarkingText("VR-Link");
esr->setLocation(DtVector(1.0,2.0,3.0));
esr->setVelocity(DtVector32(0.0,0.0,0.0));
// we can also set this for any EntityStateRep, but
// the value will only be transmitted when its an f18
f18Sr->setF18Attribute("This is an F18");
// Initialize VR-Link time.
DtClock* clock = exConn.clock();
// Send a Fire Interaction.
fire.setAttackerId(entityPub.globalId());
exConn.sendStamped(fire);
// Main loop
DtTime dt = 0.05;
DtTime simTime = 0;
while (simTime <= 10.0)
{
// Tell VR-Link the current value of simulation time.
clock->setSimTime(simTime);
// Process any incoming messages.
exConn.drainInput();
// Call tick, which insures that any data that needs to be
// updated is sent.
entityPub.tick();
simTime += dt;
// Wait till real time equals simulation time of next step.
DtSleep(simTime - clock->elapsedRealTime());
}
}
DtCATCH_AND_WARN(std::cout);
return 0;
}

Extended RPR FOM Listen Application

/****************************************************************************
* Copyright (c) 2014 MAK Technologies, Inc
* All rights reserved.
****************************************************************************/
#include <vl/topoView.h>
#include <iostream>
#include "patch.h"
// Define a callback to process fire interactions.
void fireCb(DtFireInteraction* fire, void* /*usr*/)
{
std::cout << "Fire Interaction from "
<< fire->attackerId().string() << std::endl;
}
int main( int argc, char* argv[] )
{
// Used for error handling
DtINIT_MINIDUMPER( "ExtendedFOM-Listen" );
try
{
// Create an exercise conn initializer. This will parse the comand
// line, as well as parse an mtl file with the same name as this application.
DtVrlApplicationInitializer appInit(argc, argv, "VR-Link Listen");
#if DtHLA_1516
#if DtHLA_1516_EVOLVED
// HLA 1516 Evolved
appInit.setFederateType("Extended RPR FOM F18 subscriber");
std::vector<DtString> fomModules;
fomModules.push_back("example-extendF18_evolved.xml");
appInit.setFomModules(fomModules);
#else
// HLA 1516
appInit.setFedFileName("example-extendF18.xml");
#endif
#else
// HLA 1.3
appInit.setFedFileName("example-extendF18.fed");
#endif
appInit.parseCmdLine();
DtExerciseConn exConn(appInit);
DtInfo(" Adding Encoders/Decoders for DtF18Entity\n");
DtADD_ENCODER(BaseEntity.PhysicalEntity.Platform.Aircraft.F18, DtF18EntityEncoder);
DtADD_DECODER(BaseEntity.PhysicalEntity.Platform.Aircraft.F18, DtF18EntityDecoder);
// Just to test the error message
//DtADD_ENCODER(BaseEntity.PhysicalEntity.bogus.Aircraft.F18, DtF18EntityEncoder);
// Read the comments in talk regarding why I install this SR creator
// for all entity state reps.
// Register a callback to handle fire interactions.
DtFireInteraction::addCallback(&exConn, fireCb, NULL);
// Create an object to manage entities that we hear about
// on the network.
DtReflectedEntityList rel(&exConn);
// Initialize VR-Link time.
DtClock* clock = exConn.clock();
int forever = 1;
while (forever)
{
// Check if user hit 'q' to quit.
if (input.keybrdTick() == -1)
break;
// Tell VR-Link the current value of simulation time.
clock->setSimTime(clock->elapsedRealTime());
// Process any incoming messages.
exConn.drainInput();
// Find the first entity in the reflected entity list.
DtReflectedEntity *first = rel.first();
if (first)
{
// This is a safe cast since all Entity State Reps are
// going to be F18 Entity State Reps
// Print the position.
// Since it returns a DtString, we need to force it to const char*
// with a cast.
std::cout << "special F18 Attribute: ("
<< esr->f18Attribute().string() << ")" << std::endl;
}
// Sleep till next iteration.
DtSleep(1.0);
}
}
DtCATCH_AND_WARN(std::cout);
return 0;
}

Extended RPR FOM Class, Encoder, and Decoder

//********************************************************************
// Copyright (c) 2006 MaK Technologies, Inc.
// All rights reserved.
//********************************************************************
#if DtHLA
#include <stdio.h>
#include <vl/fomMapper.h>
#include <vl/fom.h>
{
public:
{ printf("\n===================\n"
"Constructing DtF18EntityStateRep\n"
"=====================\n"); }
virtual ~DtF18EntityStateRep() {}
private:
public:
virtual void setF18Attribute(const DtString & val) { myAttrib = val;}
virtual DtString f18Attribute() const { return myAttrib;}
static DtF18EntityStateRep* create() { return new DtF18EntityStateRep; };
protected:
};
{
public:
DtObjClassDesc* classDesc):
DtPlatformDecoder(exConn, classDesc)
{
addDecoder("SpecialF18Attribute",
}
virtual ~DtF18EntityDecoder(){}
protected:
const RTI::AttributeHandleValuePairSet& attrs,
int pairSetIndex)
{
RTI::ULong length = attrs.getValueLength(pairSetIndex);
// if there is nothing in there, we can exit safely
if (length == 0) return;
char* buffer = DtDecodingBuffer(length+1);
attrs.getValue(pairSetIndex, buffer, length);
buffer[length] = '\0';
stateRep->setF18Attribute(buffer);
}
};
{
public:
DtObjClassDesc* classDesc):
// By calling the base class ctor all the needed encoders and checkers are registered
DtPlatformEncoder(exConn, classDesc)
{
// Register our encoder functions for all the attributes we need.
addEncoder("SpecialF18Attribute",
addChecker("SpecialF18Attribute",
}
virtual ~DtF18EntityEncoder() {}
protected:
static bool needSpecialF18Attribute( const DtF18EntityStateRep& stateRep,
const DtF18EntityStateRep& asSeenByRemote)
{
return(stateRep.f18Attribute() != asSeenByRemote.f18Attribute());
}
static void encodeSpecialF18Attribute( const DtF18EntityStateRep& stateRep,
RTI::AttributeHandleValuePairSet* avList,
RTI::AttributeHandle attrHandle)
{
// This is easy, they are not all this easy. The point of this example
// is not to show how to encode/decode complex data types.
avList->add(attrHandle,
stateRep.f18Attribute().c_str(),
stateRep.f18Attribute().size());
}
};
// Some useful macros for adding the encoders/decoders
#define DtADD_ENCODER(className, derivedEnc) \
{ \
DtObjClassDesc* classDesc = exConn.fom()->objClassByName(#className); \
if (classDesc) \
{ \
exConn.fomMapper()->stateEncoderFactory()->addEncoder( \
classDesc->handle(), new derivedEnc(&exConn, classDesc)); \
} \
else \
{ \
DtWarn("Can't add encoder for object class %s: Not in FOM.\n", #className); \
} \
}
#define DtADD_DECODER(className, derivedDec) \
{ \
DtObjClassDesc* classDesc = exConn.fom()->objClassByName(#className); \
if (classDesc) \
{ \
exConn.fomMapper()->stateDecoderFactory()->addDecoder( \
classDesc->handle(), new derivedDec(&exConn, classDesc)); \
} \
else \
{ \
DtWarn("Can't add decoder for object class %s: Not in FOM.\n", #className); \
} \
}
char* DtF18EntityChooser( const char* vrlinkClassName,
DtExerciseConn* exConn,
void *usr)
{
printf("=========\n");
printf(" In Class Chooser, deciding which entity Type to publish\n");
printf("=========\n");
// For Entities, the usr data is always of DtEntityType. This is just
// the way it is.
DtEntityType& type = *((DtEntityType*) usr);
if (type.subCategory() == DtF18)
{
printf("Choosing F18 Type, we will use the new class!\n");
// Return the FOM OBJECT CLASS name
return "BaseEntity.PhysicalEntity.Platform.Aircraft.F18";
}
// If this is not the right type, let the default chooser decide
exConn, usr));
}
#endif

Document ID: Generated on Wed Mar 27 02:04:30 EDT 2024 from SVN revision 264570
Copyright © 2005-2024 MAK Technologies. All Rights Reserved (www.mak.com)