Reducing Integration Time and Risk with the HLA ... - Semantic Scholar

3 downloads 8011 Views 82KB Size Report
Incorrect encoding/decoding can make federates or federations crash, hang or even ... remains: Why does it have to be so hard to get the data that is exchanged over the ... Java developers and can also be used together with the Web Service HLA API. .... recovery. The code was generally poor and some bad data was sent.
Reducing Integration Time and Risk with the HLA Evolved Encoding Helpers Björn Möller Mikael Karlsson Björn Löfstrand Pitch Technologies Nygatan 35 SE-582 19 Linköping, Sweden +46 13 13 45 45 [email protected] [email protected] [email protected] Keywords: Interoperability, HLA, RTI, HLA Evolved, FEDEP, RPR, Data Encoding ABSTRACT: During test and integration (step 5 in FEDEP), federate developers get together to ensure the technical interoperability of the participating systems. This includes testing if their systems exchange data according to the federation agreements. In HLA 1516, the technical format (encoding) of the data that is exchanged over the RTI is fully specified in the Federation Object Model. Each federate is responsible for the encoding and decoding of data based on this format. Still, this does not necessarily mean that all developers have successfully interpreted and implemented encoding/decoding. A surprisingly large portion of today’s federation efforts experience problems with this. Incorrect encoding/decoding can make federates or federations crash, hang or even worse; misinterpreting the received data. All of this introduce risk, delay and cost to integration. What is even worse, it may cause the final federation to run with incorrect outputs or provide inadequate training. While it is possible to use tools such as data loggers to check the correctness of the encoding, the fundamental question remains: Why does it have to be so hard to get the data that is exchanged over the RTI correctly encoded and decoded? This problem has already been solved in other interoperability solutions for distributed systems, such as CORBA, DCOM and Web Services. A solution for HLA, the Encoding Helpers, has been suggested for HLA Evolved based on an idea originating from the DMSO RTI Java API. A Tiger Team has been formed to provide the details. The encoding helpers are available both to C++ and Java developers and can also be used together with the Web Service HLA API. This paper gives a short overview of the HLA 1516 data encoding. It then describes the structure of the encoding helpers. Three examples are given: - Encoding and decoding a basic attribute update of a string and an integer. - Encoding and decoding spatial data from the RPR 2.0 FOM. - Encoding and decoding of arrays of records for sensor information. The encoding helpers do not add any ground-breaking new interoperability service to HLA. Still, they are believed to be a valuable addition that will save time and money and reduce risk in a large number of federations.

1. Introduction The exchange of application data is the core functionality of all interoperability solutions. This may be done through calls or interactions but also through interest-based subscriptions. Additional support for interoperability may also be provided, for example mechanisms for synchronization. Examples from HLA include time management, save/restore and synchronization points. The exchange of data between systems can always be regarded as based on an information exchange model

[1]. This model may be explicit and clearly documented or implicit and hidden in the implementation. When a group of systems interoperates, such an information model may in fact be fragmented. This typically occurs when the exchange of data between any two systems has been implemented independently as point-to-point operations. Examples of this is Remote Procedure Calls (RPC) and the currently available generation of Web Services.

In other cases, a common model exists that covers all data that is produced and consumed. This will provide for more efficient development, deployment and reuse of systems since one single syntax and semantics can be agreed upon for each type of information. The infrastructure will effectively function as an information bus where current and future systems can tap in and also contribute information. One example of such an architecture is HLA [2].

When data is exchanged between systems, it has to be moved to and from internal representation in the application code into a byte array that is to be transmitted. This requires some kind of serialization/encoding. Additional complexity is introduced by the fact that different processors or operating systems may use different byte size to represent for example an integer or use big-endian or little-endian [5] representation1.

1.1 The need for dynamic information exchange data models The HLA standard has its roots in the defense community. The requirement picture of this community is currently undergoing major changes. The changing threats, asymmetric warfare, crisis management and diminishing resources, increases the need for interoperability and reuse. Interoperability is necessary not only from a joint and combined forces perspective but also increasingly with the rest of society. As the rate of change increases and new situations arise, there is a need to rapidly provide interoperability for new situations. This in turn requires establishing and implementing new information exchange data models. Earlier interoperability standards like DIS use a fixed protocol format with the information exchange model “hard-wired” into the standard. It has been recognized as a major limitation to use a standard that is normally extended only at “committee-time”. HLA 1.3 introduced an explicit information exchange data model, the Federation Object Model (FOM), which follows a well-defined template. HLA IEEE 1516-2000 made a crucial improvement by introducing an XML-based [3] data format for the FOM, aligning it with the main thrust of data and information description formats. Along with this came the opportunity of applying a rich set of technologies and tools when working with FOMs, for example XML editors, XML Schema checking, XML transformation and more. HLA Evolved [4] continues along this line by adding for example a standardized schema for information models and support for user-defined extendability.

Processors

Big-endian

Motorola, Sparc, IBM 370

Little-endian

Intel x86, VAX

Configurable

ARM, PowerPC, MIPS, PARisc, Intel IA64

Figure 1: Processors and number representation The data encoding problem may at first seem to be diminishing as the user base decreases for systems not based on Windows or Linux on Intel 32 bit processors. However the computing world is now on the verge of moving on to inexpensive 64 bit systems. Other upcoming technologies such as Configurable Computing, where the bulk of the processing power of the CPU can be configured to fit specific algorithms, may also have an impact. Encoded representation for commonly used data types have been standardized by several organizations. The HLA 1516-2000 standard refers for example to the IEEE std 754-1985 standard [6] for representations like floating point.

2. Incorrect Encoding from a FEDEP Perspective Encoding is normally handled in the “Federation Development” step of FEDEP [7] (step 4) and documented in the FOM. The next step “Test and Integration” usually reveals to what extent the encoding was successfully implemented. Common problems include: •

1.2 Information exchange and encoding Exchanging data between HLA federates requires that a common, serialized data format has to be agreed upon and used. The alternative would be to have each system being able to interpret incoming data based on the sender. This would be suboptimal when several data systems need to exchange the same or similar data. For an information exchange infrastructure like HLA that acts like a data bus, it would be more or less impossible.

Representation

• •

1

Coding errors, usually related to the encoding and decoding of data. Incorrect interpretations of the specification. Lack of insight into technical environments such as data types in languages, compilers, operating systems and processors.

The terminology big endian and little endian was introduced in the book “Gulliver's Travels” by Jonathan Swift in 1726. It was used for boiled eggs. It was later applied to computing by Danny Cohen in 1981 [5].

Encoding errors may of course become visible in later steps too, all the way to the final preparation and delivery of results. As usual, the later the problem is discovered the more severe and costly the impact.

4) The incorrect data is not used anywhere and the federation is not affected.

Testing alone cannot prevent these problems. A developer that makes an incorrect interpretation of the specification will write tests based on the same incorrect interpretation, which means that the faulty code will succeed in the similarly faulty tests.

A fifth case that has actually occured quite often in real life is that all federate developers made the same incorrect encoding which resulted in a working federation that successfully exchanged data. It can also be the case that all developers were in agreement and made the right implementation but the documented encoding had errors.

2.1 The locality problem: Err here, fail there

2.3 Strategies to detect incorrect encoding

One additional challenge with software errors is that software seldom fails at the specific point where erroneous code or data has been introduced. The severity of this principle is increased as systems get more and more distributed and software components from different organizations are required to interoperate. The following real-life example illustrates this:

It is of course possible to detect incorrect encoding using various inspection tools. These include debuggers from software development environments, data loggers, trace functionality in RTIs, dedicated federates or even network analyzers.

What it looks like: Vendor A and vendor B bring their systems to integration testing. The systems from vendor A is completely stable whereas systems from vendor B keeps crashing. Developers from vendor B claim that they have tested their system extensively. Integration tests are abandoned and developers from vendor B are blamed and sent home to fix the bugs. What really happened: Developers from vendor B initially set up a quality assurance test to check that correct data was sent and that correct data could be received and processed. Little effort was spent on handling incorrect inputs. Vendor A developers on their side did not spend a lot of time on any quality assurance test. Some time was spent on making the system tolerant to incorrect input including proper recovery. The code was generally poor and some bad data was sent. This data caused vendor Bs systems to fail. 2.2 Description of impacts The resulting impact can typically be divided into four categories: 1) Incorrect data causes one or more federates to crash or hang, making the problem obvious at some level. 2) Incorrect data does not crash the federation, but the federation effectively becomes polluted. Values like positions may be incorrect, causing incorrect interaction between models. Lack of correct responses is another possibility. Aggregation may become incorrect if, for example, the wrong value for nationality or side is introduced. 3) The receiving federates are able to detect that the data is incorrect or an exception is caught and the federation keeps running. This results in loss of data with varying impact on the federation.

3. Overview of HLA 1516-2000 Encoding As previously described HLA 1516-2000 introduced XML representation of the FOM. The other major achievement in the HLA 1516 standard for the Object Model Template is the introduction of a full and unambiguous encoding of the data exchange, i.e. the serialized data. 3.1 Basic Data HLA 1516-2000 introduces a standardized encoding. While it focuses on todays computer architectures with 8, 16, 32 and 64 bit registers, it is possible to add other types of representations. The atomic building block is known as Basic Data. The following types of Basic Data are pre-defined in the HLA standard and are required in a FOM: Category

Predefined Basic Data types

Bytes

HLAoctet HLAoctetPairBE, HLAoctetPairLE

Integers

HLAinteger16BE, HLAinteger16LE HLAinteger32BE, HLAinteger32LE HLAinteger64BE, HLAinteger64LE

Floats

HLAfloat32BE, HLAfloat32LE HLAfloat64BE, HLAfloat64LE

Figure 2: Predefined Basic Data Note that additional Basic Data may be added as needed, for example for new processor architectures. 3.2 Simple Data Basic Data is never used directly for attributes or parameters. Instead, Simple Data types can be defined for example like this.

3.5 Common HLA 1516 encoding mistakes Based on our experience with HLA 1516 federate developers from different organizations we find that the two most common mistakes are as follows: •

Not understanding data alignment and the associated padding. When some federates use correct padding and others use incorrect padding the resulting variable values in receiving federates are totally incorrect.



Failure to implement HLAunicodeString and sending a null-terminated ASCII string instead. This mistake has actually been done by so many developers in a recent 1516 federation that a large subset, if not all of the federation worked flawlessly.

Figure 3: Example of Simple Data type An example of an integer for describing fuel level is shown. Additional information about the data type can be given for example accuracy. This data type can now be used whenever this type of information needs to be described. Three useful pre-defined Simple Data types provided with HLA are HLAASCIIchar, HLAunicodeChar and HLAbyte. 3.3 More advanced constructs The HLA standard also defines additional constructs such as: •

Enumerated data types that specify a representation (using Basic Data) and allowed values.



Arrays that may consist of other elements and have a dynamic size.



Records that may combine other types of elements. Variant records are also supported.

Note that several useful pre-defined data types exist on this level as well. Some examples are: HLA Boolean. HLAASCIIstring, HLAunicodeString and HLAopaquedata.

4. Encoding Helpers in HLA Evolved There are few basic strategies for implementing data encoding in HLA: •

Hand-craft the code that serializes the internal representation into byte-arrays. This usually includes casting variables into byte-arrays and structs. This may at first seem easier in C++ than Java. As the complexity increases, for example with the same code compiled on different processors and with the use of variable length arrays, the task gets more complicated.



Use a static middleware layer, usually developed by a specialized team or acquired as COTS, to map between a specific FOM and an internal object model.



Use code-generation tools that can generate code based on different FOMs.



Use encoding helper classes.

3.4 Cardinality and padding Two more aspects of the data encoding are covered by the HLA 1516 standard: •

Handling of cardinality for dynamic arrays. This has traditionally been implemented in several different ways but a standard method is now provided.



Padding of data to ensure that the data alignment is correct. This guarantees for example that a one-byte variable is correctly padded with another byte before a two-byte item appears or with three bytes before a four byte item appears in a record.

Correct padding is also guaranteed for dynamic arrays of records of items of varying size.

Encoding helpers were first introduced in the Java API of RTI-NG. They have since been revised and also migrated to C++. The Java and C++ Encoding Helpers are currently suggested to HLA Evolved, the next version of HLA. 4.1 Basic principles The Encoding Helpers use a base class DataElement with one derived class for each data type, e.g. HLAinteger32BE. Complex data types, such as HLAfixedRecord, can hold other DataElements.

“targetBuffer” that can be used in an AttributeHandleValueMap when sending an update:

DataElement getEncodedSize() encode(ByteWrapper b) decode(ByteWrapper b)

// Populate encoder object with data to encode encoder.setValue(myVehicle.fuelLevel) // Ask encoder object to produce // encoded representation encoder.encode(targetBuffer)

HLAinteger32BE

HLAunicodeString

HLAfixedRecord

getEncodedSize() encode(ByteWrapper b) decode(ByteWrapper b) setValue(int v) getValue() : int

getEncodedSize() encode(ByteWrapper b) decode(ByteWrapper b) setValue(String v) getValue() : String

getEncodedSize() encode(ByteWrapper b) decode(ByteWrapper b) add(DataElement d) get(int i) : DataElement

We will here show three examples of how the encoding helpers can be used:

When a reflect callback is received by the federate the decoder can be used to decode data from a “sourceBuffer” as follows: // Decode received byte array using // existing encoder object encoder.decode(sourceBuffer) // Extract decoded value myVehicle.fuelLevel = encoder.getValue()



Encoding and decoding a basic attribute update of a string and an integer.

The corresponding code for creating an encoder and a decoder for an HLAunicodeString is



Encoding and decoding spatial data from the RPR 2.0 FOM.

// Create encoder object for HLAunicodeString encoder = new HLAunicodeString



Encoding and decoding of arrays of records for sensor information.

The encoding and decoding is presented using pseudocode in the main text of this paper to make them more readable. Appendix A provides these examples in Java, Appendix B provides them in C++ with automatically managed memory and Appendix C provides them in C++ with explicit memory management. As of the writing of this paper there was no final decision upon which C++ version to include in the standard. 4.2 A simple example using an integer In the following example we want to encode data for a FOM that looks like this in the HLA object modeling tool “Visual OMT™ 1516” [8]:

Figure 4: A simple vehicle FOM Note that we are using Unicode to send strings which supports a wide international character set. The fuel level integer is a simple data type that is defined in Figure 3. To correctly encode an integer into a byte array we need to initially create an encoder/decoder object for HLAinteger32 as follows: // Create encoder object for HLAinteger32BE encoder = new HLAinteger32BE

We can now use this encoder to encode the variable “myVechicle.fuelLevel” data into the byte array

We can now encode the string as follows: // Populate encoder object with data to encode encoder.setValue(myVehicle.name) // Ask encoder object to produce // encoded representation encoder.encode(targetBuffer)

When a reflect is received the decoder can be used as follows: // Decode received byte array using // existing encoder object encoder.decode(sourceBuffer) // Extract decoded value myVehicle.name = encoder.getValue()

4.3 Spatial struct in the RPR FOM We will now look at encoding and decoding structures. These are commonly used when a number of related attributes need to be updated simultaneously. A typical example is a coordinate, where updating x, y and z as separate operations will temporarily yield incorrect states. The RPR FOM [9] is a commonly used information exchange model that has a byte layout that closely resembles the older corresponding DIS PDUs.

Each element is a 64 bit big-endian float. We can now create encoders and decoders for this as follows: // Create encoder object for HLAfixedRecord xEncoder = new HLAfloat64BE() yEncoder = new HLAfloat64BE() zEncoder = new HLAfloat64BE() posEncoder = new HLAfixedRecord() posEncoder.add(xEncoder) posEncoder.add(yEncoder) posEncoder.add(zEncoder)

Figure 5: Physical Entities in the RPR FOM The most commonly used parts of the RPR FOM are the physical entities such as platforms, humans, sensors, etc. A physical entity is, among other things, characterized by having spatial attributes such as location. The spatial information is described in a flexible way that also makes it possible to incorporate information about velocity and acceleration to facilitate dead reckoning.

We can then encode x, y and z into a byte array that can be used for sending the update as follows: // Populate encoder object with data to encode xEncoder.setValue(myVehicle.x yEncoder.setValue(myVehicle.y) zEncoder.setValue(myVehicle.z) // Ask encoder object to produce // encoded representation posEncoder.encode(targetBuffer)

When a reflect is received the decoder can be used as follows: // Decode received byte array using // existing encoder object posEncoder.decode(sourceBuffer) // Extract decoded value myVehicle.x = xEncoder.getValue() myVehicle.y = yEncoder.getValue() myVehicle.z = zEncoder.getValue()

4.4 Variable arrays Figure 6: RPR FOM platforms The spatial information is defined on the Base Entity level. However, the entities that most frequently update their locations are found under the Platform subclass. These include Aircraft, Amphibious Vehicle, Ground Vehicle, Multi Domain Platform, Spacecraft, Submersible Vessel and Surface Vessel. The spatial attribute is a struct that in turn consists of several other structs. The exact layout varies depending on the dead reckoning algorithm used. One element that is always present is the WorldLocationStruct which can be defined as follows:

Assume that we want to exchange data about a track which is represented as a variable array of world locations, as follows:

Figure 8: An array of world locations The code for encoding this array can look like this:

Figure 7: The World Location Struct

// Create encoder object for // HLAvariableArray and set values arrayEncoder = new HLAvariableArray() for location in myRadarTrack xEncoder = new HLAfloat64BE() yEncoder = new HLAfloat64BE() zEncoder = new HLAfloat64BE() posEncoder = new HLAfixedRecord() posEncoder.add(xEncoder) posEncoder.add(yEncoder) posEncoder.add(zEncoder)

xEncoder.setValue(myVehicle.x) yEncoder.setValue(myVehicle.y) zEncoder.setValue(myVehicle.z) arrayEncoder.addElement(posEncoder) addElement(coder);

Encoding the array can be done like this: // Ask encoder object to produce // encoded representation arrayEncoder.encode(targetBuffer)

Decoding the array can be done like this: // Create factory for array decoder elements elementFactory = new DataElementFactory() createElement(int index) xEncoder = new HLAfloat64BE() yEncoder = new HLAfloat64BE() zEncoder = new HLAfloat64BE() posEncoder = new HLAfixedRecord() posEncoder.add(xEncoder) posEncoder.add(yEncoder) posEncoder.add(zEncoder) xEncoder.setValue(myVehicle.x) yEncoder.setValue(myVehicle.y) zEncoder.setValue(myVehicle.z) return posEncoder // Create decoder object arrayDecoder = new HLAvariableArray(elementFactory) // Decode received byte array using // decoder object arrayDecoder.decode(sourceBuffer) // Extract decoded value for (i = 0 to arrayDecoder.size()) posDecoder = arrayDecoder.get(i) xDecoder = posDecoder.get(0) yDecoder = posDecoder.get(1) zDecoder = posDecoder.get(2) myRadarTrack[i].x = xEncoder.getValue() myVehicle.x = xEncoder.getValue() myVehicle.y = yEncoder.getValue() myVehicle.z = zEncoder.getValue()

4.8 Encoding in the WSDL API The upcoming WSDL API [10] differs significantly from the C++ and Java API since it does not provide a traditional local RTI component/library. The WSDL API makes it possible to access the functionality of an RTI through HTTP requests over a network. The requests and responses are provided in XML format. The calls are described using the Web Service Definition Language (WSDL). A large set of standard tools can be used to create code that makes the calls. The WSDL federates still need to encode and decode data according to the HLA standard. In order to support this, it has been suggested to HLA Evolved that the encoding helpers should be available in separate packages for the convenience of WSDL federates implemented in C++ or Java.

5. Discussion One major strength of the encoding helpers is that they enable rapid construction of correct software for encoding and decoding, even for complex structures. One of the major contributing factors is that they reduce the number of error-prone “cast” operations in the developers code. Encoding helpers use objects to perform their task. Creation and deletion of objects may impose a performance penalty. This penalty can be minimized by instantiating the decoder objects at startup. A skilled developer can make more compact and efficient implementations of the encoding and decoding, especially for the C++ API. However, handcoding solutions for encoding and decoding is errorprone, time-consuming and complicated, especially for more complex encodings. One limitation that applies to the Encoding Helpers, as well as other techniques for decoding, is that the recived data does not contain any control information that specifies what is actually encoded in the received data. Any mismatch between the assumptions made by the decoding machinery and the actual data will cause an undefined behavior. Using Encoding Helpers on both sending and receiving side minimizes the risk of mismatches, but no amount of help can handle a missing, incomplete or misinterpreted federation agreement. One popular approach for developers that wish to adapt their systems to HLA is to use third-party middleware. Such a middleware may be pre-built or generated from case to case using a tool. Such a middleware usually also includes encoding and decoding support. While the Encoding Helpers reduce the burden for developers that need to adapt a system to HLA, they do not provide the complete middleware layer that is usually expected. A future enhancement to the Encoding Helpers could be a solution that reads a FOM definition of a data type and generates source code that in turn builds a correct decoder structure.

6. Conclusions Encoding and decoding data is a crucial part of simulation interoperability. Errors in encoding are common and the resulting errors may not always be obvious at first sight. This introduces risk and cost. A large number of federations, if not the majority, has experienced some impact of this. Encoding and decoding is available in many other interoperability frameworks but were left out in earlier versions of HLA. It has now been suggested to include encoding helpers as part of the HLA standard to further support successful federation of systems.

HLA encoding helpers provide a standardized and verifiable code base for encoding and decoding. This can be expected to reduce time and risk in HLA projects. The encoding helpers add no ground-breaking new functionality to HLA. Still they provide opportunities for great improvements in almost all HLA projects.

9. References [1] Eddie Lasschuyt: “How to make an efficient data exchange model”, TNO [2] IEEE: "IEEE 1516, High Level Architecture (HLA)", IEEE, www.ieee.org, March 2001. [3] XML, World Wide http://www.w3.org/XML/

Web

Consortium,

[4] HLA Evolved, www.sisostds.org/ [5] Danny Cohen, "On Holy Wars and a Plea for Peace", IEEE Computer, October 1981. [6] IEEE 754-1985, www.ieee.org [7] “1516.3-2003 IEEE Recommended Practice for High Level Architecture (HLA) Federation Development and Execution Process (FEDEP), IEEE, www.ieee.org, 2003 [8] Visual OMT 1516, Pitch www.pitch.se/visualomt1516

Technologies,

[9] SISO: “Real Time Reference Platform (RPR FOM)”, www.sisostds.org [9] WSDL, World Wide Web http://www.w3.org/TR/wsdl

Consortium,

Author Biographies BJÖRN MÖLLER is the Vice President and cofounder of Pitch, the leading supplier of tools for HLA 1516 and HLA 1.3. He leads the strategic development of Pitch HLA products. He serves on several HLA standards and working groups and has a wide international contact network in simulation interoperability. He has twenty years of experience in high-tech R&D companies with an international profile in areas such as modeling and simulation, artificial intelligence and Web based collaboration. Björn Möller holds an M.Sc. in Computer Science and Technology after studies at Linköping University, Sweden and Imperial College, London.

MIKAEL KARLSSON is the Chief Architect at Pitch overseeing the world’s first certified HLA IEEE 1516 RTI as well as the first certified commercial RTI for

HLA 1.3. He has more than ten years of experience of developing simulation infrastructures based on HLA as well as earlier standards. He also serves on several HLA standards and working groups. He studied Computer Science at Linköping University, Sweden.

BJÖRN LÖFSTRAND is Manager of Modeling and Simulation Services at Pitch Technologies. He holds an M.Sc. in Computer Science from Linköping Institute of Technology and has been working with HLA federation development and tool support since 1996. Recent work includes developing design patterns for HLA based simulation in the future Swedish Networked Based Defense.

Appendix A: Java Code Example 1, HLAinteger32BE: // Step 1: Create encoder object for HLAinteger32BE HLAinteger32BE myInt32Encoder = factory.createHLAinteger32BE();

// Step 2: Populate encoder object with data to encode myInt32Encoder.setValue(myVehicle.fuelLevel);

// Step 3: Ask encoder object to produce encoded representation byte[] targetBuffer = myInt32Encoder.toByteArray();

// Step 4: Decode received byte array using existing encoder object myInt32Encoder.decode(sourceBuffer);

// Step 5: Extract decoded value myVehicle.fuelLevel = myInt32Encoder.getValue();

// Step 6: Release allocated objects

Example 2, HLAunicodeString: // Step 1: Create encoder object for HLAunicodeString HLAunicodeString myStrEncoder = factory.createHLAunicodeString();

// Step 2: Populate encoder object with data to encode myStrEncoder.setValue(myVehicle.name);

// Step 3: Ask encoder object to produce encoded representation byte[] targetBuffer = myStrEncoder.toByteArray();

// Step 4: Decode received byte array using existing encoder object myStrEncoder.decode(sourceBuffer);

// Step 5: Extract decoded value myVehicle.name = myStrEncoder.getValue();

// Step 6: Release allocated objects

Example 3, HLAfixedRecord: // Step 1: Create encoder object for HLAfixedRecord HLAfloat64BE xEncoder = factory.createHLAfloat64BE();

HLAfloat64BE yEncoder = factory.createHLAfloat64BE(); HLAfloat64BE zEncoder = factory.createHLAfloat64BE(); HLAfixedRecord posEncoder = factory.createHLAfixedRecord(); posEncoder.add(xEncoder); posEncoder.add(yEncoder); posEncoder.add(zEncoder);

// Step 2: Populate encoder object with data to encode xEncoder.setValue(myVehicle.x); yEncoder.setValue(myVehicle.y); zEncoder.setValue(myVehicle.z);

// Step 3: Ask encoder object to produce encoded representation byte[] targetBuffer = posEncoder.toByteArray();

// Step 4: Decode received byte array using existing encoder object posEncoder.decode(sourceBuffer);

// Step 5: Extract decoded value myVehicle.x = xEncoder.getValue(); myVehicle.y = yEncoder.getValue(); myVehicle.z = zEncoder.getValue();

// Step 6: Release allocated objects

Example 4, HLAvariableArray: // Step 1: Create encoder object for HLAvariableArray and set values HLAvariableArray arrayEncoder = encoderFactory.createHLAvariableArray(); for (Location location : myRadarTrack) { HLAfloat64BE xEncoder = encoderFactory.createHLAfloat64BE(); HLAfloat64BE yEncoder = encoderFactory.createHLAfloat64BE(); HLAfloat64BE zEncoder = encoderFactory.createHLAfloat64BE(); HLAfixedRecord posEncoder = encoderFactory.createHLAfixedRecord(); posEncoder.add(xEncoder); posEncoder.add(yEncoder); posEncoder.add(zEncoder); xEncoder.setValue(location.x); yEncoder.setValue(location.y); zEncoder.setValue(location.z); arrayEncoder.addElement(posEncoder); }

// Step 2: Ask encoder object to produce encoded representation

byte[] targetBuffer = arrayEncoder.toByteArray();

// Step 3: Create factory for array decoder elements DataElementFactory elementFactory = new DataElementFactory() { public DataElement createElement( int index) { HLAfloat64BE xEncoder = encoderFactory.createHLAfloat64BE(); HLAfloat64BE yEncoder = encoderFactory.createHLAfloat64BE(); HLAfloat64BE zEncoder = encoderFactory.createHLAfloat64BE(); HLAfixedRecord posEncoder = encoderFactory.createHLAfixedRecord(); posEncoder.add(xEncoder); posEncoder.add(yEncoder); posEncoder.add(zEncoder); return posEncoder; } };

// Step 4: Create decoder object HLAvariableArray arrayDecoder = encoderFactory.createHLAvariableArray(elementFactory);

// Step 5: Decode received byte array using decoder object arrayDecoder.decode(sourceBuffer);

// Step 6: Extract decoded value for (int i = 0; i < arrayEncoder.size(); i++) { HLAfixedRecord locationRecord = (HLAfixedRecord)arrayEncoder.get(i); HLAfloat64BE xDec = (HLAfloat64BE)locationRecord.get(0); HLAfloat64BE yDec = (HLAfloat64BE)locationRecord.get(1); HLAfloat64BE zDec = (HLAfloat64BE)locationRecord.get(2); myTrack[i].x = xDec.getValue(); myTrack[i].y = yDec.getValue(); myTrack[i].z = zDec.getValue(); }

// Step 6: Release allocated objects

Appendix B: C++ Code, Automatically Managed Memory

Example 1, HLAinteger32BE: // Step 1: Create encoder object for HLAinteger32BE HLAinteger32BE myInt32Encoder(factory.createHLAinteger32BE());

// Step 2: Populate encoder object with data to encode myInt32Encoder.setValue(myVehicle.fuelLevel);

// Step 3: Ask encoder object to produce encoded representation char* targetBuffer = new char[myInt32Encoder.getEncodedLength()]; ByteWrapper byteWrapper1(targetBuffer); myInt32Encoder.encode(byteWrapper1);

// Step 4: Decode received byte array using existing encoder object ByteWrapper byteWrapper2(sourceBuffer); myInt32Encoder.decode(byteWrapper2);

// Step 5: Extract decoded value myVehicle.fuelLevel = myInt32Encoder.getValue();

// Step 6: Release allocated objects delete [] targetBuffer;

Example 2, HLAunicodeString: // Step 1: Create encoder object for HLAinteger32BE HLAunicodeString myStrEncoder(factory.createHLAunicodeString());

// Step 2: Populate encoder object with data to encode myStrEncoder.setValue(myVehicle.name);

// Step 3: Ask encoder object to produce encoded representation char* targetBuffer = new char[myStrEncoder.getEncodedLength()]; ByteWrapper byteWrapper1(targetBuffer); myStrEncoder.encode(byteWrapper1);

// Step 4: Decode received byte array using existing encoder object ByteWrapper byteWrapper2(sourceBuffer); myStrEncoder.decode(byteWrapper2);

// Step 5: Extract decoded value myVehicle.name = myStrEncoder.getValue();

// Step 6: Release allocated objects delete [] targetBuffer;

Example 3, HLAfixedRecord: // Step 1: Create encoder object for HLAfixedRecord HLAfloat64BE xEncoder(factory.createHLAfloat64BE()); HLAfloat64BE yEncoder(factory.createHLAfloat64BE()); HLAfloat64BE zEncoder(factory.createHLAfloat64BE()); HLAfixedRecord posEncoder(factory.createHLAfixedRecord()); posEncoder.add(xEncoder); posEncoder.add(yEncoder); posEncoder.add(zEncoder);

// Step 2: Populate encoder object with data to encode xEncoder.setValue(myVehicle.x); yEncoder.setValue(myVehicle.y); zEncoder.setValue(myVehicle.z);

// Step 3: Ask encoder object to produce encoded representation char* targetBuffer = new char[posEncoder.getEncodedLength()]; ByteWrapper byteWrapper1(targetBuffer); posEncoder.encode(byteWrapper1);

// Step 4: Decode received byte array using existing encoder object ByteWrapper byteWrapper2(sourceBuffer); posEncoder.decode(byteWrapper2);

// Step 5: Extract decoded value myVehicle.x = xEncoder.getValue(); myVehicle.y = yEncoder.getValue(); myVehicle.z = zEncoder.getValue();

// Step 6: Release allocated objects delete [] targetBuffer;

Example 4, HLAvariableArray: // Step 1: Create encoder object for HLAvariableArray and set values HLAvariableArray arrayEncoder(encoderFactory.createHLAvariableArray()); for (int i = 0; i < myRadarTrack.size(); i++) {

Location location = myRadarTrack[i]; HLAfloat64BE xEncoder(encoderFactory.createHLAfloat64BE()); HLAfloat64BE yEncoder(encoderFactory.createHLAfloat64BE()); HLAfloat64BE zEncoder(encoderFactory.createHLAfloat64BE()); HLAfixedRecord posEncoder(encoderFactory.createHLAfixedRecord()); posEncoder.add(xEncoder); posEncoder.add(yEncoder); posEncoder.add(zEncoder); xEncoder.setValue(location.x); yEncoder.setValue(location.y); zEncoder.setValue(location.z); arrayEncoder.addElement(posEncoder); }

// Step 2: Ask encoder object to produce encoded representation char* targetBuffer = new char[arrayEncoder.getEncodedLength()]; ByteWrapper byteWrapper1(targetBuffer); arrayEncoder.encode(byteWrapper1);

// Step 3: Create factory for array decoder elements class DataElementFactory_Position : public DataElementFactory { virtual DataElement createElement(int index) { HLAfloat64BE xEncoder(encoderFactory.createHLAfloat64BE()); HLAfloat64BE yEncoder(encoderFactory.createHLAfloat64BE()); HLAfloat64BE zEncoder(encoderFactory.createHLAfloat64BE()); HLAfixedRecord posEncoder(encoderFactory.createHLAfixedRecord()); posEncoder.add(xEncoder); posEncoder.add(yEncoder); posEncoder.add(zEncoder); return posEncoder; } };

// Step 4: Create decoder object HLAvariableArray arrayDecoder(encoderFactory.createHLAvariableArray(DataElementFactory_Position()));

// Step 5: Decode received byte array using decoder object ByteWrapper byteWrapper2(sourceBuffer); arrayDecoder.decode(byteWrapper2);

// Step 6: Extract decoded value for (int i = 0; i < arrayDecoder.size(); i++) {

HLAfixedRecord* locationRecord = (HLAfixedRecord*)arrayDecoder.get(i); HLAfloat64BE* xDecoder = (HLAfloat64BE*)locationRecord.get(0); HLAfloat64BE* yDecoder = (HLAfloat64BE*)locationRecord.get(1); HLAfloat64BE* zDecoder = (HLAfloat64BE*)locationRecord.get(2); myTrack[i].x = xDecoder->getValue(); myTrack[i].y = yDecoder->getValue(); myTrack[i].z = zDecoder->getValue(); }

// Step 6: Release allocated objects delete [] targetBuffer;

Appendix C: C++ Code, Explicit Memory Management Example 1, HLAinteger32BE: // Step 1: Create encoder object for HLAinteger32BE HLAinteger32BE* myInt32Encoder = new HLAinteger32BE();

// Step 2: Populate encoder object with data to encode myInt32Encoder->setValue(myVehicle.fuelLevel);

// Step 3: Ask encoder object to produce encoded representation char* targetBuffer = new char[myInt32Encoder->getEncodedLength()]; ByteWrapper byteWrapper1(targetBuffer); myInt32Encoder->encode(byteWrapper1);

// Step 4: Decode received byte array using existing encoder object ByteWrapper byteWrapper2(sourceBuffer); myInt32Encoder->decode(byteWrapper2);

// Step 5: Extract decoded value myVehicle.fuelLevel = myInt32Encoder->getValue();

// Step 6: Release allocated objects delete [] targetBuffer; delete myInt32Encoder;

Example 2, HLAunicodeString: // Step 1: Create encoder object for HLAinteger32BE HLAunicodeString* myStrEncoder = new HLAunicodeString();

// Step 2: Populate encoder object with data to encode myStrEncoder->setValue(myVehicle.name);

// Step 3: Ask encoder object to produce encoded representation char* targetBuffer = new char[myStrEncoder->getEncodedLength()]; ByteWrapper byteWrapper1(targetBuffer); myStrEncoder->encode(byteWrapper1);

// Step 4: Decode received byte array using existing encoder object ByteWrapper byteWrapper2(sourceBuffer); myStrEncoder->decode(byteWrapper2);

// Step 5: Extract decoded value myVehicle.name = myStrEncoder->getValue();

// Step 6: Release allocated objects delete [] targetBuffer; delete myStrEncoder;

Example 3, HLAfixedRecord: // Step 1: Create encoder object for HLAfixedRecord HLAfloat64BE* xEncoder = new HLAfloat64BE(); HLAfloat64BE* yEncoder = new HLAfloat64BE(); HLAfloat64BE* zEncoder = new HLAfloat64BE(); HLAfixedRecord* posEncoder = new HLAfixedRecord(); posEncoder->add(xEncoder); posEncoder->add(yEncoder); posEncoder->add(zEncoder);

// Step 2: Populate encoder object with data to encode xEncoder->setValue(myVehicle.x); yEncoder->setValue(myVehicle.y); zEncoder->setValue(myVehicle.z);

// Step 3: Ask encoder object to produce encoded representation char* targetBuffer = new char[posEncoder->getEncodedLength()]; ByteWrapper byteWrapper1(targetBuffer); posEncoder->encode(byteWrapper1);

// Step 4: Decode received byte array using existing encoder object ByteWrapper byteWrapper2(sourceBuffer); posEncoder->decode(byteWrapper2);

// Step 5: Extract decoded value myVehicle.x = xEncoder->getValue(); myVehicle.y = yEncoder->getValue(); myVehicle.z = zEncoder->getValue();

// Step 6: Release allocated objects delete [] targetBuffer; delete posEncoder; delete xEncoder; delete yEncoder;

delete zEncoder;

Example 4, HLAvariableArray: // Step 1: Create encoder object for HLAvariableArray and set values HLAvariableArray* arrayEncoder = new HLAvariableArray(); for (int i = 0; i < myRadarTrack.size(); i++) { Location location = myRadarTrack[i]; HLAfloat64BE* xEncoder = new HLAfloat64BE(); HLAfloat64BE* yEncoder = new HLAfloat64BE(); HLAfloat64BE* zEncoder = new HLAfloat64BE(); HLAfixedRecord* posEncoder(encoderFactory.createHLAfixedRecord()); posEncoder->add(xEncoder); posEncoder->add(yEncoder); posEncoder->add(zEncoder); xEncoder->setValue(location.x); yEncoder->setValue(location.y); zEncoder->setValue(location.z); arrayEncoder->addElement(posEncoder); }

// Step 2: Ask encoder object to produce encoded representation char* targetBuffer = new char[arrayEncoder->getEncodedLength()]; ByteWrapper byteWrapper1(targetBuffer); arrayEncoder->encode(byteWrapper1);

// Step 3: Create factory for array decoder elements class DataElementFactory_Position : public DataElementFactory { virtual DataElement* createElement(int index) { HLAfloat64BE* xEncoder = new HLAfloat64BE(); HLAfloat64BE* yEncoder = new HLAfloat64BE(); HLAfloat64BE* zEncoder = new HLAfloat64BE(); HLAfixedRecord* posEncoder(encoderFactory.createHLAfixedRecord()); posEncoder->add(xEncoder); posEncoder->add(yEncoder); posEncoder->add(zEncoder); return posEncoder; } };

// Step 4: Create decoder object

HLAvariableArray* arrayDecoder = new HLAvariableArray(DataElementFactory_Position());

// Step 5: Decode received byte array using decoder object ByteWrapper byteWrapper2(sourceBuffer); arrayDecoder->decode(byteWrapper2);

// Step 6: Extract decoded value for (int i = 0; i < arrayDecoder->size(); i++) { HLAfixedRecord* locationRecord = (HLAfixedRecord*)arrayDecoder.get(i); HLAfloat64BE* xDecoder = (HLAfloat64BE*)locationRecord.get(0); HLAfloat64BE* yDecoder = (HLAfloat64BE*)locationRecord.get(1); HLAfloat64BE* zDecoder = (HLAfloat64BE*)locationRecord.get(2); myTrack[i].x = xDecoder->getValue(); myTrack[i].y = yDecoder->getValue(); myTrack[i].z = zDecoder->getValue(); }

// Step 6: Release allocated objects delete [] targetBuffer; for (int i = 0; i < arrayDecoder->size(); i++) { HLAfixedRecord* locationRecord = (HLAfixedRecord*)arrayDecoder.get(i); delete locationRecord.get(0); delete locationRecord.get(1); delete locationRecord.get(2); delete locationRecord; } delete arrayDecoder; for (int i = 0; i < arrayEncoder->size(); i++) { HLAfixedRecord* locationRecord = (HLAfixedRecord*)arrayEncoder.get(i); delete locationRecord.get(0); delete locationRecord.get(1); delete locationRecord.get(2); delete locationRecord; } delete arrayEncoder;