VR-Vantage 2.5 API Documentation
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
The Distributed Simulation Tutorial - Add an Entity Existence Listener

Introduction to Project 3D

Project 3D in the Distributed Simulation tutorial takes a look at how to add an entity existence listener to the simulation connection using VR-Vantage, and needs to be tested against a running version of VR-Forces in order to see interactions as they come through.

Examining the files that make up the project

For this project, as with Project 3B, we will create a custom connection class and a custom listener class. The connection class will be the same as was created for 3B, but the listener class will be changed to be specific to listening for entity existence data.

Adding an Entity Existence Listener

To add an entity existence listener we will need to add one callback function to the simulation class. This callback will be connected to two signals that will need to be emitted from the entity existence listener on addition and removal of an entity from the scene.

The first thing we need to do is to include a header for the reflected entity list class to the simulation driver header, and to add the declaration for a callback that will be connected to discovery signals owned by the entity existence listener (discussed below). Note that, again, the header being included is generic across protocols (DIS, HLA 1.3, HLA 1516), but because of the fact that the DistributedSimulationDriver3D.cxx file already has a define at the top specifying the use of DtDIS, the correct header files will be included.

We will also need to add an include for DIS to the DistributedSimulationDriver3D.cxx file.

#include <vrvDis/vrvDis.h>

The new onStart() function looks as follows:

bool DistributedSimulationDriver::onStart()
{
std::cout << "DistributedSimulationDriver::onStart(): " << std::endl;
init.setPort(3152);
init.setExerciseId(1);
init.setSiteId(2);
init.setApplicationNumber(1);
// VR-Link default buffer is to small for this application; set it to 1 MB.
init.setReceiveBufferSize(0x100000);
DtAgentManagerInterface* simSceneInterface = &myAgentManager;
myConnection = new DistributedSimulationConnection(*simSceneInterface,
DtSharedSettingsManager::instance(agentManager().de()));
myConnection->connect(init);
myConnection->exerciseConn()->clock()->setSimTime(0);
DistSimEntityExistenceListener& entityListener = DistSimEntityExistenceListener::instance(*myConnection);
entityListener.signal_entityDiscovered.connect(
boost::bind(&DistributedSimulationDriver::discoverCb,this,_1,_2));
return myConnection->connected();
}

The difference between this version and the version in Project 3B is that, instead of adding a detonation interaction listener, we instantiate a DistSimEntityExistenceListener object and pass it our connection object. We then add a reference to the driver's callback function to the connection object which is associated with the entity discovery signal defined in the entity existence listener. It is important that the clock sim time should be reset.

myConnection->exerciseConn()->clock()->setSimTime(0);
DistSimEntityExistenceListener& entityListener = DistSimEntityExistenceListener::instance(*myConnection);
entityListener.signal_entityDiscovered.connect(
boost::bind(&DistributedSimulationDriver::discoverCb,this,_1,_2));

Our callback function will expect to receive a DtReflectedEntity, as well as a character which will specify whether the discovery is regarding and entity addition ('A') or removal ('R'). The function will first make sure that this is a valid entity, and then if valid, print out arbitrary values regarding the entity data object.

bool DistributedSimulationDriver::discoverCb(DtReflectedObject* reflectedObj, char state)
{
DtReflectedEntity* reflectedEnt = (DtReflectedEntity*) reflectedObj;
if (reflectedEnt->esr()->entityType() == DtEntityType(0,0,0,0,0,0,0))
{
return false;
}
//Check location, lets hope that spacial are all sent at once since the
//others might be junk too.
if(reflectedEnt->esr()->lastSetLocation() == DtVector::zero())
{
return false;
}
std::cout << state << "(" << reflectedEnt->entityId().entityNum()
<< "," << reflectedEnt->id().entityNum() << ","
<< reflectedEnt->idString() << ")";
return true;
}

Now we need two more classes. The first class is merely a copy of the connection class created for Project 3B. No changes need to be made to this class. The second class is a listener, and as with the listener from Project 3B, this class inherits from DtVirtualBaseClass.

The difference between the listener used for this project and that created for Project 3B is that we need two callback functions, since there isn't one way to get data for both entity addition and removal. This listener will pass the information back to the simulation driver, so we will need a signal to inform the driver of all entity discovery. This signal will pass both the reflected entity data object and a char variable back to a connected object. The char variable will be used to inform the driver that this is either an entity addition ('A') or removal ('R'). We will also need a member pointer of type DtReflectedEntityList.

The files defining this class can be seen at the following links:

From these links we can see that the startListening() function instantiates a new DtReflectedEntityList passing the VR-Link connection pointer, and then adds both the addition and the removal callback functions to it. The stopListening() function, if the DistSimEntityExistenceListener object exists, will remove the callbacks and delete the listener object.

void DistSimEntityExistenceListener::startListening()
{
if(!myConnection.exerciseConn())
{
DtTHROW_NEW(DtInvalidInput,"Invalid DtExerciseConn Pointer.");
}
myReflectedEntityList = new DtReflectedEntityList(myConnection.exerciseConn());
myReflectedEntityList->addObjectAdditionCallback(&DistSimEntityExistenceListener::entityAddedCb,this);
myReflectedEntityList->addObjectRemovalCallback(&DistSimEntityExistenceListener::entityRemovedCb,this);
}
void DistSimEntityExistenceListener::stopListening()
{
if (myReflectedEntityList)
{
myReflectedEntityList->removeObjectAdditionCallback(&DistSimEntityExistenceListener::entityAddedCb,this);
myReflectedEntityList->removeObjectRemovalCallback(&DistSimEntityExistenceListener::entityRemovedCb,this);
delete myReflectedEntityList;
myReflectedEntityList = 0;
}
}

The instance() function, which creates an instance of the DistSimEntityExistenceListener class, will first check the simulation connection to see if it already has such a listener. If not, then it creates a vector of type string and puts the name of the listener class in as the only entry. It then instantiates the listener and registers it with the simulation connection using the vector for later identification. Finally, it returns a pointer to its instance of the listener.

DistSimEntityExistenceListener& DistSimEntityExistenceListener::instance(DistributedSimulationConnection& sim)
{
DistSimEntityExistenceListener* detListener =
dynamic_cast<DistSimEntityExistenceListener*>(sim.findInstance("DistReflectedEntityExistenceListener"));
if(!detListener)
{
std::vector<std::string> classNames;
classNames.push_back("DistSimEntityExistenceListener");
detListener = new DistSimEntityExistenceListener(classNames,sim);
sim.registerInstance("DistReflectedEntityExistenceListener",detListener);
}
return *detListener;
}

The two callback functions invoke the signal_entityDiscovery signal for the owning object that is passed in as an argument. The only difference between these callbacks is that the addition callback passes an 'A' and the removal callback passes an 'R' in order to differentiate between entity addition and removal data.

void DistSimEntityExistenceListener::entityAddedCb(DtReflectedObject* refObj, void* usr)
{
((DistSimEntityExistenceListener*)usr)->signal_entityDiscovered(refObj, 'A');
}
void DistSimEntityExistenceListener::entityRemovedCb(DtReflectedObject* refObj, void* usr)
{
((DistSimEntityExistenceListener*)usr)->signal_entityDiscovered(refObj, 'R');
}

Testing the plugin

Invoke either the tutorialDistributedSimulation3Dd.bat or tutorialDistributedSimulation3D.sh script file to start the VR-Vantage Stealth application. Follow these instructions to test the plugin:

  1. Choose File > View > Drivers Panel.
  2. Select the driver named "DistributedSimulationDriver3D".
  3. Click on the Start button.
  4. You will see in the console window that the text "DistributedSimulationDriver::onStart():" has printed, and on the subsequent line, a series of dots is now continually being printed. Each dot is followed by the number of PDUs read in from the simulation connection.
  5. Start up a VR-Forces application using DIS as the protocol, open a scenario that has at least one entity added, and then start the scenario. As before, the number of PDUs will increase notably. Once data comes in for an entity addition, an A(x,y,z) will be printed, where A stands for addition, and x, y, and z are arbitrarily chosen values from the detonation interaction object. If the scenario that you started contained detonations, when a detonation occurs, you will also see a R(x,y,z) printed, where R stands for removal. You will again notice that the string "New Entity" and "Final PDU" are printed, just before the A(x,y,z) and R(x,y,z) respectively. See the explanation for this in the "Testing the plugin" section for the tutorialDistributedSimulation3C project.
  6. Go back to Stealth, make sure the same driver is selected in the Drivers Panel, and click the Stop button.
  7. You will see in the console window that the dots, byte counts, and detonations are no longer being printed, and that the text "DistributedSimulationDriver::onStop():" has been printed on the subsequent line.
  8. Continually clicking the Start and Stop button will keep starting and stopping the driver, giving these same results in the console window.

[<< Add and Remove Reflected Entity Callbacks] [Adding an Entity Using VR-Link >>]


Project Files

DistributedSimulationDriver3D.h

/******************************************************************************
** Copyright (c) 2014 MAK Technologies, Inc.
** All rights reserved.
******************************************************************************/
#pragma once
#include <vl/reflectedEntityListDIS.h>
using namespace makVrv;
class DtExerciseConn;
namespace makVrv
{
namespace tutorialDistributedSimulation3D
{
class DistributedSimulationConnection;
class DistributedSimulationDriver : public DtDriver
{
public:
DistributedSimulationDriver(DtAgentManager& agentManager, const std::string& instanceName);
virtual ~DistributedSimulationDriver();
virtual const std::string& className() const;
protected:
virtual bool onStart();
virtual bool onStop();
virtual bool onTick();
virtual bool discoverCb(DtReflectedObject* reflectedObj, char state);
protected:
DistributedSimulationConnection* myConnection;
};
}
}

DistributedSimulationDriver3D.cxx

/******************************************************************************
** Copyright (c) 2014 MAK Technologies, Inc.
** All rights reserved.
******************************************************************************/
#define DT_PROTOCOL_NAMESPACE vrvDis
#define DtDIS 1
#include <vrvCore/DtDe.h>
#include <vl/exerciseConnInitializer.h>
#include <vrvDis/vrvDis.h>
#include <iostream>
namespace makVrv
{
namespace tutorialDistributedSimulation3D
{
DtAgentManager& agentManager, const std::string& instanceName)
: DtDriver(agentManager, instanceName)
, myConnection(0)
{
}
DistributedSimulationDriver::~DistributedSimulationDriver(void)
{
onStop();
}
{
// name must be static, since it's returned by reference
static std::string name("DistributedSimulationDriver");
return name;
}
{
std::cout << "DistributedSimulationDriver::onStart(): " << std::endl;
init.setPort(3152);
init.setExerciseId(1);
init.setSiteId(2);
init.setApplicationNumber(1);
// VR-Link default buffer is to small for this application; set it to 1 MB.
init.setReceiveBufferSize(0x100000);
DtAgentManagerInterface* simSceneInterface = &myAgentManager;
myConnection = new DistributedSimulationConnection(*simSceneInterface,
DtSharedSettingsManager::instance(agentManager().de()));
myConnection->connect(init);
myConnection->exerciseConn()->clock()->setSimTime(0);
DistSimEntityExistenceListener& entityListener = DistSimEntityExistenceListener::instance(*myConnection);
entityListener.signal_entityDiscovered.connect(
boost::bind(&DistributedSimulationDriver::discoverCb,this,_1,_2));
return myConnection->connected();
}
{
// NOTE: breakdown code goes here. Return false on bad status.
std::cout << "DistributedSimulationDriver::onStop(): " << std::endl;
delete myConnection;
myConnection = 0;
return true;
}
{
// printout to show whether or not connected at the tick level
if (myConnection->connected())
{
int numread = myConnection->exerciseConn()->drainInput();
std::cout << "." << numread;
}
else
std::cout << "x";
return myConnection->connected();
}
bool DistributedSimulationDriver::discoverCb(DtReflectedObject* reflectedObj, char state)
{
DtReflectedEntity* reflectedEnt = (DtReflectedEntity*) reflectedObj;
if (reflectedEnt->esr()->entityType() == DtEntityType(0,0,0,0,0,0,0))
{
return false;
}
//Check location, lets hope that spacial are all sent at once since the
//others might be junk too.
if(reflectedEnt->esr()->lastSetLocation() == DtVector::zero())
{
return false;
}
std::cout << state << "(" << reflectedEnt->entityId().entityNum()
<< "," << reflectedEnt->id().entityNum() << ","
<< reflectedEnt->idString() << ")";
return true;
}
}
}

DistributedSimulationPlugin3D.h

/******************************************************************************
** Copyright (c) 2011 MAK Technologies, Inc.
** All rights reserved.
******************************************************************************/
#pragma once
// Setup proper plugin export symbol
#ifdef WIN32
#define DT_DE_PLUGIN_EXPORT_MACRO __declspec ( dllexport )
#else
#define DT_DE_PLUGIN_EXPORT_MACRO
#endif
// Declare & export the plugin initialization function (bool initDeModule(DtDe* de))
namespace makVrv { class DtDe; }
// Work function for the plugin initialization
void init(makVrv::DtDe& de);
// Callback to create the stateViewDriver

DistributedSimulationPlugin3D.cxx

/******************************************************************************
** Copyright (c) 2011 MAK Technologies, Inc.
** All rights reserved.
******************************************************************************/
#include <vrvCore/DtDe.h>
#include <boost/bind.hpp>
using namespace makVrv;
using namespace makVrv::tutorialDistributedSimulation3D;
{
// We're done with the signal, so disconnect from it
// Create the driver that will implement a distributed simulation
DistributedSimulationDriver* driver = new DistributedSimulationDriver(
de->agentManager(), "DistributedSimulationDriver3D");
// After the driver is added to the display engine below, the DE will manage
// its memory. This driver will add itself to the connection list. Start
// it from the connection panel.
de->driverManager().addDriver(driver);
}
void init(DtDe& de)
{
// Ensure that init only gets called once
// (not strictly necessary here, but this is good practice in general)
// We only want to create the driver if we're running in master mode:
if (de.isInMasterMode())
{
// The driver must be created after the DE's initialization is
// finished, to make sure all the objects it uses exist.
}
}
{
// Setup the plugin. Normally, the init function and functionality
// should be in its own library.
init(*de);
return true;
}

DistributedSimulationConnection3D.h

/******************************************************************************
** Copyright (c) 2011 MAK Technologies, Inc.
** All rights reserved.
******************************************************************************/
#pragma once
#include <boost/signal.hpp>
class DtExerciseConn;
namespace makVrv
{
namespace tutorialDistributedSimulation3D
{
class DistributedSimulationConnection : public DtBaseConnection
{
public:
DistributedSimulationConnection(DtAgentManagerInterface& aSceneInterface,
DtSharedSettingsManager& settingsManager);
inline DtExerciseConn* exerciseConn()
{
return myConnection;
}
virtual bool connect(const DtExerciseConnInitializer& exConnInitializer);
virtual bool disconnect();
virtual bool connected() const;
boost::signal<void ()> signal_toBeDisconnected;
protected:
DtExerciseConn* myConnection;
};
}
}

DistributedSimulationConnection3D.cxx

/******************************************************************************
** Copyright (c) 2014 MAK Technologies, Inc.
** All rights reserved.
******************************************************************************/
#define DtDIS 1
#include <vl/exerciseConn.h>
#include <vl/exerciseConnInitializer.h>
namespace makVrv
{
namespace tutorialDistributedSimulation3D
{
DtAgentManagerInterface& aSceneInterface, DtSharedSettingsManager& settingsManager)
: DtBaseConnection(settingsManager, aSceneInterface)
, myConnection(0)
, myExConnInitializer(new DtExerciseConnInitializer())
{
}
DistributedSimulationConnection::~DistributedSimulationConnection(void)
{
delete myExConnInitializer;
}
bool DistributedSimulationConnection::connect(const DtExerciseConnInitializer& exConnInitializer)
{
*myExConnInitializer = exConnInitializer;
if (myConnection)
disconnect();
DtExerciseConn::InitializationStatus status = DtExerciseConn::DtINIT_SUCCESS;
try
{
myConnection = new DtExerciseConn(*myExConnInitializer, &status);
}
catch(...)
{
return false;
}
if(status != DtExerciseConn::DtINIT_SUCCESS)
{
return false;
}
return true;
}
bool DistributedSimulationConnection::disconnect()
{
try
{
delete myConnection;
myConnection = 0;
}
catch(...)
{
return false;
}
return true;
}
bool DistributedSimulationConnection::connected() const
{
return 0 != myConnection;
}
}
}

DistSimEntityExistenceListener3D.h

/******************************************************************************
** Copyright (c) 2014 MAK Technologies, Inc.
** All rights reserved.
******************************************************************************/
#pragma once
#include <vl/detonationInteraction.h>
#include <boost/signal.hpp>
#include <vl/reflectedEntityListDIS.h>
namespace makVrv
{
namespace tutorialDistributedSimulation3D
{
class DistSimEntityExistenceListener : public makVrv::DtVirtualBaseClass
{
public:
static DistSimEntityExistenceListener& instance(DistributedSimulationConnection& sim);
boost::signal<void (DtReflectedObject*, char)> signal_entityDiscovered;
protected:
DistSimEntityExistenceListener(const std::vector<std::string>& vrlInteractionNames,
DistributedSimulationConnection& baseSim);
void stopListening();
private:
static void entityAddedCb(DtReflectedObject* reflectedObj, void* usr);
static void entityRemovedCb(DtReflectedObject* reflectedObj, void* usr);
protected:
DistributedSimulationConnection& myConnection;
};
}
}

DistSimEntityExistenceListener3D.cxx

/******************************************************************************
** Copyright (c) 2014 MAK Technologies, Inc.
** All rights reserved.
******************************************************************************/
#define DL_DLL_IGCONVRLINK DT_DLL_VRVDIS
#define DT_PROTOCOL_NAMESPACE vrvDis
#define DtDIS 1
#include <vl/reflectedEntityListDIS.h>
#include <boost/bind.hpp>
#include <iostream>
namespace makVrv
{
namespace tutorialDistributedSimulation3D
{
const std::vector<std::string>& vrlInteractionNames,DistributedSimulationConnection& conn)
: makVrv::DtVirtualBaseClass()
, myConnection(conn)
, myReflectedEntityList(0)
{
myConnection.signal_toBeDisconnected.connect(
boost::bind(&DistSimEntityExistenceListener::stopListening,this));
if(myConnection.connected())
{
startListening();
}
}
DistSimEntityExistenceListener::~DistSimEntityExistenceListener()
{
stopListening();
}
void DistSimEntityExistenceListener::startListening()
{
if(!myConnection.exerciseConn())
{
DtTHROW_NEW(DtInvalidInput,"Invalid DtExerciseConn Pointer.");
}
myReflectedEntityList = new DtReflectedEntityList(myConnection.exerciseConn());
myReflectedEntityList->addObjectAdditionCallback(&DistSimEntityExistenceListener::entityAddedCb,this);
myReflectedEntityList->addObjectRemovalCallback(&DistSimEntityExistenceListener::entityRemovedCb,this);
}
void DistSimEntityExistenceListener::stopListening()
{
if (myReflectedEntityList)
{
myReflectedEntityList->removeObjectAdditionCallback(&DistSimEntityExistenceListener::entityAddedCb,this);
myReflectedEntityList->removeObjectRemovalCallback(&DistSimEntityExistenceListener::entityRemovedCb,this);
delete myReflectedEntityList;
myReflectedEntityList = 0;
}
}
DistSimEntityExistenceListener& DistSimEntityExistenceListener::instance(DistributedSimulationConnection& sim)
{
DistSimEntityExistenceListener* detListener =
dynamic_cast<DistSimEntityExistenceListener*>(sim.findInstance("DistReflectedEntityExistenceListener"));
if(!detListener)
{
std::vector<std::string> classNames;
classNames.push_back("DistSimEntityExistenceListener");
detListener = new DistSimEntityExistenceListener(classNames,sim);
sim.registerInstance("DistReflectedEntityExistenceListener",detListener);
}
return *detListener;
}
void DistSimEntityExistenceListener::entityAddedCb(DtReflectedObject* refObj, void* usr)
{
((DistSimEntityExistenceListener*)usr)->signal_entityDiscovered(refObj, 'A');
}
void DistSimEntityExistenceListener::entityRemovedCb(DtReflectedObject* refObj, void* usr)
{
((DistSimEntityExistenceListener*)usr)->signal_entityDiscovered(refObj, 'R');
}
}
}


Copyright © 2005-2019 VT MAK. All Rights Reserved (www.mak.com)