/* File: ProtocolDataUnit.java CVS Info: $Id: ProtocolDataUnit.java,v 1.7 1998/02/03 17:29:44 mcgredo Exp $ Compiler: jdk 1.3 */ package mil.navy.nps.dis; import mil.navy.nps.util.*; // General-purpose utilities import mil.navy.nps.disEnumerations.*; // Enumerations for DIS import java.io.*; import java.net.*; import java.util.*; /** * Top-level abstract (uninstantiated) class for all PDUs. * *@version 1.0 *@author Don McGregor (http://www.npsnet.org/~mcgredo) * *
* * I generally declare instance variables to be protected, which means that * they are directly accessible from this class and all subclasses. Those * who believe in full-jackboot mode will want them declared private, so * subclasses can't access them either.
* * The accessor methods (getProtocolVersion et al) are declared public, so * that anyone, including those outside this package, can access them. You * should always go through accessor methods when setting values inside an * object. Direct access from outside the class can cause mysterious errors * that are very hard to track down.
* * Note that, due to garbage collection, we don't have to worry about the * status of orphaned instance variables. For example, this operation will * cause memory leaks in C++ or Obj-C:
*
* protocolVersion = newProtocolVersion; ** In C++, this operation would leave an orphaned object; the _old_ object * that was held by protocolVersion would have no valid pointers to it, but * would still take up memory. With GC, the old, orphaned protocolVersion's * memory will be scavanged and returned to use.
* * Accessor methods return a copy of the thing they're getting, not the * actual thing. This prevents violations of encapsulation. If this weren't * the case, we might see something like this:
*
* this would return another pointer to the same object contained inside of
* aPdu. addOne() would modify the value inside of aPdu, a violation of
* encapsulation. So instead we implement getLength() like this:
*myLength = aPdu.getLength();
*myLength.addOne();
*
*
*
*
* this creates an identical copy of length and returns that. The calling
* method can modify that to its heart's content without affecting aPdu.
* For the same reason, the clone() method should make copies of all the
* instance variables. Otherwise, the "new" object will have pointers
* shared with the old object. The clone() operation also helps prevent
* big dependency meshes for GC.
*public UnsignedByte getLength()
*{ return (UnsignedByte)length.clone();
*}
*
* * Serialization should make a call to the superclass before it does its own * instance variables.
* * Note that we do not include the padding variables in the instance variables * of objects. Padding is used only in the serialized, external representation * of a PDU. The user would have no reason to do anything with padding in * an object. Since it is only an artifact of serialization, references to * padding are limited to the serialization/deserialization methods.
* * Also, the length, a field in the header portion, is calculated on the fly * rather than saved and set. The header has a fixed length, 18 bytes. To find * the size of a PDU, implement the length() method in all the subclasses. This * should make a call to super, do any local calculations required, then return * the correct number. Your code might look like :
*
*
* public int length() {
* return super.length() + 110 + aVector.size * anElement.length();
* }
*
* * This adds up the length of the superclass, such as the PDU header, * the basic length of the subclass (such as the entity state PDU), and * adds the length of any variable number of parameters attached to the * PDU.
* *
protocolVersion=5
*No default value is set.
*
**
sizeOf = 12 bytes
.
*/
public static final int sizeOf = 12; // size of header when written out
/**
*Constructor for a default protocol data unit. No field is initialized.
*/
public ProtocolDataUnit()
{
//*********************************************************************
if(rtpHeaderEnabled){
rtpHeader = new RtpHeader();
}
//*********************************************************************
protocolVersion = new UnsignedByte(ProtocolVersionField.IEEE127811995); // Set to IEEE 1995 standard = 5
exerciseID = new UnsignedByte(1);
/**
* @see mil.navy.nps.disEnumerations.PduTypeField for legal values
*/
pduType = new UnsignedByte(42);
/* The protocolFamily refers to the general class of PDU. This can help client
software sort incoming pdus to the correct bins for faster processing.
Valid values are below:
Value Meaning
----- -------
0 other
1 entity information/interaction
2 warfare
3 logistics
4 radio communications
5 simulation management
See the enumerations for definitive values.
*/
protocolFamily = new UnsignedByte(ProtocolFamilyField.OTHER); // Default "other" value
// Sim managaement
if(this instanceof SimulationManagementFamily)
protocolFamily = new UnsignedByte(ProtocolFamilyField.SIMULATIONMANAGEMENT);
// Radio comms
if(this instanceof RadioCommunicationsFamily)
protocolFamily = new UnsignedByte(ProtocolFamilyField.RADIOCOMMUNICATION);
// Logistics not used yet, would be set to 3
// The "warfare family" pdus consist of fire and detonation.
if((this instanceof DetonationPdu) || (this instanceof FirePdu))
protocolFamily = new UnsignedByte(ProtocolFamilyField.WARFARE);
// Entity information/interaction consists of ESPDU and collision
if((this instanceof CollisionPdu) || (this instanceof EntityStatePdu))
protocolFamily = new UnsignedByte(ProtocolFamilyField.ENTITYINFORMATIONINTERACTION); // Default of one, entity info/interaction
// timestamp = new UnsignedInt();
// Timestamp, set to VRML DEFINITION OF TIME
this.makeTimestampCurrent();
return;
}
public Object clone()
{
ProtocolDataUnit newPdu = (ProtocolDataUnit)super.clone();
newPdu.setProtocolVersion(this.getProtocolVersion());
newPdu.setExerciseID(this.getExerciseID());
newPdu.setPduType(this.getPduType());
newPdu.setProtocolFamily(this.getProtocolFamily());
newPdu.makeTimestampCurrent();
return newPdu;
}
/**
In a number of places we need to generate strings of a
specific length of spaces. this does that. This could
probably be more efficent....
Fun fact: new String(char array of spaces) generates garbage
*/
public static StringBuffer getPaddingOfLength(int pIndent)
{
StringBuffer buf = new StringBuffer();
for(int idx = 0; idx < pIndent; idx++)
{
buf.append(' ');
}
return buf;
}
public void printValues(int indentLevel, PrintStream printStream)
{
// print the values of the object out, with correct level of
// indentation on the page.
StringBuffer buf = ProtocolDataUnit.getPaddingOfLength(indentLevel);
printStream.println(buf + "protocolVersion: " + protocolVersion.intValue());
printStream.println(buf + "exerciseID: " + exerciseID.intValue());
printStream.println(buf + "pduType: " + pduType.intValue());
printStream.println(buf + "protocolFamily: " + protocolFamily.intValue());
printStream.println(buf + "timestamp: " + timestamp.longValue());
printStream.println(buf + "length: " + this.length());
return;
}
/**
*This methods sets the Timestamp to the current Time.
*String
*/
public abstract String pduName ();
public String toString ()
{
return pduName (); // PduTypeField.toString (pduVersion.intValue());
}
// Accessor methods
public UnsignedByte getProtocolVersion()
{ return (UnsignedByte)protocolVersion.clone();
}
public void setProtocolVersion(UnsignedByte pProtocolVersion)
{ protocolVersion = pProtocolVersion;
}
public void setProtocolVersion(int pProtocolVersion)
{ protocolVersion = new UnsignedByte(pProtocolVersion);
}
public UnsignedByte getExerciseID()
{ return (UnsignedByte)exerciseID.clone();
}
public void setExerciseID(UnsignedByte pExerciseID)
{ exerciseID = pExerciseID;
}
public void setExerciseID(int pExerciseID)
{ exerciseID = new UnsignedByte(pExerciseID);
}
/**
* @see mil.navy.nps.disEnumerations.PduTypeField for legal values
*/
public UnsignedByte getPduType()
{ return (UnsignedByte)pduType.clone();
}
/**
* @see mil.navy.nps.disEnumerations.PduTypeField for legal values
*/
public void setPduType(UnsignedByte pPduType)
{ pduType = pPduType;
}
/**
* @see mil.navy.nps.disEnumerations.PduTypeField for legal values
*/
public void setPduType(short pPduType)
{ pduType = new UnsignedByte(pPduType);
}
public UnsignedByte getProtocolFamily()
{ return (UnsignedByte)protocolFamily.clone();
}
public void setProtocolFamily(UnsignedByte pProtocolFamily)
{ protocolFamily = pProtocolFamily;
}
public void setProtocolFamily(int pProtocolFamily)
{ protocolFamily = new UnsignedByte(pProtocolFamily);
}
public UnsignedInt getTimestamp()
{ return (UnsignedInt)timestamp.clone();
}
public void setTimestamp(UnsignedInt pTimestamp)
{ timestamp = pTimestamp;
}
public void setTimestamp(long pTimestamp)
{ timestamp = new UnsignedInt(pTimestamp);
}
/**
*Returns the length of the PDU header.
*
*@return the length of the PDU header (currently 12 bytes). Note that
* this should NOT include the length of the RTP header.
*/
public int length()
{
// return ProtocolDataUnit.sizeOf; // we're always this long
//******************************************************
// if(rtpHeaderEnabled){
// return ProtocolDataUnit.sizeOf + RtpHeader.sizeOf;
// }
// else{
return ProtocolDataUnit.sizeOf;
// }
//**********************************************************
}
public void serialize(DataOutputStream outputStream)
{
UnsignedShort padding = new UnsignedShort(0); // padding to bring pdu up to word boundary
UnsignedShort length = new UnsignedShort(this.length()); // how long this PDU is, calculated on the fly
debug ("serializing in PDU");
//*****************************************************************************
if(rtpHeaderEnabled){
rtpHeader.prepareToSend(this);
rtpHeader.serialize(outputStream);
}
//*****************************************************************************
protocolVersion.serialize(outputStream);
exerciseID.serialize(outputStream);
pduType.serialize(outputStream);
protocolFamily.serialize(outputStream);
timestamp.serialize(outputStream);
length.serialize(outputStream);
padding.serialize(outputStream);
debug ("exiting serialize in ProtocolDataUnit");
return;
}
public void deSerialize(DataInputStream inputStream)
{
// Read from a stream. The order in which variables are read is significant.
UnsignedShort padding = new UnsignedShort(0); // padding--read just to be thrown away
UnsignedShort length = new UnsignedShort(0); // length--read just to be thrown away
//*******************************************************************************
UnsignedByte firstByte = new UnsignedByte();
firstByte.deSerialize(inputStream);
try
{
inputStream.reset();
}
catch(IOException ioException)
{
throw new
RuntimeException("deSerialize: Exception in ProtocolDataUnit. Error resetting stream.");
}
setRtpHeaderEnabled (false);
debug ("deSerialize: firstByte=" + firstByte + ", RtpHeader.RTP_VERSION * 64 = " + RtpHeader.RTP_VERSION * 64);
if( firstByte.intValue() == ( RtpHeader.RTP_VERSION * 64) )
{
setRtpHeaderEnabled (true);
rtpHeader.deSerialize(inputStream);
}
//**********************************************************************************
protocolVersion.deSerialize(inputStream);
exerciseID.deSerialize(inputStream);
pduType.deSerialize(inputStream);
protocolFamily.deSerialize(inputStream);
timestamp.deSerialize(inputStream);
length.deSerialize(inputStream); // Nothing is done with this value
padding.deSerialize(inputStream); // Nothing is done with this value
return;
}
/**
* Legacy compatability method
*/
public static ProtocolDataUnit datagramToPdu(DatagramPacket pDatagramPacket)
{
byte byteArray[] = pDatagramPacket.getData();
return ProtocolDataUnit.byteArrayToPdu(byteArray);
}
/**
*Returns a PDU completely read in from the byte array in parameter.
*
*
* ProtocolDataUnit
* |
* ^
* EntityStatePDU
*
* The deserialize message goes first to the EntityStatePdu. The ESPDU
* immediately calls the deserialize method in its superclass, ProtocolDataUnit.
* PDU deserializes the first few instance variables, then returns. The ESPDU
* then deserializes its ivars.
* Prints out some information during exectuion if debugging flag is set.
*