diff --git a/racevisionGame/src/main/java/network/BinaryMessageDecoder.java b/racevisionGame/src/main/java/network/BinaryMessageDecoder.java index 0ee0d541..b0d5b2a6 100644 --- a/racevisionGame/src/main/java/network/BinaryMessageDecoder.java +++ b/racevisionGame/src/main/java/network/BinaryMessageDecoder.java @@ -164,8 +164,8 @@ public class BinaryMessageDecoder { case RACESTARTSTATUS: //System.out.println("Race Start Status Message"); - RaceStartStatusDecoder rssDecoder = new RaceStartStatusDecoder(messageBody); - return new RaceStartStatus(rssDecoder.getTime(), rssDecoder.getAck(), rssDecoder.getStartTime(), rssDecoder.getRaceID(), rssDecoder. getNotification()); + RaceStartStatusDecoder rssDecoder = new RaceStartStatusDecoder(); + return rssDecoder.decode(messageBody); case YACHTEVENTCODE: //System.out.println("Yacht Action Code!"); diff --git a/racevisionGame/src/main/java/network/MessageDecoders/DecoderFactory.java b/racevisionGame/src/main/java/network/MessageDecoders/DecoderFactory.java index 500d20f6..3121f7f7 100644 --- a/racevisionGame/src/main/java/network/MessageDecoders/DecoderFactory.java +++ b/racevisionGame/src/main/java/network/MessageDecoders/DecoderFactory.java @@ -37,7 +37,7 @@ public class DecoderFactory { case XMLMESSAGE: return new XMLMessageDecoder(); - //case RACESTARTSTATUS: return new RaceStartStatusDecoder();//TODO + case RACESTARTSTATUS: return new RaceStartStatusDecoder(); //case YACHTEVENTCODE: return new YachtEventCodeDecoder();//TODO diff --git a/racevisionGame/src/main/java/network/MessageDecoders/RaceStartStatusDecoder.java b/racevisionGame/src/main/java/network/MessageDecoders/RaceStartStatusDecoder.java index 236c5d27..062f1212 100644 --- a/racevisionGame/src/main/java/network/MessageDecoders/RaceStartStatusDecoder.java +++ b/racevisionGame/src/main/java/network/MessageDecoders/RaceStartStatusDecoder.java @@ -1,66 +1,81 @@ package network.MessageDecoders; +import network.Messages.AC35Data; +import network.Messages.Enums.RaceStartTypeEnum; +import network.Messages.RaceStartStatus; + import java.util.Arrays; import static network.Utils.ByteConverter.*; /** - * Created by hba56 on 21/04/17. + * Decodes {@link RaceStartStatus} messages. */ -public class RaceStartStatusDecoder { - private byte messageVersion; - private byte[] timestamp; - private byte[] ackNumber; - private byte[] raceStartTime; - private byte[] raceIdentifier; - private byte notificationType; - - private long time; - private short ack; - private long startTime; - private int raceID; - private char notification; - - - public RaceStartStatusDecoder(byte[] encodedRaceStartStatus) { - messageVersion = encodedRaceStartStatus[0]; - timestamp = Arrays.copyOfRange(encodedRaceStartStatus, 1, 7); - ackNumber = Arrays.copyOfRange(encodedRaceStartStatus, 7, 9); - raceStartTime = Arrays.copyOfRange(encodedRaceStartStatus, 9, 15); - raceIdentifier = Arrays.copyOfRange(encodedRaceStartStatus, 15, 19); - notificationType = encodedRaceStartStatus[19]; - - time = bytesToLong(timestamp); - ack = bytesToShort(ackNumber); - startTime = bytesToLong(raceStartTime); - raceID = bytesToInt(raceIdentifier); - notification = bytesToChar(notificationType); - } +public class RaceStartStatusDecoder implements MessageDecoder { + /** + * The encoded message. + */ + private byte[] encodedMessage; - public byte getMessageVersion() { - return messageVersion; - } + /** + * The decoded message. + */ + private RaceStartStatus message; - public long getTime() { - return time; - } - public short getAck() { - return ack; - } - public long getStartTime() { - return startTime; + /** + * Constructs a decoder to decode a given message. + */ + public RaceStartStatusDecoder() { } - public int getRaceID() { - return raceID; + + @Override + public AC35Data decode(byte[] encodedMessage) { + this.encodedMessage = encodedMessage; + + + byte messageVersion = encodedMessage[0]; + + byte[] timestamp = Arrays.copyOfRange(encodedMessage, 1, 7); + long time = bytesToLong(timestamp); + + byte[] ackNumber = Arrays.copyOfRange(encodedMessage, 7, 9); + short ack = bytesToShort(ackNumber); + + byte[] raceStartTime = Arrays.copyOfRange(encodedMessage, 9, 15); + long startTime = bytesToLong(raceStartTime); + + byte[] raceIdentifier = Arrays.copyOfRange(encodedMessage, 15, 19); + int raceID = bytesToInt(raceIdentifier); + + byte notificationType = encodedMessage[19]; + + + + message = new RaceStartStatus( + messageVersion, + time, + ack, + startTime, + raceID, + RaceStartTypeEnum.fromByte(notificationType) + ); + + return message; + } - public char getNotification() { - return notification; + + /** + * Returns the decoded message. + * @return The decoded message. + */ + public RaceStartStatus getMessage() { + return message; } } diff --git a/racevisionGame/src/main/java/network/MessageEncoders/EncoderFactory.java b/racevisionGame/src/main/java/network/MessageEncoders/EncoderFactory.java index 6ac7ec5b..fadf6dea 100644 --- a/racevisionGame/src/main/java/network/MessageEncoders/EncoderFactory.java +++ b/racevisionGame/src/main/java/network/MessageEncoders/EncoderFactory.java @@ -37,7 +37,7 @@ public class EncoderFactory { case XMLMESSAGE: return new XMLMessageEncoder(); - //case RACESTARTSTATUS: return new RaceStartStatusEncoder();//TODO + case RACESTARTSTATUS: return new RaceStartStatusEncoder(); //case YACHTEVENTCODE: return new YachtEventCodeEncoder();//TODO diff --git a/racevisionGame/src/main/java/network/MessageEncoders/RaceStartStatusEncoder.java b/racevisionGame/src/main/java/network/MessageEncoders/RaceStartStatusEncoder.java new file mode 100644 index 00000000..f2d41d20 --- /dev/null +++ b/racevisionGame/src/main/java/network/MessageEncoders/RaceStartStatusEncoder.java @@ -0,0 +1,51 @@ +package network.MessageEncoders; + + +import network.Messages.AC35Data; +import network.Messages.RaceStartStatus; +import network.Utils.ByteConverter; + +import java.nio.ByteBuffer; + +import static network.Utils.ByteConverter.intToBytes; +import static network.Utils.ByteConverter.longToBytes; + +/** + * This encoder can encode a {@link RaceStartStatus} message. + */ +public class RaceStartStatusEncoder implements MessageEncoder { + + + /** + * Constructor. + */ + public RaceStartStatusEncoder() { + } + + + @Override + public byte[] encode(AC35Data message) { + + //Downcast. + RaceStartStatus raceStartStatus = (RaceStartStatus) message; + + + byte messageVersion = raceStartStatus.getMessageVersionNumber(); + byte[] timestamp = longToBytes(raceStartStatus.getTimestamp(), 6); + byte[] ackNumber = intToBytes(raceStartStatus.getAckNum(), 2); + byte[] raceStartTime = longToBytes(raceStartStatus.getRaceStartTime(), 6); + byte[] raceIdentifier = intToBytes(raceStartStatus.getRaceID()); + byte[] notificationType = intToBytes(raceStartStatus.getNotificationType().getValue(), 1); + + ByteBuffer result = ByteBuffer.allocate(20); + result.put(messageVersion); + result.put(timestamp); + result.put(ackNumber); + result.put(raceStartTime); + result.put(raceIdentifier); + result.put(notificationType); + + return result.array(); + + } +} diff --git a/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java b/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java index a933e179..45690bca 100644 --- a/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java +++ b/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java @@ -63,25 +63,6 @@ public class RaceVisionByteEncoder { return result.array(); } - public static byte[] raceStartStatus(long time, short ack, long startTime, int raceID, char notification){ - int messageVersion = 0b1; - byte[] timestamp = longToBytes(time, 6); - byte[] ackNumber = intToBytes(ack, 2); - byte[] raceStartTime = longToBytes(startTime, 6); - int raceIdentifier = raceID; - byte[] notificationType = intToBytes(notification, 1); - - ByteBuffer result = ByteBuffer.allocate(20); - result.put(intToBytes(messageVersion, 1)); - result.put(timestamp); - result.put(ackNumber); - result.put(raceStartTime); - result.put(intToBytes(raceIdentifier)); - result.put(notificationType); - - return result.array(); - } - public static byte[] yachtEventCode(long time, short acknowledgeNumber, int raceID, int destSourceID, int incidentID, int eventID){ int messageVersion = 0b10; diff --git a/racevisionGame/src/main/java/network/Messages/Enums/RaceStartTypeEnum.java b/racevisionGame/src/main/java/network/Messages/Enums/RaceStartTypeEnum.java new file mode 100644 index 00000000..d9b8d69b --- /dev/null +++ b/racevisionGame/src/main/java/network/Messages/Enums/RaceStartTypeEnum.java @@ -0,0 +1,97 @@ +package network.Messages.Enums; + +import java.util.HashMap; +import java.util.Map; + +/** + * Enumeration that encapsulates the various types race start status notifications. See AC35 streaming spec, 4.5. + */ +public enum RaceStartTypeEnum { + + + /** + * The race start time is being set. + */ + SET_RACE_START(1), + + /** + * The race has been postponed. + */ + RACE_POSTPONED(2), + + /** + * The race has been abandoned. + */ + RACE_ABANDONED(3), + + /** + * The race has been terminated. + */ + RACE_TERMINATED(4), + + /** + * Used to indicate that a given byte value is invalid. + */ + NOT_A_TYPE(-1); + + + /** + * Primitive value of the enum. + */ + private byte value; + + + /** + * Ctor. Creates a RaceStartTypeEnum from a given primitive integer value, cast to a byte. + * @param value Integer, which is cast to byte, to construct from. + */ + private RaceStartTypeEnum(int value) { + this.value = (byte) value; + } + + /** + * Returns the primitive value of the enum. + * @return Primitive value of the enum. + */ + public byte getValue() { + return value; + } + + + /** + * Stores a mapping between Byte values and RaceStartTypeEnum values. + */ + private static final Map byteToTypeMap = new HashMap<>(); + + + /* + Static initialization block. Initializes the byteToTypeMap. + */ + static { + for (RaceStartTypeEnum type : RaceStartTypeEnum.values()) { + RaceStartTypeEnum.byteToTypeMap.put(type.value, type); + } + } + + + /** + * Returns the enumeration value which corresponds to a given byte value. + * @param startTypeEnum Byte value to convert to a RaceStartTypeEnum value. + * @return The RaceStartTypeEnum value which corresponds to the given byte value. + */ + public static RaceStartTypeEnum fromByte(byte startTypeEnum) { + //Gets the corresponding MessageType from the map. + RaceStartTypeEnum type = RaceStartTypeEnum.byteToTypeMap.get(startTypeEnum); + + if (type == null) { + //If the byte value wasn't found, return the NOT_A_TYPE RaceStartTypeEnum. + return RaceStartTypeEnum.NOT_A_TYPE; + } else { + //Otherwise, return the RaceStartTypeEnum. + return type; + } + + } + + +} diff --git a/racevisionGame/src/main/java/network/Messages/RaceStartStatus.java b/racevisionGame/src/main/java/network/Messages/RaceStartStatus.java index 8dc442ab..6ad514d5 100644 --- a/racevisionGame/src/main/java/network/Messages/RaceStartStatus.java +++ b/racevisionGame/src/main/java/network/Messages/RaceStartStatus.java @@ -2,20 +2,63 @@ package network.Messages; import network.Messages.Enums.MessageType; +import network.Messages.Enums.RaceStartTypeEnum; + /** - * Created by fwy13 on 25/04/17. + * Represents a RaceStartStatus message from the API, section 4.5. */ public class RaceStartStatus extends AC35Data { + /** + * The current version number of this message type. + */ + public static final byte currentMessageVersionNumber = 1; + + + /** + * The version number of this message. + */ + private byte messageVersionNumber; + + /** + * The time at which this message was created. Milliseconds since unix epoch. + */ private long timestamp; + + /** + * Sequence number of message. + */ private int ackNum; + + /** + * The time the race is expected to start at. Milliseconds since unix epoch. + */ private long raceStartTime; + + /** + * The ID of the race this message relates to. + */ private int raceID; - private int notificationType; - public RaceStartStatus(long timestamp, int ackNum, long raceStartTime, int raceID, int notificationType){ + /** + * The type of notification this is. + */ + private RaceStartTypeEnum notificationType; + + + /** + * Constructs a RaceStartStatus message with the given parameters. + * @param messageVersionNumber Version number of the message. + * @param timestamp The timestamp at which this message was generated. + * @param ackNum The sequence number of this message. + * @param raceStartTime The expected race start time. + * @param raceID The ID of the race this message relates to. + * @param notificationType The type of notification this is. + */ + public RaceStartStatus(byte messageVersionNumber, long timestamp, int ackNum, long raceStartTime, int raceID, RaceStartTypeEnum notificationType) { super(MessageType.RACESTARTSTATUS); + this.messageVersionNumber = messageVersionNumber; this.timestamp = timestamp; this.ackNum = ackNum; this.raceStartTime = raceStartTime; @@ -23,4 +66,52 @@ public class RaceStartStatus extends AC35Data { this.notificationType = notificationType; } + + /** + * Returns the version number of this message. + * @return Version number of this message. + */ + public byte getMessageVersionNumber() { + return messageVersionNumber; + } + + /** + * Return the time at which this message was generated. Milliseconds since unix epoch. + * @return Time at which this message was generated. + */ + public long getTimestamp() { + return timestamp; + } + + /** + * Returns the sequence number of this message. + * @return Sequence number of this message. + */ + public int getAckNum() { + return ackNum; + } + + /** + * Returns the expected race start time. Milliseconds since unix epoch. + * @return Expected race start time. + */ + public long getRaceStartTime() { + return raceStartTime; + } + + /** + * Returns the race ID this message relates to. + * @return Race ID this message relates to. + */ + public int getRaceID() { + return raceID; + } + + /** + * Returns the type of start status notification this message is. + * @return The type of notification this is. + */ + public RaceStartTypeEnum getNotificationType() { + return notificationType; + } } diff --git a/racevisionGame/src/test/java/network/MessageDecoders/RaceStartStatusDecoderTest.java b/racevisionGame/src/test/java/network/MessageDecoders/RaceStartStatusDecoderTest.java index ce2fe475..b0029321 100644 --- a/racevisionGame/src/test/java/network/MessageDecoders/RaceStartStatusDecoderTest.java +++ b/racevisionGame/src/test/java/network/MessageDecoders/RaceStartStatusDecoderTest.java @@ -1,29 +1,62 @@ package network.MessageDecoders; import network.MessageEncoders.RaceVisionByteEncoder; +import network.Messages.Enums.RaceStartTypeEnum; +import network.Messages.RaceStartStatus; import org.junit.Assert; import org.junit.Test; /** - * Created by hba56 on 23/04/17. + * Tests for the RaceStartStatus encoder and decoder. */ public class RaceStartStatusDecoderTest { + + + /** + * Tests if a RaceStartStatus message can be encoded and decoded correctly. + * @throws Exception Thrown when an error occurs. + */ @Test - public void getByteArrayTest(){ - long time = System.currentTimeMillis(); + public void raceStartStatusEncodeDecodeTest() throws Exception { + + long timestamp = System.currentTimeMillis(); - long time2 = System.currentTimeMillis(); - byte[] encodedRaceStartStatus = RaceVisionByteEncoder.raceStartStatus(time, (short)1, - time2, 2, (char)3); + long startTime = System.currentTimeMillis() + 10 * 1000; - RaceStartStatusDecoder testDecoder = new RaceStartStatusDecoder(encodedRaceStartStatus); + RaceStartStatus raceStartStatus = new RaceStartStatus( + RaceStartStatus.currentMessageVersionNumber, + timestamp, + 55, + startTime, + 35, + RaceStartTypeEnum.SET_RACE_START + ); - Assert.assertEquals(0b1, testDecoder.getMessageVersion()); - Assert.assertEquals(time, testDecoder.getTime()); - Assert.assertEquals(1, testDecoder.getAck()); - Assert.assertEquals(time2, testDecoder.getStartTime()); - Assert.assertEquals(2, testDecoder.getRaceID()); - Assert.assertEquals((char)3, testDecoder.getNotification()); + byte[] encodedRaceStartStatus = RaceVisionByteEncoder.encode(raceStartStatus); + + RaceStartStatusDecoder testDecoder = new RaceStartStatusDecoder(); + testDecoder.decode(encodedRaceStartStatus); + RaceStartStatus raceStartStatusDecoded = testDecoder.getMessage(); + + compareRaceStartStatusMessages(raceStartStatus, raceStartStatusDecoded); } + + /** + * Compares two RaceStartStatus messages to check that they are the same. + * @param original The original message. + * @param decoded The decoded message. + */ + public static void compareRaceStartStatusMessages(RaceStartStatus original, RaceStartStatus decoded) { + + Assert.assertEquals(original.getMessageVersionNumber(), decoded.getMessageVersionNumber()); + Assert.assertEquals(original.getTimestamp(), decoded.getTimestamp()); + Assert.assertEquals(original.getAckNum(), decoded.getAckNum()); + Assert.assertEquals(original.getRaceStartTime(), decoded.getRaceStartTime()); + Assert.assertEquals(original.getRaceID(), decoded.getRaceID()); + Assert.assertEquals(original.getNotificationType(), decoded.getNotificationType()); + + } + + }