//package mock.model; // //import javafx.animation.AnimationTimer; //import network.Messages.BoatLocation; //import network.Messages.BoatStatus; //import network.Messages.Enums.BoatStatusEnum; //import network.Messages.Enums.RaceStatusEnum; //import network.Messages.LatestMessages; //import network.Messages.RaceStatus; //import network.Utils.AC35UnitConverter; //import shared.dataInput.BoatDataSource; //import shared.dataInput.RaceDataSource; //import shared.dataInput.RegattaDataSource; //import shared.model.*; // //import java.time.ZonedDateTime; //import java.time.temporal.ChronoUnit; //import java.util.ArrayList; //import java.util.Iterator; //import java.util.List; //import java.util.Map; // //import static java.lang.Math.cos; // ///** // * Unused class, copy of MockRace so methods can be deleted once they are moved (more of a checklist) // */ //public class SplitTODO { // // // /** // * 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 MockRace extends Race { // // /** // * 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). // * @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 LatestMessages to send events to. // * @param polars The polars table to be used for boat simulation. // * @param timeScale The timeScale for the race. See {@link Constants#RaceTimeScale}. // */ // public MockRace(BoatDataSource boatDataSource, RaceDataSource raceDataSource, RegattaDataSource regattaDataSource, LatestMessages latestMessages, Polars polars, int timeScale) { // // super(boatDataSource, raceDataSource, regattaDataSource, latestMessages); // // this.scaleFactor = timeScale; // } // // // // /** // * 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) { // // //Create message. // BoatLocation boatLocation = new BoatLocation( // mark.getSourceID(), // mark.getPosition().getLatitude(), // mark.getPosition().getLongitude(), // this.boatLocationSequenceNumber, // 0, 0, // this.raceClock.getCurrentTimeMilli()); // // //Iterates the sequence number. // this.boatLocationSequenceNumber++; // // this.latestMessages.setBoatLocation(boatLocation); // // // } // // /** // * Parse the boats in the race, and send it to mockOutput. // */ // private void parseBoatLocations() { // // //Parse each boat. // for (MockBoat boat : this.boats) { // // this.parseIndividualBoatLocation(boat); // // } // // } // // /** // * Parses an individual boat, and sends it to mockOutput. // * @param boat The boat to parse. // */ // private void parseIndividualBoatLocation(MockBoat boat) { // // BoatLocation boatLocation = new BoatLocation( // boat.getSourceID(), // boat.getCurrentPosition().getLatitude(), // boat.getCurrentPosition().getLongitude(), // this.boatLocationSequenceNumber, // boat.getBearing().degrees(), // boat.getCurrentSpeed(), // this.raceClock.getCurrentTimeMilli()); // // //Iterates the sequence number. // this.boatLocationSequenceNumber++; // // this.latestMessages.setBoatLocation(boatLocation); // // } // // // /** // * Updates the race time to a specified value, in milliseconds since the unix epoch. // * @param currentTime Milliseconds since unix epoch. // */ // private void updateRaceTime(long currentTime) { // this.raceClock.setUTCTime(currentTime); // } // // // /** // * Updates the race status enumeration based on the current time. // */ // private void updateRaceStatusEnum() { // // //The millisecond duration of the race. Negative means it hasn't started, so we flip sign. // long timeToStart = - this.raceClock.getDurationMilli(); // // // if (timeToStart > Constants.RacePreStartTime) { // //Time > 3 minutes is the prestart period. // this.setRaceStatusEnum(RaceStatusEnum.PRESTART); // // } else if ((timeToStart <= Constants.RacePreStartTime) && (timeToStart >= Constants.RacePreparatoryTime)) { // //Time between [1, 3] minutes is the warning period. // this.setRaceStatusEnum(RaceStatusEnum.WARNING); // // } else if ((timeToStart <= Constants.RacePreparatoryTime) && (timeToStart > 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 boatStatuses = new ArrayList<>(); // // //Add each boat status to the status list. // for (MockBoat boat : this.boats) { // // BoatStatus boatStatus = new BoatStatus( // boat.getSourceID(), // boat.getStatus(), // boat.getCurrentLeg().getLegNumber(), // boat.getEstimatedTimeAtNextMark().toInstant().toEpochMilli() ); // // boatStatuses.add(boatStatus); // } // // // //Convert wind direction and speed to ints. //TODO this conversion should be done inside the racestatus class. // int windDirectionInt = AC35UnitConverter.encodeHeading(this.getWindDirection().degrees()); // int windSpeedInt = (int) (this.getWindSpeed() * Constants.KnotsToMMPerSecond); // // //Create race status object, and send it. // RaceStatus raceStatus = new RaceStatus( // System.currentTimeMillis(), // this.raceId, // this.getRaceStatusEnum().getValue(), // this.raceClock.getStartingTimeMilli(), // windDirectionInt, // windSpeedInt, // this.getRaceType().getValue(), // boatStatuses); // // // this.latestMessages.setRaceStatus(raceStatus); // // // } // // // /** // * Sets the status of all boats in the race to RACING. // */ // private void setBoatsStatusToRacing() { // // for (MockBoat boat : this.boats) { // boat.setStatus(BoatStatusEnum.RACING); // } // } // // // /** // * Sets the estimated time at next mark for each boat to a specified time. This is used during the countdown timer to provide this value to boat before the race starts. // * @param time The time to provide to each boat. // */ // private void setBoatsTimeNextMark(ZonedDateTime time) { // // for (MockBoat boat : this.boats) { // boat.setEstimatedTimeAtNextMark(time); // } // } // // // /** // * Countdown timer until race starts. // */ // protected AnimationTimer countdownTimer = new AnimationTimer() { // // // long currentTime = System.currentTimeMillis(); // // @Override // public void handle(long arg0) { // // //Update race time. // updateRaceTime(currentTime); // // //Update the race status based on the current time. // updateRaceStatusEnum(); // // //Provide boat's with an estimated time at next mark until the race starts. // setBoatsTimeNextMark(raceClock.getCurrentTime()); // // //Parse the boat locations. // parseBoatLocations(); // // //Parse the marks. // parseMarks(); // // // Change wind direction // changeWindDirection(); // // //Parse the race status. // parseRaceStatus(); // // // if (getRaceStatusEnum() == RaceStatusEnum.STARTED) { // 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(); // // /** // * Current time during a loop iteration. // */ // long currentTime = System.currentTimeMillis(); // // /** // * The time of the previous frame, in milliseconds. // */ // long lastFrameTime = timeRaceStarted; // // @Override // public void handle(long arg0) { // // //Get the current time. // currentTime = System.currentTimeMillis(); // // //Update race time. // updateRaceTime(currentTime); // // // //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; // // //For each boat, we update its position, and generate a BoatLocationMessage. // for (MockBoat boat : boats) { // // //If it is still racing, update its position. // if (boat.getStatus() == BoatStatusEnum.RACING) { // // updatePosition(boat, framePeriod, raceClock.getDurationMilli()); // // } // // } // // } else { // //Otherwise, the race is over! // raceFinished.start(); // setRaceStatusEnum(RaceStatusEnum.FINISHED); // this.stop(); // } // // if (getNumberOfActiveBoats() != 0) { // // Change wind direction // changeWindDirection(); // // //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) { // // parseRaceStatus(); // // if (iters > 500) { // stop(); // } // iters++; // } // }; // // // /** // * Calculates a boat's VMG. // * @param boat The boat to calculate VMG for. // * @return VMG for the specified boat. // */ // private VMG calculateVMG(MockBoat boat) { // // // //Find the VMG inside these bounds. // VMG bestVMG = boat.getPolars().calculateVMG(this.getWindDirection(), this.getWindSpeed(), boat.calculateBearingToNextMarker(), Bearing.fromDegrees(0d), Bearing.fromDegrees(359.99999d)); // // // 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(MockBoat 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(MockBoat 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); // // long tackPeriod = 15000; // if (boat.getTimeSinceTackChange() > tackPeriod) { // //Calculate the new VMG. // VMG newVMG = this.calculateVMG(boat); // // // //If the new vmg improves velocity, use it. // if (improvesVelocity(boat, newVMG)) { // boat.setVMG(newVMG); // // } // } // // this.updateEstimatedTime(boat); // // // //Check the boats position (update leg and stuff). // this.checkPosition(boat, totalElapsedMilliseconds); // // } // // } // // // /** // * 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(MockBoat 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); // // } // // } // // } // // /** // * Updates the boat's estimated time to next mark if positive // * @param boat to estimate time given its velocity // */ // private void updateEstimatedTime(MockBoat boat) { // // double velocityToMark = boat.getCurrentSpeed() * cos(boat.getBearing().radians() - boat.calculateBearingToNextMarker().radians()) / Constants.KnotsToMMPerSecond; // // if (velocityToMark > 0) { // // //Calculate milliseconds until boat reaches mark. // long timeFromNow = (long) (1000 * boat.calculateDistanceToNextMarker() / velocityToMark); // // //Calculate time at which it will reach mark. // ZonedDateTime timeAtMark = this.raceClock.getCurrentTime().plus(timeFromNow, ChronoUnit.MILLIS); // boat.setEstimatedTimeAtNextMark(timeAtMark); // } // // } // } //}