Added MessageDecoder interface. All decoder implement this.

Added DecoderFactory. This creates an appropriate decoder based on a MessageType.
BoatActionDecoder implements MessageDecoder.
BoatLocationDecoder implements MessageDecoder.
HeartBeatDecoder implements MessageDecoder.
JoinAcceptance implements MessageDecoder.
RaceStatusDecoder implements MessageDecoder.
RequestToJoinDecoder implements MessageDecoder.
XMLMessageDecoder implements MessageDecoder.
Refactored CourseWind decoder/encoder. CourseWind decoder/encoder is for an individual CourseWind.
CourseWinds decoder/encoder is for the combined message from the API.
Documented BoatAction, and it now contains a BoatActionEnum instead of a byte.
Refactored CourseWind and CourseWinds classes. They now expose correct units, instead of packed units.
Added CourseWindDecoderTest, and updated CourseWindsDecoderTest.
issue #35 #36
#story[1095]
main
fjc40 8 years ago
parent 1fbdd09d70
commit 750ea5c141

@ -2,6 +2,7 @@ package network;
import network.Exceptions.InvalidMessageException; import network.Exceptions.InvalidMessageException;
import network.Exceptions.InvalidMessageTypeException;
import network.MessageDecoders.*; import network.MessageDecoders.*;
import network.Messages.*; import network.Messages.*;
import network.Messages.Enums.MessageType; import network.Messages.Enums.MessageType;
@ -130,16 +131,26 @@ public class BinaryMessageDecoder {
//Now we create the message object based on what is actually in the message body. //Now we create the message object based on what is actually in the message body.
MessageType mType = MessageType.fromByte(headerMessageType); MessageType mType = MessageType.fromByte(headerMessageType);
/*MessageDecoder decoder = null;
try {
decoder = DecoderFactory.create(mType);
} catch (InvalidMessageTypeException e) {
throw new InvalidMessageException("Could not create decoder for MessageType: " + mType, e);
}
return decoder.decode(messageBody);*/
switch(mType) { switch(mType) {
case HEARTBEAT: case HEARTBEAT:
//System.out.println("Decoding HeartBeat Message!"); //System.out.println("Decoding HeartBeat Message!");
HeartBeatDecoder heartBeatDecoder = new HeartBeatDecoder(messageBody); HeartBeatDecoder heartBeatDecoder = new HeartBeatDecoder();
return heartBeatDecoder.getMessage(); return heartBeatDecoder.decode(messageBody);
case RACESTATUS: case RACESTATUS:
//System.out.println("Race Status Message"); //System.out.println("Race Status Message");
RaceStatusDecoder rsdecoder = new RaceStatusDecoder(messageBody); RaceStatusDecoder rsdecoder = new RaceStatusDecoder();
return rsdecoder.getMessage(); return rsdecoder.decode(messageBody);
case DISPLAYTEXTMESSAGE: case DISPLAYTEXTMESSAGE:
//System.out.println("Display Text Message"); //System.out.println("Display Text Message");
@ -148,8 +159,8 @@ public class BinaryMessageDecoder {
case XMLMESSAGE: case XMLMESSAGE:
//System.out.println("XML Message!"); //System.out.println("XML Message!");
XMLMessageDecoder xmdecoder = new XMLMessageDecoder(messageBody); XMLMessageDecoder xmdecoder = new XMLMessageDecoder();
return xmdecoder.getMessage(); return xmdecoder.decode(messageBody);
case RACESTARTSTATUS: case RACESTARTSTATUS:
//System.out.println("Race Start Status Message"); //System.out.println("Race Start Status Message");
@ -173,8 +184,8 @@ public class BinaryMessageDecoder {
case BOATLOCATION: case BOATLOCATION:
//System.out.println("Boat Location Message!"); //System.out.println("Boat Location Message!");
BoatLocationDecoder blDecoder = new BoatLocationDecoder(messageBody); BoatLocationDecoder blDecoder = new BoatLocationDecoder();
return blDecoder.getMessage(); return blDecoder.decode(messageBody);
case MARKROUNDING: case MARKROUNDING:
//System.out.println("Mark Rounding Message!"); //System.out.println("Mark Rounding Message!");
@ -183,8 +194,8 @@ public class BinaryMessageDecoder {
case COURSEWIND: case COURSEWIND:
//System.out.println("Course Wind Message!"); //System.out.println("Course Wind Message!");
CourseWindDecoder cwDecoder = new CourseWindDecoder(messageBody); CourseWindsDecoder cwDecoder = new CourseWindsDecoder();
return new CourseWinds(cwDecoder.getMessageVersionNumber(), cwDecoder.getByteWindID(), cwDecoder.getLoopMessages()); return cwDecoder.decode(messageBody);
case AVGWIND: case AVGWIND:
//System.out.println("Average Wind Message!"); //System.out.println("Average Wind Message!");
@ -192,8 +203,8 @@ public class BinaryMessageDecoder {
return awDecoder.getAverageWind(); return awDecoder.getAverageWind();
case BOATACTION: case BOATACTION:
BoatActionDecoder baDecoder = new BoatActionDecoder(messageBody); BoatActionDecoder baDecoder = new BoatActionDecoder();
return new BoatAction(baDecoder.getBoatAction()); return baDecoder.decode(messageBody);
default: default:
//System.out.println("Broken Message!"); //System.out.println("Broken Message!");

@ -1,20 +1,50 @@
package network.MessageDecoders; package network.MessageDecoders;
import network.Messages.AC35Data;
import network.Messages.BoatAction;
import network.Messages.Enums.BoatActionEnum; import network.Messages.Enums.BoatActionEnum;
import java.util.Arrays; import java.util.Arrays;
public class BoatActionDecoder { /**
byte byteBoatAction; * Decodes {@link BoatAction} messages.
BoatActionEnum boatAction; */
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() {
}
@Override
public AC35Data decode(byte[] encodedMessage) {
this.encodedMessage = encodedMessage;
BoatActionEnum boatActionEnum = BoatActionEnum.fromByte(encodedMessage[0]);
message = new BoatAction(boatActionEnum);
return message;
} }
public BoatActionEnum getBoatAction() {
return boatAction; /**
* Returns the decoded message.
* @return The decoded message.
*/
public BoatAction getMessage() {
return message;
} }
} }

@ -1,6 +1,7 @@
package network.MessageDecoders; package network.MessageDecoders;
import network.Messages.AC35Data;
import network.Messages.BoatLocation; import network.Messages.BoatLocation;
import network.Messages.Enums.BoatLocationDeviceEnum; import network.Messages.Enums.BoatLocationDeviceEnum;
import shared.model.Azimuth; import shared.model.Azimuth;
@ -15,7 +16,7 @@ import static network.Utils.ByteConverter.*;
/** /**
* Decodes {@link BoatLocation} messages. * Decodes {@link BoatLocation} messages.
*/ */
public class BoatLocationDecoder { public class BoatLocationDecoder implements MessageDecoder {
/** /**
* The encoded message. * The encoded message.
@ -32,20 +33,14 @@ public class BoatLocationDecoder {
/** /**
* Constructs a decoder to decode a given message. * Constructs a decoder to decode a given message.
* @param encodedMessage The message to decode.
*/ */
public BoatLocationDecoder(byte[] encodedMessage) { public BoatLocationDecoder() {
this.encodedMessage = encodedMessage;
decode();
} }
/** @Override
* Decodes the contained message. public AC35Data decode(byte[] encodedMessage) {
*/ this.encodedMessage = encodedMessage;
private void decode() {
byte[] messageVersionNumberBytes = Arrays.copyOfRange(encodedMessage, 0, 1); byte[] messageVersionNumberBytes = Arrays.copyOfRange(encodedMessage, 0, 1);
byte messageVersionNumber = messageVersionNumberBytes[0]; byte messageVersionNumber = messageVersionNumberBytes[0];
@ -152,6 +147,8 @@ public class BoatLocationDecoder {
currentSet, currentSet,
rudderAngle ); rudderAngle );
return message;
} }

@ -30,19 +30,17 @@ public class BoatStatusDecoder {
/** /**
* Constructs a decoder to decode a given message. * Constructs a decoder to decode a given message.
* @param encodedMessage The message to decode.
*/ */
public BoatStatusDecoder(byte[] encodedMessage) { public BoatStatusDecoder() {
this.encodedMessage = encodedMessage;
decode();
} }
/** /**
* Decodes the contained message. * Decodes the contained message.
* @param encodedMessage The message to decode.
*/ */
private void decode() { public BoatStatus decode(byte[] encodedMessage) {
this.encodedMessage = encodedMessage;
byte[] sourceIDBytes = Arrays.copyOfRange(encodedMessage, 0, 4); byte[] sourceIDBytes = Arrays.copyOfRange(encodedMessage, 0, 4);
@ -75,6 +73,8 @@ public class BoatStatusDecoder {
estTimeAtNextMark, estTimeAtNextMark,
estTimeAtFinish ); estTimeAtFinish );
return message;
} }

@ -2,63 +2,93 @@ package network.MessageDecoders;
import network.Messages.CourseWind; import network.Messages.CourseWind;
import shared.model.Bearing;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import static network.Utils.ByteConverter.bytesToInt; import static network.Utils.AC35UnitConverter.*;
import static network.Utils.ByteConverter.bytesToLong; import static network.Utils.ByteConverter.*;
/** /**
* Created by hba56 on 23/04/17. * Decodes {@link CourseWind} messages.
*/ */
public class CourseWindDecoder { 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.
*/
public CourseWind decode(byte[] encodedMessage) {
this.encodedMessage = encodedMessage;
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;
} }
public byte getByteWindID() {
return byteWindID; /**
* Returns the decoded message.
* @return The decoded message.
*/
public CourseWind getMessage() {
return message;
} }
} }

@ -0,0 +1,87 @@
package network.MessageDecoders;
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) {
this.encodedMessage = encodedMessage;
//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;
}
/**
* 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();//TODO
//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()//TODO;
case COURSEWIND: return new CourseWindsDecoder();
//case AVGWIND: return new AverageWindDecoder()//TODO;
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);
}
}
}

@ -1,5 +1,6 @@
package network.MessageDecoders; package network.MessageDecoders;
import network.Messages.AC35Data;
import network.Messages.Enums.BoatActionEnum; import network.Messages.Enums.BoatActionEnum;
import network.Messages.HeartBeat; import network.Messages.HeartBeat;
@ -8,7 +9,7 @@ import static network.Utils.ByteConverter.bytesToLong;
/** /**
* Decodes {@link network.Messages.HeartBeat} messages. * Decodes {@link network.Messages.HeartBeat} messages.
*/ */
public class HeartBeatDecoder { public class HeartBeatDecoder implements MessageDecoder {
/** /**
* The encoded message. * The encoded message.
@ -24,21 +25,17 @@ public class HeartBeatDecoder {
/** /**
* Constructs a decoder to decode a given message. * Constructs a decoder to decode a given message.
* @param encodedMessage The message to decode.
*/ */
public HeartBeatDecoder(byte[] encodedMessage) { public HeartBeatDecoder() {
this.encodedMessage = encodedMessage;
decode();
} }
@Override
/** public AC35Data decode(byte[] encodedMessage) {
* Decodes the contained message. this.encodedMessage = encodedMessage;
*/
private void decode() {
message = new HeartBeat(bytesToLong(encodedMessage)); message = new HeartBeat(bytesToLong(encodedMessage));
return message;
} }

@ -1,6 +1,7 @@
package network.MessageDecoders; package network.MessageDecoders;
import network.Messages.AC35Data;
import network.Messages.Enums.JoinAcceptanceEnum; import network.Messages.Enums.JoinAcceptanceEnum;
import network.Messages.JoinAcceptance; import network.Messages.JoinAcceptance;
import network.Utils.ByteConverter; import network.Utils.ByteConverter;
@ -10,7 +11,7 @@ import java.util.Arrays;
/** /**
* Decoder for {@link JoinAcceptance} messages. * Decoder for {@link JoinAcceptance} messages.
*/ */
public class JoinAcceptanceDecoder { public class JoinAcceptanceDecoder implements MessageDecoder {
/** /**
* The encoded message. * The encoded message.
@ -25,19 +26,14 @@ public class JoinAcceptanceDecoder {
/** /**
* Constructs a decoder to decode a given message. * Constructs a decoder to decode a given message.
* @param encodedMessage The message to decode.
*/ */
public JoinAcceptanceDecoder(byte[] encodedMessage) { public JoinAcceptanceDecoder() {
this.encodedMessage = encodedMessage;
decode();
} }
/** @Override
* Decodes the contained message. public AC35Data decode(byte[] encodedMessage) {
*/ this.encodedMessage = encodedMessage;
private void decode() {
//SourceID is first four bytes. //SourceID is first four bytes.
byte[] sourceIdBytes = Arrays.copyOfRange(encodedMessage, 0, 4); byte[] sourceIdBytes = Arrays.copyOfRange(encodedMessage, 0, 4);
@ -55,6 +51,8 @@ public class JoinAcceptanceDecoder {
message = new JoinAcceptance(acceptanceType, sourceID); message = new JoinAcceptance(acceptanceType, sourceID);
return message;
} }

@ -0,0 +1,21 @@
package network.MessageDecoders;
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.
*/
public AC35Data decode(byte[] encodedMessage);
}

@ -1,6 +1,7 @@
package network.MessageDecoders; package network.MessageDecoders;
import network.Messages.AC35Data;
import network.Messages.BoatStatus; import network.Messages.BoatStatus;
import network.Messages.Enums.RaceStatusEnum; import network.Messages.Enums.RaceStatusEnum;
import network.Messages.Enums.RaceTypeEnum; import network.Messages.Enums.RaceTypeEnum;
@ -20,7 +21,7 @@ import static network.Utils.ByteConverter.bytesToShort;
/** /**
* Decodes {@link RaceStatus} messages. * Decodes {@link RaceStatus} messages.
*/ */
public class RaceStatusDecoder { public class RaceStatusDecoder implements MessageDecoder {
/** /**
* The encoded message. * The encoded message.
@ -36,19 +37,14 @@ public class RaceStatusDecoder {
/** /**
* Constructs a decoder to decode a given message. * Constructs a decoder to decode a given message.
* @param encodedMessage The message to decode.
*/ */
public RaceStatusDecoder(byte[] encodedMessage) { public RaceStatusDecoder() {
this.encodedMessage = encodedMessage;
decode();
} }
/** @Override
* Decodes the contained message. public AC35Data decode(byte[] encodedMessage) {
*/ this.encodedMessage = encodedMessage;
private void decode() {
byte[] versionNumBytes = Arrays.copyOfRange(encodedMessage, 0, 1); byte[] versionNumBytes = Arrays.copyOfRange(encodedMessage, 0, 1);
@ -84,15 +80,18 @@ public class RaceStatusDecoder {
List<BoatStatus> boatStatuses = new ArrayList<>(); List<BoatStatus> boatStatuses = new ArrayList<>();
//BoatStatus is 20 bytes.
int boatStatusByteLength = 20;
//Decode each BoatStatus. //Decode each BoatStatus.
for (int boatLoopIndex=0; boatLoopIndex < (numberOfBoats * 20); boatLoopIndex += 20) { for (int boatLoopIndex = 0; boatLoopIndex < (numberOfBoats * boatStatusByteLength); boatLoopIndex += boatStatusByteLength) {
byte[] boatStatusBytes = Arrays.copyOfRange(boatStatusesBytes, boatLoopIndex, boatLoopIndex + 20); byte[] boatStatusBytes = Arrays.copyOfRange(boatStatusesBytes, boatLoopIndex, boatLoopIndex + boatStatusByteLength);
BoatStatusDecoder boatStatusDecoder = new BoatStatusDecoder(boatStatusBytes); BoatStatusDecoder boatStatusDecoder = new BoatStatusDecoder();
boatStatuses.add(boatStatusDecoder.getMessage()); boatStatuses.add(boatStatusDecoder.decode(boatStatusBytes));
} }
@ -106,6 +105,8 @@ public class RaceStatusDecoder {
windSpeedKnots, windSpeedKnots,
raceType, raceType,
boatStatuses ); boatStatuses );
return message;
} }

@ -1,6 +1,7 @@
package network.MessageDecoders; package network.MessageDecoders;
import network.Messages.AC35Data;
import network.Messages.Enums.RequestToJoinEnum; import network.Messages.Enums.RequestToJoinEnum;
import network.Messages.RequestToJoin; import network.Messages.RequestToJoin;
import network.Utils.ByteConverter; import network.Utils.ByteConverter;
@ -10,7 +11,7 @@ import java.util.Arrays;
/** /**
* Decoder for {@link network.Messages.RequestToJoin} messages. * Decoder for {@link network.Messages.RequestToJoin} messages.
*/ */
public class RequestToJoinDecoder { public class RequestToJoinDecoder implements MessageDecoder{
/** /**
* The encoded message. * The encoded message.
@ -24,19 +25,14 @@ public class RequestToJoinDecoder {
/** /**
* Constructs a decoder to decode a given message. * Constructs a decoder to decode a given message.
* @param encodedRequest The message to decode.
*/ */
public RequestToJoinDecoder(byte[] encodedRequest) { public RequestToJoinDecoder() {
this.encodedRequest = encodedRequest;
decode();
} }
/** @Override
* Decodes the contained message. public AC35Data decode(byte[] encodedRequest) {
*/ this.encodedRequest = encodedRequest;
private void decode() {
//Request type is first four bytes. //Request type is first four bytes.
byte[] requestTypeBytes = Arrays.copyOfRange(encodedRequest, 0, 4); byte[] requestTypeBytes = Arrays.copyOfRange(encodedRequest, 0, 4);
@ -47,6 +43,8 @@ public class RequestToJoinDecoder {
message = new RequestToJoin(requestType); message = new RequestToJoin(requestType);
return message;
} }

@ -1,5 +1,6 @@
package network.MessageDecoders; package network.MessageDecoders;
import network.Messages.AC35Data;
import network.Messages.Enums.XMLMessageType; import network.Messages.Enums.XMLMessageType;
import network.Messages.XMLMessage; import network.Messages.XMLMessage;
@ -12,7 +13,7 @@ import static network.Utils.ByteConverter.bytesToShort;
/** /**
* Decodes {@link network.Messages.XMLMessage} messages. * Decodes {@link network.Messages.XMLMessage} messages.
*/ */
public class XMLMessageDecoder { public class XMLMessageDecoder implements MessageDecoder {
/** /**
* The encoded message. * The encoded message.
@ -28,18 +29,14 @@ public class XMLMessageDecoder {
/** /**
* Constructs a decoder to decode a given message. * Constructs a decoder to decode a given message.
* @param encodedMessage The message to decode.
*/ */
public XMLMessageDecoder(byte[] encodedMessage) { public XMLMessageDecoder() {
this.encodedMessage = encodedMessage;
decode();
} }
/**
* Decodes the contained message. @Override
*/ public AC35Data decode(byte[] encodedMessage) {
private void decode() { this.encodedMessage = encodedMessage;
byte[] messageVersionNumberBytes = Arrays.copyOfRange(encodedMessage, 0, 1); byte[] messageVersionNumberBytes = Arrays.copyOfRange(encodedMessage, 0, 1);
byte[] ackNumberBytes = Arrays.copyOfRange(encodedMessage, 1, 3); byte[] ackNumberBytes = Arrays.copyOfRange(encodedMessage, 1, 3);
@ -66,6 +63,8 @@ public class XMLMessageDecoder {
xmlMsgSubType, xmlMsgSubType,
sequenceNumber, sequenceNumber,
xmlMessage ); xmlMessage );
return message;
} }

@ -30,7 +30,7 @@ public class BoatActionEncoder implements MessageEncoder {
//Message is 1 byte. //Message is 1 byte.
ByteBuffer boatActionMessage = ByteBuffer.allocate(1); ByteBuffer boatActionMessage = ByteBuffer.allocate(1);
boatActionMessage.put(intToBytes(boatAction.getBoatAction(), 1)); boatActionMessage.put(intToBytes(boatAction.getBoatAction().getValue(), 1));
byte [] result = boatActionMessage.array(); byte [] result = boatActionMessage.array();

@ -0,0 +1,73 @@
package network.MessageEncoders;
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.
*/
public byte[] encode(CourseWind message) {
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();
}
}

@ -0,0 +1,57 @@
package network.MessageEncoders;
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) {
//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();
}
}

@ -49,7 +49,7 @@ public class EncoderFactory {
//case MARKROUNDING: return new MarkRoundingEncoder();//TODO //case MARKROUNDING: return new MarkRoundingEncoder();//TODO
//case COURSEWIND: return new CourseWindEncoder();//TODO case COURSEWIND: return new CourseWindsEncoder();
//case AVGWIND: return new AverageWindEncoder();//TODO //case AVGWIND: return new AverageWindEncoder();//TODO

@ -143,26 +143,7 @@ public class RaceVisionByteEncoder {
return result.array(); return result.array();
} }
public static byte[] courseWind(byte windID, ArrayList<CourseWind> courseWinds){
int messageVersionNumber = 0b1;
byte byteWindID = windID;
byte[] loopcount = intToBytes(courseWinds.size(), 1);
ByteBuffer result = ByteBuffer.allocate(3 + 20 * courseWinds.size());
result.put(intToBytes(messageVersionNumber, 1));
result.put(byteWindID);
result.put(loopcount);
for (CourseWind wind: courseWinds){
result.put(intToBytes(wind.getID(), 1));
result.put(longToBytes(wind.getTime(), 6));
result.put(intToBytes(wind.getRaceID(), 4));
result.put(intToBytes(wind.getWindDirection(), 2));
result.put(intToBytes(wind.getWindSpeed(), 2));
result.put(intToBytes(wind.getBestUpwindAngle(), 2));
result.put(intToBytes(wind.getBestDownwindAngle(), 2));
result.put(intToBytes(wind.getFlags(), 1));
}
return result.array();
}
public static byte[] averageWind(int time, int rawPeriod, int rawSampleSpeed, int period2, int speed2, int period3, int speed3, int period4, int speed4){ public static byte[] averageWind(int time, int rawPeriod, int rawSampleSpeed, int period2, int speed2, int period3, int speed3, int period4, int speed4){
int messageVersionNumber = 0b1; int messageVersionNumber = 0b1;

@ -4,19 +4,30 @@ import network.Messages.Enums.BoatActionEnum;
import network.Messages.Enums.MessageType; import network.Messages.Enums.MessageType;
/** /**
* Created by David on 10/07/2017. * Represents a BoatAction message.
*/ */
public class BoatAction extends AC35Data { public class BoatAction extends AC35Data {
private byte boatAction; /**
* The action for this message.
*/
private BoatActionEnum boatAction;
/**
* Constructs a BoatActon message with a given action.
* @param boatAction Action to use.
*/
public BoatAction(BoatActionEnum boatAction){ public BoatAction(BoatActionEnum boatAction){
super(MessageType.BOATACTION); super(MessageType.BOATACTION);
this.boatAction = boatAction.getValue(); this.boatAction = boatAction;
} }
public byte getBoatAction() { /**
* Returns the action for this message.
* @return The action for this message.
*/
public BoatActionEnum getBoatAction() {
return boatAction; return boatAction;
} }
} }

@ -2,57 +2,132 @@ package network.Messages;
import network.Messages.Enums.MessageType; import network.Messages.Enums.MessageType;
import shared.model.Bearing;
/** /**
* Created by fwy13 on 21/04/17. * Contains a single CourseWind record.
* A CourseWinds message contains one or more CourseWind messages.
*/ */
public class CourseWind extends AC35Data { public class CourseWind extends AC35Data {
private int ID, raceID, windDirection, windSpeed, bestUpwindAngle, bestDownwindAngle, flags; /**
* The ID for this wind source.
*/
private int ID;
/**
* The time the wind was captured at. Milliseconds since unix epoch.
*/
private long time; private long time;
public CourseWind(int ID, long time, int raceID, int windDirection, int windSpeed, int bestUpwindAngle, int bestDownwindAngle, /**
int flags){ * The ID of the race this applies to.
* 0 means it isn't race specific.
*/
private int raceID;
/**
* Direction of the wind.
*/
private Bearing windDirection;
/**
* The speed of the wind, in knots.
*/
private double windSpeedKnots;
/**
* Optimum upwind sailing angle.
*/
private Bearing bestUpwindAngle;
/**
* Optimum downwind sailing angle.
*/
private Bearing bestDownwindAngle;
/**
* Various flags which determine which values are valid.
*/
private short flags;
public CourseWind(int ID, long time, int raceID, Bearing windDirection, double windSpeedKnots, Bearing bestUpwindAngle, Bearing bestDownwindAngle, short flags) {
super(MessageType.COURSEWIND); super(MessageType.COURSEWIND);
this.ID = ID; this.ID = ID;
this.time = time; this.time = time;
this.raceID = raceID; this.raceID = raceID;
this.windDirection = windDirection; this.windDirection = windDirection;
this.windSpeed = windSpeed; this.windSpeedKnots = windSpeedKnots;
this.bestUpwindAngle = bestUpwindAngle; this.bestUpwindAngle = bestUpwindAngle;
this.bestDownwindAngle = bestDownwindAngle; this.bestDownwindAngle = bestDownwindAngle;
this.flags = flags; this.flags = flags;
} }
/**
* Returns the ID of the wind source.
* @return ID of the wind source.
*/
public int getID() { public int getID() {
return ID; return ID;
} }
/**
* Returns the time that this was captured at. Milliseconds since unix epoch.
* @return Time this wind was captured at.
*/
public long getTime() {
return time;
}
/**
* Returns the ID of the race this wind source belongs to. 0 means any race.
* @return ID of the race this belongs to.
*/
public int getRaceID() { public int getRaceID() {
return raceID; return raceID;
} }
public int getWindDirection() { /**
* Returns the direction of the wind.
* @return The direction of the wind.
*/
public Bearing getWindDirection() {
return windDirection; return windDirection;
} }
public int getWindSpeed() { /**
return windSpeed; * Returns the wind speed, in knots.
* @return Wind speed, in knots.
*/
public double getWindSpeedKnots() {
return windSpeedKnots;
} }
public int getBestUpwindAngle() { /**
* Returns the best upwind sailing angle.
* @return Best upwind sailing angle.
*/
public Bearing getBestUpwindAngle() {
return bestUpwindAngle; return bestUpwindAngle;
} }
public int getBestDownwindAngle() { /**
* Returns the best downwind sailing angle.
* @return The best downwind sailing angle.
*/
public Bearing getBestDownwindAngle() {
return bestDownwindAngle; return bestDownwindAngle;
} }
/**
* Returns various flags which determine which values are valid.
* @return Flag which determines which values are valid.
*/
public int getFlags() { public int getFlags() {
return flags; return flags;
} }
public long getTime() {
return time;
}
} }

@ -6,19 +6,67 @@ import network.Messages.Enums.MessageType;
import java.util.List; import java.util.List;
/** /**
* Created by fwy13 on 25/04/17. * Represents the information in a CourseWind message (AC streaming spec: 4.11).
*/ */
public class CourseWinds extends AC35Data { public class CourseWinds extends AC35Data {
private int msgVerNum; /**
private int selectedWindID; * The current version number for this message type.
*/
public static final byte currentMessageVersionNumber = 1;
/**
* The version number of this message.
*/
private byte messageVersionNumber;
/**
* The ID of the wind source currently selected.
*/
private byte selectedWindID;
/**
* A list of wind sources.
*/
private List<CourseWind> courseWinds; private List<CourseWind> courseWinds;
public CourseWinds(int msgVerNum, int selectedWindID, List<CourseWind> courseWinds){
/**
* Constructs a CourseWinds with given parameters.
* @param messageVersionNumber The version number of the message.
* @param selectedWindID The selected wind ID.
* @param courseWinds A list of wind sources.
*/
public CourseWinds(byte messageVersionNumber, byte selectedWindID, List<CourseWind> courseWinds) {
super(MessageType.COURSEWIND); super(MessageType.COURSEWIND);
this.msgVerNum = msgVerNum; this.messageVersionNumber = messageVersionNumber;
this.selectedWindID = selectedWindID; this.selectedWindID = selectedWindID;
this.courseWinds = courseWinds; this.courseWinds = courseWinds;
} }
/**
* Returns the version number of this message.
* @return Version number of this message.
*/
public byte getMessageVersionNumber() {
return messageVersionNumber;
}
/**
* Returns the ID of the selected wind source.
* @return ID of the selected wind source.
*/
public byte getSelectedWindID() {
return selectedWindID;
}
/**
* Returns the list of wind sources.
* @return List of wind sources.
*/
public List<CourseWind> getCourseWinds() {
return courseWinds;
}
} }

@ -2,6 +2,7 @@ package visualiser.gameController;
import network.BinaryMessageDecoder; import network.BinaryMessageDecoder;
import network.MessageDecoders.BoatActionDecoder; import network.MessageDecoders.BoatActionDecoder;
import network.Messages.BoatAction;
import network.Messages.Enums.BoatActionEnum; import network.Messages.Enums.BoatActionEnum;
import visualiser.gameController.Keys.ControlKey; import visualiser.gameController.Keys.ControlKey;
import visualiser.gameController.Keys.KeyFactory; import visualiser.gameController.Keys.KeyFactory;
@ -47,9 +48,10 @@ public class ControllerServer implements Runnable {
if (inputStream.available() > 0) { if (inputStream.available() > 0) {
inputStream.read(message); inputStream.read(message);
BinaryMessageDecoder encodedMessage = new BinaryMessageDecoder(message); BinaryMessageDecoder encodedMessage = new BinaryMessageDecoder(message);
BoatActionDecoder boatActionDecoder = new BoatActionDecoder(encodedMessage.getMessageBody()); BoatActionDecoder boatActionDecoder = new BoatActionDecoder();
BoatActionEnum decodedMessage = boatActionDecoder.getBoatAction(); boatActionDecoder.decode(encodedMessage.getMessageBody());
System.out.println("Received key: " + decodedMessage); BoatAction boatAction = boatActionDecoder.getMessage();
System.out.println("Received key: " + boatAction.getBoatAction());
} }
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();

@ -29,10 +29,10 @@ public class BoatActionDecoderTest {
byte [] testEncodedMessage = RaceVisionByteEncoder.encode(message); byte [] testEncodedMessage = RaceVisionByteEncoder.encode(message);
//Decode. //Decode.
BoatActionDecoder testDecoder = new BoatActionDecoder(testEncodedMessage); BoatActionDecoder testDecoder = new BoatActionDecoder();
BoatActionEnum decodedBoatAction = testDecoder.getBoatAction(); testDecoder.decode(testEncodedMessage);
BoatAction decodedMessage = new BoatAction(decodedBoatAction); BoatAction decodedMessage = testDecoder.getMessage();
return decodedMessage; return decodedMessage;
} }

@ -53,7 +53,8 @@ public class BoatLocationDecoderTest {
byte [] testEncodedMessage = RaceVisionByteEncoder.encode(testMessage); byte [] testEncodedMessage = RaceVisionByteEncoder.encode(testMessage);
//Decode. //Decode.
BoatLocationDecoder testDecoder = new BoatLocationDecoder(testEncodedMessage); BoatLocationDecoder testDecoder = new BoatLocationDecoder();
testDecoder.decode(testEncodedMessage);
BoatLocation decodedTest = testDecoder.getMessage(); BoatLocation decodedTest = testDecoder.getMessage();
//Check if valid. //Check if valid.

@ -59,12 +59,10 @@ public class BoatStatusDecoderTest {
private static BoatStatus encodeDecodeBoatStatus(BoatStatus boatStatus) { private static BoatStatus encodeDecodeBoatStatus(BoatStatus boatStatus) {
BoatStatusEncoder boatStatusEncoder = new BoatStatusEncoder(); BoatStatusEncoder boatStatusEncoder = new BoatStatusEncoder();
byte[] boatStatusEncoded = boatStatusEncoder.encode(boatStatus); byte[] boatStatusEncoded = boatStatusEncoder.encode(boatStatus);
BoatStatusDecoder boatStatusDecoder = new BoatStatusDecoder(boatStatusEncoded); BoatStatusDecoder boatStatusDecoder = new BoatStatusDecoder();
BoatStatus boatStatusDecoded = boatStatusDecoder.decode(boatStatusEncoded);
BoatStatus boatStatusDecoded = boatStatusDecoder.getMessage();
return boatStatusDecoded; return boatStatusDecoded;
} }

@ -1,57 +1,77 @@
package network.MessageDecoders; package network.MessageDecoders;
import network.MessageEncoders.RaceVisionByteEncoder; import network.MessageEncoders.CourseWindEncoder;
import network.Messages.BoatStatus;
import network.Messages.CourseWind; import network.Messages.CourseWind;
import network.Messages.Enums.BoatStatusEnum;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import shared.model.Bearing;
import java.util.ArrayList;
/** /**
* Created by hba56 on 23/04/17. * Test for the CourseWind encoder and decoder
*/ */
public class CourseWindDecoderTest { 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 @Test
public void getByteArrayTest(){ public void courseWindEncodeDecodeTest() throws Exception {
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
CourseWind testCourseWind1 = new CourseWind(1, time, 2, CourseWind courseWind = new CourseWind(
3, 4, 5, 1,
7, 6); 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>(); CourseWind courseWindDecoded = encodeDecodeCourseWind(courseWind);
testCourseWinds.add(testCourseWind1);
testCourseWinds.add(testCourseWind2);
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.
*/
private static CourseWind encodeDecodeCourseWind(CourseWind courseWind) {
ArrayList<CourseWind> testDecodedCourseWinds = testDecoder.getLoopMessages(); CourseWindEncoder courseWindEncoder = new CourseWindEncoder();
byte[] courseWindEncoded = courseWindEncoder.encode(courseWind);
Assert.assertEquals(testCourseWinds.get(0).getID(), testDecodedCourseWinds.get(0).getID()); CourseWindDecoder courseWindDecoder = new CourseWindDecoder();
Assert.assertEquals(testCourseWinds.get(0).getTime(), testDecodedCourseWinds.get(0).getTime()); CourseWind courseWindDecoded = courseWindDecoder.decode(courseWindEncoded);
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());
Assert.assertEquals(testCourseWinds.get(1).getID(), testDecodedCourseWinds.get(1).getID()); return courseWindDecoded;
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()); * Compares two CourseWind messages to check that they are equal.
Assert.assertEquals(testCourseWinds.get(1).getBestDownwindAngle(), testDecodedCourseWinds.get(1).getBestDownwindAngle()); * @param original The original CourseWind message.
Assert.assertEquals(testCourseWinds.get(1).getFlags(), testDecodedCourseWinds.get(1).getFlags()); * @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());
}
}
}

@ -28,7 +28,8 @@ public class HeartBeatDecoderTest {
byte [] testEncodedMessage = RaceVisionByteEncoder.encode(message); byte [] testEncodedMessage = RaceVisionByteEncoder.encode(message);
//Decode. //Decode.
HeartBeatDecoder testDecoder = new HeartBeatDecoder(testEncodedMessage); HeartBeatDecoder testDecoder = new HeartBeatDecoder();
testDecoder.decode(testEncodedMessage);
HeartBeat decodedMessage = testDecoder.getMessage(); HeartBeat decodedMessage = testDecoder.getMessage();
return decodedMessage; return decodedMessage;

@ -27,7 +27,8 @@ public class JoinAcceptanceDecoderTest {
byte [] testEncodedMessage = RaceVisionByteEncoder.encode(message); byte [] testEncodedMessage = RaceVisionByteEncoder.encode(message);
//Decode. //Decode.
JoinAcceptanceDecoder testDecoder = new JoinAcceptanceDecoder(testEncodedMessage); JoinAcceptanceDecoder testDecoder = new JoinAcceptanceDecoder();
testDecoder.decode(testEncodedMessage);
JoinAcceptance decodedMessage = testDecoder.getMessage(); JoinAcceptance decodedMessage = testDecoder.getMessage();
return decodedMessage; return decodedMessage;

@ -89,7 +89,8 @@ public class RaceStatusDecoderTest {
byte[] encodedRaceStatus = RaceVisionByteEncoder.encode(raceStatusOriginal); byte[] encodedRaceStatus = RaceVisionByteEncoder.encode(raceStatusOriginal);
RaceStatusDecoder decoderTest = new RaceStatusDecoder(encodedRaceStatus); RaceStatusDecoder decoderTest = new RaceStatusDecoder();
decoderTest.decode(encodedRaceStatus);
RaceStatus decodedMessage = decoderTest.getMessage(); RaceStatus decodedMessage = decoderTest.getMessage();

@ -28,7 +28,8 @@ public class RequestToJoinDecoderTest {
byte [] testEncodedMessage = RaceVisionByteEncoder.encode(message); byte [] testEncodedMessage = RaceVisionByteEncoder.encode(message);
//Decode. //Decode.
RequestToJoinDecoder testDecoder = new RequestToJoinDecoder(testEncodedMessage); RequestToJoinDecoder testDecoder = new RequestToJoinDecoder();
testDecoder.decode(testEncodedMessage);
RequestToJoin decodedMessage = testDecoder.getMessage(); RequestToJoin decodedMessage = testDecoder.getMessage();
return decodedMessage; return decodedMessage;

@ -45,7 +45,8 @@ public class XMLMessageDecoderTest {
XMLMessageDecoder decoderXML = new XMLMessageDecoder(encodedXML); XMLMessageDecoder decoderXML = new XMLMessageDecoder();
decoderXML.decode(encodedXML);
XMLMessage decodedMessage = decoderXML.getMessage(); XMLMessage decodedMessage = decoderXML.getMessage();

Loading…
Cancel
Save