From 8ef906472bd13d98d8a559c8cbfd89d6186871f2 Mon Sep 17 00:00:00 2001 From: fjc40 Date: Sat, 5 Aug 2017 23:27:28 +1200 Subject: [PATCH] Renamed Heartbeat to HeartBeat. Added HeartBeatDecoder. Added HeartBeatEncoder. BinaryMessageDecoder now uses HeartBeatDecoder. MockOutput now logs a warning if a heartBeat cannot be encoded. Added HeartBeatDecoderTest. issue #35 #36 #story[1095] --- .../src/main/java/mock/app/MockOutput.java | 26 ++++--- .../java/network/BinaryMessageDecoder.java | 5 +- .../MessageDecoders/HeartBeatDecoder.java | 53 +++++++++++++ .../MessageEncoders/EncoderFactory.java | 2 + .../MessageEncoders/HeartBeatEncoder.java | 40 ++++++++++ .../RaceVisionByteEncoder.java | 13 ---- .../{Heartbeat.java => HeartBeat.java} | 4 +- .../java/visualiser/app/VisualiserInput.java | 21 +----- .../MessageDecoders/HeartBeatDecoderTest.java | 74 +++++++++++++++++++ 9 files changed, 193 insertions(+), 45 deletions(-) create mode 100644 racevisionGame/src/main/java/network/MessageDecoders/HeartBeatDecoder.java create mode 100644 racevisionGame/src/main/java/network/MessageEncoders/HeartBeatEncoder.java rename racevisionGame/src/main/java/network/Messages/{Heartbeat.java => HeartBeat.java} (87%) create mode 100644 racevisionGame/src/test/java/network/MessageDecoders/HeartBeatDecoderTest.java diff --git a/racevisionGame/src/main/java/mock/app/MockOutput.java b/racevisionGame/src/main/java/mock/app/MockOutput.java index b773a06a..23e7b738 100644 --- a/racevisionGame/src/main/java/mock/app/MockOutput.java +++ b/racevisionGame/src/main/java/mock/app/MockOutput.java @@ -7,12 +7,9 @@ import network.Exceptions.InvalidMessageException; import network.MessageEncoders.RaceVisionByteEncoder; import network.Messages.*; import network.Messages.Enums.MessageType; -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.logging.Level; import java.util.logging.Logger; @@ -101,24 +98,25 @@ public class MockOutput implements Runnable * Generates the next heartbeat message and returns it. Increments the heartbeat sequence number. * @return The next heartbeat message. */ - private Heartbeat createHeartbeatMessage() { + private HeartBeat createHeartbeatMessage() { //Create the heartbeat message. - Heartbeat heartbeat = new Heartbeat(this.heartbeatSequenceNum); + HeartBeat heartBeat = new HeartBeat(this.heartbeatSequenceNum); heartbeatSequenceNum++; - return heartbeat; + return heartBeat; } /** * Serializes a heartbeat message into a packet to be sent, and returns the byte array. - * @param heartbeat The heartbeat message to serialize. + * @param heartBeat The heartbeat message to serialize. * @return Byte array containing the next heartbeat message. + * @throws InvalidMessageException Thrown if the message cannot be encoded. */ - private byte[] parseHeartbeat(Heartbeat heartbeat) { + private byte[] parseHeartbeat(HeartBeat heartBeat) throws InvalidMessageException { //Serializes the heartbeat message. - byte[] heartbeatMessage = RaceVisionByteEncoder.heartBeat(heartbeat); + byte[] heartbeatMessage = RaceVisionByteEncoder.encode(heartBeat); //Places the serialized message in a packet. BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder( @@ -213,7 +211,15 @@ public class MockOutput implements Runnable public void sendHeartBeat() throws IOException { //Sends a heartbeat every so often. if (timeSinceHeartbeat() >= heartbeatPeriod) { - outToVisualiser.write(parseHeartbeat(createHeartbeatMessage())); + + HeartBeat heartBeat = createHeartbeatMessage(); + + try { + outToVisualiser.write(parseHeartbeat(heartBeat)); + } catch (InvalidMessageException e) { + Logger.getGlobal().log(Level.WARNING, "Could not encode HeartBeat: " + heartBeat, e); + } + lastHeartbeatTime = System.currentTimeMillis(); } } diff --git a/racevisionGame/src/main/java/network/BinaryMessageDecoder.java b/racevisionGame/src/main/java/network/BinaryMessageDecoder.java index ecc6b2f3..69ecc1f0 100644 --- a/racevisionGame/src/main/java/network/BinaryMessageDecoder.java +++ b/racevisionGame/src/main/java/network/BinaryMessageDecoder.java @@ -133,9 +133,8 @@ public class BinaryMessageDecoder { switch(mType) { case HEARTBEAT: //System.out.println("Decoding HeartBeat Message!"); - //TODO maybe use HeartbeatDecoder.decode(message). - //TODO also, decoders for each message type should encapsulate the constructing of the object. E.g., return HeartbeatDecoder.decode(message);. - return new Heartbeat(bytesToLong(messageBody)); + HeartBeatDecoder heartBeatDecoder = new HeartBeatDecoder(messageBody); + return heartBeatDecoder.getMessage(); case RACESTATUS: //System.out.println("Race Status Message"); diff --git a/racevisionGame/src/main/java/network/MessageDecoders/HeartBeatDecoder.java b/racevisionGame/src/main/java/network/MessageDecoders/HeartBeatDecoder.java new file mode 100644 index 00000000..0984a9ea --- /dev/null +++ b/racevisionGame/src/main/java/network/MessageDecoders/HeartBeatDecoder.java @@ -0,0 +1,53 @@ +package network.MessageDecoders; + +import network.Messages.Enums.BoatActionEnum; +import network.Messages.HeartBeat; + +import static network.Utils.ByteConverter.bytesToLong; + +/** + * Decodes {@link network.Messages.HeartBeat} messages. + */ +public class HeartBeatDecoder { + + /** + * The encoded message. + */ + private byte[] encodedMessage; + + /** + * The decoded message. + */ + private HeartBeat message; + + + + /** + * Constructs a decoder to decode a given message. + * @param encodedMessage The message to decode. + */ + public HeartBeatDecoder(byte[] encodedMessage) { + this.encodedMessage = encodedMessage; + + decode(); + } + + + /** + * Decodes the contained message. + */ + private void decode() { + + message = new HeartBeat(bytesToLong(encodedMessage)); + } + + + /** + * Returns the decoded message. + * @return The decoded message. + */ + public HeartBeat getMessage() { + return message; + } + +} diff --git a/racevisionGame/src/main/java/network/MessageEncoders/EncoderFactory.java b/racevisionGame/src/main/java/network/MessageEncoders/EncoderFactory.java index e23541db..8eff9cae 100644 --- a/racevisionGame/src/main/java/network/MessageEncoders/EncoderFactory.java +++ b/racevisionGame/src/main/java/network/MessageEncoders/EncoderFactory.java @@ -29,6 +29,8 @@ public class EncoderFactory { switch (type) { + case HEARTBEAT: return new HeartBeatEncoder(); + case BOATLOCATION: return new BoatLocationEncoder(); case REQUEST_TO_JOIN: return new RequestToJoinEncoder(); diff --git a/racevisionGame/src/main/java/network/MessageEncoders/HeartBeatEncoder.java b/racevisionGame/src/main/java/network/MessageEncoders/HeartBeatEncoder.java new file mode 100644 index 00000000..934d1217 --- /dev/null +++ b/racevisionGame/src/main/java/network/MessageEncoders/HeartBeatEncoder.java @@ -0,0 +1,40 @@ +package network.MessageEncoders; + + +import network.Messages.AC35Data; +import network.Messages.HeartBeat; + +import java.nio.ByteBuffer; + +import static network.Utils.ByteConverter.intToBytes; +import static network.Utils.ByteConverter.longToBytes; + +/** + * This encoder can encode a {@link HeartBeat} message. + */ +public class HeartBeatEncoder implements MessageEncoder { + + + /** + * Constructor. + */ + public HeartBeatEncoder() { + } + + + @Override + public byte[] encode(AC35Data message) { + + //Downcast. + HeartBeat heartbeat = (HeartBeat) message; + + //Message is 4 bytes. + ByteBuffer heartBeat = ByteBuffer.allocate(4); + heartBeat.put(longToBytes(heartbeat.getSequenceNumber(), 4)); + + byte[] result = heartBeat.array(); + + return result; + + } +} diff --git a/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java b/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java index 1bd0d1f9..51e232d7 100644 --- a/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java +++ b/racevisionGame/src/main/java/network/MessageEncoders/RaceVisionByteEncoder.java @@ -19,20 +19,7 @@ import java.util.List; */ public class RaceVisionByteEncoder { - /** - * Serializes a heartbeat message. - * @param heartbeat Heartbeat message. - * @return Serialized message. - */ - public static byte[] heartBeat(Heartbeat heartbeat) { - - ByteBuffer heartBeat = ByteBuffer.allocate(4); - heartBeat.put(longToBytes(heartbeat.getSequenceNumber(), 4)); - byte[] result = heartBeat.array(); - - return result; - } /** * Serializes a RaceStatus message. diff --git a/racevisionGame/src/main/java/network/Messages/Heartbeat.java b/racevisionGame/src/main/java/network/Messages/HeartBeat.java similarity index 87% rename from racevisionGame/src/main/java/network/Messages/Heartbeat.java rename to racevisionGame/src/main/java/network/Messages/HeartBeat.java index fb1dd23f..35f378ac 100644 --- a/racevisionGame/src/main/java/network/Messages/Heartbeat.java +++ b/racevisionGame/src/main/java/network/Messages/HeartBeat.java @@ -6,7 +6,7 @@ import network.Messages.Enums.MessageType; /** * Represents a Heartbeat message. */ -public class Heartbeat extends AC35Data { +public class HeartBeat extends AC35Data { /** * Sequence number of the heartbeat. @@ -17,7 +17,7 @@ public class Heartbeat extends AC35Data { * Ctor. * @param sequenceNumber Sequence number of the heartbeat. */ - public Heartbeat(long sequenceNumber) { + public HeartBeat(long sequenceNumber) { super(MessageType.HEARTBEAT); this.sequenceNumber = sequenceNumber; } diff --git a/racevisionGame/src/main/java/visualiser/app/VisualiserInput.java b/racevisionGame/src/main/java/visualiser/app/VisualiserInput.java index 0b8102b5..e77a2fef 100644 --- a/racevisionGame/src/main/java/visualiser/app/VisualiserInput.java +++ b/racevisionGame/src/main/java/visualiser/app/VisualiserInput.java @@ -1,26 +1,13 @@ 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; import java.io.IOException; import java.net.Socket; import java.nio.ByteBuffer; import java.util.Arrays; -import java.util.HashMap; -import java.util.Map; -import java.util.concurrent.ArrayBlockingQueue; import static network.Utils.ByteConverter.bytesToShort; @@ -213,12 +200,12 @@ public class VisualiserInput implements Runnable { //Heartbeat. case HEARTBEAT: { - Heartbeat heartbeat = (Heartbeat) message; + 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) { + if (heartBeat.getSequenceNumber() > this.lastHeartbeatSequenceNum) { lastHeartbeatTime = System.currentTimeMillis(); - lastHeartbeatSequenceNum = heartbeat.getSequenceNumber(); + lastHeartbeatSequenceNum = heartBeat.getSequenceNumber(); //System.out.println("HeartBeat Message! " + lastHeartbeatSequenceNum); } diff --git a/racevisionGame/src/test/java/network/MessageDecoders/HeartBeatDecoderTest.java b/racevisionGame/src/test/java/network/MessageDecoders/HeartBeatDecoderTest.java new file mode 100644 index 00000000..ca1086e7 --- /dev/null +++ b/racevisionGame/src/test/java/network/MessageDecoders/HeartBeatDecoderTest.java @@ -0,0 +1,74 @@ +package network.MessageDecoders; + +import network.Exceptions.InvalidMessageException; +import network.MessageEncoders.RaceVisionByteEncoder; +import network.Messages.BoatAction; +import network.Messages.Enums.BoatActionEnum; +import network.Messages.HeartBeat; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + + +/** + * Test for the HeartBeat encoder and decoder + */ +public class HeartBeatDecoderTest { + + + /** + * Encodes and decodes a given message. + * @param message Message to encode/decode. + * @return The decoded message. + */ + private HeartBeat encodeDecodeMessage(HeartBeat message) throws InvalidMessageException { + + //Encode. + byte [] testEncodedMessage = RaceVisionByteEncoder.encode(message); + + //Decode. + HeartBeatDecoder testDecoder = new HeartBeatDecoder(testEncodedMessage); + HeartBeat decodedMessage = testDecoder.getMessage(); + + return decodedMessage; + } + + + /** + * Tests if a heartbeat message with a given sequence number can be encoded and decoded correctly. + * @param sequenceNumber The sequenceNumber to use. + */ + private void heartBeatTest(long sequenceNumber) throws Exception { + + //Prepare message. + HeartBeat beforeMessage = new HeartBeat(sequenceNumber); + + + //Encode/decode it. + HeartBeat afterMessage = encodeDecodeMessage(beforeMessage); + + + //Compare. + assertEquals(beforeMessage.getSequenceNumber(), afterMessage.getSequenceNumber()); + + } + + + /** + * Tests if a heartbeat message with a sequence number of zero can be encoded and decoded correctly. + */ + @Test + public void heartBeatZeroTest() throws Exception { + heartBeatTest(0); + } + + /** + * Tests if a heartbeat message with a sequence number of 1234512 can be encoded and decoded correctly. + */ + @Test + public void heartBeatNonZeroTest() throws Exception { + heartBeatTest(1234512); + } + + +}