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:
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);
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();
}
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()
{
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)
{
std::cout << " notconnected - not starting run loop ";
return;
}
std::cout << " connected - starting run loop ";
while(!timeToStop())
{
int numread = myConnection->drainInput();
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:
-
Choose File > View > Drivers Panel.
-
Select the driver named "DistributedSimulationDriver2B".
-
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. At this point, unlike Project 2A, these will all be 0.
-
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.
-
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 and byte counts 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.
[<< Create a Non-Threaded Connection] [Add a Detonation Interaction Callback >>]
Project Files
DistributedSimulationDriver2B.h
#pragma once
#include <vlutil/vlRunnable.h>
#include <vlutil/vlThread.h>
class DtExerciseConn;
class DtThread;
class DtThreadStart;
namespace makVrv
{
namespace tutorialDistributedSimulation2B
{
class DistributedSimulationDriver : public DtDriver,
{
public:
virtual const std::string&
className()
const;
{
}
protected:
protected:
};
}
}
DistributedSimulationDriver2B.cxx
#define DtDIS 1
#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
{
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);
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()
{
std::cout << "DistributedSimulationDriver::onStop(): " << std::endl;
stopThread();
delete myConnection;
myConnection = 0;
return true;
}
bool DistributedSimulationDriver::onTick()
{
if (myConnectionState)
{
std::cout << ".";
}
else
std::cout << "x";
return myConnectionState;
}
void DistributedSimulationDriver::startThread()
{
if(myThread)
{
stopThread();
}
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();
while(myThread->isRunning())
{
myAgentManager.checkForIgLock(true);
DtYield();
}
delete myThread;
myThread = 0;
myStart.reset();
}
}
void DistributedSimulationDriver::run()
{
if(!myConnection)
{
std::cout << " notconnected - not starting run loop ";
return;
}
std::cout << " connected - starting run loop ";
while(!timeToStop())
{
int numread = myConnection->drainInput();
std::cout << "r" << numread;
}
}
}
}
DistributedSimulationPlugin2B.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; }
DistributedSimulationPlugin2B.cxx
#include <boost/bind.hpp>
using namespace makVrv;
using namespace tutorialDistributedSimulation2B;
{
DistributedSimulationDriver* driver = new DistributedSimulationDriver(
}
{
{
}
}
{
return true;
}