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

Introduction to Project 2B

Project 2B in the Distributed Simulation tutorial takes a look at how to create a threaded connection to the distributed simulation using VR-Link. As was stated in project 2A, a connection can be made which uses any of the following protocols: DIS, HLA 1.3, or HLA 1516. For this project we will continue to look at creating a DIS connection, and we will need to introduce some new functions to the simulation driver in order to start, run, and stop a connection thread.

Examining the files that make up the project

For this project, all files from Project 2A will be reused. No new files will be needed. All changes, other than filenames, will be made in the DistributedSimulationDriver class files.

Creating a threaded VR-Link connection

To create a threaded VR-Link connection, we will continue to use the DtExerciseConn member pointer and the connection state boolean in the DistributedSimulationDriver class. We will also add member pointers to DtThreadStart and to DtThread. We instantiate the connection in onStart() in the same way, and then we instantiate and start the thread. In order to implement threading, the DistributedSimulationDriver class has to inherit the DtRunnable class, and because of this, must implement the pure virtual run() function. We will also add a start and stop function for controlling the thread.

The first change we want to make to the DistributedSimulationDriver class is to add the vlRunnable.h from VR-Link to the header file.

#include <vlutil/vlRunnable.h>

We will then need to inherit from this class, and under protected access, we add declarations for a virtual run(), a startThread(), and a stopThread() function, and the DtThreadStart and DtThread member pointers:

class DistributedSimulationDriver : public DtDriver,
protected DtRunnable

protected:
//! function that will be called when the thread is started.
virtual void run();
void startThread();
void stopThread();

The new onStart() function looks as follows:

bool DistributedSimulationDriver::onStart()
{
std::cout << "DistributedSimulationDriver::onStart(): " << std::endl;
DtExerciseConn::InitializationStatus status = DtExerciseConn::DtINIT_SUCCESS;
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);
try
{
myConnection = new DtExerciseConn(init, &status);
}
catch(...)
{
return false;
}
myConnectionState = (status == DtExerciseConn::DtINIT_SUCCESS);
if (myConnectionState)
{
startThread();
}
return myConnectionState;
}

The only difference in this function is the if statement which checks to make sure that the connection is valid before starting the connection thread:

if (myConnectionState)
{
startThread();
}

This calls the newly added startThread() function, which tests that instantiates a DtThreadStart, giving it the current object as its runnable, and a DtThread, giving it the DtThreadStart. We then start the thread and check to make sure that the start is successful:

void DistributedSimulationDriver::startThread()
{
if(myThread)
{
stopThread();
}
//Hold on to point since VR-Link doesn't delete it.
myStart = DtThreadStartSP(new DtThreadStart(this));
myThread = new DtThread(myStart);
if(myThread->start() != DtThreadSuccess)
{
DtTHROW_NEW(DtCorruptedState,"Unable to start Connection thread.");
}
}

The new onStop() function now stops the connection thread before deleting the connection:

bool DistributedSimulationDriver::onStop()
{
// NOTE: breakdown code goes here. Return false on bad status.
std::cout << "DistributedSimulationDriver::onStop(): " << std::endl;
stopThread();
delete myConnection;
myConnection = 0;
return true;
}

This calls the newly added stopThread() function, which, in the case where a thread has been instantiated, instructs the thread to stop, and then waits until all IG locks are relinquished. Once that is done, the thread and the thread start pointers are deleted.

void DistributedSimulationDriver::stopThread()

The stopThread() function is also called from the destructor:

DistributedSimulationDriver::~DistributedSimulationDriver(void)
{
stopThread();
delete myConnection;
myConnection = 0;
}

Because of the fact that stopThread() needs to go to the agent manager object to check the IG locks, we also add an include at the top of the file for that class:

Finally, when startThread() calls start() on the thread object, the new thread's virtual run() method is. In the run() method, we make sure that there is a valid connection to the simulation. If so, then we enter into a while loop until the class, as a runnable, is signaled that it is time to stop. The drainInput() call on the connection has been moved from the onTick() method to within this while loop.

void DistributedSimulationDriver::run()
{
if(!myConnection)
{
// printout to show not connected at the run level
std::cout << " notconnected - not starting run loop ";
return;
}
// printout to show connected at the run level
std::cout << " connected - starting run loop ";
while(!timeToStop())
{
int numread = myConnection->drainInput();
// print to show running
std::cout << "r" << numread;
}
}

The files changed to add this functionality are as follows:

Testing the plugin

Invoke either the tutorialDistributedSimulation2Bd.bat or tutorialDistributedSimulation2B.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 "DistributedSimulationDriver2B".
  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. At this point, unlike Project 2A, these will all be 0.
  5. If you start up a VR-Forces application using DIS as the protocol, open a scenario, and then start the scenario, the number of PDUs read will be non-zero in some cases, and zero in most cases, showing that a connection to the simulation has been established. The reason for there being so many zeroes, unlike Project 2A, has to do with the speed of the thread's run loop. The output from Project 2A waits on a callback, while the output here happens once per thread loop iteration.
  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 and byte counts 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.

[<< Create a Non-Threaded Connection] [Add a Detonation Interaction Callback >>]


Project Files

DistributedSimulationDriver2B.h

/******************************************************************************
** Copyright (c) 2011 MAK Technologies, Inc.
** All rights reserved.
******************************************************************************/
#pragma once
#include <vlutil/vlRunnable.h>
#include <vlutil/vlThread.h>
class DtExerciseConn;
class DtThread;
class DtThreadStart;
namespace makVrv
{
namespace tutorialDistributedSimulation2B
{
class DistributedSimulationDriver : public DtDriver,
protected DtRunnable
{
public:
virtual const std::string& className() const;
DtExerciseConn* connection()
{
return myConnection;
}
protected:
virtual bool onStart();
virtual bool onStop();
virtual bool onTick();
virtual void run();
void startThread();
void stopThread();
protected:
DtExerciseConn* myConnection;
//Cross thread variables. They are used between the two threads for
//communication.
DtThreadStartSP myStart;
DtThread* myThread;
};
}
}

DistributedSimulationDriver2B.cxx

/******************************************************************************
** Copyright (c) 2014 MAK Technologies, Inc.
** All rights reserved.
******************************************************************************/
#define DtDIS 1
#include <vrvCore/DtDe.h>
#include <vl/exerciseConnInitializer.h>
#include <vl/exerciseConn.h>
namespace makVrv
{
namespace tutorialDistributedSimulation2B
{
DtAgentManager& agentManager, const std::string& instanceName)
: DtDriver(agentManager, instanceName)
, myConnection(0)
, myConnectionState(false)
, myThread(0)
{
}
DistributedSimulationDriver::~DistributedSimulationDriver(void)
{
stopThread();
delete myConnection;
myConnection = 0;
}
const std::string& DistributedSimulationDriver::className() const
{
// name must be static, since it's returned by reference
static std::string name("DistributedSimulationDriver");
return name;
}
bool DistributedSimulationDriver::onStart()
{
std::cout << "DistributedSimulationDriver::onStart(): " << std::endl;
DtExerciseConn::InitializationStatus status = DtExerciseConn::DtINIT_SUCCESS;
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);
try
{
myConnection = new DtExerciseConn(init, &status);
}
catch(...)
{
return false;
}
myConnectionState = (status == DtExerciseConn::DtINIT_SUCCESS);
if (myConnectionState)
{
startThread();
}
return myConnectionState;
}
bool DistributedSimulationDriver::onStop()
{
// NOTE: breakdown code goes here. Return false on bad status.
std::cout << "DistributedSimulationDriver::onStop(): " << std::endl;
stopThread();
delete myConnection;
myConnection = 0;
return true;
}
bool DistributedSimulationDriver::onTick()
{
// printout to show whether or not connected at the tick level
if (myConnectionState)
{
std::cout << ".";
}
else
std::cout << "x";
return myConnectionState;
}
void DistributedSimulationDriver::startThread()
{
if(myThread)
{
stopThread();
}
//Hold on to point since VR-Link doesn't delete it.
myStart = DtThreadStartSP(new DtThreadStart(this));
myThread = new DtThread(myStart);
if(myThread->start() != DtThreadSuccess)
{
DtTHROW_NEW(DtCorruptedState,"Unable to start Connection thread.");
}
}
void DistributedSimulationDriver::stopThread()
{
if(myThread)
{
myThread->stop(false);
DtYield();
//Problem the thread might be attempting to lock the IG.
//Solution until the thread stops, service lock attempts.
while(myThread->isRunning())
{
myAgentManager.checkForIgLock(true);
DtYield();
}
delete myThread;
myThread = 0;
myStart.reset();
}
}
void DistributedSimulationDriver::run()
{
if(!myConnection)
{
// printout to show not connected at the run level
std::cout << " notconnected - not starting run loop ";
return;
}
// printout to show connected at the run level
std::cout << " connected - starting run loop ";
while(!timeToStop())
{
int numread = myConnection->drainInput();
// print to show running
std::cout << "r" << numread;
}
}
}
}

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

DistributedSimulationPlugin2B.cxx

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


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