Added MarkRoundingEncoder.

Refactored MarkRoundingDecoder - it now implements MessageDecoder.
Tidied up MarkRounding - it is now documented and has getters. Also Created MarkRoundingBoatStatusEnum, MarkRoundingIDEnum, MarkRoundingSideEnum, MarkRoundingTypeEnum.
Added MarkRoundingDecoderTest.
issue #35 #36
#story[1095]
main
fjc40 8 years ago
parent da800e659a
commit 9c64b678e3

@ -189,8 +189,8 @@ public class BinaryMessageDecoder {
case MARKROUNDING:
//System.out.println("Mark Rounding Message!");
MarkRoundingDecoder mrDecoder = new MarkRoundingDecoder(messageBody);
return mrDecoder.getMarkRounding();
MarkRoundingDecoder mrDecoder = new MarkRoundingDecoder();
return mrDecoder.decode(messageBody);
case COURSEWIND:
//System.out.println("Course Wind Message!");

@ -47,7 +47,7 @@ public class DecoderFactory {
case BOATLOCATION: return new BoatLocationDecoder();
//case MARKROUNDING: return new MarkRoundingDecoder()//TODO;
case MARKROUNDING: return new MarkRoundingDecoder();
case COURSEWIND: return new CourseWindsDecoder();

@ -1,52 +1,90 @@
package network.MessageDecoders;
import network.Messages.AC35Data;
import network.Messages.Enums.MarkRoundingBoatStatusEnum;
import network.Messages.Enums.MarkRoundingSideEnum;
import network.Messages.Enums.MarkRoundingTypeEnum;
import network.Messages.MarkRounding;
import network.Utils.ByteConverter;
import java.util.Arrays;
/**
* Created by hba56 on 23/04/17.
* Decoder for {@link MarkRounding} messages.
*/
public class MarkRoundingDecoder {
byte messageVersionNumber;
byte[] byteTime;
byte[] byteAck;
byte[] byteRaceID;
byte[] byteSourceID;
byte byteBoatStatus;
byte byteRoundingSide;
byte byteMarkType;
byte byteMarkID;
MarkRounding markRounding;
public MarkRoundingDecoder(byte[] encodedMarkRounding) {
messageVersionNumber = encodedMarkRounding[0];
byteTime = Arrays.copyOfRange(encodedMarkRounding, 1, 7);
byteAck = Arrays.copyOfRange(encodedMarkRounding, 7, 9);
byteRaceID = Arrays.copyOfRange(encodedMarkRounding, 9, 13);
byteSourceID = Arrays.copyOfRange(encodedMarkRounding, 13, 17);
byteBoatStatus = encodedMarkRounding[17];
byteRoundingSide = encodedMarkRounding[18];
byteMarkType = encodedMarkRounding[19];
byteMarkID = encodedMarkRounding[20];
int intMsgVer = ByteConverter.bytesToInt(messageVersionNumber);
long lngTime = ByteConverter.bytesToLong(byteTime);
int intAck = ByteConverter.bytesToInt(byteAck);
int intRaceID = ByteConverter.bytesToInt(byteRaceID);
int intSourceID = ByteConverter.bytesToInt(byteSourceID);
int intBoatState = ByteConverter.bytesToInt(byteBoatStatus);
int intRoundingSide = ByteConverter.bytesToInt(byteRoundingSide);
int intMarkType = ByteConverter.bytesToInt(byteMarkType);
int intMarkID = ByteConverter.bytesToInt(byteMarkID);
markRounding = new MarkRounding(intMsgVer, lngTime, intAck, intRaceID, intSourceID, intBoatState, intRoundingSide, intMarkType, intMarkID);
public class MarkRoundingDecoder implements MessageDecoder {
/**
* The encoded message.
*/
private byte[] encodedMessage;
/**
* The decoded message.
*/
private MarkRounding message;
/**
* Constructs a decoder to decode a given message.
*/
public MarkRoundingDecoder() {
}
public MarkRounding getMarkRounding() {
return markRounding;
@Override
public AC35Data decode(byte[] encodedMessage) {
this.encodedMessage = encodedMessage;
byte messageVersionNumber = encodedMessage[0];
byte[] byteTime = Arrays.copyOfRange(encodedMessage, 1, 7);
long time = ByteConverter.bytesToLong(byteTime);
byte[] byteAck = Arrays.copyOfRange(encodedMessage, 7, 9);
int ackNumber = ByteConverter.bytesToInt(byteAck);
byte[] byteRaceID = Arrays.copyOfRange(encodedMessage, 9, 13);
int raceID = ByteConverter.bytesToInt(byteRaceID);
byte[] byteSourceID = Arrays.copyOfRange(encodedMessage, 13, 17);
int sourceID = ByteConverter.bytesToInt(byteSourceID);
byte byteBoatStatus = encodedMessage[17];
MarkRoundingBoatStatusEnum boatStatus = MarkRoundingBoatStatusEnum.fromByte(byteBoatStatus);
byte byteRoundingSide = encodedMessage[18];
MarkRoundingSideEnum roundingSide = MarkRoundingSideEnum.fromByte(byteRoundingSide);
byte byteMarkType = encodedMessage[19];
MarkRoundingTypeEnum markType = MarkRoundingTypeEnum.fromByte(byteMarkType);
byte byteMarkID = encodedMessage[20];
message = new MarkRounding(
messageVersionNumber,
time,
ackNumber,
raceID,
sourceID,
boatStatus,
roundingSide,
markType,
byteMarkID);
return message;
}
/**
* Returns the decoded message.
*
* @return The decoded message.
*/
public MarkRounding getMessage() {
return message;
}
}

@ -47,7 +47,7 @@ public class EncoderFactory {
case BOATLOCATION: return new BoatLocationEncoder();
//case MARKROUNDING: return new MarkRoundingEncoder();//TODO
case MARKROUNDING: return new MarkRoundingEncoder();
case COURSEWIND: return new CourseWindsEncoder();

@ -0,0 +1,57 @@
package network.MessageEncoders;
import network.Messages.AC35Data;
import network.Messages.MarkRounding;
import java.nio.ByteBuffer;
import static network.Utils.ByteConverter.intToBytes;
import static network.Utils.ByteConverter.longToBytes;
/**
* This encoder can encode a {@link MarkRounding} message.
*/
public class MarkRoundingEncoder implements MessageEncoder {
/**
* Constructor.
*/
public MarkRoundingEncoder() {
}
@Override
public byte[] encode(AC35Data message) {
//Downcast.
MarkRounding markRounding = (MarkRounding) message;
byte messageVersionNumber = markRounding.getMessageVersionNumber();
byte[] byteTime = longToBytes(markRounding.getTime(), 6);
byte[] byteAck = intToBytes(markRounding.getAckNum(), 2);
byte[] byteRaceID = intToBytes(markRounding.getRaceID(), 4);
byte[] byteSourceID = intToBytes(markRounding.getSourceID(), 4);
byte[] byteBoatStatus = intToBytes(markRounding.getBoatStatus().getValue(), 1);
byte[] byteRoundingSide = intToBytes(markRounding.getRoundingSide().getValue(), 1);
byte[] byteMarkType = intToBytes(markRounding.getMarkType().getValue(), 1);
byte[] byteMarkID = intToBytes(markRounding.getMarkID(), 1);
ByteBuffer result = ByteBuffer.allocate(21);
result.put(messageVersionNumber);
result.put(byteTime);
result.put(byteAck);
result.put(byteRaceID);
result.put(byteSourceID);
result.put(byteBoatStatus);
result.put(byteRoundingSide);
result.put(byteMarkType);
result.put(byteMarkID);
return result.array();
}
}

@ -100,30 +100,6 @@ public class RaceVisionByteEncoder {
}
public static byte[] markRounding(int time, int ackNumber, int raceID, int sourceID, int boatStatus, int roundingSide, int markType, int markID){
int messageVersionNumber = 0b1;
byte[] byteTime = longToBytes(time, 6);
byte[] byteAck = intToBytes(ackNumber, 2);
byte[] byteRaceID = intToBytes(raceID, 4);
byte[] byteSourceID = intToBytes(sourceID, 4);
byte[] byteBoatStatus = intToBytes(boatStatus, 1);
byte[] byteRoundingSide = intToBytes(roundingSide, 1);
byte[] byteMarkType = intToBytes(markType, 1);
byte[] byteMarkID = intToBytes(markID, 1);
ByteBuffer result = ByteBuffer.allocate(21);
result.put(intToBytes(messageVersionNumber, 1));
result.put(byteTime);
result.put(byteAck);
result.put(byteRaceID);
result.put(byteSourceID);
result.put(byteBoatStatus);
result.put(byteRoundingSide);
result.put(byteMarkType);
result.put(byteMarkID);
return result.array();
}
/**

@ -0,0 +1,88 @@
package network.Messages.Enums;
import java.util.HashMap;
import java.util.Map;
/**
* Enumeration that encapsulates the various statuses a boat can have when rounding a mark.
*/
public enum MarkRoundingBoatStatusEnum {
UNKNOWN(0),
/**
* The boat is actively racing.
*/
RACING(1),
/**
* The boat has been disqualified.
*/
DSQ(2),
/**
* The boat has withdrawn from the race.
*/
WITHDRAWN(3),
NOT_A_STATUS(-1);
/**
* Primitive value of the enum.
*/
private byte value;
/**
* Ctor. Creates a {@link MarkRoundingBoatStatusEnum} from a given primitive integer value, cast to a byte.
* @param value Integer, which is cast to byte, to construct from.
*/
private MarkRoundingBoatStatusEnum(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 MarkRoundingBoatStatusEnum values.
private static final Map<Byte, MarkRoundingBoatStatusEnum> byteToStatusMap = new HashMap<>();
/*
Static initialization block. Initializes the byteToStatusMap.
*/
static {
for (MarkRoundingBoatStatusEnum type : MarkRoundingBoatStatusEnum.values()) {
byteToStatusMap.put(type.value, type);
}
}
/**
* Returns the enumeration value which corresponds to a given byte value.
* @param boatStatusByte Byte value to convert to a {@link MarkRoundingBoatStatusEnum} value.
* @return The {@link MarkRoundingBoatStatusEnum} value which corresponds to the given byte value.
*/
public static MarkRoundingBoatStatusEnum fromByte(byte boatStatusByte) {
//Gets the corresponding MarkRoundingBoatStatusEnum from the map.
MarkRoundingBoatStatusEnum type = byteToStatusMap.get(boatStatusByte);
if (type == null) {
//If the byte value wasn't found, return the NOT_A_STATUS MarkRoundingBoatStatusEnum.
return MarkRoundingBoatStatusEnum.NOT_A_STATUS;
}
else {
//Otherwise, return the MarkRoundingBoatStatusEnum.
return type;
}
}
}

@ -0,0 +1,88 @@
package network.Messages.Enums;
import java.util.HashMap;
import java.util.Map;
/**
* Enumeration that encapsulates the various mark identities.
*/
public enum MarkRoundingIDEnum {
UNKNOWN(0),
ENTRY_LIMIT_LINE(100),
ENTRY_LINE(101),
START_LINE(102),
FINISH_LINE(103),
SPEED_TEST_START(104),
SPEED_TEST_FINISH(105),
CLEAR_START(106),
NOT_AN_ID(-1);
/**
* Primitive value of the enum.
*/
private byte value;
/**
* Ctor. Creates a {@link MarkRoundingIDEnum} from a given primitive integer value, cast to a byte.
* @param value Integer, which is cast to byte, to construct from.
*/
private MarkRoundingIDEnum(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 MarkRoundingIDEnum values.
private static final Map<Byte, MarkRoundingIDEnum> byteToIDMap = new HashMap<>();
/*
Static initialization block. Initializes the byteToIDMap.
*/
static {
for (MarkRoundingIDEnum type : MarkRoundingIDEnum.values()) {
byteToIDMap.put(type.value, type);
}
}
/**
* Returns the enumeration value which corresponds to a given byte value.
* @param sideByte Byte value to convert to a {@link MarkRoundingIDEnum} value.
* @return The {@link MarkRoundingIDEnum} value which corresponds to the given byte value.
*/
public static MarkRoundingIDEnum fromByte(byte sideByte) {
//Gets the corresponding MarkRoundingIDEnum from the map.
MarkRoundingIDEnum type = byteToIDMap.get(sideByte);
if (type == null) {
//If the byte value wasn't found, return the NOT_AN_ID MarkRoundingIDEnum.
return MarkRoundingIDEnum.NOT_AN_ID;
}
else {
//Otherwise, return the MarkRoundingIDEnum.
return type;
}
}
}

@ -0,0 +1,82 @@
package network.Messages.Enums;
import java.util.HashMap;
import java.util.Map;
/**
* Enumeration that encapsulates the various sides around which a boat may pass a mark.
*/
public enum MarkRoundingSideEnum {
UNKNOWN(0),
/**
* Boat rounded around port side of mark.
*/
PORT(1),
/**
* Boat rounded around starboard side of mark.
*/
STARBOARD(2),
NOT_A_SIDE(-1);
/**
* Primitive value of the enum.
*/
private byte value;
/**
* Ctor. Creates a {@link MarkRoundingSideEnum} from a given primitive integer value, cast to a byte.
* @param value Integer, which is cast to byte, to construct from.
*/
private MarkRoundingSideEnum(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 MarkRoundingSideEnum values.
private static final Map<Byte, MarkRoundingSideEnum> byteToSideMap = new HashMap<>();
/*
Static initialization block. Initializes the byteToSideMap.
*/
static {
for (MarkRoundingSideEnum type : MarkRoundingSideEnum.values()) {
byteToSideMap.put(type.value, type);
}
}
/**
* Returns the enumeration value which corresponds to a given byte value.
* @param sideByte Byte value to convert to a {@link MarkRoundingSideEnum} value.
* @return The {@link MarkRoundingSideEnum} value which corresponds to the given byte value.
*/
public static MarkRoundingSideEnum fromByte(byte sideByte) {
//Gets the corresponding MarkRoundingSideEnum from the map.
MarkRoundingSideEnum type = byteToSideMap.get(sideByte);
if (type == null) {
//If the byte value wasn't found, return the NOT_A_SIDE MarkRoundingSideEnum.
return MarkRoundingSideEnum.NOT_A_SIDE;
}
else {
//Otherwise, return the MarkRoundingSideEnum.
return type;
}
}
}

@ -0,0 +1,82 @@
package network.Messages.Enums;
import java.util.HashMap;
import java.util.Map;
/**
* Enumeration that encapsulates the various types of marks that may be passed.
*/
public enum MarkRoundingTypeEnum {
UNKNOWN(0),
/**
* The mark is a singular mark.
*/
MARK(1),
/**
* The mark is a gate (windward, leeward, start, finish, etc...).
*/
GATE(2),
NOT_A_TYPE(-1);
/**
* Primitive value of the enum.
*/
private byte value;
/**
* Ctor. Creates a {@link MarkRoundingTypeEnum} from a given primitive integer value, cast to a byte.
* @param value Integer, which is cast to byte, to construct from.
*/
private MarkRoundingTypeEnum(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 MarkRoundingTypeEnum values.
private static final Map<Byte, MarkRoundingTypeEnum> byteToTypeMap = new HashMap<>();
/*
Static initialization block. Initializes the byteToTypeMap.
*/
static {
for (MarkRoundingTypeEnum type : MarkRoundingTypeEnum.values()) {
byteToTypeMap.put(type.value, type);
}
}
/**
* Returns the enumeration value which corresponds to a given byte value.
* @param sideByte Byte value to convert to a {@link MarkRoundingTypeEnum} value.
* @return The {@link MarkRoundingTypeEnum} value which corresponds to the given byte value.
*/
public static MarkRoundingTypeEnum fromByte(byte sideByte) {
//Gets the corresponding MarkRoundingTypeEnum from the map.
MarkRoundingTypeEnum type = byteToTypeMap.get(sideByte);
if (type == null) {
//If the byte value wasn't found, return the NOT_A_TYPE MarkRoundingTypeEnum.
return MarkRoundingTypeEnum.NOT_A_TYPE;
}
else {
//Otherwise, return the MarkRoundingTypeEnum.
return type;
}
}
}

@ -1,47 +1,94 @@
package network.Messages;
import network.Messages.Enums.MarkRoundingBoatStatusEnum;
import network.Messages.Enums.MarkRoundingSideEnum;
import network.Messages.Enums.MarkRoundingTypeEnum;
import network.Messages.Enums.MessageType;
/**
* Created by fwy13 on 25/04/17.
* Represents a MarkRound message (see AC35 spec, 4.10).
*/
public class MarkRounding extends AC35Data {
private int msgVerNum;
/**
* The current messageVersionNumber according to the API spec.
*/
public static final byte currentMessageVersionNumber = 1;
/**
* Version number of the message.
*/
private byte messageVersionNumber;
/**
* The time at which the mark was rounding. Milliseconds since unix epoch.
*/
private long time;
/**
* The ack number of the message.
*/
private int ackNum;
/**
* The raceID this message relates to.
*/
private int raceID;
/**
* The sourceID of the boat this message relates to.
*/
private int sourceID;
private int boatStatus;
private int roundingSide;
private int markType;
private int markID;
public static int BoatStatusUnknown = 0;
public static int BoatStatusRacing = 1;
public static int BoatStatusDSQ = 2;
public static int BoatStatusWithdrawn = 3;
public static int RoundingSideUnknown = 0;
public static int RoundingSidePort = 1;
public static int RoundingSideStarboard = 2;
public static int MarkTypeUnknown = 0;
public static int MarkTypeRoundingMark = 1;
public static int MarkTypeGate = 2;
public static int MarkIDEntryLimitLine = 100;
public static int MarkIDEntryLine = 101;
public static int MarkIDRaceStartStartline = 102;
public static int MarkIDRaceFinishline = 103;
public static int MarkIDSpeedTestStart = 104;
public static int MarkIDSpeedTestFinish = 105;
public static int MarkIDClearStart = 106;
public MarkRounding(int msgVerNum, long time, int ackNum, int raceID, int sourceID, int boatStatus, int roundingSide, int markType, int markID){
/**
* The status of the boat.
*/
private MarkRoundingBoatStatusEnum boatStatus;
/**
* The side around which the boat rounded.
*/
private MarkRoundingSideEnum roundingSide;
/**
* The type of mark that was rounded.
*/
private MarkRoundingTypeEnum markType;
/**
* The ID of the mark. This is not a source ID.
*/
private byte markID;
/**
* Creates a MarkRounding message with the given parameters.
* @param messageVersionNumber The version number of the message.
* @param time The time at which the message was created.
* @param ackNum The ack number of the message.
* @param raceID The raceID this message relates to.
* @param sourceID The sourceID of the boat this message relates to.
* @param boatStatus The status of the boat as it rounded the mark.
* @param roundingSide The side around which the boat rounded.
* @param markType The type of mark that was rounded.
* @param markID The ID number of the mark. Not a sourceID. See {@link network.Messages.Enums.MarkRoundingIDEnum}.
*/
public MarkRounding(
byte messageVersionNumber,
long time,
int ackNum,
int raceID,
int sourceID,
MarkRoundingBoatStatusEnum boatStatus,
MarkRoundingSideEnum roundingSide,
MarkRoundingTypeEnum markType,
byte markID ) {
super(MessageType.MARKROUNDING);
this.msgVerNum = msgVerNum;
this.messageVersionNumber = messageVersionNumber;
this.time = time;
this.ackNum = ackNum;
this.raceID = raceID;
@ -52,6 +99,40 @@ public class MarkRounding extends AC35Data {
this.markID = markID;
}
/**
* Returns the version number of this message.
* @return Version number of this message.
*/
public byte getMessageVersionNumber() {
return messageVersionNumber;
}
/**
* Returns the timestamp for this message.
* @return Timestamp for this message.
*/
public long getTime() {
return time;
}
/**
* Returns the ack number of this message.
* @return Ack number of this message.
*/
public int getAckNum() {
return ackNum;
}
/**
* Returns the raceID this message relates to.
* @return RaceID this message relates to.
*/
public int getRaceID() {
return raceID;
}
/**
* Returns the boat (source) ID for this message.
* @return Boat ID for this message.
@ -61,10 +142,34 @@ public class MarkRounding extends AC35Data {
}
/**
* Returns the timestamp for this message.
* @return Timestamp for this message.
* Returns the status of the boat as it rounded the mark.
* @return Status of boat as it rounded mark.
*/
public long getTime() {
return time;
public MarkRoundingBoatStatusEnum getBoatStatus() {
return boatStatus;
}
/**
* Returns the side to which the boat rounded the mark.
* @return Side to which boat rounded mark.
*/
public MarkRoundingSideEnum getRoundingSide() {
return roundingSide;
}
/**
* Returns the type of mark that was rounded.
* @return The type of mark that was rounded.
*/
public MarkRoundingTypeEnum getMarkType() {
return markType;
}
/**
* Returns ID number of the mark. This is not a source ID. See {@link network.Messages.Enums.MarkRoundingIDEnum}.
* @return ID number of the mark.
*/
public byte getMarkID() {
return markID;
}
}

@ -0,0 +1,69 @@
package network.MessageDecoders;
import network.MessageEncoders.RaceVisionByteEncoder;
import network.Messages.Enums.MarkRoundingBoatStatusEnum;
import network.Messages.Enums.MarkRoundingSideEnum;
import network.Messages.Enums.MarkRoundingTypeEnum;
import network.Messages.MarkRounding;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
/**
* Test for the MarkRounding encoder and decoder
*/
public class MarkRoundingDecoderTest {
/**
* Creates a MarkRounding message, encodes it, decodes it, and checks that the result matches the starting message.
* @throws Exception if test fails.
*/
@Test
public void markRoundingEncodeDecodeTest() throws Exception {
MarkRounding markRounding = new MarkRounding(
MarkRounding.currentMessageVersionNumber,
System.currentTimeMillis(),
567,
42,
125,
MarkRoundingBoatStatusEnum.RACING,
MarkRoundingSideEnum.PORT,
MarkRoundingTypeEnum.MARK,
(byte)45
);
byte[] encodedMessage = RaceVisionByteEncoder.encode(markRounding);
MarkRoundingDecoder markRoundingDecoder = new MarkRoundingDecoder();
markRoundingDecoder.decode(encodedMessage);
MarkRounding markRoundingDecoded = markRoundingDecoder.getMessage();
compareMarkRoundingMessages(markRounding, markRoundingDecoded);
}
/**
* Compares two MarkRounding messages to check that they are equal.
* @param original The original MarkRounding message.
* @param decoded The decoded MarkRounding message.
*/
public static void compareMarkRoundingMessages(MarkRounding original, MarkRounding decoded) {
assertEquals(original.getMessageVersionNumber(), decoded.getMessageVersionNumber());
assertEquals(original.getTime(), decoded.getTime());
assertEquals(original.getAckNum(), decoded.getAckNum());
assertEquals(original.getRaceID(), decoded.getRaceID());
assertEquals(original.getSourceID(), decoded.getSourceID());
assertEquals(original.getBoatStatus(), decoded.getBoatStatus());
assertEquals(original.getRoundingSide(), decoded.getRoundingSide());
assertEquals(original.getMarkType(), decoded.getMarkType());
assertEquals(original.getMarkID(), decoded.getMarkID());
}
}
Loading…
Cancel
Save