Did some large tidy ups, refactoring, and documenting.

Network:
Moved the Network message classes from the Utils package to the Messages package.
Renamed BoatLocationMessage to BoatLocation and BoatStatusMessage to BoatStatus to be consistent with other message classes.
Renamed the BoatStatus enumeration to BoatStatusEnum as it conflicted with BoatStatus (the message).
Moved the BoatStatusEnum and MessageType enumerations from the Utils package to the Messages/Enums package.
Changed the BoatStatusEnum and MessageType enumerations to use map look ups in the enum.fromByte(b) method - this means that there's less copy and pasted values, and the fromByte function doesn't need to be modified if new enumerations are added.
Added a sequenceNumber member to the Heartbeat class.
Added an InvalidMessageException in the package Networking/Exceptions. This is thrown when a message is read, but it is invalid in some way.
Refactored/tidied up the Networking/BinaryMessageEncoder and Decoder classes. The decoder throws InvalidMessageExceptions instead of returning null.

Visualiser:
VisualiserInput now wraps a DataInputStream around the socket. This provides the stream.readFully(buffer) function, which is a blocking read, removing the need for busy wait loops. Replaced the getBytes() function with getNextMessage() and getNextMessageBytes(). These read the next message from the socket, and return it as a message object and a byte array, respectively.
Changed the current heartbeat timeout to 10 seconds. Added some work-in-progress code to attempt to reconnect when connection is lost. It currently doesn't work. I think Fan-Wu was doing a proper implementation, however.
VisualiserInput also has a queue of received events. Currently not really used, but could be useful in the future. Events get added as they are read.
Changed VisualiserInputs main loop to use instanceOf instead of switch+case. Feedback wanted - are there any downsides to using this instanceOf method?

#story[778,782]
main
fjc40 9 years ago
parent 466e22437b
commit b247244665

@ -4,10 +4,10 @@ package seng302;
import seng302.Networking.BinaryMessageEncoder; import seng302.Networking.BinaryMessageEncoder;
import seng302.Networking.MessageEncoders.RaceVisionByteEncoder; import seng302.Networking.MessageEncoders.RaceVisionByteEncoder;
import seng302.Networking.MessageEncoders.XMLMessageEncoder; import seng302.Networking.MessageEncoders.XMLMessageEncoder;
import seng302.Networking.Utils.BoatLocationMessage; import seng302.Networking.Messages.BoatLocation;
import seng302.Networking.Utils.MessageType; import seng302.Networking.Messages.Enums.MessageType;
import seng302.Networking.Utils.RaceStatus; import seng302.Networking.Messages.RaceStatus;
import seng302.Networking.Utils.XMLMessage; import seng302.Networking.Messages.XMLMessage;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
@ -110,12 +110,12 @@ public class MockOutput implements Runnable
*/ */
public synchronized void parseBoatLocation(int sourceID, double lat, double lon, double heading, double speed){ public synchronized void parseBoatLocation(int sourceID, double lat, double lon, double heading, double speed){
BoatLocationMessage boatLocationMessage = new BoatLocationMessage(sourceID, lat, lon, boatLocationSequenceNumber, heading, speed); BoatLocation boatLocation = new BoatLocation(sourceID, lat, lon, boatLocationSequenceNumber, heading, speed);
//iterates the sequence number //iterates the sequence number
boatLocationSequenceNumber++; boatLocationSequenceNumber++;
//encodeds the messages //encodeds the messages
byte[] encodedBoatLoc = RaceVisionByteEncoder.boatLocation(boatLocationMessage); byte[] encodedBoatLoc = RaceVisionByteEncoder.boatLocation(boatLocation);
//encodeds the full message with header //encodeds the full message with header
BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder(MessageType.BOATLOCATION, System.currentTimeMillis(), messageNumber, (short)encodedBoatLoc.length, BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder(MessageType.BOATLOCATION, System.currentTimeMillis(), messageNumber, (short)encodedBoatLoc.length,

@ -7,13 +7,12 @@ import javafx.collections.ObservableList;
import org.geotools.referencing.GeodeticCalculator; import org.geotools.referencing.GeodeticCalculator;
import org.joda.time.tz.UTCProvider;
import seng302.Constants; import seng302.Constants;
import seng302.DataInput.RaceDataSource; import seng302.DataInput.RaceDataSource;
import seng302.MockOutput; import seng302.MockOutput;
import seng302.Networking.Utils.BoatStatusMessage; import seng302.Networking.Messages.BoatStatus;
import seng302.Networking.Utils.Enums.BoatStatus; import seng302.Networking.Messages.Enums.BoatStatusEnum;
import seng302.Networking.Utils.RaceStatus; import seng302.Networking.Messages.RaceStatus;
import java.awt.geom.Point2D; import java.awt.geom.Point2D;
import java.util.ArrayList; import java.util.ArrayList;
@ -122,20 +121,20 @@ public class Race implements Runnable {
@Override @Override
public void handle(long arg0) { public void handle(long arg0) {
timeLeft = startTime - currentTime; timeLeft = startTime - currentTime;
ArrayList<BoatStatusMessage> boatStatusMessages = new ArrayList<>(); ArrayList<BoatStatus> boatStatuses = new ArrayList<>();
//For each boat, we update it's position, and generate a BoatLocationMessage. //For each boat, we update it's position, and generate a BoatLocationMessage.
for (int i = 0; i < startingBoats.size(); i++) { for (int i = 0; i < startingBoats.size(); i++) {
Boat boat = startingBoats.get((i + boatOffset) % startingBoats.size()); Boat boat = startingBoats.get((i + boatOffset) % startingBoats.size());
if (boat != null) { if (boat != null) {
mockOutput.parseBoatLocation(boat.getSourceID(), boat.getCurrentPosition().getLatitude(), boat.getCurrentPosition().getLongitude(), boat.getHeading(), 0); mockOutput.parseBoatLocation(boat.getSourceID(), boat.getCurrentPosition().getLatitude(), boat.getCurrentPosition().getLongitude(), boat.getHeading(), 0);
boatStatusMessages.add(new BoatStatusMessage(boat.getSourceID(), boatStatuses.add(new BoatStatus(boat.getSourceID(),
boat.getCurrentLeg().getLegNumber() >= 0 ? BoatStatus.RACING : BoatStatus.DNF, boat.getCurrentLeg().getLegNumber())); boat.getCurrentLeg().getLegNumber() >= 0 ? BoatStatusEnum.RACING : BoatStatusEnum.DNF, boat.getCurrentLeg().getLegNumber()));
} }
} }
boatOffset = (boatOffset + 1) % (startingBoats.size()); boatOffset = (boatOffset + 1) % (startingBoats.size());
if (timeLeft <= 60000/scaleFactor && timeLeft > 0) { if (timeLeft <= 60000/scaleFactor && timeLeft > 0) {
RaceStatus raceStatus = new RaceStatus(System.currentTimeMillis(), raceId, 2, startTime, 0, 2300, 1, boatStatusMessages); RaceStatus raceStatus = new RaceStatus(System.currentTimeMillis(), raceId, 2, startTime, 0, 2300, 1, boatStatuses);
mockOutput.parseRaceStatus(raceStatus); mockOutput.parseRaceStatus(raceStatus);
} }
else if (timeLeft <= 0) { else if (timeLeft <= 0) {
@ -146,7 +145,7 @@ public class Race implements Runnable {
stop(); stop();
} }
else { else {
RaceStatus raceStatus = new RaceStatus(System.currentTimeMillis(), raceId, 1, startTime, 0, 2300,1, boatStatusMessages); RaceStatus raceStatus = new RaceStatus(System.currentTimeMillis(), raceId, 1, startTime, 0, 2300,1, boatStatuses);
mockOutput.parseRaceStatus(raceStatus); mockOutput.parseRaceStatus(raceStatus);
} }
currentTime = System.currentTimeMillis(); currentTime = System.currentTimeMillis();
@ -179,7 +178,7 @@ public class Race implements Runnable {
long currentTime = System.currentTimeMillis(); long currentTime = System.currentTimeMillis();
//Update the total elapsed time. //Update the total elapsed time.
totalTimeElapsed = currentTime - timeRaceStarted; totalTimeElapsed = currentTime - timeRaceStarted;
ArrayList<BoatStatusMessage> boatStatusMessages = new ArrayList<BoatStatusMessage>(); ArrayList<BoatStatus> boatStatuses = new ArrayList<BoatStatus>();
finished = 0; finished = 0;
//For each boat, we update it's position, and generate a BoatLocationMessage. //For each boat, we update it's position, and generate a BoatLocationMessage.
for (int i = 0; i < startingBoats.size(); i++) { for (int i = 0; i < startingBoats.size(); i++) {
@ -192,15 +191,15 @@ public class Race implements Runnable {
} }
if (boat.getTimeFinished() > 0) { if (boat.getTimeFinished() > 0) {
mockOutput.parseBoatLocation(boat.getSourceID(), boat.getCurrentPosition().getLatitude(), boat.getCurrentPosition().getLongitude(), boat.getHeading(), boat.getVelocity()); mockOutput.parseBoatLocation(boat.getSourceID(), boat.getCurrentPosition().getLatitude(), boat.getCurrentPosition().getLongitude(), boat.getHeading(), boat.getVelocity());
boatStatusMessages.add(new BoatStatusMessage(boat.getSourceID(), BoatStatus.FINISHED, boat.getCurrentLeg().getLegNumber())); boatStatuses.add(new BoatStatus(boat.getSourceID(), BoatStatusEnum.FINISHED, boat.getCurrentLeg().getLegNumber()));
finished++; finished++;
} else { } else {
mockOutput.parseBoatLocation(boat.getSourceID(), boat.getCurrentPosition().getLatitude(), boat.getCurrentPosition().getLongitude(), boat.getHeading(), boat.getVelocity()); mockOutput.parseBoatLocation(boat.getSourceID(), boat.getCurrentPosition().getLatitude(), boat.getCurrentPosition().getLongitude(), boat.getHeading(), boat.getVelocity());
boatStatusMessages.add(new BoatStatusMessage(boat.getSourceID(), boatStatuses.add(new BoatStatus(boat.getSourceID(),
boat.getCurrentLeg().getLegNumber() >= 0 ? BoatStatus.RACING : BoatStatus.DNF, boat.getCurrentLeg().getLegNumber())); boat.getCurrentLeg().getLegNumber() >= 0 ? BoatStatusEnum.RACING : BoatStatusEnum.DNF, boat.getCurrentLeg().getLegNumber()));
} }
if (startingBoats.size()==finished){ if (startingBoats.size()==finished){
RaceStatus raceStatus = new RaceStatus(System.currentTimeMillis(), raceId, 4, startTime, 0, 2300, 2, boatStatusMessages);//TODO FIX the second currentTime is a placeholder! Also, replace magic values. RaceStatus raceStatus = new RaceStatus(System.currentTimeMillis(), raceId, 4, startTime, 0, 2300, 2, boatStatuses);//TODO FIX the second currentTime is a placeholder! Also, replace magic values.
mockOutput.parseRaceStatus(raceStatus); mockOutput.parseRaceStatus(raceStatus);
} }
} else { } else {
@ -208,7 +207,7 @@ public class Race implements Runnable {
} }
} }
boatOffset = (boatOffset + 1) % (startingBoats.size()); boatOffset = (boatOffset + 1) % (startingBoats.size());
RaceStatus raceStatus = new RaceStatus(System.currentTimeMillis(), raceId, 3, startTime, 0, 2300, 2, boatStatusMessages);//TODO FIX the second currentTime is a placeholder! Also, replace magic values. RaceStatus raceStatus = new RaceStatus(System.currentTimeMillis(), raceId, 3, startTime, 0, 2300, 2, boatStatuses);//TODO FIX the second currentTime is a placeholder! Also, replace magic values.
mockOutput.parseRaceStatus(raceStatus); mockOutput.parseRaceStatus(raceStatus);
} }
} }

@ -1,167 +1,281 @@
package seng302.Networking; package seng302.Networking;
import seng302.Networking.Exceptions.InvalidMessageException;
import seng302.Networking.MessageDecoders.*; import seng302.Networking.MessageDecoders.*;
import seng302.Networking.Messages.*;
import seng302.Networking.Utils.*; import seng302.Networking.Utils.*;
import seng302.Networking.Messages.Enums.MessageType;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays; import java.util.Arrays;
import java.util.zip.CRC32; import java.util.zip.CRC32;
/** /**
* Created by hba56 on 21/04/17. * Created by hba56 on 21/04/17.
*/ */
/**
* This class can be used to decode/convert a byte array into a messageBody object, descended from AC35Data.
*/
public class BinaryMessageDecoder { public class BinaryMessageDecoder {
///Length of the header.
private static final int headerLength = 15;
///Length of the CRC.
private static final int CRCLength = 4;//TODO these should probably be static defined somewhere else to be shared.
///The value the first sync byte should have.
private static final byte syncByte1 = (byte) 0x47;
//The value the second sync byte should have.
private static final byte syncByte2 = (byte) 0x83;
///The full message.
private byte[] fullMessage; private byte[] fullMessage;
private byte[] header; ///The messageHeader.
private byte[] message; private byte[] messageHeader;
private byte[] crc; ///The messageBody.
private byte[] messageBody;
///The sync bytes from the header..
private byte headerSync1; private byte headerSync1;
private byte headerSync2; private byte headerSync2;
///The message type from the header.
private byte headerMessageType; private byte headerMessageType;
private byte[] headerTimeStamp;
private byte[] headerSourceID;
private byte[] headerMessageLength;
///The timestamp from the header.
private long headerTimeStamp;
///The source ID from the header.
private int headerSourceID;
///The message body length from the header.
private int messageBodyLength;
///CRC value read from message header.
private long messageCRCValue;
///Calculated CRC value from message.
private long calculatedCRCValue;
/**
* Ctor.
* @param fullMessage Entire encoded binary message.
*/
public BinaryMessageDecoder(byte[] fullMessage) { public BinaryMessageDecoder(byte[] fullMessage) {
this.fullMessage = fullMessage; this.fullMessage = fullMessage;
}
public AC35Data decode() throws IndexOutOfBoundsException{ //Get the messageHeader.
//get the header this.messageHeader = Arrays.copyOfRange(this.fullMessage, 0, 15);
this.header = Arrays.copyOfRange(this.fullMessage, 0, 15);
this.headerSync1 = this.header[0]; //Get the sync bytes.
this.headerSync2 = this.header[1]; this.headerSync1 = this.messageHeader[0];
this.headerMessageType = this.header[2]; this.headerSync2 = this.messageHeader[1];
this.headerTimeStamp = Arrays.copyOfRange(this.header, 3, 9);
this.headerSourceID = Arrays.copyOfRange(this.header, 9, 13);
this.headerMessageLength = Arrays.copyOfRange(this.header, 13, 15);
if (15 > this.fullMessage.length - 4){ //Get the message type.
//System.err.println("Message is too short."); this.headerMessageType = this.messageHeader[2];
return null;
}
//get message
this.message = Arrays.copyOfRange(this.fullMessage, 15, this.fullMessage.length - 4);
//get crc //Get the header timestamp.
this.crc = Arrays.copyOfRange(this.fullMessage, this.fullMessage.length - 4, fullMessage.length); this.headerTimeStamp = ByteConverter.bytesToLong(Arrays.copyOfRange(this.messageHeader, 3, 9));
ByteBuffer bytes = ByteBuffer.allocate(this.header.length + this.message.length);
bytes.put(this.header);
bytes.put(this.message);
//Get the source ID for the message.
this.headerSourceID = ByteConverter.bytesToInt(Arrays.copyOfRange(this.messageHeader, 9, 13));
//Get the length of the message body.
this.messageBodyLength = ByteConverter.bytesToInt(Arrays.copyOfRange(this.messageHeader, 13, 15));
//Get the messageBody.
this.messageBody = Arrays.copyOfRange(this.fullMessage, this.headerLength, this.headerLength + this.messageBodyLength);
//Get the CRC value.
this.messageCRCValue = ByteConverter.bytesToLong(Arrays.copyOfRange(this.fullMessage, this.fullMessage.length - CRCLength, this.fullMessage.length));
//Combine the header and body into a single array.
ByteBuffer headerBodyByteBuffer = ByteBuffer.allocate(messageHeader.length + messageBody.length);
headerBodyByteBuffer.put(messageHeader);
headerBodyByteBuffer.put(messageBody);
//Calculate the CRC value from the header+body array.
CRC32 crc = new CRC32(); CRC32 crc = new CRC32();
crc.reset(); crc.reset();
crc.update(bytes.array(), 0, bytes.array().length); crc.update(headerBodyByteBuffer.array());
this.calculatedCRCValue = crc.getValue();
//run through the checks }
if (this.message.length != ByteConverter.bytesToShort(this.headerMessageLength)){//keep like this - hba65
System.err.println("message length in header does not equal the message length");
System.err.println("message length in header: " + ByteConverter.bytesToInt(this.headerMessageLength)); /**
System.err.println("message length: " + this.message.length); * Decodes the byte array (binary message) this object was initialized with, and returns the corresponding message object.
return null; * @return Message object corresponding to the binary message.
}else if(this.headerSync1 != 0x47){ * @throws InvalidMessageException If the message cannot be decoded.
System.err.println("Sync byte 1 is wrong: " + this.headerSync1); */
return null; public AC35Data decode() throws InvalidMessageException {
}else if(this.headerSync2 !=(byte) 0x83){
System.err.println("Sync byte 2 is wrong: " + this.headerSync2); //Run through the checks to ensure that the message is valid.
return null; if (messageBody.length != messageBodyLength) {//keep like this - hba65
}else if(crc.getValue() != ByteConverter.bytesToLong(this.crc)){ //Check the message body length.
//todo check crc throw new InvalidMessageException("MessageBody length in header does not equal the messageBody length. MessageBody length in header is: " + messageBodyLength + ", should be: " + messageBody.length);
System.err.println("CRC is not "+ByteConverter.bytesToLong(this.crc)+" and is instead:" + crc.getValue());
return null; }else if (headerSync1 != syncByte1) {
//Check the first sync byte.
throw new InvalidMessageException("Sync byte 1 is wrong. Sync byte is: " + headerSync1 + ", should be: " + syncByte1);
}else if (headerSync2 != syncByte2) {
//Check the second sync byte.
throw new InvalidMessageException("Sync byte 2 is wrong. Sync byte is: " + headerSync2 + ", should be: " + syncByte2);
}else if (calculatedCRCValue != messageCRCValue) {
//Check the CRC value.
throw new InvalidMessageException("CRC value is wrong. The calculated value is: " + calculatedCRCValue + ", should be: " + messageCRCValue);
} }
MessageType mType = MessageType.valueOf(this.headerMessageType); //Now we create the message object based on what is actually in the message body.
AC35Data data = null; MessageType mType = MessageType.fromByte(headerMessageType);
switch(mType){ switch(mType) {
case HEARTBEAT: case HEARTBEAT:
// System.out.println("HeartBeat Message!"); //System.out.println("Decoding HeartBeat Message!");
data = new Heartbeat(); //TODO maybe use HeartbeatDecoder.decode(message).
break; //TODO also, decoders for each message type should encapsulate the constructing of the object. E.g., return HeartbeatDecoder.decode(message);.
return new Heartbeat(ByteConverter.bytesToLong(messageBody));
case RACESTATUS: case RACESTATUS:
// System.out.println("Race Status Message"); //System.out.println("Race Status Message");
RaceStatusDecoder rsdecoder = new RaceStatusDecoder(this.message); RaceStatusDecoder rsdecoder = new RaceStatusDecoder(messageBody);
data = new RaceStatus(rsdecoder.getTime(), rsdecoder.getRace(), rsdecoder.getRaceState(), rsdecoder.getStartTime(), rsdecoder.getRaceWindDir(), rsdecoder.getRaceWindSpeed(), rsdecoder.getRaceType(), rsdecoder.getBoats()); return new RaceStatus(rsdecoder.getTime(), rsdecoder.getRace(), rsdecoder.getRaceState(), rsdecoder.getStartTime(), rsdecoder.getRaceWindDir(), rsdecoder.getRaceWindSpeed(), rsdecoder.getRaceType(), rsdecoder.getBoats());
break;
case DISPLAYTEXTMESSAGE: case DISPLAYTEXTMESSAGE:
// System.out.println("Display Text Message"); //System.out.println("Display Text Message");
//no decoder for this. //No decoder for this.
break; throw new InvalidMessageException("Cannot decode DISPLAYTEXTMESSAGE - no decoder.");
case XMLMESSAGE: case XMLMESSAGE:
// System.out.println("XML Message!"); //System.out.println("XML Message!");
XMLMessageDecoder xmdecoder = new XMLMessageDecoder(this.message); XMLMessageDecoder xmdecoder = new XMLMessageDecoder(messageBody);
xmdecoder.decode(); xmdecoder.decode();
data = new XMLMessage(xmdecoder.getAckNumber(), xmdecoder.getTimeStamp(), xmdecoder.getXmlMsgSubType(), xmdecoder.getSequenceNumber(), xmdecoder.getXmlMsgLength(), xmdecoder.getXmlMessageInputStream()); return new XMLMessage(xmdecoder.getAckNumber(), xmdecoder.getTimeStamp(), xmdecoder.getXmlMsgSubType(), xmdecoder.getSequenceNumber(), xmdecoder.getXmlMsgLength(), xmdecoder.getXmlMessageInputStream());
break;
case RACESTARTSTATUS: case RACESTARTSTATUS:
// System.out.println("Race Start Status Message"); //System.out.println("Race Start Status Message");
RaceStartStatusDecoder rssDecoder = new RaceStartStatusDecoder(this.message); RaceStartStatusDecoder rssDecoder = new RaceStartStatusDecoder(messageBody);
data = new RaceStartStatus(rssDecoder.getTime(), rssDecoder.getAck(), rssDecoder.getStartTime(), rssDecoder.getRaceID(), rssDecoder. getNotification()); return new RaceStartStatus(rssDecoder.getTime(), rssDecoder.getAck(), rssDecoder.getStartTime(), rssDecoder.getRaceID(), rssDecoder. getNotification());
break;
case YACHTEVENTCODE: case YACHTEVENTCODE:
// System.out.println("Yacht Action Code!"); //System.out.println("Yacht Action Code!");
//no decoder //No decoder for this.
break; throw new InvalidMessageException("Cannot decode YACHTEVENTCODE - no decoder.");
case YACHTACTIONCODE: case YACHTACTIONCODE:
// System.out.println("Yacht Action Code!"); //System.out.println("Yacht Action Code!");
//no decoder //No decoder for this.
break; throw new InvalidMessageException("Cannot decode YACHTACTIONCODE - no decoder.");
case CHATTERTEXT: case CHATTERTEXT:
// System.out.println("Chatter Text Message!"); //System.out.println("Chatter Text Message!");
//no decoder //No decoder for this.
break; throw new InvalidMessageException("Cannot decode CHATTERTEXT - no decoder.");
case BOATLOCATION: case BOATLOCATION:
// System.out.println("Boat Location Message!"); //System.out.println("Boat Location Message!");
BoatLocationDecoder blDecoder = new BoatLocationDecoder(this.message); BoatLocationDecoder blDecoder = new BoatLocationDecoder(messageBody);
data = blDecoder.getMessage(); return blDecoder.getMessage();
break;
case MARKROUNDING: case MARKROUNDING:
// System.out.println("Mark Rounding Message!"); //System.out.println("Mark Rounding Message!");
MarkRoundingDecoder mrDecoder = new MarkRoundingDecoder(this.message); MarkRoundingDecoder mrDecoder = new MarkRoundingDecoder(messageBody);
data = mrDecoder.getMarkRounding(); return mrDecoder.getMarkRounding();
break;
case COURSEWIND: case COURSEWIND:
// System.out.println("Couse Wind Message!"); //System.out.println("Couse Wind Message!");
CourseWindDecoder cwDecoder = new CourseWindDecoder(this.message); CourseWindDecoder cwDecoder = new CourseWindDecoder(messageBody);
data =new CourseWinds(cwDecoder.getMessageVersionNumber(), cwDecoder.getByteWindID(), cwDecoder.getLoopMessages()); return new CourseWinds(cwDecoder.getMessageVersionNumber(), cwDecoder.getByteWindID(), cwDecoder.getLoopMessages());
break;
case AVGWIND: case AVGWIND:
// System.out.println("Average Wind Message!"); //System.out.println("Average Wind Message!");
AverageWindDecoder awDecoder = new AverageWindDecoder(this.message); AverageWindDecoder awDecoder = new AverageWindDecoder(messageBody);
data = awDecoder.getAverageWind(); return awDecoder.getAverageWind();
break;
default: default:
// System.out.println("Broken Message!"); //System.out.println("Broken Message!");
break; throw new InvalidMessageException("Broken message! Did not recognise message type: " + headerMessageType + ".");
} }
return data;
} }
public long getTimeStamp() { /**
return ByteConverter.bytesToLong(this.headerTimeStamp); * Returns the first sync byte value.
* @return The first sync byte value.
*/
public byte getHeaderSync1() {
return headerSync1;
}
/**
* Returns the second sync byte value.
* @return The second sync byte value.
*/
public byte getHeaderSync2() {
return headerSync2;
}
/**
* Returns the message type.
* @return The message type.
*/
public byte getHeaderMessageType() {
return headerMessageType;
}
/**
* Returns the header timestamp.
* @return The header timestamp.
*/
public long getHeaderTimeStamp() {
return headerTimeStamp;
} }
public int getSourceID() { /**
return ByteConverter.bytesToInt(this.headerSourceID, ByteOrder.BIG_ENDIAN); * Returns the header source ID.
* @return The header source ID.
*/
public int getHeaderSourceID() {
return headerSourceID;
} }
public short getMessageLength() { /**
return ByteConverter.bytesToShort(this.headerMessageLength); * Returns the message body length, according to the header.
* @return The message body length.
*/
public int getMessageBodyLength() {
return messageBodyLength;
} }
public int getMessageType(){ /**
return (int) this.headerMessageType; * Returns the message CRC value, according to the header.
* @return The message CRC value.
*/
public long getMessageCRCValue() {
return messageCRCValue;
} }
public byte[] getMessage() { /**
return message; * Returns the calculated CRC value from the message header + body contents.
* @return The calculated CRC value.
*/
public long getCalculatedCRCValue() {
return calculatedCRCValue;
}
/**
* Returns the message body.
* @return The message body.
*/
public byte[] getMessageBody() {
return messageBody;
} }
} }

@ -1,10 +1,9 @@
package seng302.Networking; package seng302.Networking;
import seng302.Networking.Utils.MessageType; import seng302.Networking.Messages.Enums.MessageType;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.Arrays;
import java.util.zip.CRC32; import java.util.zip.CRC32;
import static seng302.Networking.Utils.ByteConverter.intToBytes; import static seng302.Networking.Utils.ByteConverter.intToBytes;
@ -14,56 +13,96 @@ import static seng302.Networking.Utils.ByteConverter.shortToBytes;
/** /**
* Created by hba56 on 21/04/17. * Created by hba56 on 21/04/17.
*/ */
/**
* This class can be used to encode/convert a byte array message body, plus header data into a byte array containing the entire message, ready to send.
*/
public class BinaryMessageEncoder { public class BinaryMessageEncoder {
///Length of the header.
private static final int headerLength = 15;
///Length of the CRC.
private static final int CRCLength = 4;//TODO these should probably be static defined somewhere else to be shared.
///The full message.
private byte[] fullMessage; private byte[] fullMessage;
private byte[] header; ///The message header.
private byte[] message; private byte[] messageHeader;
//private byte[] crc; ///The message body.
private byte[] messageBody;
///First sync byte value.
private byte headerSync1 = (byte)0x47; private byte headerSync1 = (byte)0x47;
///Second sync byte value.
private byte headerSync2 = (byte)0x83; private byte headerSync2 = (byte)0x83;
///The message type to place in header.
private byte headerMessageType; private byte headerMessageType;
///The timestamp to place in header.
private long headerTimeStamp; private long headerTimeStamp;
///The source ID to place in header.
private int headerSourceID; private int headerSourceID;
private short headerMessageLength; ///The message length to place in header.
private short bodyMessageLength;
public BinaryMessageEncoder(MessageType headerMessageType, long headerTimeStamp, int headerSourceID, short headerMessageLength, byte[] message){ ///The calculated CRC value.
//set the header private long calculatedCRCValue;
/**
* Ctor. Constructs a encoder and encodes the full message. Retrieve it with encoder.getFullMessage().
* @param headerMessageType The message type to send.
* @param headerTimeStamp Timestamp of the message.
* @param headerSourceID Source ID of the message.
* @param bodyMessageLength The length of the body of the message.
* @param messageBody The body of the message (that is, the payload).
*/
public BinaryMessageEncoder(MessageType headerMessageType, long headerTimeStamp, int headerSourceID, short bodyMessageLength, byte[] messageBody) {
//Set the header parameters.
this.headerMessageType = headerMessageType.getValue(); this.headerMessageType = headerMessageType.getValue();
this.headerTimeStamp = headerTimeStamp; this.headerTimeStamp = headerTimeStamp;
this.headerSourceID = headerSourceID; this.headerSourceID = headerSourceID;
this.headerMessageLength = headerMessageLength; this.bodyMessageLength = bodyMessageLength;
ByteBuffer tempHeaderByteBuffer = ByteBuffer.allocate(15); //Place the header parameters into a buffer.
ByteBuffer tempHeaderByteBuffer = ByteBuffer.allocate(this.headerLength);
tempHeaderByteBuffer.put(this.headerSync1); tempHeaderByteBuffer.put(this.headerSync1);
tempHeaderByteBuffer.put(this.headerSync2); tempHeaderByteBuffer.put(this.headerSync2);
tempHeaderByteBuffer.put(this.headerMessageType); tempHeaderByteBuffer.put(this.headerMessageType);
tempHeaderByteBuffer.put(longToBytes(this.headerTimeStamp, 6)); tempHeaderByteBuffer.put(longToBytes(this.headerTimeStamp, 6));
tempHeaderByteBuffer.putInt(this.headerSourceID); tempHeaderByteBuffer.put(intToBytes(this.headerSourceID));
tempHeaderByteBuffer.put(shortToBytes(this.headerMessageLength)); tempHeaderByteBuffer.put(shortToBytes(this.bodyMessageLength));
this.messageHeader = tempHeaderByteBuffer.array();
this.header = tempHeaderByteBuffer.array(); //Set the message body.
this.messageBody = messageBody;
//set the message
this.message = message;
//set full message //Place header and body into a buffer.
ByteBuffer tempMessageByteBuffer = ByteBuffer.allocate(19+this.headerMessageLength); ByteBuffer tempHeaderBodyByteBuffer = ByteBuffer.allocate(this.messageHeader.length + this.bodyMessageLength);
tempMessageByteBuffer.put(this.header); tempHeaderBodyByteBuffer.put(this.messageHeader);
tempMessageByteBuffer.put(this.message); tempHeaderBodyByteBuffer.put(this.messageBody);
//Calculate the CRC from header + body.
CRC32 crc = new CRC32(); CRC32 crc = new CRC32();
crc.reset(); crc.reset();
byte[] messageAndHeader = new byte[this.header.length + this.message.length]; crc.update(tempHeaderBodyByteBuffer.array());
System.arraycopy(this.header, 0, messageAndHeader, 0, this.header.length); this.calculatedCRCValue = crc.getValue();
System.arraycopy(this.message, 0, messageAndHeader, this.header.length, this.message.length);
crc.update(messageAndHeader); //Place header, body, and CRC value in buffer.
ByteBuffer tempFullMessageByteBuffer = ByteBuffer.allocate(this.messageHeader.length + this.messageBody.length + this.CRCLength);
tempFullMessageByteBuffer.put(this.messageHeader);
tempFullMessageByteBuffer.put(this.messageBody);
tempFullMessageByteBuffer.put(intToBytes((int) this.calculatedCRCValue));
tempMessageByteBuffer.put(intToBytes((int) crc.getValue())); //Set the full message.
this.fullMessage = tempMessageByteBuffer.array(); this.fullMessage = tempFullMessageByteBuffer.array();
} }
/**
* Returns the full encoded message. This includes the header, body, and CRC.
* @return Full encoded message.
*/
public byte[] getFullMessage() { public byte[] getFullMessage() {
return fullMessage; return fullMessage;
} }

@ -0,0 +1,29 @@
package seng302.Networking.Exceptions;
/**
* Created by f123 on 07-May-17.
*/
/**
* Exception which is thrown when a message is read, but it is invalid in some way (CRC is wrong, sync bytes, etc...).
*/
public class InvalidMessageException extends Exception
{
/**
* Ctor.
* @param message String message.
*/
public InvalidMessageException(String message) {
super(message);
}
/**
* Ctor.
* @param message String message.
* @param cause Cause of the exception.
*/
public InvalidMessageException(String message, Throwable cause) {
super(message, cause);
}
}

@ -1,6 +1,6 @@
package seng302.Networking.MessageDecoders; package seng302.Networking.MessageDecoders;
import seng302.Networking.Utils.AverageWind; import seng302.Networking.Messages.AverageWind;
import seng302.Networking.Utils.ByteConverter; import seng302.Networking.Utils.ByteConverter;
import java.util.Arrays; import java.util.Arrays;

@ -1,9 +1,7 @@
package seng302.Networking.MessageDecoders; package seng302.Networking.MessageDecoders;
import seng302.Networking.Utils.BoatLocationMessage; import seng302.Networking.Messages.BoatLocation;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Arrays; import java.util.Arrays;
import static seng302.Networking.Utils.ByteConverter.*; import static seng302.Networking.Utils.ByteConverter.*;
@ -35,7 +33,7 @@ public class BoatLocationDecoder {
private byte[] currentSet; private byte[] currentSet;
private byte[] rudderAngle; private byte[] rudderAngle;
private BoatLocationMessage message; private BoatLocation message;
public BoatLocationDecoder(byte[] encodedBoatLocation) { public BoatLocationDecoder(byte[] encodedBoatLocation) {
messageVersionNumber = encodedBoatLocation[0]; messageVersionNumber = encodedBoatLocation[0];
@ -64,7 +62,7 @@ public class BoatLocationDecoder {
// System.out.println(bytesToInt(sourceID)); // System.out.println(bytesToInt(sourceID));
// System.out.println(bytesToInt(boatSpeed)); // System.out.println(bytesToInt(boatSpeed));
message = new BoatLocationMessage(messageVersionNumber, bytesToLong(time), message = new BoatLocation(messageVersionNumber, bytesToLong(time),
bytesToInt(sourceID), bytesToInt(seqNum), bytesToInt(sourceID), bytesToInt(seqNum),
deviceType, bytesToInt(latitude), deviceType, bytesToInt(latitude),
bytesToInt(longitude), bytesToInt(altitude), bytesToInt(longitude), bytesToInt(altitude),
@ -79,7 +77,7 @@ public class BoatLocationDecoder {
} }
public BoatLocationMessage getMessage() { public BoatLocation getMessage() {
return message; return message;
} }
} }

@ -1,9 +1,7 @@
package seng302.Networking.MessageDecoders; package seng302.Networking.MessageDecoders;
import seng302.Networking.Utils.CourseWind; import seng302.Networking.Messages.CourseWind;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;

@ -1,7 +1,7 @@
package seng302.Networking.MessageDecoders; package seng302.Networking.MessageDecoders;
import seng302.Networking.Utils.ByteConverter; import seng302.Networking.Utils.ByteConverter;
import seng302.Networking.Utils.MarkRounding; import seng302.Networking.Messages.MarkRounding;
import java.util.Arrays; import java.util.Arrays;

@ -1,6 +1,6 @@
package seng302.Networking.MessageDecoders; package seng302.Networking.MessageDecoders;
import seng302.Networking.Utils.BoatStatusMessage; import seng302.Networking.Messages.BoatStatus;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -30,7 +30,7 @@ public class RaceStatusDecoder {
private short raceWindSpeed; private short raceWindSpeed;
private int numberOfBoats; private int numberOfBoats;
private int raceType; private int raceType;
private ArrayList<BoatStatusMessage> boats = new ArrayList<>(); private ArrayList<BoatStatus> boats = new ArrayList<>();
public RaceStatusDecoder(byte[] encodedRaceStatus){ public RaceStatusDecoder(byte[] encodedRaceStatus){
@ -66,7 +66,7 @@ public class RaceStatusDecoder {
byte[] estTimeAtNextMark = Arrays.copyOfRange(boatBytes, 8, 14); byte[] estTimeAtNextMark = Arrays.copyOfRange(boatBytes, 8, 14);
byte[] estTimeAtFinish = Arrays.copyOfRange(boatBytes, 14, 20); byte[] estTimeAtFinish = Arrays.copyOfRange(boatBytes, 14, 20);
BoatStatusMessage boat = new BoatStatusMessage(bytesToInt(sourceID),boatStatus, BoatStatus boat = new BoatStatus(bytesToInt(sourceID),boatStatus,
legNumber, numPenaltiesAwarded, numPenaltiesServed, legNumber, numPenaltiesAwarded, numPenaltiesServed,
bytesToLong(estTimeAtNextMark), bytesToLong(estTimeAtFinish)); bytesToLong(estTimeAtNextMark), bytesToLong(estTimeAtFinish));
@ -111,7 +111,7 @@ public class RaceStatusDecoder {
return raceType; return raceType;
} }
public ArrayList<BoatStatusMessage> getBoats() { public ArrayList<BoatStatus> getBoats() {
return boats; return boats;
} }
} }

@ -1,7 +1,7 @@
package seng302.Networking.MessageEncoders; package seng302.Networking.MessageEncoders;
import seng302.Networking.Utils.*; import seng302.Networking.Messages.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.util.ArrayList; import java.util.ArrayList;
@ -32,9 +32,9 @@ public class RaceVisionByteEncoder {
*/ */
public static byte[] raceStatus(RaceStatus raceStatus){ public static byte[] raceStatus(RaceStatus raceStatus){
ArrayList<BoatStatusMessage> boatStatusMessages = raceStatus.getBoatStatusMessages(); ArrayList<BoatStatus> boatStatuses = raceStatus.getBoatStatuses();
ByteBuffer raceStatusMessage = ByteBuffer.allocate(24 + 20* boatStatusMessages.size()); ByteBuffer raceStatusMessage = ByteBuffer.allocate(24 + 20* boatStatuses.size());
//Version Number 1 bytes //Version Number 1 bytes
byte versionNum = 0b10; //this changes with the pdf. (2) byte versionNum = 0b10; //this changes with the pdf. (2)
byte[] timeBytes = longToBytes(raceStatus.getCurrentTime(), 6);//time (6 bytes) byte[] timeBytes = longToBytes(raceStatus.getCurrentTime(), 6);//time (6 bytes)
@ -43,7 +43,7 @@ public class RaceVisionByteEncoder {
byte[] expectedStart = longToBytes(raceStatus.getExpectedStartTime(), 6);//number of milliseconds from Jan 1, 1970 for when the data is valid byte[] expectedStart = longToBytes(raceStatus.getExpectedStartTime(), 6);//number of milliseconds from Jan 1, 1970 for when the data is valid
byte[] raceWind = ByteBuffer.allocate(2).put(intToBytes(raceStatus.getWindDirection(), 2)).array();//North = 0x0000 East = 0x4000 South = 0x8000. byte[] raceWind = ByteBuffer.allocate(2).put(intToBytes(raceStatus.getWindDirection(), 2)).array();//North = 0x0000 East = 0x4000 South = 0x8000.
byte[] windSpeed = ByteBuffer.allocate(2).put(intToBytes(raceStatus.getWindSpeed(), 2)).array();//mm/sec byte[] windSpeed = ByteBuffer.allocate(2).put(intToBytes(raceStatus.getWindSpeed(), 2)).array();//mm/sec
byte[] numBoats = intToBytes(boatStatusMessages.size(), 1); byte[] numBoats = intToBytes(boatStatuses.size(), 1);
byte[] bytesRaceType = intToBytes(raceStatus.getRaceType(), 1);//1 match race, 2 fleet race byte[] bytesRaceType = intToBytes(raceStatus.getRaceType(), 1);//1 match race, 2 fleet race
raceStatusMessage.put(versionNum); raceStatusMessage.put(versionNum);
@ -56,14 +56,14 @@ public class RaceVisionByteEncoder {
raceStatusMessage.put(numBoats); raceStatusMessage.put(numBoats);
raceStatusMessage.put(bytesRaceType); raceStatusMessage.put(bytesRaceType);
for (int i = 0; i < boatStatusMessages.size(); i++){ for (int i = 0; i < boatStatuses.size(); i++){
byte[] sourceID = intToBytes(boatStatusMessages.get(i).getSourceID()); byte[] sourceID = intToBytes(boatStatuses.get(i).getSourceID());
byte[] boatStatus = intToBytes(boatStatusMessages.get(i).getBoatStatus(), 1); byte[] boatStatus = intToBytes(boatStatuses.get(i).getBoatStatus(), 1);
byte[] legNum = intToBytes(boatStatusMessages.get(i).getLegNumber(), 1); byte[] legNum = intToBytes(boatStatuses.get(i).getLegNumber(), 1);
byte[] numPenalties = intToBytes(boatStatusMessages.get(i).getNumPenaltiesAwarded(), 1); byte[] numPenalties = intToBytes(boatStatuses.get(i).getNumPenaltiesAwarded(), 1);
byte[] numPenaltiesServed = intToBytes(boatStatusMessages.get(i).getNumPenaltiesServed(), 1); byte[] numPenaltiesServed = intToBytes(boatStatuses.get(i).getNumPenaltiesServed(), 1);
byte[] estNextMarkTime = longToBytes(boatStatusMessages.get(i).getEstTimeAtNextMark(), 6); byte[] estNextMarkTime = longToBytes(boatStatuses.get(i).getEstTimeAtNextMark(), 6);
byte[] estFinishTime = longToBytes( boatStatusMessages.get(i).getEstTimeAtFinish(), 6); byte[] estFinishTime = longToBytes( boatStatuses.get(i).getEstTimeAtFinish(), 6);
raceStatusMessage.put(sourceID); raceStatusMessage.put(sourceID);
raceStatusMessage.put(boatStatus); raceStatusMessage.put(boatStatus);
@ -173,29 +173,29 @@ public class RaceVisionByteEncoder {
return result.array(); return result.array();
} }
public static byte[] boatLocation(BoatLocationMessage boatLocationMessage){ public static byte[] boatLocation(BoatLocation boatLocation){
int messageVersionNumber = 0b1; int messageVersionNumber = 0b1;
byte[] time = longToBytes(boatLocationMessage.getTime(), 6); byte[] time = longToBytes(boatLocation.getTime(), 6);
byte[] sourceID = intToBytes(boatLocationMessage.getSourceID(), 4); byte[] sourceID = intToBytes(boatLocation.getSourceID(), 4);
byte[] seqNum = longToBytes(boatLocationMessage.getSequenceNumber(), 4); byte[] seqNum = longToBytes(boatLocation.getSequenceNumber(), 4);
byte[] deviceType = intToBytes(boatLocationMessage.getDeviceType(), 1); byte[] deviceType = intToBytes(boatLocation.getDeviceType(), 1);
byte[] latitude = intToBytes(boatLocationMessage.getLatitude(), 4); byte[] latitude = intToBytes(boatLocation.getLatitude(), 4);
byte[] longitude = intToBytes(boatLocationMessage.getLongitude(), 4); byte[] longitude = intToBytes(boatLocation.getLongitude(), 4);
byte[] altitude = intToBytes(boatLocationMessage.getAltitude(), 4); byte[] altitude = intToBytes(boatLocation.getAltitude(), 4);
byte[] heading = intToBytes(boatLocationMessage.getHeading(), 2); byte[] heading = intToBytes(boatLocation.getHeading(), 2);
byte[] pitch = intToBytes(boatLocationMessage.getPitch(), 2); byte[] pitch = intToBytes(boatLocation.getPitch(), 2);
byte[] roll = intToBytes(boatLocationMessage.getRoll(), 2); byte[] roll = intToBytes(boatLocation.getRoll(), 2);
byte[] boatSpeed = intToBytes(boatLocationMessage.getBoatSpeed(), 2); byte[] boatSpeed = intToBytes(boatLocation.getBoatSpeed(), 2);
byte[] cog = intToBytes(boatLocationMessage.getBoatCOG(), 2); byte[] cog = intToBytes(boatLocation.getBoatCOG(), 2);
byte[] sog = intToBytes(boatLocationMessage.getBoatSOG(), 2); byte[] sog = intToBytes(boatLocation.getBoatSOG(), 2);
byte[] apparentWindSpeed = intToBytes(boatLocationMessage.getApparentWindSpeed(), 2); byte[] apparentWindSpeed = intToBytes(boatLocation.getApparentWindSpeed(), 2);
byte[] apparentWindAngle = intToBytes(boatLocationMessage.getApparentWindAngle(), 2); byte[] apparentWindAngle = intToBytes(boatLocation.getApparentWindAngle(), 2);
byte[] trueWindSpeed = intToBytes(boatLocationMessage.getTrueWindSpeed(), 2); byte[] trueWindSpeed = intToBytes(boatLocation.getTrueWindSpeed(), 2);
byte[] trueWindDirection = intToBytes(boatLocationMessage.getTrueWindDirection(), 2); byte[] trueWindDirection = intToBytes(boatLocation.getTrueWindDirection(), 2);
byte[] trueWindAngle = intToBytes(boatLocationMessage.getTrueWindAngle(), 2); byte[] trueWindAngle = intToBytes(boatLocation.getTrueWindAngle(), 2);
byte[] currentDrift = intToBytes(boatLocationMessage.getCurrentDrift(), 2); byte[] currentDrift = intToBytes(boatLocation.getCurrentDrift(), 2);
byte[] currentSet = intToBytes(boatLocationMessage.getCurrentSet(), 2); byte[] currentSet = intToBytes(boatLocation.getCurrentSet(), 2);
byte[] rudderAngle = intToBytes(boatLocationMessage.getRudderAngle(), 2); byte[] rudderAngle = intToBytes(boatLocation.getRudderAngle(), 2);
ByteBuffer result = ByteBuffer.allocate(56); ByteBuffer result = ByteBuffer.allocate(56);
result.put(intToBytes(messageVersionNumber, 1)); result.put(intToBytes(messageVersionNumber, 1));

@ -0,0 +1,35 @@
package seng302.Networking.Messages;
/**
* Created by fwy13 on 25/04/17.
*/
import seng302.Networking.Messages.Enums.MessageType;
/**
* The base class for all message types.
*/
public abstract class AC35Data {
///Message type from the header.
private MessageType type;
/**
* Ctor.
* @param type The concrete type of this message.
*/
public AC35Data (MessageType type){
this.type = type;
}
/**
* The concrete type of message this is.
* @return The type of message this is.
*/
public MessageType getType() {
return type;
}
}

@ -1,9 +1,11 @@
package seng302.Networking.Utils; package seng302.Networking.Messages;
import seng302.Networking.Messages.Enums.MessageType;
/** /**
* Created by fwy13 on 25/04/17. * Created by fwy13 on 25/04/17.
*/ */
public class AverageWind extends AC35Data{ public class AverageWind extends AC35Data {
private int msgNum; private int msgNum;
private long lngTime; private long lngTime;

@ -1,10 +1,12 @@
package seng302.Networking.Utils; package seng302.Networking.Messages;
/** /**
* Created by f123 on 21-Apr-17. * Created by f123 on 21-Apr-17.
*/ */
import SharedModel.Constants; import SharedModel.Constants;
import seng302.Networking.Utils.AC35UnitConverter;
import seng302.Networking.Messages.Enums.MessageType;
import static seng302.Networking.Utils.AC35UnitConverter.convertGPS; import static seng302.Networking.Utils.AC35UnitConverter.convertGPS;
import static seng302.Networking.Utils.AC35UnitConverter.convertGPSToInt; import static seng302.Networking.Utils.AC35UnitConverter.convertGPSToInt;
@ -12,8 +14,8 @@ import static seng302.Networking.Utils.AC35UnitConverter.convertGPSToInt;
/** /**
* Represents the information in a boat location message (AC streaming spec: 4.9). * Represents the information in a boat location message (AC streaming spec: 4.9).
*/ */
public class BoatLocationMessage extends AC35Data public class BoatLocation extends AC35Data {
{
public static final byte Unknown = 0; public static final byte Unknown = 0;
public static final byte RacingYacht = 1; public static final byte RacingYacht = 1;
public static final byte CommitteeBoat = 2; public static final byte CommitteeBoat = 2;
@ -93,7 +95,7 @@ public class BoatLocationMessage extends AC35Data
/** /**
* Ctor. Default. * Ctor. Default.
*/ */
public BoatLocationMessage() { public BoatLocation() {
super(MessageType.BOATLOCATION); super(MessageType.BOATLOCATION);
} }
@ -123,7 +125,7 @@ public class BoatLocationMessage extends AC35Data
* @param currentSet current set * @param currentSet current set
* @param rudderAngle rudder angle * @param rudderAngle rudder angle
*/ */
public BoatLocationMessage(byte messageVersionNumber, long time, int sourceID, long sequenceNumber, byte deviceType, int latitude, int longitude, int altitude, int heading, short pitch, short roll, int boatSpeed, int boatCOG, int boatSOG, int apparentWindSpeed, short apparentWindAngle, int trueWindSpeed, int trueWindDirection, short trueWindAngle, int currentDrift, int currentSet, short rudderAngle) { public BoatLocation(byte messageVersionNumber, long time, int sourceID, long sequenceNumber, byte deviceType, int latitude, int longitude, int altitude, int heading, short pitch, short roll, int boatSpeed, int boatCOG, int boatSOG, int apparentWindSpeed, short apparentWindAngle, int trueWindSpeed, int trueWindDirection, short trueWindAngle, int currentDrift, int currentSet, short rudderAngle) {
super(MessageType.BOATLOCATION); super(MessageType.BOATLOCATION);
this.messageVersionNumber = messageVersionNumber; this.messageVersionNumber = messageVersionNumber;
@ -150,7 +152,7 @@ public class BoatLocationMessage extends AC35Data
this.rudderAngle = rudderAngle; this.rudderAngle = rudderAngle;
} }
public BoatLocationMessage(int sourceID, double lat, double lon, long sequenceNumber, double heading, double boatSpeed) { public BoatLocation(int sourceID, double lat, double lon, long sequenceNumber, double heading, double boatSpeed) {
super(MessageType.BOATLOCATION); super(MessageType.BOATLOCATION);
this.messageVersionNumber = (byte) 1; this.messageVersionNumber = (byte) 1;

@ -1,11 +1,13 @@
package seng302.Networking.Utils; package seng302.Networking.Messages;
import seng302.Networking.Utils.Enums.BoatStatus; import seng302.Networking.Utils.ByteConverter;
import seng302.Networking.Messages.Enums.BoatStatusEnum;
/** /**
* Created by hba56 on 23/04/17. * Created by hba56 on 23/04/17.
*/ */
public class BoatStatusMessage { public class BoatStatus {
private int sourceID; private int sourceID;
private byte boatStatus; private byte boatStatus;
private byte legNumber; private byte legNumber;
@ -14,7 +16,7 @@ public class BoatStatusMessage {
private long estTimeAtNextMark; private long estTimeAtNextMark;
private long estTimeAtFinish; private long estTimeAtFinish;
public BoatStatusMessage(int sourceID, byte boatStatus, byte legNumber, byte numPenaltiesAwarded, byte numPenaltiesServed, long estTimeAtNextMark, long estTimeAtFinish) { public BoatStatus(int sourceID, byte boatStatus, byte legNumber, byte numPenaltiesAwarded, byte numPenaltiesServed, long estTimeAtNextMark, long estTimeAtFinish) {
this.sourceID = sourceID; this.sourceID = sourceID;
this.boatStatus = boatStatus; this.boatStatus = boatStatus;
this.legNumber = legNumber; this.legNumber = legNumber;
@ -25,9 +27,9 @@ public class BoatStatusMessage {
} }
public BoatStatusMessage(int sourceID, BoatStatus boatStatus, int legNum) { public BoatStatus(int sourceID, BoatStatusEnum boatStatusEnum, int legNum) {
this.sourceID = sourceID; this.sourceID = sourceID;
this.boatStatus = boatStatus.getValue(); this.boatStatus = boatStatusEnum.getValue();
this.legNumber = ByteConverter.intToBytes(legNum)[0]; this.legNumber = ByteConverter.intToBytes(legNum)[0];
numPenaltiesAwarded = 0; numPenaltiesAwarded = 0;
numPenaltiesServed = 0; numPenaltiesServed = 0;

@ -1,9 +1,11 @@
package seng302.Networking.Utils; package seng302.Networking.Messages;
import seng302.Networking.Messages.Enums.MessageType;
/** /**
* Created by fwy13 on 21/04/17. * Created by fwy13 on 21/04/17.
*/ */
public class CourseWind extends AC35Data{ public class CourseWind extends AC35Data {
private int ID, raceID, windDirection, windSpeed, bestUpwindAngle, bestDownwindAngle, flags; private int ID, raceID, windDirection, windSpeed, bestUpwindAngle, bestDownwindAngle, flags;
private long time; private long time;

@ -1,11 +1,13 @@
package seng302.Networking.Utils; package seng302.Networking.Messages;
import seng302.Networking.Messages.Enums.MessageType;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
* Created by fwy13 on 25/04/17. * Created by fwy13 on 25/04/17.
*/ */
public class CourseWinds extends AC35Data{ public class CourseWinds extends AC35Data {
private int msgVerNum; private int msgVerNum;
private int selectedWindID; private int selectedWindID;

@ -0,0 +1,79 @@
package seng302.Networking.Messages.Enums;
import java.util.HashMap;
import java.util.Map;
/**
* Created by esa46 on 28/04/17.
*/
/**
* Enumeration that encapsulates the various statuses a boat can have.
*/
public enum BoatStatusEnum {
UNDEFINED(0),
PRESTART(1),
RACING(2),
FINISHED(3),
DNS(4),
DNF(5),
DSQ(6),
OCS(7),
NOT_A_STATUS(-1);
///Primitive value of the enum.
private byte value;
/**
* Ctor. Creates a BoatStatusEnum from a given primitive integer value, cast to a byte.
* @param value Integer, which is cast to byte, to construct from.
*/
private BoatStatusEnum(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 BoatStatusEnum values.
private static final Map<Byte, BoatStatusEnum> byteToStatusMap = new HashMap<>();
/**
* Static initialization block. Initializes the byteToStatusMap.
*/
static {
for (BoatStatusEnum type : BoatStatusEnum.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 BoatStatusEnum value.
* @return The BoatStatusEnum value which corresponds to the given byte value.
*/
public static BoatStatusEnum fromByte(byte boatStatusByte) {
//Gets the corresponding MessageType from the map.
BoatStatusEnum type = byteToStatusMap.get(boatStatusByte);
if (type == null) {
//If the byte value wasn't found, return the NOT_A_STATUS BoatStatusEnum.
return BoatStatusEnum.NOT_A_STATUS;
}
else {
//Otherwise, return the BoatStatusEnum.
return type;
}
}
}

@ -0,0 +1,83 @@
package seng302.Networking.Messages.Enums;
/**
* Created by hba56 on 21/04/17.
*/
import java.util.HashMap;
import java.util.Map;
/**
* Enumeration that encapsulates the various types of messages that can be sent.
*/
public enum MessageType {
HEARTBEAT(1),
RACESTATUS(12),
DISPLAYTEXTMESSAGE(20),
XMLMESSAGE(26),
RACESTARTSTATUS(27),
YACHTEVENTCODE(29),
YACHTACTIONCODE(31),
CHATTERTEXT(36),
BOATLOCATION(37),
MARKROUNDING(38),
COURSEWIND(44),
AVGWIND(47),
NOTAMESSAGE(0);
///Primitive value of the enum.
private byte value;
/**
* Ctor. Creates a MessageType enum from a given primitive integer value, cast to a byte.
* @param value Integer, which is cast to byte, to construct from.
*/
private MessageType(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 MessageType values.
private static final Map<Byte, MessageType> byteToTypeMap = new HashMap<>();
/**
* Static initialization block. Initializes the byteToTypeMap.
*/
static {
for (MessageType type : MessageType.values()) {
byteToTypeMap.put(type.value, type);
}
}
/**
* Returns the enumeration value which corresponds to a given byte value.
* @param messageTypeByte Byte value to convert to a MessageType value.
* @return The MessageType value which corresponds to the given byte value.
*/
public static MessageType fromByte(byte messageTypeByte) {
//Gets the corresponding MessageType from the map.
MessageType type = byteToTypeMap.get(messageTypeByte);
if (type == null) {
//If the byte value wasn't found, return the NOTAMESSAGE MessageType.
return MessageType.NOTAMESSAGE;
}
else {
//Otherwise, return the MessageType.
return type;
}
}
}

@ -0,0 +1,33 @@
package seng302.Networking.Messages;
import seng302.Networking.Messages.Enums.MessageType;
/**
* Created by fwy13 on 25/04/17.
*/
/**
* Represents a Heartbeat message.
*/
public class Heartbeat extends AC35Data {
///Sequence number of the heartbeat.
private long sequenceNumber;
/**
* Ctor.
* @param sequenceNumber Sequence number of the heartbeat.
*/
public Heartbeat(long sequenceNumber) {
super(MessageType.HEARTBEAT);
this.sequenceNumber = sequenceNumber;
}
/**
* Returns the sequence number of this heartbeat message.
* @return Sequence number of this heartbeat message.
*/
public long getSequenceNumber() {
return sequenceNumber;
}
}

@ -1,9 +1,11 @@
package seng302.Networking.Utils; package seng302.Networking.Messages;
import seng302.Networking.Messages.Enums.MessageType;
/** /**
* Created by fwy13 on 25/04/17. * Created by fwy13 on 25/04/17.
*/ */
public class MarkRounding extends AC35Data{ public class MarkRounding extends AC35Data {
private int msgVerNum; private int msgVerNum;
private long time; private long time;
@ -49,7 +51,19 @@ public class MarkRounding extends AC35Data{
this.markID = markID; this.markID = markID;
} }
/**
* Returns the boat (source) ID for this message.
* @return Boat ID for this message.
*/
public int getSourceID() { public int getSourceID() {
return sourceID; return sourceID;
} }
/**
* Returns the timestamp for this message.
* @return Timestamp for this message.
*/
public long getTime() {
return time;
}
} }

@ -1,4 +1,6 @@
package seng302.Networking.Utils; package seng302.Networking.Messages;
import seng302.Networking.Messages.Enums.MessageType;
/** /**
* Created by fwy13 on 19/04/17. * Created by fwy13 on 19/04/17.

@ -1,11 +1,12 @@
package seng302.Networking.Utils; package seng302.Networking.Messages;
import java.util.ArrayList; import seng302.Networking.Messages.Enums.MessageType;
/** /**
* Created by fwy13 on 25/04/17. * Created by fwy13 on 25/04/17.
*/ */
public class RaceStartStatus extends AC35Data{ public class RaceStartStatus extends AC35Data {
private long timestamp; private long timestamp;
private int ackNum; private int ackNum;
private long raceStartTime; private long raceStartTime;

@ -1,11 +1,14 @@
package seng302.Networking.Utils; package seng302.Networking.Messages;
import seng302.Networking.Messages.Enums.MessageType;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
* Created by fwy13 on 25/04/17. * Created by fwy13 on 25/04/17.
*/ */
public class RaceStatus extends AC35Data{ public class RaceStatus extends AC35Data {
private long currentTime; private long currentTime;
private int raceID; private int raceID;
private int raceStatus; private int raceStatus;
@ -13,9 +16,9 @@ public class RaceStatus extends AC35Data{
private int windDirection; private int windDirection;
private int windSpeed; private int windSpeed;
private int raceType; private int raceType;
private ArrayList<BoatStatusMessage> boatStatusMessages; private ArrayList<BoatStatus> boatStatuses;
public RaceStatus(long currentTime, int raceID, int raceStatus, long expectedStartTime, int windDirection, int windSpeed, int raceType, ArrayList<BoatStatusMessage> boatStatusMessages){ public RaceStatus(long currentTime, int raceID, int raceStatus, long expectedStartTime, int windDirection, int windSpeed, int raceType, ArrayList<BoatStatus> boatStatuses){
super(MessageType.RACESTATUS); super(MessageType.RACESTATUS);
this.currentTime = currentTime; this.currentTime = currentTime;
this.raceID = raceID; this.raceID = raceID;
@ -24,7 +27,7 @@ public class RaceStatus extends AC35Data{
this.windDirection = windDirection; this.windDirection = windDirection;
this.windSpeed = windSpeed; this.windSpeed = windSpeed;
this.raceType = raceType; this.raceType = raceType;
this.boatStatusMessages = boatStatusMessages;//note this is a copy so any alterations to the parent will affect this. this.boatStatuses = boatStatuses;//note this is a copy so any alterations to the parent will affect this.
} }
@ -70,9 +73,9 @@ public class RaceStatus extends AC35Data{
return raceType; return raceType;
} }
public ArrayList<BoatStatusMessage> getBoatStatusMessages() public ArrayList<BoatStatus> getBoatStatuses()
{ {
return boatStatusMessages; return boatStatuses;
} }
public boolean isNotActive() { public boolean isNotActive() {

@ -1,13 +1,13 @@
package seng302.Networking.Utils; package seng302.Networking.Messages;
import org.xml.sax.InputSource; import seng302.Networking.Messages.Enums.MessageType;
import java.io.InputStream; import java.io.InputStream;
/** /**
* Created by fwy13 on 25/04/17. * Created by fwy13 on 25/04/17.
*/ */
public class XMLMessage extends AC35Data{ public class XMLMessage extends AC35Data {
private int ackNumber; private int ackNumber;
private long timeStamp; private long timeStamp;

@ -1,6 +1,8 @@
package seng302.Networking.PacketDump; package seng302.Networking.PacketDump;
import seng302.Networking.BinaryMessageDecoder; import seng302.Networking.BinaryMessageDecoder;
import seng302.Networking.Exceptions.InvalidMessageException;
import seng302.Networking.Messages.AC35Data;
import java.io.IOException; import java.io.IOException;
import java.net.URISyntaxException; import java.net.URISyntaxException;
@ -48,7 +50,13 @@ public class AC35DumpReader {
} }
for (AC35Packet pack: packets){ for (AC35Packet pack: packets){
BinaryMessageDecoder decoder = new BinaryMessageDecoder(pack.getData()); BinaryMessageDecoder decoder = new BinaryMessageDecoder(pack.getData());
decoder.decode();
try {
AC35Data data = decoder.decode();
}
catch (InvalidMessageException e) {
System.out.println(e.getMessage());
}
} }
} }

@ -1,18 +0,0 @@
package seng302.Networking.Utils;
/**
* Created by fwy13 on 25/04/17.
*/
public abstract class AC35Data {
protected MessageType type;
public AC35Data (MessageType type){
this.type = type;
}
public MessageType getType() {
return type;
}
}

@ -1,41 +0,0 @@
package seng302.Networking.Utils.Enums;
/**
* Created by esa46 on 28/04/17.
*/
public enum BoatStatus {
UNDEFINED(0), PRESTART(1), RACING(2), FINISHED(3), DNS(4), DNF(5), DSQ(6), OCS(7), NOT_A_STATUS(-1);
private byte value;
private BoatStatus(int value) { this.value = (byte)value; }
public byte getValue() {
return value;
}
public static BoatStatus valueOf(byte bite){
switch(bite){
case 0:
return UNDEFINED;
case 1:
return PRESTART;
case 2:
return RACING;
case 3:
return FINISHED;
case 4:
return DNS;
case 5:
return DNF;
case 6:
return DSQ;
case 7:
return OCS;
default:
return NOT_A_STATUS;
}
}
}

@ -1,12 +0,0 @@
package seng302.Networking.Utils;
/**
* Created by fwy13 on 25/04/17.
*/
public class Heartbeat extends AC35Data{
public Heartbeat(){
super(MessageType.HEARTBEAT);
}
}

@ -1,49 +0,0 @@
package seng302.Networking.Utils;
/**
* Created by hba56 on 21/04/17.
*/
public enum MessageType {
HEARTBEAT(1), RACESTATUS(12), DISPLAYTEXTMESSAGE(20),
XMLMESSAGE(26), RACESTARTSTATUS(27), YACHTEVENTCODE(29), YACHTACTIONCODE(31),
CHATTERTEXT(36), BOATLOCATION(37), MARKROUNDING(38), COURSEWIND(44), AVGWIND(47), NOTAMESSAGE(0);
private byte value;
private MessageType(int value) { this.value = (byte)value; }
public byte getValue() {
return value;
}
public static MessageType valueOf(byte bite){
switch(bite){
case 1:
return HEARTBEAT;
case 12:
return RACESTATUS;
case 20:
return DISPLAYTEXTMESSAGE;
case 26:
return XMLMESSAGE;
case 27:
return RACESTARTSTATUS;
case 29:
return YACHTEVENTCODE;
case 31:
return YACHTACTIONCODE;
case 36:
return CHATTERTEXT;
case 37:
return BOATLOCATION;
case 38:
return MARKROUNDING;
case 44:
return COURSEWIND;
case 47:
return AVGWIND;
default:
return NOTAMESSAGE;
}
}
}

@ -2,9 +2,12 @@ package seng302.Networking;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import seng302.Networking.Exceptions.InvalidMessageException;
import seng302.Networking.MessageDecoders.XMLMessageDecoder; import seng302.Networking.MessageDecoders.XMLMessageDecoder;
import seng302.Networking.MessageEncoders.XMLMessageEncoder; import seng302.Networking.MessageEncoders.XMLMessageEncoder;
import seng302.Networking.Utils.MessageType; import seng302.Networking.Messages.AC35Data;
import seng302.Networking.Messages.Enums.MessageType;
import seng302.Networking.Messages.XMLMessage;
import java.io.*; import java.io.*;
@ -30,19 +33,32 @@ public class BinaryMessageDecoderTest {
BinaryMessageEncoder testMessage = new BinaryMessageEncoder(MessageType.XMLMESSAGE, time, 1, (short)encodedMessage.length, encodedMessage); BinaryMessageEncoder testMessage = new BinaryMessageEncoder(MessageType.XMLMESSAGE, time, 1, (short)encodedMessage.length, encodedMessage);
BinaryMessageDecoder testDecoder = new BinaryMessageDecoder(testMessage.getFullMessage()); BinaryMessageDecoder decoder = new BinaryMessageDecoder(testMessage.getFullMessage());
testDecoder.decode();
AC35Data message = null;
try {
message = decoder.decode();
}
catch (InvalidMessageException e) {
Assert.assertFalse(e.getMessage(), true);
}
if (!(message instanceof XMLMessage)){
Assert.assertFalse(true);
}
XMLMessage xmlMessage = (XMLMessage) message;
//message length //message length
Assert.assertEquals((short) encodedMessage.length,testDecoder.getMessageLength()); Assert.assertEquals((short) encodedMessage.length, decoder.getMessageBodyLength());
//time stamp //time stamp
Assert.assertEquals(time,testDecoder.getTimeStamp()); Assert.assertEquals(time, decoder.getHeaderTimeStamp());
//source ID //source ID
Assert.assertEquals((short) 1, testDecoder.getSourceID()); Assert.assertEquals((short) 1, decoder.getHeaderSourceID());
//message type //message type
Assert.assertEquals(26, testDecoder.getMessageType()); Assert.assertEquals(26, decoder.getHeaderMessageType());
XMLMessageDecoder decoderXML = new XMLMessageDecoder(testDecoder.getMessage()); XMLMessageDecoder decoderXML = new XMLMessageDecoder(decoder.getMessageBody());
decoderXML.decode(); decoderXML.decode();
//tests from seng302.Networking.MessageDecoders.XMLMessageDecoderTest to make sure the file is still good //tests from seng302.Networking.MessageDecoders.XMLMessageDecoderTest to make sure the file is still good
@ -62,7 +78,7 @@ public class BinaryMessageDecoderTest {
// Assert.assertEquals(xmlString.toString(), contents); // Assert.assertEquals(xmlString.toString(), contents);
}catch (IOException e){ }catch (IOException e){
e.printStackTrace(); Assert.assertFalse(e.getMessage(), true);
} }
} }
} }

@ -2,7 +2,7 @@ package seng302.Networking.MessageDecoders;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import seng302.Networking.Utils.BoatLocationMessage; import seng302.Networking.Messages.BoatLocation;
import seng302.Networking.MessageEncoders.RaceVisionByteEncoder; import seng302.Networking.MessageEncoders.RaceVisionByteEncoder;
@ -13,7 +13,7 @@ public class BoatLocationDecoderTest {
@Test @Test
public void getByteArrayTest(){ public void getByteArrayTest(){
long time = System.currentTimeMillis(); long time = System.currentTimeMillis();
BoatLocationMessage testMessage = new BoatLocationMessage((byte) 1, time, 2, BoatLocation testMessage = new BoatLocation((byte) 1, time, 2,
3, (byte) 1, 180, -180, 4, 5, 3, (byte) 1, 180, -180, 4, 5,
(short) 6, (short) 7, 8, 9, 10, 11, (short) 6, (short) 7, 8, 9, 10, 11,
(short) 12, 13, 14 , (short) 15, (short) 12, 13, 14 , (short) 15,
@ -22,7 +22,7 @@ public class BoatLocationDecoderTest {
byte [] testEncodedMessage = raceVisionByteEncoder.boatLocation(testMessage); byte [] testEncodedMessage = raceVisionByteEncoder.boatLocation(testMessage);
BoatLocationDecoder testDecoder = new BoatLocationDecoder(testEncodedMessage); BoatLocationDecoder testDecoder = new BoatLocationDecoder(testEncodedMessage);
BoatLocationMessage decodedTest = testDecoder.getMessage(); BoatLocation decodedTest = testDecoder.getMessage();
Assert.assertEquals(testMessage.getMessageVersionNumber(), decodedTest.getMessageVersionNumber()); Assert.assertEquals(testMessage.getMessageVersionNumber(), decodedTest.getMessageVersionNumber());
Assert.assertEquals(testMessage.getTime(), decodedTest.getTime()); Assert.assertEquals(testMessage.getTime(), decodedTest.getTime());

@ -2,7 +2,7 @@ package seng302.Networking.MessageDecoders;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import seng302.Networking.Utils.CourseWind; import seng302.Networking.Messages.CourseWind;
import seng302.Networking.MessageEncoders.RaceVisionByteEncoder; import seng302.Networking.MessageEncoders.RaceVisionByteEncoder;
import java.util.ArrayList; import java.util.ArrayList;

@ -3,8 +3,8 @@ package seng302.Networking.MessageDecoders;
import org.junit.Assert; import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import seng302.Networking.MessageEncoders.RaceVisionByteEncoder; import seng302.Networking.MessageEncoders.RaceVisionByteEncoder;
import seng302.Networking.Utils.BoatStatusMessage; import seng302.Networking.Messages.BoatStatus;
import seng302.Networking.Utils.RaceStatus; import seng302.Networking.Messages.RaceStatus;
import java.util.ArrayList; import java.util.ArrayList;
@ -32,8 +32,8 @@ public class RaceStatusDecoderTest {
long boat1TimeAtFinish = boat1TimeAtNextMark + (1000 * 15); long boat1TimeAtFinish = boat1TimeAtNextMark + (1000 * 15);
long boat2TimeAtFinish = boat2TimeAtNextMark + (1000 * 7); long boat2TimeAtFinish = boat2TimeAtNextMark + (1000 * 7);
BoatStatusMessage boatStatusMessage1 = new BoatStatusMessage(boat1SourceID, boat1Status, boat1LegNumber, boat1PenaltiesAwarded, boat1PenaltiesServed, boat1TimeAtNextMark, boat1TimeAtFinish); BoatStatus boatStatus1 = new BoatStatus(boat1SourceID, boat1Status, boat1LegNumber, boat1PenaltiesAwarded, boat1PenaltiesServed, boat1TimeAtNextMark, boat1TimeAtFinish);
BoatStatusMessage boatStatusMessage2 = new BoatStatusMessage(boat2SourceID, boat2Status, boat2LegNumber, boat2PenaltiesAwarded, boat2PenaltiesServed, boat2TimeAtNextMark, boat2TimeAtFinish); BoatStatus boatStatus2 = new BoatStatus(boat2SourceID, boat2Status, boat2LegNumber, boat2PenaltiesAwarded, boat2PenaltiesServed, boat2TimeAtNextMark, boat2TimeAtFinish);
int raceID = 585; int raceID = 585;
int raceStatus = 3; int raceStatus = 3;
@ -41,11 +41,11 @@ public class RaceStatusDecoderTest {
int windDirection = 2341; int windDirection = 2341;
int windSpeed = 10201; int windSpeed = 10201;
int raceType = 1; int raceType = 1;
ArrayList<BoatStatusMessage> boatStatusMessages = new ArrayList<>(2); ArrayList<BoatStatus> boatStatuses = new ArrayList<>(2);
boatStatusMessages.add(boatStatusMessage1); boatStatuses.add(boatStatus1);
boatStatusMessages.add(boatStatusMessage2); boatStatuses.add(boatStatus2);
RaceStatus raceStatusObject = new RaceStatus(time, raceID, raceStatus, raceStartTime, windDirection, windSpeed, raceType, boatStatusMessages); RaceStatus raceStatusObject = new RaceStatus(time, raceID, raceStatus, raceStartTime, windDirection, windSpeed, raceType, boatStatuses);
byte[] encodedRaceStatus = RaceVisionByteEncoder.raceStatus(raceStatusObject); byte[] encodedRaceStatus = RaceVisionByteEncoder.raceStatus(raceStatusObject);
@ -61,7 +61,7 @@ public class RaceStatusDecoderTest {
Assert.assertEquals(windSpeed, decoderTest.getRaceWindSpeed()); Assert.assertEquals(windSpeed, decoderTest.getRaceWindSpeed());
BoatStatusMessage boat1 = decoderTest.getBoats().get(0); BoatStatus boat1 = decoderTest.getBoats().get(0);
Assert.assertEquals(boat1SourceID, boat1.getSourceID()); Assert.assertEquals(boat1SourceID, boat1.getSourceID());
Assert.assertEquals(boat1Status, boat1.getBoatStatus()); Assert.assertEquals(boat1Status, boat1.getBoatStatus());

@ -28,7 +28,7 @@ public class StreamedCourse extends Observable implements RaceDataSource {
public void setBoatXMLReader(BoatXMLReader boatXMLReader) { public void setBoatXMLReader(BoatXMLReader boatXMLReader) {
this.boatXMLReader = boatXMLReader; this.boatXMLReader = boatXMLReader;
if (streamedCourseXMLReader != null && boatXMLReader != null) { if (streamedCourseXMLReader != null && boatXMLReader != null) {
boatXMLReader.setParticipants(streamedCourseXMLReader.getParticipants()); this.boatXMLReader.setParticipants(streamedCourseXMLReader.getParticipants());
boatXMLReader.read(); boatXMLReader.read();
} }

@ -6,9 +6,9 @@ import seng302.Model.Boat;
import seng302.Model.Leg; import seng302.Model.Leg;
import seng302.Model.Marker; import seng302.Model.Marker;
import seng302.Model.Race; import seng302.Model.Race;
import seng302.Networking.Utils.BoatStatusMessage; import seng302.Networking.Messages.BoatStatus;
import seng302.Networking.Utils.Enums.BoatStatus; import seng302.Networking.Messages.BoatLocation;
import seng302.Networking.Utils.BoatLocationMessage; import seng302.Networking.Messages.Enums.BoatStatusEnum;
import seng302.VisualiserInput; import seng302.VisualiserInput;
/** /**
@ -55,9 +55,9 @@ public class StreamedRace extends Race {
*/ */
protected void checkPosition(Boat boat, long timeElapsed) { protected void checkPosition(Boat boat, long timeElapsed) {
StreamedCourse raceData = visualiserInput.getCourse(); StreamedCourse raceData = visualiserInput.getCourse();
BoatStatusMessage boatStatusMessage = visualiserInput.getBoatStatus().get(boat.getSourceID()); BoatStatus boatStatusMessage = visualiserInput.getBoatStatusMap().get(boat.getSourceID());
if (boatStatusMessage != null) { if (boatStatusMessage != null) {
BoatStatus boatStatus = BoatStatus.valueOf(boatStatusMessage.getBoatStatus()); BoatStatusEnum boatStatusEnum = BoatStatusEnum.fromByte(boatStatusMessage.getBoatStatus());
int legNumber = boatStatusMessage.getLegNumber(); int legNumber = boatStatusMessage.getLegNumber();
@ -65,11 +65,11 @@ public class StreamedRace extends Race {
boat.setCurrentLeg(legs.get(legNumber)); boat.setCurrentLeg(legs.get(legNumber));
} }
if (boatStatus == BoatStatus.RACING) { if (boatStatusEnum == BoatStatusEnum.RACING) {
boat.addTrackPoint(boat.getCurrentPosition()); boat.addTrackPoint(boat.getCurrentPosition());
} else if (boatStatus == BoatStatus.DNF) { } else if (boatStatusEnum == BoatStatusEnum.DNF) {
boat.setDnf(true); boat.setDnf(true);
} else if (boatStatus == BoatStatus.FINISHED || legNumber == raceData.getLegs().size()) { } else if (boatStatusEnum == BoatStatusEnum.FINISHED || legNumber == raceData.getLegs().size()) {
boatsFinished++; boatsFinished++;
boat.setTimeFinished(timeElapsed); boat.setTimeFinished(timeElapsed);
boat.setFinished(true); boat.setFinished(true);
@ -88,13 +88,13 @@ public class StreamedRace extends Race {
*/ */
protected void updatePosition(Boat boat, int millisecondsElapsed) { protected void updatePosition(Boat boat, int millisecondsElapsed) {
int sourceID = boat.getSourceID(); int sourceID = boat.getSourceID();
BoatLocationMessage boatLocationMessage = visualiserInput.getBoatLocationMessage(sourceID); BoatLocation boatLocation = visualiserInput.getBoatLocationMessage(sourceID);
if(boatLocationMessage != null) { if(boatLocation != null) {
double lat = boatLocationMessage.getLatitudeDouble(); double lat = boatLocation.getLatitudeDouble();
double lon = boatLocationMessage.getLongitudeDouble(); double lon = boatLocation.getLongitudeDouble();
boat.setCurrentPosition(new GPSCoordinate(lat, lon)); boat.setCurrentPosition(new GPSCoordinate(lat, lon));
boat.setHeading(boatLocationMessage.getHeadingDegrees()); boat.setHeading(boatLocation.getHeadingDegrees());
boat.setVelocity(boatLocationMessage.getBoatSOG() * MMPS_TO_KN); boat.setVelocity(boatLocation.getBoatSOG() * MMPS_TO_KN);
} }
} }

@ -1,77 +1,89 @@
package seng302; package seng302;
import org.opengis.style.Mark;
import org.xml.sax.SAXException; import org.xml.sax.SAXException;
import seng302.Mock.*; import seng302.Mock.*;
import seng302.Networking.BinaryMessageDecoder; import seng302.Networking.BinaryMessageDecoder;
import seng302.Networking.MessageDecoders.*; import seng302.Networking.Exceptions.InvalidMessageException;
import seng302.Networking.Utils.*; import seng302.Networking.Messages.*;
import javax.xml.parsers.ParserConfigurationException; import javax.xml.parsers.ParserConfigurationException;
import java.io.*; import java.io.*;
import java.net.*; import java.net.*;
import java.nio.Buffer;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.text.ParseException; import java.text.ParseException;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit; import java.util.concurrent.ArrayBlockingQueue;
import static seng302.Networking.Utils.ByteConverter.bytesToInt; import static seng302.Networking.Utils.ByteConverter.bytesToInt;
import static seng302.Networking.Utils.ByteConverter.bytesToShort; import static seng302.Networking.Utils.ByteConverter.bytesToShort;
import static seng302.Networking.Utils.MessageType.*;
/** /**
* TCP server to act as the mock AC35 streaming interface * TCP client which receives packets/messages from a race data source (e.g., mock source, official source), and exposes them to any observers.
*/ */
public class VisualiserInput implements Runnable public class VisualiserInput implements Runnable {
{
//Time since last heartbeat.
private long lastHeartbeatTime;
//socket port 4945 as 4940 is ac35 live port and 4941 is ac35 test port ///A queue that contains messages that have been received, and need to be handled.
private Socket connectionSocket; private ArrayBlockingQueue<AC35Data> messagesReceivedQueue = new ArrayBlockingQueue<>(1000000);//We have room for 1,000,000. Is this much capacity actually needed?
long heartbeatSeqNum; ///Timestamp of the last heartbeat.
private long lastHeartbeatTime = -1;
///Sequence number of the last heartbeat.
private long lastHeartbeatSequenceNum = -1;
///How long we should wait for a heartbeat before assuming the connection is dead. Measured in milliseconds.
private long heartBeatPeriod = 10 * 1000;
private StreamedCourse course = null; ///The socket that we have connected to.
private Socket connectionSocket;
private Map<Integer, BoatLocationMessage> boatLocation; ///Object to store parsed course data. //TODO comment?
private StreamedCourse course;
///The last RaceStatus message received.
private RaceStatus raceStatus; private RaceStatus raceStatus;
private Map<Integer, BoatStatusMessage> boatStatus; ///A map of the last BoatStatus message received, for each boat.
private Map<Integer, BoatStatus> boatStatusMap = new HashMap<>();;
///A map of the last BoatLocation message received, for each boat.
private Map<Integer, BoatLocation> boatLocationMap = new HashMap<>();
///The last AverageWind message received.
private AverageWind averageWind; private AverageWind averageWind;
///The last CourseWinds message received.
private CourseWinds courseWinds; private CourseWinds courseWinds;
private Map<Integer, MarkRounding> markRounding; ///A map of the last MarkRounding message received, for each boat.
private Map<Integer, MarkRounding> markRoundingMap = new HashMap<>();
///The last RaceStartStatus message received.
private RaceStartStatus raceStartStatus; private RaceStartStatus raceStartStatus;
private BufferedInputStream inStream; ///InputStream (from the socket).
private DataInputStream inStream;
///TODO comment?
private boolean receiverLoop = true; private boolean receiverLoop = true;
public VisualiserInput(Socket socket, StreamedCourse course) throws IOException{ /**
* Ctor.
* @param socket Socket from which we will receive race data.
* @param course TODO comment?
* @throws IOException If there is something wrong with the socket's input stream.
*/
public VisualiserInput(Socket socket, StreamedCourse course) throws IOException {
this.connectionSocket = socket; this.connectionSocket = socket;
// this.connectionSocket = new Socket("livedata.americascup.com",4941); //We wrap a DataInputStream around the socket's InputStream because it has the stream.readFully(buffer) function, which is a blocking read until the buffer has been filled.
this.inStream = new BufferedInputStream(connectionSocket.getInputStream()); this.inStream = new DataInputStream(connectionSocket.getInputStream());
this.course = course; this.course = course;
this.boatLocation = new HashMap<>();
this.boatStatus = new HashMap<>();
this.markRounding = new HashMap<>();
//start Time
this.lastHeartbeatTime = System.currentTimeMillis(); this.lastHeartbeatTime = System.currentTimeMillis();
} }
/** /**
* Provides StreamedCourse container for fixed course data * Provides StreamedCourse container for fixed course data.
* @return course for current VisualiserInput instance * @return Course for current VisualiserInput instance.
* @see seng302.Mock.StreamedCourse * @see seng302.Mock.StreamedCourse
*/ */
public StreamedCourse getCourse() { public StreamedCourse getCourse() {
@ -80,228 +92,342 @@ public class VisualiserInput implements Runnable
/** /**
* Returns the last boat location message associated with the given boat source ID. * Returns the last boat location message associated with the given boat source ID.
* @param sourceID unique global identifier for boat * @param sourceID Unique global identifier for the boat.
* @return most recent location message * @return The most recent location message.
*/ */
public BoatLocationMessage getBoatLocationMessage(int sourceID) { public BoatLocation getBoatLocationMessage(int sourceID) {
return boatLocation.get(sourceID); return boatLocationMap.get(sourceID);
} }
/** /**
* calculates the time since last heartbeat * Calculates the time since last heartbeat, in milliseconds.
* @return time since last heartbeat * @return Time since last heartbeat, in milliseconds..
*/ */
private double timeSinceHeartbeat() { private double timeSinceHeartbeat() {
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
return (now - lastHeartbeatTime) / 1000.0; return (now - lastHeartbeatTime);
} }
/** /**
* Returns the boat locations * Returns the boat locations map. Maps between Integer (Boat ID) -> BoatLocation.
* @return locations of where the boats are * @return Map of boat locations.
*/ */
public Map<Integer, BoatLocationMessage> getBoatLocation() { public Map<Integer, BoatLocation> getBoatLocationMap() {
return boatLocation; return boatLocationMap;
} }
/** /**
* Gets the status of the race * Gets the status of the race.
* @return the status of the race * @return The status of the race.
*/ */
public RaceStatus getRaceStatus() { public RaceStatus getRaceStatus() {
return raceStatus; return raceStatus;
} }
/** /**
* Hashmap of the boat status' * Returns the boat statuses map. Maps between Integer (Boat ID) -> BoatStatus.
* @return Hash map of boat status * @return Map of boat statuses.
*/ */
public Map<Integer, BoatStatusMessage> getBoatStatus() { public Map<Integer, BoatStatus> getBoatStatusMap() {
return boatStatus; return boatStatusMap;
} }
/** /**
* Returns the average wind of the race * Returns the average wind of the race.
* @return average wind in the race * @return Average wind in the race.
*/ */
public AverageWind getAverageWind() { public AverageWind getAverageWind() {
return averageWind; return averageWind;
} }
/** /**
* Returns winds in the course * Returns winds in the course.
* @return winds that are in the course * @return Winds that are in the course.
*/ */
public CourseWinds getCourseWinds() { public CourseWinds getCourseWinds() {
return courseWinds; return courseWinds;
} }
/** /**
* Returns get Mark Rounding Boat Source ID, MarkRound * Returns the mark roundings map. Maps between Integer (Boat ID) -> MarkRounding.
* @return the mark rounding * @return Map of mark roundings.
*/ */
public Map<Integer, MarkRounding> getMarkRounding() { public Map<Integer, MarkRounding> getMarkRoundingMap() {
return markRounding; return markRoundingMap;
} }
/** /**
* Takes an inputStream and reads the first 15 bytes (the header) and gets the message length * Reads and returns the next message as an array of bytes from the socket. Use getNextMessage() to get the actual message object instead.
* for the whole message then reads that and returns the byte array * @return Encoded binary message bytes.
* @return encoded binary messsage bytes * @throws IOException Thrown when an error occurs while reading from the socket.
* @throws IOException made by the inputstream reading
*/ */
private byte[] getBytes() throws IOException { private byte[] getNextMessageBytes() throws IOException {
inStream.mark(0);
if (inStream.available() < 15) return null;//if there is not enough bytes foer the headerr short CRCLength = 4;
byte[] headerBytes = new byte[15]; short headerLength = 15;
inStream.read(headerBytes);
byte[] messageLenBytes = Arrays.copyOfRange(headerBytes, 13, 15); //Read the header of the next message.
short messageLen = bytesToShort(messageLenBytes); byte[] headerBytes = new byte[headerLength];
byte[] messageBytesWithCRC = new byte[messageLen+4]; inStream.readFully(headerBytes);
if (inStream.available() < messageLen + 4){//if the message is not long enough
inStream.reset();//reset pointer. //Read the message body length.
return null; byte[] messageBodyLengthBytes = Arrays.copyOfRange(headerBytes, headerLength - 2, headerLength);
} short messageBodyLength = bytesToShort(messageBodyLengthBytes);
inStream.read(messageBytesWithCRC, 0, messageLen + 4);
ByteBuffer binaryMessageBytes = ByteBuffer.allocate(headerBytes.length+messageBytesWithCRC.length); //Read the message body.
binaryMessageBytes.put(headerBytes); byte[] messageBodyBytes = new byte[messageBodyLength];
binaryMessageBytes.put(messageBytesWithCRC); inStream.readFully(messageBodyBytes);
return binaryMessageBytes.array();
//Read the message CRC.
byte[] messageCRCBytes = new byte[CRCLength];
inStream.readFully(messageCRCBytes);
//Put the head + body + crc into one large array.
ByteBuffer messageBytes = ByteBuffer.allocate(headerBytes.length + messageBodyBytes.length + messageCRCBytes.length);
messageBytes.put(headerBytes);
messageBytes.put(messageBodyBytes);
messageBytes.put(messageCRCBytes);
return messageBytes.array();
} }
/**
* Reads and returns the next message object from the socket.
* @return The message object. Use instanceof for concrete type.
* @throws IOException Thrown when an error occurs while reading from the socket.
* @throws InvalidMessageException Thrown when the message is invalid in some way.
*/
private AC35Data getNextMessage() throws IOException, InvalidMessageException
{
//Get the next message from the socket as a block of bytes.
byte[] messageBytes = this.getNextMessageBytes();
//Decode the binary message into an appropriate message object.
BinaryMessageDecoder decoder = new BinaryMessageDecoder(messageBytes);
AC35Data message = decoder.decode();
return message;
}
/**
* Main loop which reads messages from the socket, and exposes them.
*/
public void run(){ public void run(){
this.receiverLoop = true; this.receiverLoop = true;
try{ //receiver loop that gets the input
//receiver loop that gets the input while (receiverLoop) {
while(receiverLoop) {
//if no heartbeat has been received in more than 6 seconds //If no heartbeat has been received in more the heartbeat period
//the connection will need to be restarted //then the connection will need to be restarted.
if (timeSinceHeartbeat() > 6){ System.out.println("time since last heartbeat: " + timeSinceHeartbeat());//TEMP REMOVE
System.out.println("Connection has stopped, trying to reconnect"); if (timeSinceHeartbeat() > this.heartBeatPeriod) {
receiverLoop = false; System.out.println("Connection has stopped, trying to reconnect.");
//Attempt to reconnect the socket.
try {//This attempt doesn't really work. Under what circumstances would
this.connectionSocket = new Socket(this.connectionSocket.getInetAddress(), this.connectionSocket.getPort());
//this.connectionSocket.connect(this.connectionSocket.getRemoteSocketAddress());
//Reset the heartbeat timer.
this.lastHeartbeatTime = System.currentTimeMillis();
}
catch (IOException e) {
System.err.println("Unable to reconnect.");
//Wait 500ms. Ugly hack, should refactor.
long waitPeriod = 500;
long waitTimeStart = System.currentTimeMillis() + waitPeriod;
while (System.currentTimeMillis() < waitTimeStart){
//Nothing. Busyloop.
}
//Swallow the exception.
continue;
}
}
//Reads the next message.
AC35Data message = null;
try {
message = this.getNextMessage();
}
catch (InvalidMessageException | IOException e) {
//Prints exception to stderr, and iterate loop (that is, read the next message).
System.err.println("Unable to read message: " + e.getMessage());
//Continue to the next loop iteration/message.
continue;
}
//Add it to message queue.
this.messagesReceivedQueue.add(message);
//Checks which message is being received and does what is needed for that message.
//Heartbeat.
if (message instanceof Heartbeat) {
Heartbeat heartbeat = (Heartbeat) message;
//Check that the heartbeat number is greater than the previous value, and then set the last heartbeat time.
if (heartbeat.getSequenceNumber() > this.lastHeartbeatSequenceNum) {
lastHeartbeatTime = System.currentTimeMillis();
lastHeartbeatSequenceNum = heartbeat.getSequenceNumber();
//System.out.println("HeartBeat Message! " + lastHeartbeatSequenceNum);
} }
//converts the input into a byte array that can be read by the decoder }
byte[] binaryMessage = getBytes(); //RaceStatus.
//if there is no bytes read. else if (message instanceof RaceStatus) {
if (binaryMessage == null){ RaceStatus raceStatus = (RaceStatus) message;
continue;
//System.out.println("Race Status Message");
this.raceStatus = raceStatus;
for (BoatStatus boatStatus: this.raceStatus.getBoatStatuses()) {
this.boatStatusMap.put(boatStatus.getSourceID(), boatStatus);
} }
}
//DisplayTextMessage.
/*else if (message instanceof DisplayTextMessage) {
//System.out.println("Display Text Message");
//No decoder for this.
}*/
//XMLMessage.
else if (message instanceof XMLMessage) {
XMLMessage xmlMessage = (XMLMessage) message;
//System.out.println("XML Message!");
if (xmlMessage.getXmlMsgSubType() == xmlMessage.XMLTypeRegatta){
//System.out.println("Setting Regatta");
try {
course.setRegattaXMLReader(new RegattaXMLReader(xmlMessage.getXmlMessage()));
}
//TODO REFACTOR should put all of these exceptions behind a RegattaXMLReaderException.
catch (IOException | SAXException | ParserConfigurationException e) {
System.err.println("Error creating RegattaXMLReader: " + e.getMessage());
//Continue to the next loop iteration/message.
continue;
}
} else if (xmlMessage.getXmlMsgSubType() == xmlMessage.XMLTypeRace){
//System.out.println("Setting Course");
try {
course.setStreamedCourseXMLReader(new StreamedCourseXMLReader(xmlMessage.getXmlMessage()));
}
//TODO REFACTOR should put all of these exceptions behind a StreamedCourseXMLReaderException.
catch (IOException | SAXException | ParserConfigurationException | ParseException | StreamedCourseXMLException e) {
System.err.println("Error creating StreamedCourseXMLReader: " + e.getMessage());
//Continue to the next loop iteration/message.
continue;
}
} else if (xmlMessage.getXmlMsgSubType() == xmlMessage.XMLTypeBoat){
//System.out.println("Setting Boats");
try
{
course.setBoatXMLReader(new BoatXMLReader(xmlMessage.getXmlMessage()));
}
//TODO REFACTOR should put all of these exceptions behind a BoatXMLReaderException.
catch (IOException | SAXException | ParserConfigurationException e) {
System.err.println("Error creating BoatXMLReader: " + e.getMessage());
//Continue to the next loop iteration/message.
continue;
}
//decode the binary message into readable date
BinaryMessageDecoder testDecoder = new BinaryMessageDecoder(binaryMessage);
AC35Data data = testDecoder.decode();
if (data == null){
continue;
} }
//checks which message is being received and does what is needed for that message }
MessageType mType = data.getType(); //RaceStartStatus.
switch (mType) { else if (message instanceof RaceStartStatus) {
case HEARTBEAT: RaceStartStatus raceStartStatus = (RaceStartStatus) message;
lastHeartbeatTime = System.currentTimeMillis();
//note: if the program runs for over 340 years, this will crash. //System.out.println("Race Start Status Message");
heartbeatSeqNum = ByteConverter.bytesToLong(testDecoder.getMessage()); this.raceStartStatus = raceStartStatus;
// System.out.println("HeartBeat Message! " + heartbeatSeqNum); }
break; //YachtEventCode.
case RACESTATUS: /*else if (message instanceof YachtEventCode) {
// System.out.println("Race Status Message"); YachtEventCode yachtEventCode = (YachtEventCode) message;
raceStatus = ((RaceStatus) data);
for (BoatStatusMessage msg: raceStatus.getBoatStatusMessages()){ //System.out.println("Yacht Event Code!");
boatStatus.put(msg.getSourceID(), msg); //No decoder for this.
}
break; }*/
case DISPLAYTEXTMESSAGE: //YachtActionCode.
// System.out.println("Display Text Message"); /*else if (message instanceof YachtActionCode) {
//no decoder for this. YachtActionCode yachtActionCode = (YachtActionCode) message;
break;
case XMLMESSAGE: //System.out.println("Yacht Action Code!");
System.out.println("XML Message!"); //No decoder for this.
XMLMessage xml = (XMLMessage) data;
try { }*/
if (xml.getXmlMsgSubType() == xml.XMLTypeRegatta){ //ChatterText.
System.out.println("Setting Regatta"); /*else if (message instanceof ChatterText) {
course.setRegattaXMLReader(new RegattaXMLReader(xml.getXmlMessage())); ChatterText chatterText = (ChatterText) message;
} else if (xml.getXmlMsgSubType() == xml.XMLTypeRace){
System.out.println("Setting Course"); //System.out.println("Chatter Text Message!");
course.setStreamedCourseXMLReader(new StreamedCourseXMLReader(xml.getXmlMessage())); //No decoder for this.
} else if (xml.getXmlMsgSubType() == xml.XMLTypeBoat){
System.out.println("Setting Boats"); }*/
course.setBoatXMLReader(new BoatXMLReader(xml.getXmlMessage())); //BoatLocation.
else if (message instanceof BoatLocation) {
} BoatLocation boatLocation = (BoatLocation) message;
} catch (SAXException e) {
e.printStackTrace(); //System.out.println("Boat Location!");
} catch (ParserConfigurationException e) { if (this.boatLocationMap.containsKey(boatLocation.getSourceID())) {
e.printStackTrace(); //If our boatlocation map already contains a boat location message for this boat, check that the new message is actually for a later timestamp (i.e., newer).
} catch (ParseException e) { if (boatLocation.getTime() > this.boatLocationMap.get(boatLocation.getSourceID()).getTime()){
e.printStackTrace(); //If it is, replace the old message.
} catch (StreamedCourseXMLException e) { this.boatLocationMap.put(boatLocation.getSourceID(), boatLocation);
e.printStackTrace(); }
} }else{
break; //If the map _doesn't_ already contain a message for this boat, insert the message.
case RACESTARTSTATUS: this.boatLocationMap.put(boatLocation.getSourceID(), boatLocation);
//System.out.println("Race Start Status Message");
RaceStartStatus rSS = (RaceStartStatus) data;
raceStartStatus = rSS;
break;
case YACHTEVENTCODE:
// System.out.println("Yacht Action Code!");
//no decoder
break;
case YACHTACTIONCODE:
// System.out.println("Yacht Action Code!");
//no decoder
break;
case CHATTERTEXT:
// System.out.println("Chatter Text Message!");
//no decoder
break;
case BOATLOCATION:
// System.out.println("Boat Location!");
BoatLocationMessage msg = (BoatLocationMessage) data;
if (boatLocation.containsKey(msg.getSourceID())){
if (msg.getTime() > boatLocation.get(msg.getSourceID()).getTime()){
boatLocation.put(msg.getSourceID(), msg);
}
}else{
boatLocation.put(msg.getSourceID(), msg);
}
// System.out.println("Boat Location Message!");
break;
case MARKROUNDING:
// System.out.println("Mark Rounding Message!");
MarkRounding mkrounding = (MarkRounding) data;
markRounding.put(mkrounding.getSourceID(), mkrounding);
break;
case COURSEWIND:
// System.out.println("Course Wind Message!");
courseWinds = (CourseWinds) data;
break;
case AVGWIND:
// System.out.println("Average Wind Message!");
AverageWind avgWind = (AverageWind) data;
averageWind = avgWind;
break;
default:
System.out.println("Broken Message!");
break;
} }
} }
}catch(IOException e){ //MarkRounding.
e.printStackTrace(); else if (message instanceof MarkRounding) {
} MarkRounding markRounding = (MarkRounding) message;
}
//System.out.println("Mark Rounding Message!");
if (this.markRoundingMap.containsKey(markRounding.getSourceID())) {
//If our markRoundingMap already contains a mark rounding message for this boat, check that the new message is actually for a later timestamp (i.e., newer).
if (markRounding.getTime() > this.markRoundingMap.get(markRounding.getSourceID()).getTime()){
//If it is, replace the old message.
this.markRoundingMap.put(markRounding.getSourceID(), markRounding);
}
}else{
//If the map _doesn't_ already contain a message for this boat, insert the message.
this.markRoundingMap.put(markRounding.getSourceID(), markRounding);
}
public boolean isReceiverLoop() { }
return receiverLoop; //CourseWinds.
} else if (message instanceof CourseWinds) {
CourseWinds courseWinds = (CourseWinds) message;
public static void main(String argv[]) throws Exception //System.out.println("Course Wind Message!");
{ this.courseWinds = courseWinds;
Socket socket = new Socket("livedata.americascup.com", 4941);
// Socket socket = new Socket(InetAddress.getLocalHost(), 4942); }
VisualiserInput receiver = new VisualiserInput(socket, new StreamedCourse()); //AverageWind.
receiver.run(); else if (message instanceof AverageWind) {
AverageWind averageWind = (AverageWind) message;
//System.out.println("Average Wind Message!");
this.averageWind = averageWind;
}
//Unrecognised message.
else {
System.out.println("Broken Message!");
}
}
} }
} }

Loading…
Cancel
Save