Merge remote-tracking branch 'origin/master' into buoyzone_drawing

Conflicts:
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
This commit is contained in:
fmittag committed 2013-02-18 17:50:32 +01:00
commit aa7392cc2d
46 files changed
+3260 -581

No files matched your search

@@ -13,6 +13,7 @@ Export-Package: com.sap.sailing.domain.base,
com.sap.sailing.domain.leaderboard,
com.sap.sailing.domain.leaderboard.impl,
com.sap.sailing.domain.leaderboard.meta,
com.sap.sailing.domain.polarsheets,
com.sap.sailing.domain.tracking,
com.sap.sailing.domain.tracking.impl,
com.sap.sailing.util,
@@ -29,12 +29,6 @@ public class RegattaLeaderboardImpl extends AbstractLeaderboardImpl implements R
private static final long serialVersionUID = 2370461218294770084L;
private final Regatta regatta;
/**
* If this member is <code>null</code>, {@link #getName()} will use the regatta name as the default name for this
* leaderboard. Otherwise, the {@link #displayName} is used.
*/
private String displayName;
public RegattaLeaderboardImpl(Regatta regatta, SettableScoreCorrection scoreCorrection,
ThresholdBasedResultDiscardingRule resultDiscardingRule) {
super(scoreCorrection, resultDiscardingRule);
@@ -47,7 +41,7 @@ public class RegattaLeaderboardImpl extends AbstractLeaderboardImpl implements R
*/
@Override
public void setName(String newName) {
displayName = newName;
setDisplayName(newName);
}
@Override
@@ -58,8 +52,8 @@ public class RegattaLeaderboardImpl extends AbstractLeaderboardImpl implements R
@Override
public String getName() {
String result;
if (displayName != null) {
result = displayName;
if (getDisplayName() != null) {
result = getDisplayName();
} else {
result = getRegatta().getName();
}
@@ -0,0 +1,11 @@
package com.sap.sailing.domain.polarsheets;
import com.sap.sailing.domain.common.Speed;
public interface BoatAndWindSpeed {
public Speed getBoatSpeed();
public Speed getWindSpeed();
}
@@ -0,0 +1,28 @@
package com.sap.sailing.domain.polarsheets;
import com.sap.sailing.domain.common.Speed;
public class BoatAndWindSpeedImpl implements BoatAndWindSpeed{
private Speed boatSpeed;
private Speed windSpeed;
public BoatAndWindSpeedImpl(Speed boatSpeed, Speed windSpeed) {
this.boatSpeed = boatSpeed;
this.windSpeed = windSpeed;
}
@Override
public Speed getBoatSpeed() {
return boatSpeed;
}
@Override
public Speed getWindSpeed() {
return windSpeed;
}
}
@@ -0,0 +1,109 @@
package com.sap.sailing.domain.polarsheets;
import java.util.Iterator;
import java.util.NavigableSet;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.SpeedWithBearing;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.Wind;
/**
* Iterates through the fixes of one competitor in one tracked race and fills the {@link PolarSheetGenerationWorker}
* with datapoints that are found for the speed of the boat and its angle to the wind
*
* @author D054528 Frederik Petersen
*
*/
public class PerRaceAndCompetitorPolarSheetGenerationWorker implements Runnable{
private final TrackedRace race;
private final PolarSheetGenerationWorker polarSheetGenerationWorker;
private TimePoint startTime;
private TimePoint endTime;
private final Competitor competitor;
private boolean done = false;
public PerRaceAndCompetitorPolarSheetGenerationWorker(TrackedRace race,
PolarSheetGenerationWorker polarSheetGenerationWorker, TimePoint startTime, TimePoint endTime,
Competitor competitor) {
super();
this.race = race;
this.polarSheetGenerationWorker = polarSheetGenerationWorker;
this.startTime = startTime;
this.endTime = endTime;
this.competitor = competitor;
optimizeStartTime();
optimizeEndTime();
}
private void optimizeEndTime() {
NavigableSet<MarkPassing> markPassings = race.getMarkPassings(competitor);
if (markPassings == null || markPassings.size() < 1) {
return;
}
MarkPassing passedFinish = markPassings.last();
TimePoint passedFinishTimePoint = passedFinish.getTimePoint();
if (passedFinishTimePoint.before(endTime)) {
endTime = passedFinishTimePoint;
}
}
private void optimizeStartTime() {
NavigableSet<MarkPassing> markPassings = race.getMarkPassings(competitor);
if (markPassings == null || markPassings.size() < 1) {
return;
}
MarkPassing passedStart = markPassings.first();
TimePoint passedStartTimePoint = passedStart.getTimePoint();
if (passedStartTimePoint.after(startTime)) {
startTime = passedStartTimePoint;
}
}
@Override
public void run() {
GPSFixTrack<Competitor, GPSFixMoving> track = race.getTrack(competitor);
track.lockForRead();
Iterator<GPSFixMoving> fixesIterator = track.getFixesIterator(startTime, true);
while (fixesIterator.hasNext()) {
GPSFixMoving fix = fixesIterator.next();
if (fix.getTimePoint().after(endTime)) {
break;
}
if (track.hasDirectionChange(fix.getTimePoint(), race.getRace().getBoatClass()
.getManeuverDegreeAngleThreshold())) {
continue;
}
SpeedWithBearing speedWithBearing = fix.getSpeed();
Bearing bearing = speedWithBearing.getBearing();
Position position = fix.getPosition();
Wind wind = race.getWind(position, fix.getTimePoint());
Bearing windBearing = wind.getFrom();
double angleToWind = bearing.getDifferenceTo(windBearing).getDegrees();
polarSheetGenerationWorker.addPolarData(Math.round(angleToWind), speedWithBearing, wind);
}
track.unlockAfterRead();
done = true;
}
public boolean isDone() {
return done;
}
}
@@ -0,0 +1,174 @@
package com.sap.sailing.domain.polarsheets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Executor;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.common.PolarSheetsData;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.PolarSheetsDataImpl;
import com.sap.sailing.domain.tracking.TrackedRace;
/**
* Allows extracting data about average speed for each angle creating the foundation of polar-sheet generation. Tasks
* are assigned to the executor per race & per competitor and the results are filled on the go to allow progress
* indication in the polar-sheet itself.
*
* @author D054528 Frederik Petersen
*
*/
public class PolarSheetGenerationWorker {
private final Set<PerRaceAndCompetitorPolarSheetGenerationWorker> workers;
private final List<List<BoatAndWindSpeed>> polarData;
private final Executor executor;
/**
* Will prepare the {@link PerRaceAndCompetitorPolarSheetGenerationWorker}s per race & per competitor. This includes
* determining start and end time.
*
* @param trackedRaces
* from which the data is to be collected
* @param executor
* executes the tasks upon {@link #startPolarSheetGeneration()}
*/
public PolarSheetGenerationWorker(Set<TrackedRace> trackedRaces, Executor executor) {
polarData = initializePolarDataContainer();
this.executor = executor;
workers = new HashSet<PerRaceAndCompetitorPolarSheetGenerationWorker>();
for (TrackedRace race : trackedRaces) {
TimePoint startTime = race.getStartOfRace();
TimePoint endTime = race.getEndOfRace();
if (endTime == null) {
// TODO Figure out if there is an alternative:
endTime = race.getTimePointOfNewestEvent();
}
RaceDefinition raceDefinition = race.getRace();
Iterable<Competitor> competitors = raceDefinition.getCompetitors();
for (Competitor competitor : competitors) {
PerRaceAndCompetitorPolarSheetGenerationWorker task = new PerRaceAndCompetitorPolarSheetGenerationWorker(race, this, startTime, endTime, competitor);
workers.add(task);
}
}
}
private List<List<BoatAndWindSpeed>> initializePolarDataContainer() {
List<List<BoatAndWindSpeed>> container = new ArrayList<List<BoatAndWindSpeed>>();
for (int i = 0; i < 360; i++) {
container.add(new ArrayList<BoatAndWindSpeed>());
}
return container;
}
/**
* Starts the {@link PerRaceAndCompetitorPolarSheetGenerationWorker}s
*/
public void startPolarSheetGeneration() {
for (Runnable task : workers) {
executor.execute(task);
}
}
/**
* To be called from {@link PerRaceAndCompetitorPolarSheetGenerationWorker} for adding a datapoint to the list of
* results
*
* @param roundedAngle
* boat's angle to the wind
* @param boatSpeed
* boat's speed in knots
* @param windSpeed
* wind's speed in knots
*/
protected void addPolarData(long roundedAngle, Speed boatSpeed, Speed windSpeed) {
int angle = (int) roundedAngle;
if (angle < 0) {
angle = (360 + angle);
}
BoatAndWindSpeed speeds = new BoatAndWindSpeedImpl(boatSpeed, windSpeed);
polarData.get(angle).add(speeds);
}
/**
*
* @return results already averaged per angle. Also datacount overall and per angle. And a completion flag to show
* if the complete polar sheet has been generated or if the current results are only an intermediate
* product.
*/
public PolarSheetsData getPolarData() {
Number[][] averagedPolarDataByWindSpeed = new Number[13][360];
Integer[] dataCountPerAngle = new Integer[360];
int dataCount = 0;
Map<Integer, Integer[]> dataCountPerAngleForWindspeed = new HashMap<Integer, Integer[]>();
for (int i = 0; i < 13; i++) {
dataCountPerAngleForWindspeed.put(i, new Integer[360]);
}
for (int i = 0; i < 360; i++) {
// Avoid Concurrent modification, lock would slow things down
BoatAndWindSpeed[] values = polarData.get(i).toArray(new BoatAndWindSpeed[polarData.get(i).size()]);
dataCount = dataCount + values.length;
dataCountPerAngle[i] = values.length;
double[] sumsPerWindSpeed = new double[13];
int[] dataCountPerWindSpeed = new int[13];
for (BoatAndWindSpeed singleDataPoint : values) {
if (singleDataPoint != null) {
int windSpeed = (int) singleDataPoint.getWindSpeed().getBeaufort();
//Somehow beaufort sometimes gets bigger than twelve. TODO: Investigation
if (windSpeed > 12) {
windSpeed = 12;
}
//TODO enable different kinds of metrics for boats speed
sumsPerWindSpeed[windSpeed] = sumsPerWindSpeed[windSpeed] + singleDataPoint.getBoatSpeed().getKnots();
dataCountPerWindSpeed[windSpeed]++;
}
}
for (int j = 0; j < 13; j++) {
Double average = sumsPerWindSpeed[j] / dataCountPerWindSpeed[j];
if (average.isNaN()) {
average = new Double(0);
}
averagedPolarDataByWindSpeed[j][i] = average;
dataCountPerAngleForWindspeed.get(j)[i] = dataCountPerWindSpeed[j];
}
}
boolean complete = true;
for (PerRaceAndCompetitorPolarSheetGenerationWorker task : workers) {
if (!task.isDone()) {
complete = false;
break;
}
}
PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, complete, dataCount,
dataCountPerAngleForWindspeed);
return data;
}
/**
* Can be used to present more detailed data.
*
* @return The complete set of datapoints that have been added. Can be quite big, depending on the amount of races
* and competitors.
*/
public List<List<BoatAndWindSpeed>> getCompleteData() {
return polarData;
}
}
@@ -5,10 +5,10 @@ import java.util.List;
import java.util.NavigableSet;
import java.util.SortedSet;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.Leg;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.DouglasPeucker;
@@ -41,41 +41,42 @@ import com.sap.sailing.domain.common.impl.Util.Pair;
*/
public interface TrackedRace extends Serializable {
final long MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS = 30000;
final long DEFAULT_LIVE_DELAY_IN_MILLISECONDS = 5000;
RaceDefinition getRace();
RegattaAndRaceIdentifier getRaceIdentifier();
/**
* 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). When there are no {@link MarkPassing}s for the first mark, <code>null</code>
* is returned. If there are mark passings for the first mark and the start time is less than
* {@link #MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS} before the first mark passing for the
* 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). When there are no {@link MarkPassing}s for the first
* mark, <code>null</code> is returned. If there are mark passings for the first mark and the start time is less
* than {@link #MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS} 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.<p>
* {@link #MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS} is returned as the race start time.
* <p>
*
* If no start time can be determined this way, <code>null</code> is returned.
*/
TimePoint getStartOfRace();
/**
* Determine the race end time is tricky. Boats may sink, stop, not finish, although they started the race. We therefore
* cannot wait for all boats to reach the finish line.
* The following rules are used to calculate the endOfRace:
* Determine the race end time is tricky. Boats may sink, stop, not finish, although they started the race. We
* therefore cannot wait for all boats to reach the finish line. The following rules are used to calculate the
* endOfRace:
* <ol>
* <li>Returns <code>null</code> if no boat passed the finish line</li>
* <li>Returns time of the last mark passing recorded for the finish line</li>
* <li>TODO: Returns the time of the first passing of the finish line + the target window (defined in the competition rules)
* if a target window has been defined for the race</li>
* </ol>
* <li>Returns <code>null</code> if no boat passed the finish line</li>
* <li>Returns time of the last mark passing recorded for the finish line</li>
* <li>TODO: Returns the time of the first passing of the finish line + the target window (defined in the
* competition rules) if a target window has been defined for the race</li>
* </ol>
*/
TimePoint getEndOfRace();
/**
* Returns a list of the first and last mark passing times of all course waypoints. Callers wanting to iterate over the
* result must <code>synchronize</code> on the result.
* Returns a list of the first and last mark passing times of all course waypoints. Callers wanting to iterate over
* the result must <code>synchronize</code> on the result.
*/
Iterable<Pair<Waypoint, Pair<TimePoint, TimePoint>>> getMarkPassingsTimes();
@@ -83,89 +84,84 @@ public interface TrackedRace extends Serializable {
* Shorthand for <code>{@link #getStart()}.{@link TimePoint#compareTo(TimePoint) compareTo(at)} &lt;= 0</code>
*/
boolean hasStarted(TimePoint at);
/**
* Clients can safely iterate over the iterable returned because it's a non-live copy of the tracked legs of this
* tracked race. This implies that should an update to the underlying list of waypoints in this race's {@link Course}
* take place after this method has returned, then this won't be reflected in the result returned. Callers should
* obtain the {@link Course#lockForRead() course's read lock} while using the result of this call if they want to
* ensure that no course update is applied concurrently.
* tracked race. This implies that should an update to the underlying list of waypoints in this race's
* {@link Course} take place after this method has returned, then this won't be reflected in the result returned.
* Callers should obtain the {@link Course#lockForRead() course's read lock} while using the result of this call if
* they want to ensure that no course update is applied concurrently.
*/
Iterable<TrackedLeg> getTrackedLegs();
TrackedLeg getTrackedLeg(Leg leg);
/**
* Tracking information about the leg <code>competitor</code> is on at <code>timePoint</code>, or
* <code>null</code> if the competitor hasn't started any leg yet at <code>timePoint</code> or has
* already finished the race.
* Tracking information about the leg <code>competitor</code> is on at <code>timePoint</code>, or <code>null</code>
* if the competitor hasn't started any leg yet at <code>timePoint</code> or has already finished the race.
*/
TrackedLegOfCompetitor getCurrentLeg(Competitor competitor, TimePoint timePoint);
/**
* Tells which leg the leader at <code>timePoint</code> is on
*/
TrackedLeg getCurrentLeg(TimePoint timePoint);
/**
* Precondition: waypoint must still be part of {@link #getRace()}.{@link RaceDefinition#getCourse() getCourse()}.
*/
TrackedLeg getTrackedLegFinishingAt(Waypoint endOfLeg);
/**
* Precondition: waypoint must still be part of {@link #getRace()}.{@link RaceDefinition#getCourse() getCourse()}.
*/
TrackedLeg getTrackedLegStartingAt(Waypoint startOfLeg);
/**
* The raw, updating feed of a single competitor participating in this race
*/
GPSFixTrack<Competitor, GPSFixMoving> getTrack(Competitor competitor);
/**
* Tells the leg on which the <code>competitor</code> was at time <code>at</code>.
* If the competitor hasn't passed the start waypoint yet, <code>null</code> is
* returned because the competitor was not yet on any leg at that point in time. If
* the time point happens to be after the last fix received from that competitor,
* the last known leg for that competitor is returned. If the time point is after the
* competitor's mark passing for the finish line, <code>null</code> is returned.
* For all legs except the last, if the time point equals a mark passing time point
* of the leg's starting waypoint, that leg is returned. For the time point of
* the mark passing for the finish line, the last leg is returned.
* Tells the leg on which the <code>competitor</code> was at time <code>at</code>. If the competitor hasn't passed
* the start waypoint yet, <code>null</code> is returned because the competitor was not yet on any leg at that point
* in time. If the time point happens to be after the last fix received from that competitor, the last known leg for
* that competitor is returned. If the time point is after the competitor's mark passing for the finish line,
* <code>null</code> is returned. For all legs except the last, if the time point equals a mark passing time point
* of the leg's starting waypoint, that leg is returned. For the time point of the mark passing for the finish line,
* the last leg is returned.
*/
TrackedLegOfCompetitor getTrackedLeg(Competitor competitor, TimePoint at);
TrackedLegOfCompetitor getTrackedLeg(Competitor competitor, Leg leg);
/**
* @return a sequential number counting the updates that occurred to this tracked race. Callers may use this to ask
* for updates newer than such a sequence number.
*/
long getUpdateCount();
int getRankDifference(Competitor competitor, Leg leg, TimePoint timePoint);
/**
* Computes the rank of the competitor in this race for the current time.
* Computes the rank of the competitor in this race for the current time.
*/
int getRank(Competitor competitor) throws NoWindException;
/**
* Computes the rank of <code>competitor</code> in this race. A competitor is ahead of all
* competitors that are one or more legs behind. Within the same leg, the rank is determined
* by the windward distance to go and therefore depends on the assumptions of the wind direction
* for the given <code>timePoint</code>. If the race hasn't {@link #hasStarted(TimePoint) started}
* yet, the result is undefined.
* Computes the rank of <code>competitor</code> in this race. A competitor is ahead of all competitors that are one
* or more legs behind. Within the same leg, the rank is determined by the windward distance to go and therefore
* depends on the assumptions of the wind direction for the given <code>timePoint</code>. If the race hasn't
* {@link #hasStarted(TimePoint) started} yet, the result is undefined.
*
* @return <code>0</code> in case the competitor hasn't participated in the race; a rank starting
* with <code>1</code> where rank <code>1</code> identifies the leader otherwise
* @return <code>0</code> in case the competitor hasn't participated in the race; a rank starting with
* <code>1</code> where rank <code>1</code> identifies the leader otherwise
*/
int getRank(Competitor competitor, TimePoint timePoint) throws NoWindException;
/**
* For a competitor, computes the distance (TODO not yet clear whether over ground or
* projected onto wind direction) into the race <code>secondsIntoTheRace</code> after
* the race {@link TrackedRace#getStart() started}.
* For a competitor, computes the distance (TODO not yet clear whether over ground or projected onto wind direction)
* into the race <code>secondsIntoTheRace</code> after the race {@link TrackedRace#getStart() started}.
*/
Distance getStartAdvantage(Competitor competitor, double secondsIntoTheRace);
@@ -183,14 +179,14 @@ public interface TrackedRace extends Serializable {
Iterable<MarkPassing> getMarkPassingsInOrder(Waypoint waypoint);
/**
* Obtains the {@link MarkPassing} for <code>competitor</code> passing <code>waypoint</code>. If no such
* mark passing has been reported (yet), <code>null</code> is returned.
* Obtains the {@link MarkPassing} for <code>competitor</code> passing <code>waypoint</code>. If no such mark
* passing has been reported (yet), <code>null</code> is returned.
*/
MarkPassing getMarkPassing(Competitor competitor, Waypoint waypoint);
/**
* Yields the track describing <code>mark</code>'s movement over time; never <code>null</code> because a
* new track will be created in case no track was present for <code>mark</code> so far.
* Yields the track describing <code>mark</code>'s movement over time; never <code>null</code> because a new track
* will be created in case no track was present for <code>mark</code> so far.
*/
GPSFixTrack<Mark, GPSFix> getOrCreateTrack(Mark mark);
@@ -204,7 +200,7 @@ public interface TrackedRace extends Serializable {
* is returned. Otherwise, the center of gravity between the mark positions is computed and returned.
*/
Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint);
/**
* Same as {@link #getWind(Position, TimePoint, Iterable) getWind(p, at, Collections.emptyList())}
*/
@@ -212,8 +208,8 @@ public interface TrackedRace extends Serializable {
/**
* Obtains estimated interpolated wind information for a given position and time point. The information is taken
* from all wind sources available except for those listed in <code>windSourcesToExclude</code>, using the confidences
* of the wind values provided by the various sources during averaging.
* from all wind sources available except for those listed in <code>windSourcesToExclude</code>, using the
* confidences of the wind values provided by the various sources during averaging.
*/
Wind getWind(Position p, TimePoint at, Iterable<WindSource> windSourcesToExclude);
@@ -231,10 +227,11 @@ public interface TrackedRace extends Serializable {
Iterable<WindSource> getWindSources();
/**
* Same as {@link #getOrCreateWindTrack(WindSource, long) getOrCreateWindTrack(windSource, getMillisecondsOverWhichToAverageWind())}.
* Same as {@link #getOrCreateWindTrack(WindSource, long) getOrCreateWindTrack(windSource,
* getMillisecondsOverWhichToAverageWind())}.
*/
WindTrack getOrCreateWindTrack(WindSource windSource);
WindTrack getOrCreateWindTrack(WindSource windSource, long delayForWindEstimationCacheInvalidation);
/**
@@ -243,26 +240,26 @@ public interface TrackedRace extends Serializable {
void waitForNextUpdate(int sinceUpdate) throws InterruptedException;
/**
* Time stamp of the start of the actual tracking.
* The value can be null (e.g. if we have not received any signal from the tracking infrastructure)
* Time stamp of the start of the actual tracking. The value can be null (e.g. if we have not received any signal
* from the tracking infrastructure)
*/
TimePoint getStartOfTracking();
/**
* Time stamp of the end of the actual tracking.
* The value can be null (e.g. if we have not received any signal from the tracking infrastructure)
* Time stamp of the end of the actual tracking. The value can be null (e.g. if we have not received any signal from
* the tracking infrastructure)
*/
TimePoint getEndOfTracking();
/**
* Regardless of the order in which events were received, this method returns the latest time point contained by any of
* the events received and processed.
* Regardless of the order in which events were received, this method returns the latest time point contained by any
* of the events received and processed.
*/
TimePoint getTimePointOfNewestEvent();
/**
* Regardless of the order in which events were received, this method returns the oldest time point contained by any of
* the events received and processed.
* Regardless of the order in which events were received, this method returns the oldest time point contained by any
* of the events received and processed.
*/
TimePoint getTimePointOfOldestEvent();
@@ -274,17 +271,18 @@ public interface TrackedRace extends Serializable {
* some tracking providers such as TracTrac. If the caller wants to iterate on the resulting collection or
* construct a {@link SortedSet#headSet(Object)} or {@link SortedSet#tailSet(Object)} and then iterate over
* that, the caller needs to invoke {@link #lockForRead(Iterable)} with the collection returned as parameter
* because insertions into the competitor's mark passing collection will obtain the corresponding write lock.
* because insertions into the competitor's mark passing collection will obtain the corresponding write
* lock.
*/
NavigableSet<MarkPassing> getMarkPassings(Competitor competitor);
void lockForRead(Iterable<MarkPassing> markPassings);
void unlockAfterRead(Iterable<MarkPassing> markPassings);
/**
* Time stamp that the event received last from the underlying push service carried on it.
* Note that these times may not increase monotonically.
* Time stamp that the event received last from the underlying push service carried on it. Note that these times may
* not increase monotonically.
*/
TimePoint getTimePointOfLastEvent();
@@ -296,7 +294,7 @@ public interface TrackedRace extends Serializable {
* Gets the current delay of incoming events to the real time of the events in milliseconds
*/
long getDelayToLiveInMillis();
/**
* Estimates the wind direction based on the observed boat courses at the time given for the position provided. The
* estimate is based on the assumption that the boats which are on an upwind or a downwind leg sail with very
@@ -321,13 +319,12 @@ public interface TrackedRace extends Serializable {
* we may be able to also infer the wind speed from the boat tracks.
*/
Wind getEstimatedWindDirection(Position position, TimePoint timePoint);
/**
* Determines whether the <code>competitor</code> is sailing on port or starboard tack at the
* <code>timePoint</code> requested. Note that this will have to retrieve information about the wind.
* This, in turn, can lead to the current thread obtaining the monitor of the various wind tracks,
* and, if the {@link WindSource#TRACK_BASED_ESTIMATION} source is used, also the monitors of the
* competitors' GPS tracks.
* Determines whether the <code>competitor</code> is sailing on port or starboard tack at the <code>timePoint</code>
* requested. Note that this will have to retrieve information about the wind. This, in turn, can lead to the
* current thread obtaining the monitor of the various wind tracks, and, if the
* {@link WindSource#TRACK_BASED_ESTIMATION} source is used, also the monitors of the competitors' GPS tracks.
*/
Tack getTack(Competitor competitor, TimePoint timePoint) throws NoWindException;
@@ -345,30 +342,34 @@ public interface TrackedRace extends Serializable {
* mark positions are not known (yet)
*/
Wind getDirectionFromStartToNextMark(TimePoint at);
/**
* Uses a {@link DouglasPeucker Douglas-Peucker} algorithm to approximate this track's fixes starting at
* time <code>from</code> until time point <code>to</code> such that the maximum distance between the
* track's fixes and the approximation is at most <code>maxDistance</code>.
* Uses a {@link DouglasPeucker Douglas-Peucker} algorithm to approximate this track's fixes starting at time
* <code>from</code> until time point <code>to</code> such that the maximum distance between the track's fixes and
* the approximation is at most <code>maxDistance</code>.
*/
List<GPSFixMoving> approximate(Competitor competitor, Distance maxDistance, TimePoint from, TimePoint to);
/**
* @return a non-<code>null</code> but perhaps empty list of the maneuvers that <code>competitor</code> performed in
* this race between <code>from</code> and <code>to</code>.
* this race between <code>from</code> and <code>to</code>. Depending on <code>waitForLatest</code> the
* result is taken from the cache straight away (<code>waitForLatest==false</code>) or, if a re-calculation
* for the <code>key</code> is still ongoing, the result of that ongoing re-calculation is returned.
*/
List<Maneuver> getManeuvers(Competitor competitor, TimePoint from, TimePoint to, boolean waitForLatest) throws NoWindException;
List<Maneuver> getManeuvers(Competitor competitor, TimePoint from, TimePoint to, boolean waitForLatest)
throws NoWindException;
/**
* @return <code>true</code> if this race is known to start with an {@link LegType#UPWIND upwind} leg.
* If this is the case, the wind estimation may default to using the first leg's direction at race start
* time as the direction the wind comes from.
* @return <code>true</code> if this race is known to start with an {@link LegType#UPWIND upwind} leg. If this is
* the case, the wind estimation may default to using the first leg's direction at race start time as the
* direction the wind comes from.
*/
boolean raceIsKnownToStartUpwind();
/**
* Many calculations require valid wind data. In order to prevent NoWindException's to be handled by those calculation
* this method can be used to check whether the tracked race has sufficient wind information available.
* Many calculations require valid wind data. In order to prevent NoWindException's to be handled by those
* calculation this method can be used to check whether the tracked race has sufficient wind information available.
*
* @return <code>true</code> if {@link #getWind(Position, TimePoint)} delivers a (not null) wind fix.
*/
boolean hasWindData();
@@ -380,23 +381,23 @@ public interface TrackedRace extends Serializable {
boolean hasGPSData();
/**
* Adds a race change listener to the set of listeners that will be notified about changes to this race.
* The listener won't be serialized together with this object.
* Adds a race change listener to the set of listeners that will be notified about changes to this race. The
* listener won't be serialized together with this object.
*/
void addListener(RaceChangeListener listener);
void removeListener(RaceChangeListener listener);
Distance getDistanceTraveled(Competitor competitor, TimePoint timePoint);
Distance getWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint) throws NoWindException;
/**
* Calls {@link #getWindWithConfidence(Position, TimePoint, Iterable)} and excludes those wind sources listed in
* {@link #getWindSourcesToExclude}.
*/
WindWithConfidence<Pair<Position, TimePoint>> getWindWithConfidence(Position p, TimePoint at);
/**
* Lists those wind sources which by default are not considered in {@link #getWind(Position, TimePoint)} and
* {@link #getWindWithConfidence(Position, TimePoint)}.
@@ -406,8 +407,8 @@ public interface TrackedRace extends Serializable {
/**
* Loops over this tracked race's wind sources and from each asks its averaged wind for the position <code>p</code>
* and time point <code>at</code>, using the particular wind source's averaging interval. The confidences delivered
* by each wind source are used during computing the averaged result across the wind sources. The result has the averaged
* confidence attached.
* by each wind source are used during computing the averaged result across the wind sources. The result has the
* averaged confidence attached.
*/
WindWithConfidence<Pair<Position, TimePoint>> getWindWithConfidence(Position p, TimePoint at,
Iterable<WindSource> windSourcesToExclude);
@@ -419,7 +420,8 @@ public interface TrackedRace extends Serializable {
WindWithConfidence<TimePoint> getEstimatedWindDirectionWithConfidence(Position position, TimePoint timePoint);
/**
* After the call returns, {@link #getWindSourcesToExclude()} returns an iterable that equals <code>windSourcesToExclude</code>
* After the call returns, {@link #getWindSourcesToExclude()} returns an iterable that equals
* <code>windSourcesToExclude</code>
*/
void setWindSourcesToExclude(Iterable<? extends WindSource> windSourcesToExclude);
@@ -430,25 +432,27 @@ public interface TrackedRace extends Serializable {
* 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
*/
Distance getAverageCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis) throws NoWindException;
Distance getAverageCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis)
throws NoWindException;
WindStore getWindStore();
Competitor getOverallLeader(TimePoint timePoint) throws NoWindException;
/**
* Returns the competitors of this tracked race, according to their ranking. Competitors whose {@link #getRank(Competitor)} is 0 will
* be sorted "worst".
* Returns the competitors of this tracked race, according to their ranking. Competitors whose
* {@link #getRank(Competitor)} is 0 will be sorted "worst".
*/
List<Competitor> getCompetitorsFromBestToWorst(TimePoint timePoint) throws NoWindException;
Distance getAverageCrossTrackError(Competitor competitor, TimePoint from, TimePoint to, boolean upwindOnly, boolean waitForLatestAnalyses) throws NoWindException;
Distance getAverageCrossTrackError(Competitor competitor, TimePoint from, TimePoint to, boolean upwindOnly,
boolean waitForLatestAnalyses) throws NoWindException;
/**
* When provided with a {@link WindStore} during construction, the tracked race will asynchronously load the wind
* data for this tracked race from the wind store in a background thread and update this tracked race with the results.
* Clients that want to wait for the wind loading process to complete can do so by calling this method which will block
* until the wind loading has completed.
* data for this tracked race from the wind store in a background thread and update this tracked race with the
* results. Clients that want to wait for the wind loading process to complete can do so by calling this method
* which will block until the wind loading has completed.
*/
void waitUntilWindLoadingComplete() throws InterruptedException;