Fixed Records

Sometimes, an information only makes sense if it is accompanied by a group of data, such as geographical position, Cartesian coordinates or an address. The OMT provides a data type that allows transfer information grouped: the HLAfixedRecord. Unfortunately it is not very practical to implement the code to use it, it is necessary to encode each attribute individually and depending on the amount attributes, it can be a nightmare.

First, you must declare the type structure in your FOM/SOM:

<dataTypes>

    <fixedRecordDataTypes>
        <fixedRecordData>
            <name>Position</name>
            <encoding>HLAfixedRecord</encoding>
            <semantics>Position in geographic coordinates.</semantics>
            <field>
                <name>longitude</name>
                <dataType>HLAfloat64BE</dataType>
                <semantics>Longitude</semantics>
            </field>
            <field>
                <name>latitude</name>
                <dataType>HLAfloat64BE</dataType>
                <semantics>Latitude</semantics>
            </field>
        </fixedRecordData>
    </fixedRecordDataTypes>    

</dataTypes>	

Here, you can see my record called Position. It have two attributes: longitude and latitude of type HLAfloat64BE. A note here: try to avoid using basic types directly ( like I do with HLAfloat64BE ).  I suggest you to create your own data type like coordinateValue of type HLAfloat64BE or something like that.

You can now use it as attribute type in your class:

<attribute>
    <name>TankPosition</name>
    <dataType>Position</dataType>
    <updateType>Conditional</updateType>
    <updateCondition>On Change</updateCondition>
    <ownership>NoTransfer</ownership>
    <sharing>PublishSubscribe</sharing>
    <transportation>HLAreliable</transportation>
    <order>Receive</order>
    <semantics>Coordinates of the Unit</semantics>
</attribute>

You can publish and/or  subscribe to this attribute like usual:

AttributeHandle positionHandle = rtiamb.getAttributeHandle( tankHandle, "TankPosition" );
AttributeHandleSet attributes = rtiamb.getAttributeHandleSetFactory().create();
attributes.add( positionHandle );
rtiamb.publishObjectClassAttributes( tankHandle, attributes );
rtiamb.subscribeObjectClassAttributes( tankHandle, attributes );

But send/receive updates is a bit complex. First, I created a Java class for the Position:

public class Position {
  private double longitude = 0;
  private double latitude = 0;

  public Position( double longitude, double latitude ) {
    this.latitude = latitude;
    this.longitude = longitude;
  }

  public double getLatitude() {
    return latitude;
  }

  public double getLongitude() {
    return longitude;
  }

  public void setLatitude(double latitude) {
    this.latitude = latitude;
  }

  public void setLongitude(double longitude) {
    this.longitude = longitude;
  }
}

This class will be a component of my Tank class:

Class Hierarchy

Class Hierarchy

Here is my Java class. I don’t care about basic units here so I don’t extend BasicUnit. Learn more here.

public class TankObject {
  private ObjectInstanceHandle instance;
  private String model;
  private Position position;
  private String name;
  private String serial;

  public ObjectInstanceHandle getHandle() {
    return instance;
  }
  
  ...
  
}

Now, I need two functions to help me encode/decode the data.

public byte[] encodePosition( Position position ) throws Exception {
  HLAfixedRecord value = encoderFactory.createHLAfixedRecord();
  HLAfloat64BE longitude = encoderFactory.createHLAfloat64BE();
  HLAfloat64BE latitude = encoderFactory.createHLAfloat64BE();

  longitude.setValue( position.getLongitude() );
  latitude.setValue( position.getLatitude() );
  
  value.add(longitude);
  value.add(latitude);
  
  return value.toByteArray();
}

public Position decodePosition( byte[] bytes ) throws Exception {
  HLAfixedRecord value = encoderFactory.createHLAfixedRecord();
  HLAfloat64BE longitude = encoderFactory.createHLAfloat64BE();
  HLAfloat64BE latitude = encoderFactory.createHLAfloat64BE();
  value.add(longitude);
  value.add(latitude);
  value.decode(bytes);
  return new Position( longitude.getValue(), latitude.getValue() );
}

As you can see, I need to encode/decode each record attribute “by hand”. I can’t remember now if the order the records are added to the HLAfixedRecord is important. If you know more about this please feel free to contact me and share this information. By now, consider the order is important.

To send attribute updates…

HLAunicodeString modelValue = encoderFactory.createHLAunicodeString( tank.getModel() );
HLAunicodeString nameValue = encoderFactory.createHLAunicodeString( tank.getName() );
HLAunicodeString serialValue = encoderFactory.createHLAunicodeString( tank.getSerial() );

AttributeHandleValueMap attributes = rtiamb.getAttributeHandleValueMapFactory().create(4);
attributes.put( modelHandle, modelValue.toByteArray() );
attributes.put( nameHandle, nameValue.toByteArray() );
attributes.put( serialHandle, serialValue.toByteArray() );
attributes.put( positionHandle, encodePosition( tank.getPosition() ) );			

rtiamb.updateAttributeValues( tank.getHandle(), attributes, "The Tag I Like".getBytes() );

… and how to receive attribute updates…

@Override
public void reflectAttributeValues( ObjectInstanceHandle theObject,
                              AttributeHandleValueMap theAttributes,
                              byte[] tag,
                              OrderType sentOrder,
                              TransportationTypeHandle transport,
                              SupplementalReflectInfo reflectInfo ) throws FederateInternalError {
    // the code below
}

…by using my decodePosition function ( see above ):

for ( TankObject tank : allMyTanks ) {	
  if( tank.getHandle().equals( theObject) ) {

    for( AttributeHandle attributeHandle : theAttributes.keySet() ) {
      // Verify other attributes. Removed for keeping simple.
      if( attributeHandle.equals( positionHandle) ) {
        tank.setPosition( decodePosition( theAttributes.get(attributeHandle) ) );
      }
    }
    break;
  }
}

I do a lot of CTRL+C / CTRL+V / adapt from my functional code here. If you find some mistake, please contact me.

 

Add Comment