VR-Link C# API Documentation
 All Classes Namespaces Files Functions Variables Enumerations Enumerator Properties Events Macros Pages
11 - Introduction to the Java API

Table of Contents

This chapter explains how to develop applications using the VR-Link for Java library.

VR-Link for Java consists of both Java code and native platform-specific code. Because of this dependency on native code, VR-Link for Java is not portable to any Java platform, only platforms supported by VR-Link (C/C++). Both 32- and 64-bit native libraries are provided for the platform corresponding to the VR-Link (C/C++) installation.

The Java API has a version of the F-18 example, which is in the ./java/examples, to illustrate the concepts that are introduced. It is assumed that you know the Java language.

11.1 Running the examples

Assuming you have a Java runtime installed and in your path (try the command 'java -version' to confirm), you must tell the Java Virtual Machine (JVM) two things:

Tell the JVM where the Java code is by using the CLASSPATH environment variable, or the -cp command line option. Remember that JVM command line options come before the class you want to run, and the command line options to the application come last:

java -cp <list of paths> [JVM options] <class to run> [arguments to application]

Tell the vrlj Java application where the native libraries are by using the bundled native libraries, by choosing the working directory, or by using the –loadPath argument to the application.

For an example on Microsoft Windows, if your current working directory contains the jar files f18.jar and vrlj_VC10.jar (which contains bundled native libraries, you could run the F18 example like this:

java -cp f18.jar;vrlj_VC10.jar com.mak.vrlj.example.f18.F18

Remember to use the file and path separators for your platform.

You could use the –loadPath argument to the F18 example application to locate the native libraries:

java -cp f18.jar;vrlj.jar com.mak.vrlj.example.f18.F18 --loadPath c:\mak\vrlink5.2\bin

To use a relative classpath run the listen example application from the VR-Link /bin or /bin64 directory (Windows) or /lib or /lib64 directory (linux):

java -cp ..\java\listen.jar;..\java\vrlj.jar com.mak.vrlj.example.listen.Listen

Or to run the talk example application using bundled native libraries on Windows (VC10):

java -cp talk.jar;vrlj_VC10.jar com.mak.vrlj.example.talk.Talk

Example using the CLASSPATH environment variable:

Set the CLASSPATH environment variable to include the Java jars and classfiles:

set CLASSPATH=<path to VR-Link>\java\f18.jar;<path to VR-Link>\java\listen.jar;<path to VR-Link>\java\talk.jar;<path to VR-Link>\java\vrlj_VC10.jar

Now the JVM can find all the examples and the -cp option is unecessary, and the bundled native libraries are used.

java com.mak.vrlj.example.f18.F18

11.2 Setting up a Project

You need a javac compiler that supports at least Java 8. Add the correct jar file to your Java classpath to build and to run your application.

All Java code is contained in a single JAR file. However, native code might be loaded differently depending upon configuration. The basic vrlj.jar file contains no native libraries; the location of the native libraries must be provided. Other jar files contain native library code for a single platform (with both 32- and 64-bit native code versions) depending on the name of the library.

11.2.1 Setting up with Bundled Native Libraries

If a jar containing native library code is used, you just need to add that jar to your Java classpath. At runtime, the native code will be loaded from the jar. Since each jar contains native code for a different platform, the jar corresponding to the execution platform must be used in the classpath at runtime. For example, vrlj_VC10.jar contains both 32- and 64-bit DLLs for Microsoft Windows compiled using Visual C++ 10.0.

11.2.2 Setting up with External Native Libraries

If the vrlj.jar file is included in the Java classpath, it contains no native code. The path to the shared libraries must be specified by calling the initializer method setLoadPath(). Since shared libraries load their dependent shared libraries automatically, it is good practice to keep all the related shared libraries in the same directory. The load path may also be set via the command line option –loadPath if the Java application uses the ExerciseConnectionInitializer to parse the command line as shown in the example applications.

If no setLoadPath() (or –loadPath) is specified, the current working directory will be used, check System.getProperty("user.dir").

The name of the shared library may be specified using setDynamicLibraryName(). The default is managedInterface. If the environment variable MAK_VRL_USE_DEBUG_FOR_CPLUSPLUS is defined, then the debug version of the native code is loaded.

11.2.3 Setting up with Eclipse

To add vrlj_Xxx.jar (platform specific with bundled native libraries) to an Eclipse project, simply add that jar to the build path.

To add vrlj.jar to an Eclipse project, add the jar to the build path normally. To load the native libraries, there are several ways:

To load the VR-Link for Java example applications into your workspace, select Import... from the File menu. For the source, select Existing Projects into Workspace (under General), and then press next. Select the root directory as /java/examples directory under your VR-Link installation and eclipse should find all the examples and add them to the Projects shown. If you do not copy the examples into the workspace,the example applications should already be configured to use bundled native libraries and be ready to run.

If you make a copy of the projects in your workspace, you will need to add the vrlj.jar or vrlj_Xxx.jar to the Eclipse build path for each example project.

11.3 Exercise Connections

VR-Link applications connect to an exercise through an exercise connection. Currently only a single exercise connection may be opened at a time; attempting to open a second exercise connection concurrently will result in undefined behaviour.

Exercise connections are implementations of the Java interface ExerciseConnection. Higher level VR-Link classes use the ExerciseConnection to set and receive state information and other data. The member functions of ExerciseConnection allow an application to do the following:

11.3.1 Initializing an Exercise Connection

ExerciseConnections are created by the ExerciseConnectionFactory class. There is one implementation.

An ExerciseConnection takes responsibility for various resources such as network sockets and managed memory. To simplify its use, ExerciseConnection implements the AutoCloseable interface introduced in Java 7.

try (ExerciseConnection exConn = ExerciseConnectionFactory.createDefault(initializer)) {
... use exConn here ...
} catch (Exception e) {
...
}

... exConn is automatically closed at this point ...

11.3.2 Using the ExerciseInitializer class

The ExerciseInitializer class should contain all information necessary to initialize an ExerciseConnection (or use default values), and may contain additional information for user application. The method parseCmdLine(String[] args) simplifies parsing command line arguments passed to the user application, to read the configuration information.

VR-Link for Java uses the open source project jcommander to parse command line parameters. The jcommander code is bundled in the same jar as the VR-Link for Java code.

Refer to the F-18 example, to the F18Init.java file for an example of how to create a custom ExerciseInitializer.

11.3.3 jcommander @Parameter annotation

For example, look at the markings member variable:

@Parameter(names = { "--f18Markings" }, description = "hull markings")
private final String markings;

This tells jcommander that the command line parameter –f18Markings should be mapped into the markings member variable. When the command line usage is printed, the hint "hull markings" will be shown next to the –f18Markings option.

11.4 Threading

VR-Link for Java shares a single thread with your application; no extra threads run for background processing such as send DIS heartbeats or HLA housekeeping tasks. Each ExerciseConnection has a drainInput() method which performs background processing and must be called periodically (typically at least once per second).

Note
drainInput() MUST be called even if all input (received interactions, updates, etc.) is ignored. The time spent in drainInput() depends upon the amount of background processing required at that time, and there is no simple rule to make even an approximate prediction of the time required.

VR-Link for Java methods are not thread-safe. User applications may use any threads desired, but must not call any VR-Link for Java methods simultaneously from multiple threads. Strategies such as locking or messaging could be employed to enforce this requirement.

VR-Link for Java will not access or modify simulation objects and interactions other than during the drainInput() call. So a typical way to use parallelism in a Java simulation application is to use multiple threads right after drainInput() finishes, and make sure all those threads finish (join) before the next call to drainInput() begins.

11.5 Publishing Objects

To publish an object to the network, start with a PublisherFactory obtained from an exercise connection, then get an ObjectPublisher for a particular kind of object.

PublisherFactory pubFactory = exConn.getPublisherFactory();
ObjectPublisher myEntity = pubFactory.createEntity("myEntityName");
Entity myEntity = myEntityPublisher.getObject();

Each PublisherFactory creates ObjectPublishers which publish to the same ExerciseConnection. PublisherFactory has methods to create publishers for each type of simulation object such as an Entity.

To update myEntity and publish to the network:

myEntity.setWorldPosition(new Vector3d(1524517.05424356, -4464444.01466548, 4278068.90596911));
myEntityPublisher.tick()();

A common way to write simulation code is to use a time-stepped loop. The example below is taken from the talk example.

double dt = 0.05; // time step in seconds
double simTime = 0.0; // starting simulation time in seconds
while (simTime <= 10.0) {
// Tell VR-Link the current value of simulation time.
clock.setSimTime(simTime);
// Process any incoming messages and perform background processing.
exConn.drainInput();
// Update our published entity from our topographic model
entity.setWorldPosition(toGeoc.transform(topoPos));
// Publish the updated model
entityPublisher.tick();
// topographic equation of motion:
// newPos = oldPos + vel * dt
offset.assignFrom(topoVel);
offset.multiply(dt);
topoPos.add(offset);
// Increment the simulation time
simTime += dt;
// Sleep till next iteration, in milliseconds.
Thread.sleep((long) (1000 * dt));
}

First, note that drainInput() MUST be called even though we are ignoring all input (received interactions, updates, etc.). In this example we are calling it every simulation frame, every few milliseconds (depending on the time step dt).

Also note that simply updating the time does not affect the entity position (or anything else about the entity). The entity motion, etc., must be updated by the user application. The above code uses a very simple motion model that updates the position similar to dead reckoning. Other, more detailed motion models are possible, for example a vehicle that follows roads or a ship affected by ocean currents.

11.6 Reflected Objects

A reflected object is a class used to store information about an object that has been discovered, updated and deleted by the exercise connection.

Each exercise connection tracks all reflected objects in a ReflectedObjectCollection, accessed from <>exConn.getReflectedCollection(). This collection implements the Map interface where the map key is some implementation ObjectIdentifier (for Entity it is an EntityName). ReflectedObjectCollection also extends the ObjectDispatcher interface, allowing registration for callbacks when objects are created, updated, or deleted.

To define a collection which is a particular subset of objects, you can define a ReflectedMap for a particular class of object, and a particular selection criteria. A selection criteria defines a test for inclusion: when the criteria is applied to an object it passes (criteria returns true) and is included in the ReflectedMap, or else it fails and is removed. Once a collection is created with a specified criteria, the criteria may not be changed. Instead, create a new collection with a different criteria. For convenience, a ReflectedMap is pre-defined for each type of object, for example, ReflectedEntityMap.

The example below shows a ReflectedEntityMap which is automatically updated and maintained by the ExerciseConnection exConn:

// print out all the reflected Entity objects
ReflectedEntityMap rem = new ReflectedEntityMap(exConn);
Iterator<Entity> iter = rem.values().iterator();
while (iter.hasNext())
{
Entity first = iter.next();
System.out.println(iter.toString());
}

Alternatively, you could iterate through all objects and check which are Entity objects as follows:

Iterator<BaseObject<? extends ObjectIdentifier>> objIter = exConn.getReflectedCollection().values().iterator();
while(objIter.hasNext())
{
BaseObject<? extends ObjectIdentifier> obj = objIter.next();
if(obj instanceof Entity)
{
Entity e = (Entity) obj;
System.out.println(e.toString());
}
}

11.7 Callbacks and Interactions

Interactions are handled differently than objects. Typically, interactions are created and sent one time, then received and reacted to appropriately. An example of such interaction is the FireInteraction. The f18 example has the ability to fire at other entities, and it does so with fire interactions.

Sending an interaction is as simple as creating one, setting its data, then sending it using the exercise connection.

FireInteraction fire = new FireInteraction();
fire.setFiringId(myId);
... set other interaction fields as required ...
exConn.send(fire);

To illustrate receiving an interaction, consider the code fragment below from the Listen example. Note the Java 8 lambda syntax for the callback method definition.

FireInteraction.addListener(exConn, fire -> {
System.out.print("Fire Interaction from ");
System.out.println(fire.getFiringId());
});

Each Interaction class contains convenience member methods addListener and removeListener for adding and removing InteractionListener callbacks. InteractionListener defines a single method process(intr) which takes an interaction as its only argument. In the previous example, the handler method for incoming fire interactions is defined using the Java 8 lambda syntax, where 'fire' is the argument which receives the fire interaction.

The same example in older syntax using an anonymous inner class looks like this:

FireInteraction.addListener(exConn, new InteractionListener<FireInteraction>() {
public void process(FireInteraction fire)
{
System.out.print("Fire Interaction from ");
System.out.println(fire.getFiringId());
}
});

[[Home] [Top of Page]


Document ID: Generated on Mon Feb 6 18:36:34 EST 2017 from SVN revision 173107
Copyright © 2005-2016 VT MÄK. All Rights Reserved (www.mak.com)