diff --git a/pom.xml b/pom.xml
index 2003bb14..2aa9257c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,7 +11,7 @@
mock
visualiser
network
- sharedModel
+ racevisionGame
https://eng-git.canterbury.ac.nz/SENG302-2016/team-7
diff --git a/racevisionGame/pom.xml b/racevisionGame/pom.xml
index f64fb5a6..bf2952b7 100644
--- a/racevisionGame/pom.xml
+++ b/racevisionGame/pom.xml
@@ -45,6 +45,12 @@
9.0
+
+ com.github.bfsmith
+ geotimezone
+ 1.0.3
+
+
diff --git a/racevisionGame/src/main/java/mock/app/Event.java b/racevisionGame/src/main/java/mock/app/Event.java
index 1328f2a2..6b41db4e 100644
--- a/racevisionGame/src/main/java/mock/app/Event.java
+++ b/racevisionGame/src/main/java/mock/app/Event.java
@@ -3,7 +3,9 @@ package mock.app;
import mock.model.MockRace;
import mock.model.Polars;
import network.Messages.Enums.MessageType;
+import network.Messages.Enums.XMLMessageType;
import network.Messages.LatestMessages;
+import network.Messages.XMLMessage;
import org.xml.sax.SAXException;
import shared.dataInput.*;
import shared.exceptions.InvalidBoatDataException;
@@ -79,13 +81,10 @@ public class Event {
private void sendXMLs() {
mockOutput.setRegattaXml(regattaXML);
- mockOutput.parseXMLString(regattaXML, MessageType.XMLMESSAGE.getValue());
mockOutput.setRaceXml(raceXML);
- mockOutput.parseXMLString(raceXML, MessageType.XMLMESSAGE.getValue());
mockOutput.setBoatsXml(boatXML);
- mockOutput.parseXMLString(boatXML, MessageType.XMLMESSAGE.getValue());
}
/**
diff --git a/racevisionGame/src/main/java/mock/app/MockOutput.java b/racevisionGame/src/main/java/mock/app/MockOutput.java
index ca584376..e217accf 100644
--- a/racevisionGame/src/main/java/mock/app/MockOutput.java
+++ b/racevisionGame/src/main/java/mock/app/MockOutput.java
@@ -4,38 +4,46 @@ package mock.app;
import network.BinaryMessageEncoder;
import network.MessageEncoders.RaceVisionByteEncoder;
-import network.MessageEncoders.XMLMessageEncoder;
-import network.Messages.BoatLocation;
+import network.Messages.*;
import network.Messages.Enums.MessageType;
-import network.Messages.LatestMessages;
-import network.Messages.RaceStatus;
-import network.Messages.XMLMessage;
+import network.Messages.Enums.XMLMessageType;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
-import java.util.concurrent.ArrayBlockingQueue;
/**
* TCP server to send race information to connected clients.
*/
public class MockOutput implements Runnable
{
- ///Timestamp of the last sent heartbeat message.
+ /**
+ * Timestamp of the last sent heartbeat message.
+ */
private long lastHeartbeatTime;
- ///Period for the heartbeat - that is, how often we send it.
+ /**
+ * Period for the heartbeat - that is, how often we send it.
+ */
private double heartbeatPeriod = 5.0;
- ///Port to expose server on.
+ /**
+ * Port to expose server on.
+ */
private int serverPort = 4942;
- ///Socket used to listen for clients on.
+ /**
+ * Socket used to listen for clients on.
+ */
private ServerSocket serverSocket;
- ///Socket used to communicate with a client.
+ /**
+ * Socket used to communicate with a client.
+ */
private Socket mockSocket;
- ///Output stream which wraps around mockSocket outstream.
+ /**
+ * Output stream which wraps around mockSocket outstream.
+ */
private DataOutputStream outToVisualiser;
@@ -47,17 +55,32 @@ public class MockOutput implements Runnable
- ///Sequence numbers used in messages.
- private short messageNumber = 1;
- private short xmlSequenceNumber = 1;
+ /**
+ * Ack numbers used in messages.
+ */
+ private int ackNumber = 1;
+
+
+ /**
+ * Sequence number for race xml messages.
+ */
+ private short raceXMLSequenceNumber = 1;
+
+ /**
+ * Sequence number for boat xml messages.
+ */
+ private short boatXMLSequenceNumber = 1;
+
+ /**
+ * Sequence number for regatta xml messages.
+ */
+ private short regattaXMLSequenceNumber = 1;
+
+ /**
+ * Sequence number for heartbeat messages.
+ */
private int heartbeatSequenceNum = 1;
- private int boatLocationSequenceNumber = 1;
- private int raceStatusSequenceNumber = 1;
- ///Strings containing XML data as strings.
- private String raceXml;
- private String regattaXml;
- private String boatsXml;
private boolean stop = false; //whether or not hte thread keeps running
@@ -74,6 +97,19 @@ public class MockOutput implements Runnable
this.latestMessages = latestMessages;
}
+
+
+ /**
+ * Increments the ackNumber value, and returns it.
+ * @return Incremented ackNumber.
+ */
+ private int getNextAckNumber(){
+ this.ackNumber++;
+
+ return this.ackNumber;
+ }
+
+
/**
* Calculates the time since last heartbeat message, in seconds.
* @return Time since last heartbeat message, in seconds.
@@ -83,56 +119,105 @@ public class MockOutput implements Runnable
return (now - lastHeartbeatTime) / 1000.0;
}
- //returns the heartbeat message
/**
- * Increment the heartbeat value
- * @return message for heartbeat data
+ * Generates the next heartbeat message and returns it. Increments the heartbeat sequence number.
+ * @return The next heartbeat message.
*/
- private byte[] heartbeat(){
- byte[] heartbeatMessage = RaceVisionByteEncoder.heartBeat(heartbeatSequenceNum);
+ private Heartbeat createHeartbeatMessage() {
+
+ //Create the heartbeat message.
+ Heartbeat heartbeat = new Heartbeat(this.heartbeatSequenceNum);
heartbeatSequenceNum++;
- BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder(MessageType.HEARTBEAT, System.currentTimeMillis(), messageNumber, (short)heartbeatMessage.length, heartbeatMessage);
- messageNumber++;
+
+ return heartbeat;
+ }
+
+ /**
+ * Serializes a heartbeat message into a packet to be sent, and returns the byte array.
+ * @param heartbeat The heartbeat message to serialize.
+ * @return Byte array containing the next heartbeat message.
+ */
+ private byte[] parseHeartbeat(Heartbeat heartbeat) {
+
+ //Serializes the heartbeat message.
+ byte[] heartbeatMessage = RaceVisionByteEncoder.heartBeat(heartbeat);
+
+ //Places the serialized message in a packet.
+ BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder(
+ MessageType.HEARTBEAT,
+ System.currentTimeMillis(),
+ getNextAckNumber(),
+ (short) heartbeatMessage.length,
+ heartbeatMessage );
+
return binaryMessageEncoder.getFullMessage();
+
}
+
/**
- * Used to give the mockOutput an xml string to be made into a message and sent
- * @param xmlString the xml string to send
- * @param messageType the kind of xml string, values given in AC35 spec (5 regatta, 6 race, 7 boat)
+ * Creates an XMLMessage of a specified subtype using the xml contents string.
+ * @param xmlString The contents of the xml file.
+ * @param messageType The subtype of xml message (race, regatta, boat).
+ * @return The created XMLMessage object.
*/
- public synchronized byte[] parseXMLString(String xmlString, int messageType) {
+ private XMLMessage createXMLMessage(String xmlString, XMLMessageType messageType) {
+
+ //Get the correct sequence number to use, and increment it.
+ short sequenceNumber = 0;
+ if (messageType == XMLMessageType.RACE) {
+ sequenceNumber = this.raceXMLSequenceNumber;
+ this.raceXMLSequenceNumber++;
+
+ } else if (messageType == XMLMessageType.BOAT) {
+ sequenceNumber = this.boatXMLSequenceNumber;
+ this.boatXMLSequenceNumber++;
+
+ } else if (messageType == XMLMessageType.REGATTA) {
+ sequenceNumber = this.regattaXMLSequenceNumber;
+ this.regattaXMLSequenceNumber++;
- XMLMessageEncoder encoder = new XMLMessageEncoder(
- messageNumber,
+ }
+
+ //Create the message.
+ XMLMessage message = new XMLMessage(
+ XMLMessage.currentVersionNumber,
+ getNextAckNumber(),
System.currentTimeMillis(),
messageType,
- xmlSequenceNumber,
- (short) xmlString.length(),
- xmlString);
+ sequenceNumber,
+ xmlString );
- //iterates the sequence numbers
- xmlSequenceNumber++;
+ return message;
+ }
+
+ /**
+ * Encodes/serialises a XMLMessage message, and returns it.
+ * @param xmlMessage The XMLMessage message to serialise.
+ * @return The XMLMessage message in a serialised form.
+ */
+ private synchronized byte[] parseXMLMessage(XMLMessage xmlMessage) {
- byte[] encodedXML = encoder.encode();
+ //Serialize the xml message.
+ byte[] encodedXML = RaceVisionByteEncoder.xmlMessage(xmlMessage);
+ //Place the message in a packet.
BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder(
MessageType.XMLMESSAGE,
System.currentTimeMillis(),
- messageNumber,
- (short)encodedXML.length,
- encodedXML);
+ xmlMessage.getAckNumber(), //We use the ack number from the xml message.
+ (short) encodedXML.length,
+ encodedXML );
- //iterates the message number
- messageNumber++;
-
return binaryMessageEncoder.getFullMessage();
}
+
+
/**
* Encodes/serialises a BoatLocation message, and returns it.
* @param boatLocation The BoatLocation message to serialise.
@@ -148,12 +233,10 @@ public class MockOutput implements Runnable
BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder(
MessageType.BOATLOCATION,
System.currentTimeMillis(),
- messageNumber,
+ getNextAckNumber(),
(short) encodedBoatLoc.length,
encodedBoatLoc );
- //iterates the message number
- messageNumber++;
return binaryMessageEncoder.getFullMessage();
@@ -166,9 +249,6 @@ public class MockOutput implements Runnable
*/
private synchronized byte[] parseRaceStatus(RaceStatus raceStatus){
- //iterates the sequence number
- raceStatusSequenceNumber++;
-
//Encodes the messages.
byte[] encodedRaceStatus = RaceVisionByteEncoder.raceStatus(raceStatus);
@@ -176,12 +256,10 @@ public class MockOutput implements Runnable
BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder(
MessageType.RACESTATUS,
System.currentTimeMillis(),
- messageNumber,
+ getNextAckNumber(),
(short) encodedRaceStatus.length,
encodedRaceStatus );
- //iterates the message number
- messageNumber++;
return binaryMessageEncoder.getFullMessage();
@@ -196,12 +274,14 @@ public class MockOutput implements Runnable
try {
while (!stop){
+ //Wait for a client to connect.
System.out.println("Waiting for a connection...");//TEMP DEBUG REMOVE
mockSocket = serverSocket.accept();
outToVisualiser = new DataOutputStream(mockSocket.getOutputStream());
- if (boatsXml == null || regattaXml == null || raceXml == null) {
+ //Wait until all of the xml files have been set.
+ if (!this.latestMessages.hasAllXMLMessages()) {
try {
Thread.sleep(500);
} catch (InterruptedException e) {
@@ -210,11 +290,6 @@ public class MockOutput implements Runnable
continue;
}
- //Encode xml files. We send them inside the loop, depending on the sentXMLs boolean.
- byte[] raceXMLBlob = parseXMLString(raceXml, XMLMessage.XMLTypeRace);
- byte[] regattaXMLBlob = parseXMLString(regattaXml, XMLMessage.XMLTypeRegatta);
- byte[] boatsXMLBlob = parseXMLString(boatsXml, XMLMessage.XMLTypeBoat);
-
long previousFrameTime = System.currentTimeMillis();
boolean sentXMLs = false;
@@ -234,19 +309,25 @@ public class MockOutput implements Runnable
//Sends a heartbeat every so often.
if (timeSinceHeartbeat() >= heartbeatPeriod) {
- outToVisualiser.write(heartbeat());
+ outToVisualiser.write(parseHeartbeat(createHeartbeatMessage()));
lastHeartbeatTime = System.currentTimeMillis();
}
//Send XML messages.
if (!sentXMLs) {
+ //Serialise them.
+ byte[] raceXMLBlob = parseXMLMessage(latestMessages.getRaceXMLMessage());
+ byte[] regattaXMLBlob = parseXMLMessage(latestMessages.getRegattaXMLMessage());
+ byte[] boatsXMLBlob = parseXMLMessage(latestMessages.getBoatXMLMessage());
+
+ //Send them.
outToVisualiser.write(raceXMLBlob);
outToVisualiser.write(regattaXMLBlob);
outToVisualiser.write(boatsXMLBlob);
sentXMLs = true;
}
- //Sens the RaceStatus message.
+ //Sends the RaceStatus message.
if (this.latestMessages.getRaceStatus() != null) {
byte[] raceStatusBlob = this.parseRaceStatus(this.latestMessages.getRaceStatus());
@@ -305,29 +386,42 @@ public class MockOutput implements Runnable
}
/**
- * Sets the Race XML to send
- * @param raceXml XML to send to the CLient
+ * Sets the Race XML to send.
+ * @param raceXml XML to send to the Client.
*/
public void setRaceXml(String raceXml) {
- this.raceXml = raceXml;
+ //Create the message.
+ XMLMessage message = this.createXMLMessage(raceXml, XMLMessageType.RACE);
+
+ //Place it in LatestMessages.
+ this.latestMessages.setRaceXMLMessage(message);
}
/**
- * Sets the Regatta XMl to send
- * @param regattaXml XML to send to CLient
+ * Sets the Regatta XMl to send.
+ * @param regattaXml XML to send to Client.
*/
public void setRegattaXml(String regattaXml) {
- this.regattaXml = regattaXml;
+ //Create the message.
+ XMLMessage message = this.createXMLMessage(regattaXml, XMLMessageType.REGATTA);
+
+ //Place it in LatestMessages.
+ this.latestMessages.setRegattaXMLMessage(message);
}
/**
- * Sets the Boats XML to send
- * @param boatsXml XMl to send to the CLient
+ * Sets the Boats XML to send.
+ * @param boatsXml XMl to send to the Client.
*/
public void setBoatsXml(String boatsXml) {
- this.boatsXml = boatsXml;
+ //Create the message.
+ XMLMessage message = this.createXMLMessage(boatsXml, XMLMessageType.BOAT);
+
+ //Place it in LatestMessages.
+ this.latestMessages.setBoatXMLMessage(message);
}
+
public static void main(String argv[]) throws Exception
{
MockOutput client = new MockOutput(new LatestMessages());
diff --git a/racevisionGame/src/main/java/network/BinaryMessageDecoder.java b/racevisionGame/src/main/java/network/BinaryMessageDecoder.java
index 1f8ba8e9..1c34ca85 100644
--- a/racevisionGame/src/main/java/network/BinaryMessageDecoder.java
+++ b/racevisionGame/src/main/java/network/BinaryMessageDecoder.java
@@ -151,7 +151,7 @@ public class BinaryMessageDecoder {
//System.out.println("XML Message!");
XMLMessageDecoder xmdecoder = new XMLMessageDecoder(messageBody);
xmdecoder.decode();
- return new XMLMessage(xmdecoder.getAckNumber(), xmdecoder.getTimeStamp(), xmdecoder.getXmlMsgSubType(), xmdecoder.getSequenceNumber(), xmdecoder.getXmlMsgLength(), xmdecoder.getXmlMessageInputStream());
+ return new XMLMessage(XMLMessage.currentVersionNumber, xmdecoder.getAckNumber(), xmdecoder.getTimeStamp(), xmdecoder.getXmlMsgSubType(), xmdecoder.getSequenceNumber(), xmdecoder.getXmlMessageContents());
case RACESTARTSTATUS:
//System.out.println("Race Start Status Message");
diff --git a/racevisionGame/src/main/java/network/MessageDecoders/XMLMessageDecoder.java b/racevisionGame/src/main/java/network/MessageDecoders/XMLMessageDecoder.java
index 5af12a78..18ead92e 100644
--- a/racevisionGame/src/main/java/network/MessageDecoders/XMLMessageDecoder.java
+++ b/racevisionGame/src/main/java/network/MessageDecoders/XMLMessageDecoder.java
@@ -1,5 +1,7 @@
package network.MessageDecoders;
+import network.Messages.Enums.XMLMessageType;
+
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
@@ -42,7 +44,7 @@ public class XMLMessageDecoder {
this.sequenceNumber = bytesToShort(sequenceNumberBytes);
this.xmlMsgLength = bytesToShort(xmlMsgLengthBytes);
- this.xmlMessage = new String(xmlMessagebytes);
+ this.xmlMessage = new String(xmlMessagebytes).trim();
}
public byte getMessageVersionNumber() {
@@ -57,8 +59,8 @@ public class XMLMessageDecoder {
return timeStamp;
}
- public byte getXmlMsgSubType() {
- return xmlMsgSubType;
+ public XMLMessageType getXmlMsgSubType() {
+ return XMLMessageType.fromByte(xmlMsgSubType);
}
public short getSequenceNumber() {
@@ -69,14 +71,14 @@ public class XMLMessageDecoder {
return xmlMsgLength;
}
+
+
/**
- * this will be used latter for the vis
- * @return xml string as inputsource
+ * Returns the contents of the XML message (e.g., the contents of a race.xml file).
+ * @return The contents of the XML message.
*/
- public InputStream getXmlMessageInputStream() {
- InputStream is = new ByteArrayInputStream(xmlMessage.trim().getBytes(StandardCharsets.UTF_8));
-// InputSource is = new InputSource(new StringReader(xmlMessage.trim()));
- return is;
+ public String getXmlMessageContents() {
+ return xmlMessage;
}
}
diff --git a/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java b/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java
index 9137ee24..2e811d96 100644
--- a/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java
+++ b/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java
@@ -6,6 +6,7 @@ import network.Messages.*;
import static network.Utils.ByteConverter.*;
import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
@@ -18,13 +19,16 @@ public class RaceVisionByteEncoder {
/**
* Serializes a heartbeat message.
- * @param seq Heartbeat value.
+ * @param heartbeat Heartbeat message.
* @return Serialized message.
*/
- public static byte[] heartBeat(long seq){
+ public static byte[] heartBeat(Heartbeat heartbeat) {
+
ByteBuffer heartBeat = ByteBuffer.allocate(4);
- heartBeat.put(longToBytes(seq, 4));
- byte [] result = heartBeat.array();
+ heartBeat.put(longToBytes(heartbeat.getSequenceNumber(), 4));
+
+ byte[] result = heartBeat.array();
+
return result;
}
@@ -176,6 +180,44 @@ public class RaceVisionByteEncoder {
return result.array();
}
+
+ /**
+ * Serializes an xml message into a byte array.
+ * @param xmlMessage The message to serialize.
+ * @return byte array contaning serialized message.
+ */
+ public static byte[] xmlMessage(XMLMessage xmlMessage) {
+
+
+ byte[] messageBytes = xmlMessage.getXmlMessage().getBytes(StandardCharsets.UTF_8);
+
+ ByteBuffer tempOutputByteBuffer = ByteBuffer.allocate(14 + messageBytes.length);
+
+ //ackNumber converted to bytes
+ byte[] ackNumberBytes = intToBytes(xmlMessage.getAckNumber(), 2);
+
+ //Timestamp converted to bytes.
+ byte[] timestampBytes = longToBytes(xmlMessage.getTimeStamp(), 6);
+
+ //sequenceNumber converted to bytes
+ byte[] sequenceNumberBytes = intToBytes(xmlMessage.getSequenceNumber(), 2);
+
+ //xmlMsgLength converted to bytes
+ byte[] xmlMsgLengthBytes = intToBytes(xmlMessage.getXmlMsgLength(), 2);
+
+
+ tempOutputByteBuffer.put(xmlMessage.getVersionNumber());
+ tempOutputByteBuffer.put(ackNumberBytes);
+ tempOutputByteBuffer.put(timestampBytes);
+ tempOutputByteBuffer.put(xmlMessage.getXmlMsgSubType().getValue());
+ tempOutputByteBuffer.put(sequenceNumberBytes);
+ tempOutputByteBuffer.put(xmlMsgLengthBytes);
+ tempOutputByteBuffer.put(messageBytes);
+
+ return tempOutputByteBuffer.array();
+
+ }
+
public static byte[] boatLocation(BoatLocation boatLocation){
int messageVersionNumber = 0b1;
byte[] time = longToBytes(boatLocation.getTime(), 6);
diff --git a/racevisionGame/src/main/java/network/MessageEncoders/XMLMessageEncoder.java b/racevisionGame/src/main/java/network/MessageEncoders/XMLMessageEncoder.java
deleted file mode 100644
index b4f6d060..00000000
--- a/racevisionGame/src/main/java/network/MessageEncoders/XMLMessageEncoder.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package network.MessageEncoders;
-
-import java.nio.ByteBuffer;
-
-import static network.Utils.ByteConverter.*;
-
-
-/**
- * Encodes a XML file into a message of AC35 format
- */
-public class XMLMessageEncoder {
- private byte[] messageVersionNumber;
- private short ackNumber;
- private long timeStamp;
- private byte[] xmlMsgSubType;
- private short sequenceNumber;
- private short xmlMsgLength;
- private String xmlMessage;
-
- public XMLMessageEncoder(short ackNumber, long timeStamp, int xmlMsgSubType, short sequenceNumber, short xmlMsgLength, String xmlMessage) {
- this.messageVersionNumber = intToBytes(1, 1);
- this.ackNumber = ackNumber;
- this.timeStamp = timeStamp;
- this.xmlMsgSubType = intToBytes(xmlMsgSubType, 1);
- this.sequenceNumber = sequenceNumber;
- this.xmlMsgLength = xmlMsgLength;
- this.xmlMessage = xmlMessage;
- }
-
- public byte[] encode() {
- byte[] messageBytes = xmlMessage.getBytes();
- if (messageBytes.length > this.xmlMsgLength) {
- //System.err.println("Xml message is to big");
- return null;
- }
- ByteBuffer tempOutputByteBuffer = ByteBuffer.allocate(14 + messageBytes.length);
-
- //ackNumber converted to bytes
- byte[] ackNumberBytes = shortToBytes(ackNumber, 2);
-
- //sequenceNumber converted to bytes
- byte[] sequenceNumberBytes = shortToBytes(sequenceNumber, 2);
-
- //xmlMsgLength converted to bytes
- byte[] xmlMsgLengthBytes = shortToBytes(xmlMsgLength, 2);
-
-
- tempOutputByteBuffer.put(messageVersionNumber);
- tempOutputByteBuffer.put(ackNumberBytes);
- tempOutputByteBuffer.put(longToBytes(timeStamp, 6));
- tempOutputByteBuffer.put(xmlMsgSubType);
- tempOutputByteBuffer.put(sequenceNumberBytes);
- tempOutputByteBuffer.put(xmlMsgLengthBytes);
- tempOutputByteBuffer.put(messageBytes);
-
- return tempOutputByteBuffer.array();
- }
-
-}
diff --git a/racevisionGame/src/main/java/network/Messages/AC35Data.java b/racevisionGame/src/main/java/network/Messages/AC35Data.java
index 0b8b1a9b..4b9ed952 100644
--- a/racevisionGame/src/main/java/network/Messages/AC35Data.java
+++ b/racevisionGame/src/main/java/network/Messages/AC35Data.java
@@ -8,7 +8,9 @@ import network.Messages.Enums.MessageType;
*/
public abstract class AC35Data {
- ///Message type from the header.
+ /**
+ * Message type from the header.
+ */
private MessageType type;
diff --git a/racevisionGame/src/main/java/network/Messages/Enums/XMLMessageType.java b/racevisionGame/src/main/java/network/Messages/Enums/XMLMessageType.java
new file mode 100644
index 00000000..94c523fc
--- /dev/null
+++ b/racevisionGame/src/main/java/network/Messages/Enums/XMLMessageType.java
@@ -0,0 +1,87 @@
+package network.Messages.Enums;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Enumeration that encapsulates the various types of XML messages that can be sent.
+ */
+public enum XMLMessageType {
+
+ /**
+ * A regatta.xml message.
+ */
+ REGATTA(5),
+
+ /**
+ * A race.xml message.
+ */
+ RACE(6),
+
+ /**
+ * A boats.xml message.
+ */
+ BOAT(7),
+
+ /**
+ * Used for unrecognised byte values.
+ */
+ NOT_A_MESSAGE_TYPE(0);
+
+
+ ///Primitive value of the enum.
+ private byte value;
+
+ /**
+ * Ctor. Creates a XMLMessageType enum from a given primitive integer value, cast to a byte.
+ * @param value Integer, which is cast to byte, to construct from.
+ */
+ private XMLMessageType(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 XMLMessageType values.
+ private static final Map byteToTypeMap = new HashMap<>();
+
+
+ /*
+ Static initialization block. Initializes the byteToTypeMap.
+ */
+ static {
+ for (XMLMessageType type : XMLMessageType.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 XMLMessageType value.
+ * @return The XMLMessageType value which corresponds to the given byte value.
+ */
+ public static XMLMessageType fromByte(byte messageTypeByte) {
+ //Gets the corresponding XMLMessageType from the map.
+ XMLMessageType type = byteToTypeMap.get(messageTypeByte);
+
+ if (type == null) {
+ //If the byte value wasn't found, return the NOTAMESSAGE XMLMessageType.
+ return XMLMessageType.NOT_A_MESSAGE_TYPE;
+ }
+ else {
+ //Otherwise, return the XMLMessageType.
+ return type;
+ }
+
+ }
+
+
+}
diff --git a/racevisionGame/src/main/java/network/Messages/Heartbeat.java b/racevisionGame/src/main/java/network/Messages/Heartbeat.java
index 8c0014d7..fb1dd23f 100644
--- a/racevisionGame/src/main/java/network/Messages/Heartbeat.java
+++ b/racevisionGame/src/main/java/network/Messages/Heartbeat.java
@@ -8,7 +8,9 @@ import network.Messages.Enums.MessageType;
*/
public class Heartbeat extends AC35Data {
- ///Sequence number of the heartbeat.
+ /**
+ * Sequence number of the heartbeat.
+ */
private long sequenceNumber;
/**
diff --git a/racevisionGame/src/main/java/network/Messages/LatestMessages.java b/racevisionGame/src/main/java/network/Messages/LatestMessages.java
index 139543a0..a0375ea6 100644
--- a/racevisionGame/src/main/java/network/Messages/LatestMessages.java
+++ b/racevisionGame/src/main/java/network/Messages/LatestMessages.java
@@ -1,5 +1,8 @@
package network.Messages;
+import network.Messages.Enums.XMLMessageType;
+import shared.dataInput.RaceDataSource;
+
import java.util.HashMap;
import java.util.Map;
@@ -23,6 +26,11 @@ public class LatestMessages {
*/
private final Map boatLocationMap = new HashMap<>();
+ /**
+ * A map of the last MarkRounding message received, for each boat.
+ */
+ private final Map markRoundingMap = new HashMap<>();
+
/**
* The last AverageWind message received.
*/
@@ -34,6 +42,22 @@ public class LatestMessages {
private CourseWinds courseWinds;
+ /**
+ * The latest race data XML message.
+ */
+ private XMLMessage raceXMLMessage;
+
+ /**
+ * The latest boat data XML message.
+ */
+ private XMLMessage boatXMLMessage;
+
+ /**
+ * The latest regatta data XML message.
+ */
+ private XMLMessage regattaXMLMessage;
+
+
/**
@@ -100,6 +124,24 @@ public class LatestMessages {
boatLocationMap.put(boatLocation.getSourceID(), boatLocation);
}
+ /**
+ * Returns the latest MarkRounding message received for a given boat.
+ * @param sourceID Source ID of the boat.
+ * @return The latest MarkRounding message for the specified boat.
+ */
+ public MarkRounding getMarkRounding(int sourceID) {
+ return markRoundingMap.get(sourceID);
+ }
+
+ /**
+ * Inserts a MarkRounding message for a given boat.
+ * @param markRounding The MarkRounding message to set.
+ */
+ public void setMarkRounding(MarkRounding markRounding) {
+ //TODO should compare the sequence number of the new boatLocation with the existing boatLocation for this boat (if it exists), and use the newer one.
+ markRoundingMap.put(markRounding.getSourceID(), markRounding);
+ }
+
/**
@@ -136,7 +178,115 @@ public class LatestMessages {
}
+ /**
+ * Returns the map of boat sourceIDs to BoatLocation messages.
+ * @return Map between boat sourceID and BoatLocation.
+ */
public Map getBoatLocationMap() {
return boatLocationMap;
}
+
+ /**
+ * Returns the map of boat sourceIDs to BoatStatus messages.
+ * @return Map between boat sourceID and BoatStatus.
+ */
+ public Map getBoatStatusMap() {
+ return boatStatusMap;
+ }
+
+ /**
+ * Returns the map of boat sourceIDs to MarkRounding messages.
+ * @return Map between boat sourceID and MarkRounding.
+ */
+ public Map getMarkRoundingMap() {
+ return markRoundingMap;
+ }
+
+
+
+ /**
+ * Returns the latest race xml message.
+ * @return The latest race xml message.
+ */
+ public XMLMessage getRaceXMLMessage() {
+ return raceXMLMessage;
+ }
+
+ /**
+ * Sets the latest race xml message to a specified race XML message.
+ * @param raceXMLMessage The new race XML message to use.
+ */
+ public void setRaceXMLMessage(XMLMessage raceXMLMessage) {
+ this.raceXMLMessage = raceXMLMessage;
+ }
+
+
+ /**
+ * Returns the latest boat xml message.
+ * @return The latest boat xml message.
+ */
+ public XMLMessage getBoatXMLMessage() {
+ return boatXMLMessage;
+ }
+
+ /**
+ * Sets the latest boat xml message to a specified boat XML message.
+ * @param boatXMLMessage The new boat XML message to use.
+ */
+ public void setBoatXMLMessage(XMLMessage boatXMLMessage) {
+ this.boatXMLMessage = boatXMLMessage;
+ }
+
+
+ /**
+ * Returns the latest regatta xml message.
+ * @return The latest regatta xml message.
+ */
+ public XMLMessage getRegattaXMLMessage() {
+ return regattaXMLMessage;
+ }
+
+ /**
+ * Sets the latest regatta xml message to a specified regatta XML message.
+ * @param regattaXMLMessage The new regatta XML message to use.
+ */
+ public void setRegattaXMLMessage(XMLMessage regattaXMLMessage) {
+ this.regattaXMLMessage = regattaXMLMessage;
+ }
+
+ /**
+ * Checks the type of xml message, and places it in this LatestMessages object.
+ * @param xmlMessage The new xml message to use.
+ */
+ public void setXMLMessage(XMLMessage xmlMessage) {
+
+ if (xmlMessage.getXmlMsgSubType() == XMLMessageType.RACE) {
+ this.setRaceXMLMessage(xmlMessage);
+
+ } else if (xmlMessage.getXmlMsgSubType() == XMLMessageType.REGATTA) {
+ this.setRegattaXMLMessage(xmlMessage);
+
+ } else if (xmlMessage.getXmlMsgSubType() == XMLMessageType.BOAT) {
+ this.setBoatXMLMessage(xmlMessage);
+
+ }
+
+ }
+
+ /**
+ * Returns whether or not there is an xml message for each message type.
+ * @return True if race, boat, and regatta have an xml message, false otherwise.
+ */
+ public boolean hasAllXMLMessages() {
+
+ if ((this.regattaXMLMessage == null) || (this.boatXMLMessage == null) || (this.raceXMLMessage == null)) {
+ return false;
+
+ } else {
+ return true;
+
+ }
+
+ }
+
}
diff --git a/racevisionGame/src/main/java/network/Messages/XMLMessage.java b/racevisionGame/src/main/java/network/Messages/XMLMessage.java
index 5f6d7b8d..e3a4aa1b 100644
--- a/racevisionGame/src/main/java/network/Messages/XMLMessage.java
+++ b/racevisionGame/src/main/java/network/Messages/XMLMessage.java
@@ -2,49 +2,86 @@ package network.Messages;
import network.Messages.Enums.MessageType;
+import network.Messages.Enums.XMLMessageType;
import java.io.InputStream;
+import java.nio.charset.StandardCharsets;
/**
* Created by fwy13 on 25/04/17.
*/
public class XMLMessage extends AC35Data {
+
+ /**
+ * The current version number for xml messages is 1.
+ */
+ public static byte currentVersionNumber = 1;
+
+ /**
+ * The version number of the message.
+ */
+ private byte versionNumber;
+
+ /**
+ * The ack number of the message.
+ */
private int ackNumber;
+
+ /**
+ * The timestamp of the message.
+ * Milliseconds since unix epoch.
+ */
private long timeStamp;
- private int xmlMsgSubType;
+
+ /**
+ * The subtype of the xml message (e.g., race xml message).
+ */
+ private XMLMessageType xmlMsgSubType;
+
+ /**
+ * The sequence number of this specific xml subtype.
+ * Increments whenever the xml contents for a specific xml subtype changes.
+ */
private int sequenceNumber;
+
+ /**
+ * The length of the xml message.
+ * Number of bytes.
+ */
private int xmlMsgLength;
- private InputStream xmlMessage;
- public static int XMLTypeRegatta = 5;
- public static int XMLTypeRace = 6;
- public static int XMLTypeBoat = 7;
+ /**
+ * The contents of the xml message.
+ */
+ private String xmlMessage;
+
/**
* Constructor for an XML Message
+ * @param versionNumber The version number of the xml message.
* @param ackNumber Number for acknowledgement inherited for the AC35Data Packet
* @param timeStamp Time received
* @param xmlMsgSubType Type of XML message
* @param sequenceNumber Order that it has arrived in
- * @param xmlMsgLength Length of the xml message
* @param xmlMessage XML message
*/
- public XMLMessage(int ackNumber, long timeStamp, int xmlMsgSubType, int sequenceNumber, int xmlMsgLength, InputStream xmlMessage){
+ public XMLMessage(byte versionNumber, int ackNumber, long timeStamp, XMLMessageType xmlMsgSubType, int sequenceNumber, String xmlMessage) {
super(MessageType.XMLMESSAGE);
+ this.versionNumber = versionNumber;
this.ackNumber = ackNumber;
this.timeStamp = timeStamp;
this.xmlMsgSubType = xmlMsgSubType;
this.sequenceNumber = sequenceNumber;
- this.xmlMsgLength = xmlMsgLength;
+ this.xmlMsgLength = xmlMessage.getBytes(StandardCharsets.UTF_8).length;
this.xmlMessage = xmlMessage;
}
/**
- * Get the XML Message
- * @return the XML message as an input stream
+ * Get the XML Message.
+ * @return the XML message as string.
*/
- public InputStream getXmlMessage() {
+ public String getXmlMessage() {
return xmlMessage;
}
@@ -52,7 +89,48 @@ public class XMLMessage extends AC35Data {
* Get the type of message
* @return Gets the type of message the XML message is
*/
- public int getXmlMsgSubType() {
+ public XMLMessageType getXmlMsgSubType() {
return xmlMsgSubType;
}
+
+
+ /**
+ * Returns the version number of this xml message.
+ * @return The version number of this xml message.
+ */
+ public byte getVersionNumber() {
+ return versionNumber;
+ }
+
+ /**
+ * Returns the ack number of this xml message.
+ * @return The ack number of this xml message.
+ */
+ public int getAckNumber() {
+ return ackNumber;
+ }
+
+ /**
+ * Returns the timestamp of this xml message.
+ * @return The timestamp of this xml message.
+ */
+ public long getTimeStamp() {
+ return timeStamp;
+ }
+
+ /**
+ * Returns the sequence number of this xml message. This is specific to each message subtype.
+ * @return The sequence number of this xml message.
+ */
+ public int getSequenceNumber() {
+ return sequenceNumber;
+ }
+
+ /**
+ * Returns the length, in number of bytes, of the xml message.
+ * @return The length, in bytes, of the xml message.
+ */
+ public int getXmlMsgLength() {
+ return xmlMsgLength;
+ }
}
diff --git a/racevisionGame/src/main/java/shared/model/Race.java b/racevisionGame/src/main/java/shared/model/Race.java
index 49b13b50..84e22935 100644
--- a/racevisionGame/src/main/java/shared/model/Race.java
+++ b/racevisionGame/src/main/java/shared/model/Race.java
@@ -48,10 +48,15 @@ public abstract class Race implements Runnable {
protected LatestMessages latestMessages;
/**
- * The sequence number of the latest boatLocation message sent or received.
+ * The sequence number of the latest BoatLocation message sent or received.
*/
protected int boatLocationSequenceNumber = 1;
+ /**
+ * The sequence number of the latest RaceStatus message sent or received.
+ */
+ protected int raceStatusSequenceNumber = 1;
+
/**
diff --git a/racevisionGame/src/main/java/visualiser/Controllers/ConnectionController.java b/racevisionGame/src/main/java/visualiser/Controllers/ConnectionController.java
index a84f4fc7..7dacbd5e 100644
--- a/racevisionGame/src/main/java/visualiser/Controllers/ConnectionController.java
+++ b/racevisionGame/src/main/java/visualiser/Controllers/ConnectionController.java
@@ -8,7 +8,7 @@ import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;
import javafx.scene.layout.AnchorPane;
-import seng302.RaceConnection;
+import visualiser.model.RaceConnection;
import java.io.IOException;
import java.net.Socket;
@@ -22,7 +22,7 @@ public class ConnectionController extends Controller {
@FXML
private AnchorPane connectionWrapper;
@FXML
- private TableView connectionTable;
+ private TableView connectionTable;
@FXML
private TableColumn hostnameColumn;
@FXML
@@ -73,7 +73,7 @@ public class ConnectionController extends Controller {
*/
public void connectSocket() {
try{
- RaceConnection connection = (RaceConnection)connectionTable.getSelectionModel().getSelectedItem();
+ RaceConnection connection = connectionTable.getSelectionModel().getSelectedItem();
Socket socket = new Socket(connection.getHostname(), connection.getPort());
connectionWrapper.setVisible(false);
parent.enterLobby(socket);
diff --git a/racevisionGame/src/main/java/visualiser/Controllers/MainController.java b/racevisionGame/src/main/java/visualiser/Controllers/MainController.java
index 5c3d15fb..6fa4c96c 100644
--- a/racevisionGame/src/main/java/visualiser/Controllers/MainController.java
+++ b/racevisionGame/src/main/java/visualiser/Controllers/MainController.java
@@ -6,6 +6,7 @@ import javafx.scene.layout.AnchorPane;
import seng302.Model.Boat;
import seng302.Model.RaceClock;
import seng302.VisualiserInput;
+import visualiser.model.VisualiserBoat;
import java.net.Socket;
import java.net.URL;
@@ -28,7 +29,9 @@ public class MainController extends Controller {
startController.enterLobby(socket);
}
- public void enterFinish(ObservableList boats) { finishController.enterFinish(boats); }
+ public void enterFinish(ObservableList boats) {
+ finishController.enterFinish(boats);
+ }
/**
* Main Controller for the applications will house the menu and the displayed pane.
diff --git a/racevisionGame/src/main/java/visualiser/Controllers/StartController.java b/racevisionGame/src/main/java/visualiser/Controllers/StartController.java
index 7b126d2f..85288e1d 100644
--- a/racevisionGame/src/main/java/visualiser/Controllers/StartController.java
+++ b/racevisionGame/src/main/java/visualiser/Controllers/StartController.java
@@ -14,6 +14,7 @@ import javafx.scene.layout.GridPane;
import visualiser.app.VisualiserInput;
import visualiser.model.RaceClock;
import visualiser.model.VisualiserBoat;
+import visualiser.model.VisualiserRace;
import java.io.IOException;
import java.net.Socket;
@@ -48,6 +49,11 @@ public class StartController extends Controller implements Observer {
private VisualiserInput visualiserInput;
+ /**
+ * The race object which describes the currently occurring race.
+ */
+ private VisualiserRace visualiserRace;
+
///Tracks whether the race has been started (that is, has startRaceNoScaling() be called).
private boolean hasRaceStarted = false;
@@ -65,7 +71,7 @@ public class StartController extends Controller implements Observer {
@Override
public void initialize(URL location, ResourceBundle resources){
- raceData = new StreamedCourse();
+ this.visualiserRace = new VisualiserRace();
raceData.addObserver(this);
}
diff --git a/racevisionGame/src/main/java/visualiser/app/VisualiserInput.java b/racevisionGame/src/main/java/visualiser/app/VisualiserInput.java
index 0d6d97ed..9c629a37 100644
--- a/racevisionGame/src/main/java/visualiser/app/VisualiserInput.java
+++ b/racevisionGame/src/main/java/visualiser/app/VisualiserInput.java
@@ -1,7 +1,16 @@
package visualiser.app;
import javafx.application.Platform;
+import network.BinaryMessageDecoder;
+import network.Exceptions.InvalidMessageException;
import network.Messages.*;
import org.xml.sax.SAXException;
+import shared.dataInput.BoatXMLReader;
+import shared.dataInput.RaceXMLReader;
+import shared.dataInput.RegattaXMLReader;
+import shared.exceptions.InvalidBoatDataException;
+import shared.exceptions.InvalidRaceDataException;
+import shared.exceptions.InvalidRegattaDataException;
+import shared.exceptions.XMLReaderException;
import javax.xml.parsers.ParserConfigurationException;
import java.io.DataInputStream;
@@ -12,78 +21,65 @@ import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
+import static network.Utils.ByteConverter.bytesToShort;
+
/**
* 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 {
- ///Timestamp of the last heartbeat.
+ /**
+ * Timestamp of the last heartbeat.
+ */
private long lastHeartbeatTime = -1;
- ///Sequence number of the last heartbeat.
+ /**
+ * Sequence number of the last heartbeat.
+ */
private long lastHeartbeatSequenceNum = -1;
- ///The socket that we have connected to.
- private Socket connectionSocket;
+ /**
+ * The socket that we have connected to.
+ */
+ private Socket connectionSocket;
- ///The last RaceStatus message received.
- private RaceStatus raceStatus;
- ///A map of the last BoatStatus message received, for each boat.
- private final Map boatStatusMap = new HashMap<>();
+ /**
+ * InputStream (from the socket).
+ */
+ private DataInputStream inStream;
- ///A map of the last BoatLocation message received, for each boat.
- private final Map boatLocationMap = new HashMap<>();
- ///The last AverageWind message received.
- private AverageWind averageWind;
+ /**
+ * An object containing the set of latest messages to write to.
+ * Every server frame, VisualiserInput reads messages from its inputStream, and write them to this.
+ */
+ private LatestMessages latestMessages;
- ///The last CourseWinds message received.
- private CourseWinds courseWinds;
- ///A map of the last MarkRounding message received, for each boat.
- private final Map markRoundingMap = new HashMap<>();
-
- ///InputStream (from the socket).
- private DataInputStream inStream;
/**
* 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 {
+ public VisualiserInput(Socket socket) throws IOException {
+
this.connectionSocket = socket;
+
//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 DataInputStream(connectionSocket.getInputStream());
- this.course = course;
+
+ this.latestMessages = new LatestMessages();
+
this.lastHeartbeatTime = System.currentTimeMillis();
}
- /**
- * Provides StreamedCourse container for fixed course data.
- * @return Course for current VisualiserInput instance.
- * @see seng302.Mock.StreamedCourse
- */
- public StreamedCourse getCourse() {
- return course;
- }
- /**
- * Returns the last boat location message associated with the given boat source ID.
- * @param sourceID Unique global identifier for the boat.
- * @return The most recent location message.
- */
- public BoatLocation getBoatLocationMessage(int sourceID) {
- return boatLocationMap.get(sourceID);
- }
- public BoatStatus getBoatStatusMessage(int sourceID) {
- return boatStatusMap.get(sourceID);
- }
+
/**
* Calculates the time since last heartbeat, in milliseconds.
@@ -94,62 +90,7 @@ public class VisualiserInput implements Runnable {
return (now - lastHeartbeatTime);
}
- /**
- * Returns the boat locations map. Maps from Integer (Boat ID) to BoatLocation.
- * @return Map of boat locations.
- */
- public Map getBoatLocationMap() {
- return boatLocationMap;
- }
-
- /**
- * Gets the status of the race.
- * @return The status of the race.
- */
- public RaceStatus getRaceStatus() {
- return raceStatus;
- }
- /**
- * Returns the boat statuses map. Maps from Integer (Boat ID) to BoatStatus.
- * @return Map of boat statuses.
- */
- public Map getBoatStatusMap() {
- return boatStatusMap;
- }
-
- /**
- * Returns the average wind of the race.
- * @return Average wind in the race.
- */
- public AverageWind getAverageWind() {
- return averageWind;
- }
-
- /**
- * Returns winds in the course.
- * @return Winds that are in the course.
- */
- public CourseWinds getCourseWinds() {
- return courseWinds;
- }
-
-
- /**
- * Returns the mark roundings map. Maps from Integer (Boat ID) to MarkRounding.
- * @return Map of mark roundings.
- */
- public Map getMarkRoundingMap() {
- return markRoundingMap;
- }
-
- /**
- * Sets the wind direction for the current course.
- * @param direction The new wind direction for the course.
- */
- private void setCourseWindDirection(double direction) {
- this.course.setWindDirection(direction);
- }
/**
* Reads and returns the next message as an array of bytes from the socket. Use getNextMessage() to get the actual message object instead.
@@ -258,166 +199,153 @@ public class VisualiserInput implements Runnable {
}
//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);
- }
- }
- //RaceStatus.
- else if (message instanceof RaceStatus) {
- RaceStatus raceStatus = (RaceStatus) message;
-
- //System.out.println("Race Status Message");
- this.raceStatus = raceStatus;
- for (BoatStatus boatStatus: this.raceStatus.getBoatStatuses()) {
- this.boatStatusMap.put(boatStatus.getSourceID(), boatStatus);
- }
- setCourseWindDirection(raceStatus.getScaledWindDirection());
- }
- //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!");
-
- Platform.runLater(()-> {
- 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.
- }
-
- } 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 | StreamedCourseXMLException e) {
- System.err.println("Error creating StreamedCourseXMLReader: " + e.getMessage());
- //Continue to the next loop iteration/message.
- }
-
- } 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.
- }
-
- }
- });
- }
- //RaceStartStatus.
- else if (message instanceof RaceStartStatus) {
+ switch (message.getType()) {
- //System.out.println("Race Start Status Message");
- }
- //YachtEventCode.
- /*else if (message instanceof YachtEventCode) {
- YachtEventCode yachtEventCode = (YachtEventCode) message;
+ //Heartbeat.
+ case 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);
+ }
+ }
+
+ //RaceStatus.
+ case RACESTATUS: {
+ RaceStatus raceStatus = (RaceStatus) message;
+
+ //System.out.println("Race Status Message");
+ this.latestMessages.setRaceStatus(raceStatus);
+
+ for (BoatStatus boatStatus : raceStatus.getBoatStatuses()) {
+ this.latestMessages.setBoatStatus(boatStatus);
+ }
+
+ }
+
+ //DisplayTextMessage.
+ case DISPLAYTEXTMESSAGE: {
+ //System.out.println("Display Text Message");
+ //No decoder for this.
+ }
+
+ //XMLMessage.
+ case XMLMESSAGE: {
+ XMLMessage xmlMessage = (XMLMessage) message;
+
+ //System.out.println("XML Message!");
+
+ this.latestMessages.setXMLMessage(xmlMessage);
+
+ }
+
+ //RaceStartStatus.
+ case RACESTARTSTATUS: {
+
+ //System.out.println("Race Start Status Message");
+ }
+
+ //YachtEventCode.
+ case YACHTEVENTCODE: {
+ //YachtEventCode yachtEventCode = (YachtEventCode) message;
//System.out.println("Yacht Event Code!");
//No decoder for this.
- }*/
- //YachtActionCode.
- /*else if (message instanceof YachtActionCode) {
- YachtActionCode yachtActionCode = (YachtActionCode) message;
+ }
- //System.out.println("Yacht Action Code!");
- //No decoder for this.
+ //YachtActionCode.
+ case YACHTACTIONCODE: {
+ //YachtActionCode yachtActionCode = (YachtActionCode) message;
- }*/
- //ChatterText.
- /*else if (message instanceof ChatterText) {
- ChatterText chatterText = (ChatterText) message;
+ //System.out.println("Yacht Action Code!");
+ // No decoder for this.
- //System.out.println("Chatter Text Message!");
- //No decoder for this.
+ }
- }*/
- //BoatLocation.
- else if (message instanceof BoatLocation) {
- BoatLocation boatLocation = (BoatLocation) message;
-
- //System.out.println("Boat Location!");
- if (this.boatLocationMap.containsKey(boatLocation.getSourceID())) {
- //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).
- if (boatLocation.getTime() > this.boatLocationMap.get(boatLocation.getSourceID()).getTime()){
- //If it is, replace the old message.
- this.boatLocationMap.put(boatLocation.getSourceID(), boatLocation);
- }
- }else{
- //If the map _doesn't_ already contain a message for this boat, insert the message.
- this.boatLocationMap.put(boatLocation.getSourceID(), boatLocation);
- }
- }
- //MarkRounding.
- 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);
- }
+ //ChatterText.
+ case CHATTERTEXT: {
+ //ChatterText chatterText = (ChatterText) message;
- }
- //CourseWinds.
- else if (message instanceof CourseWinds) {
+ //System.out.println("Chatter Text Message!");
+ //No decoder for this.
- //System.out.println("Course Wind Message!");
- this.courseWinds = (CourseWinds) message;
+ }
- }
- //AverageWind.
- else if (message instanceof AverageWind) {
+ //BoatLocation.
+ case BOATLOCATION: {
+ BoatLocation boatLocation = (BoatLocation) message;
+
+ //System.out.println("Boat Location!");
+
+ BoatLocation existingBoatLocation = this.latestMessages.getBoatLocationMap().get(boatLocation.getSourceID());
+ if (existingBoatLocation != null) {
+ //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).
+ if (boatLocation.getTime() > existingBoatLocation.getTime()) {
+ //If it is, replace the old message.
+ this.latestMessages.setBoatLocation(boatLocation);
+ }
+ } else {
+ //If the map _doesn't_ already contain a message for this boat, insert the message.
+ this.latestMessages.setBoatLocation(boatLocation);
+ }
+ }
- //System.out.println("Average Wind Message!");
- this.averageWind = (AverageWind) message;
+ //MarkRounding.
+ case MARKROUNDING: {
+ MarkRounding markRounding = (MarkRounding) message;
- }
- //Unrecognised message.
- else {
- System.out.println("Broken Message!");
- }
+ //System.out.println("Mark Rounding Message!");
+
+ MarkRounding existingMarkRounding = this.latestMessages.getMarkRoundingMap().get(markRounding.getSourceID());
+ if (existingMarkRounding != null) {
+
+ //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() > existingMarkRounding.getTime()) {
+ //If it is, replace the old message.
+ this.latestMessages.setMarkRounding(markRounding);
+ }
+
+ } else {
+ //If the map _doesn't_ already contain a message for this boat, insert the message.
+ this.latestMessages.setMarkRounding(markRounding);
+ }
+
+ }
+
+ //CourseWinds.
+ case COURSEWIND: {
+
+ //System.out.println("Course Wind Message!");
+ CourseWinds courseWinds = (CourseWinds) message;
+
+ this.latestMessages.setCourseWinds(courseWinds);
+
+ }
+
+ //AverageWind.
+ case AVGWIND: {
+
+ //System.out.println("Average Wind Message!");
+ AverageWind averageWind = (AverageWind) message;
+
+ this.latestMessages.setAverageWind(averageWind);
+
+ }
+
+ //Unrecognised message.
+ default: {
+ System.out.println("Broken Message!");
+ }
+ }
}
}
diff --git a/racevisionGame/src/main/java/visualiser/model/RaceClock.java b/racevisionGame/src/main/java/visualiser/model/RaceClock.java
index a74f00ff..b076e4bf 100644
--- a/racevisionGame/src/main/java/visualiser/model/RaceClock.java
+++ b/racevisionGame/src/main/java/visualiser/model/RaceClock.java
@@ -5,7 +5,7 @@ import com.github.bfsmith.geotimezone.TimeZoneResult;
import javafx.animation.AnimationTimer;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
-import seng302.GPSCoordinate;
+import shared.model.GPSCoordinate;
import java.time.Duration;
import java.time.LocalDateTime;
@@ -18,9 +18,9 @@ import java.util.Date;
/**
* This class is used to implement a clock which keeps track of and
* displays times relevant to a race. This is displayed on the
- * {@link seng302.Model.ResizableRaceCanvas ResizableRaceCanvas} via the
- * {@link seng302.Controllers.RaceController RaceController} and the
- * {@link seng302.Controllers.StartController StartController}.
+ * {@link ResizableRaceCanvas} via the
+ * {@link visualiser.Controllers.RaceController} and the
+ * {@link visualiser.Controllers.StartController}.
*/
public class RaceClock implements Runnable {
private long lastTime;
diff --git a/racevisionGame/src/main/java/visualiser/model/VisualiserRace.java b/racevisionGame/src/main/java/visualiser/model/VisualiserRace.java
index fd42dff0..7644a93b 100644
--- a/racevisionGame/src/main/java/visualiser/model/VisualiserRace.java
+++ b/racevisionGame/src/main/java/visualiser/model/VisualiserRace.java
@@ -9,6 +9,7 @@ import network.Messages.BoatLocation;
import network.Messages.BoatStatus;
import network.Messages.Enums.BoatStatusEnum;
import network.Messages.Enums.RaceStatusEnum;
+import network.Messages.LatestMessages;
import network.Messages.RaceStatus;
import shared.dataInput.BoatDataSource;
import shared.dataInput.RaceDataSource;
@@ -30,9 +31,6 @@ import java.util.Map;
public class VisualiserRace extends Race {
- //TODO replace with LatestMessages
- private final VisualiserInput visualiserInput;
-
/**
* An observable list of boats in the race.
*/
@@ -49,9 +47,17 @@ public class VisualiserRace extends Race {
- public VisualiserRace(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource, List colors, VisualiserInput visualiserInput, RaceController controller) {
+ /**
+ * Constructs a race object with a given RaceDataSource, BoatDataSource, and RegattaDataSource and receives events from LatestMessages.
+ * @param boatDataSource Data source for boat related data (yachts and marker boats).
+ * @param raceDataSource Data source for race related data (participating boats, legs, etc...).
+ * @param regattaDataSource Data source for race related data (course name, location, timezone, etc...).
+ * @param colors A collection of colors used to assign a color to each boat.
+ * @param latestMessages The LatestMessages to send events to.
+ */
+ public VisualiserRace(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource, List colors, LatestMessages latestMessages, RaceController controller) {
- super(boatDataSource, raceDataSource, regattaDataSource);
+ super(boatDataSource, raceDataSource, regattaDataSource, latestMessages);
this.boats = FXCollections.observableArrayList(this.generateVisualiserBoats(boatDataSource.getBoats(), raceDataSource.getParticipants(), colors));
@@ -59,10 +65,8 @@ public class VisualiserRace extends Race {
this.boatMarkers = FXCollections.observableArrayList(boatDataSource.getMarkerBoats().values());
-
this.controller = controller;
- this.visualiserInput = visualiserInput;
}
@@ -327,16 +331,16 @@ public class VisualiserRace extends Race {
//Update racing boats.
- updateBoats(boats, visualiserInput.getBoatLocationMap(), visualiserInput.getBoatStatusMap());
+ updateBoats(boats, latestMessages.getBoatLocationMap(), latestMessages.getBoatStatusMap());
//And their positions (e.g., 5th).
updateBoatPositions(boats);
//Update marker boats.
- updateMarkers(boatMarkers, visualiserInput.getBoatLocationMap(), visualiserInput.getBoatStatusMap());
+ updateMarkers(boatMarkers, latestMessages.getBoatLocationMap(), latestMessages.getBoatStatusMap());
//Update race status.
- updateRaceStatus(visualiserInput.getRaceStatus());
+ updateRaceStatus(latestMessages.getRaceStatus());
//TODO tidy this circular dependency up