factored the RankingMetric constructor parameter into the Regatta interface from where it is used by the DynamicTrackedRace constructions

This commit is contained in:
Axel Uhl committed 2015-04-24 15:54:13 +02:00
1 parent 4ffdf82b20
commit 37eb4ddabf
12 files changed
+64 -26

No files matched your search

@@ -30,6 +30,7 @@ import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.test.AbstractGPSFixStoreTest;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
@@ -78,7 +79,7 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */
null, /* endDate */null, null, null, "a", null));
DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race,
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, store, 0, 0, 0, /*useMarkPassingCalculator*/ false);
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, store, 0, 0, 0, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new);
trackedRace.attachRaceLog(raceLog);
trackedRace.waitForLoadingFromGPSFixStoreToFinishRunning(raceLog);
@@ -123,7 +124,7 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
EmptyRegattaLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, /* startDate */
null, /* endDate */null, null, null, "a", null));
DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race,
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, store, 0, 0, 0, /*useMarkPassingCalculator*/ false);
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, store, 0, 0, 0, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new);
trackedRace.addListener(new AbstractRaceChangeListener() {
@Override
@@ -32,6 +32,7 @@ import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl;
import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
@@ -303,7 +304,7 @@ public class CourseTest {
new RaceDefinitionImpl("Test Race", course, new BoatClassImpl("49er", /* upwind start */true), hasso),
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE,/* delayToLiveInMillis */3000,
/* millisecondsOverWhichToAverageWind */30000,
/* millisecondsOverWhichToAverageSpeed */8000, /*useMarkPassingCalculator*/ false);
/* millisecondsOverWhichToAverageSpeed */8000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new);
assertLegStructure(course, trackedRace);
course.removeWaypoint(0);
assertLegStructure(course, trackedRace);
@@ -334,7 +335,7 @@ public class CourseTest {
hasso), Collections.<Sideline> emptyList(),
EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 3000,
/* millisecondsOverWhichToAverageWind */ 30000,
/* millisecondsOverWhichToAverageSpeed */ 8000, /*useMarkPassingCalculator*/ false);
/* millisecondsOverWhichToAverageSpeed */ 8000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new);
assertLegStructure(course, trackedRace);
final WaypointImpl wp1_5 = new WaypointImpl(new MarkImpl("Test Mark 1.5"));
assertWaypointIndexes(course);
@@ -36,6 +36,7 @@ import com.sap.sailing.domain.common.BoatClassMasterdata;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.TrackedRegatta;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
@@ -80,7 +81,7 @@ public class CourseUpdateDuringNonAtomicSerializationTest implements Serializabl
trackedRace = new DynamicTrackedRaceImpl(trackedRegatta, race, Collections.<Sideline> emptySet(),
EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE,
/* delayToLiveInMillis */10000, /* millisecondsOverWhichToAverageWind */30000, /* millisecondsOverWhichToAverageSpeed */
7000, /*useMarkPassingCalculator*/ false) {
7000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new) {
private static final long serialVersionUID = 9114777576548711763L;
@Override
@@ -47,6 +47,7 @@ import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.RegattaLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegatta;
@@ -137,7 +138,7 @@ public class LeaderboardCourseChangeTest {
TrackedRegatta mockedTrackedRegatta = createMockedTrackedRegatta(regatta);
RaceDefinition mockedRace = createMockedRace(course, boatClass);
TrackedRace spyedTrackedRace = spy(new DynamicTrackedRaceImpl(mockedTrackedRegatta, mockedRace,
new HashSet<Sideline>(), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, 5000, 20000, 20000, /*useMarkPassingCalculator*/ false));
new HashSet<Sideline>(), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, 5000, 20000, 20000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new));
return spyedTrackedRace;
}
@@ -22,6 +22,7 @@ import com.sap.sailing.domain.base.impl.MarkImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
@@ -46,7 +47,7 @@ public class UpdateMarkPassingTest {
/* trackedRegatta */new DynamicTrackedRegattaImpl(new RegattaImpl("test", null, null, null, new HashSet<Series>(), false, null,
"test", null)), race, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE,
/* delayToLiveInMillis */1000, /* millisecondsOverWhichToAverageWind */30000,
/* millisecondsOverWhichToAverageSpeed */30000, /*useMarkPassingCalculator*/ false);
/* millisecondsOverWhichToAverageSpeed */30000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new);
TimePoint now = MillisecondsTimePoint.now();
TimePoint later = now.plus(1000);
trackedRace.updateMarkPassings(competitor, Arrays.asList(new MarkPassing[] { new MarkPassingImpl(now, waypoint, competitor) }));
@@ -47,6 +47,7 @@ import com.sap.sailing.domain.confidence.ConfidenceBasedWindAverager;
import com.sap.sailing.domain.confidence.ConfidenceFactory;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.MarkPassing;
@@ -286,7 +287,7 @@ public class WindTest {
boatClass, Collections.singleton(competitor)), Collections.<Sideline> emptyList(),
EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, /* delayToLiveInMillis */ 1000,
/* millisecondsOverWhichToAverageWind */ 30000,
/* millisecondsOverWhichToAverageSpeed */ 30000, /*useMarkPassingCalculator*/ false);
/* millisecondsOverWhichToAverageSpeed */ 30000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new);
TimePoint start = MillisecondsTimePoint.now();
TimePoint topMarkRounding = start.plus(30000);
TimePoint finish = topMarkRounding.plus(30000);
@@ -30,6 +30,7 @@ import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
@@ -67,7 +68,7 @@ public class AbstractMockedRaceMarkPassingTest {
new ArrayList<String>(), null)), true, new HighPoint(), "ID", new CourseAreaImpl("area", new UUID(5, 5)));
Course course = new CourseImpl("course", waypoints);
RaceDefinition raceDef = new RaceDefinitionImpl("Performance Race", course, boatClass, Arrays.asList(ron, tom, ben));
race = new DynamicTrackedRaceImpl(new DynamicTrackedRegattaImpl(r), raceDef, new ArrayList<Sideline>(), new EmptyWindStore(), EmptyGPSFixStore.INSTANCE, 0, 10000, 10000, /*useMarkPassingCalculator*/ false);
race = new DynamicTrackedRaceImpl(new DynamicTrackedRegattaImpl(r), raceDef, new ArrayList<Sideline>(), new EmptyWindStore(), EmptyGPSFixStore.INSTANCE, 0, 10000, 10000, /*useMarkPassingCalculator*/ false, OneDesignRankingMetric::new);
race.setStartTimeReceived(new MillisecondsTimePoint(10000));
TimePoint t = new MillisecondsTimePoint(30000);
List<Util.Pair<Mark, Position>> pos = Arrays.asList(new Util.Pair<Mark, Position>(m, new DegreePosition(0, 0)),
@@ -1,9 +1,12 @@
package com.sap.sailing.domain.base;
import java.util.function.Function;
import com.sap.sailing.domain.base.configuration.RegattaConfiguration;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaIdentifier;
import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.ranking.RankingMetric;
import com.sap.sailing.domain.regattalike.IsRegattaLike;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.TrackedRegatta;
@@ -139,6 +142,14 @@ public interface Regatta extends Named, WithID, IsRegattaLike {
* discards local to each series rather than spreading them across the entire leaderboard.
*/
boolean definesSeriesDiscardThresholds();
/**
* Defines how this regatta is to be ranked. By default, a regatta would be ranked by one-design rules (first boat across
* the line wins). Other ranking metrics could define handicaps such as time factors ("time-on-time") or time allowances
* per distance sailed ("time-on-distance). This method returns a function that can, given a tracked race, construct the
* ranking metric in the context of that race.
*/
Function<TrackedRace, RankingMetric> getRankingMetricConstructor();
RegattaAndRaceIdentifier getRaceIdentifier(RaceDefinition race);
@@ -8,6 +8,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
@@ -37,6 +38,8 @@ import com.sap.sailing.domain.leaderboard.ScoringScheme;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.ranking.RankingMetric;
import com.sap.sailing.domain.regattalike.BaseRegattaLikeImpl;
import com.sap.sailing.domain.regattalike.IsRegattaLike;
import com.sap.sailing.domain.regattalike.RegattaAsRegattaLikeIdentifier;
@@ -81,6 +84,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
private final Serializable id;
private transient RaceLogStore raceLogStore;
private final IsRegattaLike regattaLikeHelper;
private final Function<TrackedRace, RankingMetric> rankingMetricConstructor;
private CourseArea defaultCourseArea;
private RegattaConfiguration configuration;
@@ -138,6 +142,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
String name, BoatClass boatClass, TimePoint startDate, TimePoint endDate, Iterable<S> series, boolean persistent, ScoringScheme scoringScheme,
Serializable id, CourseArea courseArea, boolean useStartTimeInference) {
super(name);
this.rankingMetricConstructor = OneDesignRankingMetric::new;
this.useStartTimeInference = useStartTimeInference;
this.id = id;
this.raceLogStore = raceLogStore;
@@ -162,6 +167,11 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
this.regattaLikeHelper = new BaseRegattaLikeImpl(new RegattaAsRegattaLikeIdentifier(this), regattaLogStore);
}
@Override
public Function<TrackedRace, RankingMetric> getRankingMetricConstructor() {
return rankingMetricConstructor;
}
private void registerRaceLogsOnRaceColumns(Series series) {
for (RaceColumn raceColumn : series.getRaceColumns()) {
setRaceLogInformationOnRaceColumn(raceColumn);
@@ -52,16 +52,25 @@ public interface RankingMetric extends Serializable {
Comparator<TrackedLegOfCompetitor> getLegRankingComparator(TrackedLeg trackedLeg, TimePoint timePoint, WindLegTypeAndLegBearingCache cache);
/**
* Calculates the time that the <code>trailing</code> competitor needs to "catch up" to reach the <code>leading</code> competitor,
* measured in the <code>trailing</code> competitor's "time system." For one-design ranking this will be the gap based on the
* <code>trailing</code> competitor's windward/along-course distance to the <code>leading</code> competitor divided by the
* <code>trailing</code> competitor's current VMG/VMC. For other ranking systems such as time-on-time, time-on-distance,
* combinations thereof or ORC performance curve scoring special rules will apply which take into account the distance traveled
* by the <code>trailing</code> competitor in her current leg and scaling any allowances according to that distance in order
* to compare to the leader. If the leader is already in another leg, the best corrected leg completion time is used as the
* reference for comparison. This may not be the overall leader of the race at that time, but anything else would require an
* uncertain prediction of the <code>trailing</code> competitor's performance in the next leg which is not considered to be
* Calculates the time that the <code>trailing</code> competitor needs to "catch up" to reach the
* <code>leading</code> competitor, measured in the <code>trailing</code> competitor's "time system." For one-design
* ranking this will be the gap based on the <code>trailing</code> competitor's windward/along-course distance to
* the <code>leading</code> competitor divided by the <code>trailing</code> competitor's current VMG/VMC. For other
* ranking systems such as time-on-time, time-on-distance, combinations thereof or ORC performance curve scoring
* special rules will apply which take into account the distance traveled by the <code>trailing</code> competitor in
* her current leg and scaling any allowances according to that distance in order to compare to the leader. If the
* leader is already in another leg, the best corrected leg completion time is used as the reference for comparison.
* This may not be the overall leader of the race at that time, but anything else would require an uncertain
* prediction of the <code>trailing</code> competitor's performance in the next leg which is not considered to be
* useful here.
* <p>
*
* Note that this way of calculating the time to improve takes into account the competitor's <em>average</em>
* performance of the <em>entire race so far</em> (as opposed to the <em>current</em> performance</em>) by looking
* at how much earlier the competitor would have had to reach the current ranking value (windward position for
* one-design, for example) in order to have caught up to the leg's leader. This makes the result robust against
* short-term speed changes but doesn't respond well to the different types of legs in which the competitors
* may sail.
*/
default Duration getTimeToImprove(Competitor trailing, Competitor leading, TimePoint timePoint) {
return getTimeToImprove(trailing, leading, timePoint, new NoCachingWindLegTypeAndLegBearingCache());
@@ -35,7 +35,6 @@ import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator;
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.ranking.RankingMetric;
import com.sap.sailing.domain.tracking.CourseDesignChangedListener;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
@@ -137,10 +136,11 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
*/
public DynamicTrackedRaceImpl(TrackedRegatta trackedRegatta, RaceDefinition race, Iterable<Sideline> sidelines,
WindStore windStore, GPSFixStore gpsFixStore, long delayToLiveInMillis,
long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, boolean useInternalMarkPassingAlgorithm) {
long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed,
boolean useInternalMarkPassingAlgorithm, Function<TrackedRace, RankingMetric> rankingMetricConstructor) {
this(trackedRegatta, race, sidelines, windStore, gpsFixStore, delayToLiveInMillis,
millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed,
millisecondsOverWhichToAverageWind / 2, useInternalMarkPassingAlgorithm, OneDesignRankingMetric::new);
millisecondsOverWhichToAverageWind / 2, useInternalMarkPassingAlgorithm, rankingMetricConstructor);
}
@Override
@@ -169,13 +169,15 @@ public class TrackedRegattaImpl implements TrackedRegatta {
}
@Override
public DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, Iterable<Sideline> sidelines, WindStore windStore, GPSFixStore gpsFixStore, long delayToLiveInMillis,
public DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, Iterable<Sideline> sidelines,
WindStore windStore, GPSFixStore gpsFixStore, long delayToLiveInMillis,
long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed,
DynamicRaceDefinitionSet raceDefinitionSetToUpdate, boolean useInternalMarkPassingAlgorithm) {
logger.log(Level.INFO, "Creating DynamicTrackedRaceImpl for RaceDefinition " + raceDefinition.getName());
DynamicTrackedRaceImpl result = new DynamicTrackedRaceImpl(this, raceDefinition, sidelines,
windStore, gpsFixStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed,
/*useMarkPassingCalculator*/ useInternalMarkPassingAlgorithm);
DynamicTrackedRaceImpl result = new DynamicTrackedRaceImpl(this, raceDefinition, sidelines, windStore,
gpsFixStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind,
millisecondsOverWhichToAverageSpeed,
/* useMarkPassingCalculator */useInternalMarkPassingAlgorithm, getRegatta().getRankingMetricConstructor());
// adding the raceDefinition to the raceDefinitionSetToUpdate BEFORE calling addTrackedRace helps those who
// are called back by RaceListener.raceAdded(TrackedRace) and who then expect the update to have happened
if (raceDefinitionSetToUpdate != null) {
@@ -184,5 +186,4 @@ public class TrackedRegattaImpl implements TrackedRegatta {
addTrackedRace(result);
return result;
}
}