mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 06:18:44 +00:00
Added boat location, and race start messages to the mock data interface
- Added proper support for signed and unsigned types. This includes automatic conversion to the correct data type (long to int, short, or byte). - Moved code related to adding values into the byte buffer into the abstract Message class Tags: #story[29]
This commit is contained in:
@@ -15,6 +15,7 @@ public class ServerThread implements Runnable{
|
||||
private StreamingServerSocket server;
|
||||
private final int HEARTBEAT_PERIOD = 5000;
|
||||
private final int RACE_STATUS_PERIOD = 1000;
|
||||
private final int BOAT_LOCATION_PERIOD = 1000/5;
|
||||
private final int PORT_NUMBER = 8085;
|
||||
|
||||
public ServerThread(String threadName){
|
||||
@@ -68,6 +69,19 @@ public class ServerThread implements Runnable{
|
||||
100, 3, RaceType.MATCH_RACE, 1, boats);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return A list of sample boat location messages
|
||||
*/
|
||||
public List<Message> getTestBoatLocationMessages(){
|
||||
List<Message> messages = new ArrayList<>();
|
||||
|
||||
messages.add(new BoatLocationMessage(1, 1, 100, 200, 231, 23));
|
||||
messages.add(new BoatLocationMessage(2, 2, 400, 300, 211, 13));
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
|
||||
public void run() {
|
||||
try{
|
||||
server = new StreamingServerSocket(PORT_NUMBER);
|
||||
@@ -105,7 +119,7 @@ public class ServerThread implements Runnable{
|
||||
try {
|
||||
server.send(hb);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
System.out.print("");
|
||||
}
|
||||
}
|
||||
}, 0, HEARTBEAT_PERIOD);
|
||||
@@ -120,11 +134,27 @@ public class ServerThread implements Runnable{
|
||||
try {
|
||||
server.send(statusMessage);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
System.out.print("");
|
||||
}
|
||||
}
|
||||
}, 100, RACE_STATUS_PERIOD);
|
||||
|
||||
t1.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
List<Message> messages = getTestBoatLocationMessages();
|
||||
|
||||
for (Message m : messages){
|
||||
try {
|
||||
server.send(m);
|
||||
} catch (IOException e) {
|
||||
System.out.print("");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}, 100, BOAT_LOCATION_PERIOD);
|
||||
|
||||
} catch (IOException e) {
|
||||
System.err.println(e.getMessage());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,109 @@
|
||||
package seng302.server.messages;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class BoatLocationMessage extends Message {
|
||||
private final int MESSAGE_SIZE = 56;
|
||||
|
||||
private long messageVersionNumber;
|
||||
private long time;
|
||||
private long sourceId;
|
||||
private long sequenceNum;
|
||||
private DeviceType deviceType;
|
||||
private long latitude;
|
||||
private long longitude;
|
||||
private long altitude;
|
||||
private long heading;
|
||||
private long pitch;
|
||||
private long roll;
|
||||
private long boatSpeed;
|
||||
private long COG;
|
||||
private long SOG;
|
||||
private long apparentWindSpeed;
|
||||
private long apparentWindAngle;
|
||||
private long trueWindSpeed;
|
||||
private long trueWindDirection;
|
||||
private long trueWindAngle;
|
||||
private long currentDrift;
|
||||
private long currentSet;
|
||||
private long rudderAngle;
|
||||
|
||||
/**
|
||||
* Describes the location, altitude and sensor data from the boat.
|
||||
* @param sourceId ID of the boat
|
||||
* @param sequenceNum Sequence number of the message
|
||||
* @param latitude The boats latitude
|
||||
* @param longitude The boats longitude
|
||||
* @param heading The boats heading
|
||||
* @param boatSpeed The boats speed
|
||||
*/
|
||||
public BoatLocationMessage(int sourceId, int sequenceNum, long latitude, long longitude, long heading, long boatSpeed){
|
||||
messageVersionNumber = 1;
|
||||
time = System.currentTimeMillis() / 1000L;
|
||||
this.sourceId = sourceId;
|
||||
this.sequenceNum = sequenceNum;
|
||||
this.deviceType = DeviceType.RACING_YACHT;
|
||||
this.latitude = -latitude;
|
||||
this.longitude = longitude;
|
||||
this.altitude = 0;
|
||||
this.heading = heading;
|
||||
this.pitch = 0;
|
||||
this.roll = 0;
|
||||
this.boatSpeed = boatSpeed;
|
||||
this.COG = 0;
|
||||
this.SOG = 0;
|
||||
this.apparentWindSpeed = 0;
|
||||
this.apparentWindAngle = 0;
|
||||
this.trueWindSpeed = 0;
|
||||
this.trueWindDirection = 0;
|
||||
this.trueWindAngle = 0;
|
||||
this.currentDrift = 0;
|
||||
this.currentSet = 0;
|
||||
this.rudderAngle = 0;
|
||||
|
||||
setHeader(new Header(MessageType.BOAT_LOCATION, 1, (short) getSize()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return 56;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(DataOutputStream outputStream) {
|
||||
allocateBuffer();
|
||||
writeHeaderToBuffer();
|
||||
|
||||
putByte((byte) messageVersionNumber);
|
||||
putInt((int) time, 6);
|
||||
putInt((int) sourceId, 4);
|
||||
putUnsignedInt((int) sequenceNum, 4);
|
||||
putByte((byte) deviceType.getCode());
|
||||
putInt((int) latitude, 4);
|
||||
putInt((int) longitude, 4);
|
||||
putInt((int) altitude, 4);
|
||||
putUnsignedInt((int) heading, 2);
|
||||
putInt((int) pitch, 2);
|
||||
putInt((int) roll, 2);
|
||||
putUnsignedInt((int) boatSpeed, 2);
|
||||
putUnsignedInt((int) COG, 2);
|
||||
putUnsignedInt((int) SOG, 2);
|
||||
putUnsignedInt((int) apparentWindSpeed, 2);
|
||||
putInt((int) apparentWindAngle, 2);
|
||||
putUnsignedInt((int) trueWindSpeed, 2);
|
||||
putUnsignedInt((int) trueWindDirection, 2);
|
||||
putInt((int) trueWindAngle, 2);
|
||||
putUnsignedInt((int) currentDrift, 2);
|
||||
putUnsignedInt((int) currentSet, 2);
|
||||
putInt((int) rudderAngle, 2);
|
||||
|
||||
|
||||
writeCRC();
|
||||
try {
|
||||
outputStream.write(getBuffer().array());
|
||||
} catch (IOException e) {
|
||||
System.out.print("");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,12 @@
|
||||
package seng302.server.messages;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* The status of each boat, sent within a race status message
|
||||
*/
|
||||
public class BoatSubMessage {
|
||||
public class BoatSubMessage{
|
||||
private final int MESSAGE_SIZE = 20;
|
||||
|
||||
private long sourceId;
|
||||
@@ -57,22 +58,22 @@ public class BoatSubMessage {
|
||||
buff.position(buffPos);
|
||||
|
||||
// Boat Status, 1 byte
|
||||
buff.put(ByteBuffer.allocate(1).put((byte) boatStatus.getCode()).array());
|
||||
buff.put(ByteBuffer.allocate(1).put((byte) (boatStatus.getCode() & 0xff)).array());
|
||||
buffPos += 1;
|
||||
buff.position(buffPos);
|
||||
|
||||
// Leg number, 1 byte
|
||||
buff.put(ByteBuffer.allocate(1).put((byte) legNumber).array());
|
||||
buff.put(ByteBuffer.allocate(1).put((byte) (legNumber & 0xff)).array());
|
||||
buffPos += 1;
|
||||
buff.position(buffPos);
|
||||
|
||||
// Number of penalties awarded, 1 byte
|
||||
buff.put(ByteBuffer.allocate(1).put((byte) numberPenaltiesAwarded).array());
|
||||
buff.put(ByteBuffer.allocate(1).put((byte) (numberPenaltiesAwarded & 0xff)).array());
|
||||
buffPos += 1;
|
||||
buff.position(buffPos);
|
||||
|
||||
// Number of penalties served, 1 byte
|
||||
buff.put(ByteBuffer.allocate(1).put((byte) numberPenaltiesServed).array());
|
||||
buff.put(ByteBuffer.allocate(1).put((byte) (numberPenaltiesServed & 0xff)).array());
|
||||
buffPos += 1;
|
||||
buff.position(buffPos);
|
||||
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package seng302.server.messages;
|
||||
|
||||
public enum DeviceType {
|
||||
UNKNOWN(0),
|
||||
RACING_YACHT(1);
|
||||
|
||||
private long code;
|
||||
|
||||
DeviceType(long code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public long getCode(){
|
||||
return code;
|
||||
}
|
||||
}
|
||||
@@ -26,24 +26,15 @@ public class Heartbeat extends Message {
|
||||
public void send(DataOutputStream outputStream) {
|
||||
setHeader(new Header(MessageType.HEARTBEAT, 0x01, (short) getSize()));
|
||||
|
||||
ByteBuffer buff = ByteBuffer.allocate(Header.getSize() + getSize() + getSize());
|
||||
allocateBuffer();
|
||||
writeHeaderToBuffer();
|
||||
|
||||
// Write header
|
||||
buff.put(getHeader().getByteBuffer());
|
||||
buff.position(Header.getSize());
|
||||
putUnsignedInt(seqNo, 4);
|
||||
|
||||
// Write seq num
|
||||
buff.put(ByteBuffer.allocate(4).putInt(seqNo).array());
|
||||
buff.position(Header.getSize()+4);
|
||||
|
||||
// Write CRC
|
||||
CRC32 crc = new CRC32();
|
||||
crc.update(buff.array());
|
||||
|
||||
buff.put(ByteBuffer.allocate(4).putInt((short)crc.getValue()).array());
|
||||
writeCRC();
|
||||
|
||||
try {
|
||||
outputStream.write(buff.array());
|
||||
outputStream.write(getBuffer().array());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -1,21 +1,27 @@
|
||||
package seng302.server.messages;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.zip.CRC32;
|
||||
|
||||
public abstract class Message {
|
||||
Header header;
|
||||
private final int CRC_SIZE = 4;
|
||||
private Header header;
|
||||
private ByteBuffer buffer;
|
||||
private int bufferPosition;
|
||||
private CRC32 crc;
|
||||
|
||||
/**
|
||||
* @param header Set the header for this message
|
||||
*/
|
||||
public void setHeader(Header header){
|
||||
void setHeader(Header header){
|
||||
this.header = header;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the header specified for this message
|
||||
*/
|
||||
public Header getHeader(){
|
||||
Header getHeader(){
|
||||
return header;
|
||||
}
|
||||
|
||||
@@ -28,4 +34,124 @@ public abstract class Message {
|
||||
* Send the message as through the outputStream
|
||||
*/
|
||||
public abstract void send(DataOutputStream outputStream);
|
||||
|
||||
/**
|
||||
* Allocate byte buffer to correct size
|
||||
*/
|
||||
void allocateBuffer(){
|
||||
buffer = ByteBuffer.allocate(Header.getSize() + getSize() + CRC_SIZE);
|
||||
bufferPosition = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write the set header to the byte buffer
|
||||
*/
|
||||
void writeHeaderToBuffer(){
|
||||
buffer.put(getHeader().getByteBuffer());
|
||||
bufferPosition += Header.getSize();
|
||||
buffer.position(bufferPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Move the buffer position by n bytes
|
||||
* @param size Number of bytes to move the buffer by
|
||||
*/
|
||||
private void moveBufferPositionBy(int size){
|
||||
bufferPosition += size;
|
||||
buffer.position(bufferPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put an unsigned byte in the buffer
|
||||
*/
|
||||
void putUnsignedByte(byte b){
|
||||
buffer.put(ByteBuffer.allocate(1).put((byte) (b & 0xff)).array());
|
||||
moveBufferPositionBy(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Put an signed byte in the buffer
|
||||
*/
|
||||
void putByte(byte b){
|
||||
buffer.put(ByteBuffer.allocate(1).put(b).array());
|
||||
moveBufferPositionBy(1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Place an unsigned integer of the specified length in the buffer
|
||||
* @param val The integer value to add (Note: This must be long due to java not supporting unsigned integers)
|
||||
* @param size The size of the int to be added to the buffer
|
||||
*/
|
||||
void putUnsignedInt(long val, int size){
|
||||
if (size <= 1){
|
||||
putUnsignedByte((byte) val);
|
||||
|
||||
}
|
||||
else if (size < 4){
|
||||
// Use short
|
||||
buffer.put(ByteBuffer.allocate(size).putShort((short) (val & 0xffff)).array());
|
||||
moveBufferPositionBy(size);
|
||||
}
|
||||
else{
|
||||
// Use int
|
||||
buffer.put(ByteBuffer.allocate(size).putInt((int) (val & 0xffffffffL)).array());
|
||||
moveBufferPositionBy(size);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Put a signed int of a specified length in the buffer
|
||||
* @param val The integer value to add
|
||||
* @param size The size of the integer to be added to the buffer
|
||||
*/
|
||||
void putInt(int val, int size){
|
||||
if (size < 4){
|
||||
buffer.put(ByteBuffer.allocate(size).putShort((short) val).array());
|
||||
}
|
||||
else{
|
||||
buffer.put(ByteBuffer.allocate(size).putInt((short) val).array());
|
||||
}
|
||||
moveBufferPositionBy(size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write an array of bytes to the buffer
|
||||
* @param bytes to write
|
||||
*/
|
||||
void putBytes(byte[] bytes){
|
||||
buffer.put(bytes);
|
||||
moveBufferPositionBy(bytes.length);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a ByteBuffer of bytes to the buffer
|
||||
* @param bytes to write
|
||||
* @param size number of bytes
|
||||
*/
|
||||
void putBytes(ByteBuffer bytes, int size){
|
||||
buffer.put(bytes);
|
||||
moveBufferPositionBy(size);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculate the CRC of the buffer and append it to the end of the buffer
|
||||
*/
|
||||
void writeCRC(){
|
||||
crc = new CRC32();
|
||||
|
||||
buffer.position(0);
|
||||
crc.update(buffer.array());
|
||||
buffer.position(bufferPosition);
|
||||
|
||||
putInt((int) crc.getValue(), CRC_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The current buffer
|
||||
*/
|
||||
public ByteBuffer getBuffer(){
|
||||
return buffer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
package seng302.server.messages;
|
||||
|
||||
/**
|
||||
* The types of race start status messages
|
||||
*/
|
||||
public enum RaceStartNotificationType {
|
||||
SET_RACE_START_TIME(1),
|
||||
RACE_POSTPONED(2),
|
||||
RACE_ABANDONED(3),
|
||||
RACE_TERMINATED(4);
|
||||
|
||||
private final long type;
|
||||
|
||||
RaceStartNotificationType(long type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
long getType(){
|
||||
return type;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package seng302.server.messages;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class RaceStartStatusMessage extends Message {
|
||||
private final int MESSAGE_SIZE = 20;
|
||||
|
||||
private long version;
|
||||
private long timeStamp;
|
||||
private long ackNumber;
|
||||
private long raceStartTime;
|
||||
private long raceId;
|
||||
private RaceStartNotificationType notificationType;
|
||||
|
||||
/**
|
||||
* Message sent to clients with the expected start time of the race
|
||||
* @param ackNumber Sequence number of message.
|
||||
* @param raceStartTime Expected race start time
|
||||
* @param raceId Race ID#
|
||||
* @param notificationType Type of this notification
|
||||
*/
|
||||
public RaceStartStatusMessage(long ackNumber, long raceStartTime, long raceId, RaceStartNotificationType notificationType){
|
||||
this.version = 1;
|
||||
this.timeStamp = System.currentTimeMillis() / 1000L;
|
||||
this.ackNumber = ackNumber;
|
||||
this.raceStartTime = raceStartTime;
|
||||
this.notificationType = notificationType;
|
||||
this.raceId = raceId;
|
||||
|
||||
setHeader(new Header(MessageType.RACE_START_STATUS, 1, (short) getSize()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSize() {
|
||||
return MESSAGE_SIZE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(DataOutputStream outputStream) {
|
||||
allocateBuffer();
|
||||
writeHeaderToBuffer();
|
||||
|
||||
putUnsignedByte((byte) version);
|
||||
putInt((int) timeStamp, 6);
|
||||
putInt((int) ackNumber, 2);
|
||||
putInt((int) raceStartTime, 6);
|
||||
putInt((int) raceId, 4);
|
||||
putUnsignedByte((byte) notificationType.getType());
|
||||
|
||||
writeCRC();
|
||||
|
||||
try {
|
||||
outputStream.write(getBuffer().array());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ public class RaceStatusMessage extends Message{
|
||||
private final int MESSAGE_VERSION = 2; //Always set to 1
|
||||
private final int MESSAGE_BASE_SIZE = 24;
|
||||
|
||||
// fields
|
||||
private long currentTime;
|
||||
private long raceId;
|
||||
private RaceStatus raceStatus;
|
||||
@@ -56,7 +55,7 @@ public class RaceStatusMessage extends Message{
|
||||
*/
|
||||
@Override
|
||||
public int getSize() {
|
||||
return MESSAGE_BASE_SIZE + (20 * (int) numBoatsInRace);
|
||||
return MESSAGE_BASE_SIZE + (20 * ((int) numBoatsInRace));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -65,64 +64,28 @@ public class RaceStatusMessage extends Message{
|
||||
*/
|
||||
@Override
|
||||
public void send(DataOutputStream outputStream) {
|
||||
ByteBuffer buff = ByteBuffer.allocate(Header.getSize() + getSize() + 4/*CRC*/);
|
||||
allocateBuffer();
|
||||
writeHeaderToBuffer();
|
||||
|
||||
buff.put(getHeader().getByteBuffer());
|
||||
buff.position(Header.getSize());
|
||||
|
||||
// Version Number, 1 byte
|
||||
buff.put(ByteBuffer.allocate(1).put((byte)MESSAGE_VERSION).array());
|
||||
buff.position(Header.getSize() + 1);
|
||||
|
||||
// Current time, 2 bytes
|
||||
buff.put(ByteBuffer.allocate(6).putInt((int)currentTime).array());
|
||||
buff.position(Header.getSize() + 7);
|
||||
|
||||
// Race id, 4 bytes
|
||||
buff.put(ByteBuffer.allocate(4).putInt((int)raceId).array());
|
||||
buff.position(Header.getSize() + 11);
|
||||
|
||||
// Race status, 1 byte
|
||||
buff.put(ByteBuffer.allocate(1).put((byte)raceStatus.getCode()).array());
|
||||
buff.position(Header.getSize() + 12);
|
||||
|
||||
// Expected start time, 6 bytes
|
||||
buff.put(ByteBuffer.allocate(6).putInt((int)expectedStartTime).array());
|
||||
buff.position(Header.getSize() + 18);
|
||||
|
||||
// Wind direction, 2 bytes
|
||||
buff.put(ByteBuffer.allocate(2).putShort((short)raceWindDirection.getCode()).array());
|
||||
buff.position(Header.getSize() + 20);
|
||||
|
||||
// Wind Speed, 2 bytes
|
||||
buff.put(ByteBuffer.allocate(2).putShort((short)windSpeed).array());
|
||||
buff.position(Header.getSize() + 22);
|
||||
|
||||
// Number of boats, 1 byte
|
||||
buff.put(ByteBuffer.allocate(1).put((byte)numBoatsInRace).array());
|
||||
buff.position(Header.getSize() + 23);
|
||||
|
||||
// Race Type, 1 byte
|
||||
buff.put(ByteBuffer.allocate(1).put((byte)raceType.getCode()).array());
|
||||
buff.position(Header.getSize() + 24);
|
||||
|
||||
int buffPosition = Header.getSize() + 24;
|
||||
putByte((byte) MESSAGE_VERSION);
|
||||
putInt((int) currentTime, 6);
|
||||
putInt((int) raceId, 4);
|
||||
putByte((byte) raceStatus.getCode());
|
||||
putInt((int) expectedStartTime, 6);
|
||||
putInt((int) raceWindDirection.getCode(), 2);
|
||||
putInt((int) windSpeed, 2);
|
||||
putByte((byte) numBoatsInRace);
|
||||
putByte((byte) raceType.getCode());
|
||||
|
||||
for (BoatSubMessage boatSubMessage : boats){
|
||||
buff.put(boatSubMessage.getByteBuffer());
|
||||
buffPosition += boatSubMessage.getSize();
|
||||
buff.position(buffPosition);
|
||||
putBytes(boatSubMessage.getByteBuffer(), boatSubMessage.getSize());
|
||||
}
|
||||
|
||||
// calculate CRC
|
||||
crc.update(buff.array());
|
||||
|
||||
// Add CRC to message
|
||||
buff.put(ByteBuffer.allocate(4).putInt((short)crc.getValue()).array());
|
||||
writeCRC();
|
||||
|
||||
// Send
|
||||
try {
|
||||
outputStream.write(buff.array());
|
||||
outputStream.write(getBuffer().array());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -11,26 +11,24 @@ public class XMLMessage extends Message{
|
||||
private final int MESSAGE_SIZE = 14;
|
||||
|
||||
// Message fields
|
||||
private int timeStamp;
|
||||
private short ack = 0x00; //Unused
|
||||
private long timeStamp;
|
||||
private long ack = 0x00; //Unused
|
||||
private XMLMessageSubType xmlMessageSubType;
|
||||
private Short length;
|
||||
private Short sequence;
|
||||
private long length;
|
||||
private long sequence;
|
||||
private String content;
|
||||
private CRC32 crc;
|
||||
|
||||
/**
|
||||
* XML Message from the AC35 Streaming data spec
|
||||
* @param content The XML content
|
||||
* @param type The XML Message Sub Type
|
||||
*/
|
||||
public XMLMessage(String content, XMLMessageSubType type, short sequenceNum){
|
||||
public XMLMessage(String content, XMLMessageSubType type, long sequenceNum){
|
||||
this.content = content;
|
||||
this.xmlMessageSubType = type;
|
||||
crc = new CRC32();
|
||||
timeStamp = (int) (System.currentTimeMillis() / 1000L);
|
||||
timeStamp = System.currentTimeMillis() / 1000L;
|
||||
ack = 0;
|
||||
length = (short) this.content.length();
|
||||
length = this.content.length();
|
||||
sequence = sequenceNum;
|
||||
|
||||
setHeader(new Header(MESSAGE_TYPE, 0x01, (short) getSize()));
|
||||
@@ -48,47 +46,23 @@ public class XMLMessage extends Message{
|
||||
* @param outputStream The output stream to send the message
|
||||
*/
|
||||
public void send(DataOutputStream outputStream) {
|
||||
ByteBuffer buff = ByteBuffer.allocate(Header.getSize() + getSize() + 4);
|
||||
buff.put(getHeader().getByteBuffer());
|
||||
buff.position(Header.getSize());
|
||||
allocateBuffer();
|
||||
writeHeaderToBuffer();
|
||||
|
||||
// Version Number, 1 byte
|
||||
buff.put(ByteBuffer.allocate(1).put((byte)MESSAGE_VERSION).array());
|
||||
buff.position(Header.getSize() + 1);
|
||||
// Write message fields
|
||||
putUnsignedByte((byte) MESSAGE_VERSION);
|
||||
putInt((int) ack, 2);
|
||||
putInt((int) timeStamp, 6);
|
||||
putByte((byte)xmlMessageSubType.getType());
|
||||
putInt((int) sequence, 2);
|
||||
putInt((int) length, 2);
|
||||
putBytes(content.getBytes());
|
||||
|
||||
// Ack, 2 bytes
|
||||
buff.put(ByteBuffer.allocate(2).putShort(ack).array());
|
||||
buff.position(Header.getSize() + 3);
|
||||
|
||||
// Timestamp, 6 bytes
|
||||
buff.put(ByteBuffer.allocate(6).putInt(timeStamp).array());
|
||||
buff.position(Header.getSize() + 9);
|
||||
|
||||
// XML message sub type, 1 byte
|
||||
buff.put(ByteBuffer.allocate(1).put((byte)xmlMessageSubType.getType()).array());
|
||||
buff.position(Header.getSize() + 10);
|
||||
|
||||
// Seq num, 2 bytes
|
||||
buff.put(ByteBuffer.allocate(2).putShort(sequence).array());
|
||||
buff.position(Header.getSize() + 12);
|
||||
|
||||
// Message length, 2 bytes
|
||||
buff.put(ByteBuffer.allocate(2).putShort(length).array());
|
||||
buff.position(Header.getSize() + 14);
|
||||
|
||||
// XML Content
|
||||
buff.put(this.content.getBytes());
|
||||
buff.position(Header.getSize() + 14 + this.content.getBytes().length);
|
||||
|
||||
// calculate CRC
|
||||
crc.update(buff.array());
|
||||
|
||||
// Add CRC to message
|
||||
buff.put(ByteBuffer.allocate(4).putInt((short)crc.getValue()).array());
|
||||
writeCRC();
|
||||
|
||||
// Send
|
||||
try {
|
||||
outputStream.write(buff.array());
|
||||
outputStream.write(getBuffer().array());
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
package seng302.server;
|
||||
|
||||
import org.junit.Test;
|
||||
import seng302.server.messages.Header;
|
||||
import seng302.server.messages.MessageType;
|
||||
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
/**
|
||||
* Tests message header
|
||||
*/
|
||||
public class TestHeader {
|
||||
|
||||
@Test
|
||||
public void testHeaderSizeEqualsActualSize(){
|
||||
Header h = new Header(MessageType.DISPLAY_TEXT_MESSAGE, 1, (short) 1);
|
||||
assertTrue(h.getSize() == h.getByteBuffer().array().length);
|
||||
|
||||
}
|
||||
|
||||
@Test
|
||||
public void headerSizeIsSameAsSpec(){
|
||||
Header h = new Header(MessageType.DISPLAY_TEXT_MESSAGE, 1, (short) 1);
|
||||
assertTrue(h.getSize() == 15); // Spec specifies 15 bytes
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,136 @@
|
||||
package seng302.server;
|
||||
|
||||
import org.junit.Test;
|
||||
import seng302.server.messages.*;
|
||||
|
||||
import java.io.DataOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static junit.framework.TestCase.assertTrue;
|
||||
|
||||
public class TestMessage {
|
||||
private static int XML_MESSAGE_LEN = 14;
|
||||
private static int RACE_STATUS_BASE_LEN = 24;
|
||||
private static int BOAT_SUB_MESSAGE_LEN = 20;
|
||||
private static int CRC_LEN = 4;
|
||||
|
||||
/**
|
||||
* Test generated output is the same as the expected output
|
||||
*/
|
||||
@Test
|
||||
public void testHeatBetBufferOutputLength(){
|
||||
Message m = new Heartbeat(1);
|
||||
List<Integer> output = new ArrayList<>();
|
||||
|
||||
DataOutputStream ds = new DataOutputStream(new OutputStream() {
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
output.add(b);
|
||||
}
|
||||
});
|
||||
|
||||
m.send(ds);
|
||||
assertTrue(output.size() == (m.getSize() + CRC_LEN + Header.getSize()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test output expected is the same as the spec
|
||||
*/
|
||||
@Test
|
||||
public void testXmlMessageSize(){
|
||||
Message m = new XMLMessage("12345", XMLMessageSubType.BOAT, 1);
|
||||
assertTrue(m.getSize() == (XML_MESSAGE_LEN + "12345".length()));
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure that when no boats are in the race, that only the base message is sent
|
||||
*/
|
||||
@Test
|
||||
public void testRaceStatusMessageBufferLenNoBoats(){
|
||||
Message m = new RaceStatusMessage(1, RaceStatus.PRESTART,1,WindDirection.EAST,1,
|
||||
0,RaceType.MATCH_RACE,1, new ArrayList<BoatSubMessage>());
|
||||
|
||||
List<Integer> output = new ArrayList<>();
|
||||
|
||||
DataOutputStream ds = new DataOutputStream(new OutputStream() {
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
output.add(b);
|
||||
}
|
||||
});
|
||||
|
||||
m.send(ds);
|
||||
assertTrue(output.size() == RACE_STATUS_BASE_LEN + Header.getSize() + CRC_LEN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test that each boat status is added to the message
|
||||
*/
|
||||
@Test
|
||||
public void testRaceStatusMessageBufferLenWithBoats(){
|
||||
List<BoatSubMessage> boatMessages = new ArrayList<>();
|
||||
List<Integer> output = new ArrayList<>();
|
||||
|
||||
BoatSubMessage boat1 = new BoatSubMessage(1, BoatStatus.PRESTART, 0, 0, 0,
|
||||
10000, 10000);
|
||||
|
||||
BoatSubMessage boat2 = new BoatSubMessage(2, BoatStatus.PRESTART, 0, 0, 0,
|
||||
10000, 10000);
|
||||
|
||||
BoatSubMessage boat3 = new BoatSubMessage(3, BoatStatus.PRESTART, 0, 0, 0,
|
||||
10000, 10000);
|
||||
|
||||
boatMessages.add(boat1);
|
||||
boatMessages.add(boat2);
|
||||
boatMessages.add(boat3);
|
||||
|
||||
Message m = new RaceStatusMessage(1, RaceStatus.PRESTART,1,WindDirection.EAST,1,
|
||||
3,RaceType.MATCH_RACE,1, boatMessages);
|
||||
|
||||
DataOutputStream ds = new DataOutputStream(new OutputStream() {
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
output.add(b);
|
||||
}
|
||||
});
|
||||
|
||||
m.send(ds);
|
||||
assertTrue(output.size() == (RACE_STATUS_BASE_LEN + (BOAT_SUB_MESSAGE_LEN * 3) + CRC_LEN + Header.getSize()));
|
||||
}
|
||||
|
||||
/**
|
||||
* IllegalArgumentException should be thrown when numBoatsInRace is smaller
|
||||
* than the number of boats actually in the race
|
||||
*/
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void testRaceStatusTooManyBoats(){
|
||||
List<BoatSubMessage> boatMessages = new ArrayList<>();
|
||||
List<Integer> output = new ArrayList<>();
|
||||
|
||||
BoatSubMessage boat1 = new BoatSubMessage(1, BoatStatus.PRESTART, 0, 0, 0,
|
||||
10000, 10000);
|
||||
|
||||
BoatSubMessage boat2 = new BoatSubMessage(2, BoatStatus.PRESTART, 0, 0, 0,
|
||||
10000, 10000);
|
||||
|
||||
BoatSubMessage boat3 = new BoatSubMessage(3, BoatStatus.PRESTART, 0, 0, 0,
|
||||
10000, 10000);
|
||||
|
||||
boatMessages.add(boat1);
|
||||
boatMessages.add(boat2);
|
||||
boatMessages.add(boat3);
|
||||
|
||||
Message m = new RaceStatusMessage(1, RaceStatus.PRESTART,1,WindDirection.EAST,1,
|
||||
1,RaceType.MATCH_RACE,1, boatMessages);
|
||||
|
||||
m.send(new DataOutputStream(new OutputStream() {
|
||||
@Override
|
||||
public void write(int b) throws IOException {
|
||||
System.out.print("");
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user