bug5217: unfinished (5) ; first version of the race comparison

This commit is contained in:
I550803
2022-08-01 16:47:57 +02:00
parent f03914a1ca
commit 36fd37f196
13 changed files with 522 additions and 408 deletions
@@ -1,95 +1,82 @@
package com.sap.sailing.domain.test.hash; package com.sap.sailing.domain.test.hash;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import java.net.MalformedURLException; import java.net.MalformedURLException;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.junit.Before; import org.junit.Before;
import org.junit.Test;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.test.OnlineTracTracBasedTest; import com.sap.sailing.domain.test.OnlineTracTracBasedTest;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tractracadapter.ReceiverType; import com.sap.sailing.domain.tractracadapter.ReceiverType;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class CalculatingHashTest extends OnlineTracTracBasedTest{ public class CalculatingHashTest extends OnlineTracTracBasedTest {
private static final Logger logger = Logger.getLogger(CalculatingHashTest.class.getName()); private static final Logger logger = Logger.getLogger(CalculatingHashTest.class.getName());
DynamicTrackedRaceImpl trackedRace1; DynamicTrackedRaceImpl trackedRace1;
DynamicTrackedRaceImpl trackedRace2; DynamicTrackedRaceImpl trackedRace2;
public CalculatingHashTest() throws MalformedURLException, URISyntaxException { public CalculatingHashTest() throws MalformedURLException, URISyntaxException {
super(); super();
} }
@Override @Override
protected String getExpectedEventName() { protected String getExpectedEventName() {
return "Academy Tracking 2011"; return "Academy Tracking 2011";
} }
@Before @Before
public void setUp() throws Exception{ public void setUp() throws Exception {
super.setUp("event_20110505_SailingTea", // Semifinale super.setUp("event_20110505_SailingTea", // Semifinale
/* raceId */ "01ea3604-02ef-11e1-9efc-406186cbf87c", /* raceId */ "01ea3604-02ef-11e1-9efc-406186cbf87c", /* liveUri */ null, /* storedUri */ null,
/* liveUri */ null, /* storedUri */ null, new ReceiverType[] { ReceiverType.MARKPASSINGS, ReceiverType.MARKPOSITIONS, ReceiverType.RACECOURSE,
new ReceiverType[] { ReceiverType.MARKPASSINGS, ReceiverType.MARKPOSITIONS, ReceiverType.RACECOURSE, ReceiverType.RACESTARTFINISH, ReceiverType.RAWPOSITIONS, ReceiverType.SENSORDATA}); ReceiverType.RACESTARTFINISH, ReceiverType.RAWPOSITIONS, ReceiverType.SENSORDATA });
trackedRace1 = getTrackedRace(); trackedRace1 = getTrackedRace();
super.setUp(); super.setUp();
super.setUp("event_20110505_SailingTea", // Semifinale super.setUp("event_20110505_SailingTea", // Semifinale
/* raceId */ "01ea3604-02ef-11e1-9efc-406186cbf87c", /* raceId */ "01ea3604-02ef-11e1-9efc-406186cbf87c", /* liveUri */ null, /* storedUri */ null,
/* liveUri */ null, /* storedUri */ null, new ReceiverType[] { ReceiverType.MARKPASSINGS, ReceiverType.MARKPOSITIONS, ReceiverType.RACECOURSE,
new ReceiverType[] { ReceiverType.MARKPASSINGS, ReceiverType.MARKPOSITIONS, ReceiverType.RACECOURSE, ReceiverType.RACESTARTFINISH, ReceiverType.RAWPOSITIONS, ReceiverType.SENSORDATA}); ReceiverType.RACESTARTFINISH, ReceiverType.RAWPOSITIONS, ReceiverType.SENSORDATA });
trackedRace2 = getTrackedRace(); trackedRace2 = getTrackedRace();
} }
// @Test // @Test
// public void testWithoutChangesInRace() { // public void testWithoutChangesInRace() {
// trackedRace1.calculateHash(); // trackedRace1.calculateHash();
// int[] hash1 = trackedRace1.getHashValue(); // int[] hash1 = trackedRace1.getHashValue();
// trackedRace2.calculateHash(); // trackedRace2.calculateHash();
// int[] hash2 = trackedRace2.getHashValue(); // int[] hash2 = trackedRace2.getHashValue();
// //
// assertEquals("0: Hash1: " + hash1[0] + " Hash2: " + hash2[0], hash1[0], hash2[0]); // assertEquals("0: Hash1: " + hash1[0] + " Hash2: " + hash2[0], hash1[0], hash2[0]);
// assertEquals("1: Hash1: " + hash1[1] + " Hash2: " + hash2[1], hash1[1], hash2[1]); // assertEquals("1: Hash1: " + hash1[1] + " Hash2: " + hash2[1], hash1[1], hash2[1]);
// assertEquals("2: Hash1: " + hash1[2] + " Hash2: " + hash2[2], hash1[2], hash2[2]); // assertEquals("2: Hash1: " + hash1[2] + " Hash2: " + hash2[2], hash1[2], hash2[2]);
// assertEquals("3: Hash1: " + hash1[3] + " Hash2: " + hash2[3], hash1[3], hash2[3]); // assertEquals("3: Hash1: " + hash1[3] + " Hash2: " + hash2[3], hash1[3], hash2[3]);
// assertEquals("4: Hash1: " + hash1[4] + " Hash2: " + hash2[4], hash1[4], hash2[4]); // assertEquals("4: Hash1: " + hash1[4] + " Hash2: " + hash2[4], hash1[4], hash2[4]);
// assertEquals("5: Hash1: " + hash1[5] + " Hash2: " + hash2[5], hash1[5], hash2[5]); // assertEquals("5: Hash1: " + hash1[5] + " Hash2: " + hash2[5], hash1[5], hash2[5]);
// assertEquals("6: Hash1: " + hash1[6] + " Hash2: " + hash2[6], hash1[6], hash2[6]); // assertEquals("6: Hash1: " + hash1[6] + " Hash2: " + hash2[6], hash1[6], hash2[6]);
// //assertEquals("7: Hash1: " + hash1[7] + " Hash2: " + hash2[7], hash1[7], hash2[7]); // //assertEquals("7: Hash1: " + hash1[7] + " Hash2: " + hash2[7], hash1[7], hash2[7]);
// } // }
// @Test // @Test
// public void testWithChangesInRace() { // public void testWithChangesInRace() {
// trackedRace1.calculateHash(); // trackedRace1.calculateHash();
// int[] hash1 = trackedRace1.getHashValue(); // int[] hash1 = trackedRace1.getHashValue();
//// Change of the race should result in a different hash //// Change of the race should result in a different hash
// TimePoint epoch = new MillisecondsTimePoint(0l); // TimePoint epoch = new MillisecondsTimePoint(0l);
// TimePoint now = MillisecondsTimePoint.now(); // TimePoint now = MillisecondsTimePoint.now();
// Map<String, Position> markPositions = new HashMap<String, Position>(); // Map<String, Position> markPositions = new HashMap<String, Position>();
// markPositions.put("CR Start (1)", new DegreePosition(53.562944999999985, 10.010104000000046)); // markPositions.put("CR Start (1)", new DegreePosition(53.562944999999985, 10.010104000000046));
// markPositions.put("CR Start (2)", new DegreePosition(53.562944999999985, 10.010104000000046)); // markPositions.put("CR Start (2)", new DegreePosition(53.562944999999985, 10.010104000000046));
// markPositions.put("Leeward mark", new DegreePosition(53.562145000000015, 10.009252)); // markPositions.put("Leeward mark", new DegreePosition(53.562145000000015, 10.009252));
// markPositions.put("Luvtonne", new DegreePosition(53.560581899999995, 10.005657)); // markPositions.put("Luvtonne", new DegreePosition(53.560581899999995, 10.005657));
// for (Waypoint w : getTrackedRace().getRace().getCourse().getWaypoints()) { // for (Waypoint w : getTrackedRace().getRace().getCourse().getWaypoints()) {
// for (Mark mark : w.getMarks()) { // for (Mark mark : w.getMarks()) {
// trackedRace2.getOrCreateTrack(mark).addGPSFix(new GPSFixImpl(markPositions.get(mark.getName()), epoch)); // trackedRace2.getOrCreateTrack(mark).addGPSFix(new GPSFixImpl(markPositions.get(mark.getName()), epoch));
// trackedRace2.getOrCreateTrack(mark).addGPSFix(new GPSFixImpl(markPositions.get(mark.getName()), now)); // trackedRace2.getOrCreateTrack(mark).addGPSFix(new GPSFixImpl(markPositions.get(mark.getName()), now));
// } // }
// } // }
// trackedRace2.calculateHash(); // trackedRace2.calculateHash();
// int[] hash2 = trackedRace2.getHashValue(); // int[] hash2 = trackedRace2.getHashValue();
// //
// assertNotEquals("Hash1 and Hash2 are equal", hash1, hash2); // assertNotEquals("Hash1 and Hash2 are equal", hash1, hash2);
// } // }
} }
@@ -50,6 +50,7 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.SensorFix; import com.sap.sailing.domain.common.tracking.SensorFix;
import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.leaderboard.impl.CompetitorProviderFromRaceColumnsAndRegattaLike; import com.sap.sailing.domain.leaderboard.impl.CompetitorProviderFromRaceColumnsAndRegattaLike;
import com.sap.sailing.domain.markpassinghash.impl.TrackedRaceHashForMarkPassingComparatorImpl.TypeOfHash;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver;
@@ -89,7 +90,6 @@ import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tracking.WindSummary; import com.sap.sailing.domain.tracking.WindSummary;
import com.sap.sailing.domain.tracking.WindTrack; import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.WindWithConfidence; import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sailing.domain.tracking.impl.TrackedRaceHashForMarkPassingComperatorImpl.typeOfHash;
import com.sap.sailing.domain.tracking.impl.WindTrackImpl; import com.sap.sailing.domain.tracking.impl.WindTrackImpl;
import com.sap.sailing.domain.windestimation.IncrementalWindEstimation; import com.sap.sailing.domain.windestimation.IncrementalWindEstimation;
import com.sap.sse.common.Bearing; import com.sap.sse.common.Bearing;
@@ -1365,11 +1365,11 @@ public class MockedTrackedRace implements DynamicTrackedRace {
} }
@Override @Override
public void setHashValuesForMarkPassingCalculation(Map<typeOfHash, Integer> hashValues) { public void setHashValuesForMarkPassingCalculation(Map<TypeOfHash, Integer> hashValues) {
} }
@Override @Override
public Map<typeOfHash, Integer> getHashValuesForMarkPassingCalculation() { public Map<TypeOfHash, Integer> getHashValuesForMarkPassingCalculation() {
return null; return null;
} }
@@ -41,6 +41,7 @@ import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.SensorFix; import com.sap.sailing.domain.common.tracking.SensorFix;
import com.sap.sailing.domain.markpassinghash.impl.TrackedRaceHashForMarkPassingComparatorImpl.TypeOfHash;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.ranking.RankingMetric; import com.sap.sailing.domain.ranking.RankingMetric;
@@ -69,7 +70,6 @@ import com.sap.sailing.domain.tracking.WindSummary;
import com.sap.sailing.domain.tracking.WindTrack; import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.WindWithConfidence; import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
import com.sap.sailing.domain.tracking.impl.TrackedRaceHashForMarkPassingComperatorImpl.typeOfHash;
import com.sap.sailing.domain.windestimation.IncrementalWindEstimation; import com.sap.sailing.domain.windestimation.IncrementalWindEstimation;
import com.sap.sse.common.Bearing; import com.sap.sse.common.Bearing;
import com.sap.sse.common.Distance; import com.sap.sse.common.Distance;
@@ -876,11 +876,11 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
} }
@Override @Override
public void setHashValuesForMarkPassingCalculation(Map<typeOfHash, Integer> hashValues) { public void setHashValuesForMarkPassingCalculation(Map<TypeOfHash, Integer> hashValues) {
} }
@Override @Override
public Map<typeOfHash, Integer> getHashValuesForMarkPassingCalculation() { public Map<TypeOfHash, Integer> getHashValuesForMarkPassingCalculation() {
return null; return null;
} }
@@ -18,6 +18,7 @@ Export-Package: com.sap.sailing.domain.base,
com.sap.sailing.domain.markpassingcalculation, com.sap.sailing.domain.markpassingcalculation,
com.sap.sailing.domain.markpassingcalculation.impl, com.sap.sailing.domain.markpassingcalculation.impl,
com.sap.sailing.domain.markpassingcalculation.splining, com.sap.sailing.domain.markpassingcalculation.splining,
com.sap.sailing.domain.markpassinghash.impl;x-friends:="com.sap.sailing.domain.persistence",
com.sap.sailing.domain.masterdataimport, com.sap.sailing.domain.masterdataimport,
com.sap.sailing.domain.orc, com.sap.sailing.domain.orc,
com.sap.sailing.domain.orc.impl;x-friends:="com.sap.sailing.domain.test,com.sap.sailing.domain.test,com.sap.sailing.mongodb.test", com.sap.sailing.domain.orc.impl;x-friends:="com.sap.sailing.domain.test,com.sap.sailing.domain.test,com.sap.sailing.mongodb.test",
@@ -0,0 +1,8 @@
package com.sap.sailing.domain.markpassinghash;
import com.sap.sailing.domain.tracking.impl.TrackedRaceImpl;
public interface TrackedRaceHashForMarkPassingCalculation {
boolean equals(TrackedRaceImpl trackedRace);
}
@@ -1,67 +1,69 @@
package com.sap.sailing.domain.tracking; package com.sap.sailing.domain.markpassinghash;
import com.sap.sailing.domain.base.Boat; import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
/** /**
* This comperator is used to calculate a set of hashes for a {@link trackedRace} * This comperator is used to calculate a set of hashes for a {@link trackedRace} in order to lay the foundation to
* in order to lay the foundation to decide if the {@link markpassings} are recalculated or * decide if the {@link markpassings} are recalculated or loaded out of the database, when restarting a server.
* loaded out of the database, when restarting a server.
* *
* @author Fabian Kallenbach (I550803) * @author Fabian Kallenbach (I550803)
* *
*/ */
public interface TrackedRaceHashForMarkPassingComperator { public interface TrackedRaceHashForMarkPassingComparator {
/** /**
* This Method loads the individual hashes into the local hashmap and then {@link #setHashValuesForMarkPassingCalculation} in {@link TrackedRace}. * This Method loads the individual hashes into the local hashmap and then
* {@link #setHashValuesForMarkPassingCalculation} in {@link TrackedRace}.
*/ */
void CalculateHash(); void calculateHash();
/** /**
* Calculates the hash for the {@link competitor} on the basis of it's ID. * Calculates the hash for the {@link competitor} on the basis of it's ID.
*/ */
int CalculateHashForCompetitor (Competitor c); int calculateHashForCompetitor(Competitor c);
/** /**
* Calculates the hash for the {@link boat} on the basis of it's ID. * Calculates the hash for the {@link boat} on the basis of it's ID.
*/ */
int CalculateHashForBoat (Boat b); int calculateHashForBoat(Boat b);
/** /**
* Calculates the hash for the race start on the basis of {@link #getStartOfTracking} and if it isn't inferred also on the {@link #getStartOfRace}. * Calculates the hash for the race start on the basis of {@link #getStartOfTracking} and if it isn't inferred also
* on the {@link #getStartOfRace}.
*/ */
int CalculateHashForStart (); int calculateHashForStart();
/** /**
* Calculates the hash for the race end on the basis of {@link #getEndOfTracking}. * Calculates the hash for the race end on the basis of {@link #getEndOfTracking}.
*/ */
int CalculateHashForEnd (); int calculateHashForEnd();
/** /**
* Calculates the hash for the number of GPSFixes of the {@link trackedRace}. * Calculates the hash for the number of GPSFixes of the {@link trackedRace}.
*/ */
int CalculateHashForNumberOfGPSFixes (); int calculateHashForNumberOfGPSFixes();
/** /**
* Calculates the hash for all {@link GPSFix} on the basis of theire {@link TimePoint}s and {@link Position}s. * Calculates the hash for all {@link GPSFix} on the basis of theire {@link TimePoint}s and {@link Position}s.
*/ */
int CalculateHashForGPSFixes (); int calculateHashForGPSFixes();
/** /**
* Calculates the hash for a {@link TimePoint}. * Calculates the hash for a {@link TimePoint}.
*/ */
int CalculateHashForTimePoint (TimePoint tp); int calculateHashForTimePoint(TimePoint tp);
/** /**
* Calculates the hash for a {@link Position}. * Calculates the hash for a {@link Position}.
*/ */
int CalculateHashForPosition (Position p); int calculateHashForPosition(Position p);
/** /**
* Calculates the hash for a {@link Waypoint}. * Calculates the hash for a {@link Waypoint}.
*/ */
int CalculateHashForWaypoints (); int calculateHashForWaypoints();
} }
@@ -0,0 +1,9 @@
package com.sap.sailing.domain.markpassinghash;
import com.sap.sailing.domain.markpassinghash.impl.TrackedRaceHashForMarkPassingCalculationImpl;
import com.sap.sailing.domain.tracking.impl.TrackedRaceImpl;
public interface TrackedRaceHashForMaskPassingCalculationFactory {
TrackedRaceHashForMarkPassingCalculationImpl create(TrackedRaceImpl trackedRace);
}
@@ -0,0 +1,20 @@
package com.sap.sailing.domain.markpassinghash.impl;
import com.sap.sailing.domain.markpassinghash.TrackedRaceHashForMaskPassingCalculationFactory;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.impl.TrackedRaceImpl;
public class TrackedRaceHashForMarkPassingCalculationFactoryImpl implements TrackedRaceHashForMaskPassingCalculationFactory{
private final TrackedRace trackedRace;
public TrackedRaceHashForMarkPassingCalculationFactoryImpl(TrackedRaceImpl trackedRace) {
this.trackedRace = trackedRace;
}
@Override
public TrackedRaceHashForMarkPassingCalculationImpl create(TrackedRaceImpl trackedRace) {
TrackedRaceHashForMarkPassingCalculationImpl hashValues = new TrackedRaceHashForMarkPassingCalculationImpl(trackedRace.getHashValuesForMarkPassingCalculation());
return hashValues;
}
}
@@ -0,0 +1,56 @@
package com.sap.sailing.domain.markpassinghash.impl;
import java.util.Map;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.markpassinghash.impl.TrackedRaceHashForMarkPassingComparatorImpl.TypeOfHash;
import com.sap.sailing.domain.tracking.impl.TrackedRaceImpl;
public class TrackedRaceHashForMarkPassingCalculationImpl {
private final int competitor;
private final int start;
private final int end;
private final int waypoints;
private final int numberOfGPSFixes;
private final int gpsFixes;
public TrackedRaceHashForMarkPassingCalculationImpl(Map<TypeOfHash, Integer> hashValues) {
this.competitor = hashValues.get(TypeOfHash.COMPETITOR);
this.start = hashValues.get(TypeOfHash.START);
this.end = hashValues.get(TypeOfHash.END);
this.waypoints = hashValues.get(TypeOfHash.WAYPOINTS);
this.numberOfGPSFixes = hashValues.get(TypeOfHash.NUMBEROFGPSFIXES);
this.gpsFixes = hashValues.get(TypeOfHash.GPSFIXES);
}
public boolean equals(TrackedRaceImpl trackedRace) {
TrackedRaceHashForMarkPassingComparatorImpl hashCalculator = new TrackedRaceHashForMarkPassingComparatorImpl(
trackedRace);
int competitorHash = 0;
if (hashCalculator.calculateHashForWaypoints() != waypoints)
return false;
for (Competitor c : trackedRace.getRace().getCompetitors()) {
competitorHash = competitorHash + hashCalculator.calculateHashForCompetitor(c);
}
if (competitorHash != competitor)
return false;
if (hashCalculator.calculateHashForStart() != start)
return false;
if (hashCalculator.calculateHashForEnd() != end)
return false;
if (hashCalculator.calculateHashForNumberOfGPSFixes() != numberOfGPSFixes)
return false;
if (hashCalculator.calculateHashForGPSFixes() != gpsFixes)
return false;
return true;
}
}
@@ -1,4 +1,4 @@
package com.sap.sailing.domain.tracking.impl; package com.sap.sailing.domain.markpassinghash.impl;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@@ -10,54 +10,46 @@ import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.tracking.TrackedRaceHashForMarkPassingComperator; import com.sap.sailing.domain.markpassinghash.TrackedRaceHashForMarkPassingComparator;
import com.sap.sailing.domain.tracking.impl.TrackedRaceImpl;
import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
public class TrackedRaceHashForMarkPassingComperatorImpl implements TrackedRaceHashForMarkPassingComperator{ public class TrackedRaceHashForMarkPassingComparatorImpl implements TrackedRaceHashForMarkPassingComparator {
public enum TypeOfHash {
COMPETITOR, START, END, WAYPOINTS, NUMBEROFGPSFIXES, GPSFIXES
}
private static final Logger logger = Logger.getLogger(TrackedRaceHashForMarkPassingComparatorImpl.class.getName());
public enum typeOfHash {
COMPETITOR,
BOAT,
START,
END,
WAYPOINTS,
NUMBEROFGPSFIXES,
GPSFIXES
}
private static final Logger logger = Logger.getLogger(TrackedRaceHashForMarkPassingComperatorImpl.class.getName());
private TrackedRaceImpl trackedRace; private TrackedRaceImpl trackedRace;
private final Iterable<Waypoint> waypoints; private final Iterable<Waypoint> waypoints;
private Map<typeOfHash, Integer> hashValues; private Map<TypeOfHash, Integer> hashValues;
public TrackedRaceHashForMarkPassingComperatorImpl (TrackedRaceImpl trackedRace) { public TrackedRaceHashForMarkPassingComparatorImpl(TrackedRaceImpl trackedRace) {
this.trackedRace = trackedRace; this.trackedRace = trackedRace;
this.waypoints = trackedRace.getRace().getCourse().getWaypoints(); this.waypoints = trackedRace.getRace().getCourse().getWaypoints();
this.hashValues = new HashMap<>(); this.hashValues = new HashMap<>();
} }
public void CalculateHash() { public void calculateHash() {
int competitorHash = 0; int competitorHash = 0;
int boatHash = 0; hashValues.put(TypeOfHash.WAYPOINTS, calculateHashForWaypoints());
hashValues.put(typeOfHash.WAYPOINTS, CalculateHashForWaypoints());
for (Competitor c : trackedRace.getRace().getCompetitors()) { for (Competitor c : trackedRace.getRace().getCompetitors()) {
competitorHash = competitorHash + CalculateHashForCompetitor(c); competitorHash = competitorHash + calculateHashForCompetitor(c);
boatHash = boatHash + CalculateHashForBoat(trackedRace.getRace().getBoatOfCompetitor(c));
} }
hashValues.put(typeOfHash.COMPETITOR, competitorHash); hashValues.put(TypeOfHash.COMPETITOR, competitorHash);
hashValues.put(typeOfHash.BOAT, boatHash); hashValues.put(TypeOfHash.START, calculateHashForStart());
hashValues.put(typeOfHash.START, CalculateHashForStart()); hashValues.put(TypeOfHash.END, calculateHashForEnd());
hashValues.put(typeOfHash.END, CalculateHashForEnd()); hashValues.put(TypeOfHash.NUMBEROFGPSFIXES, calculateHashForNumberOfGPSFixes());
hashValues.put(TypeOfHash.GPSFIXES, calculateHashForGPSFixes());
hashValues.put(typeOfHash.NUMBEROFGPSFIXES, CalculateHashForNumberOfGPSFixes());
hashValues.put(typeOfHash.GPSFIXES, CalculateHashForGPSFixes());
trackedRace.setHashValuesForMarkPassingCalculation(hashValues); trackedRace.setHashValuesForMarkPassingCalculation(hashValues);
} }
public int CalculateHashForCompetitor (Competitor c) { public int calculateHashForCompetitor(Competitor c) {
int res = 0; int res = 0;
try { try {
res = res ^ c.getId().hashCode(); res = res ^ c.getId().hashCode();
res = (res << 5) - res; res = (res << 5) - res;
@@ -66,50 +58,50 @@ public class TrackedRaceHashForMarkPassingComperatorImpl implements TrackedRaceH
} }
return res; return res;
} }
public int CalculateHashForBoat (Boat b) { public int calculateHashForBoat(Boat b) {
int res = 0; int res = 0;
try { try {
res = res ^ b.getBoatClass().getName().hashCode(); res = res ^ b.getBoatClass().getName().hashCode();
res = (res << 5) - res; res = (res << 5) - res;
} catch (Exception e) { } catch (Exception e) {
logger.info("Hash calculation for competitor " + b + " failed: " + e); logger.info("Hash calculation for competitor " + b + " failed: " + e);
} }
return res; return res;
} }
public int CalculateHashForStart () { public int calculateHashForStart() {
int res = 0; int res = 0;
try { try {
res = res ^ CalculateHashForTimePoint(trackedRace.getStartOfTracking()); res = res ^ calculateHashForTimePoint(trackedRace.getStartOfTracking());
res = (res << 5) - res; res = (res << 5) - res;
} catch (Exception e) { } catch (Exception e) {
logger.info("An error occured when getting the start of tracking: " + e); logger.info("An error occured when getting the start of tracking: " + e);
} }
try { try {
//Maybe that could be solved by using .getStartOfRace(boolean inferred) // Maybe that could be solved by using .getStartOfRace(boolean inferred)
if( trackedRace.getTrackedRegatta().getRegatta().useStartTimeInference() == false) { if (trackedRace.getTrackedRegatta().getRegatta().useStartTimeInference() == false) {
res = res ^ CalculateHashForTimePoint(trackedRace.getStartOfRace()); res = res ^ calculateHashForTimePoint(trackedRace.getStartOfRace());
res = (res << 5) - res; res = (res << 5) - res;
} }
} catch (Exception e) { } catch (Exception e) {
logger.info("An error occured when getting the starttime: " + e); logger.info("An error occured when getting the starttime: " + e);
} }
return res; return res;
} }
public int CalculateHashForEnd () { public int calculateHashForEnd() {
int res = 0; int res = 0;
try { try {
res = res ^ CalculateHashForTimePoint(trackedRace.getEndOfTracking()); res = res ^ calculateHashForTimePoint(trackedRace.getEndOfTracking());
res = (res << 5) - res; res = (res << 5) - res;
} catch (Exception e) { } catch (Exception e) {
logger.info("Hash calculation for end of Tracking failed: " + e); logger.info("Hash calculation for end of Tracking failed: " + e);
} }
return res; return res;
} }
public int CalculateHashForNumberOfGPSFixes () { public int calculateHashForNumberOfGPSFixes() {
int count = 0; int count = 0;
for (Waypoint w : waypoints) { for (Waypoint w : waypoints) {
for (Mark m : w.getMarks()) { for (Mark m : w.getMarks()) {
@@ -117,8 +109,8 @@ public class TrackedRaceHashForMarkPassingComperatorImpl implements TrackedRaceH
try { try {
trackedRace.getTrack(m).lockForRead(); trackedRace.getTrack(m).lockForRead();
gpsTrack = trackedRace.getTrack(m).getFixes(); gpsTrack = trackedRace.getTrack(m).getFixes();
//Unused warning since we need the individual Fixes only to count // Unused warning since we need the individual Fixes only to count
for (GPSFix gf : gpsTrack) { for (int i = 0; i < Util.size(gpsTrack); i++) {
count++; count++;
} }
} catch (Exception e) { } catch (Exception e) {
@@ -130,9 +122,8 @@ public class TrackedRaceHashForMarkPassingComperatorImpl implements TrackedRaceH
} }
return count; return count;
} }
public int calculateHashForGPSFixes() {
public int CalculateHashForGPSFixes () {
int res = 0; int res = 0;
for (Waypoint w : waypoints) { for (Waypoint w : waypoints) {
for (Mark m : w.getMarks()) { for (Mark m : w.getMarks()) {
@@ -141,9 +132,9 @@ public class TrackedRaceHashForMarkPassingComperatorImpl implements TrackedRaceH
trackedRace.getTrack(m).lockForRead(); trackedRace.getTrack(m).lockForRead();
gpsTrack = trackedRace.getTrack(m).getFixes(); gpsTrack = trackedRace.getTrack(m).getFixes();
for (GPSFix gf : gpsTrack) { for (GPSFix gf : gpsTrack) {
res = res ^ CalculateHashForTimePoint(gf.getTimePoint()); res = res ^ calculateHashForTimePoint(gf.getTimePoint());
res = (res << 5) - res; res = (res << 5) - res;
res = res ^ CalculateHashForPosition(gf.getPosition()); res = res ^ calculateHashForPosition(gf.getPosition());
res = (res << 5) - res; res = (res << 5) - res;
} }
} catch (Exception e) { } catch (Exception e) {
@@ -155,31 +146,31 @@ public class TrackedRaceHashForMarkPassingComperatorImpl implements TrackedRaceH
} }
return res; return res;
} }
public int CalculateHashForTimePoint (TimePoint tp) { public int calculateHashForTimePoint(TimePoint tp) {
int res = 0; int res = 0;
//How problematic is this cast? // How problematic is this cast?
try { try {
res = res ^ (int) tp.asMillis(); res = res ^ (int) tp.asMillis();
res = (res << 5) - res; res = (res << 5) - res;
} catch (Exception e) { } catch (Exception e) {
logger.info("Ther"); logger.info("Ther");
} }
return res;
}
public int CalculateHashForPosition (Position p) {
int res = 0;
res = res ^ (int) p.getLatDeg();
res = res ^ (int) p.getLngDeg();
res = (res << 5) - res;
return res; return res;
} }
public int CalculateHashForWaypoints () { public int calculateHashForPosition(Position p) {
int res = 0; int res = 0;
for(Waypoint p : waypoints) { res = res ^ (int) p.getLatDeg();
res = res ^ (int) p.getLngDeg();
res = (res << 5) - res;
return res;
}
public int calculateHashForWaypoints() {
int res = 0;
for (Waypoint p : waypoints) {
res = res ^ p.getId().hashCode(); res = res ^ p.getId().hashCode();
try { try {
res = res ^ p.getPassingInstructions().hashCode(); res = res ^ p.getPassingInstructions().hashCode();
@@ -187,6 +178,6 @@ public class TrackedRaceHashForMarkPassingComperatorImpl implements TrackedRaceH
logger.info("Hash calculation for Waypoints failed: " + e); logger.info("Hash calculation for Waypoints failed: " + e);
} }
} }
return res; return res;
} }
} }
@@ -35,13 +35,13 @@ import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.SensorFix; import com.sap.sailing.domain.common.tracking.SensorFix;
import com.sap.sailing.domain.markpassinghash.impl.TrackedRaceHashForMarkPassingComparatorImpl.TypeOfHash;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.ranking.RankingMetric; import com.sap.sailing.domain.ranking.RankingMetric;
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore; import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sailing.domain.tracking.impl.TrackedRaceHashForMarkPassingComperatorImpl.typeOfHash;
import com.sap.sailing.domain.windestimation.IncrementalWindEstimation; import com.sap.sailing.domain.windestimation.IncrementalWindEstimation;
import com.sap.sse.common.Bearing; import com.sap.sse.common.Bearing;
import com.sap.sse.common.Distance; import com.sap.sse.common.Distance;
@@ -814,11 +814,11 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
} }
@Override @Override
public void setHashValuesForMarkPassingCalculation(Map<typeOfHash, Integer> hashValues) { public void setHashValuesForMarkPassingCalculation(Map<TypeOfHash, Integer> hashValues) {
} }
@Override @Override
public Map<typeOfHash, Integer> getHashValuesForMarkPassingCalculation() { public Map<TypeOfHash, Integer> getHashValuesForMarkPassingCalculation() {
return null; return null;
} }
@@ -52,6 +52,7 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.SensorFix; import com.sap.sailing.domain.common.tracking.SensorFix;
import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache; import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache;
import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator; import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator;
import com.sap.sailing.domain.markpassinghash.impl.TrackedRaceHashForMarkPassingComparatorImpl.TypeOfHash;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver;
@@ -60,7 +61,6 @@ import com.sap.sailing.domain.ranking.RankingMetric;
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
import com.sap.sailing.domain.tracking.impl.NonCachingMarkPositionAtTimePointCache; import com.sap.sailing.domain.tracking.impl.NonCachingMarkPositionAtTimePointCache;
import com.sap.sailing.domain.tracking.impl.TrackedRaceImpl; import com.sap.sailing.domain.tracking.impl.TrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.TrackedRaceHashForMarkPassingComperatorImpl.typeOfHash;
import com.sap.sailing.domain.windestimation.IncrementalWindEstimation; import com.sap.sailing.domain.windestimation.IncrementalWindEstimation;
import com.sap.sse.common.Bearing; import com.sap.sse.common.Bearing;
import com.sap.sse.common.Distance; import com.sap.sse.common.Distance;
@@ -74,6 +74,7 @@ import com.sap.sse.security.shared.HasPermissions;
import com.sap.sse.security.shared.QualifiedObjectIdentifier; import com.sap.sse.security.shared.QualifiedObjectIdentifier;
import com.sap.sse.security.shared.TypeRelativeObjectIdentifier; import com.sap.sse.security.shared.TypeRelativeObjectIdentifier;
import com.sap.sse.security.shared.WithQualifiedObjectIdentifier; import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
/** /**
* Live tracking data of a single race. The race follows a defined {@link Course} with a sequence of {@link Leg}s. The * Live tracking data of a single race. The race follows a defined {@link Course} with a sequence of {@link Leg}s. The
* course may change over time as the race committee decides to change it. Therefore, a {@link TrackedRace} instance * course may change over time as the race committee decides to change it. Therefore, a {@link TrackedRace} instance
@@ -89,8 +90,7 @@ import com.sap.sse.security.shared.WithQualifiedObjectIdentifier;
* @author Axel Uhl (d043530) * @author Axel Uhl (d043530)
* *
*/ */
public interface TrackedRace public interface TrackedRace extends Serializable, IsManagedByCache<DomainFactory>, WithQualifiedObjectIdentifier {
extends Serializable, IsManagedByCache<DomainFactory>, WithQualifiedObjectIdentifier {
final Duration START_TRACKING_THIS_MUCH_BEFORE_RACE_START = Duration.ONE_MINUTE.times(5); final Duration START_TRACKING_THIS_MUCH_BEFORE_RACE_START = Duration.ONE_MINUTE.times(5);
final Duration STOP_TRACKING_THIS_MUCH_AFTER_RACE_FINISH = Duration.ONE_SECOND.times(30); final Duration STOP_TRACKING_THIS_MUCH_AFTER_RACE_FINISH = Duration.ONE_SECOND.times(30);
@@ -101,7 +101,7 @@ public interface TrackedRace
RaceDefinition getRace(); RaceDefinition getRace();
RegattaAndRaceIdentifier getRaceIdentifier(); RegattaAndRaceIdentifier getRaceIdentifier();
/** /**
* Tells how ranks are to be assigned to the competitors at any time during the race. For one-design boat classes * Tells how ranks are to be assigned to the competitors at any time during the race. For one-design boat classes
* this will usually happen by projecting the competitors to the wind direction for upwind and downwind legs or to * this will usually happen by projecting the competitors to the wind direction for upwind and downwind legs or to
@@ -116,17 +116,17 @@ public interface TrackedRace
* Computes the estimated start time for this race (not to be confused with the {@link #getStartOfTracking()} time * Computes the estimated start time for this race (not to be confused with the {@link #getStartOfTracking()} time
* point which is expected to be before the race start time). The highest precedence take the * point which is expected to be before the race start time). The highest precedence take the
* {@link #attachedRaceLogs race logs} and their start time events, followed by the field {@link #startTimeReceived} * {@link #attachedRaceLogs race logs} and their start time events, followed by the field {@link #startTimeReceived}
* which can explicitly be set using {@link #setStartTimeReceived(TimePoint)}. When there are no {@link MarkPassing}s * which can explicitly be set using {@link #setStartTimeReceived(TimePoint)}. When there are no
* for the first mark, <code>null</code> is returned. If there are mark passings for the first mark and the start * {@link MarkPassing}s for the first mark, <code>null</code> is returned. If there are mark passings for the first
* time is less than {@link #MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS} before the first mark * mark and the start time is less than {@link #MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS}
* passing for the first mark. Otherwise, the first mark passing for the first mark minus * before the first mark passing for the first mark. Otherwise, the first mark passing for the first mark minus
* {@link #MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS} is returned as the race start time. * {@link #MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS} is returned as the race start time.
* <p> * <p>
* *
* If no start time can be determined this way, <code>null</code> is returned. * If no start time can be determined this way, <code>null</code> is returned.
*/ */
TimePoint getStartOfRace(); TimePoint getStartOfRace();
/** /**
* Like {@link #getStartOfRace()}, but ignoring any inference from start mark passings in case {@code inferred} is * Like {@link #getStartOfRace()}, but ignoring any inference from start mark passings in case {@code inferred} is
* {@code false}. In this case, if no official start time was set, e.g., in the {@link RaceLog} or explicitly using * {@code false}. In this case, if no official start time was set, e.g., in the {@link RaceLog} or explicitly using
@@ -137,15 +137,15 @@ public interface TrackedRace
/** /**
* @return the time point taken from a valid, non-revoked {@link RaceLogRaceStatusEvent} that transfers the race * @return the time point taken from a valid, non-revoked {@link RaceLogRaceStatusEvent} that transfers the race
* into status {@link RaceLogRaceStatus#FINISHING} or {@code null} if no such event is found. * into status {@link RaceLogRaceStatus#FINISHING} or {@code null} if no such event is found.
*/ */
TimePoint getFinishingTime(); TimePoint getFinishingTime();
/** /**
* @return the time point taken from a valid, non-revoked {@link RaceLogRaceStatusEvent} that transfers the race * @return the time point taken from a valid, non-revoked {@link RaceLogRaceStatusEvent} that transfers the race
* into status {@link RaceLogRaceStatus#FINISHED} from any of the {@link RaceLog}s attached to this race, * into status {@link RaceLogRaceStatus#FINISHED} from any of the {@link RaceLog}s attached to this race, or
* or {@code null} if no such event is found. * {@code null} if no such event is found.
* *
* @see {@link RaceChangeListener#finishedTimeChanged(TimePoint, TimePoint)} * @see {@link RaceChangeListener#finishedTimeChanged(TimePoint, TimePoint)}
*/ */
TimePoint getFinishedTime(); TimePoint getFinishedTime();
@@ -172,18 +172,18 @@ public interface TrackedRace
* Shorthand for <code>{@link #getStart()}.{@link TimePoint#compareTo(TimePoint) compareTo(at)} &lt;= 0</code> * Shorthand for <code>{@link #getStart()}.{@link TimePoint#compareTo(TimePoint) compareTo(at)} &lt;= 0</code>
*/ */
boolean hasStarted(TimePoint at); boolean hasStarted(TimePoint at);
/** /**
* A race is considered "live" if it * A race is considered "live" if it {@link TrackedRaceDTO#hasGPSData has GPS data} and
* {@link TrackedRaceDTO#hasGPSData has GPS data} and {@link TrackedRaceDTO#hasWindData wind data} and if the * {@link TrackedRaceDTO#hasWindData wind data} and if the <code>at</code> time point is between the start and the
* <code>at</code> time point is between the start and the end of the race. * end of the race.
* <p> * <p>
* *
* The pre-start phase of a race is interesting also in live mode. Therefore, if a {@link TrackedRace#getStartOfRace start * The pre-start phase of a race is interesting also in live mode. Therefore, if a {@link TrackedRace#getStartOfRace
* time} is available for the race, the {@link TimingConstants#PRE_START_PHASE_DURATION_IN_MILLIS} is subtracted * start time} is available for the race, the {@link TimingConstants#PRE_START_PHASE_DURATION_IN_MILLIS} is
* from the actual start time so that the pre-start phase also counts as live. If no start time is known for the * subtracted from the actual start time so that the pre-start phase also counts as live. If no start time is known
* race, but a {@link TrackedRace#getStartOfTracking start of tracking time} is known, it is used as the start of * for the race, but a {@link TrackedRace#getStartOfTracking start of tracking time} is known, it is used as the
* the "live" interval. * start of the "live" interval.
* <p> * <p>
* *
* If an {@link TrackedRace#getEndOfRace end time} is already known for the race, * If an {@link TrackedRace#getEndOfRace end time} is already known for the race,
@@ -193,8 +193,8 @@ public interface TrackedRace
* <p> * <p>
* *
* @param at * @param at
* the time point at which to determine whether the race for <code>fleet</code> * the time point at which to determine whether the race for <code>fleet</code> is/was live. A
* is/was live. A <code>null</code> value will use the start time of the race. * <code>null</code> value will use the start time of the race.
* @return * @return
*/ */
boolean isLive(TimePoint at); boolean isLive(TimePoint at);
@@ -222,12 +222,12 @@ public interface TrackedRace
TrackedLeg getCurrentLeg(TimePoint timePoint); TrackedLeg getCurrentLeg(TimePoint timePoint);
/** /**
* Tells the number of the last started leg at <code>timePoint</code> * Tells the number of the last started leg at <code>timePoint</code> The leg number is 0 before the start, the
* The leg number is 0 before the start, the number of the current leg during the race * number of the current leg during the race and the number of the last leg at the end of the race even if the race
* and the number of the last leg at the end of the race even if the race has finished. * has finished.
*/ */
int getLastLegStarted(TimePoint timePoint); int getLastLegStarted(TimePoint timePoint);
/** /**
* Precondition: waypoint must still be part of {@link #getRace()}.{@link RaceDefinition#getCourse() getCourse()}. * Precondition: waypoint must still be part of {@link #getRace()}.{@link RaceDefinition#getCourse() getCourse()}.
* Returns {@code null} for the first waypoint of the course. If the waypoint is not part of the course, an * Returns {@code null} for the first waypoint of the course. If the waypoint is not part of the course, an
@@ -251,16 +251,21 @@ public interface TrackedRace
* So every type of track has an associated name. With this construct you can have track implementations that * So every type of track has an associated name. With this construct you can have track implementations that
* provide specific functionality based on the contained fix type. * provide specific functionality based on the contained fix type.
* *
* @param competitor the competitor to get the track for * @param competitor
* @param trackName the name of the track to get * the competitor to get the track for
* @param trackName
* the name of the track to get
* @return the track associated to the given Competitor and name or <code>null</code> if there is none. * @return the track associated to the given Competitor and name or <code>null</code> if there is none.
*/ */
<FixT extends SensorFix, TrackT extends SensorFixTrack<Competitor, FixT>> TrackT getSensorTrack(Competitor competitor, String trackName); <FixT extends SensorFix, TrackT extends SensorFixTrack<Competitor, FixT>> TrackT getSensorTrack(
Competitor competitor, String trackName);
/** /**
* Returns all contained {@link SensorFixTrack SensorFixTracks} for the given trackName and associated to any competitor. * Returns all contained {@link SensorFixTrack SensorFixTracks} for the given trackName and associated to any
* competitor.
*/ */
<FixT extends SensorFix, TrackT extends SensorFixTrack<Competitor, FixT>> Iterable<TrackT> getSensorTracks(String trackName); <FixT extends SensorFix, TrackT extends SensorFixTrack<Competitor, FixT>> Iterable<TrackT> getSensorTracks(
String trackName);
/** /**
* Tells the leg on which the <code>competitor</code> was at time <code>at</code>. If the competitor hasn't passed * Tells the leg on which the <code>competitor</code> was at time <code>at</code>. If the competitor hasn't passed
@@ -329,7 +334,7 @@ public interface TrackedRace
* @see #getTrack(Mark) * @see #getTrack(Mark)
*/ */
GPSFixTrack<Mark, GPSFix> getOrCreateTrack(Mark mark); GPSFixTrack<Mark, GPSFix> getOrCreateTrack(Mark mark);
/** /**
* Yields the track describing <code>mark</code>'s movement over time; <code>null</code> if no track exists for * Yields the track describing <code>mark</code>'s movement over time; <code>null</code> if no track exists for
* <code>mark</code> so far. * <code>mark</code> so far.
@@ -357,7 +362,7 @@ public interface TrackedRace
default Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint) { default Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint) {
return getApproximatePosition(waypoint, timePoint, new NonCachingMarkPositionAtTimePointCache(this, timePoint)); return getApproximatePosition(waypoint, timePoint, new NonCachingMarkPositionAtTimePointCache(this, timePoint));
} }
/** /**
* Same as {@link #getApproximatePosition(Waypoint, TimePoint)}, but giving the caller the possibility to pass a * Same as {@link #getApproximatePosition(Waypoint, TimePoint)}, but giving the caller the possibility to pass a
* cache of mark positions and related information that can help speed up compound operations requiring frequent * cache of mark positions and related information that can help speed up compound operations requiring frequent
@@ -411,18 +416,19 @@ public interface TrackedRace
/** /**
* Retrieves the wind sources used so far by this race that have the specified <code>type</code> as their * Retrieves the wind sources used so far by this race that have the specified <code>type</code> as their
* {@link WindSource#getType() type}. Always returns a non-<code>null</code> iterable which may be empty in case the * {@link WindSource#getType() type}. Always returns a non-<code>null</code> iterable which may be empty in case the
* race does not use any wind source of the specified type (yet).<p> * race does not use any wind source of the specified type (yet).
* <p>
* *
* It is possible to ask for the {@link WindSourceType#COMBINED} and {@link WindSourceType#LEG_MIDDLE} types and * It is possible to ask for the {@link WindSourceType#COMBINED} and {@link WindSourceType#LEG_MIDDLE} types and get
* get a non-empty result although those sources are never returned by {@link #getWindSources()}. * a non-empty result although those sources are never returned by {@link #getWindSources()}.
*/ */
Set<WindSource> getWindSources(WindSourceType type); Set<WindSource> getWindSources(WindSourceType type);
/** /**
* Retrieves all wind sources known to this race, including those {@link #getWindSourcesToExclude() to exclude}. * Retrieves all wind sources known to this race, including those {@link #getWindSourcesToExclude() to exclude}.
* Callers can freely iterate because the set returned is weakly-consistent and never throws a {@link ConcurrentModificationException} * Callers can freely iterate because the set returned is weakly-consistent and never throws a
* upon iteration. The {@link WindSourceType#COMBINED} wind source * {@link ConcurrentModificationException} upon iteration. The {@link WindSourceType#COMBINED} wind source as well
* as well as the {@link WindSourceType#LEG_MIDDLE} sources are never part of the result. * as the {@link WindSourceType#LEG_MIDDLE} sources are never part of the result.
*/ */
Set<WindSource> getWindSources(); Set<WindSource> getWindSources();
@@ -475,7 +481,7 @@ public interface TrackedRace
* lock. * lock.
*/ */
NavigableSet<MarkPassing> getMarkPassings(Competitor competitor); NavigableSet<MarkPassing> getMarkPassings(Competitor competitor);
/** /**
* Returns competitor's mark passings. * Returns competitor's mark passings.
* *
@@ -490,18 +496,20 @@ public interface TrackedRace
/** /**
* This obtains the course's read lock before asking for the read lock for the <code>markPassings</code> structure. * This obtains the course's read lock before asking for the read lock for the <code>markPassings</code> structure.
* See also bug 1370 (http://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=1370). This is necessary because the * See also bug 1370 (http://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=1370). This is necessary because the
* code that executes a course update will first ask the course's write lock and then relay execution to the * code that executes a course update will first ask the course's write lock and then relay execution to the course
* course change listeners among which there is a {@link TrackedRace} which will then update the mark passings * change listeners among which there is a {@link TrackedRace} which will then update the mark passings for all
* for all competitors and therefore will need to ask the write lock for those. If the thread calling this method * competitors and therefore will need to ask the write lock for those. If the thread calling this method first
* first obtains the mark passings read lock and later, while holding on to that lock, asks for the course's read * obtains the mark passings read lock and later, while holding on to that lock, asks for the course's read lock, a
* lock, a deadlock may result.<p> * deadlock may result.
* <p>
* *
* Furthermore, when trying to acquire both, a lock for the {@link #getMarkPassings(Competitor) mark passings for a competitor} * Furthermore, when trying to acquire both, a lock for the {@link #getMarkPassings(Competitor) mark passings for a
* and a lock for the {@link #getMarkPassingsInOrder(Waypoint) mark passings for a waypoint, this needs to happen in exactly * competitor} and a lock for the {@link #getMarkPassingsInOrder(Waypoint) mark passings for a waypoint, this needs
* this order, or a deadlock may result.<p> * to happen in exactly this order, or a deadlock may result.
* <p>
* *
* The {@link #unlockAfterRead(Iterable)} method will symmetrically unlock the course's read lock after releasing the * The {@link #unlockAfterRead(Iterable)} method will symmetrically unlock the course's read lock after releasing
* read lock for the mark passings. * the read lock for the mark passings.
*/ */
void lockForRead(Iterable<MarkPassing> markPassings); void lockForRead(Iterable<MarkPassing> markPassings);
@@ -559,7 +567,7 @@ public interface TrackedRace
* {@link WindSource#TRACK_BASED_ESTIMATION} source is used, also the monitors of the competitors' GPS tracks. * {@link WindSource#TRACK_BASED_ESTIMATION} source is used, also the monitors of the competitors' GPS tracks.
*/ */
Tack getTack(Competitor competitor, TimePoint timePoint) throws NoWindException; Tack getTack(Competitor competitor, TimePoint timePoint) throws NoWindException;
/** /**
* Based on the wind direction at <code>timePoint</code> and at position <code>where</code>, compares the * Based on the wind direction at <code>timePoint</code> and at position <code>where</code>, compares the
* <code>boatBearing</code> to the wind's bearing at that time and place and determined the tack. * <code>boatBearing</code> to the wind's bearing at that time and place and determined the tack.
@@ -585,8 +593,8 @@ public interface TrackedRace
/** /**
* Computes a default wind direction based on the direction of the first leg at time <code>at</code>, with a default * Computes a default wind direction based on the direction of the first leg at time <code>at</code>, with a default
* speed of zero knots. Note that this wind direction can only be used if {@link #raceIsKnownToStartUpwind()} returns * speed of zero knots. Note that this wind direction can only be used if {@link #raceIsKnownToStartUpwind()}
* <code>true</code>. * returns <code>true</code>.
* *
* @param at * @param at
* usually the {@link #getStart() start time} should be used; if no valid start time is provided, the * usually the {@link #getStart() start time} should be used; if no valid start time is provided, the
@@ -618,7 +626,7 @@ public interface TrackedRace
* for the <code>key</code> is still ongoing, the result of that ongoing re-calculation is returned. * for the <code>key</code> is still ongoing, the result of that ongoing re-calculation is returned.
*/ */
Iterable<Maneuver> getManeuvers(Competitor competitor, TimePoint from, TimePoint to, boolean waitForLatest); Iterable<Maneuver> getManeuvers(Competitor competitor, TimePoint from, TimePoint to, boolean waitForLatest);
/** /**
* @return a non-<code>null</code> but perhaps empty list of the maneuvers that <code>competitor</code> performed in * @return a non-<code>null</code> but perhaps empty list of the maneuvers that <code>competitor</code> performed in
* this race. Depending on <code>waitForLatest</code> the result is taken from the cache straight away * this race. Depending on <code>waitForLatest</code> the result is taken from the cache straight away
@@ -659,7 +667,8 @@ public interface TrackedRace
* tracked race. This runs synchronized with the otherwise asynchronous loading of wind tracks, triggered by the * tracked race. This runs synchronized with the otherwise asynchronous loading of wind tracks, triggered by the
* constructor of the {@link TrackedRace} implementation classes. This procedure guarantees that eventually the * constructor of the {@link TrackedRace} implementation classes. This procedure guarantees that eventually the
* listener will have received a notification for all wind fixes, regardless of whether they were already loaded at * listener will have received a notification for all wind fixes, regardless of whether they were already loaded at
* the time the listener is registered or they are loaded after the registration has completed.<p> * the time the listener is registered or they are loaded after the registration has completed.
* <p>
* *
* The same is true for the GPS fixes for marks and competitors. * The same is true for the GPS fixes for marks and competitors.
*/ */
@@ -667,10 +676,10 @@ public interface TrackedRace
boolean notifyAboutGPSFixesAlreadyLoaded); boolean notifyAboutGPSFixesAlreadyLoaded);
void removeListener(RaceChangeListener listener); void removeListener(RaceChangeListener listener);
/** /**
* @return <code>null</code> if there are no mark passings for the <code>competitor</code> in this race * @return <code>null</code> if there are no mark passings for the <code>competitor</code> in this race or if the
* or if the competitor has not finished one of the legs in the race. * competitor has not finished one of the legs in the race.
*/ */
Distance getDistanceTraveled(Competitor competitor, TimePoint timePoint); Distance getDistanceTraveled(Competitor competitor, TimePoint timePoint);
@@ -680,32 +689,35 @@ public interface TrackedRace
Distance getDistanceTraveledIncludingGateStart(Competitor competitor, TimePoint timePoint); Distance getDistanceTraveledIncludingGateStart(Competitor competitor, TimePoint timePoint);
/** /**
* @return <code>null</code> if there are no mark passings for the <code>competitor</code> in this race * @return <code>null</code> if there are no mark passings for the <code>competitor</code> in this race or if the
* or if the competitor has not finished one of the legs in the race. * competitor has not finished one of the legs in the race.
*/ */
Distance getDistanceFoiled(Competitor competitor, TimePoint timePoint); Distance getDistanceFoiled(Competitor competitor, TimePoint timePoint);
/** /**
* @return <code>null</code> if there are no mark passings for the <code>competitor</code> in this race * @return <code>null</code> if there are no mark passings for the <code>competitor</code> in this race or if the
* or if the competitor has not finished one of the legs in the race. * competitor has not finished one of the legs in the race.
*/ */
Duration getDurationFoiled(Competitor competitor, TimePoint timePoint); Duration getDurationFoiled(Competitor competitor, TimePoint timePoint);
/** /**
* See {@link TrackedLegOfCompetitor#getWindwardDistanceToCompetitorFarthestAhead(TimePoint, WindPositionMode, RankingInfo)} * See
* {@link TrackedLegOfCompetitor#getWindwardDistanceToCompetitorFarthestAhead(TimePoint, WindPositionMode, RankingInfo)}
*/ */
Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode); Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint,
WindPositionMode windPositionMode);
/** /**
* Same as {@link #getWindwardDistanceToCompetitorFarthestAhead(Competitor, TimePoint, WindPositionMode)}, only with an * Same as {@link #getWindwardDistanceToCompetitorFarthestAhead(Competitor, TimePoint, WindPositionMode)}, only with
* additional cache to speed up wind and leg type and leg bearing calculations in case of multiple similar look-ups * an additional cache to speed up wind and leg type and leg bearing calculations in case of multiple similar
* for the same time point. * look-ups for the same time point.
* *
* @param rankingInfo * @param rankingInfo
* materialized ranking information that is expensive to calculate, avoiding redundant calculations * materialized ranking information that is expensive to calculate, avoiding redundant calculations
*/ */
Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint,
WindPositionMode windPositionMode, RankingInfo rankingInfo, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache); WindPositionMode windPositionMode, RankingInfo rankingInfo,
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache);
/** /**
* Calls {@link #getWindWithConfidence(Position, TimePoint, Iterable)} and excludes those wind sources listed in * Calls {@link #getWindWithConfidence(Position, TimePoint, Iterable)} and excludes those wind sources listed in
@@ -729,8 +741,8 @@ public interface TrackedRace
Set<WindSource> windSourcesToExclude); Set<WindSource> windSourcesToExclude);
/** /**
* Same as {@link #getEstimatedWindDirection(TimePoint)}, but propagates the confidence of the wind * Same as {@link #getEstimatedWindDirection(TimePoint)}, but propagates the confidence of the wind estimation,
* estimation, relative to the <code>timePoint</code> for which the request is made, in the result. The * relative to the <code>timePoint</code> for which the request is made, in the result. The
* {@link Wind#getPosition() position} of all {@link Wind} fixes returned is <code>null</code>. * {@link Wind#getPosition() position} of all {@link Wind} fixes returned is <code>null</code>.
*/ */
WindWithConfidence<TimePoint> getEstimatedWindDirectionWithConfidence(TimePoint timePoint); WindWithConfidence<TimePoint> getEstimatedWindDirectionWithConfidence(TimePoint timePoint);
@@ -748,16 +760,18 @@ public interface TrackedRace
* if <code>true</code> and any cache update is currently going on, wait for the update to complete and * if <code>true</code> and any cache update is currently going on, wait for the update to complete and
* then fetch the updated value; otherwise, serve this requests from whatever is currently in the cache * then fetch the updated value; otherwise, serve this requests from whatever is currently in the cache
*/ */
Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis); Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint,
boolean waitForLatestAnalysis);
/** /**
* Same as {@link #getAverageAbsoluteCrossTrackError(Competitor, TimePoint, boolean)}, only that a cache for leg type, * Same as {@link #getAverageAbsoluteCrossTrackError(Competitor, TimePoint, boolean)}, only that a cache for leg
* wind on leg and leg bearing is provided. * type, wind on leg and leg bearing is provided.
*/ */
Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalyses, Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint,
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache); boolean waitForLatestAnalyses, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache);
Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint from, TimePoint to, boolean upwindOnly, boolean waitForLatestAnalyses); Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint from, TimePoint to, boolean upwindOnly,
boolean waitForLatestAnalyses);
Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis); Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis);
@@ -776,13 +790,13 @@ public interface TrackedRace
WindStore getWindStore(); WindStore getWindStore();
Competitor getOverallLeader(TimePoint timePoint); Competitor getOverallLeader(TimePoint timePoint);
Competitor getOverallLeader(TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache); Competitor getOverallLeader(TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache);
Boat getBoatOfCompetitor(Competitor competitor); Boat getBoatOfCompetitor(Competitor competitor);
Competitor getCompetitorOfBoat(Boat boat); Competitor getCompetitorOfBoat(Boat boat);
/** /**
* Returns the competitors of this tracked race, according to their ranking. Competitors whose * Returns the competitors of this tracked race, according to their ranking. Competitors whose
* {@link #getRank(Competitor)} is 0 will be sorted "worst". * {@link #getRank(Competitor)} is 0 will be sorted "worst".
@@ -790,34 +804,34 @@ public interface TrackedRace
List<Competitor> getCompetitorsFromBestToWorst(TimePoint timePoint); List<Competitor> getCompetitorsFromBestToWorst(TimePoint timePoint);
/** /**
* Same as {@link #getCompetitorsFromBestToWorst(TimePoint)}, using a cache for wind, leg type and leg * Same as {@link #getCompetitorsFromBestToWorst(TimePoint)}, using a cache for wind, leg type and leg bearing
* bearing values. * values.
*/ */
List<Competitor> getCompetitorsFromBestToWorst(TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache); List<Competitor> getCompetitorsFromBestToWorst(TimePoint timePoint,
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache);
/** /**
* When provided with a {@link WindStore} during construction, the tracked race will * When provided with a {@link WindStore} during construction, the tracked race will asynchronously load the wind
* asynchronously load the wind data for this tracked race from the wind store and the GPS store in a * data for this tracked race from the wind store and the GPS store in a background thread and update this tracked
* background thread and update this tracked race with the results. Clients that want to wait for the wind * race with the results. Clients that want to wait for the wind loading process to complete can do so by calling
* loading process to complete can do so by calling this method which will block until the wind loading has * this method which will block until the wind loading has completed.
* completed.
*/ */
void waitUntilLoadingFromWindStoreComplete() throws InterruptedException; void waitUntilLoadingFromWindStoreComplete() throws InterruptedException;
/** /**
* Whenever a {@link RegattaLog} is attached, fixes are loaded from the {@link SensorFixStore} for all mappings * Whenever a {@link RegattaLog} is attached, fixes are loaded from the {@link SensorFixStore} for all mappings
* found in the {@code RegattaLog} in a separate thread. This method blocks if there is such a thread loading * found in the {@code RegattaLog} in a separate thread. This method blocks if there is such a thread loading fixes,
* fixes, until that thread is finished. * until that thread is finished.
*/ */
void waitForLoadingToFinish() throws InterruptedException; void waitForLoadingToFinish() throws InterruptedException;
/** /**
* Returns the current status of the {@link TrackedRace}. This consists of one of the {@link TrackedRaceStatusEnum} * Returns the current status of the {@link TrackedRace}. This consists of one of the {@link TrackedRaceStatusEnum}
* values plus a progress for LOADING state.<br> * values plus a progress for LOADING state.<br>
* Due to the fact that multiple loaders can exist that load data into the {@link TrackedRace}, the returned status * Due to the fact that multiple loaders can exist that load data into the {@link TrackedRace}, the returned status
* is a composite of those loader statuses. When a loader is finished, its status isn't tracked anymore. This causes * is a composite of those loader statuses. When a loader is finished, its status isn't tracked anymore. This causes
* the overall progress to not be guaranteed to be monotonic (progress may jump to a lower percentage when one loader * the overall progress to not be guaranteed to be monotonic (progress may jump to a lower percentage when one
* that had a progress of 100% is finished and thus removed). * loader that had a progress of 100% is finished and thus removed).
* *
* @see TrackedRaceStatus * @see TrackedRaceStatus
* @see DynamicTrackedRace#onStatusChanged(TrackingDataLoader, TrackedRaceStatus) * @see DynamicTrackedRace#onStatusChanged(TrackingDataLoader, TrackedRaceStatus)
@@ -825,8 +839,8 @@ public interface TrackedRace
TrackedRaceStatus getStatus(); TrackedRaceStatus getStatus();
/** /**
* If the {@link #getStatus() status} is currently {@link TrackedRaceStatusEnum#LOADING}, blocks until the status changes to any * If the {@link #getStatus() status} is currently {@link TrackedRaceStatusEnum#LOADING}, blocks until the status
* other status. * changes to any other status.
*/ */
void waitUntilNotLoading(); void waitUntilNotLoading();
@@ -836,64 +850,64 @@ public interface TrackedRace
* @return the race log detached or {@code null} if no race log can be found by the {@code identifier} * @return the race log detached or {@code null} if no race log can be found by the {@code identifier}
*/ */
RaceLog detachRaceLog(Serializable identifier); RaceLog detachRaceLog(Serializable identifier);
/** /**
* Detaches the link {@link RaceExecutionOrderProvider} * Detaches the link {@link RaceExecutionOrderProvider}
*/ */
void detachRaceExecutionOrderProvider(RaceExecutionOrderProvider raceExecutionOrderProvider); void detachRaceExecutionOrderProvider(RaceExecutionOrderProvider raceExecutionOrderProvider);
/** /**
* Attaches the passed race log with this {@link TrackedRace}. * Attaches the passed race log with this {@link TrackedRace}. This causes fixes from the {@link SensorFixStore} to
* This causes fixes from the {@link SensorFixStore} to be loaded for such {@link RegattaLogDeviceMappingEvent RegattaLogDeviceMappingEvents} * be loaded for such {@link RegattaLogDeviceMappingEvent RegattaLogDeviceMappingEvents} that are present in the
* that are present in the {@link RaceLog raceLog}. This loading is offloaded into a separate thread, that blocks * {@link RaceLog raceLog}. This loading is offloaded into a separate thread, that blocks serialization until it is
* serialization until it is finished. If multiple race logs are attached, the loading process is * finished. If multiple race logs are attached, the loading process is forced to be serialized. To guarantee that a
* forced to be serialized. * the fixes for a race log have been fully loaded before continuing, {@link #waitForLoadingToFinish()} can be used.
* To guarantee that a the fixes for a race log have been fully loaded before continuing, *
* {@link #waitForLoadingToFinish()} can be used. * @param raceLog
* @param raceLog to be attached. * to be attached.
*/ */
void attachRaceLog(RaceLog raceLog); void attachRaceLog(RaceLog raceLog);
/** /**
* Attaches the passed {@link RegattaLog} with this {@link TrackedRace}. * Attaches the passed {@link RegattaLog} with this {@link TrackedRace}. This also causes fixes from the
* This also causes fixes from the {@link SensorFixStore} to be loaded (see {@link #attachRaceLog(RaceLog)} for details). * {@link SensorFixStore} to be loaded (see {@link #attachRaceLog(RaceLog)} for details).
*/ */
void attachRegattaLog(RegattaLog regattaLog); void attachRegattaLog(RegattaLog regattaLog);
/** /**
* @return all currently attached {@link RegattaLog}s or an empty Iterable if there aren't any * @return all currently attached {@link RegattaLog}s or an empty Iterable if there aren't any
*/ */
Iterable<RegattaLog> getAttachedRegattaLogs(); Iterable<RegattaLog> getAttachedRegattaLogs();
/** /**
* Attaches a {@link RaceExecutionOrderProvider} to make a {@link TrackedRace} aware * Attaches a {@link RaceExecutionOrderProvider} to make a {@link TrackedRace} aware which races are scheduled
* which races are scheduled around it in the execution order of a {@link Regatta}. * around it in the execution order of a {@link Regatta}.
* */ */
void attachRaceExecutionProvider(RaceExecutionOrderProvider raceExecutionOrderProvider); void attachRaceExecutionProvider(RaceExecutionOrderProvider raceExecutionOrderProvider);
/** /**
* Returns the attached race log event track for this race if any. * Returns the attached race log event track for this race if any. Otherwise <code>null</code>.
* Otherwise <code>null</code>.
*/ */
RaceLog getRaceLog(Serializable identifier); RaceLog getRaceLog(Serializable identifier);
/** /**
* A setter for the listener on course design changes suggested by one of the {@link RaceLog RaceLog} attached to this * A setter for the listener on course design changes suggested by one of the {@link RaceLog RaceLog} attached to
* race. The listener is mostly part of the tracking provider adapter. * this race. The listener is mostly part of the tracking provider adapter.
* *
* @param listener * @param listener
* the listener to operate with. * the listener to operate with.
*/ */
void addCourseDesignChangedListener(CourseDesignChangedListener listener); void addCourseDesignChangedListener(CourseDesignChangedListener listener);
void addStartTimeChangedListener(StartTimeChangedListener listener); void addStartTimeChangedListener(StartTimeChangedListener listener);
void removeStartTimeChangedListener(StartTimeChangedListener listener); void removeStartTimeChangedListener(StartTimeChangedListener listener);
void addRaceAbortedListener(RaceAbortedListener listener); void addRaceAbortedListener(RaceAbortedListener listener);
/** /**
* Tells how far the given <code>competitor</code> was from the start line at the time point of the given seconds before the start. * Tells how far the given <code>competitor</code> was from the start line at the time point of the given seconds
* before the start.
* <p> * <p>
* *
* The distance to the line is calculated by projecting the competitor's position onto the line orthogonally and * The distance to the line is calculated by projecting the competitor's position onto the line orthogonally and
@@ -938,8 +952,9 @@ public interface TrackedRace
* Like {@link #getWindwardDistanceToFavoredSideOfStartLine(Competitor, long)}, but with a cache to accelerate * Like {@link #getWindwardDistanceToFavoredSideOfStartLine(Competitor, long)}, but with a cache to accelerate
* repetitive requests for wind and leg types. * repetitive requests for wind and leg types.
*/ */
Distance getWindwardDistanceToFavoredSideOfStartLine(Competitor competitor, long millisecondsBeforeRaceStart, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache); Distance getWindwardDistanceToFavoredSideOfStartLine(Competitor competitor, long millisecondsBeforeRaceStart,
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache);
/** /**
* Tells how far, projected onto the wind for upwind/downwind and projected onto the course middle line for reaching * Tells how far, projected onto the wind for upwind/downwind and projected onto the course middle line for reaching
* starts, the given <code>competitor</code> was from the favored end of the start line at the given * starts, the given <code>competitor</code> was from the favored end of the start line at the given
@@ -952,7 +967,8 @@ public interface TrackedRace
* returned. If the competitor's position cannot be determined, <code>null</code> is returned. * returned. If the competitor's position cannot be determined, <code>null</code> is returned.
*/ */
default Distance getWindwardDistanceToFavoredSideOfStartLine(Competitor competitor, TimePoint timePoint) { default Distance getWindwardDistanceToFavoredSideOfStartLine(Competitor competitor, TimePoint timePoint) {
return getWindwardDistanceToFavoredSideOfStartLine(competitor, timePoint, new LeaderboardDTOCalculationReuseCache(timePoint)); return getWindwardDistanceToFavoredSideOfStartLine(competitor, timePoint,
new LeaderboardDTOCalculationReuseCache(timePoint));
} }
/** /**
@@ -963,19 +979,19 @@ public interface TrackedRace
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache); WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache);
/** /**
* When the <code>competitor</code> has started, this method returns the distance to the starboard end of the start line * When the <code>competitor</code> has started, this method returns the distance to the starboard end of the start
* or---if the start waypoint was a single mark---the distance to the single start mark at the time the competitor started. * line or---if the start waypoint was a single mark---the distance to the single start mark at the time the
* If the competitor hasn't started yet, <code>null</code> is returned. * competitor started. If the competitor hasn't started yet, <code>null</code> is returned.
*/ */
Distance getDistanceFromStarboardSideOfStartLineWhenPassingStart(Competitor competitor); Distance getDistanceFromStarboardSideOfStartLineWhenPassingStart(Competitor competitor);
/** /**
* At the given timepoint and for the competitor, this method returns the distance to the starboard end of the start line * At the given timepoint and for the competitor, this method returns the distance to the starboard end of the start
* or---if the start waypoint was a single mark---the distance to the single start mark at the timepoint. * line or---if the start waypoint was a single mark---the distance to the single start mark at the timepoint. If
* If the competitor hasn't started yet, <code>null</code> is returned. * the competitor hasn't started yet, <code>null</code> is returned.
*/ */
Distance getDistanceFromStarboardSideOfStartLine(Competitor competitor, TimePoint timePoint); Distance getDistanceFromStarboardSideOfStartLine(Competitor competitor, TimePoint timePoint);
/** /**
* At the given timepoint and for the competitor, this method returns the distance between the starboard end of the * At the given timepoint and for the competitor, this method returns the distance between the starboard end of the
* start line and the competitor position projected perpendicularly onto the line. If the start waypoint was a * start line and the competitor position projected perpendicularly onto the line. If the start waypoint was a
@@ -984,7 +1000,7 @@ public interface TrackedRace
* hasn't started yet, <code>null</code> is returned. * hasn't started yet, <code>null</code> is returned.
*/ */
Distance getDistanceFromStarboardSideOfStartLineProjectedOntoLine(Competitor competitor, TimePoint timePoint); Distance getDistanceFromStarboardSideOfStartLineProjectedOntoLine(Competitor competitor, TimePoint timePoint);
/** /**
* For all competitors in this race computes their * For all competitors in this race computes their
* {@link #getDistanceFromStarboardSideOfStartLineProjectedOntoLine(Competitor, TimePoint)} and puts the results in * {@link #getDistanceFromStarboardSideOfStartLineProjectedOntoLine(Competitor, TimePoint)} and puts the results in
@@ -992,50 +1008,62 @@ public interface TrackedRace
* finding adjacent records, using {@link SortedMap#headMap(Object)} and {@link SortedMap#tailMap(Object)}. * finding adjacent records, using {@link SortedMap#headMap(Object)} and {@link SortedMap#tailMap(Object)}.
* <p> * <p>
* *
* Competitors who are part of this race but for which no such {@link Distance} can be calculated, a record is * Competitors who are part of this race but for which no such {@link Distance} can be calculated, a record is put
* put to the resulting map with a {@code null} value associated. Those entries are sorted to the end of the map, * to the resulting map with a {@code null} value associated. Those entries are sorted to the end of the map,
* meaning that "{@code null} is greater" than other values.</p> * meaning that "{@code null} is greater" than other values.
* </p>
* *
* Results are cached in a fixed-size LRU cache which is invalidated by competitor or mark positions within the * Results are cached in a fixed-size LRU cache which is invalidated by competitor or mark positions within the
* averaging time range of {@code timePoint}. With this, repeated requests for equal {@code timePoint}s have a good * averaging time range of {@code timePoint}. With this, repeated requests for equal {@code timePoint}s have a good
* chance of being fulfilled from a previous computation result.<p> * chance of being fulfilled from a previous computation result.
* <p>
* *
* Boats are not considered in this if they are {@link MaxPointsReason#DNC DNC} or {@link MaxPointsReason#DNC DNS} * Boats are not considered in this if they are {@link MaxPointsReason#DNC DNC} or {@link MaxPointsReason#DNC DNS}
* which both suggest the boat may not have made a serious attempt to start. * which both suggest the boat may not have made a serious attempt to start.
*/ */
SortedMap<Competitor, Distance> getDistancesFromStarboardSideOfStartLineProjectedOntoLine(TimePoint timePoint, SortedMap<Competitor, Distance> getDistancesFromStarboardSideOfStartLineProjectedOntoLine(TimePoint timePoint,
BiFunction<Competitor, TimePoint, MaxPointsReason> maxPointsReasonSupplier); BiFunction<Competitor, TimePoint, MaxPointsReason> maxPointsReasonSupplier);
/** /**
* Based on the result of {@link #getDistancesFromStarboardSideOfStartLineProjectedOntoLine(TimePoint, Function<Triple<Competitor, RaceColumn, TimePoint>, MaxPointsReason>)}, finds the * Based on the result of {@link #getDistancesFromStarboardSideOfStartLineProjectedOntoLine(TimePoint,
* next competitor to port regarding their start line projection at {@code timePoint}. Returns {@code null} if * Function<Triple<Competitor, RaceColumn, TimePoint>, MaxPointsReason>)}, finds the next competitor to port
* the start line cannot be determined or if there is no competitor further to port.<p> * regarding their start line projection at {@code timePoint}. Returns {@code null} if the start line cannot be
* determined or if there is no competitor further to port.
* <p>
* *
* Boats are not considered in this if they are {@link MaxPointsReason#DNC DNC} or {@link MaxPointsReason#DNC DNS} * Boats are not considered in this if they are {@link MaxPointsReason#DNC DNC} or {@link MaxPointsReason#DNC DNS}
* which both suggest the boat may not have made a serious attempt to start. * which both suggest the boat may not have made a serious attempt to start.
* @param maxPointsReasonSupplier TODO *
* @param maxPointsReasonSupplier
* TODO
*/ */
Competitor getNextCompetitorToPortOnStartLine(Competitor relativeTo, TimePoint timePoint, BiFunction<Competitor, TimePoint, MaxPointsReason> maxPointsReasonSupplier); Competitor getNextCompetitorToPortOnStartLine(Competitor relativeTo, TimePoint timePoint,
BiFunction<Competitor, TimePoint, MaxPointsReason> maxPointsReasonSupplier);
/** /**
* Based on the result of {@link #getDistancesFromStarboardSideOfStartLineProjectedOntoLine(TimePoint, Function<Triple<Competitor, RaceColumn, TimePoint>, MaxPointsReason>)}, finds the * Based on the result of {@link #getDistancesFromStarboardSideOfStartLineProjectedOntoLine(TimePoint,
* next competitor to starboard regarding their start line projection at {@code timePoint}. Returns {@code null} if * Function<Triple<Competitor, RaceColumn, TimePoint>, MaxPointsReason>)}, finds the next competitor to starboard
* the start line cannot be determined or if there is no competitor further to starboard.<p> * regarding their start line projection at {@code timePoint}. Returns {@code null} if the start line cannot be
* determined or if there is no competitor further to starboard.
* <p>
* *
* Boats are not considered in this if they are {@link MaxPointsReason#DNC DNC} or {@link MaxPointsReason#DNC DNS} * Boats are not considered in this if they are {@link MaxPointsReason#DNC DNC} or {@link MaxPointsReason#DNC DNS}
* which both suggest the boat may not have made a serious attempt to start. * which both suggest the boat may not have made a serious attempt to start.
* @param maxPointsReasonSupplier TODO *
* @param maxPointsReasonSupplier
* TODO
*/ */
Competitor getNextCompetitorToStarboardOnStartLine(Competitor relativeTo, TimePoint timePoint, BiFunction<Competitor, TimePoint, MaxPointsReason> maxPointsReasonSupplier); Competitor getNextCompetitorToStarboardOnStartLine(Competitor relativeTo, TimePoint timePoint,
BiFunction<Competitor, TimePoint, MaxPointsReason> maxPointsReasonSupplier);
/** /**
* The estimated speed of the competitor at the time point of the given seconds before the start of race. * The estimated speed of the competitor at the time point of the given seconds before the start of race.
*/ */
Speed getSpeed(Competitor competitor, long millisecondsBeforeRaceStart); Speed getSpeed(Competitor competitor, long millisecondsBeforeRaceStart);
/** /**
* The speed of the competitor when crossing the start line. It will return null if there are no recorded * The speed of the competitor when crossing the start line. It will return null if there are no recorded mark
* mark passings for this competitor (competitor did not yet or never pass the start line). * passings for this competitor (competitor did not yet or never pass the start line).
*/ */
Speed getSpeedWhenCrossingStartLine(Competitor competitor); Speed getSpeedWhenCrossingStartLine(Competitor competitor);
@@ -1043,56 +1071,58 @@ public interface TrackedRace
* Start time received by the tracking infrastructure. To determine real start time use {@link #getStartOfRace()}. * Start time received by the tracking infrastructure. To determine real start time use {@link #getStartOfRace()}.
*/ */
TimePoint getStartTimeReceived(); TimePoint getStartTimeReceived();
/** /**
* @return <code>null</code> if the start waypoint does not have two marks or the course * @return <code>null</code> if the start waypoint does not have two marks or the course is empty or the start
* is empty or the start waypoint is the only waypoint * waypoint is the only waypoint
*/ */
LineDetails getStartLine(TimePoint at); LineDetails getStartLine(TimePoint at);
/** /**
* @return <code>null</code> if the finish waypoint does not have two marks or the course * @return <code>null</code> if the finish waypoint does not have two marks or the course is empty or the finish
* is empty or the finish waypoint is the only waypoint * waypoint is the only waypoint
*/ */
LineDetails getFinishLine(TimePoint at); LineDetails getFinishLine(TimePoint at);
/** /**
* Length of course if there are mark passings for competitors. * Length of course if there are mark passings for competitors.
*/ */
Distance getCourseLength(); Distance getCourseLength();
/** /**
* The average wind speed with confidence for this race. It uses the timepoint of the {@link #getEndOfRace race end} as * The average wind speed with confidence for this race. It uses the timepoint of the {@link #getEndOfRace race end}
* a reference point, or, if that is {@code null}, the {@link #getTimePointOfNewestEvent()}. * as a reference point, or, if that is {@code null}, the {@link #getTimePointOfNewestEvent()}.
*/ */
SpeedWithConfidence<TimePoint> getAverageWindSpeedWithConfidence(long resolutionInMillis); SpeedWithConfidence<TimePoint> getAverageWindSpeedWithConfidence(long resolutionInMillis);
SpeedWithConfidence<TimePoint> getAverageWindSpeedWithConfidenceWithNumberOfSamples(int numberOfSamples); SpeedWithConfidence<TimePoint> getAverageWindSpeedWithConfidenceWithNumberOfSamples(int numberOfSamples);
/** /**
* The average wind speed with confidence for this race. * The average wind speed with confidence for this race.
*/ */
SpeedWithConfidence<TimePoint> getAverageWindSpeedWithConfidence(TimePoint fromTimePoint, TimePoint toTimePoint, int numberOfSamples); SpeedWithConfidence<TimePoint> getAverageWindSpeedWithConfidence(TimePoint fromTimePoint, TimePoint toTimePoint,
int numberOfSamples);
/** /**
* Computes the center point of the course's marks at the given time point. * Computes the center point of the course's marks at the given time point.
*/ */
Position getCenterOfCourse(TimePoint at); Position getCenterOfCourse(TimePoint at);
/** /**
* If the {@link RacingProcedure} defined by any of the {@link #attachedRaceLogs attached} {@link RaceLog}s * If the {@link RacingProcedure} defined by any of the {@link #attachedRaceLogs attached} {@link RaceLog}s has type
* has type {@link RacingProcedureType#GateStart}, this method returns <code>true</code>, <code>false</code> for * {@link RacingProcedureType#GateStart}, this method returns <code>true</code>, <code>false</code> for any other
* any other type found. If no type is found, e.g., because no race log is currently attached to this tracked race, * type found. If no type is found, e.g., because no race log is currently attached to this tracked race,
* <code>null</code> is returned, meaning that the type is not known. * <code>null</code> is returned, meaning that the type is not known.
*/ */
Boolean isGateStart(); Boolean isGateStart();
/** /**
* Returns the time in milliseconds when the line was closed with lowering flag {@link Flags#GOLF} if {@link #isGateStart()} is <code>true</code>. * Returns the time in milliseconds when the line was closed with lowering flag {@link Flags#GOLF} if
* If flag was not raised or {@link #isGateStart()} is <code>false</code> it returns <code>null</code>. * {@link #isGateStart()} is <code>true</code>. If flag was not raised or {@link #isGateStart()} is
* <code>false</code> it returns <code>null</code>.
*/ */
long getGateStartGolfDownTime(); long getGateStartGolfDownTime();
/** /**
* If the race was started with a gate start (see {@link #isGateStart()}, this method returns the distance between * If the race was started with a gate start (see {@link #isGateStart()}, this method returns the distance between
* the competitor's starting position and the port side of the start line (pin end); otherwise, returns a zero * the competitor's starting position and the port side of the start line (pin end); otherwise, returns a zero
@@ -1101,7 +1131,7 @@ public interface TrackedRace
Distance getAdditionalGateStartDistance(Competitor competitor, TimePoint timePoint); Distance getAdditionalGateStartDistance(Competitor competitor, TimePoint timePoint);
boolean isUsingMarkPassingCalculator(); boolean isUsingMarkPassingCalculator();
/** /**
* Calculates the estimated time it takes a competitor to sail the race, from start to finish. * Calculates the estimated time it takes a competitor to sail the race, from start to finish.
* *
@@ -1109,13 +1139,15 @@ public interface TrackedRace
* Used for positions of marks and wind information; note that sometimes the marks are not in place yet * Used for positions of marks and wind information; note that sometimes the marks are not in place yet
* when the race starts and that a windward mark may be collected already before the race finishes. * when the race starts and that a windward mark may be collected already before the race finishes.
* *
* @return estimated time it takes to complete the race, plus more useful information about how this result came about * @return estimated time it takes to complete the race, plus more useful information about how this result came
* about
* *
* @throws NotEnoughDataHasBeenAddedException * @throws NotEnoughDataHasBeenAddedException
* thrown if not enough polar data has been added or polar data service is not available * thrown if not enough polar data has been added or polar data service is not available
* @throws NoWindException * @throws NoWindException
*/ */
TargetTimeInfo getEstimatedTimeToComplete(TimePoint timepoint) throws NotEnoughDataHasBeenAddedException, NoWindException; TargetTimeInfo getEstimatedTimeToComplete(TimePoint timepoint)
throws NotEnoughDataHasBeenAddedException, NoWindException;
/** /**
* Determine the time sailed for the {@code competitor} at {@code timePoint} in this race. This ignores whether or * Determine the time sailed for the {@code competitor} at {@code timePoint} in this race. This ignores whether or
@@ -1154,38 +1186,40 @@ public interface TrackedRace
default Pair<TimePointSpecificationFoundInLog, TimePointSpecificationFoundInLog> getTrackingTimesFromRaceLogs() { default Pair<TimePointSpecificationFoundInLog, TimePointSpecificationFoundInLog> getTrackingTimesFromRaceLogs() {
return null; return null;
} }
default Pair<TimePoint, TimePoint> getStartAndFinishedTimeFromRaceLogs() { default Pair<TimePoint, TimePoint> getStartAndFinishedTimeFromRaceLogs() {
return null; return null;
} }
/** /**
* Returns all marks found in the {@link #markTracks} map and the mark device mappings and mark * Returns all marks found in the {@link #markTracks} map and the mark device mappings and mark definition events in
* definition events in all attached race and regatta logs. Note that usually a device mapping should * all attached race and regatta logs. Note that usually a device mapping should exist for a mark only if that mark
* exist for a mark only if that mark is also defined by a {@link RegattaLogDefineMarkEvent}, so for * is also defined by a {@link RegattaLogDefineMarkEvent}, so for this standard case, adding the marks from the
* this standard case, adding the marks from the device mark mappings would be redundant. * device mark mappings would be redundant.
*/ */
default Iterable<Mark> getMarksFromRegattaLogs() { default Iterable<Mark> getMarksFromRegattaLogs() {
return getMarks(); return getMarks();
} }
/** /**
* Updates the start and end of tracking in the following precedence order: * Updates the start and end of tracking in the following precedence order:
* *
* <ol> * <ol>
* <li>start/end of tracking in Racelog</li> * <li>start/end of tracking in Racelog</li>
* <li>manually set start/end of tracking via {@link #setStartOfTrackingReceived(TimePoint, boolean)} and {@link #setEndOfTrackingReceived(TimePoint, boolean)}</li> * <li>manually set start/end of tracking via {@link #setStartOfTrackingReceived(TimePoint, boolean)} and
* <li>start/end of race in Racelog -/+ {@link #START_TRACKING_THIS_MUCH_BEFORE_RACE_START}/{@link #STOP_TRACKING_THIS_MUCH_AFTER_RACE_FINISH}</li> * {@link #setEndOfTrackingReceived(TimePoint, boolean)}</li>
* <li>start/end of race in Racelog -/+
* {@link #START_TRACKING_THIS_MUCH_BEFORE_RACE_START}/{@link #STOP_TRACKING_THIS_MUCH_AFTER_RACE_FINISH}</li>
* </ol> * </ol>
*/ */
public void updateStartAndEndOfTracking(boolean waitForGPSFixesToLoad); public void updateStartAndEndOfTracking(boolean waitForGPSFixesToLoad);
default void lockForSerializationRead() { default void lockForSerializationRead() {
} }
default void unlockAfterSerializationRead() { default void unlockAfterSerializationRead() {
} }
/** /**
* @return all currently attached {@link RaceLog}s or an empty Iterable if there aren't any * @return all currently attached {@link RaceLog}s or an empty Iterable if there aren't any
*/ */
@@ -1201,7 +1235,6 @@ public interface TrackedRace
*/ */
Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint); Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint);
/** /**
* Computes the competitor's speed projected onto the wind (if wind data is available and the competitor is not * Computes the competitor's speed projected onto the wind (if wind data is available and the competitor is not
* between start and finish (not racing) or not on a {@link LegType#REACHING reaching} leg; if outside a race and no * between start and finish (not racing) or not on a {@link LegType#REACHING reaching} leg; if outside a race and no
@@ -1214,21 +1247,25 @@ public interface TrackedRace
} }
/** /**
* Like {@link #getVelocityMadeGood(Competitor, TimePoint)}, but allowing callers to specify a {@link WindPositionMode} * Like {@link #getVelocityMadeGood(Competitor, TimePoint)}, but allowing callers to specify a
* other than the default {@link WindPositionMode#EXACT}. If {@link WindPositionMode#LEG_MIDDLE} is used and the * {@link WindPositionMode} other than the default {@link WindPositionMode#EXACT}. If
* competitor is not currently sailing on a leg (hasn't started or has already finished), {@code null} is returned. * {@link WindPositionMode#LEG_MIDDLE} is used and the competitor is not currently sailing on a leg (hasn't started
* or has already finished), {@code null} is returned.
*/ */
default SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) { default SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint,
return getVelocityMadeGood(competitor, timePoint, windPositionMode, new LeaderboardDTOCalculationReuseCache(timePoint)); WindPositionMode windPositionMode) {
return getVelocityMadeGood(competitor, timePoint, windPositionMode,
new LeaderboardDTOCalculationReuseCache(timePoint));
} }
/** /**
* Computes the angle between the competitors direction and the wind's "from" direction. The angle's direction is chosen such that * Computes the angle between the competitors direction and the wind's "from" direction. The angle's direction is
* it can be added to the boat's course over ground to arrive at the wind's {@link Wind#getFrom() "from"} direction. Example: wind * chosen such that it can be added to the boat's course over ground to arrive at the wind's {@link Wind#getFrom()
* from the north (0deg), boat's course over ground 90deg (moving east), then the bearing returned is -90deg. * "from"} direction. Example: wind from the north (0deg), boat's course over ground 90deg (moving east), then the
* bearing returned is -90deg.
*/ */
default Bearing getTWA(Competitor competitor, TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) { default Bearing getTWA(Competitor competitor, TimePoint timePoint,
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
Bearing twa = null; Bearing twa = null;
final GPSFixTrack<Competitor, GPSFixMoving> sogTrack = this.getTrack(competitor); final GPSFixTrack<Competitor, GPSFixMoving> sogTrack = this.getTrack(competitor);
if (sogTrack != null) { if (sogTrack != null) {
@@ -1241,20 +1278,21 @@ public interface TrackedRace
} }
return twa; return twa;
} }
/** /**
* Same as {@link #getTWA}, only that additionally a cache is provided that can allow the method to use * Same as {@link #getTWA}, only that additionally a cache is provided that can allow the method to use cached wind
* cached wind and leg type values. * and leg type values.
*/ */
default Bearing getTWA(Competitor competitor, TimePoint at){ default Bearing getTWA(Competitor competitor, TimePoint at) {
return getTWA(competitor, at, new LeaderboardDTOCalculationReuseCache(at)); return getTWA(competitor, at, new LeaderboardDTOCalculationReuseCache(at));
} }
/** /**
* Like {@link #getVelocityMadeGood(Competitor, TimePoint)}, but allowing callers to specify a cache that can * Like {@link #getVelocityMadeGood(Competitor, TimePoint)}, but allowing callers to specify a cache that can
* accelerate requests for wind directions, the leg type and the competitor's current leg's bearing. * accelerate requests for wind directions, the leg type and the competitor's current leg's bearing.
*/ */
default SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) { default SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint,
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
return getVelocityMadeGood(competitor, timePoint, WindPositionMode.EXACT, cache); return getVelocityMadeGood(competitor, timePoint, WindPositionMode.EXACT, cache);
} }
@@ -1274,6 +1312,7 @@ public interface TrackedRace
/** /**
* Gets polar service which is currently set in this tracked race instance. * Gets polar service which is currently set in this tracked race instance.
*
* @see #setPolarDataService(PolarDataService) * @see #setPolarDataService(PolarDataService)
*/ */
PolarDataService getPolarDataService(); PolarDataService getPolarDataService();
@@ -1291,16 +1330,16 @@ public interface TrackedRace
/** /**
* Obtains a quick, rough summary of the wind conditions during this race, based on a few wind samples at the * Obtains a quick, rough summary of the wind conditions during this race, based on a few wind samples at the
* beginning, in the middle and at the end of the race. This is summarized in a min and max wind speed as well * beginning, in the middle and at the end of the race. This is summarized in a min and max wind speed as well as a
* as a single average wind direction. * single average wind direction.
*/ */
WindSummary getWindSummary(); WindSummary getWindSummary();
@Override @Override
default QualifiedObjectIdentifier getIdentifier() { default QualifiedObjectIdentifier getIdentifier() {
return getIdentifier(getRaceIdentifier()); return getIdentifier(getRaceIdentifier());
} }
public static QualifiedObjectIdentifier getIdentifier(RegattaAndRaceIdentifier regattaAndRaceId) { public static QualifiedObjectIdentifier getIdentifier(RegattaAndRaceIdentifier regattaAndRaceId) {
return getSecuredDomainType().getQualifiedObjectIdentifier(regattaAndRaceId.getTypeRelativeObjectIdentifier()); return getSecuredDomainType().getQualifiedObjectIdentifier(regattaAndRaceId.getTypeRelativeObjectIdentifier());
} }
@@ -1318,7 +1357,7 @@ public interface TrackedRace
default HasPermissions getPermissionType() { default HasPermissions getPermissionType() {
return getSecuredDomainType(); return getSecuredDomainType();
} }
public static HasPermissions getSecuredDomainType() { public static HasPermissions getSecuredDomainType() {
return SecuredDomainType.TRACKED_RACE; return SecuredDomainType.TRACKED_RACE;
} }
@@ -1341,13 +1380,14 @@ public interface TrackedRace
* the maximum across their competitors' implied winds, or in case the implied wind was explicitly fixed by a * the maximum across their competitors' implied winds, or in case the implied wind was explicitly fixed by a
* corresponding {@link RaceLogORCImpliedWindSourceEvent}, that fixed implied wind speed. * corresponding {@link RaceLogORCImpliedWindSourceEvent}, that fixed implied wind speed.
*/ */
default Speed getReferenceImpliedWind(TimePoint timePoint, WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) { default Speed getReferenceImpliedWind(TimePoint timePoint,
WindLegTypeAndLegBearingAndORCPerformanceCurveCache cache) {
return getRankingMetric().getReferenceImpliedWind(timePoint, cache); return getRankingMetric().getReferenceImpliedWind(timePoint, cache);
} }
/** /**
* gets a {@link TrackingConnectorInfo}, which contains a String representation of the Tracking-Service used to Track the Race and a web-URL for the Event Event; * gets a {@link TrackingConnectorInfo}, which contains a String representation of the Tracking-Service used to
* may be {@code null}, particularly in test set-ups * Track the Race and a web-URL for the Event Event; may be {@code null}, particularly in test set-ups
*/ */
TrackingConnectorInfo getTrackingConnectorInfo(); TrackingConnectorInfo getTrackingConnectorInfo();
@@ -1381,8 +1421,8 @@ public interface TrackedRace
* hypothetical "line" perpendicular to the first leg's bearing. * hypothetical "line" perpendicular to the first leg's bearing.
*/ */
Pair<Bearing, Position> getStartLineBearingAndStarboardMarkPosition(TimePoint timePoint); Pair<Bearing, Position> getStartLineBearingAndStarboardMarkPosition(TimePoint timePoint);
void setHashValuesForMarkPassingCalculation(Map<typeOfHash, Integer> hashValues); void setHashValuesForMarkPassingCalculation(Map<TypeOfHash, Integer> hashValues);
Map<typeOfHash, Integer> getHashValuesForMarkPassingCalculation (); Map<TypeOfHash, Integer> getHashValuesForMarkPassingCalculation();
} }
@@ -123,6 +123,7 @@ import com.sap.sailing.domain.maneuverdetection.ManeuverDetector;
import com.sap.sailing.domain.maneuverdetection.ShortTimeAfterLastHitCache; import com.sap.sailing.domain.maneuverdetection.ShortTimeAfterLastHitCache;
import com.sap.sailing.domain.maneuverdetection.impl.IncrementalManeuverDetectorImpl; import com.sap.sailing.domain.maneuverdetection.impl.IncrementalManeuverDetectorImpl;
import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator; import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator;
import com.sap.sailing.domain.markpassinghash.impl.TrackedRaceHashForMarkPassingComparatorImpl.TypeOfHash;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver; import com.sap.sailing.domain.racelog.RaceLogAndTrackedRaceResolver;
@@ -158,7 +159,6 @@ import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tracking.WindSummary; import com.sap.sailing.domain.tracking.WindSummary;
import com.sap.sailing.domain.tracking.WindTrack; import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.WindWithConfidence; import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sailing.domain.tracking.impl.TrackedRaceHashForMarkPassingComperatorImpl.typeOfHash;
import com.sap.sailing.domain.windestimation.IncrementalWindEstimation; import com.sap.sailing.domain.windestimation.IncrementalWindEstimation;
import com.sap.sse.common.Bearing; import com.sap.sse.common.Bearing;
import com.sap.sse.common.Distance; import com.sap.sse.common.Distance;
@@ -444,7 +444,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
private transient ConcurrentMap<TimePoint, SortedMap<Competitor, Distance>> distancesFromStarboardSideOfStartLineProjectedOntoLineCache; private transient ConcurrentMap<TimePoint, SortedMap<Competitor, Distance>> distancesFromStarboardSideOfStartLineProjectedOntoLineCache;
private transient ConcurrentMap<TimePoint, TimePoint> distancesFromStarboardSideOfStartLineProjectedOntoLineCacheLastAccessTimes; private transient ConcurrentMap<TimePoint, TimePoint> distancesFromStarboardSideOfStartLineProjectedOntoLineCacheLastAccessTimes;
private Map<typeOfHash, Integer> hashValuesForMarkPassingCalculation; private Map<TypeOfHash, Integer> hashValuesForMarkPassingCalculation;
/** /**
* When a regatta's {@link Regatta#useStartTimeInference()} or {@link Regatta#isControlTrackingFromStartAndFinishTimes()} * When a regatta's {@link Regatta#useStartTimeInference()} or {@link Regatta#isControlTrackingFromStartAndFinishTimes()}
@@ -4307,11 +4307,11 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
return trackingConnectorInfo; return trackingConnectorInfo;
} }
public void setHashValuesForMarkPassingCalculation(Map<typeOfHash, Integer> hashValues) { public void setHashValuesForMarkPassingCalculation(Map<TypeOfHash, Integer> hashValues) {
this.hashValuesForMarkPassingCalculation = hashValues; this.hashValuesForMarkPassingCalculation = hashValues;
} }
public Map<typeOfHash, Integer> getHashValuesForMarkPassingCalculation () { public Map<TypeOfHash, Integer> getHashValuesForMarkPassingCalculation () {
return this.hashValuesForMarkPassingCalculation; return this.hashValuesForMarkPassingCalculation;
} }