diff --git a/racevisionGame/src/main/java/mock/app/Event.java b/racevisionGame/src/main/java/mock/app/Event.java index c054e2fb..526223ba 100644 --- a/racevisionGame/src/main/java/mock/app/Event.java +++ b/racevisionGame/src/main/java/mock/app/Event.java @@ -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 potentialParticipants = new ArrayList<>(boatDataSource.getBoats().keySet()); + this.sourceIdAllocator = new SourceIdAllocator(potentialParticipants); this.compositeCommand = new CompositeCommand(); this.latestMessages = new LatestMessages(); diff --git a/racevisionGame/src/main/java/mock/model/MockRace.java b/racevisionGame/src/main/java/mock/model/MockRace.java index 379123af..357d34ce 100644 --- a/racevisionGame/src/main/java/mock/model/MockRace.java +++ b/racevisionGame/src/main/java/mock/model/MockRace.java @@ -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 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 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 getCompoundMarks() { - return compoundMarks; - } -} \ No newline at end of file +} diff --git a/racevisionGame/src/main/java/mock/xml/RaceXMLCreator.java b/racevisionGame/src/main/java/mock/xml/RaceXMLCreator.java index f1b83479..5cb74625 100644 --- a/racevisionGame/src/main/java/mock/xml/RaceXMLCreator.java +++ b/racevisionGame/src/main/java/mock/xml/RaceXMLCreator.java @@ -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); } /** diff --git a/racevisionGame/src/main/java/shared/model/Race.java b/racevisionGame/src/main/java/shared/model/Race.java deleted file mode 100644 index a15e2edd..00000000 --- a/racevisionGame/src/main/java/shared/model/Race.java +++ /dev/null @@ -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 compoundMarks; - /** - * A list of legs in the race. - */ - protected List legs; - /** - * A list of coordinates describing the boundary of the course. - */ - protected List 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 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 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 getBoundary() { - return boundary; - } - - - /** - * Returns the marks of the race. - * @return Marks of the race. - */ - public List getCompoundMarks() { - return compoundMarks; - } - - /** - * Returns the legs of the race. - * @return Legs of the race. - */ - public List 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; - } -} diff --git a/racevisionGame/src/main/java/shared/xml/XMLUtilities.java b/racevisionGame/src/main/java/shared/xml/XMLUtilities.java index 8e01cb76..0a50f77d 100644 --- a/racevisionGame/src/main/java/shared/xml/XMLUtilities.java +++ b/racevisionGame/src/main/java/shared/xml/XMLUtilities.java @@ -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 The XML class to convert to. + * @return The XML class object. + * @throws ParserConfigurationException + * @throws IOException + * @throws SAXException + * @throws JAXBException + */ + public static T xmlToClass(InputStream i, URL schemaURL, Class 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 xmlToClass(Document document, URL schemaURL, Class 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){