# Conflicts: # racevisionGame/src/main/java/mock/app/ConnectionAcceptor.java # racevisionGame/src/main/java/mock/app/Event.java # racevisionGame/src/main/java/mock/model/commandFactory/TackGybeCommand.java # racevisionGame/src/main/java/mock/model/commandFactory/VMGCommand.java # racevisionGame/src/main/java/visualiser/gameController/ControllerServer.javamain
commit
c3ea62dfee
@ -1,23 +1,24 @@
|
||||
package mock.model.commandFactory;
|
||||
|
||||
import java.util.Stack;
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Queue;
|
||||
|
||||
/**
|
||||
* Wraps multiple commands into a composite to execute queued commands during a frame.
|
||||
*/
|
||||
public class CompositeCommand implements Command {
|
||||
private Stack<Command> commands;
|
||||
private Queue<Command> commands;
|
||||
|
||||
public CompositeCommand() {
|
||||
this.commands = new Stack<>();
|
||||
this.commands = new ArrayDeque<>();
|
||||
}
|
||||
|
||||
public void addCommand(Command command) {
|
||||
commands.push(command);
|
||||
commands.add(command);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute() {
|
||||
while(!commands.isEmpty()) commands.pop().execute();
|
||||
while(!commands.isEmpty()) commands.remove().execute();
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,17 @@
|
||||
package network.Exceptions;
|
||||
|
||||
|
||||
/**
|
||||
* An exception thrown when we encounter a message type that isn't recognised.
|
||||
*/
|
||||
public class InvalidMessageTypeException extends Exception {
|
||||
|
||||
|
||||
public InvalidMessageTypeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public InvalidMessageTypeException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@ -1,56 +1,107 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.AC35Data;
|
||||
import network.Messages.AverageWind;
|
||||
import network.Utils.ByteConverter;
|
||||
|
||||
import static network.Utils.AC35UnitConverter.*;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Created by hba56 on 23/04/17.
|
||||
* Decodes {@link AverageWind} messages.
|
||||
*/
|
||||
public class AverageWindDecoder {
|
||||
byte messageVersionNumber;
|
||||
byte[] byteTime;
|
||||
byte[] byteRawPeriod;
|
||||
byte[] byteRawSpeed;
|
||||
byte[] bytePeriod2;
|
||||
byte[] byteSpeed2;
|
||||
byte[] bytePeriod3;
|
||||
byte[] byteSpeed3;
|
||||
byte[] bytePeriod4;
|
||||
byte[] byteSpeed4;
|
||||
|
||||
AverageWind averageWind;
|
||||
|
||||
public AverageWindDecoder(byte[] encodedAverageWind) {
|
||||
messageVersionNumber = encodedAverageWind[0];
|
||||
byteTime = Arrays.copyOfRange(encodedAverageWind, 1, 7);
|
||||
byteRawPeriod = Arrays.copyOfRange(encodedAverageWind, 7, 9);
|
||||
byteRawSpeed = Arrays.copyOfRange(encodedAverageWind, 9, 11);
|
||||
bytePeriod2 = Arrays.copyOfRange(encodedAverageWind, 11, 13);
|
||||
byteSpeed2 = Arrays.copyOfRange(encodedAverageWind, 13, 15);
|
||||
bytePeriod3 = Arrays.copyOfRange(encodedAverageWind, 15, 17);
|
||||
byteSpeed3 = Arrays.copyOfRange(encodedAverageWind, 17, 19);
|
||||
bytePeriod4 = Arrays.copyOfRange(encodedAverageWind, 19, 21);
|
||||
byteSpeed4 = Arrays.copyOfRange(encodedAverageWind, 21, 23);
|
||||
|
||||
int msgNum = ByteConverter.bytesToInt(messageVersionNumber);
|
||||
long lngTime = ByteConverter.bytesToLong(byteTime);
|
||||
int intRawPeriod = ByteConverter.bytesToInt(byteRawPeriod);
|
||||
int intRawSpeed = ByteConverter.bytesToInt(byteRawSpeed);
|
||||
int intPeriod2 = ByteConverter.bytesToInt(bytePeriod2);
|
||||
int intSpeed2 = ByteConverter.bytesToInt(byteSpeed2);
|
||||
int intPeriod3 = ByteConverter.bytesToInt(bytePeriod3);
|
||||
int intSpeed3 = ByteConverter.bytesToInt(byteSpeed3);
|
||||
int intPeriod4 = ByteConverter.bytesToInt(bytePeriod4);
|
||||
int intSpeed4 = ByteConverter.bytesToInt(byteSpeed4);
|
||||
|
||||
this.averageWind = new AverageWind(msgNum, lngTime, intRawPeriod, intRawSpeed, intPeriod2, intSpeed2, intPeriod3, intSpeed3, intPeriod4, intSpeed4);
|
||||
public class AverageWindDecoder implements MessageDecoder {
|
||||
|
||||
/**
|
||||
* The encoded message.
|
||||
*/
|
||||
private byte[] encodedMessage;
|
||||
|
||||
/**
|
||||
* The decoded message.
|
||||
*/
|
||||
private AverageWind message;
|
||||
|
||||
|
||||
|
||||
public AverageWindDecoder() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Override
|
||||
public AC35Data decode(byte[] encodedMessage) throws InvalidMessageException {
|
||||
this.encodedMessage = encodedMessage;
|
||||
|
||||
try {
|
||||
|
||||
byte messageVersionNumber = encodedMessage[0];
|
||||
|
||||
|
||||
byte[] byteTime = Arrays.copyOfRange(encodedMessage, 1, 7);
|
||||
long time = ByteConverter.bytesToLong(byteTime);
|
||||
|
||||
byte[] byteRawPeriod = Arrays.copyOfRange(encodedMessage, 7, 9);
|
||||
int intRawPeriod = ByteConverter.bytesToInt(byteRawPeriod);
|
||||
long rawPeriod = unpackAverageWindPeriod(intRawPeriod);
|
||||
|
||||
byte[] byteRawSpeed = Arrays.copyOfRange(encodedMessage, 9, 11);
|
||||
int intRawSpeed = ByteConverter.bytesToInt(byteRawSpeed);
|
||||
double rawSpeedKnots = unpackMMperSecToKnots(intRawSpeed);
|
||||
|
||||
byte[] bytePeriod2 = Arrays.copyOfRange(encodedMessage, 11, 13);
|
||||
int intPeriod2 = ByteConverter.bytesToInt(bytePeriod2);
|
||||
long period2 = unpackAverageWindPeriod(intPeriod2);
|
||||
|
||||
byte[] byteSpeed2 = Arrays.copyOfRange(encodedMessage, 13, 15);
|
||||
int intSpeed2 = ByteConverter.bytesToInt(byteSpeed2);
|
||||
double speed2Knots = unpackMMperSecToKnots(intSpeed2);
|
||||
|
||||
byte[] bytePeriod3 = Arrays.copyOfRange(encodedMessage, 15, 17);
|
||||
int intPeriod3 = ByteConverter.bytesToInt(bytePeriod3);
|
||||
long period3 = unpackAverageWindPeriod(intPeriod3);
|
||||
|
||||
byte[] byteSpeed3 = Arrays.copyOfRange(encodedMessage, 17, 19);
|
||||
int intSpeed3 = ByteConverter.bytesToInt(byteSpeed3);
|
||||
double speed3Knots = unpackMMperSecToKnots(intSpeed3);
|
||||
|
||||
byte[] bytePeriod4 = Arrays.copyOfRange(encodedMessage, 19, 21);
|
||||
int intPeriod4 = ByteConverter.bytesToInt(bytePeriod4);
|
||||
long period4 = unpackAverageWindPeriod(intPeriod4);
|
||||
|
||||
byte[] byteSpeed4 = Arrays.copyOfRange(encodedMessage, 21, 23);
|
||||
int intSpeed4 = ByteConverter.bytesToInt(byteSpeed4);
|
||||
double speed4Knots = unpackMMperSecToKnots(intSpeed4);
|
||||
|
||||
|
||||
message = new AverageWind(
|
||||
messageVersionNumber,
|
||||
time,
|
||||
rawPeriod,
|
||||
rawSpeedKnots,
|
||||
period2,
|
||||
speed2Knots,
|
||||
period3,
|
||||
speed3Knots,
|
||||
period4,
|
||||
speed4Knots);
|
||||
|
||||
return message;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not decode AverageWind message.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public AverageWind getAverageWind() {
|
||||
return averageWind;
|
||||
/**
|
||||
* Returns the decoded message.
|
||||
* @return The decoded message.
|
||||
*/
|
||||
public AverageWind getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,20 +1,56 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.AC35Data;
|
||||
import network.Messages.BoatAction;
|
||||
import network.Messages.Enums.BoatActionEnum;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public class BoatActionDecoder {
|
||||
byte byteBoatAction;
|
||||
BoatActionEnum boatAction;
|
||||
/**
|
||||
* Decodes {@link BoatAction} messages.
|
||||
*/
|
||||
public class BoatActionDecoder implements MessageDecoder {
|
||||
|
||||
public BoatActionDecoder(byte[] encodedBoatAction) {
|
||||
byteBoatAction = encodedBoatAction[0];
|
||||
/**
|
||||
* The encoded message.
|
||||
*/
|
||||
private byte[] encodedMessage;
|
||||
|
||||
boatAction = BoatActionEnum.fromByte(byteBoatAction);
|
||||
/**
|
||||
* The decoded message.
|
||||
*/
|
||||
private BoatAction message;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a decoder to decode a given message.
|
||||
*/
|
||||
public BoatActionDecoder() {
|
||||
}
|
||||
|
||||
public BoatActionEnum getBoatAction() {
|
||||
return boatAction;
|
||||
@Override
|
||||
public AC35Data decode(byte[] encodedMessage) throws InvalidMessageException {
|
||||
this.encodedMessage = encodedMessage;
|
||||
|
||||
try {
|
||||
BoatActionEnum boatActionEnum = BoatActionEnum.fromByte(encodedMessage[0]);
|
||||
|
||||
message = new BoatAction(boatActionEnum);
|
||||
|
||||
return message;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not decode BoatAction message.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the decoded message.
|
||||
* @return The decoded message.
|
||||
*/
|
||||
public BoatAction getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,98 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
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.
|
||||
*/
|
||||
public BoatStatusDecoder() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Decodes the contained message.
|
||||
* @param encodedMessage The message to decode.
|
||||
* @return The decoded message.
|
||||
* @throws InvalidMessageException Thrown if the encoded message is invalid in some way, or cannot be decoded.
|
||||
*/
|
||||
public BoatStatus decode(byte[] encodedMessage) throws InvalidMessageException {
|
||||
this.encodedMessage = encodedMessage;
|
||||
|
||||
try {
|
||||
|
||||
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 );
|
||||
|
||||
return message;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not decode BoatStatus message.", e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Returns the decoded message.
|
||||
* @return The decoded message.
|
||||
*/
|
||||
public BoatStatus getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
@ -1,64 +1,102 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.CourseWind;
|
||||
import shared.model.Bearing;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static network.Utils.ByteConverter.bytesToInt;
|
||||
import static network.Utils.ByteConverter.bytesToLong;
|
||||
import static network.Utils.AC35UnitConverter.*;
|
||||
import static network.Utils.ByteConverter.*;
|
||||
|
||||
|
||||
/**
|
||||
* Created by hba56 on 23/04/17.
|
||||
* Decodes {@link CourseWind} messages.
|
||||
*/
|
||||
public class CourseWindDecoder {
|
||||
byte messageVersionNumber;
|
||||
byte byteWindID;
|
||||
byte loopCount;
|
||||
ArrayList<CourseWind> loopMessages = new ArrayList();
|
||||
|
||||
public CourseWindDecoder(byte[] encodedCourseWind) {
|
||||
final int lengthInBytesOfMessages = 20;
|
||||
|
||||
messageVersionNumber = encodedCourseWind[0];
|
||||
byteWindID = encodedCourseWind[1];
|
||||
loopCount = encodedCourseWind[2];
|
||||
byte[] loopMessagesBytes = Arrays.copyOfRange(encodedCourseWind, 3, lengthInBytesOfMessages*loopCount+3);
|
||||
int messageLoopIndex = 0;
|
||||
|
||||
for (int i=0; i < loopCount; i++) {
|
||||
byte[] messageBytes = Arrays.copyOfRange(loopMessagesBytes, messageLoopIndex, messageLoopIndex+20);
|
||||
ArrayList test = new ArrayList();
|
||||
byte[] windId = Arrays.copyOfRange(messageBytes, 0, 1);
|
||||
byte[] time = Arrays.copyOfRange(messageBytes, 1, 7);
|
||||
byte[] raceID = Arrays.copyOfRange(messageBytes, 7, 11);
|
||||
byte[] windDirection = Arrays.copyOfRange(messageBytes, 11, 13);
|
||||
byte[] windSpeed = Arrays.copyOfRange(messageBytes, 13, 15);
|
||||
byte[] bestUpwindAngle = Arrays.copyOfRange(messageBytes, 15, 17);
|
||||
byte[] bestDownwindAngle = Arrays.copyOfRange(messageBytes, 17, 19);
|
||||
byte[] flags = Arrays.copyOfRange(messageBytes, 19, 20);
|
||||
|
||||
CourseWind message = new CourseWind(windId[0], bytesToLong(time),
|
||||
bytesToInt(raceID), bytesToInt(windDirection),
|
||||
bytesToInt(windSpeed), bytesToInt(bestUpwindAngle),
|
||||
bytesToInt(bestDownwindAngle), flags[0]);
|
||||
|
||||
loopMessages.add(message);
|
||||
messageLoopIndex += 20;
|
||||
}
|
||||
}
|
||||
|
||||
public ArrayList<CourseWind> getLoopMessages() {
|
||||
return loopMessages;
|
||||
/**
|
||||
* The encoded message.
|
||||
*/
|
||||
private byte[] encodedMessage;
|
||||
|
||||
/**
|
||||
* The decoded message.
|
||||
*/
|
||||
private CourseWind message;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a decoder to decode a given message.
|
||||
*/
|
||||
public CourseWindDecoder() {
|
||||
}
|
||||
|
||||
public byte getMessageVersionNumber() {
|
||||
return messageVersionNumber;
|
||||
|
||||
/**
|
||||
* Decodes the contained message.
|
||||
* @param encodedMessage The message to decode.
|
||||
* @return The decoded message.
|
||||
* @throws InvalidMessageException Thrown if the encoded message is invalid in some way, or cannot be decoded.
|
||||
*/
|
||||
public CourseWind decode(byte[] encodedMessage) throws InvalidMessageException {
|
||||
this.encodedMessage = encodedMessage;
|
||||
|
||||
try {
|
||||
|
||||
byte[] windId = Arrays.copyOfRange(encodedMessage, 0, 1);
|
||||
|
||||
byte[] timeBytes = Arrays.copyOfRange(encodedMessage, 1, 7);
|
||||
long time = bytesToLong(timeBytes);
|
||||
|
||||
byte[] raceIDBytes = Arrays.copyOfRange(encodedMessage, 7, 11);
|
||||
int raceIDInt = bytesToInt(raceIDBytes);
|
||||
|
||||
byte[] windDirectionBytes = Arrays.copyOfRange(encodedMessage, 11, 13);
|
||||
int windDirectionInt = bytesToInt(windDirectionBytes);
|
||||
Bearing windDirection = Bearing.fromDegrees(unpackHeading(windDirectionInt));
|
||||
|
||||
byte[] windSpeedBytes = Arrays.copyOfRange(encodedMessage, 13, 15);
|
||||
int windSpeedInt = bytesToInt(windSpeedBytes);
|
||||
double windSpeedKnots = unpackMMperSecToKnots(windSpeedInt);
|
||||
|
||||
byte[] bestUpwindAngleBytes = Arrays.copyOfRange(encodedMessage, 15, 17);
|
||||
int bestUpwindAngleInt = bytesToInt(bestUpwindAngleBytes);
|
||||
Bearing bestUpwindAngle = Bearing.fromDegrees(unpackHeading(bestUpwindAngleInt));
|
||||
|
||||
byte[] bestDownwindAngleBytes = Arrays.copyOfRange(encodedMessage, 17, 19);
|
||||
int bestDownwindAngleInt = bytesToInt(bestDownwindAngleBytes);
|
||||
Bearing bestDownwindAngle = Bearing.fromDegrees(unpackHeading(bestDownwindAngleInt));
|
||||
|
||||
byte[] flags = Arrays.copyOfRange(encodedMessage, 19, 20);
|
||||
|
||||
|
||||
message = new CourseWind(
|
||||
windId[0],
|
||||
time,
|
||||
raceIDInt,
|
||||
windDirection,
|
||||
windSpeedKnots,
|
||||
bestUpwindAngle,
|
||||
bestDownwindAngle,
|
||||
flags[0]);
|
||||
|
||||
return message;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not decode CourseWind message.", e);
|
||||
}
|
||||
}
|
||||
|
||||
public byte getByteWindID() {
|
||||
return byteWindID;
|
||||
|
||||
/**
|
||||
* Returns the decoded message.
|
||||
* @return The decoded message.
|
||||
*/
|
||||
public CourseWind getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,93 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.AC35Data;
|
||||
import network.Messages.CourseWind;
|
||||
import network.Messages.CourseWinds;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static network.Utils.ByteConverter.bytesToInt;
|
||||
import static network.Utils.ByteConverter.bytesToLong;
|
||||
|
||||
|
||||
/**
|
||||
* Decodes {@link CourseWinds} messages.
|
||||
*/
|
||||
public class CourseWindsDecoder implements MessageDecoder {
|
||||
|
||||
/**
|
||||
* The encoded message.
|
||||
*/
|
||||
private byte[] encodedMessage;
|
||||
|
||||
/**
|
||||
* The decoded message.
|
||||
*/
|
||||
private CourseWinds message;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a decoder to decode a given message.
|
||||
*/
|
||||
public CourseWindsDecoder() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AC35Data decode(byte[] encodedMessage) throws InvalidMessageException {
|
||||
this.encodedMessage = encodedMessage;
|
||||
|
||||
try {
|
||||
|
||||
//The header is three bytes.
|
||||
byte messageVersionNumber = encodedMessage[0];
|
||||
byte byteWindID = encodedMessage[1];
|
||||
byte loopCount = encodedMessage[2];
|
||||
|
||||
|
||||
//A CourseWind object is 20 bytes.
|
||||
final int courseWindByteLength = 20;
|
||||
|
||||
List<CourseWind> loopMessages = new ArrayList();
|
||||
|
||||
//The header is 3 bytes, so we need the remaining bytes.
|
||||
byte[] loopMessagesBytes = Arrays.copyOfRange(encodedMessage, 3, courseWindByteLength * loopCount + 3);
|
||||
|
||||
for (int messageLoopIndex = 0; messageLoopIndex < (loopCount * courseWindByteLength); messageLoopIndex += courseWindByteLength) {
|
||||
|
||||
byte[] messageBytes = Arrays.copyOfRange(loopMessagesBytes, messageLoopIndex, messageLoopIndex + courseWindByteLength);
|
||||
|
||||
CourseWindDecoder courseWindDecoder = new CourseWindDecoder();
|
||||
CourseWind courseWind = courseWindDecoder.decode(messageBytes);
|
||||
|
||||
loopMessages.add(courseWind);
|
||||
}
|
||||
|
||||
|
||||
message = new CourseWinds(
|
||||
messageVersionNumber,
|
||||
byteWindID,
|
||||
loopMessages);
|
||||
|
||||
return message;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not decode CourseWinds message.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the decoded message.
|
||||
* @return The decoded message.
|
||||
*/
|
||||
public CourseWinds getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageTypeException;
|
||||
import network.Messages.Enums.MessageType;
|
||||
|
||||
/**
|
||||
* Factory to create the appropriate decoder for a given message.
|
||||
*/
|
||||
public class DecoderFactory {
|
||||
|
||||
|
||||
/**
|
||||
* Private constructor. Currently doesn't need to be constructed.
|
||||
*/
|
||||
private DecoderFactory(){
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the correct type of decoder for a given message type.
|
||||
* @param type Type of message you want a decoder for.
|
||||
* @return The decoder.
|
||||
* @throws InvalidMessageTypeException If you pass in a {@link MessageType} that isn't recognised.
|
||||
*/
|
||||
public static MessageDecoder create(MessageType type) throws InvalidMessageTypeException {
|
||||
|
||||
|
||||
switch (type) {
|
||||
|
||||
case HEARTBEAT: return new HeartBeatDecoder();
|
||||
|
||||
case RACESTATUS: return new RaceStatusDecoder();
|
||||
|
||||
//case DISPLAYTEXTMESSAGE: return new DisplayTextMessageDecoder();//TODO
|
||||
|
||||
case XMLMESSAGE: return new XMLMessageDecoder();
|
||||
|
||||
case RACESTARTSTATUS: return new RaceStartStatusDecoder();
|
||||
|
||||
//case YACHTEVENTCODE: return new YachtEventCodeDecoder();//TODO
|
||||
|
||||
//case YACHTACTIONCODE: return new YachtActionCodeDecoder();//TODO
|
||||
|
||||
//case CHATTERTEXT: return new ChatterTextDecoder();//TODO
|
||||
|
||||
case BOATLOCATION: return new BoatLocationDecoder();
|
||||
|
||||
case MARKROUNDING: return new MarkRoundingDecoder();
|
||||
|
||||
case COURSEWIND: return new CourseWindsDecoder();
|
||||
|
||||
case AVGWIND: return new AverageWindDecoder();
|
||||
|
||||
case REQUEST_TO_JOIN: return new RequestToJoinDecoder();
|
||||
|
||||
case JOIN_ACCEPTANCE: return new JoinAcceptanceDecoder();
|
||||
|
||||
case BOATACTION: return new BoatActionDecoder();
|
||||
|
||||
|
||||
default: throw new InvalidMessageTypeException("Unrecognised message type: " + type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.AC35Data;
|
||||
import network.Messages.Enums.BoatActionEnum;
|
||||
import network.Messages.HeartBeat;
|
||||
|
||||
import static network.Utils.ByteConverter.bytesToLong;
|
||||
|
||||
/**
|
||||
* Decodes {@link network.Messages.HeartBeat} messages.
|
||||
*/
|
||||
public class HeartBeatDecoder implements MessageDecoder {
|
||||
|
||||
/**
|
||||
* The encoded message.
|
||||
*/
|
||||
private byte[] encodedMessage;
|
||||
|
||||
/**
|
||||
* The decoded message.
|
||||
*/
|
||||
private HeartBeat message;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a decoder to decode a given message.
|
||||
*/
|
||||
public HeartBeatDecoder() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public AC35Data decode(byte[] encodedMessage) throws InvalidMessageException {
|
||||
this.encodedMessage = encodedMessage;
|
||||
|
||||
try {
|
||||
|
||||
message = new HeartBeat(bytesToLong(encodedMessage));
|
||||
|
||||
return message;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not decode HeartBeat message.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the decoded message.
|
||||
* @return The decoded message.
|
||||
*/
|
||||
public HeartBeat getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.AC35Data;
|
||||
import network.Messages.Enums.JoinAcceptanceEnum;
|
||||
import network.Messages.JoinAcceptance;
|
||||
import network.Utils.ByteConverter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Decoder for {@link JoinAcceptance} messages.
|
||||
*/
|
||||
public class JoinAcceptanceDecoder implements MessageDecoder {
|
||||
|
||||
/**
|
||||
* The encoded message.
|
||||
*/
|
||||
private byte[] encodedMessage;
|
||||
|
||||
/**
|
||||
* The decoded message.
|
||||
*/
|
||||
private JoinAcceptance message;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a decoder to decode a given message.
|
||||
*/
|
||||
public JoinAcceptanceDecoder() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AC35Data decode(byte[] encodedMessage) throws InvalidMessageException {
|
||||
this.encodedMessage = encodedMessage;
|
||||
|
||||
try {
|
||||
|
||||
//SourceID is first four bytes.
|
||||
byte[] sourceIdBytes = Arrays.copyOfRange(encodedMessage, 0, 4);
|
||||
|
||||
//Next byte is acceptance type.
|
||||
byte[] acceptanceBytes = Arrays.copyOfRange(encodedMessage, 4, 5);
|
||||
|
||||
|
||||
//SourceID is an int.
|
||||
int sourceID = ByteConverter.bytesToInt(sourceIdBytes);
|
||||
|
||||
//Acceptance enum is a byte.
|
||||
JoinAcceptanceEnum acceptanceType = JoinAcceptanceEnum.fromByte(acceptanceBytes[0]);
|
||||
|
||||
|
||||
message = new JoinAcceptance(acceptanceType, sourceID);
|
||||
|
||||
return message;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not decode JoinAcceptance message.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the decoded message.
|
||||
* @return The decoded message.
|
||||
*/
|
||||
public JoinAcceptance getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -1,52 +1,97 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
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) throws InvalidMessageException {
|
||||
this.encodedMessage = encodedMessage;
|
||||
|
||||
try {
|
||||
|
||||
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;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not decode AverageWind message.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the decoded message.
|
||||
*
|
||||
* @return The decoded message.
|
||||
*/
|
||||
public MarkRounding getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.AC35Data;
|
||||
|
||||
|
||||
/**
|
||||
* This is the interface that all message decoders must implement.
|
||||
* It allows for {@link #decode(byte[])}ing messages.
|
||||
*/
|
||||
public interface MessageDecoder {
|
||||
|
||||
|
||||
/**
|
||||
* Decodes a given message.
|
||||
* @param encodedMessage The message to decode.
|
||||
* @return The decoded message.
|
||||
* @throws InvalidMessageException Thrown if the encoded message is invalid in some way, or cannot be decoded.
|
||||
*/
|
||||
public AC35Data decode(byte[] encodedMessage) throws InvalidMessageException;
|
||||
|
||||
}
|
||||
@ -1,66 +1,86 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
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) throws InvalidMessageException {
|
||||
this.encodedMessage = encodedMessage;
|
||||
|
||||
try {
|
||||
|
||||
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;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not decode RaceStartStatus message.", e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public char getNotification() {
|
||||
return notification;
|
||||
|
||||
/**
|
||||
* Returns the decoded message.
|
||||
* @return The decoded message.
|
||||
*/
|
||||
public RaceStartStatus getMessage() {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,67 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.AC35Data;
|
||||
import network.Messages.Enums.RequestToJoinEnum;
|
||||
import network.Messages.RequestToJoin;
|
||||
import network.Utils.ByteConverter;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
/**
|
||||
* Decoder for {@link network.Messages.RequestToJoin} messages.
|
||||
*/
|
||||
public class RequestToJoinDecoder implements MessageDecoder{
|
||||
|
||||
/**
|
||||
* The encoded message.
|
||||
*/
|
||||
private byte[] encodedRequest;
|
||||
|
||||
/**
|
||||
* The decoded message.
|
||||
*/
|
||||
private RequestToJoin message;
|
||||
|
||||
/**
|
||||
* Constructs a decoder to decode a given message.
|
||||
*/
|
||||
public RequestToJoinDecoder() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public AC35Data decode(byte[] encodedRequest) throws InvalidMessageException {
|
||||
this.encodedRequest = encodedRequest;
|
||||
|
||||
try {
|
||||
|
||||
//Request type is first four bytes.
|
||||
byte[] requestTypeBytes = Arrays.copyOfRange(encodedRequest, 0, 4);
|
||||
|
||||
//Request type is an integral type.
|
||||
int requestTypeInt = ByteConverter.bytesToInt(requestTypeBytes);
|
||||
RequestToJoinEnum requestType = RequestToJoinEnum.fromInt(requestTypeInt);
|
||||
|
||||
|
||||
message = new RequestToJoin(requestType);
|
||||
|
||||
return message;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not decode RequestToJoin message.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the decoded message.
|
||||
* @return The decoded message.
|
||||
*/
|
||||
public RequestToJoin getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
package network.MessageEncoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.AC35Data;
|
||||
import network.Messages.AverageWind;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static network.Utils.AC35UnitConverter.*;
|
||||
import static network.Utils.ByteConverter.intToBytes;
|
||||
import static network.Utils.ByteConverter.longToBytes;
|
||||
|
||||
/**
|
||||
* This encoder can encode a {@link AverageWind} message.
|
||||
*/
|
||||
public class AverageWindEncoder implements MessageEncoder {
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public AverageWindEncoder() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte[] encode(AC35Data message) throws InvalidMessageException {
|
||||
|
||||
try {
|
||||
|
||||
//Downcast.
|
||||
AverageWind averageWind = (AverageWind) message;
|
||||
|
||||
|
||||
byte messageVersionNumber = averageWind.getMessageVersionNumber();
|
||||
|
||||
long time = averageWind.getTime();
|
||||
byte[] byteTime = longToBytes(time, 6);
|
||||
|
||||
long rawPeriod = averageWind.getRawPeriod();
|
||||
int rawPeriodInt = packAverageWindPeriod(rawPeriod);
|
||||
byte[] byteRawPeriod = intToBytes(rawPeriodInt, 2);
|
||||
|
||||
double rawSampleSpeed = averageWind.getRawSpeedKnots();
|
||||
int rawSampleSpeedInt = packKnotsToMMperSec(rawSampleSpeed);
|
||||
byte[] byteRawSpeed = intToBytes(rawSampleSpeedInt, 2);
|
||||
|
||||
long period2 = averageWind.getSampleTwoPeriod();
|
||||
int period2Int = packAverageWindPeriod(period2);
|
||||
byte[] bytePeriod2 = intToBytes(period2Int, 2);
|
||||
|
||||
double speed2 = averageWind.getSampleTwoSpeedKnots();
|
||||
int speed2Int = packKnotsToMMperSec(speed2);
|
||||
byte[] byteSpeed2 = intToBytes(speed2Int, 2);
|
||||
|
||||
long period3 = averageWind.getSampleThreePeriod();
|
||||
int period3Int = packAverageWindPeriod(period3);
|
||||
byte[] bytePeriod3 = intToBytes(period3Int, 2);
|
||||
|
||||
double speed3 = averageWind.getSampleThreeSpeedKnots();
|
||||
int speed3Int = packKnotsToMMperSec(speed3);
|
||||
byte[] byteSpeed3 = intToBytes(speed3Int, 2);
|
||||
|
||||
long period4 = averageWind.getSampleFourPeriod();
|
||||
int period4Int = packAverageWindPeriod(period4);
|
||||
byte[] bytePeriod4 = intToBytes(period4Int, 2);
|
||||
|
||||
double speed4 = averageWind.getSampleFourSpeedKnots();
|
||||
int speed4Int = packKnotsToMMperSec(speed4);
|
||||
byte[] byteSpeed4 = intToBytes(speed4Int, 2);
|
||||
|
||||
|
||||
ByteBuffer result = ByteBuffer.allocate(23);
|
||||
result.put(messageVersionNumber);
|
||||
result.put(byteTime);
|
||||
result.put(byteRawPeriod);
|
||||
result.put(byteRawSpeed);
|
||||
result.put(bytePeriod2);
|
||||
result.put(byteSpeed2);
|
||||
result.put(bytePeriod3);
|
||||
result.put(byteSpeed3);
|
||||
result.put(bytePeriod4);
|
||||
result.put(byteSpeed4);
|
||||
return result.array();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not encode AverageWind message.", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,47 @@
|
||||
package network.MessageEncoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.AC35Data;
|
||||
import network.Messages.BoatAction;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static network.Utils.ByteConverter.intToBytes;
|
||||
|
||||
/**
|
||||
* This encoder can encode a {@link BoatAction} message.
|
||||
*/
|
||||
public class BoatActionEncoder implements MessageEncoder {
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public BoatActionEncoder() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte[] encode(AC35Data message) throws InvalidMessageException {
|
||||
|
||||
try {
|
||||
|
||||
//Downcast.
|
||||
BoatAction boatAction = (BoatAction) message;
|
||||
|
||||
//Message is 1 byte.
|
||||
ByteBuffer boatActionMessage = ByteBuffer.allocate(1);
|
||||
|
||||
boatActionMessage.put(intToBytes(boatAction.getBoatAction().getValue(), 1));
|
||||
|
||||
byte[] result = boatActionMessage.array();
|
||||
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not encode BoatAction message.", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,91 @@
|
||||
package network.MessageEncoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.AC35Data;
|
||||
import network.Messages.BoatLocation;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static network.Utils.AC35UnitConverter.*;
|
||||
import static network.Utils.ByteConverter.intToBytes;
|
||||
import static network.Utils.ByteConverter.longToBytes;
|
||||
|
||||
/**
|
||||
* This encoder can encode a {@link BoatLocation} message.
|
||||
*/
|
||||
public class BoatLocationEncoder implements MessageEncoder {
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public BoatLocationEncoder() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte[] encode(AC35Data message) throws InvalidMessageException {
|
||||
|
||||
try {
|
||||
|
||||
//Downcast.
|
||||
BoatLocation boatLocation = (BoatLocation) message;
|
||||
|
||||
|
||||
int messageVersionNumber = 0b1;
|
||||
byte[] messageVersionBytes = intToBytes(messageVersionNumber, 1);
|
||||
byte[] time = longToBytes(boatLocation.getTime(), 6);
|
||||
byte[] sourceID = intToBytes(boatLocation.getSourceID(), 4);
|
||||
byte[] seqNum = longToBytes(boatLocation.getSequenceNumber(), 4);
|
||||
byte[] deviceType = intToBytes(boatLocation.getDeviceType().getValue(), 1);
|
||||
byte[] latitude = intToBytes(packGPS(boatLocation.getLatitude()), 4);
|
||||
byte[] longitude = intToBytes(packGPS(boatLocation.getLongitude()), 4);
|
||||
byte[] altitude = intToBytes(boatLocation.getAltitude(), 4);
|
||||
byte[] heading = intToBytes(packHeading(boatLocation.getHeading().degrees()), 2);
|
||||
byte[] pitch = intToBytes(boatLocation.getPitch(), 2);
|
||||
byte[] roll = intToBytes(boatLocation.getRoll(), 2);
|
||||
byte[] boatSpeed = intToBytes(packKnotsToMMperSec(boatLocation.getBoatSpeedKnots()), 2);
|
||||
byte[] cog = intToBytes(packHeading(boatLocation.getBoatCOG().degrees()), 2);
|
||||
byte[] sog = intToBytes(packKnotsToMMperSec(boatLocation.getBoatSOGKnots()), 2);
|
||||
byte[] apparentWindSpeed = intToBytes(packKnotsToMMperSec(boatLocation.getApparentWindSpeedKnots()), 2);
|
||||
byte[] apparentWindAngle = intToBytes(packTrueWindAngle(boatLocation.getApparentWindAngle().degrees()), 2);
|
||||
byte[] trueWindSpeed = intToBytes(packKnotsToMMperSec(boatLocation.getTrueWindSpeedKnots()), 2);
|
||||
byte[] trueWindDirection = intToBytes(packHeading(boatLocation.getTrueWindDirection().degrees()), 2);
|
||||
byte[] trueWindAngle = intToBytes(packTrueWindAngle(boatLocation.getTrueWindAngle().degrees()), 2);
|
||||
byte[] currentDrift = intToBytes(packKnotsToMMperSec(boatLocation.getCurrentDriftKnots()), 2);
|
||||
byte[] currentSet = intToBytes(packHeading(boatLocation.getCurrentSet().degrees()), 2);
|
||||
byte[] rudderAngle = intToBytes(packTrueWindAngle(boatLocation.getRudderAngle().degrees()), 2);
|
||||
|
||||
ByteBuffer result = ByteBuffer.allocate(56);
|
||||
result.put(messageVersionBytes);
|
||||
result.put(time);
|
||||
result.put(sourceID);
|
||||
result.put(seqNum);
|
||||
result.put(deviceType);
|
||||
result.put(latitude);
|
||||
result.put(longitude);
|
||||
result.put(altitude);
|
||||
result.put(heading);
|
||||
result.put(pitch);
|
||||
result.put(roll);
|
||||
result.put(boatSpeed);
|
||||
result.put(cog);
|
||||
result.put(sog);
|
||||
result.put(apparentWindSpeed);
|
||||
result.put(apparentWindAngle);
|
||||
result.put(trueWindSpeed);
|
||||
result.put(trueWindDirection);
|
||||
result.put(trueWindAngle);
|
||||
result.put(currentDrift);
|
||||
result.put(currentSet);
|
||||
result.put(rudderAngle);
|
||||
|
||||
return result.array();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not encode BoatLocation message.", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,65 @@
|
||||
package network.MessageEncoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
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.
|
||||
* @throws InvalidMessageException Thrown if the message is invalid in some way, or cannot be encoded.
|
||||
*/
|
||||
public byte[] encode(BoatStatus message) throws InvalidMessageException {
|
||||
|
||||
try {
|
||||
|
||||
|
||||
//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();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not encode BoatStatus message.", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,81 @@
|
||||
package network.MessageEncoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.CourseWind;
|
||||
import shared.model.Bearing;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static network.Utils.AC35UnitConverter.*;
|
||||
import static network.Utils.ByteConverter.*;
|
||||
import static network.Utils.ByteConverter.bytesToInt;
|
||||
|
||||
/**
|
||||
* This encoder can encode a {@link CourseWind} message.
|
||||
*/
|
||||
public class CourseWindEncoder {
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public CourseWindEncoder() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Encodes a given CourseWind message.
|
||||
* @param message The message to encode.
|
||||
* @return The encoded message.
|
||||
* @throws InvalidMessageException Thrown if the message is invalid in some way, or cannot be encoded.
|
||||
*/
|
||||
public byte[] encode(CourseWind message) throws InvalidMessageException {
|
||||
|
||||
try {
|
||||
|
||||
CourseWind courseWind = message;
|
||||
|
||||
|
||||
//CourseWind is 20 bytes.
|
||||
ByteBuffer courseWindBuffer = ByteBuffer.allocate(20);
|
||||
|
||||
|
||||
byte[] windId = intToBytes(courseWind.getID(), 1);
|
||||
|
||||
byte[] timeBytes = longToBytes(courseWind.getTime(), 6);
|
||||
|
||||
byte[] raceIDBytes = intToBytes(courseWind.getRaceID(), 4);
|
||||
|
||||
int windDirectionInt = packHeading(courseWind.getWindDirection().degrees());
|
||||
byte[] windDirectionBytes = intToBytes(windDirectionInt, 2);
|
||||
|
||||
int windSpeedInt = packKnotsToMMperSec(courseWind.getWindSpeedKnots());
|
||||
byte[] windSpeedBytes = intToBytes(windSpeedInt, 2);
|
||||
|
||||
int bestUpwindAngleInt = packHeading(courseWind.getBestUpwindAngle().degrees());
|
||||
byte[] bestUpwindAngleBytes = intToBytes(bestUpwindAngleInt, 2);
|
||||
|
||||
int bestDownwindAngleInt = packHeading(courseWind.getBestDownwindAngle().degrees());
|
||||
byte[] bestDownwindAngleBytes = intToBytes(bestDownwindAngleInt, 2);
|
||||
|
||||
byte[] flags = intToBytes(courseWind.getFlags(), 1);
|
||||
|
||||
courseWindBuffer.put(windId);
|
||||
courseWindBuffer.put(timeBytes);
|
||||
courseWindBuffer.put(raceIDBytes);
|
||||
courseWindBuffer.put(windDirectionBytes);
|
||||
courseWindBuffer.put(windSpeedBytes);
|
||||
courseWindBuffer.put(bestUpwindAngleBytes);
|
||||
courseWindBuffer.put(bestDownwindAngleBytes);
|
||||
courseWindBuffer.put(flags);
|
||||
|
||||
return courseWindBuffer.array();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not encode CourseWind message.", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package network.MessageEncoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.AC35Data;
|
||||
import network.Messages.BoatAction;
|
||||
import network.Messages.CourseWind;
|
||||
import network.Messages.CourseWinds;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static network.Utils.ByteConverter.intToBytes;
|
||||
|
||||
/**
|
||||
* This encoder can encode a {@link CourseWinds} message.
|
||||
*/
|
||||
public class CourseWindsEncoder implements MessageEncoder {
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public CourseWindsEncoder() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte[] encode(AC35Data message) throws InvalidMessageException {
|
||||
|
||||
try {
|
||||
|
||||
//Downcast.
|
||||
CourseWinds courseWinds = (CourseWinds) message;
|
||||
|
||||
|
||||
byte messageVersionNumber = CourseWinds.currentMessageVersionNumber;
|
||||
|
||||
byte byteWindID = courseWinds.getSelectedWindID();
|
||||
|
||||
byte[] loopcount = intToBytes(courseWinds.getCourseWinds().size(), 1);
|
||||
|
||||
ByteBuffer result = ByteBuffer.allocate(3 + 20 * courseWinds.getCourseWinds().size());
|
||||
|
||||
result.put(messageVersionNumber);
|
||||
result.put(byteWindID);
|
||||
result.put(loopcount);
|
||||
|
||||
//Encode each CourseWind.
|
||||
for (CourseWind wind : courseWinds.getCourseWinds()) {
|
||||
|
||||
CourseWindEncoder courseWindEncoder = new CourseWindEncoder();
|
||||
byte[] encodedCourseWind = courseWindEncoder.encode(wind);
|
||||
|
||||
result.put(encodedCourseWind);
|
||||
}
|
||||
return result.array();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not encode CourseWinds message.", e);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,70 @@
|
||||
package network.MessageEncoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageTypeException;
|
||||
import network.Messages.Enums.MessageType;
|
||||
|
||||
/**
|
||||
* Factory to create the appropriate encoder for a given message.
|
||||
*/
|
||||
public class EncoderFactory {
|
||||
|
||||
|
||||
/**
|
||||
* Private constructor. Currently doesn't need to be constructed.
|
||||
*/
|
||||
private EncoderFactory(){
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates the correct type of encoder for a given message type.
|
||||
* @param type Type of message you want an encoder for.
|
||||
* @return The encoder.
|
||||
* @throws InvalidMessageTypeException If you pass in a {@link MessageType} that isn't recognised.
|
||||
*/
|
||||
public static MessageEncoder create(MessageType type) throws InvalidMessageTypeException {
|
||||
|
||||
|
||||
switch (type) {
|
||||
|
||||
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();
|
||||
|
||||
//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();
|
||||
|
||||
case COURSEWIND: return new CourseWindsEncoder();
|
||||
|
||||
case AVGWIND: return new AverageWindEncoder();
|
||||
|
||||
case REQUEST_TO_JOIN: return new RequestToJoinEncoder();
|
||||
|
||||
case JOIN_ACCEPTANCE: return new JoinAcceptanceEncoder();
|
||||
|
||||
case BOATACTION: return new BoatActionEncoder();
|
||||
|
||||
|
||||
default: throw new InvalidMessageTypeException("Unrecognised message type: " + type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package network.MessageEncoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.AC35Data;
|
||||
import network.Messages.HeartBeat;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static network.Utils.ByteConverter.longToBytes;
|
||||
|
||||
/**
|
||||
* This encoder can encode a {@link HeartBeat} message.
|
||||
*/
|
||||
public class HeartBeatEncoder implements MessageEncoder {
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public HeartBeatEncoder() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte[] encode(AC35Data message) throws InvalidMessageException {
|
||||
|
||||
try {
|
||||
|
||||
//Downcast.
|
||||
HeartBeat heartbeat = (HeartBeat) message;
|
||||
|
||||
//Message is 4 bytes.
|
||||
ByteBuffer heartBeat = ByteBuffer.allocate(4);
|
||||
heartBeat.put(longToBytes(heartbeat.getSequenceNumber(), 4));
|
||||
|
||||
byte[] result = heartBeat.array();
|
||||
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not encode HeartBeat message.", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package network.MessageEncoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.AC35Data;
|
||||
import network.Messages.JoinAcceptance;
|
||||
import network.Utils.ByteConverter;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
import static network.Utils.ByteConverter.intToBytes;
|
||||
|
||||
/**
|
||||
* This encoder can encode a {@link JoinAcceptance} message.
|
||||
*/
|
||||
public class JoinAcceptanceEncoder implements MessageEncoder {
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public JoinAcceptanceEncoder() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte[] encode(AC35Data message) throws InvalidMessageException {
|
||||
|
||||
try {
|
||||
|
||||
//Downcast.
|
||||
JoinAcceptance joinAcceptance = (JoinAcceptance) message;
|
||||
|
||||
//Message is 5 bytes.
|
||||
ByteBuffer joinAcceptanceBuffer = ByteBuffer.allocate(5);
|
||||
|
||||
//Source ID is first four bytes.
|
||||
joinAcceptanceBuffer.put(intToBytes(joinAcceptance.getSourceID(), 4));
|
||||
//Acceptance type is next byte.
|
||||
joinAcceptanceBuffer.put(intToBytes(joinAcceptance.getAcceptanceType().getValue(), 1));
|
||||
|
||||
byte[] result = joinAcceptanceBuffer.array();
|
||||
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not encode JoinAcceptance message.", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,64 @@
|
||||
package network.MessageEncoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
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) throws InvalidMessageException {
|
||||
|
||||
try {
|
||||
|
||||
//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();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not encode MarkRounding message.", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package network.MessageEncoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.AC35Data;
|
||||
|
||||
|
||||
/**
|
||||
* This is the interface that all message encoders must implement.
|
||||
* It allows for {@link #encode(AC35Data)}ing messages.
|
||||
*/
|
||||
public interface MessageEncoder {
|
||||
|
||||
|
||||
/**
|
||||
* Encodes a given message.
|
||||
* @param message The message to encode.
|
||||
* @return Message in byte encoded form.
|
||||
* @throws InvalidMessageException Thrown if the message is invalid in some way, or cannot be encoded.
|
||||
*/
|
||||
public byte[] encode(AC35Data message) throws InvalidMessageException;
|
||||
|
||||
}
|
||||
@ -0,0 +1,58 @@
|
||||
package network.MessageEncoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
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) throws InvalidMessageException {
|
||||
|
||||
try {
|
||||
|
||||
//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();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not encode RaceStartStatus message.", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,102 @@
|
||||
package network.MessageEncoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.AC35Data;
|
||||
import network.Messages.BoatStatus;
|
||||
import network.Messages.RaceStatus;
|
||||
import network.Utils.AC35UnitConverter;
|
||||
import shared.model.Bearing;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.List;
|
||||
|
||||
import static network.Utils.ByteConverter.bytesToInt;
|
||||
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) throws InvalidMessageException {
|
||||
|
||||
try {
|
||||
|
||||
//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.
|
||||
int windDirectionInt = AC35UnitConverter.packHeading(raceStatus.getWindDirection().degrees());
|
||||
byte[] raceWind = intToBytes(windDirectionInt, 2);
|
||||
|
||||
//mm/sec
|
||||
int windSpeedInt = AC35UnitConverter.packKnotsToMMperSec(raceStatus.getWindSpeed());
|
||||
byte[] windSpeed = intToBytes(windSpeedInt, 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();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not encode RaceStatus message.", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package network.MessageEncoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.AC35Data;
|
||||
import network.Messages.RequestToJoin;
|
||||
import network.Utils.ByteConverter;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
|
||||
/**
|
||||
* This encoder can encode a {@link network.Messages.RequestToJoin} message.
|
||||
*/
|
||||
public class RequestToJoinEncoder implements MessageEncoder {
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public RequestToJoinEncoder() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte[] encode(AC35Data message) throws InvalidMessageException {
|
||||
|
||||
try {
|
||||
|
||||
//Downcast.
|
||||
RequestToJoin requestToJoin = (RequestToJoin) message;
|
||||
|
||||
|
||||
ByteBuffer requestToJoinBuffer = ByteBuffer.allocate(4);
|
||||
|
||||
requestToJoinBuffer.put(ByteConverter.intToBytes(requestToJoin.getRequestType().getValue(), 4));
|
||||
|
||||
byte[] result = requestToJoinBuffer.array();
|
||||
|
||||
return result;
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not encode RequestToJoin message.", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
package network.MessageEncoders;
|
||||
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.Messages.AC35Data;
|
||||
import network.Messages.XMLMessage;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static network.Utils.ByteConverter.intToBytes;
|
||||
import static network.Utils.ByteConverter.longToBytes;
|
||||
|
||||
/**
|
||||
* This encoder can encode a {@link XMLMessage} message.
|
||||
*/
|
||||
public class XMLMessageEncoder implements MessageEncoder {
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public XMLMessageEncoder() {
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public byte[] encode(AC35Data message) throws InvalidMessageException {
|
||||
|
||||
try {
|
||||
|
||||
//Downcast.
|
||||
XMLMessage xmlMessage = (XMLMessage) message;
|
||||
|
||||
|
||||
byte[] messageBytes = xmlMessage.getXmlMessage().getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
//Message is 14 + xmlMessage.length bytes.
|
||||
ByteBuffer tempOutputByteBuffer = ByteBuffer.allocate(14 + messageBytes.length);
|
||||
|
||||
//ackNumber converted to bytes
|
||||
byte[] ackNumberBytes = intToBytes(xmlMessage.getAckNumber(), 2);
|
||||
|
||||
//Timestamp converted to bytes.
|
||||
byte[] timestampBytes = longToBytes(xmlMessage.getTimeStamp(), 6);
|
||||
|
||||
//sequenceNumber converted to bytes
|
||||
byte[] sequenceNumberBytes = intToBytes(xmlMessage.getSequenceNumber(), 2);
|
||||
|
||||
//xmlMsgLength converted to bytes
|
||||
byte[] xmlMsgLengthBytes = intToBytes(xmlMessage.getXmlMsgLength(), 2);
|
||||
|
||||
|
||||
tempOutputByteBuffer.put(xmlMessage.getVersionNumber());
|
||||
tempOutputByteBuffer.put(ackNumberBytes);
|
||||
tempOutputByteBuffer.put(timestampBytes);
|
||||
tempOutputByteBuffer.put(xmlMessage.getXmlMsgSubType().getValue());
|
||||
tempOutputByteBuffer.put(sequenceNumberBytes);
|
||||
tempOutputByteBuffer.put(xmlMsgLengthBytes);
|
||||
tempOutputByteBuffer.put(messageBytes);
|
||||
|
||||
return tempOutputByteBuffer.array();
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new InvalidMessageException("Could not encode XMLMessage message.", e);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,107 @@
|
||||
package network.Messages.Enums;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Various device sources for a BoatLocation message.
|
||||
*/
|
||||
public enum BoatLocationDeviceEnum {
|
||||
|
||||
|
||||
NOT_A_DEVICE(-1),
|
||||
|
||||
|
||||
Unknown(0),
|
||||
|
||||
/**
|
||||
* A yacht particpating in the race.
|
||||
*/
|
||||
RacingYacht(1),
|
||||
|
||||
CommitteeBoat(2),
|
||||
|
||||
/**
|
||||
* A marker boat.
|
||||
*/
|
||||
Mark(3),
|
||||
|
||||
Pin(4),
|
||||
|
||||
ChaseBoat(5),
|
||||
|
||||
MedicalBoat(6),
|
||||
|
||||
MarshallBoat(7),
|
||||
|
||||
UmpireBoat(8),
|
||||
|
||||
UmpireSoftwareApplication(9),
|
||||
|
||||
PrincipalRaceOfficerApplication(10),
|
||||
|
||||
WeatherStation(11),
|
||||
|
||||
Helicopter(12),
|
||||
|
||||
DataProcessingApplication(13);
|
||||
|
||||
|
||||
/**
|
||||
* Value of the enum.
|
||||
*/
|
||||
private byte value;
|
||||
|
||||
/**
|
||||
* Creates a BoatLocationDeviceEnum from a given primitive integer value, cast to a byte.
|
||||
* @param value Integer, which is cast to byte, to construct from.
|
||||
*/
|
||||
private BoatLocationDeviceEnum(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 BoatLocationDeviceEnum values.
|
||||
*/
|
||||
private static final Map<Byte, BoatLocationDeviceEnum> byteToDeviceMap = new HashMap<>();
|
||||
|
||||
|
||||
/*
|
||||
Static initialization block. Initializes the byteToDeviceMap.
|
||||
*/
|
||||
static {
|
||||
for (BoatLocationDeviceEnum type : BoatLocationDeviceEnum.values()) {
|
||||
BoatLocationDeviceEnum.byteToDeviceMap.put(type.value, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the enumeration value which corresponds to a given byte value.
|
||||
* @param deviceValue Byte value to convert to a BoatLocationDeviceEnum value.
|
||||
* @return The BoatLocationDeviceEnum value which corresponds to the given byte value.
|
||||
*/
|
||||
public static BoatLocationDeviceEnum fromByte(byte deviceValue) {
|
||||
//Gets the corresponding BoatLocationDeviceEnum from the map.
|
||||
BoatLocationDeviceEnum type = BoatLocationDeviceEnum.byteToDeviceMap.get(deviceValue);
|
||||
|
||||
if (type == null) {
|
||||
//If the byte value wasn't found, return the NOT_A_DEVICE BoatLocationDeviceEnum.
|
||||
return BoatLocationDeviceEnum.NOT_A_DEVICE;
|
||||
} else {
|
||||
//Otherwise, return the BoatLocationDeviceEnum.
|
||||
return type;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,102 @@
|
||||
package network.Messages.Enums;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This enum encapsulates the different ways in which a server may respond to a client {@link network.Messages.RequestToJoin} message.
|
||||
*/
|
||||
public enum JoinAcceptanceEnum {
|
||||
|
||||
|
||||
/**
|
||||
* Client is allowed to join.
|
||||
*/
|
||||
JOIN_SUCCESSFUL(1),
|
||||
|
||||
/**
|
||||
* The race is full - no more participants allowed.
|
||||
*/
|
||||
RACE_PARTICIPANTS_FULL(2),
|
||||
|
||||
/**
|
||||
* The race cannot allow any more ghost participants to join.
|
||||
*/
|
||||
GHOST_PARTICIPANTS_FULL(3),
|
||||
|
||||
/**
|
||||
* The server is completely full, cannot participate or spectate.
|
||||
*/
|
||||
SERVER_FULL(4),
|
||||
|
||||
|
||||
/**
|
||||
* Used to indicate that a given byte value is invalid.
|
||||
*/
|
||||
NOT_AN_ACCEPTANCE_TYPE(-1);
|
||||
|
||||
|
||||
/**
|
||||
* Primitive value of the enum.
|
||||
*/
|
||||
private byte value;
|
||||
|
||||
|
||||
/**
|
||||
* Ctor. Creates a JoinAcceptanceEnum from a given primitive integer value, cast to a byte.
|
||||
* @param value Integer, which is cast to byte, to construct from.
|
||||
*/
|
||||
private JoinAcceptanceEnum(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 JoinAcceptanceEnum values.
|
||||
*/
|
||||
private static final Map<Byte, JoinAcceptanceEnum> byteToAcceptanceMap = new HashMap<>();
|
||||
|
||||
|
||||
/*
|
||||
Static initialization block. Initializes the byteToAcceptanceMap.
|
||||
*/
|
||||
static {
|
||||
for (JoinAcceptanceEnum type : JoinAcceptanceEnum.values()) {
|
||||
JoinAcceptanceEnum.byteToAcceptanceMap.put(type.value, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the enumeration value which corresponds to a given byte value.
|
||||
* @param joinAcceptanceEnum Byte value to convert to a JoinAcceptanceEnum value.
|
||||
* @return The RequestToJoinEnum value which corresponds to the given byte value.
|
||||
*/
|
||||
public static JoinAcceptanceEnum fromByte(byte joinAcceptanceEnum) {
|
||||
//Gets the corresponding MessageType from the map.
|
||||
JoinAcceptanceEnum type = JoinAcceptanceEnum.byteToAcceptanceMap.get(joinAcceptanceEnum);
|
||||
|
||||
if (type == null) {
|
||||
//If the byte value wasn't found, return the NOT_AN_ACCEPTANCE_TYPE JoinAcceptanceEnum.
|
||||
return JoinAcceptanceEnum.NOT_AN_ACCEPTANCE_TYPE;
|
||||
} else {
|
||||
//Otherwise, return the JoinAcceptanceEnum.
|
||||
return type;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@ -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<Byte, RaceStartTypeEnum> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,97 @@
|
||||
package network.Messages.Enums;
|
||||
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* This enum encapsulates the different ways in which a client may wish to connect to a server.
|
||||
*/
|
||||
public enum RequestToJoinEnum {
|
||||
|
||||
|
||||
/**
|
||||
* Client wants to spectate.
|
||||
*/
|
||||
SPECTATOR(0),
|
||||
|
||||
/**
|
||||
* Client wants to participate.
|
||||
*/
|
||||
PARTICIPANT(1),
|
||||
|
||||
/**
|
||||
* Client wants to particpate as a ghost.
|
||||
*/
|
||||
GHOST(5),
|
||||
|
||||
|
||||
/**
|
||||
* Used to indicate that a given byte value is invalid.
|
||||
*/
|
||||
NOT_A_REQUEST_TYPE(-1);
|
||||
|
||||
|
||||
/**
|
||||
* Primitive value of the enum.
|
||||
*/
|
||||
private int value;
|
||||
|
||||
|
||||
/**
|
||||
* Ctor. Creates a RequestToJoinEnum from a given int value.
|
||||
* @param value Integer to construct from.
|
||||
*/
|
||||
private RequestToJoinEnum(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the primitive value of the enum.
|
||||
* @return Primitive value of the enum.
|
||||
*/
|
||||
public int getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Stores a mapping between Integer values and RequestToJoinEnum values.
|
||||
*/
|
||||
private static final Map<Integer, RequestToJoinEnum> intToRequestMap = new HashMap<>();
|
||||
|
||||
|
||||
/*
|
||||
Static initialization block. Initializes the intToRequestMap.
|
||||
*/
|
||||
static {
|
||||
for (RequestToJoinEnum type : RequestToJoinEnum.values()) {
|
||||
RequestToJoinEnum.intToRequestMap.put(type.value, type);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the enumeration value which corresponds to a given int value.
|
||||
* @param requestToJoinEnum int value to convert to a RequestToJoinEnum value.
|
||||
* @return The RequestToJoinEnum value which corresponds to the given int value.
|
||||
*/
|
||||
public static RequestToJoinEnum fromInt(int requestToJoinEnum) {
|
||||
//Gets the corresponding MessageType from the map.
|
||||
RequestToJoinEnum type = RequestToJoinEnum.intToRequestMap.get(requestToJoinEnum);
|
||||
|
||||
if (type == null) {
|
||||
//If the int value wasn't found, return the NOT_A_REQUEST_TYPE RequestToJoinEnum.
|
||||
return RequestToJoinEnum.NOT_A_REQUEST_TYPE;
|
||||
} else {
|
||||
//Otherwise, return the RequestToJoinEnum.
|
||||
return type;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
package network.Messages;
|
||||
|
||||
import network.Messages.Enums.JoinAcceptanceEnum;
|
||||
import network.Messages.Enums.MessageType;
|
||||
|
||||
|
||||
/**
|
||||
* This is the message a server sends to a client to tell them their boat sourceID, and if they have actually managed to join the server.
|
||||
*/
|
||||
public class JoinAcceptance extends AC35Data {
|
||||
|
||||
|
||||
/**
|
||||
* The source ID of the boat assigned to the client.
|
||||
* 0 indicates they haven't been assigned a boat.
|
||||
*/
|
||||
private int sourceID = 0;
|
||||
|
||||
/**
|
||||
* The type of acceptance response this is.
|
||||
*/
|
||||
private JoinAcceptanceEnum acceptanceType;
|
||||
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a JoinAcceptance message of a given acceptance type.
|
||||
* @param acceptanceType The type of join acceptance this is.
|
||||
* @param sourceID The sourceID to assign to the client. 0 indicates no sourceID.
|
||||
*/
|
||||
public JoinAcceptance(JoinAcceptanceEnum acceptanceType, int sourceID){
|
||||
super(MessageType.JOIN_ACCEPTANCE);
|
||||
this.acceptanceType = acceptanceType;
|
||||
this.sourceID = sourceID;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The type of acceptance response this is.
|
||||
* @return The type of acceptance response.
|
||||
*/
|
||||
public JoinAcceptanceEnum getAcceptanceType() {
|
||||
return acceptanceType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the source ID of the boat assigned to the client.
|
||||
* @return The source ID of the boat assigned to the client.
|
||||
*/
|
||||
public int getSourceID() {
|
||||
return sourceID;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
package network.Messages;
|
||||
|
||||
|
||||
import network.Messages.Enums.MessageType;
|
||||
import network.Messages.Enums.RequestToJoinEnum;
|
||||
|
||||
/**
|
||||
* This is the message a client sends to a server to request to join/view a race.
|
||||
*/
|
||||
public class RequestToJoin extends AC35Data {
|
||||
|
||||
|
||||
/**
|
||||
* The type of join request this is.
|
||||
*/
|
||||
private RequestToJoinEnum requestType;
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a RequestToJoin message of a given request type.
|
||||
* @param requestType The type of join request this is.
|
||||
*/
|
||||
public RequestToJoin(RequestToJoinEnum requestType){
|
||||
super(MessageType.REQUEST_TO_JOIN);
|
||||
this.requestType = requestType;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The type of join request this is.
|
||||
* @return The type of join request.
|
||||
*/
|
||||
public RequestToJoinEnum getRequestType() {
|
||||
return requestType;
|
||||
}
|
||||
}
|
||||
@ -1,43 +1,107 @@
|
||||
package network.Utils;
|
||||
|
||||
import shared.model.Constants;
|
||||
|
||||
/**
|
||||
* Created by fwy13 on 28/04/17.
|
||||
* Contains various unit conversion for encoding/decoding messages.
|
||||
* Our program uses the "unpacked" units, and the over-the-wire format uses "packed" units (e.g., degrees stored as ints).
|
||||
*/
|
||||
public class AC35UnitConverter {
|
||||
|
||||
public static double convertGPS(int value){
|
||||
//converts latitude or longitue to angle
|
||||
|
||||
/**
|
||||
* Converts a packed GPSCoordinate (latitude or longitude) into the unpacked unit.
|
||||
* @param value Packed lat/long value.
|
||||
* @return Unpacked lat/long angle, in degrees.
|
||||
*/
|
||||
public static double unpackGPS(int value) {
|
||||
return (double) value * 180.0 / 2147483648.0;//2^31 = 2147483648
|
||||
}
|
||||
|
||||
public static int convertGPSToInt(double value){
|
||||
//converts latitude or longitue to angle
|
||||
return (int) (value * 2147483648.0/180.0);//2^31 = 2147483648
|
||||
/**
|
||||
* Converts a latitude or longitude angle into a packed unit.
|
||||
* @param value The lat/long angle, in degrees, to convert.
|
||||
* @return The packed value.
|
||||
*/
|
||||
public static int packGPS(double value) {
|
||||
return (int) (value * 2147483648.0 / 180.0);//2^31 = 2147483648
|
||||
}
|
||||
|
||||
public static double convertHeading(long value){
|
||||
return (double) value * 360.0/65536.0;//2^15
|
||||
|
||||
/**
|
||||
* Unpacks a heading from an int to an angle in degrees (this is a bearing).
|
||||
* @param value The packed value to unpack.
|
||||
* @return The unpacked value in degrees.
|
||||
*/
|
||||
public static double unpackHeading(int value) {
|
||||
return (value * 360.0 / 65536.0);//2^15
|
||||
}
|
||||
|
||||
public static double convertHeading(int value){
|
||||
return (double) value * 360.0/65536.0;//2^15
|
||||
/**
|
||||
* Packs a heading (this is a bearing), in degrees, to a packed int value.
|
||||
* @param value The heading in degrees.
|
||||
* @return The packed value.
|
||||
*/
|
||||
public static int packHeading(double value) {
|
||||
return (int) (value / 360.0 * 65536.0);//2^15
|
||||
}
|
||||
|
||||
|
||||
public static double convertHeading(double value){
|
||||
return value * 360.0/65536.0;//2^15
|
||||
/**
|
||||
* Unpacks a true wind angle from a short to an angle in degrees (this is an azimuth).
|
||||
* @param value The packed value to unpack.
|
||||
* @return The unpacked value in degrees.
|
||||
*/
|
||||
public static double unpackTrueWindAngle(short value) {
|
||||
return (value * 180.0 / 32768.0);//-2^15 to 2^15
|
||||
}
|
||||
|
||||
public static int encodeHeading(int value){
|
||||
return (int) (value / 360.0 * 65536.0);//2^15
|
||||
/**
|
||||
* Packs a true wind angle (this is an azimuth) from an angle in degrees to a packed short value.
|
||||
* @param value The unpacked value in degrees.
|
||||
* @return The packed value.
|
||||
*/
|
||||
public static short packTrueWindAngle(double value) {
|
||||
return (short) (value / 180.0 * 32768.0);//-2^15 to 2^15
|
||||
}
|
||||
|
||||
public static int encodeHeading(double value){
|
||||
return (int) (value / 360.0 * 65536.0);//2^15
|
||||
|
||||
/**
|
||||
* Unpacks a speed, in millimeters per second, to a double, in knots.
|
||||
* @param millimetersPerSec Speed in millimeters per second.
|
||||
* @return Speed in knots.
|
||||
*/
|
||||
public static double unpackMMperSecToKnots(int millimetersPerSec) {
|
||||
return (millimetersPerSec / Constants.KnotsToMMPerSecond);
|
||||
}
|
||||
|
||||
public static double convertTrueWindAngle(long value){
|
||||
return (double) value * 180.0/32768.0;//-2^15 to 2^15
|
||||
/**
|
||||
* Packs a speed, in knots, into an int, in millimeters per second.
|
||||
* @param speedKnots Speed in knots.
|
||||
* @return Speed in millimeters per second.
|
||||
*/
|
||||
public static int packKnotsToMMperSec(double speedKnots) {
|
||||
return (int) (speedKnots * Constants.KnotsToMMPerSecond);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Packs an average wind period, in milliseconds, into an int, in tenths of a second.
|
||||
* @param periodMilliseconds Period in milliseconds.
|
||||
* @return Period in tenths of a second.
|
||||
*/
|
||||
public static int packAverageWindPeriod(long periodMilliseconds) {
|
||||
return (int) (periodMilliseconds / 100);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unpacks an average wind period, in tenths of a second, into an long, in milliseconds.
|
||||
* @param periodTenthsOfSecond Period in tenths of a second.
|
||||
* @return Period in milliseconds
|
||||
*/
|
||||
public static long unpackAverageWindPeriod(int periodTenthsOfSecond) {
|
||||
return (periodTenthsOfSecond * 100);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,152 @@
|
||||
package visualiser.Controllers;
|
||||
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.beans.property.Property;
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.scene.Node;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.StackPane;
|
||||
import javafx.scene.shape.Circle;
|
||||
import shared.model.Bearing;
|
||||
import shared.model.Wind;
|
||||
import visualiser.model.VisualiserRace;
|
||||
|
||||
/**
|
||||
* Controller for the arrow.fxml view.
|
||||
*/
|
||||
public class ArrowController {
|
||||
|
||||
|
||||
@FXML
|
||||
private Pane compass;
|
||||
|
||||
@FXML
|
||||
private StackPane arrowStackPane;
|
||||
|
||||
@FXML
|
||||
private ImageView arrowImage;
|
||||
|
||||
@FXML
|
||||
private Circle circle;
|
||||
|
||||
@FXML
|
||||
private Label northLabel;
|
||||
|
||||
@FXML
|
||||
private Label windLabel;
|
||||
|
||||
@FXML
|
||||
private Label speedLabel;
|
||||
|
||||
|
||||
/**
|
||||
* This is the property our arrow control binds to.
|
||||
*/
|
||||
private Property<Wind> wind;
|
||||
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*/
|
||||
public ArrowController() {
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets which wind property the arrow control should bind to.
|
||||
* @param wind The wind property to bind to.
|
||||
*/
|
||||
public void setWindProperty(Property<Wind> wind) {
|
||||
this.wind = wind;
|
||||
|
||||
wind.addListener((observable, oldValue, newValue) -> {
|
||||
if (newValue != null) {
|
||||
Platform.runLater(() -> updateWind(newValue));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the control to use the new wind value.
|
||||
* This updates the arrow direction (due to bearing), arrow length (due to speed), and label (due to speed).
|
||||
* @param wind The wind value to use.
|
||||
*/
|
||||
private void updateWind(Wind wind) {
|
||||
updateWindBearing(wind.getWindDirection());
|
||||
updateWindSpeed(wind.getWindSpeed());
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the control to account for the new wind speed.
|
||||
* This changes the length (height) of the wind arrow, and updates the speed label.
|
||||
* @param speedKnots The new wind speed, in knots.
|
||||
*/
|
||||
private void updateWindSpeed(double speedKnots) {
|
||||
updateWindArrowLength(speedKnots);
|
||||
updateWindSpeedLabel(speedKnots);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the length of the wind arrow according to the specified wind speed.
|
||||
* @param speedKnots Wind speed, in knots.
|
||||
*/
|
||||
private void updateWindArrowLength(double speedKnots) {
|
||||
|
||||
//At 2 knots, the arrow reaches its minimum height, and at 30 knots it reaches its maximum height.
|
||||
double minKnots = 2;
|
||||
double maxKnots = 30;
|
||||
double deltaKnots = maxKnots - minKnots;
|
||||
|
||||
double minHeight = 25;
|
||||
double maxHeight = 75;
|
||||
double deltaHeight = maxHeight - minHeight;
|
||||
|
||||
//Clamp speed.
|
||||
if (speedKnots > maxKnots) {
|
||||
speedKnots = maxKnots;
|
||||
} else if (speedKnots < minKnots) {
|
||||
speedKnots = minKnots;
|
||||
}
|
||||
|
||||
//How far between the knots bounds is the current speed?
|
||||
double currentDeltaKnots = speedKnots - minKnots;
|
||||
double currentKnotsScalar = currentDeltaKnots / deltaKnots;
|
||||
|
||||
//Thus, how far between the pixel height bounds should the arrow height be?
|
||||
double newHeight = minHeight + (currentKnotsScalar * deltaHeight);
|
||||
|
||||
arrowImage.setFitHeight(newHeight);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the wind speed label according to the specified wind speed.
|
||||
* @param speedKnots Wind speed, in knots.
|
||||
*/
|
||||
private void updateWindSpeedLabel(double speedKnots) {
|
||||
speedLabel.setText(String.format("%.1fkn", speedKnots));
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the control to account for a new wind bearing.
|
||||
* This rotates the arrow according to the bearing.
|
||||
* @param bearing The bearing to use to rotate arrow.
|
||||
*/
|
||||
private void updateWindBearing(Bearing bearing) {
|
||||
|
||||
//We need to display wind-from, so add 180 degrees.
|
||||
Bearing fromBearing = Bearing.fromDegrees(bearing.degrees() + 180d);
|
||||
|
||||
//Rotate the wind arrow.
|
||||
arrowStackPane.setRotate(fromBearing.degrees());
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
After Width: | Height: | Size: 23 KiB |
@ -1,34 +1,58 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
|
||||
<?import javafx.scene.paint.*?>
|
||||
<?import javafx.scene.text.*?>
|
||||
<?import javafx.scene.control.*?>
|
||||
<?import javafx.scene.shape.*?>
|
||||
<?import javafx.scene.image.*?>
|
||||
<?import java.lang.*?>
|
||||
<?import javafx.scene.layout.*?>
|
||||
<?import javafx.geometry.Insets?>
|
||||
<?import javafx.scene.control.Label?>
|
||||
<?import javafx.scene.image.Image?>
|
||||
<?import javafx.scene.image.ImageView?>
|
||||
<?import javafx.scene.layout.ColumnConstraints?>
|
||||
<?import javafx.scene.layout.GridPane?>
|
||||
<?import javafx.scene.layout.Pane?>
|
||||
<?import javafx.scene.layout.RowConstraints?>
|
||||
<?import javafx.scene.layout.StackPane?>
|
||||
<?import javafx.scene.shape.Circle?>
|
||||
<?import javafx.scene.text.Font?>
|
||||
|
||||
<Pane fx:id="compass" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="125.0" prefWidth="125.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1">
|
||||
|
||||
<GridPane fx:id="arrowGridPane" maxHeight="-Infinity" maxWidth="-Infinity" xmlns="http://javafx.com/javafx/8.0.111" xmlns:fx="http://javafx.com/fxml/1" fx:controller="visualiser.Controllers.ArrowController">
|
||||
<columnConstraints>
|
||||
<ColumnConstraints hgrow="SOMETIMES" minWidth="10.0" />
|
||||
</columnConstraints>
|
||||
<rowConstraints>
|
||||
<RowConstraints minHeight="10.0" vgrow="SOMETIMES" />
|
||||
<RowConstraints minHeight="10.0" prefHeight="30.0" vgrow="SOMETIMES" />
|
||||
</rowConstraints>
|
||||
<children>
|
||||
<StackPane fx:id="arrow" prefHeight="125.0" prefWidth="125.0">
|
||||
<Pane fx:id="compass" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="125.0" prefWidth="125.0">
|
||||
<children>
|
||||
<ImageView fitHeight="75.0" fitWidth="75.0">
|
||||
<image>
|
||||
<Image url="@../images/arrow.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
<StackPane fx:id="arrowStackPane" prefHeight="125.0" prefWidth="125.0">
|
||||
<children>
|
||||
<ImageView fx:id="arrowImage" fitHeight="75.0" fitWidth="75.0">
|
||||
<image>
|
||||
<Image url="@../images/arrow.png" />
|
||||
</image>
|
||||
</ImageView>
|
||||
</children>
|
||||
</StackPane>
|
||||
<Circle fx:id="circle" fill="#1f93ff00" layoutX="63.0" layoutY="63.0" radius="60.0" stroke="BLACK" strokeType="INSIDE" strokeWidth="3.0" />
|
||||
<Label fx:id="northLabel" layoutX="55.0" layoutY="1.0" text="N">
|
||||
<font>
|
||||
<Font name="System Bold" size="18.0" />
|
||||
</font>
|
||||
</Label>
|
||||
<Label fx:id="windLabel" layoutX="42.0" layoutY="95.0" text="Wind">
|
||||
<font>
|
||||
<Font name="System Bold" size="16.0" />
|
||||
</font>
|
||||
</Label>
|
||||
</children>
|
||||
</StackPane>
|
||||
<Circle fill="#1f93ff00" layoutX="63.0" layoutY="63.0" radius="60.0" stroke="BLACK" strokeType="INSIDE" strokeWidth="3.0" />
|
||||
<Label layoutX="55.0" layoutY="1.0" text="N">
|
||||
<font>
|
||||
<Font name="System Bold" size="18.0" />
|
||||
</font>
|
||||
</Label>
|
||||
<Label layoutX="42.0" layoutY="99.0" text="Wind">
|
||||
</Pane>
|
||||
<Label fx:id="speedLabel" text="SPEED" GridPane.halignment="CENTER" GridPane.hgrow="NEVER" GridPane.rowIndex="1">
|
||||
<font>
|
||||
<Font name="System Bold" size="16.0" />
|
||||
</font>
|
||||
<GridPane.margin>
|
||||
<Insets />
|
||||
</GridPane.margin>
|
||||
</Label>
|
||||
</children>
|
||||
</Pane>
|
||||
</GridPane>
|
||||
|
||||
@ -0,0 +1,111 @@
|
||||
package mock.model;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import shared.model.Bearing;
|
||||
import shared.model.Wind;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
||||
public class WindGeneratorTest {
|
||||
|
||||
|
||||
private WindGenerator windGenerator;
|
||||
|
||||
private Bearing windBaselineBearing;
|
||||
private Bearing windBearingLowerBound;
|
||||
private Bearing windBearingUpperBound;
|
||||
private double windBaselineSpeed;
|
||||
private double windSpeedLowerBound;
|
||||
private double windSpeedUpperBound;
|
||||
|
||||
private double speedKnotsEpsilon;
|
||||
private double bearingDegreeEpsilon;
|
||||
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
|
||||
|
||||
//Bounds.
|
||||
this.windBaselineBearing = Bearing.fromDegrees(88.3);
|
||||
this.windBearingLowerBound = Bearing.fromDegrees(66.5);
|
||||
this.windBearingUpperBound = Bearing.fromDegrees(248.6);
|
||||
this.windBaselineSpeed = 13;
|
||||
this.windSpeedLowerBound = 7;
|
||||
this.windSpeedUpperBound = 20;
|
||||
|
||||
this.windGenerator = new WindGenerator(
|
||||
windBaselineBearing,
|
||||
windBearingLowerBound,
|
||||
windBearingUpperBound,
|
||||
windBaselineSpeed,
|
||||
windSpeedLowerBound,
|
||||
windSpeedUpperBound );
|
||||
|
||||
this.bearingDegreeEpsilon = 0.001;
|
||||
this.speedKnotsEpsilon = 0.001;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests if the baseline wind generated it accurate.
|
||||
*/
|
||||
@Test
|
||||
public void generateBaselineWindTest() {
|
||||
|
||||
Wind wind = windGenerator.generateBaselineWind();
|
||||
|
||||
assertEquals(windBaselineSpeed, wind.getWindSpeed(), speedKnotsEpsilon);
|
||||
assertEquals(windBaselineBearing.degrees(), wind.getWindDirection().degrees(), bearingDegreeEpsilon);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if the random wind generated is inside the bounds.
|
||||
*/
|
||||
@Test
|
||||
public void generateRandomWindTest() {
|
||||
|
||||
int randomWindCount = 1000;
|
||||
|
||||
for (int i = 0; i < randomWindCount; i++) {
|
||||
|
||||
Wind wind = windGenerator.generateRandomWind();
|
||||
|
||||
assertTrue(wind.getWindSpeed() >= windSpeedLowerBound);
|
||||
assertTrue(wind.getWindSpeed() <= windSpeedUpperBound);
|
||||
|
||||
assertTrue(wind.getWindDirection().degrees() >= windBearingLowerBound.degrees());
|
||||
assertTrue(wind.getWindDirection().degrees() <= windBearingUpperBound.degrees());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests if the next wind generated is inside the bounds.
|
||||
*/
|
||||
@Test
|
||||
public void generateNextWindTest() {
|
||||
|
||||
Wind wind = windGenerator.generateBaselineWind();
|
||||
|
||||
int randomWindCount = 1000;
|
||||
|
||||
for (int i = 0; i < randomWindCount; i++) {
|
||||
|
||||
wind = windGenerator.generateNextWind(wind);
|
||||
|
||||
assertTrue(wind.getWindSpeed() >= windSpeedLowerBound);
|
||||
assertTrue(wind.getWindSpeed() <= windSpeedUpperBound);
|
||||
|
||||
assertTrue(wind.getWindDirection().degrees() >= windBearingLowerBound.degrees());
|
||||
assertTrue(wind.getWindDirection().degrees() <= windBearingUpperBound.degrees());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -1,31 +1,58 @@
|
||||
package mock.model.commandFactory;
|
||||
|
||||
import mock.model.MockBoat;
|
||||
import mock.model.MockRace;
|
||||
import network.Messages.Enums.BoatActionEnum;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mockito.Mock;
|
||||
import shared.model.Bearing;
|
||||
import shared.model.Boat;
|
||||
import shared.model.Race;
|
||||
import visualiser.model.VisualiserRace;
|
||||
|
||||
import static org.mockito.Mockito.when;
|
||||
import static org.testng.Assert.*;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
/**
|
||||
* Created by connortaylorbrown on 4/08/17.
|
||||
*/
|
||||
public class WindCommandTest {
|
||||
private Race race;
|
||||
private Boat boat;
|
||||
private MockRace race;
|
||||
private MockBoat boat;
|
||||
private Command upwind;
|
||||
private Command downwind;
|
||||
private double initial;
|
||||
|
||||
private double offset = 3.0;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
boat = new Boat(0, "Bob", "NZ");
|
||||
race = mock(MockRace.class);
|
||||
boat = new MockBoat(0, "Bob", "NZ", null);
|
||||
|
||||
when(race.getWindDirection()).thenReturn(Bearing.fromDegrees(0.0));
|
||||
boat.setBearing(Bearing.fromDegrees(45.0));
|
||||
|
||||
upwind = CommandFactory.createCommand(race, boat, BoatActionEnum.UPWIND);
|
||||
downwind = CommandFactory.createCommand(race, boat, BoatActionEnum.DOWNWIND);
|
||||
|
||||
initial = boat.getBearing().degrees();
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensure the difference between initial and final angle is 3 degrees
|
||||
*/
|
||||
@Test
|
||||
public void upwindCommandDecreasesAngle() {
|
||||
upwind.execute();
|
||||
assertEquals(initial - boat.getBearing().degrees(), -offset, 1e-5);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void downwindCommandIncreasesAngle() {
|
||||
downwind.execute();
|
||||
assertEquals(initial - boat.getBearing().degrees(), offset, 1e-5);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,75 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
import network.MessageEncoders.RaceVisionByteEncoder;
|
||||
import network.Messages.AverageWind;
|
||||
import static org.junit.Assert.*;
|
||||
import org.junit.Test;
|
||||
import shared.model.Bearing;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Test for the AverageWind encoder and decoder
|
||||
*/
|
||||
public class AverageWindDecoderTest {
|
||||
|
||||
|
||||
/**
|
||||
* Creates a AverageWind message, encodes it, decodes it, and checks that the result matches the starting message.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
@Test
|
||||
public void averageWindEncodeDecodeTest() throws Exception {
|
||||
|
||||
AverageWind averageWind = new AverageWind(
|
||||
AverageWind.currentMessageVersionNumber,
|
||||
System.currentTimeMillis(),
|
||||
3000,
|
||||
12.5,
|
||||
4050,
|
||||
12.6,
|
||||
3055,
|
||||
12.7,
|
||||
6051,
|
||||
13.37
|
||||
);
|
||||
|
||||
byte[] encodedMessage = RaceVisionByteEncoder.encode(averageWind);
|
||||
|
||||
AverageWindDecoder averageWindDecoder = new AverageWindDecoder();
|
||||
averageWindDecoder.decode(encodedMessage);
|
||||
AverageWind averageWindDecoded = averageWindDecoder.getMessage();
|
||||
|
||||
compareAverageWindMessages(averageWind, averageWindDecoded);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares two AverageWind messages to check that they are equal.
|
||||
* @param original The original AverageWind message.
|
||||
* @param decoded The decoded AverageWind message.
|
||||
*/
|
||||
public static void compareAverageWindMessages(AverageWind original, AverageWind decoded) {
|
||||
|
||||
|
||||
assertEquals(original.getMessageVersionNumber(), decoded.getMessageVersionNumber());
|
||||
assertEquals(original.getTime(), decoded.getTime());
|
||||
|
||||
assertEquals(original.getRawPeriod(), decoded.getRawPeriod(), 100);
|
||||
assertEquals(original.getRawSpeedKnots(), decoded.getRawSpeedKnots(), 0.01);
|
||||
|
||||
assertEquals(original.getSampleTwoPeriod(), decoded.getSampleTwoPeriod(), 100);
|
||||
assertEquals(original.getSampleTwoSpeedKnots(), decoded.getSampleTwoSpeedKnots(), 0.01);
|
||||
|
||||
assertEquals(original.getSampleThreePeriod(), decoded.getSampleThreePeriod(), 100);
|
||||
assertEquals(original.getSampleThreeSpeedKnots(), decoded.getSampleThreeSpeedKnots(), 0.01);
|
||||
|
||||
assertEquals(original.getSampleFourPeriod(), decoded.getSampleFourPeriod(), 100);
|
||||
assertEquals(original.getSampleFourSpeedKnots(), decoded.getSampleFourSpeedKnots(), 0.01);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,117 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.MessageEncoders.RaceVisionByteEncoder;
|
||||
import network.Messages.BoatAction;
|
||||
import network.Messages.Enums.BoatActionEnum;
|
||||
import network.Messages.Enums.RequestToJoinEnum;
|
||||
import network.Messages.RequestToJoin;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
|
||||
/**
|
||||
* Test for the BoatAction encoder and decoder
|
||||
*/
|
||||
public class BoatActionDecoderTest {
|
||||
|
||||
|
||||
/**
|
||||
* Encodes and decodes a given message.
|
||||
* @param message Message to encode/decode.
|
||||
* @return The decoded message.
|
||||
* @throws InvalidMessageException If the message cannot be encoded.
|
||||
*/
|
||||
private BoatAction encodeDecodeMessage(BoatAction message) throws InvalidMessageException {
|
||||
|
||||
//Encode.
|
||||
byte [] testEncodedMessage = RaceVisionByteEncoder.encode(message);
|
||||
|
||||
//Decode.
|
||||
BoatActionDecoder testDecoder = new BoatActionDecoder();
|
||||
testDecoder.decode(testEncodedMessage);
|
||||
|
||||
BoatAction decodedMessage = testDecoder.getMessage();
|
||||
|
||||
return decodedMessage;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests if a specific boat action type message can be encoded and decoded correctly.
|
||||
* @param type The type of boat action.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
private void boatActionTypeTest(BoatActionEnum type) throws Exception {
|
||||
|
||||
//Prepare message.
|
||||
BoatAction beforeMessage = new BoatAction(type);
|
||||
|
||||
|
||||
//Encode/decode it.
|
||||
BoatAction afterMessage = encodeDecodeMessage(beforeMessage);
|
||||
|
||||
|
||||
//Compare.
|
||||
assertEquals(beforeMessage.getBoatAction(), afterMessage.getBoatAction());
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests if an autopilot message can be encoded and decoded correctly.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
@Test
|
||||
public void autoPilotTest() throws Exception {
|
||||
boatActionTypeTest(BoatActionEnum.AUTO_PILOT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a sails in message can be encoded and decoded correctly.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
@Test
|
||||
public void sailsInTest() throws Exception {
|
||||
boatActionTypeTest(BoatActionEnum.SAILS_IN);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a sails out message can be encoded and decoded correctly.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
@Test
|
||||
public void sailsOutTest() throws Exception {
|
||||
boatActionTypeTest(BoatActionEnum.SAILS_OUT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a tack/gybe message can be encoded and decoded correctly.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
@Test
|
||||
public void tackGybeTest() throws Exception {
|
||||
boatActionTypeTest(BoatActionEnum.TACK_GYBE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if an upwind message can be encoded and decoded correctly.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
@Test
|
||||
public void upwindTest() throws Exception {
|
||||
boatActionTypeTest(BoatActionEnum.UPWIND);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a downwind message can be encoded and decoded correctly.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
@Test
|
||||
public void downwindTest() throws Exception {
|
||||
boatActionTypeTest(BoatActionEnum.DOWNWIND);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,90 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
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.
|
||||
* @throws InvalidMessageException Thrown if message cannot be encoded or decoded.
|
||||
*/
|
||||
private static BoatStatus encodeDecodeBoatStatus(BoatStatus boatStatus) throws InvalidMessageException {
|
||||
|
||||
BoatStatusEncoder boatStatusEncoder = new BoatStatusEncoder();
|
||||
byte[] boatStatusEncoded = boatStatusEncoder.encode(boatStatus);
|
||||
|
||||
BoatStatusDecoder boatStatusDecoder = new BoatStatusDecoder();
|
||||
BoatStatus boatStatusDecoded = boatStatusDecoder.decode(boatStatusEncoded);
|
||||
|
||||
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,57 +1,79 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
import network.MessageEncoders.RaceVisionByteEncoder;
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.MessageEncoders.CourseWindEncoder;
|
||||
import network.Messages.BoatStatus;
|
||||
import network.Messages.CourseWind;
|
||||
import network.Messages.Enums.BoatStatusEnum;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import shared.model.Bearing;
|
||||
|
||||
/**
|
||||
* Created by hba56 on 23/04/17.
|
||||
* Test for the CourseWind encoder and decoder
|
||||
*/
|
||||
public class CourseWindDecoderTest {
|
||||
|
||||
|
||||
/**
|
||||
* Creates a CourseWind 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 courseWindEncodeDecodeTest() throws Exception {
|
||||
|
||||
long time = System.currentTimeMillis();
|
||||
CourseWind testCourseWind1 = new CourseWind(1, time, 2,
|
||||
3, 4, 5,
|
||||
7, 6);
|
||||
CourseWind courseWind = new CourseWind(
|
||||
1,
|
||||
time,
|
||||
2,
|
||||
Bearing.fromDegrees(45),
|
||||
4,
|
||||
Bearing.fromDegrees(70),
|
||||
Bearing.fromDegrees(160),
|
||||
(byte) 0x13 );
|
||||
|
||||
long time2 = System.currentTimeMillis();
|
||||
CourseWind testCourseWind2 = new CourseWind(2, time2, 2,
|
||||
3, 4, 5,
|
||||
7, 6);
|
||||
|
||||
ArrayList<CourseWind> testCourseWinds = new ArrayList<CourseWind>();
|
||||
testCourseWinds.add(testCourseWind1);
|
||||
testCourseWinds.add(testCourseWind2);
|
||||
CourseWind courseWindDecoded = encodeDecodeCourseWind(courseWind);
|
||||
|
||||
compareCourseWindMessages(courseWind, courseWindDecoded);
|
||||
|
||||
byte[] testEncodedCourseWind = RaceVisionByteEncoder.courseWind((byte) 1, testCourseWinds);
|
||||
}
|
||||
|
||||
CourseWindDecoder testDecoder = new CourseWindDecoder(testEncodedCourseWind);
|
||||
/**
|
||||
* Encodes and decodes a CourseWind, and returns it.
|
||||
* @param courseWind The CourseWind to encode and decode.
|
||||
* @return The decoded CourseWind.
|
||||
* @throws InvalidMessageException Thrown if message cannot be encoded or decoded.
|
||||
*/
|
||||
private static CourseWind encodeDecodeCourseWind(CourseWind courseWind) throws InvalidMessageException {
|
||||
|
||||
ArrayList<CourseWind> testDecodedCourseWinds = testDecoder.getLoopMessages();
|
||||
CourseWindEncoder courseWindEncoder = new CourseWindEncoder();
|
||||
byte[] courseWindEncoded = courseWindEncoder.encode(courseWind);
|
||||
|
||||
Assert.assertEquals(testCourseWinds.get(0).getID(), testDecodedCourseWinds.get(0).getID());
|
||||
Assert.assertEquals(testCourseWinds.get(0).getTime(), testDecodedCourseWinds.get(0).getTime());
|
||||
Assert.assertEquals(testCourseWinds.get(0).getRaceID(), testDecodedCourseWinds.get(0).getRaceID());
|
||||
Assert.assertEquals(testCourseWinds.get(0).getWindDirection(), testDecodedCourseWinds.get(0).getWindDirection());
|
||||
Assert.assertEquals(testCourseWinds.get(0).getWindSpeed(), testDecodedCourseWinds.get(0).getWindSpeed());
|
||||
Assert.assertEquals(testCourseWinds.get(0).getBestUpwindAngle(), testDecodedCourseWinds.get(0).getBestUpwindAngle());
|
||||
Assert.assertEquals(testCourseWinds.get(0).getBestDownwindAngle(), testDecodedCourseWinds.get(0).getBestDownwindAngle());
|
||||
Assert.assertEquals(testCourseWinds.get(0).getFlags(), testDecodedCourseWinds.get(0).getFlags());
|
||||
CourseWindDecoder courseWindDecoder = new CourseWindDecoder();
|
||||
CourseWind courseWindDecoded = courseWindDecoder.decode(courseWindEncoded);
|
||||
|
||||
Assert.assertEquals(testCourseWinds.get(1).getID(), testDecodedCourseWinds.get(1).getID());
|
||||
Assert.assertEquals(testCourseWinds.get(1).getTime(), testDecodedCourseWinds.get(1).getTime());
|
||||
Assert.assertEquals(testCourseWinds.get(1).getRaceID(), testDecodedCourseWinds.get(1).getRaceID());
|
||||
Assert.assertEquals(testCourseWinds.get(1).getWindDirection(), testDecodedCourseWinds.get(1).getWindDirection());
|
||||
Assert.assertEquals(testCourseWinds.get(1).getWindSpeed(), testDecodedCourseWinds.get(1).getWindSpeed());
|
||||
Assert.assertEquals(testCourseWinds.get(1).getBestUpwindAngle(), testDecodedCourseWinds.get(1).getBestUpwindAngle());
|
||||
Assert.assertEquals(testCourseWinds.get(1).getBestDownwindAngle(), testDecodedCourseWinds.get(1).getBestDownwindAngle());
|
||||
Assert.assertEquals(testCourseWinds.get(1).getFlags(), testDecodedCourseWinds.get(1).getFlags());
|
||||
return courseWindDecoded;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares two CourseWind messages to check that they are equal.
|
||||
* @param original The original CourseWind message.
|
||||
* @param decoded The decoded CourseWind message.
|
||||
*/
|
||||
public static void compareCourseWindMessages(CourseWind original, CourseWind decoded) {
|
||||
|
||||
Assert.assertEquals(original.getID(), decoded.getID());
|
||||
Assert.assertEquals(original.getTime(), decoded.getTime());
|
||||
Assert.assertEquals(original.getRaceID(), decoded.getRaceID());
|
||||
Assert.assertEquals(original.getWindDirection().degrees(), decoded.getWindDirection().degrees(), 0.01);
|
||||
Assert.assertEquals(original.getWindSpeedKnots(), decoded.getWindSpeedKnots(), 0.01);
|
||||
Assert.assertEquals(original.getBestUpwindAngle().degrees(), decoded.getBestUpwindAngle().degrees(), 0.01);
|
||||
Assert.assertEquals(original.getBestDownwindAngle().degrees(), decoded.getBestDownwindAngle().degrees(), 0.01);
|
||||
Assert.assertEquals(original.getFlags(), decoded.getFlags());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,96 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
import network.MessageEncoders.RaceVisionByteEncoder;
|
||||
import network.Messages.CourseWind;
|
||||
import network.Messages.CourseWinds;
|
||||
import org.junit.Test;
|
||||
import shared.model.Bearing;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
||||
/**
|
||||
* Tests for CourseWinds encoder and decoder.
|
||||
*/
|
||||
public class CourseWindsDecoderTest {
|
||||
|
||||
/**
|
||||
* Tests if a CourseWinds message can be encoded and decoded correctly.
|
||||
* @throws Exception Thrown if an error occurs.
|
||||
*/
|
||||
@Test
|
||||
public void courseWindsEncodeDecodeTest() throws Exception {
|
||||
|
||||
long time1 = System.currentTimeMillis();
|
||||
CourseWind testCourseWind1 = new CourseWind(
|
||||
1,
|
||||
time1,
|
||||
2,
|
||||
Bearing.fromDegrees(45),
|
||||
4,
|
||||
Bearing.fromDegrees(70),
|
||||
Bearing.fromDegrees(160),
|
||||
(byte) 0xCE );
|
||||
|
||||
long time2 = System.currentTimeMillis();
|
||||
CourseWind testCourseWind2 = new CourseWind(
|
||||
2,
|
||||
time2,
|
||||
2,
|
||||
Bearing.fromDegrees(55),
|
||||
4,
|
||||
Bearing.fromDegrees(80),
|
||||
Bearing.fromDegrees(180),
|
||||
(byte) 0x0D );
|
||||
|
||||
List<CourseWind> testCourseWinds = new ArrayList<>();
|
||||
testCourseWinds.add(testCourseWind1);
|
||||
testCourseWinds.add(testCourseWind2);
|
||||
|
||||
CourseWinds courseWinds = new CourseWinds(CourseWinds.currentMessageVersionNumber, (byte) 2, testCourseWinds);
|
||||
|
||||
|
||||
byte[] testEncodedCourseWind = RaceVisionByteEncoder.encode(courseWinds);
|
||||
|
||||
CourseWindsDecoder courseWindsDecoder = new CourseWindsDecoder();
|
||||
courseWindsDecoder.decode(testEncodedCourseWind);
|
||||
CourseWinds courseWindsDecoded = courseWindsDecoder.getMessage();
|
||||
|
||||
compareCourseWindsMessages(courseWinds, courseWindsDecoded);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Compares two course winds messages to ensure they are the same.
|
||||
* @param original The original message.
|
||||
* @param decoded The decoded message.
|
||||
*/
|
||||
public static void compareCourseWindsMessages(CourseWinds original, CourseWinds decoded) {
|
||||
|
||||
//Compare header.
|
||||
assertEquals(original.getMessageVersionNumber(), decoded.getMessageVersionNumber());
|
||||
assertEquals(original.getSelectedWindID(), decoded.getSelectedWindID());
|
||||
assertEquals(original.getCourseWinds().size(), decoded.getCourseWinds().size());
|
||||
|
||||
//Compare each CourseWind.
|
||||
List<CourseWind> originalWinds = original.getCourseWinds();
|
||||
List<CourseWind> decodedWinds = decoded.getCourseWinds();
|
||||
|
||||
Iterator<CourseWind> originalIterator = originalWinds.iterator();
|
||||
Iterator<CourseWind> decodedIterator = decodedWinds.iterator();
|
||||
|
||||
while (originalIterator.hasNext() && decodedIterator.hasNext()) {
|
||||
|
||||
CourseWindDecoderTest.compareCourseWindMessages(originalIterator.next(), decodedIterator.next());
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,79 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.MessageEncoders.RaceVisionByteEncoder;
|
||||
import network.Messages.BoatAction;
|
||||
import network.Messages.Enums.BoatActionEnum;
|
||||
import network.Messages.HeartBeat;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
|
||||
/**
|
||||
* Test for the HeartBeat encoder and decoder
|
||||
*/
|
||||
public class HeartBeatDecoderTest {
|
||||
|
||||
|
||||
/**
|
||||
* Encodes and decodes a given message.
|
||||
* @param message Message to encode/decode.
|
||||
* @return The decoded message.
|
||||
* @throws InvalidMessageException if the message cannot be encoded.
|
||||
*/
|
||||
private HeartBeat encodeDecodeMessage(HeartBeat message) throws InvalidMessageException {
|
||||
|
||||
//Encode.
|
||||
byte [] testEncodedMessage = RaceVisionByteEncoder.encode(message);
|
||||
|
||||
//Decode.
|
||||
HeartBeatDecoder testDecoder = new HeartBeatDecoder();
|
||||
testDecoder.decode(testEncodedMessage);
|
||||
HeartBeat decodedMessage = testDecoder.getMessage();
|
||||
|
||||
return decodedMessage;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests if a heartbeat message with a given sequence number can be encoded and decoded correctly.
|
||||
* @param sequenceNumber The sequenceNumber to use.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
private void heartBeatTest(long sequenceNumber) throws Exception {
|
||||
|
||||
//Prepare message.
|
||||
HeartBeat beforeMessage = new HeartBeat(sequenceNumber);
|
||||
|
||||
|
||||
//Encode/decode it.
|
||||
HeartBeat afterMessage = encodeDecodeMessage(beforeMessage);
|
||||
|
||||
|
||||
//Compare.
|
||||
assertEquals(beforeMessage.getSequenceNumber(), afterMessage.getSequenceNumber());
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests if a heartbeat message with a sequence number of zero can be encoded and decoded correctly.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
@Test
|
||||
public void heartBeatZeroTest() throws Exception {
|
||||
heartBeatTest(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a heartbeat message with a sequence number of 1234512 can be encoded and decoded correctly.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
@Test
|
||||
public void heartBeatNonZeroTest() throws Exception {
|
||||
heartBeatTest(1234512);
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -0,0 +1,108 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.MessageEncoders.RaceVisionByteEncoder;
|
||||
import network.Messages.Enums.JoinAcceptanceEnum;
|
||||
import network.Messages.JoinAcceptance;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
||||
/**
|
||||
* Test for the {@link network.Messages.JoinAcceptance} encoder and decoder
|
||||
*/
|
||||
public class JoinAcceptanceDecoderTest {
|
||||
|
||||
|
||||
/**
|
||||
* Encodes and decodes a given message.
|
||||
* @param message Message to encode/decode.
|
||||
* @return The decoded message.
|
||||
* @throws InvalidMessageException If the message cannot be encoded.
|
||||
*/
|
||||
private JoinAcceptance encodeDecodeMessage(JoinAcceptance message) throws InvalidMessageException {
|
||||
|
||||
//Encode.
|
||||
byte [] testEncodedMessage = RaceVisionByteEncoder.encode(message);
|
||||
|
||||
//Decode.
|
||||
JoinAcceptanceDecoder testDecoder = new JoinAcceptanceDecoder();
|
||||
testDecoder.decode(testEncodedMessage);
|
||||
JoinAcceptance decodedMessage = testDecoder.getMessage();
|
||||
|
||||
return decodedMessage;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests if a specific acceptance type message can be encoded and decoded correctly.
|
||||
* @param type The type of acceptance response.
|
||||
* @param sourceID The source ID to assign.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
private void responseTypeTest(JoinAcceptanceEnum type, int sourceID) throws Exception {
|
||||
|
||||
//Prepare message.
|
||||
JoinAcceptance beforeMessage = new JoinAcceptance(type, sourceID);
|
||||
|
||||
|
||||
//Encode/decode it.
|
||||
JoinAcceptance afterMessage = encodeDecodeMessage(beforeMessage);
|
||||
|
||||
|
||||
//Compare.
|
||||
assertEquals(beforeMessage.getAcceptanceType().getValue(), afterMessage.getAcceptanceType().getValue());
|
||||
assertEquals(beforeMessage.getSourceID(), afterMessage.getSourceID());
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests if a join success message, with a source ID, can be encoded and decoded correctly.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
@Test
|
||||
public void joinSuccessSourceIDTest() throws Exception {
|
||||
responseTypeTest(JoinAcceptanceEnum.JOIN_SUCCESSFUL, 12345);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a join success message, with no source ID, can be encoded and decoded correctly.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
@Test
|
||||
public void joinSuccessNoSourceIDTest() throws Exception {
|
||||
responseTypeTest(JoinAcceptanceEnum.JOIN_SUCCESSFUL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a participants full message can be encoded and decoded correctly.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
@Test
|
||||
public void participantFullTest() throws Exception {
|
||||
responseTypeTest(JoinAcceptanceEnum.RACE_PARTICIPANTS_FULL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a ghosts full message can be encoded and decoded correctly.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
@Test
|
||||
public void ghostFullTest() throws Exception {
|
||||
responseTypeTest(JoinAcceptanceEnum.GHOST_PARTICIPANTS_FULL, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a server full message can be encoded and decoded correctly.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
@Test
|
||||
public void serverFullTest() throws Exception {
|
||||
responseTypeTest(JoinAcceptanceEnum.SERVER_FULL, 0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@ -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());
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -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());
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@ -0,0 +1,89 @@
|
||||
package network.MessageDecoders;
|
||||
|
||||
import network.Exceptions.InvalidMessageException;
|
||||
import network.MessageEncoders.RaceVisionByteEncoder;
|
||||
import network.Messages.Enums.RequestToJoinEnum;
|
||||
import network.Messages.RequestToJoin;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
|
||||
/**
|
||||
* Test for the RequestToJoin encoder and decoder
|
||||
*/
|
||||
public class RequestToJoinDecoderTest {
|
||||
|
||||
|
||||
/**
|
||||
* Encodes and decodes a given message.
|
||||
* @param message Message to encode/decode.
|
||||
* @return The decoded message.
|
||||
* @throws InvalidMessageException If the message cannot be encoded.
|
||||
*/
|
||||
private RequestToJoin encodeDecodeMessage(RequestToJoin message) throws InvalidMessageException {
|
||||
|
||||
//Encode.
|
||||
byte [] testEncodedMessage = RaceVisionByteEncoder.encode(message);
|
||||
|
||||
//Decode.
|
||||
RequestToJoinDecoder testDecoder = new RequestToJoinDecoder();
|
||||
testDecoder.decode(testEncodedMessage);
|
||||
RequestToJoin decodedMessage = testDecoder.getMessage();
|
||||
|
||||
return decodedMessage;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests if a specific request type message can be encoded and decoded correctly.
|
||||
* @param type The type of join request.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
private void requestTypeTest(RequestToJoinEnum type) throws Exception {
|
||||
|
||||
//Prepare message.
|
||||
RequestToJoin beforeMessage = new RequestToJoin(type);
|
||||
|
||||
|
||||
//Encode/decode it.
|
||||
RequestToJoin afterMessage = encodeDecodeMessage(beforeMessage);
|
||||
|
||||
|
||||
//Compare.
|
||||
assertEquals(beforeMessage.getRequestType().getValue(), afterMessage.getRequestType().getValue());
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Tests if a spectator request message can be encoded and decoded correctly.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
@Test
|
||||
public void spectatorTest() throws Exception {
|
||||
requestTypeTest(RequestToJoinEnum.SPECTATOR);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a participant request message can be encoded and decoded correctly.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
@Test
|
||||
public void participantTest() throws Exception {
|
||||
requestTypeTest(RequestToJoinEnum.PARTICIPANT);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests if a ghost request message can be encoded and decoded correctly.
|
||||
* @throws Exception if test fails.
|
||||
*/
|
||||
@Test
|
||||
public void ghostTest() throws Exception {
|
||||
requestTypeTest(RequestToJoinEnum.GHOST);
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue