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; import shared.dataInput.BoatDataSource; import shared.dataInput.RaceDataSource; import shared.dataInput.RegattaDataSource; import java.util.List; /** * Represents a yacht race. * This is a base class inherited by {@link mock.model.MockRace} and {@link visualiser.model.VisualiserRace}. * 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 implements Runnable { /** * 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; /** * The collection of latest race messages. * Can be either read from or written to. */ protected LatestMessages latestMessages; /** * The sequence number of the latest BoatLocation message sent or received. */ protected int boatLocationSequenceNumber = 1; /** * The sequence number of the latest RaceStatus message sent or received. */ protected int raceStatusSequenceNumber = 1; /** * 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 current wind direction bearing. */ protected Bearing windDirection; /** * Wind speed (knots). * Convert this to millimeters per second before passing to RaceStatus. */ protected double windSpeed; /** * 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, {@link #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...). * @param latestMessages The collection of latest messages, which can be written to, or read from. */ public Race(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource, LatestMessages latestMessages) { //Keep a reference to data sources. this.raceDataSource = raceDataSource; this.boatDataSource = boatDataSource; this.regattaDataSource = regattaDataSource; this.latestMessages = latestMessages; //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()); //this.raceClock.run();//TODO looks like we may not actually need this. //Race status. this.setRaceStatusEnum(RaceStatusEnum.NOT_ACTIVE); //Race type. this.raceType = raceDataSource.getRaceType(); //Wind speed. this.windSpeed = 0; //Wind direction. this.windDirection = Bearing.fromDegrees(0); } /** * 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. */ protected 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; } /** * Returns the wind bearing. * @return The wind bearing. */ public Bearing getWindDirection() { return windDirection; } /** * Returns the wind speed. * Measured in knots. * @return The wind speed. */ public double getWindSpeed() { return windSpeed; } /** * 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 number of frames generated per second. * @return Frames per second. */ public int getFps() { return lastFps.getValue(); } /** * 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; } } }