I had accidentally broken the raceXML construction - fixed it.

Fixed a few issues where boat didn't have nextMark/previousMark times under certain circumstances.
LatestMessages wasn't actually notifying properly - fixed.
The various XML readers now treat a string constructor as containing file contents, not file name - this was how it was mostly already used.
Fixed some issues in RaceXMLReader, including where element.getChildNodes() was used instead of element.getElementsByName(...).
Boat: changes speed to a doubleProperty. Changed leg to a Property<Leg>.
Race.lastFps is now an IntegerProperty.
Added breaks to the case statements in VisualiserInput. Whoops.
ResizableRaceCanvas now handles drawing the race boundary. Tidied up code a bit.
Removed ResizableRaceMap.

Started refactoring Sparkline. Currently doesn't work.
This commit is contained in:
fjc40
2017-07-10 08:35:43 +12:00
parent abbbf70146
commit 7d3cf6ee80
38 changed files with 1414 additions and 775 deletions
@@ -31,12 +31,12 @@ public class BoatXMLReader extends XMLReader implements BoatDataSource {
/**
* Constructor for Boat XML using a file read as a resource.
*
* @param filePath Name/path of file to read. Read as a resource.
* @param fileContents Contents of xml file.
* @throws XMLReaderException Thrown if the file cannot be parsed.
* @throws InvalidBoatDataException Thrown if the file cannot be parsed correctly.
*/
public BoatXMLReader(String filePath) throws XMLReaderException, InvalidBoatDataException {
super(filePath);
public BoatXMLReader(String fileContents) throws XMLReaderException, InvalidBoatDataException {
super(fileContents);
//Attempt to read boat xml file.
try {
@@ -82,13 +82,13 @@ public class RaceXMLReader extends XMLReader implements RaceDataSource {
/**
* Constructor for Streamed Race XML
* @param filePath file path to read
* @param fileContents Contents of xml file.
* @throws XMLReaderException Thrown if an XML reader cannot be constructed for the given file.
* @throws InvalidRaceDataException Thrown if the XML file is invalid in some way.
*/
public RaceXMLReader(String filePath) throws XMLReaderException, InvalidRaceDataException {
public RaceXMLReader(String fileContents) throws XMLReaderException, InvalidRaceDataException {
super(filePath);
super(fileContents);
//Attempt to read race xml file.
@@ -101,8 +101,9 @@ public class RaceXMLReader extends XMLReader implements RaceDataSource {
/**
* Reads the contents of the race xml file.
* @throws InvalidRaceDataException Thrown if we cannot parse the document properly.
*/
private void read() {
private void read() throws InvalidRaceDataException {
readRace();
readParticipants();
readCourse();
@@ -127,7 +128,7 @@ public class RaceXMLReader extends XMLReader implements RaceDataSource {
//Race type.
String raceTypeString = getTextValueOfNode(settings, "RaceType");
raceType = RaceTypeEnum.valueOf(raceTypeString);
raceType = RaceTypeEnum.fromString(raceTypeString);
//XML creation time.
creationTimeDate = ZonedDateTime.parse(getTextValueOfNode(settings, "CreationTimeDate"), dateFormat);
@@ -178,8 +179,9 @@ public class RaceXMLReader extends XMLReader implements RaceDataSource {
/**
* Reads course data from the xml file.
* @throws InvalidRaceDataException Thrown if we cannot parse the document properly.
*/
private void readCourse() {
private void readCourse() throws InvalidRaceDataException {
readCompoundMarks();
readCompoundMarkSequence();
readCourseLimits();
@@ -189,30 +191,31 @@ public class RaceXMLReader extends XMLReader implements RaceDataSource {
/**
* Indexes CompoundMark elements by their ID for use in generating the course, and populates list of Markers.
* @throws InvalidRaceDataException thrown if we cannot create a compound mark from the document.
* @see CompoundMark
*/
private void readCompoundMarks() {
private void readCompoundMarks() throws InvalidRaceDataException {
//Gets the "<Course>...</..>" element.
Element course = (Element) doc.getElementsByTagName("Course").item(0);
//Get the list of CompoundMark elements.
NodeList compoundMarkList = course.getElementsByTagName("CompoundMark");
//Number of compound marks in the course.
int numberOfCompoundMarks = course.getChildNodes().getLength();
int numberOfCompoundMarks = compoundMarkList.getLength();
//For each CompoundMark element, create a CompoundMark object.
for(int i = 0; i < numberOfCompoundMarks; i++) {
//Get the CompoundMark element.
Element compoundMarkElement = (Element) course.getChildNodes().item(i);
Element compoundMarkElement = (Element) compoundMarkList.item(i);
//If it is actually a CompoundMark element, turn it into a CompoundMark object.
if(compoundMarkElement.getNodeName().equals("CompoundMark")) {
CompoundMark compoundMark = createCompoundMark(compoundMarkElement);
//Convert to CompoundMark object.
CompoundMark compoundMark = createCompoundMark(compoundMarkElement);
compoundMarkMap.put(compoundMark.getId(), compoundMark);
}
compoundMarkMap.put(compoundMark.getId(), compoundMark);
}
}
@@ -321,7 +324,7 @@ public class RaceXMLReader extends XMLReader implements RaceDataSource {
NodeList corners = compoundMarkSequence.getElementsByTagName("Corner");
//Gets the first corner.
Element cornerElement = (Element)corners.item(0);
Element cornerElement = (Element) corners.item(0);
//Gets the ID number of this corner element.
int cornerID = getCompoundMarkID(cornerElement);
@@ -365,20 +368,19 @@ public class RaceXMLReader extends XMLReader implements RaceDataSource {
//The "<CourseLimit>...</...>" element. This contains a sequence of Limit elements.
Element courseLimit = (Element) doc.getElementsByTagName("CourseLimit").item(0);
//Get the list of Limit elements.
NodeList limitList = courseLimit.getElementsByTagName("Limit");
//For each limit element...
for(int i = 0; i < courseLimit.getChildNodes().getLength(); i++) {
for(int i = 0; i < limitList.getLength(); i++) {
//Get the Limit element.
Element limit = (Element) courseLimit.getChildNodes().item(i);
Element limit = (Element) limitList.item(i);
//If it is actually a Limit element, add the limit to boundary list.
if (limit.getNodeName().equals("Limit")) {
double latitude = Double.parseDouble(limit.getAttribute("Lat"));
double longitude = Double.parseDouble(limit.getAttribute("Lon"));
boundary.add(new GPSCoordinate(latitude, longitude));
}
//Convert to GPSCoordinate.
double latitude = Double.parseDouble(limit.getAttribute("Lat"));
double longitude = Double.parseDouble(limit.getAttribute("Lon"));
boundary.add(new GPSCoordinate(latitude, longitude));
}
}
@@ -66,12 +66,12 @@ public class RegattaXMLReader extends XMLReader implements RegattaDataSource {
/**
* Constructor for Regatta XML using a file read as a resource.
*
* @param filePath path of the file to read. Read as a resource.
* @param fileContents Contents of xml file.
* @throws XMLReaderException Thrown if the file cannot be parsed.
* @throws InvalidRegattaDataException Thrown if the file cannot be parsed correctly.
*/
public RegattaXMLReader(String filePath) throws XMLReaderException, InvalidRegattaDataException {
super(filePath);
public RegattaXMLReader(String fileContents) throws XMLReaderException, InvalidRegattaDataException {
super(fileContents);
//Attempt to read boat xml file.
try {
@@ -15,9 +15,8 @@ import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringWriter;
import java.io.*;
import java.nio.charset.StandardCharsets;
/**
* Base Reader for XML Files
@@ -27,14 +26,14 @@ public abstract class XMLReader {
protected Document doc;
/**
* Read an XML file by name as a resource.
* @param filePath filepath for XML file. Loaded as a resource.
* Read an XML file.
* @param fileContents Contents of the xml file.
* @throws XMLReaderException Thrown if the file cannot be parsed.
*/
public XMLReader(String filePath) throws XMLReaderException {
public XMLReader(String fileContents) throws XMLReaderException {
//Read file as resource.
InputStream xmlInputStream = getClass().getClassLoader().getResourceAsStream(filePath);
//Wrap file contents in input stream.
InputStream xmlInputStream = new ByteArrayInputStream(fileContents.getBytes(StandardCharsets.UTF_8));
this.doc = parseInputStream(xmlInputStream);
@@ -1,10 +1,12 @@
package shared.model;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import com.sun.istack.internal.Nullable;
import javafx.beans.property.*;
import network.Messages.Enums.BoatStatusEnum;
import java.time.ZonedDateTime;
/**
* Boat Model that is used to store information on the boats that are running in the race.
*/
@@ -18,7 +20,7 @@ public class Boat {
* The current speed of the boat, in knots.
* TODO knots
*/
private double currentSpeed;
private DoubleProperty currentSpeed = new SimpleDoubleProperty(0);
/**
* The current bearing/heading of the boat.
@@ -44,7 +46,7 @@ public class Boat {
/**
* The leg of the race that the boat is currently on.
*/
private Leg currentLeg;
private Property<Leg> currentLeg = new SimpleObjectProperty<>();
/**
* The distance, in meters, that the boat has travelled in the current leg.
@@ -55,7 +57,7 @@ public class Boat {
/**
* The boat's position within the race (e.g., 5th).
*/
private StringProperty positionInRace;
private StringProperty positionInRace = new SimpleStringProperty();
/**
* The time, in milliseconds, that has elapsed during the current leg.
@@ -77,9 +79,15 @@ public class Boat {
/**
* The amount of time, in seconds, until the boat reaches the next mark.
* The time at which the boat is estimated to reach the next mark, in milliseconds since unix epoch.
*/
private long estimatedTime = 0;
private ZonedDateTime estimatedTimeAtNextMark;
/**
* The time at which the boat reached the previous mark.
*/
@Nullable
private ZonedDateTime timeAtLastMark;
/**
@@ -132,7 +140,7 @@ public class Boat {
* @return The current speed of the boat, in knots.
*/
public double getCurrentSpeed() {
return currentSpeed;
return currentSpeed.get();
}
/**
@@ -140,7 +148,15 @@ public class Boat {
* @param currentSpeed The new speed of the boat, in knots.
*/
public void setCurrentSpeed(double currentSpeed) {
this.currentSpeed = currentSpeed;
this.currentSpeed.set(currentSpeed);
}
/**
* Returns the current speed property of the boat.
* @return The current speed of the boat, in a DoubleProperty.
*/
public DoubleProperty currentSpeedProperty() {
return currentSpeed;
}
@@ -189,6 +205,14 @@ public class Boat {
* @return The current leg of the race the boat is in.
*/
public Leg getCurrentLeg() {
return currentLeg.getValue();
}
/**
* Returns the current leg, wrapped in a property.
* @return Current leg, wrapped in a property.
*/
public Property<Leg> legProperty() {
return currentLeg;
}
@@ -198,7 +222,7 @@ public class Boat {
* @param currentLeg The new leg of the race the boat is in.
*/
public void setCurrentLeg(Leg currentLeg) {
this.currentLeg = currentLeg;
this.currentLeg.setValue(currentLeg);
this.setTimeElapsedInCurrentLeg(0);
this.setDistanceTravelledInLeg(0);
}
@@ -221,14 +245,26 @@ public class Boat {
}
/**
* Returns the position within the race the boat has (e.g., 5th).
* @return The boat's position in race.
*/
public StringProperty positionProperty() {
return positionInRace;
}
/**
* Sets the position within the race the boat has (e.g., 5th).
* @param position The boat's position in race.
*/
public void setPosition(String position) {
this.positionInRace.set(position);
}
/**
* Returns the position within the race the boat has (e.g., 5th).
* @return The boat's position in race.
*/
public String getPosition() {
return this.positionInRace.get();
}
@@ -321,13 +357,38 @@ public class Boat {
}
public long getEstimatedTime() {
return estimatedTime;
/**
* Returns the time at which the boat should reach the next mark.
* @return Time at which the boat should reach next mark.
*/
public ZonedDateTime getEstimatedTimeAtNextMark() {
return estimatedTimeAtNextMark;
}
public void setEstimatedTime(long estimatedTime) {
this.estimatedTime = estimatedTime;
/**
* Sets the time at which the boat should reach the next mark.
* @param estimatedTimeAtNextMark Time at which the boat should reach next mark.
*/
public void setEstimatedTimeAtNextMark(ZonedDateTime estimatedTimeAtNextMark) {
this.estimatedTimeAtNextMark = estimatedTimeAtNextMark;
}
/**
* Returns the time at which the boat reached the previous mark.
* @return The time at which the boat reached the previous mark. May be null.
*/
@Nullable
public ZonedDateTime getTimeAtLastMark() {
return timeAtLastMark;
}
/**
* Sets the time at which the boat reached the previous mark to a specified time.
* @param timeAtLastMark Time at which boat passed previous mark.
*/
public void setTimeAtLastMark(ZonedDateTime timeAtLastMark) {
this.timeAtLastMark = timeAtLastMark;
}
}
@@ -33,7 +33,7 @@ public class Constants {
* Frame periods are multiplied by this to get the amount of time a single frame represents.
* E.g., frame period = 20ms, scale = 5, frame represents 20 * 5 = 100ms, and so boats are simulated for 100ms, even though only 20ms actually occurred.
*/
public static final int RaceTimeScale = 1;
public static final int RaceTimeScale = 25;
/**
* The race pre-start time, in milliseconds. 3 minutes.
@@ -49,9 +49,22 @@ public class Constants {
/**
* The number of milliseconds in one hour.
* <br>
* Multiply by this factor to convert milliseconds to hours.
* <br>
* Divide by this factor to convert hours to milliseconds.
*/
public static long OneHourMilliseconds = 1 * 60 * 60 * 1000;
/**
* The number of seconds in one hour.
* <br>
* Multiply by this factor to convert seconds to hours.
* <br>
* Divide by this factor to convert hours to seconds.
*/
public static long OneHourSeconds = 1 * 60 * 60;
}
@@ -299,6 +299,7 @@ public class GPSCoordinate {
*/
public static List<GPSCoordinate> getShrinkBoundary(List<GPSCoordinate> boundary) {
//TODO shrinkDistance should be a parameter. Also the code should be refactored to be smaller/simpler.
double shrinkDistance = 50d;
List<GPSCoordinate> shrunkBoundary = new ArrayList<>(boundary.size());
@@ -1,5 +1,7 @@
package shared.model;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import network.Messages.Enums.RaceStatusEnum;
import network.Messages.Enums.RaceTypeEnum;
import network.Messages.LatestMessages;
@@ -116,7 +118,7 @@ public abstract class Race implements Runnable {
/**
* The number of frames per second we generated over the last 1 second period.
*/
private int lastFps = 0;
private IntegerProperty lastFps = new SimpleIntegerProperty(0);
/**
* The time, in milliseconds, since we last reset our {@link #currentFps} counter.
@@ -280,12 +282,46 @@ public abstract class Race implements Runnable {
}
/**
* Returns the RaceDataSource used for the race.
* @return The RaceDataSource used for the race.
*/
public RaceDataSource getRaceDataSource() {
return raceDataSource;
}
/**
* Returns the number of legs in the race.
* @return The number of legs in the race.
*/
public int getLegCount() {
//We minus one, as we have added an extra "dummy" leg.
return legs.size() - 1;
}
/**
* Returns the race boundary.
* @return The race boundary.
*/
public List<GPSCoordinate> getBoundary() {
return boundary;
}
/**
* Returns the number of frames generated per second.
* @return Frames per second.
*/
public int getFps() {
return lastFps;
return lastFps.getValue();
}
/**
* Returns the fps property.
* @return The fps property.
*/
public IntegerProperty fpsProperty() {
return lastFps;
}
@@ -302,7 +338,7 @@ public abstract class Race implements Runnable {
//If we have reached 1 second period, snapshot the framerate and reset.
if (this.lastFpsResetTime > 1000) {
this.lastFps = this.currentFps;
this.lastFps.set(this.currentFps);
this.currentFps = 0;
this.lastFpsResetTime = 0;
@@ -72,8 +72,9 @@ public class RaceClock implements Runnable {
/**
* Format string used for current time.
* TODO probably need to fix this.
*/
private String currentTimeFormat = "'Starting time:' HH:mm dd/MM/YYYY";
private String currentTimeFormat = "'Current time:' HH:mm dd/MM/YYYY";
/**
* Format string used for duration before it has started.