1. Declaration Management
    1. Introduction
    2. This chapter introduces the RTIambassador service and FederateAmbassador callback methods that support declaration management. Declaration management includes publication, subscription and supporting control functions. Federates that produce objects (or object parts) or that produce interactions must declare exactly what they are able to publish (i.e., generate). Federates that consume objects (or object parts) or that consume interactions must declare their subscription interests.

      The RTI keeps track of what participating federates can produce and what they are interested in consuming and sends control signals to throttle what is produced based on consumer interest. As depicted in Figure 7-1, the RTI uses control signals to inform producers exactly what they should transmit. The goal is to keep traffic off the communications network.

      Figure 7-1. Control Signal Schema

    3. Object Vocabulary Review
    4. It's worth a moment to review some basic HLA terminology.

      Object classes are comprised of attributes. Object classes describe types of things that can persist. For example, "tank" might be an object class. Objects of type "tank" have certain attributes (e.g., size, weight, and range). Actual, real tanks are instances of the object class tank. The term "object" standing alone is sometimes used to describe an instance of a particular object class, but sometimes refers to the type information. Object classes may be related to cookie cutters and object instances to the cookies produced using the cookie cutters.

      Interaction classes are comprised of parameters. Interaction classes describe types of events. Interaction instances are specific events. It's fair to say, "Objects are similar to interactions in so much as objects are comprised of attributes and interactions are comprised of parameters." The HLA recognizes this inherent symmetry and leverages it when appropriate. The primary difference between objects and interactions is persistence. Objects persist, interactions do not.

      Would a missile be described by an object class or an interaction class? The answer depends on the simulation and the persistence of missile instances. A simulation that focuses on missile launchers and their targets may perceive missiles (or missile launches) as events. The launcher fires a missile, which effects some damage. The time that the missile is in the air may be trivial with respect to the simulation. Here, the missile could be modeled as an interaction – possibly between the launcher and the target. Another simulation may focus on the in-flight characteristics of missiles. The fact that the missile launches or impacts may be incidental. Here, the missile persists and should be modeled as an object.

    5. Object Hierarchies
    6. Figure 7-2 illustrates a class hierarchy and accompanying Venn diagram. Object classes and interaction classes can be constructed hierarchically. For example, assume that objects of type W are comprised of the attributes "a," "b," "c," and "d" – abbreviated "{a, b, c, d}." It is possible to define object classes that extend object class W. Object class W is extended to produce the object classes X and Z. Object class X is further extended to produce the object class Y.

      Figure 7-2. Class Hierarchy – Venn Diagram

      Object-oriented programming enthusiasts will recognize such hierarchical representations. Various communities use different phrases to describe object hierarchies. Some examples include:

      X extends W.
      W is a base type.
      X is derived from W.
      Y is a descendant of W.
      W is the parent of Z.

      Y inherits from X.
      W is an ancestor of Z.
      X is a child of W.
      Y and Z are leaf objects.

      The basic idea is that when an object class is extended to produce a new object class, the new object class contains all the attributes of the class being extended and possibly more. The object diagram and Venn diagram (Figure 7-2) illustrate the relationship between the object classes W, X, Y, and Z. Object class W has the four attributes {a, b, c, d}, class X adds the attributes {e, f, g} so instances of class X have attributes {a, b, c, d, e, f, g}.

    7. Publishing and Subscribing Objects
    8. Each federate must publish the object classes and interaction classes it plans to produce. It is possible for a federate to publish a subset of the available attributes for a given class.

      1. Object Publication
      2. The object class Y contains the attributes {a, b, c, d, e, f, g, h}. A federate can create instances of object class Y, without specifying all of the attributes associated with Y. For example, Y might be a particular kind of aircraft. A given federate may know some information about aircraft instances (e.g., position information), but rely on other federates to "fill in" the missing pieces (e.g., intelligence about the aircraft). In such a case, the federate would indicate that it can publish particular attributes associated with Y. In Figure 7-3, Federate #1 indicates that it can publish attributes {b, e, f, g, h} for object class Y.

        Each federate must indicate explicitly which attributes it can produce (i.e., introduce or update) on a per class basis. Multiple federates may be able to publish Y instances. Federate #3 might publish all of the attributes associated with object class Y. Federate #7 may be able to publish attributes {a, c, f} for Y.

        An implicit attribute, known by the name "privilegeToDelete," is included whenever a publication capability is registered for an object class. Only the federate that created a particular object instance is allowed to delete the instance unless the privilege to delete is conveyed to another Federate. Chapter 9, Ownership Management, explores the ability to exchange attribute update and object deletion responsibility among federates.

        Figure 7-3. Object Publishing

        A federate must explicitly state every object class it intends to produce via the RTIambassador's publishObjectClass() method. A separate call to publishObjectClass() is required for every object class including objects that appear in class hierarchies. If Federate #9 wishes to produce instances of object classes W, X, Y, and Z, it must say so explicitly using four publication calls.

      3. Interaction Publication
      4. As with object classes, each federate must state explicitly which interaction classes it intends to produce using the publishInteractionClass() method. Interactions are produced as "all or nothing." It isn't possible to specify which parameters in an interaction will be published. If a federate indicates that it intends to publish an interaction, it should be capable of specifying all parameters associated with the interaction.

      5. Object Subscription

Federates indicate their interest in certain object classes via the RTIambassador method subscribeObjectClassAttributes(). Object subscription differs from object publication. When a federate subscribes to an object class, it is expressing an interest in learning about all object instances of the class. For example, a federate subscribing to object class X (as shown in Figure 7-2) will discover all instances of class X produced by other federates in the federation. Additionally, a federate subscribing to X will discover all instances of class Y (produced by other federates) as though they were instances of class X. This is an example of type promotion.

Whenever a federate expresses a subscription interest in a particular object class, the RTI presumes that the federate is interested in instances of the descendant classes as well. A federate subscribing to class W would see external instances of classes X, Y, and Z as instances of class W. This can be a useful tool. Class W might represent all aircraft. Class X might represent military aircraft, while class Y represents commercial aircraft. A federate may wish to know about all aircraft, but not care about the details – including the military v. commercial designation.

A federate is informed about a new object instance if (a) the federate has subscribed to the object class of the instance or (b) the instance can be promoted (i.e., up the hierarchy) to a subscribed object class. When an object is promoted, attributes particular to the original class are dropped. An instance of object class Y has attributes {a, b, c, d, e, f, g, h}. A federate subscribing to object class X can discover the Y instance as an X. Since attribute "h" is not present in instances of class X, that information is lost.

A federate can subscribe to multiple classes in a class hierarchy. If a federate subscribed to class W and X, the following would be true:

When a federate discovers an object, it learns the object class of the instance. If the federate discovers the object instance to be of object class X, it will always believe the object’s type to be X. If a federate subscribes to class X and not to class Y, it will discover Y instances as X instances. If the federate subsequently subscribes to class Y, object instances previously discovered as X instances (via promotion) will continue to be seen as X instances. Subsequently discovered instances of object class Y will be discovered as instances of object class Y.

      1. Interaction Subscription
      2. As with object classes, each federate subscribes to the interaction classes it wishes to receive. It is not possible to subscribe to individual parameters of an interaction class. Again, interactions are "all or nothing." As with object classes, a federate is informed about a new interaction instance if (a) the federate has subscribed to the interaction class of the instance or (b) the instance can be promoted (i.e., up the hierarchy) to a subscribed interaction class. When an interaction instance is promoted, only the parameters of the subscribed class are presented to the receiving federate.

      3. Control Signals

In Figure 7-3, above, Federate #1 indicated that it was capable of producing Y instances, but could only provide the attributes {b, e, f, g, h}. In that same figure, Federate #2 subscribes to attributes {a, b, c, d, e} for object class X. The Y instances produced by Federate #1 are discovered as X instances by Federate #2.

Federate #2 is only interested in a few of the Y attributes produced by Federate #1. As discussed previously, Federate #2 cannot access attribute "h" since the attribute isn't a part of class X. Further, Federate #2 has no interest in attributes {f, g}. Of the information Federate #1 is able to produce Y:{b, e, f, g, h}, only the information Y:{b, e} is required – assuming Federate #2 is the only other federate in the federation.

The RTI issues control signals to indicate the information Federate #1 should produce. By default, a federate should refrain from producing object updates unless the Local RTI Component (LRC) has indicated that a consumer exists. If Federate #1 is first on the scene (i.e., there are no consumers), it will never be signaled to begin registering Y instance information.

Once Federate #2 arrives, the LRC will indicate to Federate #1 that it should register any instances of object class Y with the federation execution and it should start providing updates for Y:{b, e}. If Federate #2 goes away, Federate #1 will be told to stop registering instances of object class Y and to stop providing updates for Y:{b, e}.

Each LRC informs its federate (via callbacks) which object attributes and which interactions to start or stop producing based on consumer demand. Each federate’s Simulation Object Model (SOM) will identify the extent to which the federate does or does not make use of the control signals provided by the LRC.

    1. Object Publication and Subscription

Each federate is responsible for identifying its publication and subscription interests to the RTI LRC using the RTIambassador methods subscribeObjectClassAttributes()and publishObjectClass(). The interaction diagram shown in Figure 7-4, Object Publication and Subscription, illustrates the procedure for building the information required to use these methods.

The publish and subscribe methods both require an RTI::ObjectClassHandle and an RTI::AttributeHandleSet. The LRC has an internal (numeric) representation for object classes, object class attributes, interaction classes and interaction class parameter string representations that appear in the FED file. RTIambassador methods like getObjectClassHandle() and getAttributeHandle() translate character descriptions into LRC handles.

The (abstract) class RTI::AttributeHandleSet identifies a set of attributes – e.g., {a, b, c, d}. To express interest in publishing or subscribing to an object class, the following steps are required.

For each object class to be published:

    1. Obtain the handle for the current object class.
    2. Create a free-store allocated AttributeHandleSet using the static create() method in the class AttributeHandleSetFactory.
    3. For each attribute the federate can publish:
      1. Obtain the handle for the current attribute.
      2. Add the handle to the AttributeHandleSet

    4. Publish|Subscribe the AttributeHandleSet for the object class.

 

Figure 7-4. Object Publication and Subscription

 

    1. Throttling Publications
    2. The LRC signals a federate to start or stop registering object instances for all published object classes and generating interactions for all published interaction classes.

    3. FoodFight Object Declaration
    4. The following code excerpts demonstrate publication and subscription to the object class "Student" which has attributes "LunchMoney," "Cleanliness," and "AmmoAmount." The class name and attribute names would appear in the FED file. The student object class also has the hidden attribute "privilegeToDelete."

      1. Excerpt from Student.h

The following excerpt is taken from the declaration of the C++ class "Student." In this example, a C++ class is used to realize the HLA object class "Student." This is but one possible way of realizing an HLA object class. Information about students could be maintained in a database or a C structure.

Student.h

  1. :
  2. class Student
  3. {
  4. friend ostream& operator<< (ostream&, const Student&);
  5. public:
  6. // In the following enumeration, ATTRIBUTE_COUNT denotes the end of the
  7. // enumeration and is equal to the total number of attributes.
  8. enum AttributeNames { PRIVILEGE_TO_DELETE = 0, LUNCH_MONEY,
  9. CLEANLINESS, AMMO_AMOUNT, ATTRIBUTE_COUNT };
  10. static const char* attribute_names[];
  11. // Variables to store class handles.
  12. static RTI::ObjectClassHandle class_handle;
  13. // Array to store attribute/parameter handles.
  14. static RTI::AttributeHandle attributes[];
  15. static RTI::AttributeHandleSet* p_all_attribute_vector;
  16. static void RegisterObject ();
  17. :
  18. :
  19. // Student Characteristics
  20. RTI::ObjectHandle getId () const { return id_self; }
  21. double getLunchMoney () const { return lunch_money; }
  22. double getCleanliness () const { return cleanliness; }
  23. long getAmmoAmount () const { return ammo_amount; }
  24. protected:
  25. static const int BUY_AMMO_CHANCE;
  26. static const double AMMO_COST_MEAN;
  27. static const double AMMO_COST_ADJ;
  28. static const int ATTEMPT_LAUNCH_CHANCE;
  29. RTI::ObjectHandle id_self; // RTI ID by which this student is known.
  30. double lunch_money; // Funds for new ammo.
  31. double cleanliness; // Food damage the student has sustained.
  32. unsigned long ammo_amount; // Launchable food possessed.
  33. :
  34. };
  35. The static function Student::RegisterObject() will eventually contain the code that registers this federate's publication and subscription requests with regard to the object class Student. To support this static registration process, several static variables are declared. The Student::AttributeNames enumeration provides an identifier for each attribute in the HLA object class Student. The static handle Student::class_handle will contain the LRC's internal representation of the Student object class (type). The static array Student:: attribute_names will hold the string representation of each attribute in Student. The static array Student::attributes will house the LRC's internal handle for each Student attribute. Eventually, the pointer Student::p_all_attribute_vector will identify a free-store allocated attribute handle set containing all the attribute handles we wish to publish and subscribe.

    Student.cxx

    Definitions for Student static variables are as follows.

  36. :
  37. const char* Student::attribute_names[Student::ATTRIBUTE_COUNT] = {
  38. "privilegeToDelete", "LunchMoney", "Cleanliness", "AmmoAmount" };
  39. RTI::ObjectClassHandle Student::class_handle
  40. = RTI::ObjectClassHandle(); // Default constructor provides null handle.
  41. RTI::AttributeHandle Student::attributes[Student::ATTRIBUTE_COUNT];
  42. RTI::AttributeHandleSet* Student::p_all_attribute_vector = 0;
  43. :
  44.  

    The publish and subscribe registration process is conducted by the static function Student::RegisterObject().

  45. void
  46. Student::RegisterObject ()
  47. {
  48. // Abstract
  49. // Register the class. Then, register each attribute recording the
  50. // corresponding handle in the vector of all attributes (e.g.,
  51. // p_all_attribute_vector).
  52. const char* object_name = "Student";
  53. cout << "Registering '" << object_name << "'." << endl;
  54. class_handle = ::rtiAmb.getObjectClassHandle(object_name);
  55. if (p_all_attribute_vector) delete p_all_attribute_vector;
  56. p_all_attribute_vector
  57. = RTI::AttributeHandleSetFactory::create(Student::ATTRIBUTE_COUNT);
  58. for (int i = 0; i < ATTRIBUTE_COUNT; i++ )
  59. {
  60. cout << "\tGetting attribute handle for '" << attribute_names[i]
  61. << "'." << endl;
  62. RTI::AttributeHandle handle = ::rtiAmb.getAttributeHandle(
  63. attribute_names[i],
  64. class_handle);
  65. // Here, we maintain an array of handles and an attribute vector
  66. // that contains all the handles for this class.
  67. attributes[i] = handle;
  68. p_all_attribute_vector->add(handle);
  69. }
  70. // Publish and subscribe to all attributes.
  71. ::rtiAmb.publishObjectClass(class_handle, *p_all_attribute_vector);
  72. ::rtiAmb.subscribeObjectClassAttributes(
  73. class_handle,
  74. *p_all_attribute_vector);
  75. // NB: For now, not bothering to delete p_all_attribute_vector. It will
  76. // live as long as the program does.
  77. }

 

RegisterObject() closely follows the interactions identified in Figure 7.4, Object Publication and Subscription.

      1. Dynamic Object Publication and Subscription

Each call to publishObjectClass() and subscribeObjectClassAttributes() for an object class replaces previous calls. The methods unpublishObjectClass() and unsubscribeObjectClass() should be called when a federate is no longer interested in any attributes of an object class.

    1. Publishing and Subscribing Interactions

Registering publication and subscription interest in interaction classes is more straightforward than registering interest in object classes. Figure 7-5, Declaring Interactions, identifies RTIambassador declaration management methods interactions. Interactions are "all or nothing." Unlike object registration, you cannot specify interest in particular interaction parameters.

Splat.h

  1. class Splat
  2. {
  3. friend ostream& operator<< (ostream& os, const Splat& splat);
  4. public:
  5. enum ParameterNames { ENSUING_MESS, TARGET, PARAMETER_COUNT};
  6. static const char* parameter_names[];
  7. // Variables to store class handles.
  8. static RTI::InteractionClassHandle interaction_handle;
  9. // Array to store attribute/parameter handles.
  10. static RTI::ParameterHandle parameters[];
  11. static void RegisterInteraction ();
  12. :
  13. Figure 7-5. Declaring Interactions

    Splat.cxx

  14. const char* Splat::parameter_names[Splat::PARAMETER_COUNT] = {
  15. "EnsuingMess", "Target" };
  16. RTI::InteractionClassHandle Splat::interaction_handle
  17. = RTI::InteractionClassHandle(); // Null handle.
  18. RTI::ParameterHandle Splat::parameters[Splat::PARAMETER_COUNT];
  19. :
  20. :
  21. void
  22. Splat::RegisterInteraction ()
  23. {
  24. const char* interaction_name = "Splat";
  25. cout << "Registering '" << interaction_name << "'." << endl;
  26. // Register the interaction.
  27. interaction_handle = ::rtiAmb.getInteractionClassHandle(interaction_name);
  28. for (int i = 0; i < PARAMETER_COUNT; i++ )
  29. {
  30. cout << "\tGetting parameter handle for '" << parameter_names[i]
  31. << "'." << endl;
  32. parameters[i] = ::rtiAmb.getParameterHandle(
  33. parameter_names[i],
  34. interaction_handle);
  35. }
  36. // Publish and subscribe interaction.
  37. ::rtiAmb.publishInteractionClass(interaction_handle);
  38. ::rtiAmb.subscribeInteractionClass(interaction_handle);
  39. }

 

As with object class declaration, interaction interest can be declared dynamically. Each call to publishInteractionClass() and subscribeInteractionClass() for an interaction class replaces previous calls. The methods unpublishInteractionClass() and unsubscribeInteractionClass() should be called when a federate is no longer interested in an interaction class.