Now uses RaceState as its base class, rather than Race. This aligns it with VisualiserRace. RaceState contains the shared race state between the client and server, and stores most of its data in RaceDataSource etc..., instead of maintaining its own copy.
Moved collider registry member to MockRace.

XMLUtilities:
Added documentation to two important functions, and updated them to use generics instead of Objects.
#story[1188]
main
fjc40 8 years ago
parent 7086af6057
commit 3176e76c8c

@ -25,6 +25,8 @@ import java.nio.charset.StandardCharsets;
import java.time.Duration;
import java.time.ZonedDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
@ -118,7 +120,8 @@ public class Event {
}
this.sourceIdAllocator = new SourceIdAllocator(raceDataSource.getParticipants());
List<Integer> potentialParticipants = new ArrayList<>(boatDataSource.getBoats().keySet());
this.sourceIdAllocator = new SourceIdAllocator(potentialParticipants);
this.compositeCommand = new CompositeCommand();
this.latestMessages = new LatestMessages();

@ -1,6 +1,7 @@
package mock.model;
import javafx.animation.AnimationTimer;
import mock.model.collider.ColliderRegistry;
import mock.xml.*;
import network.Messages.BoatLocation;
import network.Messages.BoatStatus;
@ -13,7 +14,6 @@ import shared.exceptions.BoatNotFoundException;
import shared.enums.RoundingType;
import shared.model.*;
import shared.model.Bearing;
import shared.model.Race;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
@ -27,7 +27,7 @@ import static java.lang.Math.cos;
* Has a course, boats, boundaries, etc...
* Is responsible for simulating the race, and sending messages to a MockOutput instance.
*/
public class MockRace extends Race {
public class MockRace extends RaceState {
/**
* An observable list of boats in the race.
@ -40,6 +40,12 @@ public class MockRace extends Race {
private List<GPSCoordinate> shrinkBoundary;
/**
* Registry for all collider object in this race
*/
protected ColliderRegistry colliderRegistry;
/**
* The scale factor of the race.
* See {@link Constants#RaceTimeScale}.
@ -51,6 +57,8 @@ public class MockRace extends Race {
*/
private WindGenerator windGenerator;
/**
* Constructs a race object with a given RaceDataSource, BoatDataSource, and RegattaDataSource and sends events to the given mockOutput.
* @param boatDataSource Data source for boat related data (yachts and marker boats).
@ -62,13 +70,15 @@ public class MockRace extends Race {
*/
public MockRace(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource, Polars polars, int timeScale, WindGenerator windGenerator) {
super(boatDataSource, raceDataSource, regattaDataSource);
this.setBoatDataSource(boatDataSource);
this.setRaceDataSource(raceDataSource);
this.setRegattaDataSource(regattaDataSource);
this.scaleFactor = timeScale;
this.boats = this.generateMockBoats(boatDataSource.getBoats(), raceDataSource.getParticipants(), polars);
this.shrinkBoundary = GPSCoordinate.getShrinkBoundary(this.boundary);
this.shrinkBoundary = GPSCoordinate.getShrinkBoundary(this.getBoundary());
this.windGenerator = windGenerator;
@ -76,9 +86,19 @@ public class MockRace extends Race {
//Wind.
this.setWind(windGenerator.generateBaselineWind());
// Set up colliders
this.colliderRegistry = new ColliderRegistry();
for(CompoundMark mark: this.getCompoundMarks()) {
colliderRegistry.addCollider(mark.getMark1());
if(mark.getMark2() != null) colliderRegistry.addCollider(mark.getMark2());
}
this.colliderRegistry.addAllColliders(boats);
}
/**
* Generates a list of MockBoats given a list of Boats, and a list of participating boats.
* @param boats The map of Boats describing boats that are potentially in the race. Maps boat sourceID to boat.
@ -108,12 +128,17 @@ public class MockRace extends Race {
}
public ColliderRegistry getColliderRegistry() {
return colliderRegistry;
}
/**
* Updates the race time to a specified value, in milliseconds since the unix epoch.
* @param currentTime Milliseconds since unix epoch.
*/
public void updateRaceTime(long currentTime) {
this.raceClock.setUTCTime(currentTime);
this.getRaceClock().setUTCTime(currentTime);
}
@ -123,7 +148,7 @@ public class MockRace extends Race {
public void updateRaceStatusEnum() {
//The millisecond duration of the race. Negative means it hasn't started, so we flip sign.
long timeToStart = - this.raceClock.getDurationMilli();
long timeToStart = - this.getRaceClock().getDurationMilli();
if (timeToStart > Constants.RacePreStartTime) {
@ -194,7 +219,7 @@ public class MockRace extends Race {
//The boat starts on the first leg of the race.
boat.setCurrentLeg(this.legs.get(0));
boat.setCurrentLeg(this.getLegs().get(0));
//Boats start with 0 knots speed.
boat.setCurrentSpeed(0d);
@ -223,7 +248,7 @@ public class MockRace extends Race {
private List<GPSCoordinate> getSpreadStartingPositions() {
//The first compound marker of the race - the starting gate.
CompoundMark compoundMark = this.legs.get(0).getStartCompoundMark();
CompoundMark compoundMark = this.getLegs().get(0).getStartCompoundMark();
//The position of the two markers from the compound marker.
GPSCoordinate mark1Position = compoundMark.getMark1Position();
@ -485,7 +510,7 @@ public class MockRace extends Race {
roundingChecks.get(0), boat.getPosition(), legBearing) &&
gateCheck && boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(0)))) {
boat.increaseRoundingStatus();
if (boat.getCurrentLeg().getLegNumber() + 2 >= legs.size()){
if (boat.getCurrentLeg().getLegNumber() + 2 >= getLegs().size()){
//boat has finished race
boat.increaseRoundingStatus();
}
@ -503,7 +528,7 @@ public class MockRace extends Race {
case 2://has traveled 180 degrees around the mark
//Move boat on to next leg.
boat.resetRoundingStatus();
Leg nextLeg = this.legs.get(boat.getCurrentLeg().getLegNumber() + 1);
Leg nextLeg = this.getLegs().get(boat.getCurrentLeg().getLegNumber() + 1);
boat.setCurrentLeg(nextLeg);
break;
}
@ -530,7 +555,7 @@ public class MockRace extends Race {
gateCheck &&
boat.isBetweenGate(roundingMark, Mark.tempMark(roundingChecks.get(0)))) {
boat.increaseRoundingStatus();
if (boat.getCurrentLeg().getLegNumber() + 2 >= legs.size()){
if (boat.getCurrentLeg().getLegNumber() + 2 >= getLegs().size()){
//boat has finished race
boat.increaseRoundingStatus();
}
@ -547,7 +572,7 @@ public class MockRace extends Race {
case 2://has traveled 180 degrees around the mark
//Move boat on to next leg.
boat.resetRoundingStatus();
Leg nextLeg = this.legs.get(boat.getCurrentLeg().getLegNumber() + 1);
Leg nextLeg = this.getLegs().get(boat.getCurrentLeg().getLegNumber() + 1);
boat.setCurrentLeg(nextLeg);
break;
}
@ -585,7 +610,7 @@ public class MockRace extends Race {
GPSCoordinate roundCheck2;
try{
Leg nextLeg = legs.get(legs.indexOf(boat.getCurrentLeg()) + 1);
Leg nextLeg = getLegs().get(getLegs().indexOf(boat.getCurrentLeg()) + 1);
GPSCoordinate startNextDirectionLinePoint = nextLeg.getStartCompoundMark().getMark1Position();
GPSCoordinate endNextDirectionLinePoint = nextLeg.getEndCompoundMark().getMark1Position();
@ -681,7 +706,7 @@ public class MockRace extends Race {
*/
public void changeWindDirection() {
Wind nextWind = windGenerator.generateNextWind(raceWind.getValue());
Wind nextWind = windGenerator.generateNextWind(windProperty().getValue());
setWind(nextWind);
}
@ -702,13 +727,10 @@ public class MockRace extends Race {
long timeFromNow = (long) (1000 * boat.calculateDistanceToNextMarker() / velocityToMark);
//Calculate time at which it will reach mark.
ZonedDateTime timeAtMark = this.raceClock.getCurrentTime().plus(timeFromNow, ChronoUnit.MILLIS);
ZonedDateTime timeAtMark = this.getRaceClock().getCurrentTime().plus(timeFromNow, ChronoUnit.MILLIS);
boat.setEstimatedTimeAtNextMark(timeAtMark);
}
}
public List<CompoundMark> getCompoundMarks() {
return compoundMarks;
}
}

@ -77,7 +77,8 @@ public class RaceXMLCreator {
public static String alterRaceToWind(String s, double degrees) throws XMLReaderException, InvalidRaceDataException, JAXBException, IOException, SAXException, ParserConfigurationException {
RaceXMLReader reader = new RaceXMLReader(s, XMLFileType.ResourcePath);
XMLRace race = (XMLRace) XMLUtilities.xmlToClass(RaceXMLCreator.class.getClassLoader().getResourceAsStream(s),
XMLRace race = XMLUtilities.xmlToClass(
RaceXMLCreator.class.getClassLoader().getResourceAsStream(s),
RaceXMLCreator.class.getClassLoader().getResource("mock/mockXML/schema/raceSchema.xsd"),
XMLRace.class);
@ -89,14 +90,7 @@ public class RaceXMLCreator {
alterRaceRotation(race, degreesToRotate);
JAXBContext context = JAXBContext.newInstance(XMLRace.class);
Marshaller jaxbMarshaller = context.createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
StringWriter sw = new StringWriter();
jaxbMarshaller.marshal(race, sw);
return sw.toString();
return XMLUtilities.classToXML(race);
}
/**

@ -1,318 +0,0 @@
package shared.model;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleObjectProperty;
import mock.model.collider.ColliderRegistry;
import network.Messages.Enums.RaceStatusEnum;
import network.Messages.Enums.RaceTypeEnum;
import shared.dataInput.BoatDataSource;
import shared.dataInput.RaceDataSource;
import shared.dataInput.RegattaDataSource;
import java.util.List;
/**
* Represents a yacht race.
* Has a course, state, wind, boundaries, etc.... Boats are added by inheriting classes (see {@link Boat}, {@link mock.model.MockBoat}, {@link visualiser.model.VisualiserBoat}.
*/
public abstract class Race {
/**
* The source of race related data.
*/
protected RaceDataSource raceDataSource;
/**
* The source of boat related data.
*/
protected BoatDataSource boatDataSource;
/**
* The source of regatta related data.
*/
protected RegattaDataSource regattaDataSource;
/**
* A list of compound marks in the race.
*/
protected List<CompoundMark> compoundMarks;
/**
* A list of legs in the race.
*/
protected List<Leg> legs;
/**
* A list of coordinates describing the boundary of the course.
*/
protected List<GPSCoordinate> boundary;
/**
* The clock which tracks the race's start time, current time, and elapsed duration.
*/
protected RaceClock raceClock;
/**
* The race ID of the course.
*/
protected int raceId;
/**
* The name of the regatta.
*/
protected String regattaName;
/**
* The current status of the race.
*/
protected RaceStatusEnum raceStatusEnum;
/**
* The type of race this is.
*/
protected RaceTypeEnum raceType;
/**
* The race's wind.
*/
protected Property<Wind> raceWind = new SimpleObjectProperty<>();
/**
* Registry for all collider object in this race
*/
protected ColliderRegistry colliderRegistry;
/**
* The number of frames per second.
* We essentially track the number of frames generated per second, over a one second period. When {@link #lastFpsResetTime} reaches 1 second, currentFps is reset.
*/
private int currentFps = 0;
/**
* The number of frames per second we generated over the last 1 second period.
*/
private IntegerProperty lastFps = new SimpleIntegerProperty(0);
/**
* The time, in milliseconds, since we last reset our {@link #currentFps} counter.
*/
private long lastFpsResetTime;
/**
* Constructs a race object with a given BoatDataSource, RaceDataSource, and RegattaDataSource.
* @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...).
*/
public Race(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource) {
//Keep a reference to data sources.
this.raceDataSource = raceDataSource;
this.boatDataSource = boatDataSource;
this.regattaDataSource = regattaDataSource;
//Marks.
this.compoundMarks = raceDataSource.getCompoundMarks();
//Boundaries.
this.boundary = raceDataSource.getBoundary();
//Legs.
this.useLegsList(raceDataSource.getLegs());
//Race ID.
this.raceId = raceDataSource.getRaceId();
//Regatta name.
this.regattaName = regattaDataSource.getRegattaName();
//Race clock.
this.raceClock = new RaceClock(this.raceDataSource.getStartDateTime());
//Race status.
this.setRaceStatusEnum(RaceStatusEnum.NOT_ACTIVE);
//Race type.
this.raceType = raceDataSource.getRaceType();
//Wind.
this.setWind(Bearing.fromDegrees(0), 0);
// Set up colliders
this.colliderRegistry = new ColliderRegistry();
for(CompoundMark mark: compoundMarks) {
colliderRegistry.addCollider(mark.getMark1());
if(mark.getMark2() != null) colliderRegistry.addCollider(mark.getMark2());
}
}
public ColliderRegistry getColliderRegistry() {
return colliderRegistry;
}
/**
* Initialise the boats in the race.
* This sets their starting positions and current legs.
*/
protected abstract void initialiseBoats();
/**
* Updates the race to use a new list of legs, and adds a dummy "Finish" leg at the end.
* @param legs The new list of legs to use.
*/
protected void useLegsList(List<Leg> legs) {
//We add a "dummy" leg at the end of the race.
this.legs = legs;
this.legs.add(new Leg("Finish", this.legs.size()));
}
/**
* Determines whether or not a specific leg is the last leg in the race.
* @param leg The leg to check.
* @return Returns true if it is the last, false otherwise.
*/
protected boolean isLastLeg(Leg leg) {
//Get the last leg.
Leg lastLeg = this.legs.get(this.legs.size() - 1);
//Check its ID.
int lastLegID = lastLeg.getLegNumber();
//Get the specified leg's ID.
int legID = leg.getLegNumber();
//Check if they are the same.
return legID == lastLegID;
}
/**
* Returns the current race status.
* @return The current race status.
*/
public RaceStatusEnum getRaceStatusEnum() {
return raceStatusEnum;
}
/**
* Sets the current race status.
* @param raceStatusEnum The new status of the race.
*/
public void setRaceStatusEnum(RaceStatusEnum raceStatusEnum) {
this.raceStatusEnum = raceStatusEnum;
}
/**
* Returns the type of race this is.
* @return The type of race this is.
*/
public RaceTypeEnum getRaceType() {
return raceType;
}
/**
* Returns the name of the regatta.
* @return The name of the regatta.
*/
public String getRegattaName() {
return regattaName;
}
/**
* Updates the race to have a specified wind bearing and speed.
* @param windBearing New wind bearing.
* @param windSpeedKnots New wind speed, in knots.
*/
protected void setWind(Bearing windBearing, double windSpeedKnots) {
Wind wind = new Wind(windBearing, windSpeedKnots);
setWind(wind);
}
/**
* Updates the race to have a specified wind (bearing and speed).
* @param wind New wind.
*/
protected void setWind(Wind wind) {
this.raceWind.setValue(wind);
}
/**
* Returns the wind bearing.
* @return The wind bearing.
*/
public Bearing getWindDirection() {
return raceWind.getValue().getWindDirection();
}
/**
* Returns the wind speed.
* Measured in knots.
* @return The wind speed.
*/
public double getWindSpeed() {
return raceWind.getValue().getWindSpeed();
}
/**
* Returns the RaceClock for this race.
* This is used to track the start time, current time, and elapsed duration of the race.
* @return The RaceClock for the race.
*/
public RaceClock getRaceClock() {
return raceClock;
}
/**
* 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 marks of the race.
* @return Marks of the race.
*/
public List<CompoundMark> getCompoundMarks() {
return compoundMarks;
}
/**
* Returns the legs of the race.
* @return Legs of the race.
*/
public List<Leg> getLegs() {
return legs;
}
/**
* Returns the fps property.
* @return The fps property.
*/
public IntegerProperty fpsProperty() {
return lastFps;
}
/**
* Increments the FPS counter, and adds timePeriod milliseconds to our FPS reset timer.
* @param timePeriod Time, in milliseconds, to add to {@link #lastFpsResetTime}.
*/
protected void incrementFps(long timePeriod) {
//Increment.
this.currentFps++;
//Add period to timer.
this.lastFpsResetTime += timePeriod;
//If we have reached 1 second period, snapshot the framerate and reset.
if (this.lastFpsResetTime > 1000) {
this.lastFps.set(this.currentFps);
this.currentFps = 0;
this.lastFpsResetTime = 0;
}
}
public int getRaceId() {
return raceId;
}
}

@ -20,10 +20,17 @@ import java.io.*;
import java.net.URL;
/**
* Created by fwy13 on 13/08/17.
* Contains utility functions to convert between xml files and xml class objects.
*/
public class XMLUtilities {
/**
* Converts an XML class object to an XML string.
* @param o The XML class object to convert.
* @return String containing the serialised XML data.
* @throws JAXBException Thrown if the object is cannot be serialised to XML.
*/
public static String classToXML(Object o) throws JAXBException {
JAXBContext context = JAXBContext.newInstance(o.getClass());
Marshaller jaxbMarshaller = context.createMarshaller();
@ -49,14 +56,26 @@ public class XMLUtilities {
return xmlToClass(document, schemaURL, c);
}
public static Object xmlToClass(InputStream i, URL schemaURL, Class c) throws ParserConfigurationException, IOException, SAXException, JAXBException {
/**
* Converts an XML file to an XML class (e.g., {@link shared.xml.Race.XMLRace}).
* @param i The input stream for the XML file.
* @param schemaURL URL for the XML schema.
* @param c The XML class to convert to.
* @param <T> The XML class to convert to.
* @return The XML class object.
* @throws ParserConfigurationException
* @throws IOException
* @throws SAXException
* @throws JAXBException
*/
public static <T> T xmlToClass(InputStream i, URL schemaURL, Class<T> c) throws ParserConfigurationException, IOException, SAXException, JAXBException {
DocumentBuilder parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document document = parser.parse(i);
return xmlToClass(document, schemaURL, c);
}
public static Object xmlToClass(Document document, URL schemaURL, Class c) throws ParserConfigurationException, IOException, SAXException, JAXBException {
public static <T> T xmlToClass(Document document, URL schemaURL, Class<T> c) throws ParserConfigurationException, IOException, SAXException, JAXBException {
JAXBContext jc = JAXBContext.newInstance(c);
Unmarshaller unmarshaller = jc.createUnmarshaller();
@ -64,7 +83,7 @@ public class XMLUtilities {
Schema schema = sf.newSchema(schemaURL);
unmarshaller.setSchema(schema);
return unmarshaller.unmarshal(new DOMSource(document));
return (T) unmarshaller.unmarshal(new DOMSource(document));
}
public static boolean validateXML(String file, URL schemaURL){

Loading…
Cancel
Save