diff --git a/racevisionGame/src/main/java/network/MessageDecoders/DecoderFactory.java b/racevisionGame/src/main/java/network/MessageDecoders/DecoderFactory.java index 09ea4b95..24caf97c 100644 --- a/racevisionGame/src/main/java/network/MessageDecoders/DecoderFactory.java +++ b/racevisionGame/src/main/java/network/MessageDecoders/DecoderFactory.java @@ -39,7 +39,7 @@ public class DecoderFactory { case RACESTARTSTATUS: return new RaceStartStatusDecoder(); - //case YACHTEVENTCODE: return new YachtEventCodeDecoder();//TODO + case YACHTEVENTCODE: return new YachtEventCodeDecoder(); //case YACHTACTIONCODE: return new YachtActionCodeDecoder();//TODO diff --git a/racevisionGame/src/main/java/network/MessageDecoders/YachtEventCodeDecoder.java b/racevisionGame/src/main/java/network/MessageDecoders/YachtEventCodeDecoder.java new file mode 100644 index 00000000..fb601c24 --- /dev/null +++ b/racevisionGame/src/main/java/network/MessageDecoders/YachtEventCodeDecoder.java @@ -0,0 +1,47 @@ +package network.MessageDecoders; + +import network.Exceptions.InvalidMessageException; +import network.Messages.AC35Data; +import network.Messages.Enums.YachtEventEnum; +import network.Messages.YachtEvent; + +import java.util.Arrays; + +import static network.Utils.ByteConverter.bytesToInt; +import static network.Utils.ByteConverter.bytesToLong; +import static network.Utils.ByteConverter.bytesToShort; + +/** + * Decodes {@link YachtEvent} messages. + */ +public class YachtEventCodeDecoder implements MessageDecoder { + private YachtEvent message; + + @Override + public AC35Data decode(byte[] encodedMessage) throws InvalidMessageException { + // Deserialise message + byte[] timestamp = Arrays.copyOfRange(encodedMessage, 1, 7); + byte[] ackNum = Arrays.copyOfRange(encodedMessage, 7, 9); + byte[] raceID = Arrays.copyOfRange(encodedMessage, 9, 13); + byte[] sourceID = Arrays.copyOfRange(encodedMessage, 13, 17); + byte[] incidentID = Arrays.copyOfRange(encodedMessage, 17, 21); + byte eventID = encodedMessage[21]; + + // Unpack bytes into YachtEvent + this.message = new YachtEvent( + bytesToLong(timestamp), + bytesToShort(ackNum), + bytesToInt(raceID), + bytesToInt(sourceID), + bytesToInt(incidentID), + YachtEventEnum.fromByte(eventID) + ); + + // Return YachtEvent + return message; + } + + public YachtEvent getMessage() { + return message; + } +} diff --git a/racevisionGame/src/main/java/network/MessageEncoders/EncoderFactory.java b/racevisionGame/src/main/java/network/MessageEncoders/EncoderFactory.java index b59150e4..d274f435 100644 --- a/racevisionGame/src/main/java/network/MessageEncoders/EncoderFactory.java +++ b/racevisionGame/src/main/java/network/MessageEncoders/EncoderFactory.java @@ -39,7 +39,7 @@ public class EncoderFactory { case RACESTARTSTATUS: return new RaceStartStatusEncoder(); - //case YACHTEVENTCODE: return new YachtEventCodeEncoder();//TODO + case YACHTEVENTCODE: return new YachtEventCodeEncoder(); //case YACHTACTIONCODE: return new YachtActionCodeEncoder();//TODO diff --git a/racevisionGame/src/main/java/network/MessageEncoders/YachtEventCodeEncoder.java b/racevisionGame/src/main/java/network/MessageEncoders/YachtEventCodeEncoder.java new file mode 100644 index 00000000..505761f1 --- /dev/null +++ b/racevisionGame/src/main/java/network/MessageEncoders/YachtEventCodeEncoder.java @@ -0,0 +1,43 @@ +package network.MessageEncoders; + +import network.Exceptions.InvalidMessageException; +import network.Messages.AC35Data; +import network.Messages.YachtEvent; + +import java.nio.ByteBuffer; + +import static network.Utils.ByteConverter.intToBytes; +import static network.Utils.ByteConverter.longToBytes; + +/** + * Encodes a {@link YachtEvent} message. + */ +public class YachtEventCodeEncoder implements MessageEncoder { + @Override + public byte[] encode(AC35Data message) throws InvalidMessageException { + // Downcast message + YachtEvent yachtEvent = (YachtEvent)message; + + // Serialise message + byte messageVersion = 0b10; + byte[] timestamp = longToBytes(yachtEvent.getCurrentTime(), 6); + byte[] ackNum = intToBytes(yachtEvent.getAckNum(), 2); + byte[] raceID = intToBytes(yachtEvent.getRaceID()); + byte[] sourceID = intToBytes(yachtEvent.getSourceID()); + byte[] incidentID = intToBytes(yachtEvent.getIncidentID()); + byte eventID = yachtEvent.getYachtEvent().getValue(); + + // Pack bytes into string + ByteBuffer yachtEventMessage = ByteBuffer.allocate(22); + yachtEventMessage.put(messageVersion); + yachtEventMessage.put(timestamp); + yachtEventMessage.put(ackNum); + yachtEventMessage.put(raceID); + yachtEventMessage.put(sourceID); + yachtEventMessage.put(incidentID); + yachtEventMessage.put(eventID); + + // Return byte string + return yachtEventMessage.array(); + } +} diff --git a/racevisionGame/src/main/java/network/Messages/Enums/YachtEventEnum.java b/racevisionGame/src/main/java/network/Messages/Enums/YachtEventEnum.java new file mode 100644 index 00000000..73be05b5 --- /dev/null +++ b/racevisionGame/src/main/java/network/Messages/Enums/YachtEventEnum.java @@ -0,0 +1,24 @@ +package network.Messages.Enums; + +/** + * Yacht event codes + */ +public enum YachtEventEnum { + NOT_AN_EVENT(-1), + COLLISION(1); + + private byte value; + + YachtEventEnum(int value) { this.value = (byte)value; } + + public byte getValue() { + return value; + } + + public static YachtEventEnum fromByte(byte value) { + switch(value) { + case 1: return COLLISION; + default: return NOT_AN_EVENT; + } + } +} diff --git a/racevisionGame/src/main/java/network/Messages/YachtEvent.java b/racevisionGame/src/main/java/network/Messages/YachtEvent.java new file mode 100644 index 00000000..db2ff931 --- /dev/null +++ b/racevisionGame/src/main/java/network/Messages/YachtEvent.java @@ -0,0 +1,51 @@ +package network.Messages; + +import network.Messages.Enums.MessageType; +import network.Messages.Enums.YachtEventEnum; + +/** + * Represents a Yacht Event Code message defined in the AC35 spec, with Event IDs amended for the purposes of + * a game. + */ +public class YachtEvent extends AC35Data { + private long currentTime; + private int ackNum; + private int raceID; + private int sourceID; + private int incidentID; + private YachtEventEnum yachtEvent; + + public YachtEvent(long currentTime, int ackNum, int raceID, int sourceID, int incidentID, YachtEventEnum yachtEvent) { + super(MessageType.YACHTEVENTCODE); + this.currentTime = currentTime; + this.ackNum = ackNum; + this.raceID = raceID; + this.sourceID = sourceID; + this.incidentID = incidentID; + this.yachtEvent = yachtEvent; + } + + public YachtEventEnum getYachtEvent() { + return yachtEvent; + } + + public int getSourceID() { + return sourceID; + } + + public int getIncidentID() { + return incidentID; + } + + public long getCurrentTime() { + return currentTime; + } + + public int getAckNum() { + return ackNum; + } + + public int getRaceID() { + return raceID; + } +} diff --git a/racevisionGame/src/test/java/network/MessageDecoders/YachtEventCodeDecoderTest.java b/racevisionGame/src/test/java/network/MessageDecoders/YachtEventCodeDecoderTest.java new file mode 100644 index 00000000..3f3aadac --- /dev/null +++ b/racevisionGame/src/test/java/network/MessageDecoders/YachtEventCodeDecoderTest.java @@ -0,0 +1,47 @@ +package network.MessageDecoders; + +import network.MessageEncoders.RaceVisionByteEncoder; +import network.Messages.Enums.YachtEventEnum; +import network.Messages.YachtEvent; +import org.junit.Before; +import org.junit.Test; + +import static org.testng.Assert.*; + +/** + * Tests for the YachtEvent decoder and encoder + */ +public class YachtEventCodeDecoderTest { + private YachtEvent decodedMessage; + private YachtEvent originalMessage; + + @Before + public void setUp() throws Exception { + long timestamp = System.currentTimeMillis(); + + originalMessage = new YachtEvent( + timestamp, + 55, + 35, + 0, + 1, + YachtEventEnum.COLLISION + ); + + byte[] encodedMessage = RaceVisionByteEncoder.encode(originalMessage); + + YachtEventCodeDecoder testDecoder = new YachtEventCodeDecoder(); + testDecoder.decode(encodedMessage); + decodedMessage = testDecoder.getMessage(); + } + + @Test + public void decodingEqualsOriginal() { + assertEquals(originalMessage.getCurrentTime(), decodedMessage.getCurrentTime()); + assertEquals(originalMessage.getAckNum(), decodedMessage.getAckNum()); + assertEquals(originalMessage.getRaceID(), decodedMessage.getRaceID()); + assertEquals(originalMessage.getSourceID(), decodedMessage.getSourceID()); + assertEquals(originalMessage.getIncidentID(), decodedMessage.getIncidentID()); + assertEquals(originalMessage.getYachtEvent(), decodedMessage.getYachtEvent()); + } +} \ No newline at end of file