![]() |
VR-Vantage 1.4.1 API Class Documentation
|
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.
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; DtExerciseConnInitializer init; 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 = 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() { 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; delete myStart; myStart = 0; } }
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:
#include <vrvCore/DtAgentManager.h>
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:
Invoke either the tutorialDistributedSimulation2Bd.bat or tutorialDistributedSimulation2B.sh script file to start the VR-Vantage Stealth application. Follow these instructions to test the plugin:
[<< Create a Non-Threaded Connection] [Add a Detonation Interaction Callback >>]
/****************************************************************************** ** Copyright (c) 2011 MAK Technologies, Inc. ** All rights reserved. ******************************************************************************/ #pragma once #include <vrvCore/DtDriver.h> #include <vlutil/vlRunnable.h> class DtExerciseConn; class DtThread; class DtThreadStart; namespace makVrv { namespace tutorialDistributedSimulation2B { class DistributedSimulationDriver : public DtDriver, protected DtRunnable { public: DistributedSimulationDriver(DtAgentManager& agentManager, const std::string& instanceName); virtual ~DistributedSimulationDriver(); 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; bool myConnectionState; //Cross thread variables. They are used between the two threads for //communication. DtThreadStart* myStart; DtThread* myThread; }; } }
/****************************************************************************** ** Copyright (c) 2011 MAK Technologies, Inc. ** All rights reserved. ******************************************************************************/ #define DtDIS 1 #include "DistributedSimulationDriver2B.h" #include <vrvCore/DtDe.h> #include <vrvCore/DtAgentManager.h> #include <vl/exConnInit.h> namespace makVrv { namespace tutorialDistributedSimulation2B { DistributedSimulationDriver::DistributedSimulationDriver( DtAgentManager& agentManager, const std::string& instanceName) : DtDriver(agentManager, instanceName) , DtRunnable() , myConnection(0) , myConnectionState(false) , myStart(0) , 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; DtExerciseConnInitializer init; 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 = 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; delete myStart; myStart = 0; } } 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; } } } }
/****************************************************************************** ** 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)) #include <vrvCore/exportPlugin.h> namespace makVrv { class DtDe; } // Work function for the plugin initialization void init(makVrv::DtDe& de); // Callback to create the stateViewDriver void createDistributedSimDriver(makVrv::DtDe* de);
/****************************************************************************** ** Copyright (c) 2011 MAK Technologies, Inc. ** All rights reserved. ******************************************************************************/ #include "DistributedSimulationPlugin2B.h" #include "DistributedSimulationDriver2B.h" #include <vrvCore/DtDe.h> #include <vrvCore/DtDriverManager.h> #include <boost/bind.hpp> using namespace makVrv; using namespace tutorialDistributedSimulation2B; void createDistributedSimDriver(DtDe* de) { // We're done with the signal, so disconnect from it de->signal_postInitialize.disconnect(boost::bind( &createDistributedSimDriver, de)); // 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) DT_DE_INIT_ONCE(de); // 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. de.signal_postInitialize.connect(boost::bind( &createDistributedSimDriver, &de)); } } bool initDeModule(makVrv::DtDe* de) { // Setup the plugin. Normally, the init function and functionality // should be in its own library. init(*de); return true; }