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]
This commit is contained in:
fjc40
2017-08-06 18:51:13 +12:00
parent 1fbdd09d70
commit 750ea5c141
32 changed files with 840 additions and 234 deletions
@@ -1,20 +1,50 @@
package network.MessageDecoders;
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) {
this.encodedMessage = encodedMessage;
BoatActionEnum boatActionEnum = BoatActionEnum.fromByte(encodedMessage[0]);
message = new BoatAction(boatActionEnum);
return message;
}
}
/**
* Returns the decoded message.
* @return The decoded message.
*/
public BoatAction getMessage() {
return message;
}
}
@@ -1,6 +1,7 @@
package network.MessageDecoders;
import network.Messages.AC35Data;
import network.Messages.BoatLocation;
import network.Messages.Enums.BoatLocationDeviceEnum;
import shared.model.Azimuth;
@@ -15,7 +16,7 @@ import static network.Utils.ByteConverter.*;
/**
* Decodes {@link BoatLocation} messages.
*/
public class BoatLocationDecoder {
public class BoatLocationDecoder implements MessageDecoder {
/**
* The encoded message.
@@ -32,20 +33,14 @@ public class BoatLocationDecoder {
/**
* Constructs a decoder to decode a given message.
* @param encodedMessage The message to decode.
*/
public BoatLocationDecoder(byte[] encodedMessage) {
this.encodedMessage = encodedMessage;
decode();
public BoatLocationDecoder() {
}
/**
* Decodes the contained message.
*/
private void decode() {
@Override
public AC35Data decode(byte[] encodedMessage) {
this.encodedMessage = encodedMessage;
byte[] messageVersionNumberBytes = Arrays.copyOfRange(encodedMessage, 0, 1);
byte messageVersionNumber = messageVersionNumberBytes[0];
@@ -152,6 +147,8 @@ public class BoatLocationDecoder {
currentSet,
rudderAngle );
return message;
}
@@ -30,19 +30,17 @@ public class BoatStatusDecoder {
/**
* Constructs a decoder to decode a given message.
* @param encodedMessage The message to decode.
*/
public BoatStatusDecoder(byte[] encodedMessage) {
this.encodedMessage = encodedMessage;
decode();
public BoatStatusDecoder() {
}
/**
* 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);
@@ -75,6 +73,8 @@ public class BoatStatusDecoder {
estTimeAtNextMark,
estTimeAtFinish );
return message;
}
@@ -2,63 +2,93 @@ package network.MessageDecoders;
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;
/**
* The encoded message.
*/
private byte[] encodedMessage;
messageVersionNumber = encodedCourseWind[0];
byteWindID = encodedCourseWind[1];
loopCount = encodedCourseWind[2];
byte[] loopMessagesBytes = Arrays.copyOfRange(encodedCourseWind, 3, lengthInBytesOfMessages*loopCount+3);
int messageLoopIndex = 0;
/**
* The decoded message.
*/
private CourseWind message;
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;
}
/**
* Constructs a decoder to decode a given message.
*/
public CourseWindDecoder() {
}
public ArrayList<CourseWind> getLoopMessages() {
return loopMessages;
/**
* 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 getMessageVersionNumber() {
return messageVersionNumber;
/**
* Returns the decoded message.
* @return The decoded message.
*/
public CourseWind getMessage() {
return message;
}
public byte getByteWindID() {
return byteWindID;
}
}
@@ -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;
import network.Messages.AC35Data;
import network.Messages.Enums.BoatActionEnum;
import network.Messages.HeartBeat;
@@ -8,7 +9,7 @@ import static network.Utils.ByteConverter.bytesToLong;
/**
* Decodes {@link network.Messages.HeartBeat} messages.
*/
public class HeartBeatDecoder {
public class HeartBeatDecoder implements MessageDecoder {
/**
* The encoded message.
@@ -24,21 +25,17 @@ public class HeartBeatDecoder {
/**
* Constructs a decoder to decode a given message.
* @param encodedMessage The message to decode.
*/
public HeartBeatDecoder(byte[] encodedMessage) {
this.encodedMessage = encodedMessage;
decode();
public HeartBeatDecoder() {
}
/**
* Decodes the contained message.
*/
private void decode() {
@Override
public AC35Data decode(byte[] encodedMessage) {
this.encodedMessage = encodedMessage;
message = new HeartBeat(bytesToLong(encodedMessage));
return message;
}
@@ -1,6 +1,7 @@
package network.MessageDecoders;
import network.Messages.AC35Data;
import network.Messages.Enums.JoinAcceptanceEnum;
import network.Messages.JoinAcceptance;
import network.Utils.ByteConverter;
@@ -10,7 +11,7 @@ import java.util.Arrays;
/**
* Decoder for {@link JoinAcceptance} messages.
*/
public class JoinAcceptanceDecoder {
public class JoinAcceptanceDecoder implements MessageDecoder {
/**
* The encoded message.
@@ -25,19 +26,14 @@ public class JoinAcceptanceDecoder {
/**
* Constructs a decoder to decode a given message.
* @param encodedMessage The message to decode.
*/
public JoinAcceptanceDecoder(byte[] encodedMessage) {
this.encodedMessage = encodedMessage;
decode();
public JoinAcceptanceDecoder() {
}
/**
* Decodes the contained message.
*/
private void decode() {
@Override
public AC35Data decode(byte[] encodedMessage) {
this.encodedMessage = encodedMessage;
//SourceID is first four bytes.
byte[] sourceIdBytes = Arrays.copyOfRange(encodedMessage, 0, 4);
@@ -55,6 +51,8 @@ public class JoinAcceptanceDecoder {
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;
import network.Messages.AC35Data;
import network.Messages.BoatStatus;
import network.Messages.Enums.RaceStatusEnum;
import network.Messages.Enums.RaceTypeEnum;
@@ -20,7 +21,7 @@ import static network.Utils.ByteConverter.bytesToShort;
/**
* Decodes {@link RaceStatus} messages.
*/
public class RaceStatusDecoder {
public class RaceStatusDecoder implements MessageDecoder {
/**
* The encoded message.
@@ -36,19 +37,14 @@ public class RaceStatusDecoder {
/**
* Constructs a decoder to decode a given message.
* @param encodedMessage The message to decode.
*/
public RaceStatusDecoder(byte[] encodedMessage) {
this.encodedMessage = encodedMessage;
decode();
public RaceStatusDecoder() {
}
/**
* Decodes the contained message.
*/
private void decode() {
@Override
public AC35Data decode(byte[] encodedMessage) {
this.encodedMessage = encodedMessage;
byte[] versionNumBytes = Arrays.copyOfRange(encodedMessage, 0, 1);
@@ -84,15 +80,18 @@ public class RaceStatusDecoder {
List<BoatStatus> boatStatuses = new ArrayList<>();
//BoatStatus is 20 bytes.
int boatStatusByteLength = 20;
//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,
raceType,
boatStatuses );
return message;
}
@@ -1,6 +1,7 @@
package network.MessageDecoders;
import network.Messages.AC35Data;
import network.Messages.Enums.RequestToJoinEnum;
import network.Messages.RequestToJoin;
import network.Utils.ByteConverter;
@@ -10,7 +11,7 @@ import java.util.Arrays;
/**
* Decoder for {@link network.Messages.RequestToJoin} messages.
*/
public class RequestToJoinDecoder {
public class RequestToJoinDecoder implements MessageDecoder{
/**
* The encoded message.
@@ -24,19 +25,14 @@ public class RequestToJoinDecoder {
/**
* Constructs a decoder to decode a given message.
* @param encodedRequest The message to decode.
*/
public RequestToJoinDecoder(byte[] encodedRequest) {
this.encodedRequest = encodedRequest;
decode();
public RequestToJoinDecoder() {
}
/**
* Decodes the contained message.
*/
private void decode() {
@Override
public AC35Data decode(byte[] encodedRequest) {
this.encodedRequest = encodedRequest;
//Request type is first four bytes.
byte[] requestTypeBytes = Arrays.copyOfRange(encodedRequest, 0, 4);
@@ -47,6 +43,8 @@ public class RequestToJoinDecoder {
message = new RequestToJoin(requestType);
return message;
}
@@ -1,5 +1,6 @@
package network.MessageDecoders;
import network.Messages.AC35Data;
import network.Messages.Enums.XMLMessageType;
import network.Messages.XMLMessage;
@@ -12,7 +13,7 @@ import static network.Utils.ByteConverter.bytesToShort;
/**
* Decodes {@link network.Messages.XMLMessage} messages.
*/
public class XMLMessageDecoder {
public class XMLMessageDecoder implements MessageDecoder {
/**
* The encoded message.
@@ -28,18 +29,14 @@ public class XMLMessageDecoder {
/**
* Constructs a decoder to decode a given message.
* @param encodedMessage The message to decode.
*/
public XMLMessageDecoder(byte[] encodedMessage) {
this.encodedMessage = encodedMessage;
decode();
public XMLMessageDecoder() {
}
/**
* Decodes the contained message.
*/
private void decode() {
@Override
public AC35Data decode(byte[] encodedMessage) {
this.encodedMessage = encodedMessage;
byte[] messageVersionNumberBytes = Arrays.copyOfRange(encodedMessage, 0, 1);
byte[] ackNumberBytes = Arrays.copyOfRange(encodedMessage, 1, 3);
@@ -66,6 +63,8 @@ public class XMLMessageDecoder {
xmlMsgSubType,
sequenceNumber,
xmlMessage );
return message;
}