mirror of
https://github.com/michaelrausch/Party-Parrots-At-Sea.git
synced 2026-05-09 14:28:43 +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 StreamingServerSocket server;
|
||||||
private final int HEARTBEAT_PERIOD = 5000;
|
private final int HEARTBEAT_PERIOD = 5000;
|
||||||
private final int RACE_STATUS_PERIOD = 1000;
|
private final int RACE_STATUS_PERIOD = 1000;
|
||||||
|
private final int BOAT_LOCATION_PERIOD = 1000/5;
|
||||||
private final int PORT_NUMBER = 8085;
|
private final int PORT_NUMBER = 8085;
|
||||||
|
|
||||||
public ServerThread(String threadName){
|
public ServerThread(String threadName){
|
||||||
@@ -68,6 +69,19 @@ public class ServerThread implements Runnable{
|
|||||||
100, 3, RaceType.MATCH_RACE, 1, boats);
|
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() {
|
public void run() {
|
||||||
try{
|
try{
|
||||||
server = new StreamingServerSocket(PORT_NUMBER);
|
server = new StreamingServerSocket(PORT_NUMBER);
|
||||||
@@ -105,7 +119,7 @@ public class ServerThread implements Runnable{
|
|||||||
try {
|
try {
|
||||||
server.send(hb);
|
server.send(hb);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
System.out.print("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 0, HEARTBEAT_PERIOD);
|
}, 0, HEARTBEAT_PERIOD);
|
||||||
@@ -120,11 +134,27 @@ public class ServerThread implements Runnable{
|
|||||||
try {
|
try {
|
||||||
server.send(statusMessage);
|
server.send(statusMessage);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
System.out.print("");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 100, RACE_STATUS_PERIOD);
|
}, 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) {
|
} catch (IOException e) {
|
||||||
System.err.println(e.getMessage());
|
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;
|
package seng302.server.messages;
|
||||||
|
|
||||||
|
import java.io.DataOutputStream;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The status of each boat, sent within a race status message
|
* The status of each boat, sent within a race status message
|
||||||
*/
|
*/
|
||||||
public class BoatSubMessage {
|
public class BoatSubMessage{
|
||||||
private final int MESSAGE_SIZE = 20;
|
private final int MESSAGE_SIZE = 20;
|
||||||
|
|
||||||
private long sourceId;
|
private long sourceId;
|
||||||
@@ -57,22 +58,22 @@ public class BoatSubMessage {
|
|||||||
buff.position(buffPos);
|
buff.position(buffPos);
|
||||||
|
|
||||||
// Boat Status, 1 byte
|
// 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;
|
buffPos += 1;
|
||||||
buff.position(buffPos);
|
buff.position(buffPos);
|
||||||
|
|
||||||
// Leg number, 1 byte
|
// 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;
|
buffPos += 1;
|
||||||
buff.position(buffPos);
|
buff.position(buffPos);
|
||||||
|
|
||||||
// Number of penalties awarded, 1 byte
|
// 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;
|
buffPos += 1;
|
||||||
buff.position(buffPos);
|
buff.position(buffPos);
|
||||||
|
|
||||||
// Number of penalties served, 1 byte
|
// 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;
|
buffPos += 1;
|
||||||
buff.position(buffPos);
|
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) {
|
public void send(DataOutputStream outputStream) {
|
||||||
setHeader(new Header(MessageType.HEARTBEAT, 0x01, (short) getSize()));
|
setHeader(new Header(MessageType.HEARTBEAT, 0x01, (short) getSize()));
|
||||||
|
|
||||||
ByteBuffer buff = ByteBuffer.allocate(Header.getSize() + getSize() + getSize());
|
allocateBuffer();
|
||||||
|
writeHeaderToBuffer();
|
||||||
|
|
||||||
// Write header
|
putUnsignedInt(seqNo, 4);
|
||||||
buff.put(getHeader().getByteBuffer());
|
|
||||||
buff.position(Header.getSize());
|
|
||||||
|
|
||||||
// Write seq num
|
writeCRC();
|
||||||
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());
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
outputStream.write(buff.array());
|
outputStream.write(getBuffer().array());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +1,27 @@
|
|||||||
package seng302.server.messages;
|
package seng302.server.messages;
|
||||||
|
|
||||||
import java.io.DataOutputStream;
|
import java.io.DataOutputStream;
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.util.zip.CRC32;
|
||||||
|
|
||||||
public abstract class Message {
|
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
|
* @param header Set the header for this message
|
||||||
*/
|
*/
|
||||||
public void setHeader(Header header){
|
void setHeader(Header header){
|
||||||
this.header = header;
|
this.header = header;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the header specified for this message
|
* @return the header specified for this message
|
||||||
*/
|
*/
|
||||||
public Header getHeader(){
|
Header getHeader(){
|
||||||
return header;
|
return header;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,4 +34,124 @@ public abstract class Message {
|
|||||||
* Send the message as through the outputStream
|
* Send the message as through the outputStream
|
||||||
*/
|
*/
|
||||||
public abstract void send(DataOutputStream 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_VERSION = 2; //Always set to 1
|
||||||
private final int MESSAGE_BASE_SIZE = 24;
|
private final int MESSAGE_BASE_SIZE = 24;
|
||||||
|
|
||||||
// fields
|
|
||||||
private long currentTime;
|
private long currentTime;
|
||||||
private long raceId;
|
private long raceId;
|
||||||
private RaceStatus raceStatus;
|
private RaceStatus raceStatus;
|
||||||
@@ -56,7 +55,7 @@ public class RaceStatusMessage extends Message{
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public int getSize() {
|
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
|
@Override
|
||||||
public void send(DataOutputStream outputStream) {
|
public void send(DataOutputStream outputStream) {
|
||||||
ByteBuffer buff = ByteBuffer.allocate(Header.getSize() + getSize() + 4/*CRC*/);
|
allocateBuffer();
|
||||||
|
writeHeaderToBuffer();
|
||||||
|
|
||||||
buff.put(getHeader().getByteBuffer());
|
putByte((byte) MESSAGE_VERSION);
|
||||||
buff.position(Header.getSize());
|
putInt((int) currentTime, 6);
|
||||||
|
putInt((int) raceId, 4);
|
||||||
// Version Number, 1 byte
|
putByte((byte) raceStatus.getCode());
|
||||||
buff.put(ByteBuffer.allocate(1).put((byte)MESSAGE_VERSION).array());
|
putInt((int) expectedStartTime, 6);
|
||||||
buff.position(Header.getSize() + 1);
|
putInt((int) raceWindDirection.getCode(), 2);
|
||||||
|
putInt((int) windSpeed, 2);
|
||||||
// Current time, 2 bytes
|
putByte((byte) numBoatsInRace);
|
||||||
buff.put(ByteBuffer.allocate(6).putInt((int)currentTime).array());
|
putByte((byte) raceType.getCode());
|
||||||
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;
|
|
||||||
|
|
||||||
for (BoatSubMessage boatSubMessage : boats){
|
for (BoatSubMessage boatSubMessage : boats){
|
||||||
buff.put(boatSubMessage.getByteBuffer());
|
putBytes(boatSubMessage.getByteBuffer(), boatSubMessage.getSize());
|
||||||
buffPosition += boatSubMessage.getSize();
|
|
||||||
buff.position(buffPosition);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// calculate CRC
|
writeCRC();
|
||||||
crc.update(buff.array());
|
|
||||||
|
|
||||||
// Add CRC to message
|
|
||||||
buff.put(ByteBuffer.allocate(4).putInt((short)crc.getValue()).array());
|
|
||||||
|
|
||||||
// Send
|
// Send
|
||||||
try {
|
try {
|
||||||
outputStream.write(buff.array());
|
outputStream.write(getBuffer().array());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,26 +11,24 @@ public class XMLMessage extends Message{
|
|||||||
private final int MESSAGE_SIZE = 14;
|
private final int MESSAGE_SIZE = 14;
|
||||||
|
|
||||||
// Message fields
|
// Message fields
|
||||||
private int timeStamp;
|
private long timeStamp;
|
||||||
private short ack = 0x00; //Unused
|
private long ack = 0x00; //Unused
|
||||||
private XMLMessageSubType xmlMessageSubType;
|
private XMLMessageSubType xmlMessageSubType;
|
||||||
private Short length;
|
private long length;
|
||||||
private Short sequence;
|
private long sequence;
|
||||||
private String content;
|
private String content;
|
||||||
private CRC32 crc;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* XML Message from the AC35 Streaming data spec
|
* XML Message from the AC35 Streaming data spec
|
||||||
* @param content The XML content
|
* @param content The XML content
|
||||||
* @param type The XML Message Sub Type
|
* @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.content = content;
|
||||||
this.xmlMessageSubType = type;
|
this.xmlMessageSubType = type;
|
||||||
crc = new CRC32();
|
timeStamp = System.currentTimeMillis() / 1000L;
|
||||||
timeStamp = (int) (System.currentTimeMillis() / 1000L);
|
|
||||||
ack = 0;
|
ack = 0;
|
||||||
length = (short) this.content.length();
|
length = this.content.length();
|
||||||
sequence = sequenceNum;
|
sequence = sequenceNum;
|
||||||
|
|
||||||
setHeader(new Header(MESSAGE_TYPE, 0x01, (short) getSize()));
|
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
|
* @param outputStream The output stream to send the message
|
||||||
*/
|
*/
|
||||||
public void send(DataOutputStream outputStream) {
|
public void send(DataOutputStream outputStream) {
|
||||||
ByteBuffer buff = ByteBuffer.allocate(Header.getSize() + getSize() + 4);
|
allocateBuffer();
|
||||||
buff.put(getHeader().getByteBuffer());
|
writeHeaderToBuffer();
|
||||||
buff.position(Header.getSize());
|
|
||||||
|
|
||||||
// Version Number, 1 byte
|
// Write message fields
|
||||||
buff.put(ByteBuffer.allocate(1).put((byte)MESSAGE_VERSION).array());
|
putUnsignedByte((byte) MESSAGE_VERSION);
|
||||||
buff.position(Header.getSize() + 1);
|
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
|
writeCRC();
|
||||||
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());
|
|
||||||
|
|
||||||
// Send
|
// Send
|
||||||
try {
|
try {
|
||||||
outputStream.write(buff.array());
|
outputStream.write(getBuffer().array());
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
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