HelloWorld program tries to simulate the population growth of particular country provided that the initial population and the number of years for the simulation entered from command line. This simple example explains the detailes about HLA/RTI software so that we can build more sophisticated simulations by using this communication bus.
Demo explains how we can define a particular federate and interact with the RTI software. This demos allows us to define a HelloWorld federation and then we can see the simulation results of the other joined countries to this federation. We discover them on the fly (if they exist) and send our updates (if it is required) without knowing other participants.
There are five objects in this demo. Country object represents the Country data, EntityManager keeps the track of discovered Country objects and their states, SimCountry object represents the country that this instance is simulating, HwFederateAmbassador object is the callback object to receive RTI calls and HelloWorld object controls the whole simulation. HelloWorld object completes the required declaration procedures, runs the main simulation loop and clears all the definitions related to this simulation instance.
To communicate with RTI we need to get the handle of RTIAmbassador object.
Therefore we first obtain the RTIKernel object and
ask it a RTIAmbassador object. Current design defines RTIAmbassador object
for each federate in the server side (not necessarily on the same machine).
The next version of this software will allow definition of this object
on the client side and hide the necessary CORBA mechanism from the user.
/**
*/
public static void
main(String args[])
{
try
{
// Create and initialize the ORB
ORB orb = ORB.init(args, null);
/* ----- This Part is required if we are using Naming Service ---
// Get the root naming context
org.omg.CORBA.Object objRef = orb.resolve_initial_references("NameService");
NamingContext ncRef = NamingContextHelper.narrow(objRef);
// Resolve the object reference in naming
NameComponent nc = new NameComponent("RTIKernel", " ");
NameComponent path[] = {nc};
RTIKernelInterface rtiKernel = RTIKernelInterfaceHelper.narrow(ncRef.resolve(path));
*/
try
{
BufferedReader input = new BufferedReader(new FileReader("../IOR"));
String strIOR = input.readLine(); // get IOR address of RTIKernelInterface
if( strIOR != null )
{
RTIKernelInterface rtiKernel = RTIKernelInterfaceHelper.narrow(orb.string_to_object(strIOR));
// Get the Version of the Kernel
String version = rtiKernel.getVersion();
System.out.println(" Version is : "+version);
if(args.length>2)
{
RTIambassador rtiAmb = rtiKernel.createRTIAmbassador();
HelloWorld hello = new HelloWorld(rtiAmb,args[0],Double.valueOf(args[1]).doubleValue(),Integer.parseInt(args[2]),orb);
hello.start();
}// end of if
} else System.out.println(" IOR file is empty| \n "+
"I can not find the address of RTIKernelInterface ");
} catch(java.io.FileNotFoundException fnf) {}
catch(java.io.IOException ioe) {}
catch(NumberFormatException ioe) {}
} catch(Exception e)
{
System.out.println("ERROR : " + e);
e.printStackTrace(System.out);
}
}// end of main()
All the process is being handled in the run() method of HelloWorld object.
Therefore, we will go through the each line of this method and try to
explain what the purpose of a particular code is.
/**
*/
public void
run()
{
int count = 0;
/*******************************************************************/
/** Define FederateAmbassador for HelloWorld. This objects serves */
/** as a callback mechanism so that RTI can bring us the necessary */
/** events. */
/*******************************************************************/
HwFederateAmbassador fedAmb = new HwFederateAmbassador(EntityMng,this);
orb.connect(fedAmb);
Define a callback object so that HelloWorld control object can receive the
RTI calls via this object.
/************************************************************/
/** Create FederationExecution, get RTI id of the objects, */
/** attributes, interactions, and parameters. */
/** Define interest for Subscription/Publishing. */
/** Define my simulated object. */
/************************************************************/
if( joinFederationExecution(fedAmb) && getRtiIds() &&
publishSubscribe() && registerMyCountry() )
{
We need to join the federation execution.
boolean joinFederationExecution(RTIcap.FederateAmbassador fedAmb)
{
boolean ret = false;
if( rtiAmb != null )
{
try {
rtiAmb.createFederationExecution("HelloWorld","HelloWorld.fed");
} catch( RTIcap.FederationExecutionAlreadyExists icnd) {}
catch( RTIcap.RTIinternalError ie) {}
try {
federateId = rtiAmb.joinFederationExecution(myCountryName,"HelloWorld",fedAmb);
ret = true;
} catch( RTIcap.FederateAlreadyExecutionMember fnem) {}
catch( RTIcap.FederationExecutionDoesNotExist icnd) {}
catch( RTIcap.CouldNotOpenFED fnem) {}
catch( RTIcap.ErrorReadingFED fnem) {}
catch( RTIcap.SaveInProgress sip) {}
catch( RTIcap.RestoreInProgress rip) {}
catch( RTIcap.RTIinternalError ie) {}
}// end of if
return ret;
}// end of joinFederationExecution()
Ask RTI to create a FederationExecution named as HelloWorld.
RTI finds the HelloWorld.fed file and creates this federation execution
according to those parameters (OMT definition) if this federation does not
exist.
If it is, RTI send the proper exception (FederationExecutionAlreadyExists).
After we make sure that federation execution is defined somehow, then we need to register this instance with the FederateAmbassador object. If we can join this FederationExecution, then RTI gives a uniqe federate id as a return value.
Now we need to find out the exact object class ids, object attribute ids,
interaction ids and parameter ids assigned by the RTI.
boolean getRtiIds()
{
boolean ret = false;
if( rtiAmb != null )
{
try
{
countryTypeId = rtiAmb.getObjectClassHandle(countryTypeStr);
} catch( RTIcap.FederateNotExecutionMember icnd ) {}
catch( RTIcap.NameNotFound icnd ) {}
catch( RTIcap.RTIinternalError icnd ) {}
try
{
countryNameTypeId =
rtiAmb.getAttributeHandle(countryNameTypeStr,countryTypeId );
} catch( RTIcap.ObjectClassNotDefined icnd ) {}
catch( RTIcap.NameNotFound icnd ) {}
catch( RTIcap.FederateNotExecutionMember icnd ) {}
catch( RTIcap.RTIinternalError icnd ) {}
try
{
countryPopuTypeId =
rtiAmb.getAttributeHandle(countryPopuTypeStr, countryTypeId );
} catch( RTIcap.ObjectClassNotDefined icnd ) {}
catch( RTIcap.NameNotFound icnd ) {}
catch( RTIcap.FederateNotExecutionMember icnd ) {}
catch( RTIcap.RTIinternalError icnd ) {}
try
{
countryCommTypeId =
rtiAmb.getInteractionClassHandle(countryCommTypeStr);
} catch( RTIcap.NameNotFound icnd ) {}
catch( RTIcap.FederateNotExecutionMember icnd ) {}
catch( RTIcap.RTIinternalError icnd ) {}
try
{
countryCommMsgTypeId =
rtiAmb.getParameterHandle(countryCommMsgTypeStr,countryCommTypeId);
ret = true;
} catch( RTIcap.InteractionClassNotDefined icnd ) {}
catch( RTIcap.NameNotFound icnd ) {}
catch( RTIcap.FederateNotExecutionMember icnd ) {}
catch( RTIcap.RTIinternalError icnd ) {}
/** Place them into a hashtable */
}// end of if
return ret;
}// end of getRtiIds()
Object classes, attributes, interaction classes and parameters are accessed
with their names. These names are defined in the FED file (HelloWorld.fed).
First we ask the id of Country object class(getObjectClassHandle). Then we can get the id of attributes Name and Population(getAttributeHandle).
We do the same for interaction class Communication and its parameter Message with getInteractionClassHandle and getParameterHandle calls.
Since we know the each object and interaction class related information, we
can define our interest about sending/receiving these object updates and
interactions when they occur in the federatation execution.
boolean publishSubscribe()
{
boolean ret = false;
if( rtiAmb != null )
{
short[] myAttributes = new short[2];
myAttributes[0] = (short) countryNameTypeId;
myAttributes[1] = (short) countryPopuTypeId;
try
{
rtiAmb.subscribeObjectClassAttributes( countryTypeId, myAttributes , RTIcap._Boolean.RTI_TRUE);
} catch( RTIcap.ObjectClassNotDefined flsc) {}
catch( RTIcap.AttributeNotDefined fnem) {}
catch( RTIcap.FederateNotExecutionMember fnem) {}
catch( RTIcap.RTIinternalError ie) {}
catch( RTIcap.RestoreInProgress rip) {}
catch( RTIcap.SaveInProgress sip) {}
try
{
rtiAmb.publishObjectClass( countryTypeId, myAttributes );
} catch( RTIcap.ObjectClassNotDefined flsc) {}
catch( RTIcap.AttributeNotDefined fnem) {}
catch( RTIcap.FederateNotExecutionMember fnem) {}
catch( RTIcap.RTIinternalError ie) {}
catch( RTIcap.RestoreInProgress rip) {}
catch( RTIcap.SaveInProgress sip) {}
try
{
rtiAmb.subscribeInteractionClass( countryCommTypeId, RTIcap._Boolean.RTI_TRUE);
} catch( RTIcap.FederateLoggingServiceCalls flsc) {}
catch( RTIcap.FederateNotExecutionMember fnem) {}
catch( RTIcap.RTIinternalError ie) {}
catch( RTIcap.RestoreInProgress rip) {}
catch( RTIcap.InteractionClassNotDefined icnd) {}
catch( RTIcap.SaveInProgress sip) {}
try
{
rtiAmb.publishInteractionClass( countryCommTypeId );
ret = true;
} catch( RTIcap.FederateNotExecutionMember fnem) {}
catch( RTIcap.RestoreInProgress rip) {}
catch( RTIcap.InteractionClassNotDefined icnd) {}
catch( RTIcap.SaveInProgress sip) {}
catch( RTIcap.RTIinternalError ie) {}
}// end of if
return ret;
}// end of publishSubscribe()
First we subscribe the object class (Country). This subscription will inform us all the instances of Country object in the federation execution and the value of their Name and Population attributes. It is possible to subscribe only a group of attributes instead of all of them. Then we inform RTI that we are going to publish an instance of Country object and we are going to provide the given attributes for this instance.
We subscribed for the interaction class (Communication) and all its parameters(subscribeInteractionClass). We informed the RTI that we are going to publish these interactions also(publishInteractionClass).
Now we told RTI what kind of messages we might produce and process. We need
to define our simulation object and register it through RTI so that it can
be perceived by other interested federates in the execution.
boolean registerMyCountry()
{
boolean ret = false;
int objInstanceId;
if( rtiAmb != null )
{
try
{
objInstanceId = rtiAmb.registerObjectInstance(countryTypeId);
myCountry = new SimCountry(rtiAmb,this,myCountryName,myPopulation,objInstanceId);
ret = true;
} catch( RTIcap.ObjectClassNotDefined sip) {}
catch( RTIcap.ObjectClassNotPublished sip) {}
catch( RTIcap.SaveInProgress sip) {}
catch( RTIcap.RestoreInProgress rip) {}
catch( RTIcap.FederateNotExecutionMember icnd) {}
catch( RTIcap.RTIinternalError ie) {}
}// end of if
return ret;
}// end of registerMyCountry()
We asked an object instance id from RTI so that RTI can define this object in its internal tables for us and sends the appropriate messages to other parties. For each update of the country object we are simulating, we inform RTI with this given object instance id so that RTI can accept this updates provided that we are allowed to this update for this object's attribute(Ownership Management).
We defined our intentions related to objects and interactions
but we did not tell anything about the Time Management strategy we want to
use. We have to decide whether we want to contribute the calculation
of current time in the
federation(enableTimeRegulation/disableTimeRegulation).
Similarly, we have to decide whether we want to receive events in Time
Stamp Ordered (TSO) or not(enableTimeConstrained/disableTimeConstrained);
/*********************************************************************/
/** TimeConstrained : we want to receive timestamped events in order */
/*********************************************************************/
resetTimeAdvanceGranted();
try
{
rtiAmb.enableTimeConstrained( );
} catch( RTIcap.TimeAdvanceAlreadyInProgress aa ) {}
catch( RTIcap.EnableTimeConstrainedPending aa ) {}
catch( RTIcap.TimeConstrainedAlreadyEnabled aa ) {}
catch( RTIcap.FederateNotExecutionMember aa ) {}
catch( RTIcap.SaveInProgress aa ) {}
catch( RTIcap.RestoreInProgress aa ) {}
catch( RTIcap.RTIinternalError aa ) {}
/***************************************************/
/** Tick RTI to get timeConstrainedEnabled message */
/***************************************************/
tickRTI(2);
We want to receive events in TSO therefore we send
enableTimeConstrained message. We tick the RTI to receive the
result of this call.
/**********************************************************/
/** TimeRegulating : We want to vote for time advancement */
/**********************************************************/
resetTimeAdvanceGranted();
try
{
rtiAmb.enableTimeRegulation( currentTime, 5.0 );
} catch( RTIcap.TimeAdvanceAlreadyInProgress aa ) {}
catch( RTIcap.EnableTimeRegulationPending aa ) {}
catch( RTIcap.ConcurrentAccessAttempted aa ) {}
catch( RTIcap.TimeRegulationAlreadyEnabled aa ) {}
catch( RTIcap.InvalidLookahead aa ) {}
catch( RTIcap.InvalidFederationTime aa ) {}
catch( RTIcap.FederateNotExecutionMember aa ) {}
catch( RTIcap.SaveInProgress aa ) {}
catch( RTIcap.RestoreInProgress aa ) {}
catch( RTIcap.RTIinternalError aa ) {}
/**************************************************/
/** Tick RTI to get timeRegulationEnabled message */
/**************************************************/
tickRTI(2);
We want to contribute the time evaluation of the federation
therefore we send
enableTimeRegulation message. We tick the RTI to receive the
result of this call. This also informs us about the current time in the
federation.
try
{
rtiAmb.enableAsynchronousDelivery( );
}
catch( RTIcap.FederateNotExecutionMember aa ) {}
catch( RTIcap.AsynchronousDeliveryAlreadyEnabled aa ) {}
catch( RTIcap.ConcurrentAccessAttempted aa ) {}
catch( RTIcap.SaveInProgress aa ) {}
catch( RTIcap.RestoreInProgress aa ) {}
catch( RTIcap.RTIinternalError aa ) {}
We want to receive the messages if they do not have any time stamp.
/**************************************************/
/** Add my simulation object to the EntityManager */
/**************************************************/
EntityMng.add( myCountry.getObjectID(), myCountry );
Register our simulation object to the EntityManager. Note that
EntityManager keeps the other simulated objects as well as the object
simulated by this HelloWorld instance.
The following loop will be executed the number of times we provided from the command line. In this simulation loop we do the followings:
/***************/ /** Event Loop */ /***************/ while( count++ < MaximumNumberOfIterations ) { /***************************************************/ /** Simulate the current population for my country */ /***************************************************/ myCountry.UpdateTime(currentTime); /***************************************************************/ /** Print state of all the simulated objects in the Simulation */ /***************************************************************/ printSimObjects(); /*****************/ /** Advance time */ /*****************/ resetTimeAdvanceGranted(); requestTime = currentTime + timeStep; /************************************/ /** Try to advance the current time */ /************************************/ do { try { rtiAmb.timeAdvanceRequest( requestTime ); } catch( RTIcap.TimeAdvanceAlreadyInProgress aa ) {} catch( RTIcap.EnableTimeConstrainedPending aa ) {} catch( RTIcap.EnableTimeRegulationPending aa ) {} catch( RTIcap.InvalidFederationTime aa ) {} catch( RTIcap.FederationTimeAlreadyPassed aa ) {} catch( RTIcap.FederateNotExecutionMember aa ) {} catch( RTIcap.SaveInProgress aa ) {} catch( RTIcap.RestoreInProgress aa ) {} catch( RTIcap.RTIinternalError aa ) {} mySleep(2); } while( !isTimeAdvanceGranted() ); }// end of while()
Since the simulation loop is over we need to leave the federation and
inform the RTI about our intention so that it can update its internal
state.
/************************************/
/** Leave the Federation Execution */
/************************************/
leaveFederationExecution();
}// end of if
}// end of run()
Before we leave the federation we need to delete the simulation object we
defined (deleteObjectInstance).
Then we resign from the federation
execution(resignFederationExecution) and
try to destroy the federation
execution(destroyFederationExecution).
If we are the last federate, then federation execution will be destroyed.
void leaveFederationExecution()
{
if( rtiAmb != null )
{
try
{
rtiAmb.deleteObjectInstance(myCountry.getObjectID(),"destroy");
} catch( RTIcap.DeletePrivilegeNotHeld ie) {}
catch( RTIcap.ObjectNotKnown ie) {}
catch( RTIcap.FederateNotExecutionMember ie) {}
catch( RTIcap.SaveInProgress ie) {}
catch( RTIcap.RestoreInProgress ie) {}
catch( RTIcap.RTIinternalError ie) {}
try
{
rtiAmb.resignFederationExecution(RTIcap.ResignAction.DELETE_OBJECTS_AND_RELEASE_ATTRIBUTES);
} catch( RTIcap.InvalidResignAction ie) {}
catch( RTIcap.FederateOwnsAttributes ie) {}
catch( RTIcap.FederateNotExecutionMember ie) {}
catch( RTIcap.RTIinternalError ie) {}
try
{
rtiAmb.destroyFederationExecution("HelloWorld");
} catch( RTIcap.FederationExecutionDoesNotExist icnd) {}
catch( RTIcap.FederatesCurrentlyJoined ie) {}
catch( RTIcap.RTIinternalError ie) {}
}// end of if
}// end of leaveFederationExecution()
Receiving/Sending Events From/To RTI
In the previous section we explained how you can start interact with RTI software but we did not explain how you can update the attributes of a simulated object or receive messages from RTI.
Sending Population Attribute Update
UpdateTime() method of
SimCountry
object is being called in each simulation loop.
public final void
UpdateTime( double time )
{
double diff = time - currentTime;
if( diff > 0 )
{
setCurrentTime(time);
/*******************************/
/** Set the population changed */
/*******************************/
setPopuChanged();
UpdatePopulation(myPopulation+diff*grRate);
UpdateName( getName() );
}// end of if
}// end of Update()
This method updates the population and executes UpdatePopulation() and
UpdateName() methods.
public final void
UpdatePopulation( double newPop )
{
myPopulation = newPop ;
/********************************************************************/
/** If we are allowed to send this update and population is changed,*/
/** then we will send this update message to the RTI. */
/********************************************************************/
if( rtiAmb != null && sendPopuUpdateFlag && isPopuChanged() )
{
RTIcap.AttributeHandleValuePair[] eles = new RTIcap.AttributeHandleValuePair[1];
eles[0] = new RTIcap.AttributeHandleValuePair(
boss.getPopuAttributeId(),
Double.toString(myPopulation).getBytes());
try
{
rtiAmb.updateAttributeValues(ObjectID,eles,"population");
resetPopuChanged();
} catch (RTIcap.ObjectNotKnown onk) {}
catch (RTIcap.AttributeNotDefined onk) {}
catch (RTIcap.AttributeNotOwned onk) {}
catch (RTIcap.FederateNotExecutionMember onk) {}
catch (RTIcap.SaveInProgress onk) {}
catch (RTIcap.RestoreInProgress onk) {}
catch (RTIcap.RTIinternalError onk) {}
}// end of if
}// end of Update()
If we need to send this update(sendPopuUpdateFlag) and the population is changed, then we send updateAttributeValues message to RTI. This message contains the object instance id, id of attribute and value of attribute (coded into a byte array).
RTI informs us whether it requires attribute update or not. Assume that we are the only participant of an execution or there is nobody who wants to receive our updates in an execution, then there is no point for us to send this update message and use the network. When RTI informs that update is required UpdateControls() method of SimCountry will be called.
Recieving Messages from RTI
FederateAmbassador object is defined for callback purposes so that RTI sends messages back to the federates via this object. It is the responsibility of the developer to handle each message properly.
We defined HwFederateAmbassador object which implements RTIcap.FederateAmbassador interface. We will try to explain each method we handled in this code.
Discovering New Object Instance
public void
discoverObjectInstance(int theObject, short theObjectClass)
throws RTIcap.CouldNotDiscover, RTIcap.ObjectClassNotKnown,
RTIcap.InvalidFederationTime, RTIcap.FederateInternalError
{
Country c = entManager.getEntity(theObject);
if(c==null) entManager.add(theObject,new Country(theObject));
}// end of discoverObjectInstance()
RTI send discoverObjectInstance message to federate whenever a new instance of a particular object class is defined. The first parameter gives the object id and the second gives the object class id.
Here we look at the EntityManager object whether this object is already discovered by us. If not, we defined a Country object for this new object and add it to EntityManager.
Removing Object Instance
Whenever an object instance removed from execution, RTI informs all the
federates which received discover message before about this event by
sending removeObjectInstance or
removeObjectInstanceWithTime message.
public void
removeObjectInstance(int theObject, String theTag)
throws RTIcap.ObjectNotKnown, RTIcap.FederateInternalError
{
Country c = entManager.getEntity(theObject);
if(c!=null) entManager.remove(theObject);
}// end of removeObjectInstanceWith()
When we receive this message, we remove the representation of this object from the EntityManager.
Turn Updates On/Off For Object Instance
RTI sends turnUpdatesOffForObjectInstance message when the
update of a particular attribute is no longer required.
public void
turnUpdatesOffForObjectInstance(int theObject, short[] theAttributes)
throws RTIcap.ObjectNotKnown, RTIcap.AttributeNotOwned,
RTIcap.FederateInternalError
{
Country c = entManager.getEntity(theObject);
if( c != null && c instanceof SimCountry )
((SimCountry)c).UpdateControls(false,theAttributes);
}// end of turnUpdatesOffForObjectInstance()
Code asks EntityManager about the object and executes the UpdateControls() method of the SimCountry object. This will disable the attribute updates for the given object.
Provide Attribute Updates For Object Instance
When the update of an attribute is required by somebody in the federation,
RTI sends this message to the owner of that attribute. It is the
responsibility of the owner to provide the update.
public void
provideAttributeValueUpdate(int theObject, short[] theAttributes)
throws RTIcap.ObjectNotKnown, RTIcap.AttributeNotKnown,
RTIcap.AttributeNotOwned, RTIcap.FederateInternalError
{
Country c = entManager.getEntity(theObject);
if(c!=null && c instanceof SimCountry )
{
for(int i=0;i < theAttributes.length;i++)
if( theAttributes[i] == boss.getNameAttributeId() )
((SimCountry)c).setNameChanged();
else if( theAttributes[i] == boss.getPopuAttributeId() )
((SimCountry)c).setPopuChanged();
}// end of if
}// end of provideAttributeValueUpdate()
Code informs the SimCountry object to send the update of the particular attribute.
Receiving Attribute Updates
Whenever RTI receives an update of an object attribute, it sends the
reflectAttributeValues message to interested
participants(suscribers).
public void
reflectAttributeValues(int theObject,
RTIcap.AttributeHandleValuePair[] theAttributes, String theTag)
throws RTIcap.ObjectNotKnown, RTIcap.AttributeNotKnown,
RTIcap.FederateOwnsAttributes, RTIcap.FederateInternalError
{
Country c = entManager.getEntity(theObject);
if( c != null )
{
for(int i=0;i < theAttributes.length;i++)
{
if( theAttributes[i].handle == boss.getNameAttributeId() )
c.setName(new String(theAttributes[i].value));
else if( theAttributes[i].handle == boss.getPopuAttributeId()
)
c.setPopulation(Double.valueOf(new String(theAttributes[i].value)).doubleValue());
}// end of for
} else System.out.println(theObject+" is not known ");
}// end of reflectAttributeValues()
First we find the proper Country object in the EntityManager. According to the handle of attribute, we update Name or Population attributes.
List Of Source Files
Prepared by | : | H. Timucin Ozdemir |
Date | : | July 01, 1998 |