Added LatestMessages to network.Messages. This is an object that encapsulates the latest up to date set of race messages.
Race stores a reference to it. MockRace writes to it, and eventually, VisualiserRace will read from it. Updated MockRace, MockOutput, Event to use it. Angle now implements hashCode().
This commit is contained in:
@@ -3,6 +3,7 @@ package mock.app;
|
||||
import mock.model.MockRace;
|
||||
import mock.model.Polars;
|
||||
import network.Messages.Enums.MessageType;
|
||||
import network.Messages.LatestMessages;
|
||||
import org.xml.sax.SAXException;
|
||||
import shared.dataInput.*;
|
||||
import shared.exceptions.InvalidBoatDataException;
|
||||
@@ -28,6 +29,8 @@ public class Event {
|
||||
private String boatXML;
|
||||
private Polars boatPolars;
|
||||
private MockOutput mockOutput;
|
||||
private LatestMessages latestMessages;
|
||||
|
||||
|
||||
public Event(String raceXML, String regattaXML, String boatXML, Polars boatPolars) {
|
||||
|
||||
@@ -36,9 +39,13 @@ public class Event {
|
||||
this.regattaXML = regattaXML;
|
||||
this.boatPolars = boatPolars;
|
||||
|
||||
this.latestMessages = new LatestMessages();
|
||||
|
||||
|
||||
try {
|
||||
mockOutput = new MockOutput();
|
||||
this.mockOutput = new MockOutput(this.latestMessages);
|
||||
new Thread(mockOutput).start();
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -57,7 +64,7 @@ public class Event {
|
||||
RegattaDataSource regattaDataSource = new RegattaXMLReader(this.regattaXML);
|
||||
|
||||
//Create and start race.
|
||||
MockRace newRace = new MockRace(boatDataSource, raceDataSource, regattaDataSource, this.boatPolars, this.mockOutput);
|
||||
MockRace newRace = new MockRace(boatDataSource, raceDataSource, regattaDataSource, this.boatPolars, this.latestMessages);
|
||||
|
||||
new Thread(newRace).start();
|
||||
|
||||
|
||||
@@ -7,6 +7,7 @@ import network.MessageEncoders.RaceVisionByteEncoder;
|
||||
import network.MessageEncoders.XMLMessageEncoder;
|
||||
import network.Messages.BoatLocation;
|
||||
import network.Messages.Enums.MessageType;
|
||||
import network.Messages.LatestMessages;
|
||||
import network.Messages.RaceStatus;
|
||||
import network.Messages.XMLMessage;
|
||||
|
||||
@@ -37,8 +38,14 @@ public class MockOutput implements Runnable
|
||||
///Output stream which wraps around mockSocket outstream.
|
||||
private DataOutputStream outToVisualiser;
|
||||
|
||||
///A queue that contains items that are waiting to be sent.
|
||||
private ArrayBlockingQueue<byte[]> messagesToSendQueue = new ArrayBlockingQueue<>(99999999);
|
||||
|
||||
/**
|
||||
* An object containing the set of latest messages to send.
|
||||
* Every server frame, MockOutput reads messages from this, and send them.
|
||||
*/
|
||||
private LatestMessages latestMessages;
|
||||
|
||||
|
||||
|
||||
///Sequence numbers used in messages.
|
||||
private short messageNumber = 1;
|
||||
@@ -56,11 +63,15 @@ public class MockOutput implements Runnable
|
||||
|
||||
/**
|
||||
* Ctor.
|
||||
* @param latestMessages The collection of messages to send to connected clients.
|
||||
* @throws IOException if server socket cannot be opened.
|
||||
*/
|
||||
public MockOutput() throws IOException {
|
||||
lastHeartbeatTime = System.currentTimeMillis();
|
||||
serverSocket = new ServerSocket(serverPort);
|
||||
public MockOutput(LatestMessages latestMessages) throws IOException {
|
||||
|
||||
this.lastHeartbeatTime = System.currentTimeMillis();
|
||||
this.serverSocket = new ServerSocket(serverPort);
|
||||
|
||||
this.latestMessages = latestMessages;
|
||||
|
||||
}
|
||||
/**
|
||||
@@ -91,77 +102,92 @@ public class MockOutput implements Runnable
|
||||
* @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)
|
||||
*/
|
||||
public synchronized void parseXMLString(String xmlString, int messageType){
|
||||
XMLMessageEncoder encoder = new XMLMessageEncoder(messageNumber, System.currentTimeMillis(), messageType, xmlSequenceNumber,(short) xmlString.length(), xmlString);
|
||||
public synchronized byte[] parseXMLString(String xmlString, int messageType) {
|
||||
|
||||
XMLMessageEncoder encoder = new XMLMessageEncoder(
|
||||
messageNumber,
|
||||
System.currentTimeMillis(),
|
||||
messageType,
|
||||
xmlSequenceNumber,
|
||||
(short) xmlString.length(),
|
||||
xmlString);
|
||||
|
||||
|
||||
//iterates the sequence numbers
|
||||
xmlSequenceNumber++;
|
||||
|
||||
byte[] encodedXML = encoder.encode();
|
||||
|
||||
BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder(MessageType.XMLMESSAGE, System.currentTimeMillis(), messageNumber, (short)encodedXML.length, encodedXML);
|
||||
BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder(
|
||||
MessageType.XMLMESSAGE,
|
||||
System.currentTimeMillis(),
|
||||
messageNumber,
|
||||
(short)encodedXML.length,
|
||||
encodedXML);
|
||||
|
||||
|
||||
//iterates the message number
|
||||
messageNumber++;
|
||||
|
||||
addMessageToBufferToSend(binaryMessageEncoder.getFullMessage());
|
||||
return binaryMessageEncoder.getFullMessage();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to give the mocOutput information about boat location to be made into a message and sent
|
||||
* @param sourceID id of the boat
|
||||
* @param lat latitude of boat
|
||||
* @param lon longitude of boat
|
||||
* @param heading heading of boat
|
||||
* @param speed speed of boat
|
||||
* @param time historical time of race
|
||||
* Encodes/serialises a BoatLocation message, and returns it.
|
||||
* @param boatLocation The BoatLocation message to serialise.
|
||||
* @return The BoatLocation message in a serialised form.
|
||||
*/
|
||||
public synchronized void parseBoatLocation(int sourceID, double lat, double lon, double heading, double speed, long time){
|
||||
private synchronized byte[] parseBoatLocation(BoatLocation boatLocation){
|
||||
|
||||
BoatLocation boatLocation = new BoatLocation(sourceID, lat, lon, boatLocationSequenceNumber, heading, speed, time);
|
||||
//iterates the sequence number
|
||||
boatLocationSequenceNumber++;
|
||||
|
||||
//encodeds the messages
|
||||
//Encodes the message.
|
||||
byte[] encodedBoatLoc = RaceVisionByteEncoder.boatLocation(boatLocation);
|
||||
|
||||
//encodeds the full message with header
|
||||
BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder(MessageType.BOATLOCATION, System.currentTimeMillis(), messageNumber, (short)encodedBoatLoc.length,
|
||||
encodedBoatLoc);
|
||||
//Encodes the full message with header.
|
||||
BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder(
|
||||
MessageType.BOATLOCATION,
|
||||
System.currentTimeMillis(),
|
||||
messageNumber,
|
||||
(short) encodedBoatLoc.length,
|
||||
encodedBoatLoc );
|
||||
|
||||
//iterates the message number
|
||||
messageNumber++;
|
||||
|
||||
addMessageToBufferToSend(binaryMessageEncoder.getFullMessage());
|
||||
return binaryMessageEncoder.getFullMessage();
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the race status data and add it to the buffer to be sent
|
||||
* @param raceStatus race status to parses
|
||||
* Encodes/serialises a RaceStatus message, and returns it.
|
||||
* @param raceStatus The RaceStatus message to serialise.
|
||||
* @return The RaceStatus message in a serialised form.
|
||||
*/
|
||||
public synchronized void parseRaceStatus(RaceStatus raceStatus){
|
||||
private synchronized byte[] parseRaceStatus(RaceStatus raceStatus){
|
||||
|
||||
//iterates the sequence number
|
||||
raceStatusSequenceNumber++;
|
||||
|
||||
//encodeds the messages
|
||||
//Encodes the messages.
|
||||
byte[] encodedRaceStatus = RaceVisionByteEncoder.raceStatus(raceStatus);
|
||||
|
||||
//encodeds the full message with header
|
||||
BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder(MessageType.RACESTATUS, System.currentTimeMillis(), messageNumber, (short)encodedRaceStatus.length,
|
||||
encodedRaceStatus);
|
||||
//Encodes the full message with header.
|
||||
BinaryMessageEncoder binaryMessageEncoder = new BinaryMessageEncoder(
|
||||
MessageType.RACESTATUS,
|
||||
System.currentTimeMillis(),
|
||||
messageNumber,
|
||||
(short) encodedRaceStatus.length,
|
||||
encodedRaceStatus );
|
||||
|
||||
//iterates the message number
|
||||
messageNumber++;
|
||||
|
||||
addMessageToBufferToSend(binaryMessageEncoder.getFullMessage());
|
||||
return binaryMessageEncoder.getFullMessage();
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a message to the buffer to be sent
|
||||
* @param messagesToSendBuffer message to add to the buffer
|
||||
*/
|
||||
private synchronized void addMessageToBufferToSend(byte[] messagesToSendBuffer) {
|
||||
this.messagesToSendQueue.add(messagesToSendBuffer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sending loop of the Server
|
||||
@@ -175,7 +201,7 @@ public class MockOutput implements Runnable
|
||||
|
||||
outToVisualiser = new DataOutputStream(mockSocket.getOutputStream());
|
||||
|
||||
if (boatsXml == null || regattaXml == null || raceXml == null){
|
||||
if (boatsXml == null || regattaXml == null || raceXml == null) {
|
||||
try {
|
||||
Thread.sleep(500);
|
||||
} catch (InterruptedException e) {
|
||||
@@ -184,34 +210,91 @@ public class MockOutput implements Runnable
|
||||
continue;
|
||||
}
|
||||
|
||||
parseXMLString(raceXml, XMLMessage.XMLTypeRace);
|
||||
parseXMLString(regattaXml, XMLMessage.XMLTypeRegatta);
|
||||
parseXMLString(boatsXml, XMLMessage.XMLTypeBoat);
|
||||
//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;
|
||||
|
||||
|
||||
while(true) {
|
||||
try {
|
||||
//Sends a heartbeat every so often.
|
||||
if (timeSinceHeartbeat() >= heartbeatPeriod) {
|
||||
outToVisualiser.write(heartbeat());
|
||||
lastHeartbeatTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
//Checks the buffer to see if there is anything to send.
|
||||
while (messagesToSendQueue.size() > 0) {
|
||||
//Grabs message from head of queue.
|
||||
byte[] binaryMessage = messagesToSendQueue.remove();
|
||||
long currentFrameTime = System.currentTimeMillis();
|
||||
|
||||
//sends the message to the visualiser
|
||||
outToVisualiser.write(binaryMessage);
|
||||
//This is the time elapsed, in milliseconds, since the last server "frame".
|
||||
long framePeriod = currentFrameTime - previousFrameTime;
|
||||
|
||||
//We only attempt to send packets every X milliseconds.
|
||||
long minimumFramePeriod = 16;
|
||||
if (framePeriod >= minimumFramePeriod) {
|
||||
|
||||
//Sends a heartbeat every so often.
|
||||
if (timeSinceHeartbeat() >= heartbeatPeriod) {
|
||||
outToVisualiser.write(heartbeat());
|
||||
lastHeartbeatTime = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
//Send XML messages.
|
||||
if (!sentXMLs) {
|
||||
outToVisualiser.write(raceXMLBlob);
|
||||
outToVisualiser.write(regattaXMLBlob);
|
||||
outToVisualiser.write(boatsXMLBlob);
|
||||
sentXMLs = true;
|
||||
}
|
||||
|
||||
//Sens the RaceStatus message.
|
||||
if (this.latestMessages.getRaceStatus() != null) {
|
||||
byte[] raceStatusBlob = this.parseRaceStatus(this.latestMessages.getRaceStatus());
|
||||
|
||||
this.outToVisualiser.write(raceStatusBlob);
|
||||
}
|
||||
|
||||
//Send all of the BoatLocation messages.
|
||||
for (int sourceID : this.latestMessages.getBoatLocationMap().keySet()) {
|
||||
|
||||
//Get the message.
|
||||
BoatLocation boatLocation = this.latestMessages.getBoatLocation(sourceID);
|
||||
if (boatLocation != null) {
|
||||
|
||||
//Encode.
|
||||
byte[] boatLocationBlob = this.parseBoatLocation(boatLocation);
|
||||
|
||||
//Write it.
|
||||
this.outToVisualiser.write(boatLocationBlob);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
previousFrameTime = currentFrameTime;
|
||||
|
||||
|
||||
} else {
|
||||
//Wait until the frame period will be large enough.
|
||||
long timeToWait = minimumFramePeriod - framePeriod;
|
||||
|
||||
try {
|
||||
Thread.sleep(timeToWait);
|
||||
} catch (InterruptedException e) {
|
||||
//If we get interrupted, exit the function.
|
||||
e.printStackTrace();
|
||||
//Re-set the interrupt flag.
|
||||
Thread.currentThread().interrupt();
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
}catch(SocketException e){
|
||||
|
||||
} catch (SocketException e) {
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@@ -247,7 +330,7 @@ public class MockOutput implements Runnable
|
||||
|
||||
public static void main(String argv[]) throws Exception
|
||||
{
|
||||
MockOutput client = new MockOutput();
|
||||
MockOutput client = new MockOutput(new LatestMessages());
|
||||
client.run();
|
||||
}
|
||||
|
||||
|
||||
@@ -2,8 +2,10 @@ package mock.model;
|
||||
|
||||
import javafx.animation.AnimationTimer;
|
||||
import mock.app.MockOutput;
|
||||
import network.Messages.BoatLocation;
|
||||
import network.Messages.BoatStatus;
|
||||
import network.Messages.Enums.BoatStatusEnum;
|
||||
import network.Messages.LatestMessages;
|
||||
import network.Messages.RaceStatus;
|
||||
import network.Utils.AC35UnitConverter;
|
||||
import shared.dataInput.BoatDataSource;
|
||||
@@ -52,12 +54,6 @@ public class MockRace extends Race {
|
||||
private int dnfChance = 0;
|
||||
|
||||
|
||||
/**
|
||||
* The mockOutput to send messages to.
|
||||
*/
|
||||
private MockOutput mockOutput;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Used to generate random numbers when changing the wind direction.
|
||||
@@ -88,13 +84,12 @@ public class MockRace extends Race {
|
||||
* @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 polars The polars table to be used for boat simulation.
|
||||
* @param mockOutput The mockOutput to send events to.
|
||||
* @param latestMessages The LatestMessages to send events to.
|
||||
*/
|
||||
public MockRace(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource, Polars polars, MockOutput mockOutput) {
|
||||
public MockRace(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource, Polars polars, LatestMessages latestMessages) {
|
||||
|
||||
super(boatDataSource, raceDataSource, regattaDataSource);
|
||||
super(boatDataSource, raceDataSource, regattaDataSource, latestMessages);
|
||||
|
||||
this.mockOutput = mockOutput;
|
||||
|
||||
this.boats = this.generateMockBoats(boatDataSource.getBoats(), raceDataSource.getParticipants(), polars);
|
||||
|
||||
@@ -175,14 +170,20 @@ public class MockRace extends Race {
|
||||
*/
|
||||
private void parseIndividualMark(Mark mark) {
|
||||
|
||||
this.mockOutput.parseBoatLocation(
|
||||
//Create message.
|
||||
BoatLocation boatLocation = new BoatLocation(
|
||||
mark.getSourceID(),
|
||||
mark.getPosition().getLatitude(),
|
||||
mark.getPosition().getLongitude(),
|
||||
0,
|
||||
0,
|
||||
this.totalTimeElapsed + this.startTime
|
||||
);
|
||||
boatLocationSequenceNumber,
|
||||
0, 0,
|
||||
totalTimeElapsed + startTime);
|
||||
|
||||
//Iterates the sequence number.
|
||||
boatLocationSequenceNumber++;
|
||||
|
||||
this.latestMessages.setBoatLocation(boatLocation);
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -206,14 +207,19 @@ public class MockRace extends Race {
|
||||
*/
|
||||
private void parseIndividualBoatLocation(MockBoat boat) {
|
||||
|
||||
this.mockOutput.parseBoatLocation(
|
||||
BoatLocation boatLocation = new BoatLocation(
|
||||
boat.getSourceID(),
|
||||
boat.getCurrentPosition().getLatitude(),
|
||||
boat.getCurrentPosition().getLongitude(),
|
||||
boatLocationSequenceNumber,
|
||||
boat.getBearing().degrees(),
|
||||
boat.getCurrentSpeed(),
|
||||
startTime + totalTimeElapsed
|
||||
);
|
||||
startTime + totalTimeElapsed);
|
||||
|
||||
//Iterates the sequence number.
|
||||
boatLocationSequenceNumber++;
|
||||
|
||||
this.latestMessages.setBoatLocation(boatLocation);
|
||||
|
||||
}
|
||||
|
||||
@@ -272,7 +278,6 @@ public class MockRace extends Race {
|
||||
boatStatuses.add(boatStatus);
|
||||
}
|
||||
|
||||
//TODO REFACTOR for consistency, could send parameters to mockOutput instead of the whole racestatus. This will also fix the sequence number issue.
|
||||
|
||||
//Convert wind direction and speed to ints. //TODO this conversion should be done inside the racestatus class.
|
||||
int windDirectionInt = AC35UnitConverter.encodeHeading(this.windDirection.degrees());
|
||||
@@ -283,12 +288,14 @@ public class MockRace extends Race {
|
||||
System.currentTimeMillis(),
|
||||
this.raceId,
|
||||
this.getRaceStatusEnum().getValue(),
|
||||
this.startTime, windDirectionInt,
|
||||
this.startTime,
|
||||
windDirectionInt,
|
||||
windSpeedInt,
|
||||
this.getRaceType().getValue(),
|
||||
boatStatuses );
|
||||
boatStatuses);
|
||||
|
||||
mockOutput.parseRaceStatus(raceStatus);
|
||||
|
||||
this.latestMessages.setRaceStatus(raceStatus);
|
||||
|
||||
|
||||
}
|
||||
@@ -427,7 +434,6 @@ public class MockRace extends Race {
|
||||
parseRaceStatus();
|
||||
|
||||
if (iters > 500) {
|
||||
mockOutput.stop();
|
||||
stop();
|
||||
}
|
||||
iters++;
|
||||
|
||||
Reference in New Issue
Block a user