
edu.nps.moves.disutil.PduFactory Maven / Gradle / Ivy
package edu.nps.moves.disutil;
import java.nio.ByteBuffer;
import java.util.logging.*;
import java.util.*;
import edu.nps.moves.dis.*;
import edu.nps.moves.disenum.PduType;
/**
* Simple factory for PDUs. When bytes are received on the wire, they're passed
* off to us and the correct constructor called to return the correct PDU
* type.
*
* @author DMcG
*/
public class PduFactory {
private Logger logger;
public PduFactory() {
// By default don't log anything
logger = Logger.getLogger(PduFactory.class.getName());
logger.setLevel(Level.OFF);
}
/**
* Set the logging level that will be printed, typically to Level.INFO
*
* @param loggingLevel
*/
public void setLoggingLevel(Level loggingLevel) {
logger.setLevel(loggingLevel);
}
/**
* PDU factory. Pass in an array of bytes, get the correct type of pdu back,
* based on the PDU type field contained in the byte array.
*
* @param data
* @return A PDU of the appropriate concrete subclass of PDU
*/
public Pdu createPdu(byte data[]) {
return createPdu(ByteBuffer.wrap(data));
}
/**
* PDU factory. Pass in an array of bytes, get the correct type of pdu back,
* based on the PDU type field contained in the byte buffer.
*
* @param buff
* @return null if there was an error creating the Pdu
*/
public Pdu createPdu(java.nio.ByteBuffer buff) {
final int pduType = peekAtPduType(buff);
if (pduType == -1) {
return null;
}
Pdu aPdu = null;
if (pduType < 129) {
// Normal pdu type code range is 0..128 inclusive.
final PduType pduTypeEnum = PduType.lookup[pduType];
switch (pduTypeEnum) {
// NOTE: OTHER is a valid pduTypeEnum, but has no corresponding object
case ENTITY_STATE:
aPdu = new EntityStatePdu();
break;
case FIRE:
aPdu = new FirePdu();
break;
case DETONATION:
aPdu = new DetonationPdu();
break;
case COLLISION:
aPdu = new CollisionPdu();
break;
case SERVICE_REQUEST:
aPdu = new ServiceRequestPdu();
break;
case RESUPPLY_OFFER:
aPdu = new ResupplyOfferPdu();
break;
case RESUPPLY_RECEIVED:
aPdu = new ResupplyReceivedPdu();
break;
case RESUPPLY_CANCEL:
aPdu = new ResupplyCancelPdu();
break;
case REPAIR_COMPLETE:
aPdu = new RepairCompletePdu();
break;
case REPAIR_RESPONSE:
aPdu = new RepairResponsePdu();
break;
case CREATE_ENTITY:
aPdu = new CreateEntityPdu();
break;
case REMOVE_ENTITY:
aPdu = new RemoveEntityPdu();
break;
case START_RESUME:
aPdu = new StartResumePdu();
break;
case STOP_FREEZE:
aPdu = new StopFreezePdu();
break;
case ACKNOWLEDGE:
aPdu = new AcknowledgePdu();
break;
case ACTION_REQUEST:
aPdu = new ActionRequestPdu();
break;
case ACTION_RESPONSE:
aPdu = new ActionResponsePdu();
break;
case DATA_QUERY:
aPdu = new DataQueryPdu();
break;
case SET_DATA:
aPdu = new SetDataPdu();
break;
case DATA:
aPdu = new DataPdu();
break;
case EVENT_REPORT:
aPdu = new EventReportPdu();
break;
case COMMENT:
aPdu = new CommentPdu();
break;
case ELECTROMAGNETIC_EMISSION:
aPdu = new ElectronicEmissionsPdu();
break;
case DESIGNATOR:
aPdu = new DesignatorPdu();
break;
case TRANSMITTER:
aPdu = new TransmitterPdu();
break;
case SIGNAL:
aPdu = new SignalPdu();
break;
case RECEIVER:
aPdu = new ReceiverPdu();
break;
// FIXME: IFF_ATC_NAVAIDS (28)
case UNDERWATER_ACOUSTIC:
aPdu = new UaPdu();
break;
case SUPPLEMENTAL_EMISSION_ENTITY_STATE:
aPdu = new SeesPdu();
break;
case INTERCOM_SIGNAL:
aPdu = new IntercomSignalPdu();
break;
case INTERCOM_CONTROL:
aPdu = new IntercomControlPdu();
break;
case AGGREGATE_STATE:
aPdu = new AggregateStatePdu();
break;
case ISGROUPOF:
aPdu = new IsGroupOfPdu();
break;
case TRANSFER_CONTROL:
aPdu = new TransferControlRequestPdu();
break;
case ISPARTOF:
aPdu = new IsPartOfPdu();
break;
case MINEFIELD_STATE:
aPdu = new MinefieldStatePdu();
break;
case MINEFIELD_QUERY:
aPdu = new MinefieldQueryPdu();
break;
case MINEFIELD_DATA:
aPdu = new MinefieldDataPdu();
break;
case MINEFIELD_RESPONSE_NAK:
aPdu = new MinefieldResponseNackPdu();
break;
case ENVIRONMENTAL_PROCESS:
aPdu = new EnvironmentalProcessPdu();
break;
case GRIDDED_DATA:
aPdu = new GriddedDataPdu();
break;
case POINT_OBJECT_STATE:
aPdu = new PointObjectStatePdu();
break;
case LINEAR_OBJECT_STATE:
aPdu = new LinearObjectStatePdu();
break;
case AREAL_OBJECT_STATE:
aPdu = new ArealObjectStatePdu();
break;
// FIXME: case TSPI: (46)
// FIXME: case APPEARANCE: (47)
// FIXME: case ARTICULATED_PARTS: (48)
// FIXME: case LE_FIRE: (49)
// FIXME: case LE_DETONATION: (50)
case CREATE_ENTITY_R:
aPdu = new CreateEntityReliablePdu();
break;
case REMOVE_ENTITY_R:
aPdu = new RemoveEntityReliablePdu();
break;
case START_RESUME_R:
aPdu = new StartResumeReliablePdu();
break;
case STOP_FREEZE_R:
aPdu = new StopFreezeReliablePdu();
break;
case ACKNOWLEDGE_R:
aPdu = new AcknowledgeReliablePdu();
break;
case ACTION_REQUEST_R:
aPdu = new ActionRequestReliablePdu();
break;
case ACTION_RESPONSE_R:
aPdu = new ActionResponseReliablePdu();
break;
case DATA_QUERY_R:
aPdu = new DataQueryReliablePdu();
break;
case SET_DATA_R:
aPdu = new SetDataReliablePdu();
break;
case DATA_R:
aPdu = new DataReliablePdu();
break;
case EVENT_REPORT_R:
aPdu = new EventReportReliablePdu();
break;
case COMMENT_R:
aPdu = new CommentReliablePdu();
break;
// FIXME: case RECORD_R: (63)
case SET_RECORD_R:
aPdu = new SetRecordReliablePdu();
break;
case RECORD_QUERY_R:
aPdu = new RecordQueryReliablePdu();
break;
case COLLISION_ELASTIC:
aPdu = new CollisionElasticPdu();
break;
case ENTITY_STATE_UPDATE:
aPdu = new EntityStateUpdatePdu();
break;
case IFF_ATC_NAVAIDS:
aPdu = new IffAtcNavAidsLayer1Pdu();
break;
default:
logger.log(Level.INFO, "PDU not implemented. Type = " + pduType + "\n");
if (pduTypeEnum != null) {
logger.log(Level.INFO, " PDU name is: " + pduTypeEnum.getDescription());
}
} // end switch
} else {
// Received an experimental pdu type. Type 129..255.
logger.log(Level.INFO, "Received experimental PDU Type " + pduType + ". Not supported.");
aPdu = new ExperimentalPdu();
}
if (aPdu != null) {
aPdu.unmarshal(buff);
}
return aPdu;
}
/**
* Decodes datagram contents with bundled PDUs. As a performance hack DIS
* may include several PDUs in one datagram. Typically the max datagram size
* is 8K (above that it runs into some issues with the default incoming
* socket buffer size) but it may be more. The PDUs may be of multiple types
* and different lengths, so we have to step through the buffer and depend
* on the reported PDU length in the header. There's a lot that can go
* wrong. If something blows up, we return all the decoded PDUs we can.
*
* @param data
* @return List of PDUs decoded
*/
public List getPdusFromBundle(byte data[]) {
// All the PDUs in this bundle we were able to decode
ArrayList pdus = new ArrayList();
// The start point of a PDU in the data. We advance this by the size
// of each PDU as we read it.
int pduStartPointInData = 0;
while (true) {
// This is inefficient, but screw it. Give the GC a workout. Create a new
// data array from where the last PDU left off to the end of the original
// data array. This lets us reuse a bunch of old code.
byte remaining[] = Arrays.copyOfRange(data, pduStartPointInData, data.length);
try {
// Decode one PDU
Pdu pdu = this.createPdu(remaining);
// If the read is muffed somehow, give up on decoding the rest of
// the data
if (pdu == null) {
//System.out.println("Stopped reading bundled PDU due to bad PDU");
break;
} else // otherwise add it to the list of PDUs we have decoded from this UDP packet
{
pdus.add(pdu);
}
// Advance the index to the start of the next PDU
int pduLength = pdu.getPduLength();
pduStartPointInData = pduStartPointInData + pduLength;
//System.out.println("PDUStartPOint:" + pduStartPointInData + " data: " + data.length);
// Have we read all the data?
if (pduStartPointInData >= data.length) {
//System.out.println("Out of data to read" + pduStartPointInData + " data length:" + data.length);
break;
}
} catch (Exception e) {
System.out.println("Problems decoding multiple PDUs in datagram; decoded as may as possible");
break;
}
} // end while
return pdus;
}
/**
* For checking the pdu type within the buffer before unmarshalling.
*/
private static int peekAtPduType(java.nio.ByteBuffer buff) {
int pos = buff.position(); // Save buffer's position
if (pos + 2 > buff.limit()) { // Make sure there's enough space in buffer
return -1; // Else return
} // end if: buffer too short
buff.position(pos + 2); // Advance to third byte
final int pduType = Pdu.toUnsignedInt(buff.get()); // Read Pdu type
buff.position(pos); // Reset buffer
return pduType;
}
}