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.
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);
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)
{
if (reflectedEnt->esr()->entityType() == DtEntityType(0,0,0,0,0,0,0))
{
return false;
}
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->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:
-
Choose File > View > Drivers Panel.
-
Select the driver named "DistributedSimulationDriver3D".
-
Click on the Start button.
-
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.
-
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.
-
Go back to Stealth, make sure the same driver is selected in the Drivers Panel, and click the Stop button.
-
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.
-
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
#pragma once
#include <vl/reflectedEntityListDIS.h>
using namespace makVrv;
class DtExerciseConn;
namespace makVrv
{
namespace tutorialDistributedSimulation3D
{
class DistributedSimulationConnection;
class DistributedSimulationDriver :
public DtDriver
{
public:
virtual ~DistributedSimulationDriver();
protected:
virtual bool onStart();
virtual bool onStop();
virtual bool onTick();
protected:
DistributedSimulationConnection* myConnection;
};
}
}
DistributedSimulationDriver3D.cxx
#define DT_PROTOCOL_NAMESPACE vrvDis
#define DtDIS 1
#include <vl/exerciseConnInitializer.h>
#include <iostream>
namespace makVrv
{
namespace tutorialDistributedSimulation3D
{
DtAgentManager& agentManager,
const std::string& instanceName)
: DtDriver(agentManager, instanceName)
, myConnection(0)
{
}
DistributedSimulationDriver::~DistributedSimulationDriver(void)
{
onStop();
}
{
}
{
std::cout << "DistributedSimulationDriver::onStart(): " << std::endl;
init.setPort(3152);
init.setExerciseId(1);
init.setSiteId(2);
init.setApplicationNumber(1);
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();
}
{
std::cout << "DistributedSimulationDriver::onStop(): " << std::endl;
delete myConnection;
myConnection = 0;
return true;
}
{
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)
{
if (reflectedEnt->esr()->entityType() == DtEntityType(0,0,0,0,0,0,0))
{
return false;
}
if(reflectedEnt->esr()->lastSetLocation() == DtVector::zero())
{
return false;
}
std::cout << state << "(" << reflectedEnt->entityId().entityNum()
<< "," << reflectedEnt->id().entityNum() << ","
<< reflectedEnt->idString() << ")";
return true;
}
}
}
DistributedSimulationPlugin3D.h
#pragma once
#ifdef WIN32
#define DT_DE_PLUGIN_EXPORT_MACRO __declspec ( dllexport )
#else
#define DT_DE_PLUGIN_EXPORT_MACRO
#endif
namespace makVrv { class DtDe; }
DistributedSimulationPlugin3D.cxx
#include <boost/bind.hpp>
using namespace makVrv;
using namespace makVrv::tutorialDistributedSimulation3D;
{
DistributedSimulationDriver* driver = new DistributedSimulationDriver(
}
{
{
}
}
{
return true;
}
DistributedSimulationConnection3D.h
#pragma once
#include <boost/signal.hpp>
class DtExerciseConn;
namespace makVrv
{
namespace tutorialDistributedSimulation3D
{
class DistributedSimulationConnection : public DtBaseConnection
{
public:
{
}
protected:
};
}
}
DistributedSimulationConnection3D.cxx
#define DtDIS 1
#include <vl/exerciseConn.h>
#include <vl/exerciseConnInitializer.h>
namespace makVrv
{
namespace tutorialDistributedSimulation3D
{
DtAgentManagerInterface& aSceneInterface, DtSharedSettingsManager& settingsManager)
: DtBaseConnection(settingsManager, aSceneInterface)
, myConnection(0)
{
}
DistributedSimulationConnection::~DistributedSimulationConnection(void)
{
delete myExConnInitializer;
}
{
*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
#pragma once
#include <vl/detonationInteraction.h>
#include <boost/signal.hpp>
#include <vl/reflectedEntityListDIS.h>
namespace makVrv
{
namespace tutorialDistributedSimulation3D
{
{
public:
protected:
DistributedSimulationConnection& baseSim);
private:
protected:
};
}
}
DistSimEntityExistenceListener3D.cxx
#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->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');
}
}
}