Refactored and moved XMLReader, RaceDataSource, BoatXMLReader, BoatDataSource, RegattaXMLReader to shared/dataInput.
parent
d0d63ca236
commit
7f027c8cc5
@ -1,14 +0,0 @@
|
||||
package mock.dataInput;
|
||||
|
||||
import seng302.Model.Boat;
|
||||
import seng302.Model.Mark;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Boats Data
|
||||
*/
|
||||
public interface BoatDataSource {
|
||||
Map<Integer, Boat> getBoats();
|
||||
Map<Integer, Mark> getMarkerBoats();
|
||||
}
|
||||
@ -1,32 +0,0 @@
|
||||
package mock.dataInput;
|
||||
|
||||
import seng302.Model.Boat;
|
||||
import seng302.Model.CompoundMark;
|
||||
import seng302.Model.GPSCoordinate;
|
||||
import seng302.Model.Leg;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Data Class for a Race
|
||||
*/
|
||||
public interface RaceDataSource {
|
||||
List<Boat> getBoats();
|
||||
|
||||
List<Leg> getLegs();
|
||||
|
||||
List<GPSCoordinate> getBoundary();
|
||||
|
||||
List<CompoundMark> getCompoundMarks();
|
||||
|
||||
int getRaceId();
|
||||
|
||||
String getRaceType();
|
||||
|
||||
ZonedDateTime getZonedDateTime();
|
||||
|
||||
GPSCoordinate getMapTopLeft();
|
||||
|
||||
GPSCoordinate getMapBottomRight();
|
||||
}
|
||||
@ -0,0 +1,927 @@
|
||||
package mock.model;
|
||||
|
||||
import javafx.animation.AnimationTimer;
|
||||
import javafx.collections.FXCollections;
|
||||
import mock.app.MockOutput;
|
||||
import shared.dataInput.RaceDataSource;
|
||||
import network.Messages.Enums.RaceStatusEnum;
|
||||
import network.Messages.Enums.RaceTypeEnum;
|
||||
import shared.model.Bearing;
|
||||
import shared.model.Constants;
|
||||
import shared.model.GPSCoordinate;
|
||||
import shared.model.Leg;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import static java.lang.Math.cos;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a yacht race.
|
||||
* Has a course, boats, boundaries, etc...
|
||||
* Is responsible for simulating the race, and sending messages to a MockOutput instance.
|
||||
*/
|
||||
public class Race implements Runnable {
|
||||
|
||||
/**
|
||||
* An observable list of boats in the race.
|
||||
*/
|
||||
private ObservableList<Boat> boats;
|
||||
|
||||
/**
|
||||
* An observable list of compound marks in the race.
|
||||
*/
|
||||
private ObservableList<CompoundMark> compoundMarks;
|
||||
|
||||
/**
|
||||
* A list of legs in the race.
|
||||
*/
|
||||
private List<Leg> legs;
|
||||
|
||||
/**
|
||||
* A list of coordinates describing the boundary of the course.
|
||||
*/
|
||||
private List<GPSCoordinate> boundary;
|
||||
|
||||
/**
|
||||
* A copy of the boundary list, except "shrunk" inwards by 50m.
|
||||
*/
|
||||
private List<GPSCoordinate> shrinkBoundary;
|
||||
|
||||
/**
|
||||
* The elapsed time, in milliseconds, of the race.
|
||||
*/
|
||||
private long totalTimeElapsed;
|
||||
|
||||
/**
|
||||
* The starting timestamp, in milliseconds, of the race.
|
||||
*/
|
||||
private long startTime;
|
||||
|
||||
/**
|
||||
* The scale factor of the race.
|
||||
* 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.
|
||||
*/
|
||||
private int scaleFactor = 5;
|
||||
|
||||
/**
|
||||
* The race ID of the course.
|
||||
*/
|
||||
private int raceId;
|
||||
|
||||
/**
|
||||
* The current status of the race.
|
||||
*/
|
||||
private RaceStatusEnum raceStatusEnum;
|
||||
|
||||
/**
|
||||
* The type of race this is.
|
||||
*/
|
||||
private RaceTypeEnum raceType;
|
||||
|
||||
/**
|
||||
* The percent chance that a boat fails the race, and enters a DNF state, at each checkpoint.
|
||||
* 0 = 0%, 100 = 100%.
|
||||
*/
|
||||
private int dnfChance = 0;
|
||||
|
||||
|
||||
/**
|
||||
* The mockOutput to send messages to.
|
||||
*/
|
||||
private MockOutput mockOutput;
|
||||
|
||||
|
||||
/**
|
||||
* Wind direction bearing.
|
||||
*/
|
||||
private Bearing windDirection;
|
||||
|
||||
/**
|
||||
* Wind speed (knots).
|
||||
* Convert this to millimeters per second before passing to RaceStatus.
|
||||
*/
|
||||
private double windSpeed;
|
||||
|
||||
private double windDirDegrees;
|
||||
private double windDir;
|
||||
private int changeWind = 4;
|
||||
private static final int windUpperBound = 235;
|
||||
private static final int windLowerBound = 215;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Constructs a race object with a given RaceDataSource and sends events to the given mockOutput.
|
||||
* @param raceData Data source for race related data (boats, legs, etc...).
|
||||
* @param mockOutput The mockOutput to send events to.
|
||||
*/
|
||||
public Race(RaceDataSource raceData, MockOutput mockOutput) {
|
||||
|
||||
this.mockOutput = mockOutput;
|
||||
|
||||
this.boats = FXCollections.observableArrayList(raceData.getBoats());
|
||||
this.compoundMarks = FXCollections.observableArrayList(raceData.getCompoundMarks());
|
||||
this.boundary = raceData.getBoundary();
|
||||
this.shrinkBoundary = GPSCoordinate.getShrinkBoundary(this.boundary);
|
||||
|
||||
|
||||
this.legs = raceData.getLegs();
|
||||
this.legs.add(new Leg("Finish", this.legs.size()));
|
||||
|
||||
this.raceId = raceData.getRaceId();
|
||||
|
||||
//The start time is current time + 4 minutes, scaled. prestart is 3 minutes, and we add another.
|
||||
this.startTime = System.currentTimeMillis() + ((Constants.RacePreStartTime + (1 * 60 * 1000)) / this.scaleFactor);
|
||||
|
||||
this.setRaceStatusEnum(RaceStatusEnum.NOT_ACTIVE);
|
||||
this.raceType = raceData.getRaceType();
|
||||
|
||||
this.windSpeed = 12;
|
||||
this.windDirection = Bearing.fromDegrees(180);
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Runnable for the thread.
|
||||
*/
|
||||
public void run() {
|
||||
initialiseBoats();
|
||||
initialiseWindDir();
|
||||
countdownTimer.start();
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the compound marker boats through mock output.
|
||||
*/
|
||||
private void parseMarks() {
|
||||
for (CompoundMark compoundMark : this.compoundMarks) {
|
||||
|
||||
//Get the individual marks from the compound mark.
|
||||
Mark mark1 = compoundMark.getMark1();
|
||||
Mark mark2 = compoundMark.getMark2();
|
||||
|
||||
//If they aren't null, parse them (some compound marks only have one mark).
|
||||
if (mark1 != null) {
|
||||
this.parseIndividualMark(mark1);
|
||||
}
|
||||
|
||||
if (mark2 != null) {
|
||||
this.parseIndividualMark(mark2);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an individual marker boat, and sends it to mockOutput.
|
||||
* @param mark The marker boat to parse.
|
||||
*/
|
||||
private void parseIndividualMark(Mark mark) {
|
||||
|
||||
this.mockOutput.parseBoatLocation(mark.getSourceID(), mark.getPosition().getLatitude(), mark.getPosition().getLongitude(),0,0, totalTimeElapsed+startTime);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse the boats in the race, and send it to mockOutput.
|
||||
*/
|
||||
private void parseBoatLocations() {
|
||||
|
||||
//Parse each boat.
|
||||
for (Boat boat : this.boats) {
|
||||
|
||||
this.parseIndividualBoatLocation(boat);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses an individual boat, and sends it to mockOutput.
|
||||
* @param boat The boat to parse.
|
||||
*/
|
||||
private void parseIndividualBoatLocation(Boat boat) {
|
||||
|
||||
this.mockOutput.parseBoatLocation(
|
||||
boat.getSourceID(),
|
||||
boat.getCurrentPosition().getLatitude(),
|
||||
boat.getCurrentPosition().getLongitude(),
|
||||
boat.getBearing().degrees(),
|
||||
boat.getCurrentSpeed(),
|
||||
startTime + totalTimeElapsed
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Updates the race status enumeration based on the current time, in milliseconds.
|
||||
* @param currentTime The current time, in milliseconds.
|
||||
*/
|
||||
private void updateRaceStatusEnum(long currentTime) {
|
||||
|
||||
//The amount of milliseconds until the race starts.
|
||||
long timeToStart = this.startTime - currentTime;
|
||||
|
||||
//Scale the time to start based on the scale factor.
|
||||
long timeToStartScaled = timeToStart / this.scaleFactor;
|
||||
|
||||
|
||||
if (timeToStartScaled > Constants.RacePreStartTime) {
|
||||
//Time > 3 minutes is the prestart period.
|
||||
this.setRaceStatusEnum(RaceStatusEnum.PRESTART);
|
||||
|
||||
} else if ((timeToStartScaled <= Constants.RacePreStartTime) && (timeToStartScaled >= Constants.RacePreparatoryTime)) {
|
||||
//Time between [1, 3] minutes is the warning period.
|
||||
this.setRaceStatusEnum(RaceStatusEnum.WARNING);
|
||||
|
||||
} else if ((timeToStartScaled <= Constants.RacePreparatoryTime) && (timeToStartScaled > 0)) {
|
||||
//Time between (0, 1] minutes is the preparatory period.
|
||||
this.setRaceStatusEnum(RaceStatusEnum.PREPARATORY);
|
||||
|
||||
} else {
|
||||
//Otherwise, the race has started!
|
||||
this.setRaceStatusEnum(RaceStatusEnum.STARTED);
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the race status, and sends it to mockOutput.
|
||||
*/
|
||||
private void parseRaceStatus() {
|
||||
|
||||
//A race status message contains a list of boat statuses.
|
||||
List<BoatStatus> boatStatuses = new ArrayList<>();
|
||||
|
||||
//Add each boat status to the status list.
|
||||
for (Boat boat : boats) {
|
||||
|
||||
BoatStatus boatStatus = new BoatStatus(boat.getSourceID(), boat.getStatus(), boat.getCurrentLeg().getLegNumber(), boat.getEstimatedTime());
|
||||
|
||||
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());
|
||||
int windSpeedInt = (int) (windSpeed * Constants.KnotsToMMPerSecond);
|
||||
|
||||
//Create race status object, and send it.
|
||||
RaceStatus raceStatus = new RaceStatus(System.currentTimeMillis(), this.raceId, this.getRaceStatusEnum().getValue(), this.startTime, windDirectionInt, windSpeedInt, this.getRaceType().getValue(), boatStatuses);
|
||||
|
||||
mockOutput.parseRaceStatus(raceStatus);
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the status of all boats in the race to RACING.
|
||||
*/
|
||||
private void setBoatsStatusToRacing() {
|
||||
|
||||
for (Boat boat : this.boats) {
|
||||
boat.setStatus(BoatStatusEnum.RACING);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Countdown timer until race starts.
|
||||
*/
|
||||
protected AnimationTimer countdownTimer = new AnimationTimer() {
|
||||
|
||||
|
||||
long currentTime = System.currentTimeMillis();
|
||||
|
||||
@Override
|
||||
public void handle(long arg0) {
|
||||
|
||||
//Update the race status based on the current time.
|
||||
updateRaceStatusEnum(this.currentTime);
|
||||
|
||||
//Parse the boat locations.
|
||||
parseBoatLocations();
|
||||
|
||||
//Parse the marks.
|
||||
parseMarks();
|
||||
|
||||
// Change wind direction
|
||||
changeWindDir();
|
||||
|
||||
//Parse the race status.
|
||||
parseRaceStatus();
|
||||
|
||||
|
||||
if (getRaceStatusEnum() == RaceStatusEnum.STARTED) {
|
||||
System.setProperty("javafx.animation.fullspeed", "true");
|
||||
setBoatsStatusToRacing();
|
||||
raceTimer.start();
|
||||
this.stop();
|
||||
}
|
||||
|
||||
//Update the animations timer's time.
|
||||
currentTime = System.currentTimeMillis();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Timer that runs for the duration of the race, until all boats finish.
|
||||
*/
|
||||
private AnimationTimer raceTimer = new AnimationTimer() {
|
||||
|
||||
/**
|
||||
* Start time of loop, in milliseconds.
|
||||
*/
|
||||
long timeRaceStarted = System.currentTimeMillis();
|
||||
|
||||
/**
|
||||
* The time of the previous frame, in milliseconds.
|
||||
*/
|
||||
long lastFrameTime = timeRaceStarted;
|
||||
|
||||
@Override
|
||||
public void handle(long arg0) {
|
||||
|
||||
//Get the current time.
|
||||
long currentTime = System.currentTimeMillis();
|
||||
|
||||
//Update the total elapsed time.
|
||||
totalTimeElapsed = currentTime - this.timeRaceStarted;
|
||||
|
||||
//As long as there is at least one boat racing, we still simulate the race.
|
||||
if (getNumberOfActiveBoats() != 0) {
|
||||
|
||||
//Get the time period of this frame.
|
||||
long framePeriod = currentTime - lastFrameTime;
|
||||
//We actually simulate 20ms istead of the amount of time that has occurred, as that ensure that we don't end up with large frame periods on slow computers, causing position issues.
|
||||
framePeriod = 20;
|
||||
|
||||
|
||||
//For each boat, we update its position, and generate a BoatLocationMessage.
|
||||
for (Boat boat : boats) {
|
||||
|
||||
//If it is still racing, update its position.
|
||||
if (boat.getStatus() == BoatStatusEnum.RACING) {
|
||||
|
||||
updatePosition(boat, framePeriod, totalTimeElapsed);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
} else {
|
||||
//Otherwise, the race is over!
|
||||
raceFinished.start();
|
||||
setRaceStatusEnum(RaceStatusEnum.FINISHED);
|
||||
this.stop();
|
||||
}
|
||||
|
||||
if (getNumberOfActiveBoats() != 0) {
|
||||
// Change wind direction
|
||||
changeWindDir();
|
||||
|
||||
//Parse the boat locations.
|
||||
parseBoatLocations();
|
||||
|
||||
//Parse the marks.
|
||||
parseMarks();
|
||||
|
||||
//Parse the race status.
|
||||
parseRaceStatus();
|
||||
|
||||
|
||||
//Update the last frame time.
|
||||
this.lastFrameTime = currentTime;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Broadcast that the race has finished.
|
||||
*/
|
||||
protected AnimationTimer raceFinished = new AnimationTimer(){
|
||||
int iters = 0;
|
||||
@Override
|
||||
public void handle(long now) {
|
||||
RaceStatus raceStatus = new RaceStatus(System.currentTimeMillis(), raceId, 4, startTime, 0, 2300, 2, new ArrayList<>());
|
||||
mockOutput.parseRaceStatus(raceStatus);
|
||||
if (iters > 500){
|
||||
mockOutput.stop();
|
||||
stop();
|
||||
}
|
||||
iters++;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialise the boats in the race.
|
||||
* This sets their starting positions and current legs.
|
||||
*/
|
||||
public void initialiseBoats() {
|
||||
|
||||
//Gets the starting positions of the boats.
|
||||
List<GPSCoordinate> startingPositions = getSpreadStartingPositions();
|
||||
|
||||
//Get iterators for our boat and position lists.
|
||||
Iterator<Boat> boatIt = this.boats.iterator();
|
||||
Iterator<GPSCoordinate> startPositionIt = startingPositions.iterator();
|
||||
|
||||
//Iterate over the pair of lists.
|
||||
while (boatIt.hasNext() && startPositionIt.hasNext()) {
|
||||
|
||||
//Get the next boat and position.
|
||||
Boat boat = boatIt.next();
|
||||
GPSCoordinate startPosition = startPositionIt.next();
|
||||
|
||||
|
||||
//The boat starts on the first leg of the race.
|
||||
boat.setCurrentLeg(this.legs.get(0));
|
||||
|
||||
//Boats start with 0 knots speed.
|
||||
boat.setCurrentSpeed(0d);
|
||||
|
||||
//Place the boat at its starting position.
|
||||
boat.setCurrentPosition(startPosition);
|
||||
|
||||
//Boats start facing their next marker.
|
||||
boat.setBearing(boat.calculateBearingToNextMarker());
|
||||
|
||||
//Sets the boats status to prestart - it changes to racing when the race starts.
|
||||
boat.setStatus(BoatStatusEnum.PRESTART);
|
||||
|
||||
//We set a large time since tack change so that it calculates a new VMG when the simulation starts.
|
||||
boat.setTimeSinceTackChange(999999);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates a list of starting positions for the different boats, so they do not appear cramped at the start line.
|
||||
*
|
||||
* @return A list of starting positions.
|
||||
*/
|
||||
public List<GPSCoordinate> getSpreadStartingPositions() {
|
||||
|
||||
//The first compound marker of the race - the starting gate.
|
||||
CompoundMark compoundMark = this.legs.get(0).getStartCompoundMark();
|
||||
|
||||
//The position of the two markers from the compound marker.
|
||||
GPSCoordinate mark1Position = compoundMark.getMark1Position();
|
||||
GPSCoordinate mark2Position = compoundMark.getMark2Position();
|
||||
|
||||
|
||||
//Calculates the azimuth between the two points.
|
||||
Azimuth azimuth = GPSCoordinate.calculateAzimuth(mark1Position, mark2Position);
|
||||
|
||||
//Calculates the distance between the two points.
|
||||
double distanceMeters = GPSCoordinate.calculateDistanceMeters(mark1Position, mark2Position);
|
||||
|
||||
//The number of boats in the race.
|
||||
int numberOfBoats = this.boats.size();
|
||||
|
||||
//Calculates the distance between each boat. We divide by numberOfBoats + 1 to ensure that no boat is placed on one of the starting gate's marks.
|
||||
double distanceBetweenBoatsMeters = distanceMeters / (numberOfBoats + 1);
|
||||
|
||||
|
||||
//List to store coordinates in.
|
||||
List<GPSCoordinate> positions = new ArrayList<>();
|
||||
|
||||
//We start spacing boats out from mark 1.
|
||||
GPSCoordinate position = mark1Position;
|
||||
|
||||
//For each boat, displace position, and store it.
|
||||
for (int i = 0; i < numberOfBoats; i++) {
|
||||
|
||||
position = GPSCoordinate.calculateNewPosition(position, distanceBetweenBoatsMeters, azimuth);
|
||||
|
||||
positions.add(position);
|
||||
|
||||
}
|
||||
|
||||
return positions;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates a boat's VMG.
|
||||
* @param boat The boat to calculate VMG for.
|
||||
* @param bearingBounds An array containing the lower and upper acceptable bearing bounds to keep the boat in the course.
|
||||
* @return VMG for the specified boat.
|
||||
*/
|
||||
private VMG calculateVMG(Boat boat, Bearing[] bearingBounds) {
|
||||
|
||||
//Get the lower and upper acceptable bounds.
|
||||
Bearing lowerAcceptableBound = bearingBounds[0];
|
||||
Bearing upperAcceptableBound = bearingBounds[1];
|
||||
|
||||
|
||||
//Find the VMG inside these bounds.
|
||||
VMG bestVMG = boat.getPolars().calculateVMG(this.windDirection, this.windSpeed, boat.calculateBearingToNextMarker(), lowerAcceptableBound, upperAcceptableBound);
|
||||
|
||||
|
||||
|
||||
return bestVMG;
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Determines whether or not a given VMG improves the velocity of a boat, if it were currently using currentVMG.
|
||||
* @param currentVMG The current VMG of the boat.
|
||||
* @param potentialVMG The new VMG to test.
|
||||
* @param bearingToDestination The bearing between the boat and its destination.
|
||||
* @return True if the new VMG is improves velocity, false otherwise.
|
||||
*/
|
||||
private boolean improvesVelocity(VMG currentVMG, VMG potentialVMG, Bearing bearingToDestination) {
|
||||
|
||||
//Calculates the angle between the boat and its destination.
|
||||
Angle angleBetweenDestAndHeading = Angle.fromDegrees(currentVMG.getBearing().degrees() - bearingToDestination.degrees());
|
||||
|
||||
//Calculates the angle between the new VMG and the boat's destination.
|
||||
Angle angleBetweenDestAndNewVMG = Angle.fromDegrees(potentialVMG.getBearing().degrees() - bearingToDestination.degrees());
|
||||
|
||||
|
||||
//Calculate the boat's current velocity.
|
||||
double currentVelocity = Math.cos(angleBetweenDestAndHeading.radians()) * currentVMG.getSpeed();
|
||||
|
||||
//Calculate the potential velocity with the new VMG.
|
||||
double vmgVelocity = Math.cos(angleBetweenDestAndNewVMG.radians()) * potentialVMG.getSpeed();
|
||||
|
||||
//Return whether or not the new VMG gives better velocity.
|
||||
return vmgVelocity > currentVelocity;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not a given VMG improves the velocity of a boat.
|
||||
* @param boat The boat to test.
|
||||
* @param vmg The new VMG to test.
|
||||
* @return True if the new VMG is improves velocity, false otherwise.
|
||||
*/
|
||||
private boolean improvesVelocity(Boat boat, VMG vmg) {
|
||||
|
||||
//Get the boats "current" VMG.
|
||||
VMG boatVMG = new VMG(boat.getCurrentSpeed(), boat.getBearing());
|
||||
|
||||
//Check if the new VMG is better than the boat's current VMG.
|
||||
return this.improvesVelocity(boatVMG, vmg, boat.calculateBearingToNextMarker());
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Calculates the distance a boat has travelled and updates its current position according to this value.
|
||||
*
|
||||
* @param boat The boat to be updated.
|
||||
* @param updatePeriodMilliseconds The time, in milliseconds, since the last update.
|
||||
* @param totalElapsedMilliseconds The total number of milliseconds that have elapsed since the start of the race.
|
||||
*/
|
||||
protected void updatePosition(Boat boat, long updatePeriodMilliseconds, long totalElapsedMilliseconds) {
|
||||
|
||||
//Checks if the current boat has finished the race or not.
|
||||
boolean finish = this.isLastLeg(boat.getCurrentLeg());
|
||||
|
||||
if (!finish) {
|
||||
|
||||
|
||||
//Calculates the distance travelled, in meters, in the current timeslice.
|
||||
double distanceTravelledMeters = boat.calculateMetersTravelled(updatePeriodMilliseconds);
|
||||
|
||||
//Scale it.
|
||||
distanceTravelledMeters = distanceTravelledMeters * this.scaleFactor;
|
||||
|
||||
|
||||
//Move the boat forwards that many meters, and advances its time counters by enough milliseconds.
|
||||
boat.moveForwards(distanceTravelledMeters, updatePeriodMilliseconds * this.scaleFactor);
|
||||
|
||||
|
||||
//Only get a new VMG if the boat will go outside the course, or X seconds have elapsed.
|
||||
boolean willStayInsideCourse = this.checkBearingInsideCourse(boat.getBearing(), boat.getCurrentPosition());
|
||||
long tackPeriod = 15000;
|
||||
if (!willStayInsideCourse || (boat.getTimeSinceTackChange() > tackPeriod)) {
|
||||
|
||||
//Calculate the boat's bearing bounds, to ensure that it doesn't go out of the course.
|
||||
Bearing[] bearingBounds = this.calculateBearingBounds(boat);
|
||||
|
||||
|
||||
//Calculate the new VMG.
|
||||
VMG newVMG = this.calculateVMG(boat, bearingBounds);
|
||||
|
||||
|
||||
//If the new vmg improves velocity, use it.
|
||||
if (improvesVelocity(boat, newVMG)) {
|
||||
boat.setVMG(newVMG);
|
||||
|
||||
} else {
|
||||
//We also need to use the new VMG if our current bearing will take us out of the course.
|
||||
if (!willStayInsideCourse) {
|
||||
boat.setVMG(newVMG);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.updateEstimatedTime(boat);
|
||||
|
||||
|
||||
//Check the boats position (update leg and stuff).
|
||||
this.checkPosition(boat, totalTimeElapsed);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the upper and lower bounds that the boat may have in order to not go outside of the course.
|
||||
* @param boat The boat to check.
|
||||
* @return An array of bearings. The first is the lower bound, the second is the upper bound.
|
||||
*/
|
||||
private Bearing[] calculateBearingBounds(Boat boat) {
|
||||
|
||||
Bearing[] bearings = new Bearing[2];
|
||||
|
||||
Bearing lowerBearing = Bearing.fromDegrees(0.001);
|
||||
Bearing upperBearing = Bearing.fromDegrees(359.999);
|
||||
|
||||
|
||||
|
||||
double lastAngle = -1;
|
||||
boolean lastAngleWasGood = false;
|
||||
|
||||
//Check all bearings between [0, 360).
|
||||
for (double angle = 0; angle < 360; angle += 1) {
|
||||
|
||||
//Create bearing from angle.
|
||||
Bearing bearing = Bearing.fromDegrees(angle);
|
||||
|
||||
//Check that if it is acceptable.
|
||||
boolean bearingIsGood = this.checkBearingInsideCourse(bearing, boat.getCurrentPosition());
|
||||
|
||||
|
||||
if (lastAngle != -1) {
|
||||
|
||||
if (lastAngleWasGood && !bearingIsGood) {
|
||||
//We have flipped over from good bearings to bad bearings. So the last good bearing is the upper bearing.
|
||||
upperBearing = Bearing.fromDegrees(lastAngle);
|
||||
}
|
||||
|
||||
if (!lastAngleWasGood && bearingIsGood) {
|
||||
//We have flipped over from bad bearings to good bearings. So the current bearing is the lower bearing.
|
||||
lowerBearing = Bearing.fromDegrees(angle);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
lastAngle = angle;
|
||||
lastAngleWasGood = bearingIsGood;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
//TODO BUG if it can't find either upper or lower, it returns (0, 359.999). Should return (boatbearing, boatbearing+0.0001)
|
||||
bearings[0] = lowerBearing;
|
||||
bearings[1] = upperBearing;
|
||||
|
||||
return bearings;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a given bearing, starting at a given position, would put a boat out of the course boundaries.
|
||||
* @param bearing The bearing to check.
|
||||
* @param position The position to start from.
|
||||
* @return True if the bearing would keep the boat in the course, false if it would take it out of the course.
|
||||
*/
|
||||
private boolean checkBearingInsideCourse(Bearing bearing, GPSCoordinate position) {
|
||||
|
||||
//Get azimuth from bearing.
|
||||
Azimuth azimuth = Azimuth.fromBearing(bearing);
|
||||
|
||||
|
||||
//Tests to see if a point in front of the boat is out of bounds.
|
||||
double epsilonMeters = 50d;
|
||||
GPSCoordinate testCoord = GPSCoordinate.calculateNewPosition(position, epsilonMeters, azimuth);
|
||||
|
||||
//If it isn't inside the boundary, calculate new bearing.
|
||||
if (GPSCoordinate.isInsideBoundary(testCoord, this.shrinkBoundary)) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Checks if a boat has finished any legs, or has pulled out of race (DNF).
|
||||
* @param boat The boat to check.
|
||||
* @param timeElapsed The total time, in milliseconds, that has elapsed since the race started.
|
||||
*/
|
||||
protected void checkPosition(Boat boat, long timeElapsed) {
|
||||
|
||||
//The distance, in nautical miles, within which the boat needs to get in order to consider that it has reached the marker.
|
||||
double epsilonNauticalMiles = 100.0 / Constants.NMToMetersConversion; //100 meters. TODO should be more like 5-10.
|
||||
|
||||
if (boat.calculateDistanceToNextMarker() < epsilonNauticalMiles) {
|
||||
//Boat has reached its target marker, and has moved on to a new leg.
|
||||
|
||||
|
||||
|
||||
//Calculate how much the boat overshot the marker by.
|
||||
double overshootMeters = boat.calculateDistanceToNextMarker();
|
||||
|
||||
|
||||
//Move boat on to next leg.
|
||||
Leg nextLeg = this.legs.get(boat.getCurrentLeg().getLegNumber() + 1);
|
||||
boat.setCurrentLeg(nextLeg);
|
||||
|
||||
//Add overshoot distance into the distance travelled for the next leg.
|
||||
boat.setDistanceTravelledInLeg(overshootMeters);
|
||||
|
||||
//Setting a high value for this allows the boat to immediately do a large turn, as it needs to in order to get to the next mark.
|
||||
boat.setTimeSinceTackChange(999999);
|
||||
|
||||
|
||||
//Check if the boat has finished or stopped racing.
|
||||
|
||||
if (this.isLastLeg(boat.getCurrentLeg())) {
|
||||
//Boat has finished.
|
||||
boat.setTimeFinished(timeElapsed);
|
||||
boat.setCurrentSpeed(0);
|
||||
boat.setStatus(BoatStatusEnum.FINISHED);
|
||||
} else if (doNotFinish()) {
|
||||
//Boat has pulled out of race.
|
||||
boat.setTimeFinished(timeElapsed);
|
||||
boat.setCurrentLeg(new Leg("DNF", -1));
|
||||
boat.setCurrentSpeed(0);
|
||||
boat.setStatus(BoatStatusEnum.DNF);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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 otherwse.
|
||||
*/
|
||||
private 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;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Sets the chance each boat has of failing at a gate or marker
|
||||
*
|
||||
* @param chance percentage chance a boat has of failing per checkpoint.
|
||||
*/
|
||||
protected void setDnfChance(int chance) {
|
||||
if (chance >= 0 && chance <= 100) {
|
||||
dnfChance = chance;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Decides if a boat should received a DNF status.
|
||||
* @return True means it should DNF, false means it shouldn't.
|
||||
*/
|
||||
protected boolean doNotFinish() {
|
||||
Random rand = new Random();
|
||||
return rand.nextInt(100) < dnfChance;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
private 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 number of boats that are still active in the race.
|
||||
* They become inactive by either finishing or withdrawing.
|
||||
* @return The number of boats still active in the race.
|
||||
*/
|
||||
protected int getNumberOfActiveBoats() {
|
||||
|
||||
int numberofActiveBoats = 0;
|
||||
|
||||
for (Boat boat : this.boats) {
|
||||
|
||||
//If the boat is currently racing, count it.
|
||||
if (boat.getStatus() == BoatStatusEnum.RACING) {
|
||||
numberofActiveBoats++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return numberofActiveBoats;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns an observable list of boats in the race.
|
||||
* @return List of boats in the race.
|
||||
*/
|
||||
public ObservableList<Boat> getBoats() {
|
||||
return boats;
|
||||
}
|
||||
|
||||
protected void initialiseWindDir(){
|
||||
windDirDegrees = 225;
|
||||
windDir = AC35UnitConverter.convertHeading(windDirDegrees);
|
||||
/*windDir = new Random().nextInt(65535+1);
|
||||
windDir = BoatLocation.convertHeadingIntToDouble(255);*/
|
||||
this.windDirection = new Bearing((int)windDir);
|
||||
}
|
||||
|
||||
protected void changeWindDir(){
|
||||
int r = new Random().nextInt(changeWind)+1;
|
||||
if(r==1){
|
||||
windDirDegrees = (0.5 + windDirDegrees) % 360;
|
||||
} else if (r==2){
|
||||
windDirDegrees = ((windDirDegrees - 0.5) + 360) % 360;///keep the degrees positive when below 0
|
||||
}
|
||||
if (windDirDegrees > windUpperBound){
|
||||
windDirDegrees = windUpperBound;
|
||||
}
|
||||
if (windDirDegrees < windLowerBound){
|
||||
windDirDegrees = windLowerBound;
|
||||
}
|
||||
|
||||
windDir = AC35UnitConverter.convertHeading(windDirDegrees);
|
||||
this.windDirection = new Bearing(windDirDegrees);
|
||||
}
|
||||
|
||||
protected void setChangeWind(int changeVal){
|
||||
if (changeVal>=0){
|
||||
changeWind = changeVal;
|
||||
}
|
||||
}
|
||||
|
||||
protected int getWind(){
|
||||
return (int)windDir;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the boat's estimated time to next mark if positive
|
||||
* @param boat to estimate time given its velocity
|
||||
*/
|
||||
private void updateEstimatedTime(Boat boat) {
|
||||
double velocityToMark = boat.getCurrentSpeed() * cos(boat.getBearing().radians() - boat.calculateBearingToNextMarker().radians()) / Constants.KnotsToMMPerSecond;
|
||||
if (velocityToMark > 0) {
|
||||
long timeFromNow = (long)(1000*boat.calculateDistanceToNextMarker()/velocityToMark);
|
||||
boat.setEstimatedTime(startTime + totalTimeElapsed + timeFromNow);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,25 @@
|
||||
package shared.dataInput;
|
||||
|
||||
|
||||
import shared.model.Boat;
|
||||
import shared.model.Mark;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Provides information about the boats and marker boats in a race.
|
||||
*/
|
||||
public interface BoatDataSource {
|
||||
|
||||
/**
|
||||
* Returns a map between source ID and boat for all boats in the race.
|
||||
* @return Map between source ID and boat.
|
||||
*/
|
||||
Map<Integer, Boat> getBoats();
|
||||
|
||||
/**
|
||||
* Returns a map between source ID and mark for all marks in the race.
|
||||
* @return Map between source ID and mark.
|
||||
*/
|
||||
Map<Integer, Mark> getMarkerBoats();
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
package shared.dataInput;
|
||||
|
||||
import network.Messages.Enums.RaceTypeEnum;
|
||||
import shared.model.Boat;
|
||||
import shared.model.CompoundMark;
|
||||
import shared.model.GPSCoordinate;
|
||||
import shared.model.Leg;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An object that holds relevant data for a race. <br>
|
||||
* Information includes: {@link shared.model.Boat Boat}s,
|
||||
* {@link shared.model.Leg Leg}s, {@link shared.model.CompoundMark CompoundMark}s and
|
||||
* the {@link shared.model.GPSCoordinate GPSCoordinate}s.
|
||||
*/
|
||||
public interface RaceDataSource {
|
||||
/**
|
||||
* Returns the list of boats competing in the race.
|
||||
* @return Boats competing in the race.
|
||||
*/
|
||||
List<Boat> getBoats();
|
||||
|
||||
/**
|
||||
* Returns the list of legs in the race.
|
||||
* @return The list of legs in the race.
|
||||
*/
|
||||
List<Leg> getLegs();
|
||||
|
||||
/**
|
||||
* Returns a list of coordinates representing the boundary of the race.
|
||||
* @return The boundary of the race.
|
||||
*/
|
||||
List<GPSCoordinate> getBoundary();
|
||||
|
||||
/**
|
||||
* Returns a list of CompoundMarks in the race.
|
||||
* @return
|
||||
*/
|
||||
List<CompoundMark> getCompoundMarks();
|
||||
|
||||
/**
|
||||
* Returns the ID of the race.
|
||||
* @return The ID of the race.
|
||||
*/
|
||||
int getRaceId();
|
||||
|
||||
/**
|
||||
* Returns the type of race.
|
||||
* @return The type of race.
|
||||
*/
|
||||
RaceTypeEnum getRaceType();
|
||||
|
||||
/**
|
||||
* Returns the start time/date of the race.
|
||||
* @return The race's start time.
|
||||
*/
|
||||
ZonedDateTime getZonedDateTime();
|
||||
|
||||
/**
|
||||
* Returns the GPS coordinate of the top left of the race map area.
|
||||
* @return Top left GPS coordinate.
|
||||
*/
|
||||
GPSCoordinate getMapTopLeft();
|
||||
|
||||
/**
|
||||
* Returns the GPS coordinate of the bottom right of the race map area.
|
||||
* @return Bottom right GPS coordinate.
|
||||
*/
|
||||
GPSCoordinate getMapBottomRight();
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package shared.exceptions;
|
||||
|
||||
/**
|
||||
* An exception thrown when an XMLReader cannot be constructed for some reason.
|
||||
*/
|
||||
public class XMLReaderException extends Exception {
|
||||
|
||||
public XMLReaderException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public XMLReaderException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@ -1,156 +0,0 @@
|
||||
package visualiser.dataInput;
|
||||
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.SAXException;
|
||||
import seng302.Model.Boat;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* XML Reader that reads in a file and initializes
|
||||
* {@link seng302.Mock.StreamedBoat StreamedBoat}s that will be participating
|
||||
* in a race.
|
||||
*/
|
||||
public class BoatXMLReader extends XMLReader {
|
||||
private final Map<Integer, StreamedBoat> streamedBoatMap = new HashMap<>();
|
||||
private Map<Integer, StreamedBoat> participants = new HashMap<>();
|
||||
|
||||
/**
|
||||
* Constructor for Boat XML Reader
|
||||
* @param filePath path of the file
|
||||
* @throws IOException error
|
||||
* @throws SAXException error
|
||||
* @throws ParserConfigurationException error
|
||||
*/
|
||||
public BoatXMLReader(String filePath) throws IOException, SAXException, ParserConfigurationException {
|
||||
this(filePath, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for Boat XML Reader
|
||||
* @param filePath file path to read
|
||||
* @param read whether or not to read and store the files straight away.
|
||||
* @throws IOException error
|
||||
* @throws SAXException error
|
||||
* @throws ParserConfigurationException error
|
||||
*/
|
||||
public BoatXMLReader(String filePath, boolean read) throws IOException, SAXException, ParserConfigurationException {
|
||||
super(filePath);
|
||||
if (read) {
|
||||
read();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor for Boat XML Reader
|
||||
* @param xmlString sting to read
|
||||
* @throws IOException error
|
||||
* @throws SAXException error
|
||||
* @throws ParserConfigurationException error
|
||||
*/
|
||||
public BoatXMLReader(InputStream xmlString) throws IOException, SAXException, ParserConfigurationException {
|
||||
super(xmlString);
|
||||
read();
|
||||
}
|
||||
|
||||
public void read() {
|
||||
readSettings();
|
||||
readShapes();
|
||||
readBoats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads boats settings.
|
||||
* INFORMATION FROM HERE IS IGNORED FOR NOW
|
||||
*/
|
||||
private void readSettings() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads different kinds of boat.
|
||||
* INFORMATION FROM HERE IS IGNORED FOR NOW
|
||||
*/
|
||||
private void readShapes() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the boats in the race
|
||||
*/
|
||||
private void readBoats() {
|
||||
Element nBoats = (Element) doc.getElementsByTagName("Boats").item(0);
|
||||
for (int i = 0; i < nBoats.getChildNodes().getLength(); i++) {
|
||||
Node boat = nBoats.getChildNodes().item(i);
|
||||
if (boat.getNodeName().equals("Boat") && boat.getAttributes().getNamedItem("Type").getTextContent().equals("Yacht")) {
|
||||
readSingleBoat(boat);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the information about one boat
|
||||
* Ignored values: ShapeID, StoweName, HullNum, Skipper, Type
|
||||
* @param boat The node to read boat data from.
|
||||
*/
|
||||
private void readSingleBoat(Node boat) {
|
||||
StreamedBoat streamedBoat;
|
||||
String country = null;
|
||||
int sourceID = Integer.parseInt(boat.getAttributes().getNamedItem("SourceID").getTextContent());
|
||||
String boatName = boat.getAttributes().getNamedItem("BoatName").getTextContent();
|
||||
String shortName = boat.getAttributes().getNamedItem("ShortName").getTextContent();
|
||||
if (exists(boat, "Country")) country = boat.getAttributes().getNamedItem("Country").getTextContent();
|
||||
|
||||
// Ignore all non participating boats
|
||||
if (participants.containsKey(sourceID)) {
|
||||
|
||||
if (!streamedBoatMap.containsKey(sourceID)) {
|
||||
if (country != null) {
|
||||
streamedBoat = new StreamedBoat(sourceID, boatName, country);
|
||||
} else {
|
||||
streamedBoat = new StreamedBoat(sourceID, boatName, shortName);
|
||||
}
|
||||
streamedBoatMap.put(sourceID, streamedBoat);
|
||||
// Override boat with new boat
|
||||
participants.put(sourceID, streamedBoat);
|
||||
}
|
||||
|
||||
for (int i = 0; i < boat.getChildNodes().getLength(); i++) {
|
||||
Node GPSposition = boat.getChildNodes().item(i);
|
||||
if (GPSposition.getNodeName().equals("GPSposition"))
|
||||
readBoatPositionInformation(sourceID, GPSposition);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Reads the positional information about a boat
|
||||
* Ignored values: FlagPosition, MastTop, Z value of GPSPosition
|
||||
* @param sourceID The source ID of the boat.
|
||||
* @param GPSposition The relative GPS position of the boat.
|
||||
*/
|
||||
private void readBoatPositionInformation(int sourceID, Node GPSposition) {
|
||||
// TODO Get relative point before implementing. (GPSposition is based
|
||||
// off a relative point).
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the participants
|
||||
* @param participants boats participating the race mapped by their source ID's
|
||||
*/
|
||||
public void setParticipants(Map<Integer, StreamedBoat> participants) {
|
||||
this.participants = participants;
|
||||
}
|
||||
|
||||
public List<Boat> getBoats() {
|
||||
return new ArrayList<>(streamedBoatMap.values());
|
||||
}
|
||||
}
|
||||
@ -1,26 +0,0 @@
|
||||
package visualiser.dataInput;
|
||||
|
||||
import seng302.Model.Boat;
|
||||
import seng302.Model.Leg;
|
||||
import seng302.Model.Marker;
|
||||
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* An object that holds relevant data for a race. <br>
|
||||
* Information includes: {@link seng302.Model.Boat Boat}s,
|
||||
* {@link seng302.Model.Leg Leg}s, {@link seng302.Model.Marker Marker}s and
|
||||
* the {@link seng302.GPSCoordinate GPSCoordinate}s to create a
|
||||
* {@link seng302.Model.ResizableRaceMap ResizableRaceMap}.
|
||||
*/
|
||||
public interface RaceDataSource {
|
||||
List<Boat> getBoats();
|
||||
List<Leg> getLegs();
|
||||
List<Marker> getMarkers();
|
||||
List<GPSCoordinate> getBoundary();
|
||||
|
||||
ZonedDateTime getZonedDateTime();
|
||||
GPSCoordinate getMapTopLeft();
|
||||
GPSCoordinate getMapBottomRight();
|
||||
}
|
||||
@ -1,52 +0,0 @@
|
||||
package visualiser.dataInput;
|
||||
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.Node;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
/**
|
||||
* The abstract class for reading in XML race data.
|
||||
*/
|
||||
public abstract class XMLReader {
|
||||
|
||||
protected Document doc;
|
||||
|
||||
protected XMLReader(String filePath) throws ParserConfigurationException, IOException, SAXException {
|
||||
InputStream fXmlFile = getClass().getClassLoader().getResourceAsStream(filePath);
|
||||
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
|
||||
doc = dBuilder.parse(fXmlFile);
|
||||
doc.getDocumentElement().normalize();
|
||||
}
|
||||
|
||||
protected XMLReader(InputStream xmlInput) throws ParserConfigurationException, IOException, SAXException {
|
||||
|
||||
DocumentBuilderFactory dbFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder dBuilder = dbFactory.newDocumentBuilder();
|
||||
doc = dBuilder.parse(xmlInput);
|
||||
}
|
||||
|
||||
public Document getDocument() {
|
||||
return doc;
|
||||
}
|
||||
|
||||
protected String getTextValueOfNode(Element n, String tagName) {
|
||||
return n.getElementsByTagName(tagName).item(0).getTextContent();
|
||||
}
|
||||
|
||||
protected boolean exists(Node node, String attribute) {
|
||||
return node.getAttributes().getNamedItem(attribute) != null;
|
||||
}
|
||||
|
||||
public String getAttribute(Element n, String attr) {
|
||||
return n.getAttribute(attr);
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in new issue