VR-Link API Documentation for DIS
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
Talk-Listen Example

Table of Contents

The Talk Application

Talk is a minimal VR-Link application. The program simulates the flight of an F18 aircraft. The program begins by sending a fire PDU or HLA fire interaction. Then, the F18 flies north for 10 seconds, updating its position by sending DIS entity state PDUs or HLA attribute updates.

This example is an extremely simplified version of the F18 example. It publishes a stripped down aircraft. The published data can be viewed by The Listen Application.

This example builds and runs in DIS, HLA1.3, HLA1516 and HLA1516e, because we use the VR-Link Protocol Independent API.

Connecting to An Exercise

Like The Listen Application example, this program creates a DtExerciseConn to provide an interface to the RTI or DIS network.

Managing Entities

We define the entity type the F18 will use.

To be visible to other applications in the exercise, each locally-simulated entity requires a DtEntityPublisher. The entity publisher manages the generation of messages for this particular entity. It provides an entity state repository where you can set state values, and a tick() function, which causes state information to be sent to the network if necessary.

It sets up a pointer to the entity state repository, then creates a topographic view on that repository. This lets us set the entity's positional data using topographic coordinates, rather than the default geocentric coordinates. (The coordinates are hard-coded for this example.) We show an example of storing a non-positional state value in the repository.

Sending Interactions

The example sends an interaction. You can send the interaction using the exercise connection's sendStamped() function.

You can use sendStamped() to send state updates, but it is preferable to let the entity publisher send state updates for you, using data in the entity state repository.

Sending State Messages

The main loop executes twenty times per second for ten seconds. As in the listen-only example, this program sets VR-Link simulation time at the start of each iteration.

While this program's main purpose is to demonstrate the sending of data to the network, it is not a true send-only application. Incoming data is also processed with the drainInput() call. This call is required for HLA, because this is where we tick the RTI.

The program updates the F18's positional data in its entity state repositoryentity state repository and ticks the entity publisher to send the updated data onto the network. It sets topographic coordinates through a view, which VR-Link converts to geocentric coordinates.

Then, the only remaining tasks are to increment the F18's position, increment the simulation time, and sleep until it is time to begin the next iteration.

The Listen Application

Listen is a simple listen-only VR-Link application. This application can be compiled for either DIS or HLA. The only protocol-specific code is contained in the #if statement (#if DtDIS).

With each iteration of the loop, the program prints an entity's updated, dead-reckoned position in topographic coordinates. If a fire PDU or interaction is detected on the network, the program prints a message showing the entity ID of the attacker.

To exit, press the 'q' key. This example works with the Talk example.

Connecting to An Exercise

The program creates an exercise connection (DtExerciseConn.) This connection serves as the program's interface to an exercise. DtExerciseConn has several constructors. In this example, we create a DtVrlApplicationInitializer and pass it to the DtExerciseConn. Use of a DtVrlApplicationInitializer provides support for command line arguments. The default protocol-specific initialization values are set in the DtVrlApplicationInitializer and its base class, DtExerciseConnInitializer. This is the only protocol-specific part of the code.

Managing State and Interaction Information

Applications based on VR-Link typically use callbacks to handle incoming interactions such as fire, detonations, and collisions. For example, a callback named fireCb is registered with the DtFireInteraction class. This callback prints a message containing the attacker ID. It executes whenever the exercise connection receives a Fire PDU or interaction during a call to drainInput().

Tracking Entities

We create a reflected entity list to keep track of entities found on the network. The entity list tracks the arrival and departure of entities, performs dead reckoning, manages time outs, and performs other entity-tracking tasks.

Managing Time

We obtain a pointer to the simulation clock from the exercise connection.

Listening to the Network

At the start of each iteration, the program sets VR-Link simulation time to provide a common time value for use by time-related operations that occur within an iteration of the loop (such as the dead-reckoning of multiple entities.) The drainInput() call reads and processes any messages arriving through the exercise connection. This call triggers the execution, if needed, of any callbacks you have registered for that exercise connection.

The program finds the first entity in the entity list, then it retrieves the pointer to the entity's entity state repository. It creates a topographic view of that entity state repository, allowing us to retrieve its position data in topographic coordinates rather than geocentric. (The coordinates for this example are hard-coded.) The application obtains and prints the dead-reckoned entity location.

Example Code

Talk Example Code

/****************************************************************************
* Copyright (c) 2023 MAK Technologies, Inc
* All rights reserved.
****************************************************************************/
#include <vl/topoView.h>
#include <vlutil/vlUuid.h>
#include <iostream>
#include <vector>
void DtRtiShutdownHandler(const char * label, void* finished)
{
//This callback may occur multiple times, since it gets called
//every time we make an RTI call with an error. So, we mark this
//as finished and make sure that we only signal once.
int* finishedInt = (int*)finished;
if (*finishedInt)
{
DtWarn << "Shutting down: " << label << std::endl;
}
*finishedInt = 0; //Order a shutdown;
}
int main( int argc, char* argv[] )
{
// Used for error handling
DtINIT_MINIDUMPER( "Talk" );
try
{
#if (_MSC_VER && _MSC_VER >= 1900) || (__cplusplus >= 201103L)
#define hla_ptr std::unique_ptr
#else
#define hla_ptr std::auto_ptr
#endif
hla_ptr<DtString> m_ptr;
// Create a connection to the exercise or federation execution.
DtVrlApplicationInitializer appInit( argc, argv, "VR-Link Talk" );
#if DtDIS
// Change some defaults. Please note that this is DIS specific code.
// Notice how this is the only line of DIS specific code in this example,
// everything else is using the protocol independent API.
appInit.setUseAsynchIO( true );
appInit.setDisVersionToSend( 7 );
#else
appInit.setFedFileName( DtDefaultRpr2Fom );
appInit.setExecName( "MAK-One-2024" );
appInit.setRprFomVersion( 2.0 );
appInit.setRprFomRevision( 2 );
#ifdef DtHLA_1516_EVOLVED
std::vector<DtString> fomModules;
std::vector<DtString> fomModuleFromCommandLine;
appInit.fomModules(fomModuleFromCommandLine);
fomModules.push_back("NETN-BASE.xml");
fomModules.push_back("NETN-Physical.xml");
fomModules.push_back("NETN-METOC.xml");
fomModules.push_back("NETN-MRM.xml");
fomModules.push_back("MAK-Physical-2_evolved.xml");
fomModules.push_back("MAK-Aerodrome-1_evolved.xml");
fomModules.push_back("MAK-METOC-1_evolved.xml");
fomModules.push_back("MAK-VRFExt-9_evolved.xml");
fomModules.push_back("MAK-DIGuy-7_evolved.xml");
fomModules.push_back("MAK-LgrControl-2_evolved.xml");
fomModules.push_back("MAK-VRFAggregate-6_evolved.xml");
fomModules.push_back("MAK-DynamicTerrain-2_evolved.xml");
fomModules.push_back("MAK-VRLExt-3_evolved.xml");
fomModules.push_back("MAK-DER-1_evolved.xml");
fomModules.push_back("RPR-Enumerations_Experimental_IFF.xml");
fomModules.push_back("RPR-MAK_Experimental_IFF-3.xml");
fomModules.insert(fomModules.end(), fomModuleFromCommandLine.begin(), fomModuleFromCommandLine.end());
appInit.setFomModules(fomModules);
#endif
#endif
// Process the command line. This will actually set values in the
// DtVrlApplicationInitializer class and should be called before
// creating the DtExerciseConn instance.
appInit.parseCmdLine();
// The DtExerciseConn instance is perhaps the most important class
// in any VR-Link exercise. Think of it as the hub which holds
// everything else together. Technically this instance is what's
// responsible for connecting this federate to another federate via
// a network, or an RTI. After exConn is created, and if no error
// has occurred this simulator will be a live federate.
DtExerciseConn exConn(appInit, &status);
if (status != 0)
{
std::cout << "Error creating exercise connection." << std::endl;
return -1;
}
// Create an entity publisher for the entity we are simulating.
DtEntityPublisher entityPub(f18Type, &exConn,
// Hold on to the entity's state repository, where we can set data.
DtEntityStateRepository* esr = entityPub.entityStateRep();
esr->setEntityId(*id);
// Create a topographic view on the state repository, so we
// can set position information in topographic coordinates.
double refLatitude = DtDeg2Rad( 35.699760 );
double refLongitude = DtDeg2Rad( -121.326577 );
DtTopoView topoView(esr, refLatitude, refLongitude);
esr->setMarkingText("VR-Link");
//example NETN attributes
//esr->setNumGridAxes(2);
topoView.setOrientation(DtTaitBryan(0.0, 0.0, 0.0));
// Initialize VR-Link time.
DtClock* clock = exConn.clock();
DtVector position(0, 0, -100);
DtVector32 velocity(20, 0, 0);
//Shutdown handler in order to smoothly end if the HLA exercise shuts down
int forever = 1;
#ifdef DtHLA
exConn. addRtiErrorCb(&DtRtiShutdownHandler, &forever);
#endif
// Send a Fire Interaction.
fire.setAttackerId(entityPub.globalId());
exConn.sendStamped(fire);
#ifdef DtHLA
//Spectrum transmission
DtRadioTransmitterPublisher rtPubliser(rtRepo, &exConn, DtString(esr->hostGlobalId() + "-Radio23"), 0);
rtRepo->setEntityId(esr->entityId());
rtRepo->setRadioId(23);
rtRepo->setHostId(esr->hostGlobalId());
//rtRepo->setGlobalId(esr->hostGlobalId() + "-Radio23"); //DtGlobalObjectDesignator("RadioId"));
rtRepo->setFHNetId(2000);
rtRepo->setHopSetId(300);
rtRepo->setLockoutSetId(40);
DtInfo << "TSR Entity Id " << rtRepo->entityId() << std::endl;
#endif // DtHLA
// Main loop
DtTime dt = 0.05;
DtTime simTime = 0;
int lockout = 40;
clock->init();
while (forever && simTime <= 10.0)
{
// Tell VR-Link the current value of simulation time.
clock->setSimTime(simTime);
// Process any incoming messages.
exConn.drainInput();
// Set the current position information.
topoView.setLocation(position);
topoView.setVelocity(velocity);
// Call tick, which insures that any data that needs to be
// updated is sent.
entityPub.tick();
#ifdef DtHLA
rtPubliser.tick();
rtRepo->setLockoutSetId(lockout++);
#endif // DtHLA
// Set up for next iteration.
position[0] += velocity[0] * dt;
simTime += dt;
// Wait till real time equals simulation time of next step.
DtSleep(simTime - clock->elapsedRealTime());
}
}
DtCATCH_AND_WARN(std::cout);
return 0;
}

Listen Example Code

/****************************************************************************
* Copyright (c) 2017 MAK Technologies, Inc
* All rights reserved.
****************************************************************************/
#include <vl/topoView.h>
#include <vl/fomMapper.h>
#include <iostream>
int theReceivedUpdateCount = 0;
void DtRtiShutdownHandler(const char * label, void* finished)
{
//This callback may occur multiple times, since it gets called
//every time we make an RTI call with an error. So, we mark this
//as finished and make sure that we only signal once.
int* finishedInt = (int*)finished;
if (*finishedInt)
{
DtWarn << "Shutting down: " << label << std::endl;
}
*finishedInt = 0; //Order a shutdown;
}
// A callback for when a DtReflectedEnvironmentProcess object is updated.
void printEnvProc(DtReflectedEnvironmentProcess* obj, void* userData)
{
obj->epsr()->printData();
theReceivedUpdateCount++;
}
// A callback for when a new DtReflectedEnvironmentProcess object is added.
void envProcAddedCb(DtReflectedEnvironmentProcess* obj, void* userData)
{
// register a callback for when an existing object is updated.
obj->addPostUpdateCallback(printEnvProc, userData);
}
// A callback for when a DtReflectedEntity object is updated.
void entityUpdateCount(DtReflectedEntity* obj, void* userData)
{
theReceivedUpdateCount++;
}
// A callback for when a new DtReflectedEntity object is added.
void entityAddedCb(DtReflectedEntity* obj, void* userData)
{
// register a callback for when an existing object is updated.
obj->addPostUpdateCallback(entityUpdateCount, userData);
}
//Criteria function checks object's entity type before allowing discovery
//Will do the same as the discoverOnlyWhenEntityTypeKnown, but modifiable
bool criteria(DtReflectedObject* obj, void* usr)
{
//Use this line to make custom criteria for delayed object discovery.
if (ent->esr()->entityType() == DtEntityType(0, 0, 0, 0, 0, 0, 0))
{
return false;
}
return true;
}
// Define a callback to process fire interactions.
void fireCb(DtFireInteraction* fire, void* /*usr*/)
{
std::cout << "Fire Interaction from " << fire->attackerId().string() << std::endl;
}
void sigCb(DtSignalInteraction* sig, void* /*usr*/)
{
}
int main(int argc, char** argv)
{
DtINIT_MINIDUMPER( "Listen" );
try
{
// Create an exercise conn initializer. This will parse the command
// line, as well as parse an mtl file with the same name as this application.
DtVrlApplicationInitializer appInit(argc, argv, "VR-Link Listen");
#if DtDIS
// Please note, this is the only protocol specific code in this example. For
// HLA there is no network connection, so there is no concept of AsyncIO at the
// VR-Link level.
appInit.setUseAsynchIO( true );
appInit.setDisVersionToSend( 7 );
#else
// Set the default FED File, executable, and version
appInit.setFedFileName( DtDefaultRpr2Fom );
appInit.setExecName( "MAK-One-2024" );
appInit.setRprFomVersion( 2.0 );
appInit.setRprFomRevision( 2 );
#ifdef DtHLA_1516_EVOLVED
std::vector<DtString> fomModules;
std::vector<DtString> fomModuleFromCommandLine;
appInit.fomModules(fomModuleFromCommandLine);
fomModules.push_back("NETN-BASE.xml");
fomModules.push_back("NETN-Physical.xml");
fomModules.push_back("NETN-METOC.xml");
fomModules.push_back("NETN-MRM.xml");
fomModules.push_back("MAK-Physical-2_evolved.xml");
fomModules.push_back("MAK-Aerodrome-1_evolved.xml");
fomModules.push_back("MAK-METOC-1_evolved.xml");
fomModules.push_back("MAK-VRFExt-9_evolved.xml");
fomModules.push_back("MAK-DIGuy-7_evolved.xml");
fomModules.push_back("MAK-LgrControl-2_evolved.xml");
fomModules.push_back("MAK-VRFAggregate-6_evolved.xml");
fomModules.push_back("MAK-DynamicTerrain-2_evolved.xml");
fomModules.push_back("MAK-VRLExt-3_evolved.xml");
fomModules.push_back("MAK-DER-1_evolved.xml");
fomModules.push_back("RPR-Enumerations_Experimental_IFF.xml");
fomModules.push_back("RPR-MAK_Experimental_IFF-3.xml");
fomModules.insert(fomModules.end(), fomModuleFromCommandLine.begin(), fomModuleFromCommandLine.end());
appInit.setFomModules(fomModules);
#endif
#endif
// Process the command line. This will actually set values in the
// DtVrlApplicationInitializer class and should be called before
// creating the DtExerciseConn instance.
appInit.parseCmdLine();
// The DtExerciseConn instance is perhaps the most important class
// in any VR-Link exercise. Think of it as the hub which holds
// everything else together. Technically this instance is what's
// responsible for connecting this federate to another federate via
// a network, or an RTI. After exConn is created, and if no error
// has occurred this simulator will be a live federate.
DtExerciseConn exConn(appInit, &status);
// Initialize VR-Link time.
DtClock* clock = exConn.clock();
#ifdef DtHLA
// Let other VR-Link federates know that we are initializing
// Helps control request update calls when creating many entities
// Requests are delayed until all federates are initialized or executing
std::cout << "[" << clock->simTime() << ", " << " 0] Initializing" << std::endl;
exConn.setExecutionStatus(vrlstatus_initializing);
exConn.drainInput(0.1);
#endif
if (status != 0)
{
std::cout << "Error creating exercise connection." << std::endl;
return -1;
}
relEnv.addEnvironmentProcessAdditionCallback(envProcAddedCb, NULL);
// Register a callback to handle fire interactions.
DtFireInteraction::addCallback(&exConn, fireCb, NULL);
DtSignalInteraction::addCallback(&exConn, sigCb, NULL);
// Create an object to manage entities that we hear about on the network.
DtReflectedEntityList rel(&exConn);
rel.addEntityAdditionCallback (entityAddedCb, NULL);
//Attaching criteria function to DtReflectedEntityList
//for delayed object discovery.
rel.setDiscoveryCondition(criteria, NULL);
#ifdef DtHLA
// Let other VR-Link federates know that we are executing
std::cout << "[" << clock->simTime() << ", " << " 0] Executing" << std::endl;
exConn.setExecutionStatus(vrlstatus_executing);
exConn.drainInput(0.1);
#endif
int forever = 1;
#ifdef DtHLA
//Shutdown handler in order to smoothly end if the HLA exercise shuts down
exConn.addRtiErrorCb(&DtRtiShutdownHandler, &forever);
#endif
unsigned long loopCount = 0;
DtTime outputTime = 0.0;
double outputInterval = appInit.outputInterval();
double frameTime = 0.1;
clock->init();
std::cout << std::fixed << std::setprecision(2);
while (forever)
{
DtTime startTime = clock->elapsedRealTime();
// Tell VR-Link the current value of simulation time.
clock->setSimTime(startTime);
loopCount++;
// Check if user hit 'q' to quit.
if (input.keybrdTick() == -1)
break;
// Find the first entity in the reflected entity list.
DtReflectedEntity *first = rel.first();
if (first && (outputInterval <= 0.0 || outputTime < clock->absRealTime()))
{
// Grab its state repository, where we can inspect its data.
esr->printData();
#if DtDIS
const char* entityId = first->entityId().string();
#else
const char* entityId = first->globalId().string();
#endif
// Create a topographic view on the state repository, so we
// can look at position information in topographic coordinates.
double refLatitude = DtDeg2Rad( 35.699760);
double refLongitude = DtDeg2Rad(-121.326577);
DtTopoView topoView(esr, refLatitude, refLongitude);
// Print some listen stats and some info from first entity.
// Could use location string for printing but since its topo view, only need precision of 2 that we set above.
std::cout << "[" << clock->simTime() << ", " << loopCount << "] #entities: " << rel.count()
<< " #updates received: " << theReceivedUpdateCount << ", "
<< "First entity: \"" << entityId << "\" " << " position: "
<< "(" << topoView.location().x() << ", " << topoView.location().y() << ", " << topoView.location().z() << ")" << std::endl;
theReceivedUpdateCount = 0;
outputTime = clock->absRealTime() + outputInterval;
}
DtTime duration = clock->elapsedRealTime() - startTime;
#ifdef DtDIS
// Process any incoming messages if available for up to max interval
// Drain input will return early if there is no input.
exConn.drainInput(frameTime - duration);
duration = clock->elapsedRealTime() - startTime;
if (duration < frameTime)
{
// Sleep for the remaining frame time
DtSleep(frameTime - duration);
}
#else
// Process any incoming messages for up to 1st max interval but at least for the 2nd min interval.
// Drain input will sleep and wakeup internally for input while waiting for the minimum interval to expire
// so the max and min intervals govern the frame time minus the time it took to print above.
exConn.drainInput(frameTime - duration, frameTime - duration);
#endif
}
}
DtCATCH_AND_WARN(std::cout);
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)