Connection between the network and visualization threads of the application can be done in the following ways:
Using Agents to Communication Between Network and Visualization Threads
Agents are used to communicate from the network thread to the visualization thread. They will use a makVrv::DtMessage object to archive contents of a message to be put onto an agent message interface queue which is processed in the visualization thread every tick. Agents are created from ocdx files:
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
<!DOCTYPE boost_serialization>
<boost_serialization signature="serialization::archive" version="8">
<myClass class_id="0" tracking_level="1" version="4" object_id="_0">
<myNamespace></myNamespace>
<myHeaderFile>"DtAttachableRangeLineTextUpdater.h"</myHeaderFile>
<myInheritedFlag>1</myInheritedFlag>
<mySuperClassName>DtAttachableUpdater</mySuperClassName>
<mySuperClassNamespace>makVrv</mySuperClassNamespace>
<mySuperClassHeaderFile>vrvCore/DtAttachableUpdater</mySuperClassHeaderFile>
<myStates class_id="1" tracking_level="0" version="0">
<count>2</count>
<item_version>3</item_version>
<item class_id="2" tracking_level="0" version="3">
<myName>setComputeToNextPoint</myName>
<myArguments class_id="3" tracking_level="0" version="0">
<count>1</count>
<item_version>2</item_version>
<item class_id="4" tracking_level="0" version="2">
<myType>bool</myType>
<myName>enabled</myName>
</item>
</myArguments>
<myComments></myComments>
<myVirtualFlag>1</myVirtualFlag>
</item>
<item>
<myName>setParentId</myName>
<myArguments>
<count>1</count>
<item_version>2</item_version>
<item>
<myName>parentId</myName>
</item>
</myArguments>
<myComments></myComments>
<myVirtualFlag>1</myVirtualFlag>
</item>
</myStates>
<myDllExportMacro></myDllExportMacro>
<myDllExportHeaderFile></myDllExportHeaderFile>
<myProxyDllExportMacro></myProxyDllExportMacro>
<myProxyDllExportHeaderFile></myProxyDllExportHeaderFile>
<myHeaderCode>
</myHeaderCode>
<myEncodingCode></myEncodingCode>
<myIsAbstractFlag>0</myIsAbstractFlag>
</myClass>
</boost_serialization>
As part of the project generation process, the dcgen application willbe used to generate Agent and Class code from the ocdx file. The agent is what is created in network thread to provide API access to calls that will create makVrv::DtMessage to be processed:
myRangeLineTextUpdaterAgent = DtAttachableRangeLineTextUpdaterAgent::create(mySimulation.sceneInterface(), myDistributeFlag);
The Classes class is the implementation of the agent. This class will be responsible for the creation of the messages. When an Agent class is created, on the visualization size the receiver class needs to be implemented. The ocdx file will specify what this receiver class is called, and, it must contain all the methods that are defined by the agent:
#pragma once
{
public:
protected:
protected:
};
The code will then receive the data from the network thread such that is can be processed in the visualization thread:
#include <matrix/geodeticCoord.h>
#include <matrix/coordTransform.h>
#include <matrix/topoCoord.h>
using namespace makVrv;
using namespace makVrf;
using namespace makArchives;
#define DtALMOST(x, y, e) (((x)<=((y)+(e))) && ((x)>=((y)-(e))))
, myComputeToNextPoint(false)
, myParentId(0)
, myLastRange(0)
, mySourceIndex(-1)
, myStopChecking(false)
{
}
{
if (myDe.destructing())
{
}
}
{
}
{
DtAttachableUpdater::update( simTime );
{
return;
}
{
return;
}
for ( ; i != e; ++i )
{
if ( textModel )
{
std::vector<DtWidget*>& widgets = textModel->
widgets();
if ( widgets.size() > 0 )
{
if ( widgetLabel )
{
if (range < 0)
{
widgetLabel->
setText(DtUnicode::fromAscii(
""));
}
else
{
DtUnicode label = DtUnicode::fromAscii( altData );
label += DtUnicode::fromAscii(" ");
label += altNotation;
}
}
}
}
}
}
{
ObjectAttachedPoint*
attachedPoint = (ObjectAttachedPoint*) point;
if( resolver )
{
if ( sceneObject )
{
int pointCount = sceneObject->
transforms().size();
{
int i;
for (i = 0; i < pointCount; i++)
{
if (linePosition == vertexPosition)
{
}
else if (
DtALMOST(linePosition.x(), vertexPosition.x(), 0.00001) &&
DtALMOST(linePosition.y(), vertexPosition.y(), 0.00001) &&
DtALMOST(linePosition.z(), vertexPosition.z(), 0.00001))
{
break;
}
}
}
{
{
return -1;
}
{
return -1;
}
else
{
{
}
else
{
}
myDe.sharedState().coordinateSystem().localToNetPos(vertexPosition, vertexPosition);
myDe.sharedState().coordinateSystem().localToNetPos(linePosition, linePosition);
DtGeodeticCoord geod;
DtCoordTransform geocToTopo;
geod.setGeocentric(vertexPosition);
DtGeocToTopoTransform(geod.lat(), geod.lon(), &geocToTopo);
geocToTopo.coordTrans(linePosition, linePosition);
return sqrt(linePosition.magnitudeSquared());
}
}
}
}
return -1;
}
{
if (DtSelectionManager::instance(myDe).objectIsVertex(id))
{
id = DtSelectionManager::instance(myDe).parentId(id);
}
}
GUI Shared Signalers
Shared signalers are used to communicate from the visualization thread to the network thread. These use a variation of a boost signal (makVrv::event_signal) to add signals to a queue that can be processed in the network thread. This is how the visualization thread can communicate with the network thread to perform an action. One of the main signalers in use is the makVrf::DtSharedNetworkSignaler. This signaler is used to communicate with the makVrf::DtVrfConnectionHelper to perform operations on the network.
DtSharedNetworkSignaler::instance(DtSharedSettingsManager::instance(myDe)).signal_sendMessageToObject((
DtVrfObjectMessage*)m.clone(), addr);
The Network Callback Manager example shows how to create and use an event signaler:
#pragma once
namespace makVrv
{
class DtDe;
class DtSharedSettingsManager;
}
{
public:
protected:
private:
};
The visualization thread uses this event signaler to post a signal to the network thread:
The network thread accessory then connects to that signal to process the contents during the network thread tick:
{
(connection);
if(vrlCon)
{
DtCommentInteraction testPdu;
std::string message =
"Sent from " +
myName;
testPdu.setComment(message.c_str(), message.length() + 1);
#if DtHLA
#endif
}
}
If event signals send parameters as part of the signal, you need to make sure these parameters can be placed on the stack. Sending temporary variables in the event signal will cause a crash when that signal goes out of scope.
Shared Read and Write Locks
Shared settings (subclasses of makVrv::DtSharedSettings) are used to communicate data in a shared, thread safeway from one thread to another. These, in conjunction with signalers, can be used to store complex data that can be written to in on thread and accessed in another. One example of this is the makVrf::DtSharedVrfVrlApplicationSettings class. This class stores data that is used between the visualization and network thread. These settings classes, in conjunction with event signalers, can be used to indicate when the status of a shared value has changed. For example, the way that the visualization thread and the network thread synchronize on start up is by this class. The setApplicationInitialized method is used to indication when the visualization thread has been initialized and the network thread can continue operation. Once the visualation thread is ready, it will get a makVrv::DtWriteSettingsLock on the application settings:
{
settings->setGumballsDirectory(de().dePathConfiguration().dataPath() + "/images/icons/");
settings->setApplicationInitialized(true);
}
The network thread, each tick, checks to see if this variable has been set. If it has been, it will continue on with network processing:
if (settings->applicationInitialized())
{
start = true;
}
Network Message Agent
The Network Message Interface Handler is an agent class used in the network thread to wrap protocol independent DtSimInterfaceContent messages and pass them to the main thread. The makVrf::DtNetworkMessageInterface base class is used to wrap an number of protocol messages in a protocol independent way so the visualization thread can be notified of them. The messageInterfaces.h class contains the majority of these interfaces with makVrf::DtSimMessageInterface and makVrf::DtVrfObjectMessageInterface being most commonly used. The makVrf::DtNetworkMessageInterfaceHandler is the main thread representation of this agent. The messageReceived message will be called on the visual side and then pass it to the makVrf::DtGuiThreadNetworkCallbackManager. Once received, the makVrf::DtGuiThreadNetworkCallbackManager will use the name() method to figure out, by callbacks registered, what method to call
networkInterfaceMessageSignal(DtSimMessageInterface::theName()).connect(
boost::bind(&DtGuiThreadNetworkCallbackManager::processSimMessageCallback,
this, _1));
networkInterfaceMessageSignal(DtVrfObjectMessageInterface::theName()).connect(
boost::bind(&DtGuiThreadNetworkCallbackManager::processVrfObjectMessageInterfaceCallback,
this, _1));
When this message is received from the agent, methods that were registered to be called when an interface message of a certain type is received will be executed
boost::bind(&DtVrfSensorInfoManager::processSensorData,
this, _1));
void DtVrfSensorInfoManager::processSensorData(
DtSimMessage* msg)
{
boost::bind(&DtVrfSensorInfoManager::processSensorData,
this, _1));
The following convenience macros (in guiThreadNetworkManager.h) are available:
#define CONNECT_FOR_SIM_MESSAGE(_type, _function) makVrf::DtGuiThreadNetworkCallbackManager::instance(myDe).simMessageSignal(_type).connect(_function);
#define DISCONNECT_FOR_SIM_MESSAGE(_type, _function) makVrf::DtGuiThreadNetworkCallbackManager::instance(myDe).simMessageSignal(_type).disconnect(_function);
#define CONNECT_FOR_OBJECT_MESSAGE(_type, _function) makVrf::DtGuiThreadNetworkCallbackManager::instance(myDe).objectMessageSignal(_type).connect(_function);
#define DISCONNECT_FOR_OBJECT_MESSAGE(_type, _function) makVrf::DtGuiThreadNetworkCallbackManager::instance(myDe).objectMessageSignal(_type).disconnect(_function);
#define CONNECT_FOR_COMMENT_INTERACTION(_function) makVrf::DtGuiThreadNetworkCallbackManager::instance(myDe).networkInterfaceMessageSignal(DtCommentInteractionInterface::theName()).connect(_function);
#define DISCONNECT_FOR_COMMENT_INTERACTION(_function) makVrf::DtGuiThreadNetworkCallbackManager::instance(myDe).networkInterfaceMessageSignal(DtCommentInteractionInterface::theName()).disconnect(_function);
#define CONNECT_FOR_INFLUENCE_INTERACTION(_function) makVrf::DtGuiThreadNetworkCallbackManager::instance(myDe).networkInterfaceMessageSignal(DtInfluenceInteractionInterface::theName()).connect(_function);
#define DISCONNECT_FOR_INFLUENCE_INTERACTION(_function) makVrf::DtGuiThreadNetworkCallbackManager::instance(myDe).networkInterfaceMessageSignal(DtInfluenceInteractionInterface::theName()).disconnect(_function);
#define CONNECT_FOR_RADIO_MESSAGE(_type, _function) makVrf::DtGuiThreadNetworkCallbackManager::instance(myDe).radioMessageSignal(_type).connect(_function);
#define DISCONNECT_FOR_RADIO_MESSAGE(_type, _function) makVrf::DtGuiThreadNetworkCallbackManager::instance(myDe).radioMessageSignal(_type).disconnect(_function);
Also see The Network Callback Manager see Display Metrics for an example of how to connect to a radio message