VR-Vantage 2.3 API Documentation
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
The Distributed Simulation Tutorial - Add a Detonation Listener

Introduction to Project 3B

Project 3B in the Distributed Simulation tutorial takes a look at how to add a detonation 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, we will create a custom connection class and a custom listener class. This is a simplification of the built-in VR-Vantage connection and listener classes, since the existing VR-Vantage detonation listener requires a VR-Vantage connection object, the creation of which requires more complex knowledge of the scene than we might want to deal with for a given project.

Adding a Detonation Interaction Listener

To add a detonation interaction listener, we will need to add only a local callback function to the simulation driver class, rather than the static and local functions used in Project 3A. This callback will be connected to the interaction received signal emitted from an instance of a detonation interaction listener.

The first thing we need to do is to add the declaration for a processInteraction() function to the header file. This will need to take a reference to a DtDetonationPdu object. Again, although not necessary, for better readability we can typedef this to a more self-documenting type name:

typedef DtDetonationPdu DtDetonationInteraction;

void processInteraction(const DtDetonationInteraction& inter);

Then we can define the function as follows, being the same functionality as was defined in the local function for the DistributedSimulationDriver class in Project 3A:

void DistributedSimulationDriver::processInteraction(
const DtDetonationInteraction& inter)
{
std::cout << "((detonation interaction))";
std::cout << "D(" << inter.attackerId().entityNum() << "," << inter.targetId().entityNum() << "," << inter.result() << ")";
}

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;
mySimulation = new DistributedSimulationConnection(*simSceneInterface,
DtSharedSettingsManager::instance(agentManager().de()));
mySimulation->connect(init);
// Create a detonation listener instance and connect to its signal
DistSimDetonationInteractionListener& instance = DistSimDetonationInteractionListener::instance(*mySimulation);
instance.signal_interactionReceived.connect(
boost::bind(&DistributedSimulationDriver::processInteraction,this,_1));
return mySimulation->connected();
}

Here, we are creating an initializer as before, but the instantiation of the connection is a little bit more involved. We first need a reference to our agent manager, which was passed in during construction of the DistributedSimulationDriver object. Given this, we can request a reference to the display engine (DE), which we use to get an instance of the shared settings manager. The references to the agent manager and the settings manager can then be used to instantiate a VR-Vantage type of connection, which we will be discussing below, using the name DistributedSimulationConnection. This new connection class will be the new owner of the reference to the VR-Link connection object. Finally we call connect() on this connection object.

DtAgentManagerInterface* simSceneInterface = &myAgentManager;
mySimulation = new DistributedSimulationConnection(*simSceneInterface,
DtSharedSettingsManager::instance(agentManager().de()));
mySimulation->connect(init);

Next we pass the newly instantiated connection as an argument to get an instance of a detonation listener of type DistSimDetonationInteractionListener, also discussed below. Once we have that instance, all we need to do is connect its signal_interactionReceived to the local callback function using a boost::bind() call. The arguments to this call are: a reference to the callback function, a pointer to the object (this) which owns the function, and a placeholder for the parameter which the callback will be expecting.

DistSimDetonationInteractionListener& instance = DistSimDetonationInteractionListener::instance(*mySimulation);
instance.signal_interactionReceived.connect(
boost::bind(&DistributedSimulationDriver::processInteraction,this,_1));

Then we ask the connection object itself whether it is connected, and return the result.

return mySimulation->connected();

Now we need two more classes. The first class is a new connection class which inherits from DtBaseConnection, which is the base for VR-Vantage connections. This will allow us to tie a connection object to an object of the second class which is a listener, inheriting from DtVirtualBaseClass.

The new connection class implements the functions connect(), disconnect(), and connected(), each returning a bool to tell whether the connect or disconnect worked, or in the case of the latter function, whether the connection is currently established. This class will own pointers to a DtExerciseConn and a DtExerciseConnInitializer, which you will remember from being introduced in Project 2A, and the functionality for each of these functions is the same as was implemented in that project. The files defining this class can be seen at the following links:

The new listener class implements the functions startListening(), which is called from its constructor, and stopListening(), which is called from its destructor. It implements a callback function for receiving detonation interactions, just as was done in Project 3A, and it has a connection pointer as a member variable. The files defining this class can be seen at the following links:

Taking a closer look at this listener class, we see that it is a templated class. This is so that any number of listeners of different types can be created using this class. To make use of this fact, the last line in the header is a typedef specifying that the DistSimInteractionListener class can be referenced with the name DistSimDetonationInteractionListener in order to make it specific to the DtDetonationInteraction type of interaction.

template <typename InteractionClass>
class DistSimInteractionListener : public DtVirtualBaseClass
{

typedef DistSimInteractionListener<DtDetonationInteraction> DistSimDetonationInteractionListener;

A boost::signal is also defined as a member of this class. This is the signal_interactionReceived signal that is referred to above.

boost::signal<void (const InteractionClass&)> signal_interactionReceived;

The custom detonation interaction listener class is then instantiated by calling the class's protected constructor using the static instance() function, and passing the connection object as its only parameter.

template <>
DistSimInteractionListener<DtDetonationInteraction>&
DistSimInteractionListener<DtDetonationInteraction>::instance(DistributedSimulationConnection& sim)
{
DistSimDetonationInteractionListener* detListener =
dynamic_cast<DistSimDetonationInteractionListener*>(sim.findInstance("DistSimDetonationInteractionListener"));
if(!detListener)
{
detListener = new DistSimDetonationInteractionListener(sim);
sim.registerInstance("DistSimDetonationInteractionListener",detListener);
}
return *detListener;
}

The startListening() function connects the connection object's signal_toBeDisconnected signal to the instance-specific stopListening() function by the use of a call to boost::bind(). The connection object's VR-Link connection pointer is then added to the template's InteractionClass (in our case DtDetonationInteraction) along with a reference to the callback function and to the object (this) which owns the callback.

template <typename InteractionClass>
void DistSimInteractionListener<InteractionClass>::startListening()
{
if(!myConnection.exerciseConn())
{
DtTHROW_NEW(DtInvalidInput,"Invalid DtExerciseConn Pointer.");
}
myConnection.signal_toBeDisconnected.connect(
boost::bind(&DistSimInteractionListener<InteractionClass>::stopListening, this));
}

The stopListening() function does the opposite, disconnecting the callback from the signal_toBeDisconnected signal, and then removing the reference to the callback from the interaction class (again in our case DtDetonationInteraction).

template <typename InteractionClass>
void DistSimInteractionListener<InteractionClass>::stopListening()
{
//Ok the exercise connection is already done, well we're stopped listening.
//so it should be safe to just return.
if(!myConnection.exerciseConn())
{
return;
}
myConnection.signal_toBeDisconnected.disconnect(
boost::bind(&DistSimInteractionListener<InteractionClass>::stopListening, this));
}

Finally, the callback function emits the specific interaction listener object's signal_interactionReceived signal, passing with it the interaction class pointer that it had received.

template <typename InteractionClass>
void DistSimInteractionListener<InteractionClass>::receiveInteractionCB( InteractionClass* inter, void* usr )
{
((DistSimInteractionListener<InteractionClass>*)usr)->signal_interactionReceived(*inter);
}

Testing the plugin

Invoke either the tutorialDistributedSimulation3Bd.bat or tutorialDistributedSimulation3B.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 "DistributedSimulationDriver3B".
  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 detonation in it (the Makland scenario has many), and then start the scenario. As before, the number of PDUs will increase notably. Once there a detonation occurs in VR-Forces, you will see the text "((detonation interaction))" printed in the console window, followed by D(x,y,z), where x, y, and z are arbitrarily chosen values from the detonation interaction object.
  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. Note that if detonations occur in VR-Forces during the time that the driver is stopped, these interactions will never be received by the driver, even after it is restarted.
  8. Continually clicking the Start and Stop button will keep starting and stopping the driver, giving these same results in the console window.

[<< Add a Detonation Interaction Callback] [Add and Remove Reflected Entity Callbacks >>]


Project Files

DistributedSimulationDriver3B.h

/******************************************************************************
** Copyright (c) 2011 MAK Technologies, Inc.
** All rights reserved.
******************************************************************************/
#pragma once
using namespace makVrv;
class DtExerciseConn;
class DtDetonationPdu;
namespace makVrv
{
namespace tutorialDistributedSimulation3B
{
class DistributedSimulationConnection;
typedef DtDetonationPdu DtDetonationInteraction;
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();
protected:
void processInteraction(const DtDetonationInteraction& inter);
protected:
DistributedSimulationConnection* mySimulation;
};
}
}

DistributedSimulationDriver3B.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 <vl/exerciseConn.h>
#include <iostream>
using namespace makVrv;
namespace makVrv
{
namespace tutorialDistributedSimulation3B
{
DistributedSimulationDriver::DistributedSimulationDriver(
DtAgentManager& agentManager, const std::string& instanceName)
: DtDriver(agentManager, instanceName)
, mySimulation(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;
mySimulation = new DistributedSimulationConnection(*simSceneInterface,
DtSharedSettingsManager::instance(agentManager().de()));
mySimulation->connect(init);
// Create a detonation listener instance and connect to its signal
DistSimDetonationInteractionListener& instance = DistSimDetonationInteractionListener::instance(*mySimulation);
instance.signal_interactionReceived.connect(
boost::bind(&DistributedSimulationDriver::processInteraction,this,_1));
return mySimulation->connected();
}
{
// NOTE: breakdown code goes here. Return false on bad status.
std::cout << "DistributedSimulationDriver::onStop(): " << std::endl;
if (mySimulation)
{
DistSimDetonationInteractionListener& instance = DistSimDetonationInteractionListener::instance(*mySimulation);
instance.signal_interactionReceived.disconnect(
boost::bind(&DistributedSimulationDriver::processInteraction,this,_1));
delete mySimulation;
mySimulation = 0;
}
return true;
}
{
// printout to show whether or not connected at the tick level
bool connState = mySimulation->connected();
if (connState)
{
int numread = mySimulation->exerciseConn()->drainInput();
std::cout << "." << numread;
}
else
std::cout << "x";
return connState;
}
void DistributedSimulationDriver::processInteraction(
const DtDetonationInteraction& inter)
{
std::cout << "((detonation interaction))";
std::cout << "D(" << inter.attackerId().entityNum() << "," << inter.targetId().entityNum() << "," << inter.result() << ")";
}
}
}

DistributedSimulationPlugin3B.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

DistributedSimulationPlugin3B.cxx

/******************************************************************************
** Copyright (c) 2011 MAK Technologies, Inc.
** All rights reserved.
******************************************************************************/
#include <vrvCore/DtDe.h>
#include <boost/bind.hpp>
using namespace makVrv;
using namespace tutorialDistributedSimulation3B;
{
// 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(), "DistributedSimulationDriver3B");
// 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;
}

DistributedSimulationConnection3B.h

/******************************************************************************
** Copyright (c) 2011 MAK Technologies, Inc.
** All rights reserved.
******************************************************************************/
#pragma once
#include <boost/signal.hpp>
using namespace makVrv;
class DtExerciseConn;
namespace makVrv
{
namespace tutorialDistributedSimulation3B
{
class DistributedSimulationConnection : public DtBaseConnection
{
public:
DistributedSimulationConnection(DtAgentManagerInterface& aSceneInterface,
DtSharedSettingsManager& settingsManager);
virtual ~DistributedSimulationConnection();
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;
DtExerciseConnInitializer* myExConnInitializer;
};
}
}

DistributedSimulationConnection3B.cxx

/******************************************************************************
** Copyright (c) 2014 MAK Technologies, Inc.
** All rights reserved.
******************************************************************************/
#define DtDIS 1
#include <vl/exerciseConn.h>
#include <vl/exerciseConnInitializer.h>
namespace makVrv
{
namespace tutorialDistributedSimulation3B
{
DtAgentManagerInterface& aSceneInterface, DtSharedSettingsManager& settingsManager)
: DtBaseConnection(settingsManager, aSceneInterface)
, myConnection(0)
, myExConnInitializer(new DtExerciseConnInitializer())
{
}
DistributedSimulationConnection::~DistributedSimulationConnection(void)
{
}
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 myConnection != 0;
}
}
}

DistSimInteractionListener3B.h

/******************************************************************************
** Copyright (c) 2014 MAK Technologies, Inc.
** All rights reserved.
******************************************************************************/
#pragma once
#include <vl/detonationInteraction.h>
#include <boost/signal.hpp>
using namespace makVrv;
namespace makVrv
{
namespace tutorialDistributedSimulation3B
{
class InteractionClass;
//The InteractionClass should be a VR-Link interaction class.
template <typename InteractionClass>
class DistSimInteractionListener : public DtVirtualBaseClass
{
public:
static DistSimInteractionListener<InteractionClass>& instance(DistributedSimulationConnection& sim);
virtual ~DistSimInteractionListener();
boost::signal<void (const InteractionClass&)> signal_interactionReceived;
protected:
DistSimInteractionListener(DistributedSimulationConnection& baseSim);
void startListening();
void stopListening();
static void receiveInteractionCB(InteractionClass* inter, void* usr);
protected:
DistributedSimulationConnection& myConnection;
};
typedef DistSimInteractionListener<DtDetonationInteraction> DistSimDetonationInteractionListener;
}
}

DistSimInteractionListener3B.cxx

/******************************************************************************
** Copyright (c) 2011 MAK Technologies, Inc.
** All rights reserved.
******************************************************************************/
#define DtDIS 1
#include <boost/bind.hpp>
namespace makVrv
{
namespace tutorialDistributedSimulation3B
{
template <typename InteractionClass>
DistributedSimulationConnection& conn)
: makVrv::DtVirtualBaseClass()
, myConnection(conn)
{
//Stop listening on disconnect.
startListening();
}
template <typename InteractionClass>
DistSimInteractionListener<InteractionClass>::~DistSimInteractionListener()
{
stopListening();
}
template <typename InteractionClass>
void DistSimInteractionListener<InteractionClass>::receiveInteractionCB( InteractionClass* inter, void* usr )
{
((DistSimInteractionListener<InteractionClass>*)usr)->signal_interactionReceived(*inter);
}
template <typename InteractionClass>
void DistSimInteractionListener<InteractionClass>::startListening()
{
if(!myConnection.exerciseConn())
{
DtTHROW_NEW(DtInvalidInput,"Invalid DtExerciseConn Pointer.");
}
myConnection.signal_toBeDisconnected.connect(
boost::bind(&DistSimInteractionListener<InteractionClass>::stopListening, this));
InteractionClass::addCallback(myConnection.exerciseConn(),
&DistSimInteractionListener<InteractionClass>::receiveInteractionCB, this);
}
template <typename InteractionClass>
void DistSimInteractionListener<InteractionClass>::stopListening()
{
//Ok the exercise connection is already done, well we're stopped listening.
//so it should be safe to just return.
if(!myConnection.exerciseConn())
{
return;
}
myConnection.signal_toBeDisconnected.disconnect(
boost::bind(&DistSimInteractionListener<InteractionClass>::stopListening, this));
InteractionClass::removeCallback(myConnection.exerciseConn(),
&DistSimInteractionListener<InteractionClass>::receiveInteractionCB, this);
}
// Specific definitions of the static instance method
template <>
DistSimInteractionListener<DtDetonationInteraction>&
DistSimInteractionListener<DtDetonationInteraction>::instance(DistributedSimulationConnection& sim)
{
DistSimDetonationInteractionListener* detListener =
dynamic_cast<DistSimDetonationInteractionListener*>(sim.findInstance("DistSimDetonationInteractionListener"));
if(!detListener)
{
detListener = new DistSimDetonationInteractionListener(sim);
sim.registerInstance("DistSimDetonationInteractionListener",detListener);
}
return *detListener;
}
}
}


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