Added RaceStatusEncoder.

Refactored RaceStatusDecoder to be more consistent with other decoders - it now has a getMessage() function.
Added BoatStatus encoder and decoder - the RaceStatus encoder and decoder uses this for BoatStatuses. The BoatStatus encoder doesn't implement the MessageEncoder interface as BoatStatus is not a proper message type (doesn't inherit from AC35Data).
Added remaining cases to EncoderFactory, but commented them out.
BoatStatus now uses BoatStatusEnum instead of a byte.
Added some comments to RaceStatus, and it uses enums instead of bytes.
MockOutput logs a warning if a RaceStatus cannot be encoded.
Added a BoatStatusDecoderTest.
Updated RaceStatusDecoder to use new encoders/decoders.
issue #35 #36
#story[1095]
main
fjc40 8 years ago
parent e464ee298e
commit b1922fc3fc

@ -185,11 +185,12 @@ public class MockOutput implements Runnable
* Encodes/serialises a RaceStatus message, and returns it.
* @param raceStatus The RaceStatus message to serialise.
* @return The RaceStatus message in a serialised form.
* @throws InvalidMessageException Thrown if the message cannot be encoded.
*/
private synchronized byte[] parseRaceStatus(RaceStatus raceStatus){
private synchronized byte[] parseRaceStatus(RaceStatus raceStatus) throws InvalidMessageException {
//Encodes the messages.
byte[] encodedRaceStatus = RaceVisionByteEncoder.raceStatus(raceStatus);
byte[] encodedRaceStatus = RaceVisionByteEncoder.encode(raceStatus);
//Encodes the full message with header.
BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder(
@ -284,9 +285,15 @@ public class MockOutput implements Runnable
//Sends the RaceStatus message.
if (this.latestMessages.getRaceStatus() != null) {
try {
byte[] raceStatusBlob = this.parseRaceStatus(this.latestMessages.getRaceStatus());
this.outToVisualiser.write(raceStatusBlob);
} catch (InvalidMessageException e) {
Logger.getGlobal().log(Level.WARNING, "Could not encode RaceStatus: " + latestMessages.getRaceStatus(), e);
}
}
//Send all of the BoatLocation messages.

@ -139,13 +139,14 @@ public class RaceServer {
//Create race status object, and send it.
RaceStatus raceStatus = new RaceStatus(
RaceStatus.currentMessageVersionNumber,
System.currentTimeMillis(),
race.getRaceId(),
race.getRaceStatusEnum().getValue(),
race.getRaceStatusEnum(),
race.getRaceClock().getStartingTimeMilli(),
windDirectionInt,
windSpeedInt,
race.getRaceType().getValue(),
race.getRaceType(),
boatStatuses);
this.latestMessages.setRaceStatus(raceStatus);

@ -139,7 +139,7 @@ public class BinaryMessageDecoder {
case RACESTATUS:
//System.out.println("Race Status Message");
RaceStatusDecoder rsdecoder = new RaceStatusDecoder(messageBody);
return new RaceStatus(rsdecoder.getTime(), rsdecoder.getRace(), rsdecoder.getRaceState(), rsdecoder.getStartTime(), rsdecoder.getRaceWindDir(), rsdecoder.getRaceWindSpeed(), rsdecoder.getRaceType(), rsdecoder.getBoats());
return rsdecoder.getMessage();
case DISPLAYTEXTMESSAGE:
//System.out.println("Display Text Message");

@ -0,0 +1,90 @@
package network.MessageDecoders;
import network.Messages.BoatStatus;
import network.Messages.Enums.BoatStatusEnum;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static network.Utils.ByteConverter.*;
/**
* Decodes {@link BoatStatus} messages.
*/
public class BoatStatusDecoder {
/**
* The encoded message.
*/
private byte[] encodedMessage;
/**
* The decoded message.
*/
private BoatStatus message;
/**
* Constructs a decoder to decode a given message.
* @param encodedMessage The message to decode.
*/
public BoatStatusDecoder(byte[] encodedMessage) {
this.encodedMessage = encodedMessage;
decode();
}
/**
* Decodes the contained message.
*/
private void decode() {
byte[] sourceIDBytes = Arrays.copyOfRange(encodedMessage, 0, 4);
int sourceID = bytesToInt(sourceIDBytes);
byte[] boatStatusBytes = Arrays.copyOfRange(encodedMessage, 4, 5);
BoatStatusEnum boatStatus = BoatStatusEnum.fromByte(boatStatusBytes[0]);
byte[] legNumberBytes = Arrays.copyOfRange(encodedMessage, 5, 6);
byte legNumber = legNumberBytes[0];
byte[] numPenaltiesAwardedBytes = Arrays.copyOfRange(encodedMessage, 6, 7);
byte numPenaltiesAwarded = numPenaltiesAwardedBytes[0];
byte[] numPenaltiesServedBytes = Arrays.copyOfRange(encodedMessage, 7, 8);
byte numPenaltiesServed = numPenaltiesServedBytes[0];
byte[] estTimeAtNextMarkBytes = Arrays.copyOfRange(encodedMessage, 8, 14);
long estTimeAtNextMark = bytesToLong(estTimeAtNextMarkBytes);
byte[] estTimeAtFinishBytes = Arrays.copyOfRange(encodedMessage, 14, 20);
long estTimeAtFinish = bytesToLong(estTimeAtFinishBytes);
message = new BoatStatus(
sourceID,
boatStatus,
legNumber,
numPenaltiesAwarded,
numPenaltiesServed,
estTimeAtNextMark,
estTimeAtFinish );
}
/**
* Returns the decoded message.
* @return The decoded message.
*/
public BoatStatus getMessage() {
return message;
}
}

@ -2,9 +2,13 @@ package network.MessageDecoders;
import network.Messages.BoatStatus;
import network.Messages.Enums.RaceStatusEnum;
import network.Messages.Enums.RaceTypeEnum;
import network.Messages.RaceStatus;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import static network.Utils.ByteConverter.bytesToInt;
import static network.Utils.ByteConverter.bytesToLong;
@ -12,110 +16,101 @@ import static network.Utils.ByteConverter.bytesToShort;
/**
* Created by hba56 on 21/04/17.
* Decodes {@link RaceStatus} messages.
*/
public class RaceStatusDecoder {
private byte versionNum;
private byte[] timeBytes;
private byte[] raceID;
private byte raceStatus;
private byte[] expectedStart;
private byte[] raceWind;
private byte[] windSpeed;
private byte numBoats;
private byte bytesRaceType;
private byte[] boatsBytes;
private long time;
private int race;
private byte raceState;
private long startTime;
private int raceWindDir;
private short raceWindSpeed;
private int numberOfBoats;
private int raceType;
private ArrayList<BoatStatus> boats = new ArrayList<>();
public RaceStatusDecoder(byte[] encodedRaceStatus){
versionNum = encodedRaceStatus[0];
timeBytes = Arrays.copyOfRange(encodedRaceStatus, 1, 7);
raceID = Arrays.copyOfRange(encodedRaceStatus, 7, 11);
raceStatus = encodedRaceStatus[11];
expectedStart = Arrays.copyOfRange(encodedRaceStatus, 12, 18);
raceWind = Arrays.copyOfRange(encodedRaceStatus, 18, 20);
windSpeed = Arrays.copyOfRange(encodedRaceStatus, 20, 22);
numBoats = encodedRaceStatus[22];
bytesRaceType = encodedRaceStatus[23];
boatsBytes = Arrays.copyOfRange(encodedRaceStatus, 24, 25+20*this.numBoats);
time = bytesToLong(timeBytes);
race = bytesToInt(raceID);
raceState = raceStatus;
startTime = bytesToLong(expectedStart);
raceWindDir = bytesToInt(raceWind);
raceWindSpeed = bytesToShort(windSpeed);
numberOfBoats = bytesToInt(numBoats);
int boatLoopIndex = 0;
for (int i=0; i < numberOfBoats; i++) {
byte[] boatBytes = Arrays.copyOfRange(boatsBytes, boatLoopIndex, boatLoopIndex+20);
byte[] sourceID = Arrays.copyOfRange(boatBytes, 0, 3);
byte boatStatus = boatBytes[4];
byte legNumber = boatBytes[5];
byte numPenaltiesAwarded = boatBytes[6];
byte numPenaltiesServed = boatBytes[7];
byte[] estTimeAtNextMark = Arrays.copyOfRange(boatBytes, 8, 14);
byte[] estTimeAtFinish = Arrays.copyOfRange(boatBytes, 14, 20);
BoatStatus boat = new BoatStatus(bytesToInt(sourceID),boatStatus,
legNumber, numPenaltiesAwarded, numPenaltiesServed,
bytesToLong(estTimeAtNextMark), bytesToLong(estTimeAtFinish));
boats.add(boat);
boatLoopIndex += 20;
}
}
public byte getVersionNum() {
return versionNum;
}
/**
* The encoded message.
*/
private byte[] encodedMessage;
public long getTime() {
return time;
}
/**
* The decoded message.
*/
private RaceStatus message;
public int getRace() {
return race;
}
public byte getRaceState() {
return raceState;
}
public long getStartTime() {
return startTime;
}
/**
* Constructs a decoder to decode a given message.
* @param encodedMessage The message to decode.
*/
public RaceStatusDecoder(byte[] encodedMessage) {
this.encodedMessage = encodedMessage;
public int getRaceWindDir() {
return raceWindDir;
decode();
}
public short getRaceWindSpeed() {
return raceWindSpeed;
}
public int getNumberOfBoats() {
return numberOfBoats;
/**
* Decodes the contained message.
*/
private void decode() {
byte[] versionNumBytes = Arrays.copyOfRange(encodedMessage, 0, 1);
byte versionNum = versionNumBytes[0];
byte[] timeBytes = Arrays.copyOfRange(encodedMessage, 1, 7);
long time = bytesToLong(timeBytes);
byte[] raceIDBytes = Arrays.copyOfRange(encodedMessage, 7, 11);
int raceID = bytesToInt(raceIDBytes);
byte[] raceStatusBytes = Arrays.copyOfRange(encodedMessage, 11, 12);
RaceStatusEnum raceStatus = RaceStatusEnum.fromByte(raceStatusBytes[0]);
byte[] expectedStartBytes = Arrays.copyOfRange(encodedMessage, 12, 18);
long expectedStart = bytesToLong(expectedStartBytes);
byte[] windDirectionBytes = Arrays.copyOfRange(encodedMessage, 18, 20);
int windDirection = bytesToInt(windDirectionBytes);
byte[] windSpeedBytes = Arrays.copyOfRange(encodedMessage, 20, 22);
short windSpeed = bytesToShort(windSpeedBytes);
byte[] numberOfBoatsBytes = Arrays.copyOfRange(encodedMessage, 22, 23);
int numberOfBoats = bytesToInt(numberOfBoatsBytes);
byte[] raceTypeBytes = Arrays.copyOfRange(encodedMessage, 23, 24);
RaceTypeEnum raceType = RaceTypeEnum.fromByte(raceTypeBytes[0]);
byte[] boatStatusesBytes = Arrays.copyOfRange(encodedMessage, 24, 25 + 20 * numberOfBoats);
List<BoatStatus> boatStatuses = new ArrayList<>();
//Decode each BoatStatus.
for (int boatLoopIndex=0; boatLoopIndex < (numberOfBoats * 20); boatLoopIndex += 20) {
byte[] boatStatusBytes = Arrays.copyOfRange(boatStatusesBytes, boatLoopIndex, boatLoopIndex + 20);
BoatStatusDecoder boatStatusDecoder = new BoatStatusDecoder(boatStatusBytes);
boatStatuses.add(boatStatusDecoder.getMessage());
}
public int getRaceType() {
return raceType;
message = new RaceStatus(
versionNum,
time,
raceID,
raceStatus,
expectedStart,
windDirection,
windSpeed,
raceType,
boatStatuses );
}
public ArrayList<BoatStatus> getBoats() {
return boats;
/**
* Returns the decoded message.
* @return The decoded message.
*/
public RaceStatus getMessage() {
return message;
}
}

@ -0,0 +1,56 @@
package network.MessageEncoders;
import network.Messages.BoatStatus;
import java.nio.ByteBuffer;
import static network.Utils.ByteConverter.intToBytes;
import static network.Utils.ByteConverter.longToBytes;
/**
* This encoder can encode a {@link BoatStatus} message.
*/
public class BoatStatusEncoder {
/**
* Constructor.
*/
public BoatStatusEncoder() {
}
/**
* Encodes a given BoatStatus message.
* @param message The message to encode.
* @return The encoded message.
*/
public byte[] encode(BoatStatus message) {
//Downcast.
BoatStatus boatStatus = (BoatStatus) message;
//BoatStatus is 20 bytes.
ByteBuffer boatStatusBuffer = ByteBuffer.allocate(20);
byte[] sourceID = intToBytes(boatStatus.getSourceID());
byte[] boatStatusBytes = intToBytes(boatStatus.getBoatStatus().getValue(), 1);
byte[] legNum = intToBytes(boatStatus.getLegNumber(), 1);
byte[] numPenalties = intToBytes(boatStatus.getNumPenaltiesAwarded(), 1);
byte[] numPenaltiesServed = intToBytes(boatStatus.getNumPenaltiesServed(), 1);
byte[] estNextMarkTime = longToBytes(boatStatus.getEstTimeAtNextMark(), 6);
byte[] estFinishTime = longToBytes(boatStatus.getEstTimeAtFinish(), 6);
boatStatusBuffer.put(sourceID);
boatStatusBuffer.put(boatStatusBytes);
boatStatusBuffer.put(legNum);
boatStatusBuffer.put(numPenalties);
boatStatusBuffer.put(numPenaltiesServed);
boatStatusBuffer.put(estNextMarkTime);
boatStatusBuffer.put(estFinishTime);
return boatStatusBuffer.array();
}
}

@ -31,10 +31,28 @@ public class EncoderFactory {
case HEARTBEAT: return new HeartBeatEncoder();
case RACESTATUS: return new RaceStatusEncoder();
//case DISPLAYTEXTMESSAGE: return new DisplayTextMessageEncoder();//TODO
case XMLMESSAGE: return new XMLMessageEncoder();
//case RACESTARTSTATUS: return new RaceStartStatusEncoder();//TODO
//case YACHTEVENTCODE: return new YachtEventCodeEncoder();//TODO
//case YACHTACTIONCODE: return new YachtActionCodeEncoder();//TODO
//case CHATTERTEXT: return new ChatterTextEncoder();//TODO
case BOATLOCATION: return new BoatLocationEncoder();
//case MARKROUNDING: return new MarkRoundingEncoder();//TODO
//case COURSEWIND: return new CourseWindEncoder();//TODO
//case AVGWIND: return new AverageWindEncoder();//TODO
case REQUEST_TO_JOIN: return new RequestToJoinEncoder();
case JOIN_ACCEPTANCE: return new JoinAcceptanceEncoder();

@ -6,7 +6,6 @@ import network.Messages.HeartBeat;
import java.nio.ByteBuffer;
import static network.Utils.ByteConverter.intToBytes;
import static network.Utils.ByteConverter.longToBytes;
/**

@ -0,0 +1,90 @@
package network.MessageEncoders;
import network.Messages.AC35Data;
import network.Messages.BoatStatus;
import network.Messages.RaceStatus;
import java.nio.ByteBuffer;
import java.util.List;
import static network.Utils.ByteConverter.intToBytes;
import static network.Utils.ByteConverter.longToBytes;
/**
* This encoder can encode a {@link RaceStatus} message.
*/
public class RaceStatusEncoder implements MessageEncoder {
/**
* Constructor.
*/
public RaceStatusEncoder() {
}
@Override
public byte[] encode(AC35Data message) {
//Downcast.
RaceStatus raceStatus = (RaceStatus) message;
List<BoatStatus> boatStatuses = raceStatus.getBoatStatuses();
//24 byte header, plus 20 bytes per boat status.
ByteBuffer raceStatusMessage = ByteBuffer.allocate(24 + 20 * boatStatuses.size());
//Version Number 1 bytes. this changes with the pdf. (2)
byte versionNum = 0b10;
//time (6 bytes)
byte[] timeBytes = longToBytes(raceStatus.getCurrentTime(), 6);
//race identifier in case multiple races are going at once.
byte[] raceID = intToBytes(raceStatus.getRaceID());
//race status 0 - 10
byte[] raceStatusByte = intToBytes(raceStatus.getRaceStatus().getValue(), 1);
//number of milliseconds from Jan 1, 1970 for when the data is valid
byte[] expectedStart = longToBytes(raceStatus.getExpectedStartTime(), 6);
//North = 0x0000 East = 0x4000 South = 0x8000.
byte[] raceWind = intToBytes(raceStatus.getWindDirection(), 2);
//mm/sec
byte[] windSpeed = intToBytes(raceStatus.getWindSpeed(), 2);
byte[] numBoats = intToBytes(boatStatuses.size(), 1);
//1 match race, 2 fleet race
byte[] bytesRaceType = intToBytes(raceStatus.getRaceType().getValue(), 1);
raceStatusMessage.put(versionNum);
raceStatusMessage.put(timeBytes);
raceStatusMessage.put(raceID);
raceStatusMessage.put(raceStatusByte);
raceStatusMessage.put(expectedStart);
raceStatusMessage.put(raceWind);
raceStatusMessage.put(windSpeed);
raceStatusMessage.put(numBoats);
raceStatusMessage.put(bytesRaceType);
//Encode each BoatStatus.
for (BoatStatus boatStatus : boatStatuses) {
BoatStatusEncoder boatStatusEncoder = new BoatStatusEncoder();
byte[] boatStatusEncoded = boatStatusEncoder.encode(boatStatus);
raceStatusMessage.put(boatStatusEncoded);
}
return raceStatusMessage.array();
}
}

@ -21,57 +21,6 @@ public class RaceVisionByteEncoder {
/**
* Serializes a RaceStatus message.
* @param raceStatus Message to serialize.
* @return Serialized (byte array) message, ready to be written to a socket.
*/
public static byte[] raceStatus(RaceStatus raceStatus){
List<BoatStatus> boatStatuses = raceStatus.getBoatStatuses();
ByteBuffer raceStatusMessage = ByteBuffer.allocate(24 + 20* boatStatuses.size());
//Version Number 1 bytes
byte versionNum = 0b10; //this changes with the pdf. (2)
byte[] timeBytes = longToBytes(raceStatus.getCurrentTime(), 6);//time (6 bytes)
byte[] raceID = ByteBuffer.allocate(4).put(intToBytes(raceStatus.getRaceID())).array();//race identifier incase multiple races are going at once.
byte[] raceStatusByte = intToBytes(raceStatus.getRaceStatus(), 1);//race status 0 - 10
byte[] expectedStart = longToBytes(raceStatus.getExpectedStartTime(), 6);//number of milliseconds from Jan 1, 1970 for when the data is valid
byte[] raceWind = ByteBuffer.allocate(2).put(intToBytes(raceStatus.getWindDirection(), 2)).array();//North = 0x0000 East = 0x4000 South = 0x8000.
byte[] windSpeed = ByteBuffer.allocate(2).put(intToBytes(raceStatus.getWindSpeed(), 2)).array();//mm/sec
byte[] numBoats = intToBytes(boatStatuses.size(), 1);
byte[] bytesRaceType = intToBytes(raceStatus.getRaceType(), 1);//1 match race, 2 fleet race
raceStatusMessage.put(versionNum);
raceStatusMessage.put(timeBytes);
raceStatusMessage.put(raceID);
raceStatusMessage.put(raceStatusByte);
raceStatusMessage.put(expectedStart);
raceStatusMessage.put(raceWind);
raceStatusMessage.put(windSpeed);
raceStatusMessage.put(numBoats);
raceStatusMessage.put(bytesRaceType);
for (int i = 0; i < boatStatuses.size(); i++){
byte[] sourceID = intToBytes(boatStatuses.get(i).getSourceID());
byte[] boatStatus = intToBytes(boatStatuses.get(i).getBoatStatus(), 1);
byte[] legNum = intToBytes(boatStatuses.get(i).getLegNumber(), 1);
byte[] numPenalties = intToBytes(boatStatuses.get(i).getNumPenaltiesAwarded(), 1);
byte[] numPenaltiesServed = intToBytes(boatStatuses.get(i).getNumPenaltiesServed(), 1);
byte[] estNextMarkTime = longToBytes(boatStatuses.get(i).getEstTimeAtNextMark(), 6);
byte[] estFinishTime = longToBytes( boatStatuses.get(i).getEstTimeAtFinish(), 6);
raceStatusMessage.put(sourceID);
raceStatusMessage.put(boatStatus);
raceStatusMessage.put(legNum);
raceStatusMessage.put(numPenalties);
raceStatusMessage.put(numPenaltiesServed);
raceStatusMessage.put(estNextMarkTime);
raceStatusMessage.put(estFinishTime);
}
return raceStatusMessage.array();
}
public static byte[] displayTextMessage(RaceMessage[] message){
//ByteBuffer result = ByteBuffer.allocate(4 + numLines * 32);

@ -29,8 +29,14 @@ public class BoatLocation extends AC35Data {
public static final byte Helicopter = 12;
public static final byte DataProcessingApplication = 13;
///Version number of the message - is always 1.
private byte messageVersionNumber = 1;
/**
* The current messageVersionNumber according to the API spec.
*/
public static final byte currentMessageVersionNumber = 1;
///Version number of the message.
private byte messageVersionNumber;
///Time of the event - milliseconds since jan 1 1970. Proper type is 6 byte int.
private long time;
///Source ID of the boat.
@ -91,12 +97,6 @@ public class BoatLocation extends AC35Data {
private short rudderAngle;
/**
* Ctor. Default.
*/
public BoatLocation() {
super(MessageType.BOATLOCATION);
}
/**
* Ctor, with all parameters.
@ -154,7 +154,7 @@ public class BoatLocation extends AC35Data {
public BoatLocation(int sourceID, double lat, double lon, long sequenceNumber, double heading, double boatSpeed, long time) {
super(MessageType.BOATLOCATION);
this.messageVersionNumber = (byte) 1;
this.messageVersionNumber = BoatLocation.currentMessageVersionNumber;
this.time = time;
this.sourceID = sourceID;
this.sequenceNumber = sequenceNumber;

@ -10,14 +10,14 @@ import network.Utils.ByteConverter;
public class BoatStatus {
private int sourceID;
private byte boatStatus;
private BoatStatusEnum boatStatus;
private byte legNumber;
private byte numPenaltiesAwarded;
private byte numPenaltiesServed;
private long estTimeAtNextMark;
private long estTimeAtFinish;
public BoatStatus(int sourceID, byte boatStatus, byte legNumber, byte numPenaltiesAwarded, byte numPenaltiesServed, long estTimeAtNextMark, long estTimeAtFinish) {
public BoatStatus(int sourceID, BoatStatusEnum boatStatus, byte legNumber, byte numPenaltiesAwarded, byte numPenaltiesServed, long estTimeAtNextMark, long estTimeAtFinish) {
this.sourceID = sourceID;
this.boatStatus = boatStatus;
this.legNumber = legNumber;
@ -30,7 +30,7 @@ public class BoatStatus {
public BoatStatus(int sourceID, BoatStatusEnum boatStatusEnum, int legNum, long estTimeAtNextMark) {
this.sourceID = sourceID;
this.boatStatus = boatStatusEnum.getValue();
this.boatStatus = boatStatusEnum;
this.legNumber = ByteConverter.intToBytes(legNum)[0];
this.numPenaltiesAwarded = 0;
this.numPenaltiesServed = 0;
@ -43,7 +43,7 @@ public class BoatStatus {
return sourceID;
}
public byte getBoatStatus() {
public BoatStatusEnum getBoatStatus() {
return boatStatus;
}

@ -33,11 +33,15 @@ public enum MessageType {
BOATACTION(100),
NOTAMESSAGE(0);
///Primitive value of the enum.
/**
* Primitive value of the enum.
*/
private byte value;
/**
* Ctor. Creates a MessageType enum from a given primitive integer value, cast to a byte.
* Creates a MessageType enum from a given primitive integer value, cast to a byte.
* @param value Integer, which is cast to byte, to construct from.
*/
private MessageType(int value) {

@ -2,27 +2,89 @@ package network.Messages;
import network.Messages.Enums.MessageType;
import network.Messages.Enums.RaceStatusEnum;
import network.Messages.Enums.RaceTypeEnum;
import network.Utils.AC35UnitConverter;
import shared.model.Constants;
import java.util.List;
/**
* Created by fwy13 on 25/04/17.
* Represents the information in a RaceStatus message (AC streaming spec: 4.2).
*/
public class RaceStatus extends AC35Data {
/**
* The current messageVersionNumber according to the API spec.
*/
public static final byte currentMessageVersionNumber = 2;
/**
* Version number of the message.
*/
private byte messageVersionNumber;
/**
* Time the message was generated at.
* Milliseconds since unix epoch.
*/
private long currentTime;
/**
* ID number of the race.
*/
private int raceID;
private byte raceStatus;
/**
* The status of the race.
*/
private RaceStatusEnum raceStatus;
/**
* The expected race start time.
* Milliseconds since unix epoch.
*/
private long expectedStartTime;
/**
* The wind direction of the course.
*/
private int windDirection;
/**
* The wind speed of the course.
*/
private int windSpeed;
private int raceType;
/**
* The type of race this is.
*/
private RaceTypeEnum raceType;
/**
* A list of boat statuses.
* One for each boat.
*/
private List<BoatStatus> boatStatuses;
public RaceStatus(long currentTime, int raceID, byte raceStatus, long expectedStartTime, int windDirection, int windSpeed, int raceType, List<BoatStatus> boatStatuses){
/**
* Constructs a RaceStatus message with the given parameters.
* @param messageVersionNumber The version number of the message.
* @param currentTime Time at which the message was generated.
* @param raceID The ID of the race.
* @param raceStatus The status of the race.
* @param expectedStartTime The expected start time of the race.
* @param windDirection The current wind direction in the race.
* @param windSpeed The current wind speed in the race.
* @param raceType The type of race this is.
* @param boatStatuses A list of BoatStatuses. One for each boat.
*/
public RaceStatus(byte messageVersionNumber, long currentTime, int raceID, RaceStatusEnum raceStatus, long expectedStartTime, int windDirection, int windSpeed, RaceTypeEnum raceType, List<BoatStatus> boatStatuses) {
super(MessageType.RACESTATUS);
this.messageVersionNumber = messageVersionNumber;
this.currentTime = currentTime;
this.raceID = raceID;
this.raceStatus = raceStatus;
@ -30,37 +92,58 @@ public class RaceStatus extends AC35Data {
this.windDirection = windDirection;
this.windSpeed = windSpeed;
this.raceType = raceType;
this.boatStatuses = boatStatuses;//note this is not a copy so any alterations to the parent will affect this.
this.boatStatuses = boatStatuses;
}
/**
* Returns the version number of this message.
* @return The version number of the message.
*/
public byte getMessageVersionNumber() {
return messageVersionNumber;
}
///Getters.
/**
* Returns the current time at which this message was generated. Milliseconds since unix epoch.
* @return Time this message was generated at.
*/
public long getCurrentTime()
{
return currentTime;
}
/**
* Returns the RaceID.
* @return The raceID.
*/
public int getRaceID()
{
return raceID;
}
/**
*
* @return race status number
* Returns the race status.
* @return The current race status.
*/
public byte getRaceStatus()
public RaceStatusEnum getRaceStatus()
{
return raceStatus;
}
/**
* Returns the expected start time for the race. Milliseconds since unix epoch.
* @return Expected start time for the race.
*/
public long getExpectedStartTime()
{
return expectedStartTime;
}
/**
* Returns the current direction of the wind in the race.
* @return Current wind direction.
*/
public int getWindDirection()
{
return windDirection;
@ -75,60 +158,70 @@ public class RaceStatus extends AC35Data {
return windSpeed;
}
public int getRaceType()
/**
* Retrusn the type of race this is.
* @return The type of race this is.
*/
public RaceTypeEnum getRaceType()
{
return raceType;
}
/**
* Returns the list of BoatStatuses. One for each boat.
* @return List of BoatStatuses.
*/
public List<BoatStatus> getBoatStatuses()
{
return boatStatuses;
}
public boolean isNotActive() {
return raceStatus == 0;
return raceStatus == RaceStatusEnum.NOT_ACTIVE;
}
public boolean isWarning() {
return raceStatus == 1;
return raceStatus == RaceStatusEnum.WARNING;
}
public boolean isPreparatory() {
return raceStatus == 2;
return raceStatus == RaceStatusEnum.PREPARATORY;
}
public boolean isStarted() {
return raceStatus == 3;
return raceStatus == RaceStatusEnum.STARTED;
}
public boolean isFinished() {
return raceStatus == 4;
return raceStatus == RaceStatusEnum.FINISHED;
}
public boolean isRetired() {
return raceStatus == 5;
return raceStatus == RaceStatusEnum.RETIRED;
}
public boolean isAbandoned() {
return raceStatus == 6;
return raceStatus == RaceStatusEnum.ABANDONED;
}
public boolean isPostponed() {
return raceStatus == 7;
return raceStatus == RaceStatusEnum.POSTPONED;
}
public boolean isTerminated() {
return raceStatus == 8;
return raceStatus == RaceStatusEnum.TERMINATED;
}
public boolean isStartTimeSet() {
return raceStatus != 9;
return raceStatus != RaceStatusEnum.RACE_START_TIME_NOT_SET;
}
public boolean isPrestart() {
return raceStatus == 10;
return raceStatus == RaceStatusEnum.PRESTART;
}
public double getScaledWindDirection() {
return AC35UnitConverter.convertHeading(windDirection);
}

@ -215,7 +215,7 @@ public class VisualiserRace extends Race implements Runnable {
//Boat status.
BoatStatusEnum newBoatStatusEnum = BoatStatusEnum.fromByte(boatStatus.getBoatStatus());
BoatStatusEnum newBoatStatusEnum = boatStatus.getBoatStatus();
//If we are changing from non-racing to racing, we need to initialise boat with their time at last mark.
if ((boat.getStatus() != BoatStatusEnum.RACING) && (newBoatStatusEnum == BoatStatusEnum.RACING)) {
@ -309,7 +309,7 @@ public class VisualiserRace extends Race implements Runnable {
private void updateRaceStatus(RaceStatus raceStatus) {
//Race status enum.
this.raceStatusEnum = RaceStatusEnum.fromByte(raceStatus.getRaceStatus());
this.raceStatusEnum = raceStatus.getRaceStatus();
//Wind.
this.setWind(

@ -0,0 +1,90 @@
package network.MessageDecoders;
import network.MessageEncoders.BoatStatusEncoder;
import network.MessageEncoders.RaceVisionByteEncoder;
import network.Messages.BoatStatus;
import network.Messages.Enums.BoatStatusEnum;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Test for the BoatStatus encoder and decoder
*/
public class BoatStatusDecoderTest {
/**
* Creates a BoatStatus message, encodes it, decodes it, and checks that the result matches the starting message.
* @throws Exception if test fails.
*/
@Test
public void boatStatusEncodeDecodeTest() throws Exception {
long time = System.currentTimeMillis();
//Create data to serialize.
int boatSourceID = 5;
BoatStatusEnum boatStatusEnum = BoatStatusEnum.RACING;
byte boatLegNumber = 5;
byte boatPenaltiesAwarded = 4;
byte boatPenaltiesServed = 2;
long boatTimeAtNextMark = time + (1000 * 3);
long boatTimeAtFinish = boatTimeAtNextMark + (1000 * 15);
BoatStatus boatStatus = new BoatStatus(
boatSourceID,
boatStatusEnum,
boatLegNumber,
boatPenaltiesAwarded,
boatPenaltiesServed,
boatTimeAtNextMark,
boatTimeAtFinish );
BoatStatus boatStatusDecoded = encodeDecodeBoatStatus(boatStatus);
compareBoatStatusMessages(boatStatus, boatStatusDecoded);
}
/**
* Encodes and decodes a BoatStatus, and returns it.
* @param boatStatus The BoatStatus to encode and decode.
* @return The decoded BoatStatus.
*/
private static BoatStatus encodeDecodeBoatStatus(BoatStatus boatStatus) {
BoatStatusEncoder boatStatusEncoder = new BoatStatusEncoder();
byte[] boatStatusEncoded = boatStatusEncoder.encode(boatStatus);
BoatStatusDecoder boatStatusDecoder = new BoatStatusDecoder(boatStatusEncoded);
BoatStatus boatStatusDecoded = boatStatusDecoder.getMessage();
return boatStatusDecoded;
}
/**
* Compares two BoatStatus messages to check that they are equal.
* @param original The original BoatStatus message.
* @param decoded The decoded BoatStatus message.
*/
public static void compareBoatStatusMessages(BoatStatus original, BoatStatus decoded) {
Assert.assertEquals(original.getSourceID(), decoded.getSourceID());
Assert.assertEquals(original.getBoatStatus(), decoded.getBoatStatus());
Assert.assertEquals(original.getLegNumber(), decoded.getLegNumber());
Assert.assertEquals(original.getNumPenaltiesAwarded(), decoded.getNumPenaltiesAwarded());
Assert.assertEquals(original.getNumPenaltiesServed(), decoded.getNumPenaltiesServed());
Assert.assertEquals(original.getEstTimeAtNextMark(), decoded.getEstTimeAtNextMark());
Assert.assertEquals(original.getEstTimeAtFinish(), decoded.getEstTimeAtFinish());
}
}

@ -1,27 +1,39 @@
package network.MessageDecoders;
import network.Exceptions.InvalidMessageException;
import network.MessageEncoders.RaceVisionByteEncoder;
import network.Messages.BoatStatus;
import network.Messages.Enums.BoatStatusEnum;
import network.Messages.Enums.RaceStatusEnum;
import network.Messages.Enums.RaceTypeEnum;
import network.Messages.RaceStatus;
import org.junit.Assert;
import org.junit.Test;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
/**
* Created by hba56 on 23/04/17.
* Test for the RaceStatus encoder and decoder
*/
public class RaceStatusDecoderTest {
/**
* Creates a RaceStatus message, encodes it, decodes it, and checks that the result matches the starting message.
* @throws Exception if test fails.
*/
@Test
public void getByteArrayTest(){
public void raceStatusEncodeDecodeTest() throws Exception {
long time = System.currentTimeMillis();
//Create data to serialize.
int boat1SourceID = 5;
int boat2SourceID = 8;
byte boat1Status = 2;
byte boat2Status = 2;
BoatStatusEnum boat1Status = BoatStatusEnum.RACING;
BoatStatusEnum boat2Status = BoatStatusEnum.RACING;
byte boat1LegNumber = 5;
byte boat2LegNumber = 3;
byte boat1PenaltiesAwarded = 4;
@ -33,44 +45,85 @@ public class RaceStatusDecoderTest {
long boat1TimeAtFinish = boat1TimeAtNextMark + (1000 * 15);
long boat2TimeAtFinish = boat2TimeAtNextMark + (1000 * 7);
BoatStatus boatStatus1 = new BoatStatus(boat1SourceID, boat1Status, boat1LegNumber, boat1PenaltiesAwarded, boat1PenaltiesServed, boat1TimeAtNextMark, boat1TimeAtFinish);
BoatStatus boatStatus2 = new BoatStatus(boat2SourceID, boat2Status, boat2LegNumber, boat2PenaltiesAwarded, boat2PenaltiesServed, boat2TimeAtNextMark, boat2TimeAtFinish);
BoatStatus boatStatus1 = new BoatStatus(
boat1SourceID,
boat1Status,
boat1LegNumber,
boat1PenaltiesAwarded,
boat1PenaltiesServed,
boat1TimeAtNextMark,
boat1TimeAtFinish );
BoatStatus boatStatus2 = new BoatStatus(
boat2SourceID,
boat2Status,
boat2LegNumber,
boat2PenaltiesAwarded,
boat2PenaltiesServed,
boat2TimeAtNextMark,
boat2TimeAtFinish );
int raceID = 585;
byte raceStatus = 3;
RaceStatusEnum raceStatus = RaceStatusEnum.STARTED;
long raceStartTime = time - (1000 * 31);
int windDirection = 2341;
int windSpeed = 10201;
int raceType = 1;
RaceTypeEnum raceType = RaceTypeEnum.MATCH_RACE;
List<BoatStatus> boatStatuses = new ArrayList<>(2);
boatStatuses.add(boatStatus1);
boatStatuses.add(boatStatus2);
RaceStatus raceStatusObject = new RaceStatus(time, raceID, raceStatus, raceStartTime, windDirection, windSpeed, raceType, boatStatuses);
RaceStatus raceStatusOriginal = new RaceStatus(
RaceStatus.currentMessageVersionNumber,
time,
raceID,
raceStatus,
raceStartTime,
windDirection,
windSpeed,
raceType,
boatStatuses );
byte[] encodedRaceStatus = RaceVisionByteEncoder.raceStatus(raceStatusObject);
byte[] encodedRaceStatus = RaceVisionByteEncoder.encode(raceStatusOriginal);
RaceStatusDecoder decoderTest = new RaceStatusDecoder(encodedRaceStatus);
Assert.assertEquals(0b10, decoderTest.getVersionNum());
Assert.assertEquals(time, decoderTest.getTime());
Assert.assertEquals(raceID, decoderTest.getRace());
Assert.assertEquals(raceStatus, decoderTest.getRaceState());
Assert.assertEquals(raceStartTime, decoderTest.getStartTime());
Assert.assertEquals(windDirection, decoderTest.getRaceWindDir());
Assert.assertEquals(windSpeed, decoderTest.getRaceWindSpeed());
RaceStatus decodedMessage = decoderTest.getMessage();
compareRaceStatusMessages(raceStatusOriginal, decodedMessage);
BoatStatus boat1 = decoderTest.getBoats().get(0);
}
Assert.assertEquals(boat1SourceID, boat1.getSourceID());
Assert.assertEquals(boat1Status, boat1.getBoatStatus());
Assert.assertEquals(boat1LegNumber, boat1.getLegNumber());
Assert.assertEquals(boat1PenaltiesAwarded, boat1.getNumPenaltiesAwarded());
Assert.assertEquals(boat1PenaltiesServed, boat1.getNumPenaltiesServed());
Assert.assertEquals(boat1TimeAtNextMark, boat1.getEstTimeAtNextMark());
Assert.assertEquals(boat1TimeAtFinish, boat1.getEstTimeAtFinish());
/**
* Compares two RaceStatus messages to check that they are equal.
* @param original The original RaceStatus message.
* @param decoded The decoded RaceStatus message.
*/
public static void compareRaceStatusMessages(RaceStatus original, RaceStatus decoded) {
//Compare RaceStatus body.
Assert.assertEquals(original.getMessageVersionNumber(), decoded.getMessageVersionNumber());
Assert.assertEquals(original.getCurrentTime(), decoded.getCurrentTime());
Assert.assertEquals(original.getRaceID(), decoded.getRaceID());
Assert.assertEquals(original.getRaceStatus(), decoded.getRaceStatus());
Assert.assertEquals(original.getExpectedStartTime(), decoded.getExpectedStartTime());
Assert.assertEquals(original.getWindDirection(), decoded.getWindDirection());
Assert.assertEquals(original.getWindSpeed(), decoded.getWindSpeed());
//Compare all BoatStatuses
Iterator<BoatStatus> originalIterator = original.getBoatStatuses().iterator();
Iterator<BoatStatus> decodedIterator = decoded.getBoatStatuses().iterator();
while (originalIterator.hasNext() && decodedIterator.hasNext()) {
BoatStatusDecoderTest.compareBoatStatusMessages(originalIterator.next(), decodedIterator.next());
}
}
}

Loading…
Cancel
Save