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