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

Table of Contents

Extend the RPR FOM

The Add Attribute example demonstrates how to extend the RPR FOM by adding new attributes or parameters to existing classes, particularly when this requires extending VR-Link's API to work with them. It creates an executable that works with the example-VrlExtend.fed FED file that is in the VR-Link root directory.

The code in the addAttr example explains the steps you need to take when you add a new attribute or parameter to existing classes in your FOM. In this example, we assume that the new attribute and parameter represent concepts not present in the VR-Link top-level API. Therefore, we must first extend the API to provide accessors for the new concepts, then configure the FOM Mapper so that it can map between the new FOM elements and the API extensions.

Extended FOM Elements

The following FOM elements that we have added for this example are in the file VrlExtend.fed:

Implemention and Extending Classes

To work with these additions do the following:

  1. Create a subclass of DtEntityStateRepository (myEsr.h.) with accessors for the new Mass attribute.
  2. Create a subclass of DtFireInteraction (myFireInter.h.) with accessors for the new Temperature parameter.
  3. Add mappings for the new attribute and parameter to the FOM Mapper, and tell the FOM Mapper to instantiate MyFireInteraction when an interaction of class WeaponFire is received. (For more information, please see configFomMap.h and configFomMap.cxx.)
  4. Tell DtReflectedEntity and DtEntityPublisher to use MyEntityStateRepository instead of the standard DtEntityStateRepository. (For more information, please see addAttr.cxx.)

Extending the Entity State Repository

We extend the DtEntityStateRepository in MyEntityStateRep and declare and define an entity state repository that adds the attribute "Mass" to the existing DtEntityStateRepository.

Applications typically get state information about entities from a DtEntityStateRepository. This class has mutators and inspectors that provide access to many components of an entity's state, but the concept of mass is not one of these components. So in the file myEsr.h, we extend the DtEntityStateRepository API by deriving a class called MyEntityStateRep.

We added an inspector and mutator function for the concept of mass, and overrode the virtual printData() function, so that mass will be printed along with the other state data. Finally, we provided a static create() function that you can register with other VR-Link classes, so that they can create an instance of our new state repository. These functions have straightforward implementations.

Extending the Fire Interaction Class

In myFireInter.h, we extend the API provided by the DtFireInteraction class, to add accessor functions for the concept of temperature. Again, we override printData(), and provide a static create() function.

Configuring the FOM Mapper

We need to provide code that can map between the FOM attribute and parameter, and our API extensions. This code is in configFomMap.cxx. The function configFomMapper() takes a pointer to a FOM Mapper as an argument and configures that FOM Mapper so that it contains our new mappings. The assumption is that the FOM Mapper that will be passed to us already contains the standard RPR FOM mappings, so that we only need to add our new mappings. We do not have to worry about adding mappings for the rest of the FOM.

In configFomMapper(), listed below, we first register our MyFireInteraction class's create function with the FOM Mapper's interaction factory, so that it knows that this is the DtInteraction subclass that it should instantiate to represent incoming interactions of the FOM class WeaponFire. We do not need to do anything similar for objects here, but we will see later that we need to tell our DtEntityPublisher and DtReflectedEntity classes that they should be using our MyEntityStateRepository class to store the state of the objects that they represent.

The rest of configFomMapper() shows the registration of encoding, decoding, and checking functions for our new attribute and parameter. Decoding functions take data from FOM representation, and pass it to interaction or state repository mutator functions (after performing any necessary format conversions.) Encoding functions obtain data using interaction or state repository inspector functions, and add them to outgoing state updates in FOM representation (again after performing any necessary conversions.) Checking functions check whether an attribute's update condition has been met, usually by comparing the data in a current state repository, and a state repository that represents the state that would be seen by remote federates based on updates that we have sent.

The functions use VR-Link's "Net" types, so byte swapping occurs automatically during conversions to and from native types.

In addAttr.cxx, we construct a DtExerciseConn, telling it to use our modified FED file, VrlExtend.fed. By not passing a DtFomMapper argument, we are indicating that the default RPR FOM FOM Mapper should be used:

DtExerciseConn conn("VrlExtend", "addAttr");

After the DtExerciseConn is created, we pass its FOM Mapper to the configFomMapper() function that we wrote in configFomMap.cxx. This adds our new mappings:

configFomMapper(conn.fomMapper());

Using the New Classes

The application uses these classes and functions to send and receive the entities and fire interactions with the new attributes (mass for entities and temperature for fire). Running two instances of addAttr demonstrates that the new attributes are sent and received.

Plugging into VR-Link

To get our DtEntityStateRepository extensions plugged into VR-Link, we tell the DtReflectedEntity and DtEntityPublisher classes that they should use instances of MyEntityStateRepository, rather than the default DtEntityStateRepository, to store their objects' state. We do this by using those classes' static member function setStateRepCreator().

Later, when we create a DtEntityPublisher to manage sending updates for a locally simulated entity, we can cast the state repository returned by its esr() function to a MyEntityStateRepository, and use its setMass() function to set the entity's mass.

Recieving Reflected Data

On the receiving side, we can cast a DtReflectedEntity's state repository to a MyEntityStateRepository as well, and inspect the entity's mass using its mass() member. In the example, we just call its printData() virtual function, for which the cast is not really necessary.

To send a fire interaction that includes our temperature parameter, create an instance of MyFireInteraction, set the temperature, and send it.

To receive it, register a callback function with DtFireInteraction.

Within the callback, we can cast the DtFireInteraction pointer to a pointer to MyFireInteraction, and inspect the temperature using its temperature() member, or just use its virtual printData() function, which should print the temperature along with the other parameters.

In this example, we did not provide static addCallback() and removeCallback() functions in the MyFireInteraction class, as most VR-Link interaction classes do. Had we done so, they would have allowed callback functions that take a MyFireInteraction pointer rather than a DtFireInteraction pointer. If we were able to use such specific callbacks, we could avoid a cast to MyFireInteraction within the callback, which is otherwise necessary to inspect subclass-specific data.

How to Run the Example

After you build the addAttr example, run two copies of it. You should see them communicating with each other. Each should print state and interaction information received from the other, including values for the new attribute and parameter.

Add Attribute Example Code

The addAttr example is contained in the following files.

MyEsr

This file contains the class declaration and definition of an entity state repository that adds the attribute "Mass" to the existing DtEntityStateRepository.

MyEsr Code

/*********************************************************************
** Copyright (c) 1992-2010 MAK Technologies, Inc
** All rights reserved.
*********************************************************************/
#pragma once
#if DtHLA
#include <stdio.h>
{
public:
virtual void setMass(float mass);
virtual float mass() const;
virtual void printData() const;
public:
virtual DtStateRepository* clone(bool copy = false) const;
protected:
float myMass;
};
inline void MyEntityStateRep::setMass(float temp)
{
myMass = temp;
}
inline float MyEntityStateRep::mass() const
{
return myMass;
}
inline void MyEntityStateRep::printData() const
{
printf("Mass: %lf\n", mass());
}
{
return new MyEntityStateRep();
}
{
if (copy)
{
return new MyEntityStateRep(*this);
}
else
{
return new MyEntityStateRep();
}
}
#endif

MyFireInteraction

MyFireInteraction Code

/*********************************************************************
** Copyright (c) 1992-2010 MAK Technologies, Inc
** All rights reserved.
*********************************************************************/
#pragma once
#if DtHLA
#include <stdio.h>
{
public:
virtual void setTemperature(float temp);
virtual float temperature() const;
virtual void printData() const;
public:
static DtInteraction* create();
protected:
};
// Inline functions
inline void MyFireInteraction::setTemperature(float temp)
{
myTemperature = temp;
}
inline float MyFireInteraction::temperature() const
{
return myTemperature;
}
inline void MyFireInteraction::printData() const
{
printf("Temperature: %lf\n", temperature());
}
{
return new MyFireInteraction();
}
#endif

ConfigFomMap Class

This file contains the encoder, decoder, and checker functions to properly send the "Mass" and "Temperature" attributes across the network. It also contains a function that configures the FOM Mapper to use these functions.

ConfigFomMap Header

/*********************************************************************
** Copyright (c) 1992-2010 MAK Technologies, Inc
** All rights reserved.
*********************************************************************/
#pragma once
#if DtHLA
#include <vl/fomMapper.h>
#include "myEsr.h"
#include "myFireInter.h"
// This function registers new encoding, decoding, and checking functions with
// a FOM Mapper for the new attribute of the "BaseEntity" object class called
// "Mass". It also registers encoding and decoding functions for a new
// parameter of the "WeaponFire" interaction class called "Temperature".
// The encoding, decoding and checking functions that configFomMapper
// registers:
// Encoding function to be used for the "Temperature" parameter.
RTI::ParameterHandleValuePairSet* pvList,
RTI::ParameterHandle handle);
// Decoding function to be used for the "Temperature" parameter.
const RTI::ParameterHandleValuePairSet& pvlist,
int pairSetIndex);
// Encoding function to be used for the "Mass" attribute.
void encodeMass(const MyEntityStateRep& stateRep,
RTI::AttributeHandleValuePairSet* avList,
RTI::AttributeHandle handle);
// Decoding function to be used for the "Mass" attribute.
const RTI::AttributeHandleValuePairSet& avlist,
int pairSetIndex);
// Checking function to be used for the "Mass" attribute (checks whether
// update condition has been met).
bool needMass(
const MyEntityStateRep& stateRep,
const MyEntityStateRep& asSeenByRemote);
#endif

ConfigFomMap Source

/*********************************************************************
** Copyright (c) 1992-2010 MAK Technologies, Inc
** All rights reserved.
*********************************************************************/
#if DtHLA
#include "configFomMap.h"
{
// Register MyFireInteraction's creator function with the FOM Mapper's
// interaction factory, so that VR-Link creates an instance of
// MyFireInteraction instead of DtFireInteraction to represent an incoming
// WeaponFire interaction.
"WeaponFire", MyFireInteraction::create);
// Add your encoding function for the "Temperature" parameter to the
// prototype encoders for WeaponFire and any subclasses if they existed.
"WeaponFire", "Temperature",
// Add your decoding function for the "Temperature" parameter to the
// prototype decoders for WeaponFire and any subclasses if they existed.
"WeaponFire", "Temperature",
// Add your encoding function for the "Mass" attribute to the prototype
// encoders for BaseEntity and all of its subclasses.
"BaseEntity", "Mass",
// Add your decoding function for the "Mass" attribute to the prototype
// decoders for BaseEntity and all of its subclasses.
"BaseEntity", "Mass",
// Add your checking function for the "Mass" attribute to the prototype
// encoders for BaseEntity and all of its subclasses.
"BaseEntity", "Mass",
}
// Define the new encoding, decoding and checking functions that we register
// with the FOM Mapper. By using VR-Link's "Net" types in out functions, byte
// swapping occurs automatically when necessary.
// Encoding function to be used for the "Mass" attribute.
void encodeMass(const MyEntityStateRep& stateRep,
RTI::AttributeHandleValuePairSet* avList,
RTI::AttributeHandle handle)
{
// Get the value using stateRep's mass(), and add it to the avList.
double mass = stateRep.mass();
DtNetFloat64 netVal(mass);
avList->add(handle, (char*) &netVal, sizeof(DtNetFloat64));
}
// Decoding function to be used for the "Mass" attribute.
const RTI::AttributeHandleValuePairSet& avlist,
int pairSetIndex)
{
// Get the value from the avList, and pass it to stateRep's setMass().
DtNetFloat64 netVal;
RTI::ULong length = 0;
avlist.getValue(pairSetIndex, (char* )&netVal, length);
stateRep->setMass((double) netVal);
}
// Checking function to be used for the "Mass" attribute (checks whether
// update condition has been met).
bool needMass(
const MyEntityStateRep& stateRep,
const MyEntityStateRep& asSeenByRemote)
{
// Just compare the masses in the two state repositories.
return (bool) (stateRep.mass() != asSeenByRemote.mass());
}
// Encoding function to be used for the "Temperature" parameter.
RTI::ParameterHandleValuePairSet* pvList,
RTI::ParameterHandle handle)
{
// Get the value using the interaction's temperature() and add it to the
// avList.
double temp = fire.temperature();
DtNetFloat64 netVal(temp);
pvList->add(handle, (char*) &netVal, sizeof(netVal));
}
// Decoding function to be used for the "Temperature" parameter.
const RTI::ParameterHandleValuePairSet& pvlist,
int pairSetIndex)
{
// Get the value from the pvlist, and pass it to the interaction's
// setTemperature().
DtNetFloat64 netVal;
RTI::ULong length = 0;
pvlist.getValue(pairSetIndex, (char* )&netVal, length);
fire->setTemperature((float) netVal);
}
#endif

Main Application

/****************************************************************************
* Copyright (c) 2014 MAK Technologies, Inc
* All rights reserved.
****************************************************************************/
#include "myFireInter.h"
#include "myEsr.h"
#include "configFomMap.h"
#include <iostream>
// There are several places where you can configure a FOM Mapper to add an
// attribute. In this example, we create the DtExerciseConn with the default
// FOM Mapper, and then add attribute decoders, encoders and checkers after
// the DtExerciseConn constructor returns (using configFomMapper).
// Alternatively, we could have 1) created a subclass of DtRprFomMapper whose
// constructor registers the functions to deal with the new attribute, then 2)
// passed an instance of the new FOM Mapper subclass to the DtExerciseConn
// constructor.
// Callback function to be called when a Fire Interaction is received.
void fireCb(DtFireInteraction* inter, void*)
{
std::cout << "Received Fire Interaction!\n";
inter->printData();
std::cout << std::endl;
}
int main( int argc, char* argv[] )
{
// Used for error handling
DtINIT_MINIDUMPER( "addAttr" );
// Create a DtExerciseConn with FOM Mapper.
#ifdef DtHLA13
DtExerciseConn conn("example-vrlExtend", "addAttr", new DtRprFomMapper(0.8));
#else
//The FOM for 1516 is a little newer, so we use a different FOM Mapper
DtExerciseConn conn("example-vrlExtend", "addAttr", new DtRprFomMapper(1.0));
#endif
// Call configFomMapper to register functions to handle the new attribute
// and parameter.
configFomMapper(conn.fomMapper());
// Register the callback on incoming Fire Interactions.
DtFireInteraction::addCallback(&conn, fireCb, NULL);
// Tell reflected entity and entity publisher that they should create
// instances of MyEntityStateRep rather than the base
// DtEntityStateRepository to store state of objects.
// Create a reflected entity list
// Create an entity publisher
DtEntityPublisher pub(DtEntityType(1, 1, 225, 1, 1, 0, 0), &conn);
// We've told the publisher to use a MyEntityStateRep as its state
// repository, so this cast should be safe.
MyEntityStateRep* esr = (MyEntityStateRep*) pub.esr();
esr->setMass(215.0);
// Set essential attributes
// Middle of island in MAKland terrain
esr->setLocation(DtVector(3114872.406839,5449826.517832,1126516.629432));
esr->setOrientation(DtTaitBryan( 0.604654, 1.373067, 2.722311));
esr->setMarkingText(pub.objectId().string());
DtClock* clock = conn.clock();
while (1)
{
clock->setSimTime(clock->absRealTime());
if (input.keybrdTick() == -1) break;
conn.drainInput();
// Send a TestInteraction.
inter.setTemperature(150.0);
//conn.sendStamped(inter);
// Tick the publisher
pub.tick();
// Print the mass of the first entity in the list.
DtReflectedEntity* ent = rel.first();
if (ent)
{
// We've told DtReflectedEntity to use a MyEntityStateRep as its
// state repository, so this cast should be safe.
std::cout << "Current state of first entity.\n";
esr->printData();
std::cout << std::endl;
}
DtSleep(1.0);
}
return 0;
}

Document ID: Generated on Thu Sep 19 02:12:35 EDT 2024 from SVN revision 269601
Copyright © 2005-2024 MAK Technologies. All Rights Reserved (www.mak.com)