VR-Forces Developer's Guide
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Groups Pages
exampleElementAttributesPlugin

Table of Contents

Overview

Builds an index of element ID's to (DIS/RPR FOM) marking text.

Expected Result

exampleElementAttributesPlugin.PNG
Element Attribute Monitor Result

Example details

The DtDataBank contains simulation-specific and element-specific data elements. However there is substantial overlap - VR-Link driver's Marking Text is an element (that comes from that driver) Display Name. Element Data is, for the most part, static, and created when the Element itself is created. Simulation Data, on the other hand, contains both static and dynamic data.

The element data is easier to work with and faster/cheaper to retrieve. In addition to the the already existing Display Name attribute, there is now a Connection ID attribute and a Connection Entity Type attribute. These are whatever a particular connection uses for an ID and entity type, in string form. For example, an element from a DIS connection might have a connection ID of "3001:1:1" and a connection entity type of "1:225:1:1:1:0:0". An element created by a CIGI connection, on the other hand, might have an connection ID of "5" and a connection Entity Type of "17".

It is common for users who are used to working in a DIS or HLA environment to want to look up elements by a connection ID or Marking Text, for example. Connection objects makes that much easier to do.

The exampleElementAttributesPlugin example shows the signal that is raised with all the elements whose attribute data has been received that frame; elements can be added to indices or sets based on attribute data at that time. It builds an index of element IDs to Marking Text (Display Name) and vice versa. The example can easily be extended to build similar indices using connection ID attributes.

Building the Example

VR-Vantage includes pre-built versions of the example plug-in. To build it yourself, follow the instructions at Building VR-Vantage Examples, Applications, and Plug-ins.

Running the Example

This example is a plug-in. You can run it by running ./bin64/exampleElementAttributesPlugin_stealth.bat (on Windows) or ./bin64/exampleElementAttributesPlugin_stealth.sh (on Linux). Load the Ala Moana terrain, once Vantage is started, connect with DIS. Open MAK DIS Logger and load "HawaiiTour-2019-DIS.lgr". Hit Play and you will see the connection information printed in the console. For more information about running examples, please see Running Applications and Examples.

Example Source Files


exampleElementAttributesPlugin.h

/******************************************************************************
** Copyright (c) 2019 MAK Technologies, Inc.
** All rights reserved.
******************************************************************************/
#pragma once
// Get proper local export symbol
#ifdef _WIN32
#ifdef EXAMPLEELEMENTATTRIBUTESPLUGIN_EXPORTS
#define DT_DLL_EXAMPLEELEMENTATTRIBUTESPLUGIN __declspec ( dllexport )
#else
#define DT_DLL_EXAMPLEELEMENTATTRIBUTESPLUGIN __declspec ( dllimport )
#endif
#else
#define DT_DLL_EXAMPLEELEMENTATTRIBUTESPLUGIN
#endif
// Setup proper plugin export symbol
#define DT_DE_PLUGIN_EXPORT_MACRO DT_DLL_EXAMPLEELEMENTATTRIBUTESPLUGIN
// Declare & export the plugin initialization function (bool initDeModule(DtDe* de))
namespace makVrv
{
class DtDe;
}
// Work function for the plugin initialization
// Callback to create the attribute monitor and register it with the DtDe

exampleElementAttributesPlugin.cxx

/*****************************************************************************
* Copyright (c) 2024 MAK Technologies, Inc.
* All rights reserved.
*****************************************************************************/
#include <vrvCore/DtDe.h>
using namespace makVrv;
static vrvSignalsLib::connection postInitializeConnection;
{
// We're done with the signal, so disconnect from it
postInitializeConnection.disconnect();
// By registering the instance with the DtDe, it will delete our monitor on exit
de.registerInstance("AttributeMonitor", monitor);
// Set up a key command to dump all named elements
DtInputDriver& inputDriver = de.driverManager().inputDriver();
keyMapManager.addKeyFunction("Dump Elements", boost::bind(
// Bind the above key function to the 'f' key in the default
// ("Observer Frame") key map
DtKeyMap* obsFrame = keyMapManager.findKeyMap("Observer Frame");
if (obsFrame)
{
inputDriver.addKeyBinding(obsFrame, DtKeyState::KEY_X, "Dump Elements");
}
}
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.
postInitializeConnection = de.signal_postInitialize.connect(boost::bind(&createMonitor, std::ref(de)));
}
}
{
// Setup the plug-in. Normally, the init function and functionality
// should be in its own library.
init(*de);
return true;
}

DtElementAttributesMonitor.h

/*****************************************************************************
* Copyright (c) 2024 MAK Technologies, Inc.
* All rights reserved.
*****************************************************************************/
#pragma once
#include <string>
namespace makVrv
{
class DT_DLL_EXAMPLEELEMENTATTRIBUTESPLUGIN DtElementAttributesMonitor : public DtVirtualBaseClass
{
public:
DtElementAttributesMonitor(DtDe& de);
virtual ~DtElementAttributesMonitor();
virtual void processElementInitialized(makVrv::DtElementID entity);
virtual void processElementRemoved(makVrv::DtElementID entity);
virtual void dumpElements();
static std::string elementTypeToString(DtElementEntry::ElementType elementType);
static std::string elementSubTypeToString(DtElementAttributes::ElementSubtypes subType);
protected:
virtual void slot_elementsInitialized(const DtElementData::ElementIdList& elements);
virtual void slot_elementsRemoved(const DtElementData::ElementIdList& elements);
virtual void slot_attributesChanged(const DtElementAttributeManager::EntryAttributesList& entryAttributeList);
protected:
DtDe& myDe;
DtSignalConnectionManager mySignalConnections;
};
}

DtElementAttributesMonitor.cxx

/*****************************************************************************
* Copyright (c) 2024 MAK Technologies, Inc.
* All rights reserved.
*****************************************************************************/
#include <vrvCore/DtDe.h>
#include <iostream>
// Use the VR-Vantage namespace.
// All classes in VR-Vantage are in this namespace.
namespace makVrv
{
//
// Custom Element Attribute Definition
// This custom element attribute is reported by exampleCustomElementAttributePlugin{protocol} example
//
// enumeration of unique ids for custom element attributes
enum class CustomElementAttributeIds : unsigned short
{
};
// create a new type of string-based element attribute, called DtElementAttributeEntityLabel, assigned to the 'EntityLabel' id
typedef DtUniqueElementAttribute<std::string, (unsigned short)CustomElementAttributeIds::EntityLabel> DtElementAttributeEntityLabel;
//
// Simple example of using the type and attribute data in the ElementData class
//
: DtVirtualBaseClass()
, myDe(de)
{
DtElementData& elements = myDe.dataBank().elementData();
// By using the signal connection manager to hold our signal connections, we do not
// need to disconnect them in the destructor
mySignalConnections += elements.signal_elementsInitialized.connect(boost::bind(
&DtElementAttributesMonitor::slot_elementsInitialized, this, boost::placeholders::_1));
mySignalConnections += elements.signal_elementsToBeRemoved.connect(boost::bind(
&DtElementAttributesMonitor::slot_elementsRemoved, this, boost::placeholders::_1));
mySignalConnections += elements.attributeManager().signal_attributesChanged.connect(std::bind(
&DtElementAttributesMonitor::slot_attributesChanged, this, std::placeholders::_1));
}
DtElementAttributesMonitor::~DtElementAttributesMonitor()
{
// intentional noop
}
void DtElementAttributesMonitor::processElementInitialized(DtElementID entity)
{
const DtElementAttributeManager& attrMgr = myDe.dataBank().elementData().attributeManager();
const DtElementEntry* entry = myDe.dataBank().elementData().findElement(entity);
if (!entry)
{
std::string displayName = "Unnamed";
attrMgr.getElementDisplayName(entity, displayName);
std::cout << "Invalid Element ID received: " << std::hex << entity << " - "
<< " not found" << std::endl;
return;
}
std::cout << "Added ";
std::string displayName;
if (attrMgr.getElementDisplayName(*entry, displayName))
{
std::cout << displayName;
}
else
{
std::cout << "Unnamed (" << std::hex << entity << ")";
}
std::string driverType;
if (attrMgr.getElementDriverType(*entry, driverType))
{
std::cout << " from " << driverType << std::endl;
}
else
{
std::cout << std::endl;
}
DtElementEntry::ElementType elementType = attrMgr.elementType(*entry);
std::string elementTypeStr = elementTypeToString(elementType);
std::cout << " Element Type " << elementTypeStr << std::endl;
std::string objectId;
if (attrMgr.getElementObjectId(*entry, objectId))
{
std::cout << " Connection ID " << objectId << std::endl;
}
std::string objectType;
if (attrMgr.getElementObjectType(*entry, objectType))
{
std::cout << " Connection Type " << objectType << std::endl;
}
DtElementAttributes::ElementSubtypes subtype;
if (attrMgr.getElementSubtype(*entry, subtype))
{
std::string subtypeStr = elementSubTypeToString(subtype);
std::cout << " Subtype " << subtypeStr << std::endl;
}
DtUnicode elementDefName;
if (attrMgr.getElementElementDefinitionName(*entry, elementDefName))
{
std::string elementDefStr;
elementDefName.toAscii(elementDefStr);
std::cout << " Element Definition " << elementDefStr << std::endl;
}
// Each frame the entity may be updated (either from the network, dead reckoning, or some other
// 'tickable' updater. If you want to access the entity in your driver AFTER these updates, you can
// listen for signal_postUpdate. Here is an example that would print out the entity's new position,
// but left commented out since this will generate a LOT of console output.
// myAgentManager.de().renderer().signal_postUpdate.connect(
// boost::bind( &DtConnectionObjectsDriver::slot_postUpdate, this, entity ) );
}
void DtElementAttributesMonitor::processElementRemoved(DtElementID entity)
{
// Disconnect this entity from the postUpdate calls if you uncommented the code above to
// connect it...
// myAgentManager.de().renderer().signal_postUpdate.disconnect(
// boost::bind( &DtConnectionObjectsDriver::slot_postUpdate, this, entity ) );
const DtElementAttributeManager& attrMgr = myDe.dataBank().elementData().attributeManager();
// At this point, you might want to insert this information into a class that can look up
// everything else by marking text, by element ID, etc...
std::cout << "Removed " << std::hex << entity << " - ";
std::string displayName;
if (attrMgr.getElementDisplayName(entity, displayName))
{
std::cout << displayName << std::endl;
}
else
{
DtElementEntry::ElementType elementType = attrMgr.elementType(entity);
std::string elementTypeStr = elementTypeToString(elementType);
std::cout << "Unnamed " << elementTypeStr << " (" << entity << ")" << std::endl;
}
}
void DtElementAttributesMonitor::slot_elementsInitialized(const DtElementData::ElementIdList& elements)
{
DtElementData::ElementIdList::const_iterator curIter = elements.begin();
DtElementData::ElementIdList::const_iterator endIter = elements.end();
for (; curIter != endIter; ++curIter)
{
processElementInitialized(*curIter);
}
}
void DtElementAttributesMonitor::slot_elementsRemoved(const DtElementData::ElementIdList& elements)
{
DtElementData::ElementIdList::const_iterator curIter = elements.begin();
DtElementData::ElementIdList::const_iterator endIter = elements.end();
for (; curIter != endIter; ++curIter)
{
// signal before actually removing the entries from the maps so the
// maps are still usable to receivers of the signal
processElementRemoved(*curIter);
}
}
void DtElementAttributesMonitor::slot_attributesChanged(const DtElementAttributeManager::EntryAttributesList& entryAttributeList)
{
const DtElementAttributeManager& attrMgr = myDe.dataBank().elementData().attributeManager();
for (const DtElementAttributeManager::EntryAttributes& entryAttributes : entryAttributeList)
{
const DtElementID entryElementId = entryAttributes.first->elementID();
for (const DtElementAttribute* currentAttribute : entryAttributes.second)
{
const DtElementAttribute* attribute = currentAttribute;
if (attribute->attributeId() == (unsigned short)CustomElementAttributeIds::EntityLabel)
{
std::cout << "Modified ";
std::string displayName;
if (attrMgr.getElementDisplayName(entryElementId, displayName))
{
std::cout << displayName << std::endl;
}
else
{
DtElementEntry::ElementType elementType = attrMgr.elementType(entryElementId);
std::string elementTypeStr = elementTypeToString(elementType);
std::cout << "Unnamed " << elementTypeStr << " (" << entryElementId << ")" << std::endl;
}
const DtElementAttributeEntityLabel* entityLabelIdAttr =
dynamic_cast<const DtElementAttributeEntityLabel*>(entryAttributes.first->findAttribute((unsigned short)CustomElementAttributeIds::EntityLabel));
if (entityLabelIdAttr != nullptr)
{
std::cout << " Element Label " << entityLabelIdAttr->value() << std::endl;
}
}
}
}
}
void DtElementAttributesMonitor::dumpElements()
{
const DtElementAttributeManager& attrMgr = myDe.dataBank().elementData().attributeManager();
DtElementID elementId;
std::string elementName;
attrMgr.startNamedElementIteration();
while (attrMgr.getNextElementIdAndDisplayName(elementId, elementName))
{
std::cout << "ID: " << std::hex << elementId << " NAME: " << elementName << std::endl;
}
}
std::string DtElementAttributesMonitor::elementTypeToString(DtElementEntry::ElementType elementType)
{
std::string elementTypeStr;
switch (elementType)
{
case DtElementEntry::Unknown:
default:
elementTypeStr = "Unknown";
break;
elementTypeStr = "Entity";
break;
elementTypeStr = "Prop";
break;
case DtElementEntry::TerrainPatch:
elementTypeStr = "Terrain Patch";
break;
case DtElementEntry::Aggregate:
elementTypeStr = "Aggregate";
break;
case DtElementEntry::Observer:
elementTypeStr = "Observer";
break;
case DtElementEntry::Environmental:
elementTypeStr = "Environmental";
break;
case DtElementEntry::GraphicPoint:
elementTypeStr = "Graphic Point";
break;
case DtElementEntry::GraphicShape:
elementTypeStr = "Graphic Shape";
break;
case DtElementEntry::GraphicVertex:
elementTypeStr = "Graphic Vertex";
break;
case DtElementEntry::RemoteGraphic:
elementTypeStr = "Remote Graphic";
break;
case DtElementEntry::TacticalGraphic:
elementTypeStr = "Tactical Graphic";
break;
case DtElementEntry::Intervisibility:
elementTypeStr = "Intervisibility";
break;
case DtElementEntry::Feature:
elementTypeStr = "Feature";
break;
case DtElementEntry::TacticalSmoke:
elementTypeStr = "Tactical Smoke";
break;
case DtElementEntry::BuoyOrBeacon:
elementTypeStr = "Bouy Or Beacon";
break;
case DtElementEntry::DynamicTerrain:
elementTypeStr = "Dynamic Terrain";
break;
}
return elementTypeStr;
}
std::string DtElementAttributesMonitor::elementSubTypeToString(DtElementAttributes::ElementSubtypes subType)
{
std::string subtypeStr;
switch (subType)
{
case DtElementAttributes::FriendlyMunitions:
case DtElementAttributes::OpposingMunitions:
case DtElementAttributes::NeutralMunitions:
case DtElementAttributes::OtherMunitions:
subtypeStr = "Munitions";
break;
case DtElementAttributes::Friendly:
subtypeStr = "Friendly";
break;
case DtElementAttributes::Opposing:
subtypeStr = "Opposing";
break;
case DtElementAttributes::Other:
subtypeStr = "Other";
break;
case DtElementAttributes::Neutral:
subtypeStr = "Neutral";
break;
default:
subtypeStr = "Unrecognized!";
break;
}
return subtypeStr;
}
}

[<< Examples] [Home] [Top of Page]


Document ID: Generated on Thu Oct 23 22:29:17 EDT 2025 from SVN revision 280951
Copyright © 2005-2024 MAK Technologies. All Rights Reserved (www.mak.com)