VR-Link API Documentation for HLA Evolved
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
10.9 - Laser Designator Example

Table of Contents

The included example is the complete source code for a plug-in that adds support for Laser Designators in DIS and HLA.

It uses the C++ VR-Link toolkit and the VR-Link C# library.

10.9.1 Project Description and Setup

The project files for the example are broken down into five projects. One project for the protocol independent message representations to be loaded into the message factory, then 4 protocol project to create plug-ins that load a protocol specific strategy into the strategy factory and registers it with the initialization strategy so that it gets created on connection. Since VR-Link is protocol independent and uses compiler flags to determine which protocol to support, most of the source code for the example is shared between the 4 protocol specific projects. Changes to files in project will show up as changes in other projects.

Since the project files are configured to use environment variables to find the C++ VR-Link SDK, it is recommended to edit the included setupEnvironment.bat file to match your system and use it to launch Visual Studio.

10.9.2 Laser Designator Messages

Laser designators are stateful objects within DIS and HLA and change their state when painting a target. This means that we will need a discovery, state update, and removed message to inform VR-Link C# about laser designators.

These three messages are defined in laser.lua. Two things to note: entity ID’s are transferred to VR-Link C# as strings and all positions are vectors in VR-Link C#.

10.9.3 Laser Designator Plugin

The Laser Designator plug-in loads the laser designator messages into the message factory in its initManagedInterfaceModule() function:

bool initManagedInterfaceModule( DtManagedInterface* gl)
{
//add the message types to the factory so that ManagedInterface knows how to decode them
gl->messageFactory().registerCreator(LaserDesignatorDiscoveryMessage::theMessageName(), LaserDesignatorDiscoveryMessage::decode);
gl->messageFactory().registerCreator(LaserDesignatorRemovedMessage::theMessageName(), LaserDesignatorRemovedMessage::decode);
gl->messageFactory().registerCreator(LaserDesignatorStateMessage::theMessageName(), LaserDesignatorStateMessage::decode);
return true;
}

10.9.4 Laser Designator Protocol Specific Plug-ins

The protocol specific laser designator plug-ins load their specific strategy into the strategy factory and register with the initialization strategy to be loaded during connection.

bool initManagedInterfaceModule( DtManagedInterface* gl)
{
//add the strategies to the factory
//this is ok to do multiple times since it won't damage anything/or leak resources
gl->addStrategyCreator(DtLaserDesignatorStrategy::theName(), DtLaserDesignatorStrategy::create);
//add the strategy to the correct init strategy so that it gets created during initialization
DtInitStrategy* initStr=dynamic_cast<DtInitStrategy*>(gl->findStrategy(DtInitStrategy::theName()));
if(initStr!=NULL)
{
initStr->addInitChild(DtLaserDesignatorStrategy::theName());
}
else
{
DtWarn << "Unable to register Laser strategy with the Initialization strategy for DIS protocol" << std::endl;
}
return true;
}

10.9.5 Laser Designator Strategy

When the laser designator strategy is created, it registers interest in laser object messages with ManagedInterface. It does this by wrapping its handler member functions with a delegate and passing this delegate to ManagedInterface:

myDesignatorDiscoveredHandler=new DtMessageDelegate(this, &DtLaserDesignatorStrategy::handleUnityDesignatorDiscovered);
myDesignatorRemovedHandler=new DtMessageDelegate(this, &DtLaserDesignatorStrategy::handleUnityDesignatorRemoved);
myDesignatorStateHandler=new DtMessageDelegate(this, &DtLaserDesignatorStrategy::handleUnityDesignatorState);
myManagedInterface->addHandler(LaserDesignatorDiscoveryMessage::theMessageName(), myDesignatorDiscoveredHandler);
myManagedInterface->addHandler(LaserDesignatorRemovedMessage::theMessageName(), myDesignatorRemovedHandler);
myManagedInterface->addHandler(LaserDesignatorStateMessage::theMessageName(), myDesignatorStateHandler);

It is important to remember to remove these delegates from ManagedInterface during shutdown so that they do not get called after the strategy is destroyed.

myManagedInterface->removeHandler(LaserDesignatorDiscoveryMessage::theMessageName(), myDesignatorDiscoveredHandler);
myManagedInterface->removeHandler(LaserDesignatorRemovedMessage::theMessageName(), myDesignatorRemovedHandler);
myManagedInterface->removeHandler(LaserDesignatorStateMessage::theMessageName(), myDesignatorStateHandler);

The initialization function of the laser designator strategy finds the exercise connection shared resource, creates a reflected laser designator list, and installs some callbacks on it to listen for laser designators coming from the DIS/HLA network. It also creates a laser designator publisher list to hold publishers that it creates to mirror laser designators being controlled in VR-Link C#.

bool DtLaserDesignatorStrategy::init()
{
if(myInitalized==true) return true;
myExconn=findExConnResource();
if(myExconn!=NULL)
{
//radio Designators
myRelDx=new DtReflectedDesignatorList(myExconn);
myRelDx->addDesignatorAdditionCallback(&DtLaserDesignatorStrategy::vrlDesignatorDiscovered, this);
myRelDx->addDesignatorRemovalCallback(&DtLaserDesignatorStrategy::vrlDesignatorRemoved, this);
myManagedInterface->setResource("REFLECTED_DESIGNATOR_LIST", myRelDx);
myManagedInterface->setResource("DESIGNATOR_PUBLISHER_LIST", myDxPubs);
myInitalized=true;
DtDebug << "Laser Designator strategy initialized" << std::endl;
}
return myInitalized;
}

Since this initialization function will be run more than once, it protects itself against being initialized more than once. In the event it cannot get the shared resource exercise connection, it may return false, causing it to be run again until is able to get the shared resource and fully initialize.

During the tick function, which is called every frame, the Laser Designator strategy updates any laser designator publishers that it created to mirror laser designators from VR-Link C# to the DIS/HLA network.

myDxPubs->tick();

The strategy contains several callbacks for registering with VR-Link as well as with the ManagedInterface message handlers to handle discovery, removal and state update of laser designators from both the DIS/HLA network and within VR-Link C#. These callbacks marshal the data to and from the laser designator messages and VR-Link data structures ensuring to convert entity names and terrain coordinates using the conversion routines in the initialization strategy and terrain strategy respectively.

pub->dsr()->setDesignatedObject(DtInitStrategy::unityToVrlinkId(dxMsg->getTargetId()));

10.9.6 Laser Designators in C#

There are a few things that need to be defined in C# to now receive and send laser designators. The first step is to define a .lua file that will output a state message, a discovery message, and a removed message. This was covered in the message definition section. Here is an example of such:

MESSAGE{
fileName="laserDesignatorDiscovery";
className="LaserDesignatorDiscovery";
includes={"plugin.h"};
dllExport="LASER_DESIGNATOR_DLL";
attributes={
{type="string", name="entityId"};
{type="UInt16", name="designatorId"};
}
}
MESSAGE{
fileName="laserDesignatorRemoved";
className="LaserDesignatorRemoved";
includes={"plugin.h"};
dllExport="LASER_DESIGNATOR_DLL";
attributes={
{type="string", name="entityId"};
{type="UInt16", name="designatorId"};
}
}
MESSAGE{
fileName="laserDesignatorState";
className="LaserDesignatorState";
includes={"plugin.h"};
dllExport="LASER_DESIGNATOR_DLL";
enums={
CodeName=[[OTHER]];
DesignatorCode=[[OTHER]];
};
attributes={
{type="string", name="entityId"};
{type="enum", name="codeName", enum="CodeName"};
{type="string", name="targetId"};
{type="enum", name="designatorCode", enum="DesignatorCode"};
{type="float", name="power"};
{type="float", name="wavelength"};
{type="Vector3d", name="relativeDesignatorSpot", new=true};
{type="Vector3d", name="worldDesignatorSpot", new=true};
}
}

Of course, as we have learned from the message definition section, this will output C# files defining the messages we supplied. Next, we must define a state repository class for laser designators. The state repository contains the items that make up the object being reflected, in our case, a laser designator.

To define a laserStateRepository, we derive from the VR-Link C# library class StateRepository. The StateRepository class contains a map of class members to their name. They are stored as C# "object" classes with a string as the key. This allows the StateRepository to remain very flexible in its use. To define and set new variables in our laserDesignator, we use the setAttribute function in the constructor with some initial value to signify the type.

public laserDesignatorStateRepository()
: base()
{
setAttribute("entityId", "");
setAttribute("codeName", CodeName.OTHER);
setAttribute("targetId", "");
setAttribute("designatorCode", DesignatorCode.OTHER);
setAttribute("power", 0.0F);
setAttribute("wavelength", 0.0F);
setAttribute("relativeDesignatorSpot", Vector3d.Zero);
setAttribute("worldDesignatorSpot", Vector3d.Zero);
}

We use the getAttribute function to get an attribute from the repository, casting the return value to the type we used in setAttribute.

public string entityId
{
get { return (string)getAttribute("entityId"); }
set { setAttribute("entityId", value); }
}
public CodeName codeName
{
get { return (CodeName)getAttribute("codeName"); }
set { setAttribute("codeName", value); }
}

Lastly, we need to provide functions that update from a state message, and output a state message from current values. These will be very simple functions that assign the state repository values to their corresponding state message values, and vice versa.

internal void update(LaserDesignatorStateMessage sm)
{
//this shouldn't change
entityId = sm.entityId;
codeName = (makVrl.CodeName)sm.codeName;
targetId = sm.targetId;
designatorCode = (makVrl.DesignatorCode)sm.designatorCode;
power = sm.power;
wavelength = sm.wavelength;
relativeDesignatorSpot = sm.relativeDesignatorSpot;
worldDesignatorSpot = sm.worldDesignatorSpot;
}
internal LaserDesignatorStateMessage getStateMessage()
{
LaserDesignatorStateMessage sm = new LaserDesignatorStateMessage();
sm.entityId = entityId;
sm.codeName = (LaserDesignatorStateMessage.CodeName)codeName;
sm.targetId = targetId;
sm.designatorCode = (LaserDesignatorStateMessage.DesignatorCode)designatorCode;
sm.power = power;
sm.wavelength = wavelength;
sm.relativeDesignatorSpot = relativeDesignatorSpot;
sm.worldDesignatorSpot = worldDesignatorSpot;
return sm;
}

Next, we will create the reflected object that will be populated when a remote laser designator is discovered. The reflected object class will contain the state repository we just defined, as well as a constructor as well as a few wrappers around some of the state repository functions.

public class ReflectedLaserDesignator
{
laserDesignatorStateRepository myLsr = new laserDesignatorStateRepository();
public ReflectedLaserDesignator(string id)
{
myLsr.entityId = id;
}
public laserDesignatorStateRepository esr { get { return myLsr; } }
internal void update(LaserDesignatorStateMessage sm)
{
myLsr.update(sm);
}
}

Now that we have a reflected object, we can define our reflected object list. The reflected object lists are where the callbacks for discovery, update, and removed are registered and handled. In other words, to receive incoming laser designators, you would construct the ReflectedLaserDesignatorList. The reflected list needs an exercise connection to be constructed, and will also create two maps of strings to reflected objects (entityIds to ReflectedLaserDesignators). These two maps handle undiscovered lasers and discovered lasers.

In the constructor, we will set the exercise connection to what was given, and register our callbacks with that exercise connection.

public class ReflectedLaserDesignatorList
{
ExerciseConnection myExConn;
Dictionary<String, ReflectedLaserDesignator> myUnprocessedLasers = new Dictionary<String, ReflectedLaserDesignator>();
Dictionary<String, ReflectedLaserDesignator> myLasers = new Dictionary<String, ReflectedLaserDesignator>();
public ReflectedLaserDesignatorList(ExerciseConnection exConn)
{
myExConn = exConn;
myExConn.addMessageHandler(LaserDesignatorDiscoveryMessage.theName, new MessageDelegate(handleLaserDesignatorDiscoveredMessage));
myExConn.addMessageHandler(LaserDesignatorRemovedMessage.theName, new MessageDelegate(handleLaserDesignatorRemovedMessage));
myExConn.addMessageHandler(LaserDesignatorStateMessage.theName, new MessageDelegate(handleLaserDesignatorUpdatedMessage));
}

We then define some delegates to handle anything specific we want to happen when our callbacks are reached.

public delegate void LaserDesignatorDiscoveredCallback(ReflectedLaserDesignator laser);
public delegate void LaserDesignatorRemovedCallback(ReflectedLaserDesignator laser);
public delegate bool DiscoveryCondition(ReflectedLaserDesignator laser);
public LaserDesignatorDiscoveredCallback laserDiscovered;
public LaserDesignatorRemovedCallback laserRemoved;
public DiscoveryCondition discoveryCondition;

Lastly, we define the callback functions that we registered with the exercise connection. Here are a few examples.

internal void handleLaserDesignatorDiscoveredMessage(Message m)
{
LaserDesignatorDiscoveryMessage dm = m as LaserDesignatorDiscoveryMessage;
ReflectedLaserDesignator laser = new ReflectedLaserDesignator(dm.entityId);
myUnprocessedLasers[dm.entityId] = laser;
}
internal void handleLaserDesignatorUpdatedMessage(Message m)
{
LaserDesignatorStateMessage sm = m as LaserDesignatorStateMessage;
ReflectedLaserDesignator laser = null;
if (myLasers.TryGetValue(sm.entityId, out laser) == true)
{
laser.update(sm);
}
else
{
//waiting for an update so we can be discovered
if (myUnprocessedLasers.ContainsKey(sm.entityId) == true)
{
laser = myUnprocessedLasers[sm.entityId];
}
else
{
//need to discover the entity
laser = new ReflectedLaserDesignator(sm.entityId);
}
laser.update(sm);
discoverLasers(laser);
}
}

As you can see, there are some helper functions to do some of the work required after a callback. This is typically how callbacks are handled, but once you have entered your callback, you can handle the information any way you would like. At this point we have all that we need to receive a laser designator from VR-Link, but we have not handled publishing our VR-Link C# laser designators to VR-Link. To achieve this, we define a publisher class – laserDesignatorPublisher.

This class will contain an exercise connection to publish on and a laser state repository that has the information we want to publish.

public class laserDesignatorPublisher
{
ExerciseConnection myExConn;
laserDesignatorStateRepository mylsr = new laserDesignatorStateRepository();
public laserDesignatorPublisher(ExerciseConnection exConn)
{
myExConn = exConn;
}
}

The actual publishing is handled in the tick function. The tick function is typically called on every simulation "tick" or update. Inside our publisher tick function, we will create a state message from the current information in our state repository and then use our exercise connection to send that message.

public void tick(double dt)
{
LaserDesignatorStateMessage sm = mylsr.getStateMessage();
myExConn.sendMessage(sm);
}

Lastly, we will add a dispose method that will be used once the entity is destroyed or deleted. To do so we will create a new LaserDesignatorRemovedMessage, assign the entityId value of the message to our state repository entityId, and send the message with the exercise connection.

public void dispose()
{
LaserDesignatorRemovedMessage lrm = new LaserDesignatorRemovedMessage();
lrm.entityId = lsr.entityId;
myExConn.sendMessage(lrm);
}

Now if we define a publisher that uses an existing state repository and call its tick function in our simulation tick, we are able to send a laserDesignator that VR-Link can understand. Since we have already defined our strategy for laser designators, the published information will be formatted into what VR-Link will understand.

[<< Code Generator] [Home] [Top of Page]


Document ID: Generated on Wed Jan 7 13:31:29 EST 2015 from SVN revision 149162
Copyright © 2005-2014 VT MÄK. All Rights Reserved (www.mak.com)