made RankingMetric serializable, added serialVersionUIDs. Implemented getCorrectedTime for TimeOnTimeAndDistanceRankingMetric

This commit is contained in:
Axel Uhl committed 2015-04-21 14:19:55 +02:00
1 parent d2e318899c
commit 636bc9e925
8 files changed
+295 -39

No files matched your search

@@ -0,0 +1,69 @@
package com.sap.sailing.domain.ranking;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
public abstract class AbstractRankingMetric implements RankingMetric {
private static final long serialVersionUID = -3671039530564696392L;
private final TrackedRace trackedRace;
protected AbstractRankingMetric(TrackedRace trackedRace) {
super();
this.trackedRace = trackedRace;
}
@Override
public TrackedRace getTrackedRace() {
return trackedRace;
}
protected Duration getActualTimeSinceStartOfRace(Competitor competitor, TimePoint timePoint) {
final Duration result;
final TimePoint startOfRace = getTrackedRace().getStartOfRace();
if (startOfRace == null) {
result = null;
} else {
final Waypoint finish = getTrackedRace().getRace().getCourse().getLastWaypoint();
if (finish == null) {
result = null;
} else {
final MarkPassing finishingMarkPassing = getTrackedRace().getMarkPassing(competitor, finish);
if (finishingMarkPassing != null) {
result = startOfRace.until(finishingMarkPassing.getTimePoint());
} else {
result = startOfRace.until(timePoint);
}
}
}
return result;
}
protected Distance getWindwardDistanceTraveled(Competitor competitor, TimePoint timePoint) {
TrackedLegOfCompetitor currentLeg = getTrackedRace().getCurrentLeg(competitor, timePoint);
final Distance result;
if (currentLeg == null) {
result = null;
} else {
Distance d = Distance.NULL;
for (TrackedLeg trackedLeg : getTrackedRace().getTrackedLegs()) {
if (trackedLeg.getLeg() == currentLeg.getLeg()) {
// partial distance sailed:
d = d.add(trackedLeg.getWindwardDistanceFromLegStart(getTrackedRace().getTrack(competitor)
.getEstimatedPosition(timePoint, /* extrapolate */true)));
break;
} else {
d = d.add(trackedLeg.getWindwardDistance());
}
}
result = d;
}
return result;
}
}
@@ -0,0 +1,46 @@
package com.sap.sailing.domain.ranking;
import java.util.Comparator;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
public class ORCPerformanceCurveRankingMetric extends AbstractRankingMetric {
private static final long serialVersionUID = -7814822523533929816L;
public ORCPerformanceCurveRankingMetric(TrackedRace trackedRace) {
super(trackedRace);
}
@Override
public Comparator<Competitor> getRaceRankingComparator(TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
// TODO Auto-generated method stub
return null;
}
@Override
public Comparator<TrackedLegOfCompetitor> getLegRankingComparator(TrackedLeg trackedLeg, TimePoint timePoint,
WindLegTypeAndLegBearingCache cache) {
// TODO Auto-generated method stub
return null;
}
@Override
public Duration getTimeToImprove(Competitor trailing, Competitor leading, TimePoint timePoint,
WindLegTypeAndLegBearingCache cache) {
// TODO Auto-generated method stub
return null;
}
@Override
public Duration getCorrectedTime(Competitor competitor, TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
// TODO Auto-generated method stub
return null;
}
}
@@ -3,8 +3,6 @@ package com.sap.sailing.domain.ranking;
import java.util.Comparator;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
@@ -15,10 +13,16 @@ import com.sap.sailing.domain.tracking.impl.WindwardToGoComparator;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
public class OneDesignRankingMetric implements RankingMetric {
public class OneDesignRankingMetric extends AbstractRankingMetric {
private static final long serialVersionUID = -8141113385324184349L;
public OneDesignRankingMetric(TrackedRace trackedRace) {
super(trackedRace);
}
@Override
public Comparator<Competitor> getRaceRankingComparator(TrackedRace trackedRace, TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
return new RaceRankComparator(trackedRace, timePoint, cache);
public Comparator<Competitor> getRaceRankingComparator(TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
return new RaceRankComparator(getTrackedRace(), timePoint, cache);
}
@Override
@@ -28,10 +32,10 @@ public class OneDesignRankingMetric implements RankingMetric {
}
@Override
public Duration getTimeToImprove(TrackedRace trackedRace, Competitor trailing, Competitor leading,
TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
public Duration getTimeToImprove(Competitor trailing, Competitor leading, TimePoint timePoint,
WindLegTypeAndLegBearingCache cache) {
final Duration result;
final TrackedLegOfCompetitor currentLeg = trackedRace.getCurrentLeg(trailing, timePoint);
final TrackedLegOfCompetitor currentLeg = getTrackedRace().getCurrentLeg(trailing, timePoint);
if (currentLeg != null) {
result = currentLeg.getGapToLeader(timePoint, leading, WindPositionMode.LEG_MIDDLE, cache);
} else {
@@ -41,25 +45,7 @@ public class OneDesignRankingMetric implements RankingMetric {
}
@Override
public Duration getCorrectedTime(TrackedRace trackedRace, Competitor competitor, TimePoint timePoint,
WindLegTypeAndLegBearingCache cache) {
final Duration result;
final TimePoint startOfRace = trackedRace.getStartOfRace();
if (startOfRace == null) {
result = null;
} else {
final Waypoint finish = trackedRace.getRace().getCourse().getLastWaypoint();
if (finish == null) {
result = null;
} else {
final MarkPassing finishingMarkPassing = trackedRace.getMarkPassing(competitor, finish);
if (finishingMarkPassing != null) {
result = startOfRace.until(finishingMarkPassing.getTimePoint());
} else {
result = startOfRace.until(timePoint);
}
}
}
return result;
public Duration getCorrectedTime(Competitor competitor, TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
return getActualTimeSinceStartOfRace(competitor, timePoint);
}
}
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.ranking;
import java.io.Serializable;
import java.util.Comparator;
import com.sap.sailing.domain.base.Competitor;
@@ -7,7 +8,6 @@ import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
import com.sap.sailing.domain.tracking.impl.NoCachingWindLegTypeAndLegBearingCache;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
@@ -32,12 +32,13 @@ import com.sap.sse.common.TimePoint;
*
* @param <T>
*/
public interface RankingMetric {
default Comparator<Competitor> getRankingComparator(TrackedRace trackedRace, TimePoint timePoint) {
return getRaceRankingComparator(trackedRace, null, new NoCachingWindLegTypeAndLegBearingCache());
}
public interface RankingMetric extends Serializable {
/**
* @return the tracked race to which this ranking metric is specific
*/
TrackedRace getTrackedRace();
Comparator<Competitor> getRaceRankingComparator(TrackedRace trackedRace, TimePoint timePoint, WindLegTypeAndLegBearingCache cache);
Comparator<Competitor> getRaceRankingComparator(TimePoint timePoint, WindLegTypeAndLegBearingCache cache);
Comparator<TrackedLegOfCompetitor> getLegRankingComparator(TrackedLeg trackedLeg, TimePoint timePoint, WindLegTypeAndLegBearingCache cache);
@@ -53,8 +54,7 @@ public interface RankingMetric {
* uncertain prediction of the <code>trailing</code> competitor's performance in the next leg which is not considered to be
* useful here.
*/
Duration getTimeToImprove(TrackedRace trackedRace, Competitor trailing, Competitor leading, TimePoint timePoint,
WindLegTypeAndLegBearingCache cache);
Duration getTimeToImprove(Competitor trailing, Competitor leading, TimePoint timePoint, WindLegTypeAndLegBearingCache cache);
/**
* How much time did the <code>competitor</code> spend in the {@link TrackedRace#getRace() race} described by
@@ -72,5 +72,5 @@ public interface RankingMetric {
* This method can also be used to calculate the corrected times at the end of each leg, simply by passing the time
* when the competitor finished the respective leg as <code>timePoint</code>.
*/
Duration getCorrectedTime(TrackedRace trackedRace, Competitor competitor, TimePoint timePoint, WindLegTypeAndLegBearingCache cache);
Duration getCorrectedTime(Competitor competitor, TimePoint timePoint, WindLegTypeAndLegBearingCache cache);
}
@@ -0,0 +1,71 @@
package com.sap.sailing.domain.ranking;
import java.util.Comparator;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
public class TimeOnTimeAndDistanceRankingMetric extends AbstractRankingMetric {
private static final long serialVersionUID = -2321904208518686420L;
public TimeOnTimeAndDistanceRankingMetric(TrackedRace trackedRace) {
super(trackedRace);
}
@Override
public Comparator<Competitor> getRaceRankingComparator(TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
// TODO Auto-generated method stub
return null;
}
@Override
public Comparator<TrackedLegOfCompetitor> getLegRankingComparator(TrackedLeg trackedLeg, TimePoint timePoint,
WindLegTypeAndLegBearingCache cache) {
// TODO Auto-generated method stub
return null;
}
@Override
public Duration getTimeToImprove(Competitor trailing, Competitor leading, TimePoint timePoint,
WindLegTypeAndLegBearingCache cache) {
// TODO Auto-generated method stub
return null;
}
@Override
public Duration getCorrectedTime(Competitor competitor, TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
final Duration timeActuallySpent = getActualTimeSinceStartOfRace(competitor, timePoint);
final Duration result;
if (timeActuallySpent == null) {
result = null;
} else {
final Distance windwardDistanceSailed = getWindwardDistanceTraveled(competitor, timePoint);
final Duration correctedTimeByTimeOnTime = timeActuallySpent.times(Double.valueOf(getTimeOnTimeFactor(competitor, timePoint)).longValue());
if (windwardDistanceSailed == null) {
result = correctedTimeByTimeOnTime;
} else {
final Duration timeOnDistanceAllowance = Duration.ONE_SECOND.times(Double.valueOf(
windwardDistanceSailed.getNauticalMiles() * getTimeOnDistanceFactorInSecondsPerNauticalMile(competitor, timePoint)).
longValue());
result = correctedTimeByTimeOnTime.minus(timeOnDistanceAllowance);
}
}
return result;
}
private double getTimeOnTimeFactor(Competitor competitor, TimePoint timePoint) {
return 1.0; // TODO see bug 2765; obtain this from the Competitor
}
private double getTimeOnDistanceFactorInSecondsPerNauticalMile(Competitor competitor, TimePoint timePoint) {
return 0.0; // TODO see bug 2765; obtain this from the Competitor
}
}
@@ -101,10 +101,34 @@ public interface TrackedLeg extends Serializable {
*/
Distance getWindwardDistance(Position pos1, Position pos2, TimePoint at, WindPositionMode windPositionMode);
/**
* Computes the windward distance for upwind/downwind legs or the great circle distance between this leg's waypoints
* that competitors need to travel in this leg. Due to the dynamics of the wind direction and the mark positions,
* the result is time dependent. The wind direction is determined at the leg's middle at the time point <code>at</code>.
*/
Distance getWindwardDistance(TimePoint at);
/**
* Computes the windward distance for upwind/downwind legs or the great circle distance between this leg's waypoints
* that competitors need to travel in this leg. Due to the dynamics of the wind direction and the mark positions,
* the result is time dependent. As time point, the middle of the interval defined by the first mark passing starting the
* leg and the last mark passing finishing the leg is used, where both time points default to "now."
*
* @see #getWindwardDistance(TimePoint)
*/
Distance getWindwardDistance();
/**
* The middle (traveling half the length) of the course middle line, connecting the center of gravity of the leg's start
* waypoint's position at time point <code>at</code> and the position of the leg's end waypoint at time point <code>at</code>.
*/
Position getMiddleOfLeg(TimePoint at);
/**
* Computes the windward distance (for upwind/downwind legs) or the along-course distance (for reaching legs) at a reference time point
* between the leg's start waypoint position and <code>pos</code>. The reference time point is chosen as the middle between the first
* leg entry and the last leg exit, both defaulting to "now" if no such time point exists.
*/
Distance getWindwardDistanceFromLegStart(Position pos);
}
@@ -6,6 +6,7 @@ import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@@ -24,6 +25,7 @@ import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
@@ -32,6 +34,7 @@ import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
import com.sap.sailing.domain.tracking.WindPositionMode;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class TrackedLegImpl implements TrackedLeg {
private static final long serialVersionUID = -1944668527284130545L;
@@ -336,11 +339,68 @@ public class TrackedLegImpl implements TrackedLeg {
return result;
}
@Override
public Distance getWindwardDistanceFromLegStart(Position pos) {
final TimePoint referenceTimePoint = getReferenceTimePoint();
return getWindwardDistance(getTrackedRace().getApproximatePosition(getLeg().getFrom(), referenceTimePoint),
pos, referenceTimePoint, WindPositionMode.LEG_MIDDLE);
}
@Override
public Distance getWindwardDistance(Position pos1, Position pos2, TimePoint at, WindPositionMode windPositionMode) {
return getWindwardDistance(pos1, pos2, at, windPositionMode, new NoCachingWindLegTypeAndLegBearingCache());
}
@Override
public Distance getWindwardDistance() {
final TimePoint middle = getReferenceTimePoint();
return getWindwardDistance(middle);
}
/**
* Computes a reference time point for this leg that is the same for all competitors and that is the middle between
* the time points of first leg entry and last leg exit. If no competitor has entered the leg, "now" is used as a
* default. If competitors have entered the leg but none has finished it yet, the middle between first entry and
* "now" is used.
*/
private TimePoint getReferenceTimePoint() {
Iterable<MarkPassing> legStartMarkPassings = getTrackedRace().getMarkPassingsInOrder(getLeg().getFrom());
Iterable<MarkPassing> legFinishMarkPassings = getTrackedRace().getMarkPassingsInOrder(getLeg().getTo());
getTrackedRace().lockForRead(legStartMarkPassings);
final TimePoint firstLegStartMarkPassingTimePoint;
final TimePoint lastLegFinishMarkPassingTimePoint;
try {
Iterator<MarkPassing> i = legStartMarkPassings.iterator();
if (i.hasNext()) {
firstLegStartMarkPassingTimePoint = i.next().getTimePoint();
} else {
firstLegStartMarkPassingTimePoint = MillisecondsTimePoint.now();
}
} finally {
getTrackedRace().unlockAfterRead(legStartMarkPassings);
}
getTrackedRace().lockForRead(legFinishMarkPassings);
try {
Iterator<MarkPassing> i = legFinishMarkPassings.iterator();
if (i.hasNext()) {
lastLegFinishMarkPassingTimePoint = i.next().getTimePoint();
} else {
lastLegFinishMarkPassingTimePoint = MillisecondsTimePoint.now();
}
} finally {
getTrackedRace().unlockAfterRead(legFinishMarkPassings);
}
final TimePoint middle = firstLegStartMarkPassingTimePoint.plus(firstLegStartMarkPassingTimePoint.until(lastLegFinishMarkPassingTimePoint).divide(2));
return middle;
}
@Override
public Distance getWindwardDistance(final TimePoint middle) {
final Position fromPos = getTrackedRace().getApproximatePosition(getLeg().getFrom(), middle);
final Position toPos = getTrackedRace().getApproximatePosition(getLeg().getTo(), middle);
return getWindwardDistance(fromPos, toPos, middle, WindPositionMode.LEG_MIDDLE);
}
Distance getWindwardDistance(final Position pos1, final Position pos2, TimePoint at, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) {
Distance result;
try {
@@ -335,7 +335,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
long millisecondsOverWhichToAverageSpeed, long delayForWindEstimationCacheInvalidation,
boolean useInternalMarkPassingAlgorithm) {
super(race, trackedRegatta, windStore, millisecondsOverWhichToAverageWind);
rankingMetric = new OneDesignRankingMetric(); // for now we can only do one-design classes; see bug 1018
rankingMetric = new OneDesignRankingMetric(this); // for now we can only do one-design classes; see bug 1018
raceStates = new WeakHashMap<>();
shortTimeWindCache = new ShortTimeWindCache(this, millisecondsOverWhichToAverageWind / 2);
locksForMarkPassings = new IdentityHashMap<>();
@@ -1173,7 +1173,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
// RaceRankComparator requires course read lock
getRace().getCourse().lockForRead();
try {
Comparator<Competitor> comparator = getRankingMetric().getRaceRankingComparator(this, timePoint, cache);
Comparator<Competitor> comparator = getRankingMetric().getRaceRankingComparator(timePoint, cache);
rankedCompetitors = new ArrayList<Competitor>();
for (Competitor c : getRace().getCompetitors()) {
rankedCompetitors.add(c);