Merge remote-tracking branch 'ExternalGit/master' into raceboard-simulation

This commit is contained in:
Christopher Ronnewinkel (D036654)
2015-07-28 13:59:52 +02:00
545 changed files with 8324 additions and 4143 deletions
@@ -35,7 +35,7 @@ import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class BarbadosResultImportTest {
private static final MillisecondsTimePoint NOW = MillisecondsTimePoint.now();
private static final TimePoint NOW = MillisecondsTimePoint.now();
private static final String SAMPLE_INPUT_NAME_EMPTY_RESULTS = "RESULTS-505Barbados.xlsx";
private static final String SAMPLE_INPUT_NAME_SOME_RESULTS = "RESULTS2.xlsx";
private static final String RESOURCES = "resources/";
@@ -1,6 +1,6 @@
package com.sap.sailing.datamining.test.util;
import com.sap.sse.datamining.AdditionalResultDataBuilder;
import com.sap.sse.datamining.components.AdditionalResultDataBuilder;
import com.sap.sse.datamining.impl.components.AbstractProcessor;
public class NullProcessor<InputType, ResultType> extends AbstractProcessor<InputType, ResultType> {
@@ -20,6 +20,7 @@ LegSailingDomainRetrieverChain=Segel-Dom
GPSFixSailingDomainRetrieverChain=Segel-Domänen GPS-Fix-Beschaffer
MarkPassingSailingDomainRetrieverChain=Segel-Domänen Tonnenrundungs-Beschaffer
RaceResultSailingDomainRetrieverChain=Segel-Domänen Rennergebnis-Beschaffer
RaceOfCompetitorSailingDomainRetrieverChain=Rennen eines Teilnehmers-Beschaffer
LeaderboardGroup=Ranglisten Gruppe
Leaderboard=Rangliste
Leg=Schenkel
@@ -44,4 +45,15 @@ Bft10=schwerer Sturm
Bft11=orkanartiger Sturm
Bft12=Orkan
RelativeScoreInRace=Relative Wertung im Rennen (0 am besten; 1 am schlechtesten)
WindSpeedInBeaufort=Windgeschwindigkeit in Bft.
WindSpeedInBeaufort=Windgeschwindigkeit in Bft.
DistanceAtStart=Distanz zur Startlinie zum Startzeitpunkt
SpeedAtStart=Geschwindigkeit zum Startzeitpunkt
SpeedTenSecondsBeforeStart=Geschwindigkeit 10s vor dem Startzeitpunkt
RankAtFirstMark=Position beim ersten Wegpunkt
NumberOfTacks=Anzahl der Wenden
NumberOfJibes=Anzahl der Halsen
NumberOfPenaltyCircles=Anzahl der Straf-Kreise
RankGainsOrLosses=Positions-Verluste oder -Gewinne
DistanceToStarboardSideAtStart=Distanz zur Steuerbordseite (% der Länge)
NumberOfPodiumFinish=Azahl der Podium-Siege
NumberOfWins=Anzahl der Siege
@@ -22,6 +22,7 @@ LegSailingDomainRetrieverChain=Sailing Domain Leg Retriever
GPSFixSailingDomainRetrieverChain=Sailing Domain GPS Fix Retriever
MarkPassingSailingDomainRetrieverChain=Sailing Domain Mark Passing Retriever
RaceResultSailingDomainRetrieverChain=Sailing Domain Race Result Retriever
RaceOfCompetitorSailingDomainRetrieverChain=Race of Competitor Retriever
LeaderboardGroup=Leaderboard Group
Leaderboard=Leaderboard
Leg=Leg
@@ -44,4 +45,15 @@ Bft10=Storm
Bft11=Violent storm
Bft12=Hurricane
RelativeScoreInRace=Relative score in race (0 best; 1 worst)
WindSpeedInBeaufort=Wind speed in Bft
WindSpeedInBeaufort=Wind speed in Bft
DistanceAtStart=Distance to start at start
SpeedAtStart=Speed at start
SpeedTenSecondsBeforeStart=Speed 10s before start
RankAtFirstMark=Rank at First Mark
NumberOfTacks=Number of Tacks
NumberOfJibes=Number of Jibes
NumberOfPenaltyCircles=Number of Penalty Circles
RankGainsOrLosses=Rank Gains or Losses
DistanceToStarboardSideAtStart=Distance to Starboard Side (% of Length)
NumberOfPodiumFinish=Number of Podium Finishes
NumberOfWins=Number of Wins
@@ -6,16 +6,19 @@ import java.util.Set;
import org.osgi.framework.BundleContext;
import com.sap.sailing.datamining.data.HasRaceResultOfCompetitorContext;
import com.sap.sailing.datamining.data.HasGPSFixContext;
import com.sap.sailing.datamining.data.HasMarkPassingContext;
import com.sap.sailing.datamining.data.HasRaceOfCompetitorContext;
import com.sap.sailing.datamining.data.HasRaceResultOfCompetitorContext;
import com.sap.sailing.datamining.data.HasTrackedLegContext;
import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
import com.sap.sailing.datamining.data.HasTrackedRaceContext;
import com.sap.sailing.datamining.impl.components.aggregators.ParallelTrueSumAggregationProcessor;
import com.sap.sailing.domain.common.Speed;
import com.sap.sse.datamining.DataMiningBundleService;
import com.sap.sse.datamining.DataRetrieverChainDefinition;
import com.sap.sse.datamining.DataSourceProvider;
import com.sap.sse.datamining.components.AggregationProcessorDefinition;
import com.sap.sse.datamining.components.DataRetrieverChainDefinition;
import com.sap.sse.datamining.data.ClusterGroup;
import com.sap.sse.datamining.impl.AbstractDataMiningActivator;
import com.sap.sse.i18n.ResourceBundleStringMessages;
@@ -74,6 +77,7 @@ public class Activator extends AbstractDataMiningActivator implements DataMining
internalClasses.add(HasTrackedLegOfCompetitorContext.class);
internalClasses.add(HasGPSFixContext.class);
internalClasses.add(HasMarkPassingContext.class);
internalClasses.add(HasRaceOfCompetitorContext.class);
return internalClasses;
}
@@ -91,6 +95,13 @@ public class Activator extends AbstractDataMiningActivator implements DataMining
return dataSourceProviders;
}
@Override
public Iterable<AggregationProcessorDefinition<?, ?>> getAggregationProcessorDefinitions() {
HashSet<AggregationProcessorDefinition<?, ?>> aggregators = new HashSet<>();
aggregators.add(ParallelTrueSumAggregationProcessor.getDefinition());
return aggregators;
}
private void initializeDataSourceProviders() {
dataSourceProviders = new HashSet<>();
dataSourceProviders.add(new RacingEventServiceProvider(context));
@@ -6,6 +6,7 @@ import java.util.Collection;
import com.sap.sailing.datamining.data.HasGPSFixContext;
import com.sap.sailing.datamining.data.HasLeaderboardContext;
import com.sap.sailing.datamining.data.HasMarkPassingContext;
import com.sap.sailing.datamining.data.HasRaceOfCompetitorContext;
import com.sap.sailing.datamining.data.HasRaceResultOfCompetitorContext;
import com.sap.sailing.datamining.data.HasTrackedLegContext;
import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
@@ -15,13 +16,14 @@ import com.sap.sailing.datamining.impl.components.GPSFixRetrievalProcessor;
import com.sap.sailing.datamining.impl.components.LeaderboardGroupRetrievalProcessor;
import com.sap.sailing.datamining.impl.components.LeaderboardRetrievalProcessor;
import com.sap.sailing.datamining.impl.components.MarkPassingRetrievalProcessor;
import com.sap.sailing.datamining.impl.components.RaceOfCompetitorRetrievalProcessor;
import com.sap.sailing.datamining.impl.components.TrackedLegOfCompetitorRetrievalProcessor;
import com.sap.sailing.datamining.impl.components.TrackedLegRetrievalProcessor;
import com.sap.sailing.datamining.impl.components.TrackedRaceRetrievalProcessor;
import com.sap.sailing.datamining.impl.data.LeaderboardGroupWithContext;
import com.sap.sailing.server.RacingEventService;
import com.sap.sse.datamining.DataRetrieverChainDefinition;
import com.sap.sse.datamining.impl.SimpleDataRetrieverChainDefinition;
import com.sap.sse.datamining.components.DataRetrieverChainDefinition;
import com.sap.sse.datamining.impl.components.SimpleDataRetrieverChainDefinition;
public class SailingDataRetrievalChainDefinitions {
@@ -50,6 +52,11 @@ public class SailingDataRetrievalChainDefinitions {
trackedRaceRetrieverChainDefinition, HasMarkPassingContext.class, "MarkPassingSailingDomainRetrieverChain");
markPassingRetrieverChainDefinition.endWith(TrackedRaceRetrievalProcessor.class, MarkPassingRetrievalProcessor.class, HasMarkPassingContext.class, "MarkPassing");
dataRetrieverChainDefinitions.add(markPassingRetrieverChainDefinition);
final DataRetrieverChainDefinition<RacingEventService, HasRaceOfCompetitorContext> raceOfCompetitorRetrieverChainDefinition = new SimpleDataRetrieverChainDefinition<>(
trackedRaceRetrieverChainDefinition, HasRaceOfCompetitorContext.class, "RaceOfCompetitorSailingDomainRetrieverChain");
raceOfCompetitorRetrieverChainDefinition.endWith(TrackedRaceRetrievalProcessor.class, RaceOfCompetitorRetrievalProcessor.class, HasRaceOfCompetitorContext.class, "Competitor");
dataRetrieverChainDefinitions.add(raceOfCompetitorRetrieverChainDefinition);
final DataRetrieverChainDefinition<RacingEventService, HasTrackedLegOfCompetitorContext> legOfCompetitorRetrieverChainDefinition = new SimpleDataRetrieverChainDefinition<>(
trackedRaceRetrieverChainDefinition, HasTrackedLegOfCompetitorContext.class, "LegSailingDomainRetrieverChain");
@@ -0,0 +1,44 @@
package com.sap.sailing.datamining.data;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.Speed;
import com.sap.sse.datamining.shared.annotations.Connector;
import com.sap.sse.datamining.shared.annotations.Statistic;
import com.sap.sse.datamining.shared.data.Unit;
public interface HasRaceOfCompetitorContext {
@Connector(scanForStatistics=false)
public HasTrackedRaceContext getTrackedRaceContext();
@Connector(messageKey="Competitor")
public Competitor getCompetitor();
@Statistic(messageKey="DistanceAtStart", resultUnit=Unit.Meters, resultDecimals=0, ordinal=0)
public double getDistanceToStartLineAtStart();
@Statistic(messageKey="DistanceToStarboardSideAtStart", resultDecimals=2, ordinal=1)
public Double getNormalizedDistanceToStarboardSideAtStart();
@Connector(messageKey="SpeedAtStart", ordinal=2)
public Speed getSpeedAtStart();
@Connector(messageKey="SpeedTenSecondsBeforeStart", ordinal=3)
public Speed getSpeedTenSecondsBeforeStart();
@Statistic(messageKey="RankAtFirstMark", resultDecimals=2, ordinal=4)
public Double getRankAtFirstMark();
@Statistic(messageKey="NumberOfTacks", resultDecimals=2, ordinal=5)
public Double getNumberOfTacks();
@Statistic(messageKey="NumberOfJibes", resultDecimals=2, ordinal=6)
public Double getNumberOfJibes();
@Statistic(messageKey="NumberOfPenaltyCircles", resultDecimals=2, ordinal=7)
public Double getNumberOfPenaltyCircles();
@Statistic(messageKey="RankGainsOrLosses", resultDecimals=2, ordinal=8)
public Double getRankGainsOrLosses();
}
@@ -16,7 +16,7 @@ public interface HasRaceResultOfCompetitorContext {
/**
* 0 means the competitor won the race, 1 means the competitor ranked last
*/
@Statistic(messageKey="RelativeScoreInRace", ordinal=3, resultDecimals=2)
@Statistic(messageKey="RelativeScoreInRace", ordinal=1, resultDecimals=2)
public double getRelativeRank();
@Dimension(messageKey="WindSpeedInBeaufort")
@@ -24,4 +24,10 @@ public interface HasRaceResultOfCompetitorContext {
@Dimension(messageKey="Regatta")
String getRegattaName();
@Statistic(messageKey="NumberOfPodiumFinish", ordinal=2)
public Boolean isPodiumFinish();
@Statistic(messageKey="NumberOfWins", ordinal=3)
public Boolean isWin();
}
@@ -18,5 +18,8 @@ public interface HasTrackedLegOfCompetitorContext {
@Statistic(messageKey="DistanceTraveled", resultUnit=Unit.Meters, resultDecimals=0, ordinal=0)
public Double getDistanceTraveled();
@Statistic(messageKey="RankGainsOrLosses", resultDecimals=2, ordinal=1)
public Double getRankGainsOrLosses();
}
@@ -0,0 +1,31 @@
package com.sap.sailing.datamining.impl.components;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.ExecutorService;
import com.sap.sailing.datamining.data.HasRaceOfCompetitorContext;
import com.sap.sailing.datamining.data.HasTrackedRaceContext;
import com.sap.sailing.datamining.impl.data.RaceOfCompetitorWithContext;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.impl.components.AbstractRetrievalProcessor;
public class RaceOfCompetitorRetrievalProcessor extends AbstractRetrievalProcessor<HasTrackedRaceContext, HasRaceOfCompetitorContext> {
public RaceOfCompetitorRetrievalProcessor(ExecutorService executor,
Collection<Processor<HasRaceOfCompetitorContext, ?>> resultReceivers, int retrievalLevel) {
super(HasTrackedRaceContext.class, HasRaceOfCompetitorContext.class, executor, resultReceivers, retrievalLevel);
}
@Override
protected Iterable<HasRaceOfCompetitorContext> retrieveData(HasTrackedRaceContext element) {
Collection<HasRaceOfCompetitorContext> raceOfCompetitorsWithContext = new ArrayList<>();
for (Competitor competitor : element.getTrackedRace().getRace().getCompetitors()) {
HasRaceOfCompetitorContext raceOfCompetitorWithContext = new RaceOfCompetitorWithContext(element, competitor);
raceOfCompetitorsWithContext.add(raceOfCompetitorWithContext);
}
return raceOfCompetitorsWithContext;
}
}
@@ -0,0 +1,50 @@
package com.sap.sailing.datamining.impl.components.aggregators;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import com.sap.sse.datamining.components.AggregationProcessorDefinition;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.impl.components.GroupedDataEntry;
import com.sap.sse.datamining.impl.components.SimpleAggregationProcessorDefinition;
import com.sap.sse.datamining.impl.components.aggregators.AbstractParallelGroupedDataStoringAggregationProcessor;
import com.sap.sse.datamining.shared.GroupKey;
public class ParallelTrueSumAggregationProcessor
extends AbstractParallelGroupedDataStoringAggregationProcessor<Boolean, Double> {
private static final AggregationProcessorDefinition<Boolean, Double> DEFINITION =
new SimpleAggregationProcessorDefinition<>(Boolean.class, Double.class, "Sum", ParallelTrueSumAggregationProcessor.class);
public static AggregationProcessorDefinition<Boolean, Double> getDefinition() {
return DEFINITION;
}
private Map<GroupKey, Double> result;
public ParallelTrueSumAggregationProcessor(ExecutorService executor,
Collection<Processor<Map<GroupKey, Double>, ?>> resultReceivers) {
super(executor, resultReceivers, "Sum");
result = new HashMap<>();
}
@Override
protected void storeElement(GroupedDataEntry<Boolean> element) {
GroupKey key = element.getKey();
if (!result.containsKey(key)) {
result.put(key, 0.0);
}
Double currentAmount = result.get(key);
if (element.getDataEntry()) {
result.put(key, currentAmount + 1);
}
}
@Override
protected Map<GroupKey, Double> aggregateResult() {
return result;
}
}
@@ -0,0 +1,111 @@
package com.sap.sailing.datamining.impl.data;
import java.util.concurrent.TimeUnit;
import com.sap.sailing.datamining.data.HasRaceOfCompetitorContext;
import com.sap.sailing.datamining.data.HasTrackedRaceContext;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.TimePoint;
public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
private final HasTrackedRaceContext trackedRaceContext;
private final Competitor competitor;
public RaceOfCompetitorWithContext(HasTrackedRaceContext trackedRaceContext, Competitor competitor) {
this.trackedRaceContext = trackedRaceContext;
this.competitor = competitor;
}
@Override
public HasTrackedRaceContext getTrackedRaceContext() {
return trackedRaceContext;
}
private TrackedRace getTrackedRace() {
return getTrackedRaceContext().getTrackedRace();
}
@Override
public Competitor getCompetitor() {
return competitor;
}
@Override
public double getDistanceToStartLineAtStart() {
return getTrackedRace().getDistanceToStartLine(getCompetitor(), 0).getMeters();
}
@Override
public Speed getSpeedAtStart() {
return getTrackedRace().getSpeed(getCompetitor(), 0);
}
@Override
public Speed getSpeedTenSecondsBeforeStart() {
return getTrackedRace().getSpeed(getCompetitor(), TimeUnit.SECONDS.toMillis(10));
}
@Override
public Double getRankAtFirstMark() {
Course course = getTrackedRace().getRace().getCourse();
Waypoint firstMark = course.getFirstLeg().getTo();
Competitor competitor = getCompetitor();
return Double.valueOf(getTrackedRace().getRank(competitor, getTrackedRace().getMarkPassing(competitor, firstMark).getTimePoint()));
}
@Override
public Double getNumberOfTacks() {
return getNumberOf(ManeuverType.TACK);
}
@Override
public Double getNumberOfJibes() {
return getNumberOf(ManeuverType.JIBE);
}
@Override
public Double getNumberOfPenaltyCircles() {
return getNumberOf(ManeuverType.PENALTY_CIRCLE);
}
private Double getNumberOf(ManeuverType maneuverType) {
TrackedRace trackedRace = getTrackedRace();
double number = 0;
for (Maneuver maneuver : trackedRace.getManeuvers(getCompetitor(), trackedRace.getStartOfRace(), trackedRace.getEndOfTracking(), false)) {
if (maneuver.getType() == maneuverType) {
number++;
}
}
return number;
}
@Override
public Double getRankGainsOrLosses() {
Double rankAtFirstMark = getRankAtFirstMark();
Double rankAtFinish = getRankAtFinish();
return rankAtFirstMark - rankAtFinish;
}
private Double getRankAtFinish() {
return Double.valueOf(getTrackedRace().getRank(getCompetitor(), getTrackedRace().getEndOfTracking()));
}
@Override
public Double getNormalizedDistanceToStarboardSideAtStart() {
TrackedRace trackedRace = getTrackedRace();
TrackedLegOfCompetitor firstTrackedLegOfCompetitor = trackedRace.getTrackedLeg(competitor, trackedRace.getRace().getCourse().getFirstLeg());
TimePoint competitorStartTime = firstTrackedLegOfCompetitor.getStartTime();
Double distance = trackedRace.getDistanceFromStarboardSideOfStartLine(getCompetitor(), competitorStartTime).getMeters();
Double length = trackedRace.getStartLine(competitorStartTime).getLength().getMeters();
return distance / length;
}
}
@@ -52,6 +52,10 @@ public class RaceResultOfCompetitorWithContext implements HasRaceResultOfCompeti
return leaderboardWithContext;
}
private Leaderboard getLeaderboard() {
return getLeaderboardContext().getLeaderboard();
}
@Override
public Competitor getCompetitor() {
return competitor;
@@ -59,8 +63,8 @@ public class RaceResultOfCompetitorWithContext implements HasRaceResultOfCompeti
@Override
public double getRelativeRank() {
Leaderboard leaderboard = getLeaderboardContext().getLeaderboard();
final MillisecondsTimePoint now = MillisecondsTimePoint.now();
Leaderboard leaderboard = getLeaderboard();
final TimePoint now = MillisecondsTimePoint.now();
double competitorCount = Util.size(leaderboard.getCompetitors());
double points = leaderboard.getNetPoints(competitor, raceColumn, now);
double relativeLowPoints = leaderboard.getScoringScheme().isHigherBetter() ?
@@ -154,9 +158,35 @@ public class RaceResultOfCompetitorWithContext implements HasRaceResultOfCompeti
@Override
public String getRegattaName() {
Leaderboard leaderboard = getLeaderboardContext().getLeaderboard();;
Leaderboard leaderboard = getLeaderboard();;
final String result = leaderboard.getName();
return result;
}
@Override
public Boolean isPodiumFinish() {
Leaderboard leaderboard = getLeaderboard();
final TimePoint now = MillisecondsTimePoint.now();
double points = leaderboard.getNetPoints(competitor, raceColumn, now);
if (leaderboard.getScoringScheme().isHigherBetter()) {
double competitorCount = Util.size(leaderboard.getCompetitors());
return points >= (competitorCount - 2.05);
} else {
return points <= 3.05;
}
}
@Override
public Boolean isWin() {
Leaderboard leaderboard = getLeaderboard();
final TimePoint now = MillisecondsTimePoint.now();
double points = leaderboard.getNetPoints(competitor, raceColumn, now);
if (leaderboard.getScoringScheme().isHigherBetter()) {
double competitorCount = Util.size(leaderboard.getCompetitors());
return points >= (competitorCount - 0.05);
} else {
return points <= 1.05;
}
}
}
@@ -4,6 +4,7 @@ import com.sap.sailing.datamining.data.HasTrackedLegContext;
import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.TimePoint;
public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompetitorContext {
@@ -39,5 +40,13 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti
TimePoint timePoint = getTrackedLegContext().getTrackedRaceContext().getTrackedRace().getEndOfTracking();
return getTrackedLegOfCompetitor().getDistanceTraveled(timePoint).getMeters();
}
@Override
public Double getRankGainsOrLosses() {
TrackedRace trackedRace = getTrackedLegContext().getTrackedRaceContext().getTrackedRace();
Double rankAtStart = Double.valueOf(trackedRace.getRank(getCompetitor(), getTrackedLegOfCompetitor().getStartTime()));
Double rankAtFinish = Double.valueOf(trackedRace.getRank(getCompetitor(), getTrackedLegOfCompetitor().getFinishTime()));
return rankAtStart - rankAtFinish;
}
}
@@ -41,7 +41,7 @@ public enum BoatClassMasterdata {
J24 ("J/24", true, 7.32, 2.67, BoatHullType.MONOHULL, true, "J24", "J-24"),
J70 ("J/70", true, 6.93, 2.25, BoatHullType.MONOHULL, true, "J70", "J-70"),
J80 ("J/80", true, 8.0, 2.51, BoatHullType.MONOHULL, true, "J80", "J-80"),
KIELZUGVOGEL ("Kielzugvogel", true, 5.80, 1.88, BoatHullType.MONOHULL, false),
KIELZUGVOGEL ("Kielzugvogel", true, 5.80, 1.88, BoatHullType.MONOHULL, false, "KZV"),
KITE ("Kite", true, 3.35, 1.52, BoatHullType.MONOHULL, false),
LASER_2 ("Laser 2", true, 4.39, 1.42, BoatHullType.MONOHULL, false, "Laser II", "Laser2", "Laser-2", "Laser-II"),
LASER_4_7 ("Laser 4.7", true, 4.20, 1.39, BoatHullType.MONOHULL, false, "L4.7"),
@@ -1,8 +1,6 @@
package com.sap.sailing.domain.common;
public interface LeaderboardNameConstants {
static final String DEFAULT_LEADERBOARD_NAME = "Default Leaderboard";
static final String OVERALL = "Overall";
static final String DEFAULT_FLEET_NAME = "Default";
@@ -7,10 +7,6 @@ import com.sap.sse.common.Util.Pair;
public interface PolarSheetsXYDiagramData extends Serializable {
List<Pair<Double, Double>> getPointsForAverageSpeedMovingAverage(Tack tack, LegType legType);
List<Pair<Double, Double>> getPointsForAverageConfidence(Tack tack, LegType legType);
List<Pair<Double, Double>> getPointsForAverageSpeedRegression(Tack tack, LegType legType);
}
@@ -14,30 +14,12 @@ public class PolarSheetsXYDiagramDataImpl implements PolarSheetsXYDiagramData {
PolarSheetsXYDiagramDataImpl() {
}
private Map<Pair<LegType, Tack>, List<Pair<Double, Double>>> pointsForAverageSpeedMovingAverage;
private Map<Pair<LegType, Tack>, List<Pair<Double, Double>>> pointsForAverageSpeedRegression;
private Map<Pair<LegType, Tack>, List<Pair<Double, Double>>> pointsForAverageConfidence;
public PolarSheetsXYDiagramDataImpl(
Map<Pair<LegType, Tack>, List<Pair<Double, Double>>> movingAverageSpeedDataLists,
Map<Pair<LegType, Tack>, List<Pair<Double, Double>>> regressionSpeedDataLists,
Map<Pair<LegType, Tack>, List<Pair<Double, Double>>> averageConfidenceDataLists) {
pointsForAverageSpeedMovingAverage = movingAverageSpeedDataLists;
Map<Pair<LegType, Tack>, List<Pair<Double, Double>>> regressionSpeedDataLists) {
pointsForAverageSpeedRegression = regressionSpeedDataLists;
pointsForAverageConfidence = averageConfidenceDataLists;
}
@Override
public List<Pair<Double, Double>> getPointsForAverageSpeedMovingAverage(Tack tack, LegType legType) {
return pointsForAverageSpeedMovingAverage.get(new Pair<LegType, Tack>(legType, tack));
}
@Override
public List<Pair<Double, Double>> getPointsForAverageConfidence(Tack tack, LegType legType) {
return pointsForAverageConfidence.get(new Pair<LegType, Tack>(legType, tack));
}
@Override
@@ -8,6 +8,7 @@ import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.AbstractTimePoint;
/**
* A compact representation of a GPS fix which collects all primitive-typed attributes in one object to avoid
@@ -41,7 +42,7 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
private final double latDeg;
private final double lngDeg;
private final TimePoint timePoint;
private final long timePointAsMillis;
/**
* Tells if in the containing {@link DynamicGPSFixTrackImpl} this fix is considered valid. This cache
@@ -77,6 +78,15 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
}
}
private class CompactTimePoint extends AbstractTimePoint implements TimePoint {
private static final long serialVersionUID = -2470922642359937437L;
@Override
public long asMillis() {
return timePointAsMillis;
}
}
private class CompactEstimatedSpeedBearing extends AbstractBearing {
private static final long serialVersionUID = 8549231429037883121L;
@@ -103,7 +113,7 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
public CompactGPSFixImpl(Position position, TimePoint timePoint) {
latDeg = position.getLatDeg();
lngDeg = position.getLngDeg();
this.timePoint = timePoint;
timePointAsMillis = timePoint==null?-1:timePoint.asMillis();
}
public CompactGPSFixImpl(GPSFix gpsFix) {
@@ -122,7 +132,7 @@ public class CompactGPSFixImpl extends AbstractGPSFixImpl {
@Override
public TimePoint getTimePoint() {
return timePoint;
return new CompactTimePoint();
}
@Override
@@ -0,0 +1,28 @@
package com.sap.sailing.domain.common.tracking.impl;
/**
* Used to assemble a compatible JSON string by themselves, using the constants from here.The field identified by this
* name tells the fully-qualified class name from which the value of the field identified by {@link #FIELD_ID} has been
* created. When deserializing, the ID needs to be re-constructed as an instance of that type.
*/
public class CompetitorJsonConstants {
public static final String FIELD_ID = "id";
public static final String FIELD_NAME = "name";
public static final String FIELD_SAIL_ID = "sailID";
public static final String FIELD_NATIONALITY = "nationality";
public static final String FIELD_COUNTRY_CODE = "countryCode";
public static final String FIELD_BOAT_CLASS_NAME = "boatClassName";
public static final String FIELD_COLOR = "color";
public static final String FIELD_FLAG_IMAGE = "flagImage";
public static final String FIELD_ID_TYPE = "idtype";
public static final String FIELD_NATIONALITY_ISO2 = "nationalityISO2";
public static final String FIELD_NATIONALITY_ISO3 = "nationalityISO3";
public static final String FIELD_TEAM = "team";
public static final String FIELD_BOAT = "boat";
public static final String FIELD_DISPLAY_COLOR = "displayColor";
public static final String FIELD_EMAIL = "email";
public static final String FIELD_FLAG_IMAGE_URI = "flagImageUri";
public static final String FIELD_TIME_ON_TIME_FACTOR = "timeOnTimeFactor";
public static final String FIELD_TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE = "timeOnDistanceAllowanceInSecondsPerNauticalMile";
}
@@ -1,4 +1,4 @@
package com.sap.sailing.server.gateway.serialization.impl;
package com.sap.sailing.domain.common.tracking.impl;
/**
* So far only holds the constants required to assemble a JSON object properly. The challenge for this serializer is
@@ -15,7 +15,8 @@ Require-Bundle: org.json.simple;bundle-version="1.1.0",
com.sap.sailing.domain.shared.android,
com.sap.sailing.domain,
com.sap.sailing.declination,
com.sap.sse.common
com.sap.sse.common,
com.sap.sse
Import-Package: javax.ws.rs;version="1.1.1",
javax.ws.rs.core;version="1.1.1",
javax.ws.rs.ext;version="1.1.1",
@@ -20,6 +20,7 @@ import com.sap.sailing.domain.igtimiadapter.persistence.DomainObjectFactory;
import com.sap.sailing.domain.igtimiadapter.persistence.MongoObjectFactory;
import com.sap.sailing.domain.igtimiadapter.persistence.PersistenceFactory;
import com.sap.sailing.domain.tracking.WindTrackerFactory;
import com.sap.sse.util.impl.ThreadFactoryWithPriority;
/**
* Maintains data about a default {@link Client} that represents this application when interacting with the Igtimi
@@ -43,7 +44,7 @@ public class Activator implements BundleActivator {
private static final String CLIENT_REDIRECT_URI_PROPERTY_NAME = "igtimi.client.redirecturi";
private final Future<IgtimiConnectionFactoryImpl> connectionFactory;
private final Future<IgtimiWindTrackerFactory> windTrackerFactory;
private final ExecutorService executor = Executors.newSingleThreadExecutor();
private final ExecutorService executor = Executors.newSingleThreadExecutor(new ThreadFactoryWithPriority(Thread.NORM_PRIORITY, /* daemon */ true));
public Activator() throws ClientProtocolException, IllegalStateException, IOException, ParseException {
logger.info(getClass().getName()+" constructor");
@@ -97,7 +97,7 @@ public class DependentStartTimeFinderTest {
assertEquals(cAfterB, result.getStartTimeDiff());
assertEquals(expectedDependingOnRaces, result.getRacesDependingOn());
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
raceLogA.add(new RaceLogStartTimeEventImpl(now, author, now, "12", null, 12, new MillisecondsTimePoint(5000)));
finder = new StartTimeFinder(raceLogResolver, raceLogB);
@@ -156,7 +156,7 @@ public class DependentStartTimeFinderTest {
assertEquals(aAfterC, finder.analyze().getStartTimeDiff());
// Check that all resolve correctly after changing some element in cycle
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
raceLogB.add(new RaceLogStartTimeEventImpl(now, author, now, "12", null, 12, new MillisecondsTimePoint(5000)));
// now A -> C -> B
@@ -102,7 +102,7 @@ public class DependentRaceStateTest {
raceLogB.add(new RaceLogDependentStartTimeEventImpl(nowMock, author, nowMock, "12", null, 12,
new SimpleRaceLogIdentifierImpl("A", "", ""), new MillisecondsDurationImpl(5000)));
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
raceLogA.add(new RaceLogStartTimeEventImpl(now, author, now, "12", null, 12, new MillisecondsTimePoint(5000)));
verify(listenerC, times(3)).onStartTimeChanged(stateC);
@@ -124,7 +124,7 @@ public class DependentRaceStateTest {
bTime[0] = state.getStartTime();
}
});
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
raceLogA.add(new RaceLogStartTimeEventImpl(now, author, now, "12", null, 12, now));
assertEquals(now.plus(delta), bTime[0]);
}
@@ -137,7 +137,7 @@ public class DependentRaceStateTest {
raceLogB.add(new RaceLogDependentStartTimeEventImpl(nowMock, author, nowMock, "12", null, 12,
new SimpleRaceLogIdentifierImpl("A", "", ""), new MillisecondsDurationImpl(5000)));
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
raceLogA.add(new RaceLogStartTimeEventImpl(now, author, now, "12", null, 12, new MillisecondsTimePoint(5000)));
raceLogB.add(new RaceLogStartTimeEventImpl(now, author, now, "12", null, 12, new MillisecondsTimePoint(20000)));
@@ -432,7 +432,7 @@ public class RaceLogTest {
RaceLog raceLog = new RaceLogImpl("RaceLogTest", "test-identifier");
UUID client1Id = UUID.randomUUID();
UUID client2Id = UUID.randomUUID();
final MillisecondsTimePoint now = MillisecondsTimePoint.now();
final TimePoint now = MillisecondsTimePoint.now();
RaceLogStartTimeEvent startTimeEvent1 = RaceLogEventFactory.INSTANCE.createStartTimeEvent(now, author, 1, now.plus(1));
Iterable<RaceLogEvent> empty = raceLog.add(startTimeEvent1, client1Id);
assertTrue(Util.isEmpty(empty));
@@ -8,6 +8,7 @@ import java.util.List;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogDependentStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinderResult.ResolutionFailed;
import com.sap.sse.common.Duration;
import com.sap.sse.common.Util;
@@ -31,13 +32,13 @@ public class DependentStartTimeResolver {
final StartTimeFinderResult result;
if (raceLog == null) {
result = new StartTimeFinderResult(dependingOnRaces, null, startTimeDifference);
result = new StartTimeFinderResult(dependingOnRaces, startTimeDifference, ResolutionFailed.RACE_LOG_UNRESOLVED);
} else {
List<SimpleRaceLogIdentifier> extendedDependingOnRaces = new ArrayList<>();
Util.addAll(dependingOnRaces, extendedDependingOnRaces);
extendedDependingOnRaces.add(identifier);
if (containsCycle(extendedDependingOnRaces)) {
result = new StartTimeFinderResult(extendedDependingOnRaces, null, null);
result = new StartTimeFinderResult(extendedDependingOnRaces, null, ResolutionFailed.CYCLIC_DEPENDENCY);
} else {
StartTimeFinder dependentStartTimeFinder = new StartTimeFinder(raceLogResolver, raceLog);
StartTimeFinderResult resultOfDependentRace = dependentStartTimeFinder.analyze(extendedDependingOnRaces);
@@ -7,6 +7,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogDependentStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinderResult.ResolutionFailed;
import com.sap.sse.common.TimePoint;
public class StartTimeFinder extends RaceLogAnalyzer<StartTimeFinderResult> {
@@ -35,7 +36,7 @@ public class StartTimeFinder extends RaceLogAnalyzer<StartTimeFinderResult> {
return dependentStartTimeResolver.internalResolve((RaceLogDependentStartTimeEvent) event, dependingOnRaces);
}
}
return new StartTimeFinderResult(dependingOnRaces, null, null);
return new StartTimeFinderResult(dependingOnRaces, null, null, ResolutionFailed.NO_START_TIME_SET);
} finally {
log.unlockAfterRead();
}
@@ -8,20 +8,42 @@ import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
public class StartTimeFinderResult {
public static enum ResolutionFailed { RACE_LOG_UNRESOLVED, CYCLIC_DEPENDENCY, NO_START_TIME_SET };
private final Iterable<SimpleRaceLogIdentifier> racesDependingOn;
private final TimePoint startTime;
/**
* <code>null</code> if resolution worked
*/
private final ResolutionFailed resolutionFailed;
private Duration startTimeDiff;
public StartTimeFinderResult(Iterable<SimpleRaceLogIdentifier> racesDependingOn, TimePoint startTime, Duration startTimeDiff) {
this(racesDependingOn, startTime, startTimeDiff, /* resolutionFailed */ null);
}
public StartTimeFinderResult(Iterable<SimpleRaceLogIdentifier> racesDependingOn, Duration startTimeDiff, ResolutionFailed resolutionFailed) {
this(racesDependingOn, /* startTime */ null, startTimeDiff, resolutionFailed);
}
public StartTimeFinderResult(Iterable<SimpleRaceLogIdentifier> racesDependingOn, TimePoint startTime, Duration startTimeDiff,
ResolutionFailed resolutionFailed) {
this.startTime = startTime;
this.startTimeDiff = startTimeDiff;
this.racesDependingOn = racesDependingOn;
this.resolutionFailed = resolutionFailed;
}
public StartTimeFinderResult(TimePoint startTime, Duration startTimeDiff) {
this(/* racesDependingOn */ Collections.<SimpleRaceLogIdentifier>emptyList(), startTime, startTimeDiff);
}
public ResolutionFailed getResolutionFailed() {
return resolutionFailed;
}
public Iterable<SimpleRaceLogIdentifier> getRacesDependingOn() {
return racesDependingOn;
}
@@ -47,6 +69,7 @@ public class StartTimeFinderResult {
final int prime = 31;
int result = 1;
result = prime * result + ((racesDependingOn == null) ? 0 : racesDependingOn.hashCode());
result = prime * result + ((resolutionFailed == null) ? 0 : resolutionFailed.hashCode());
result = prime * result + ((startTime == null) ? 0 : startTime.hashCode());
result = prime * result + ((startTimeDiff == null) ? 0 : startTimeDiff.hashCode());
return result;
@@ -66,6 +89,8 @@ public class StartTimeFinderResult {
return false;
} else if (!racesDependingOn.equals(other.racesDependingOn))
return false;
if (resolutionFailed != other.resolutionFailed)
return false;
if (startTime == null) {
if (other.startTime != null)
return false;
@@ -145,6 +145,12 @@ public class NoAddingRaceLogWrapper implements RaceLog {
return innerRaceLog.getFixesIterator(startingAt, inclusive);
}
@Override
public Iterator<RaceLogEvent> getFixesIterator(TimePoint startingAt, boolean startingAtInclusive,
TimePoint endingAt, boolean endingAtInclusive) {
return innerRaceLog.getFixesIterator(startingAt, startingAtInclusive, endingAt, endingAtInclusive);
}
@Override
public Iterator<RaceLogEvent> getRawFixesIterator(TimePoint startingAt, boolean inclusive) {
return innerRaceLog.getRawFixesIterator(startingAt, inclusive);
@@ -5,6 +5,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartTimeEvent;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogAnalyzer;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinderResult;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogEventComparator;
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogImpl;
import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.ReadonlyRacingProcedure;
@@ -102,6 +103,12 @@ public interface ReadonlyRaceState extends RaceStateEventProcessor {
*/
TimePoint getStartTime();
/**
* A more comprehensive answer than {@link #getStartTime()} delivers. The result allows callers to
* understand, in particular, why a start time is not known yet.
*/
StartTimeFinderResult getStartTimeFinderResult();
/**
* If there is a finishing time set for the current pass, returns it. Otherwise <code>null</code>.
*/
@@ -138,4 +145,5 @@ public interface ReadonlyRaceState extends RaceStateEventProcessor {
* If wind has been entered, returns the most recent one. Otherwise <code>null</code>.
*/
Wind getWindFix();
}
@@ -308,6 +308,11 @@ public class ReadonlyRaceStateImpl implements ReadonlyRaceState, RaceLogChangedL
public TimePoint getStartTime() {
return cachedStartTimeFinderResult.getStartTime();
}
@Override
public StartTimeFinderResult getStartTimeFinderResult() {
return cachedStartTimeFinderResult;
}
@Override
public TimePoint getFinishingTime() {
@@ -126,7 +126,23 @@ public interface Track<FixType extends Timed> extends Serializable {
* will be thrown in case the caller has failed to do so.
*/
Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean inclusive);
/**
* Returns an iterator starting at the first fix after <code>startingAt</code> (or "at or after" in case
* <code>inclusive</code> is <code>true</code>) and that ends at the <code>endingAt</code> time point or just before
* in case <code>endingAtIncluive</code> is false. The fixes returned by the iterator are the smoothened fixes (see
* also {@link #getFixes()}, without any smoothening or dampening applied.
*
* Callers must have called {@link #lockForRead()} before calling this method. This will be checked, and an
* exception will be thrown in case the caller has failed to do so.
*
* @param startingAt
* if <code>null</code>, starts with the first fix available
* @param endingAt
* if <code>null</code>., ends with the last fix available
*/
Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean startingAtInclusive, TimePoint endingAt, boolean endingAtInclusive);
/**
* Returns an iterator starting at the first raw fix after <code>startingAt</code> (or "at or after" in case
* <code>inclusive</code> is <code>true</code>). The fixes returned by the iterator are the raw fixes (see also
@@ -259,6 +259,22 @@ public class TrackImpl<FixType extends Timed> implements Track<FixType> {
return result;
}
@Override
public Iterator<FixType> getFixesIterator(TimePoint startingAt, boolean startingAtInclusive, TimePoint endingAt,
boolean endingAtInclusive) {
assertReadLock();
NavigableSet<FixType> set = getInternalFixes();
if (startingAt != null && endingAt != null) {
set = set.subSet(getDummyFix(startingAt), startingAtInclusive, getDummyFix(endingAt), endingAtInclusive);
} else if (endingAt != null) {
set = set.headSet(getDummyFix(endingAt), endingAtInclusive);
} else if (startingAt != null) {
set = set.tailSet(getDummyFix(startingAt), startingAtInclusive);
}
Iterator<FixType> result = set.iterator();
return result;
}
@Override
public Iterator<FixType> getFixesDescendingIterator(TimePoint startingAt, boolean inclusive) {
assertReadLock();
@@ -336,10 +336,9 @@ public class DomainFactoryImpl implements DomainFactory {
Regatta regatta = raceIDToRegattaCache.get(raceID);
if (regatta != null) {
Set<RaceDefinition> toRemove = new HashSet<RaceDefinition>();
for (RaceDefinition race : regatta.getAllRaces()) {
if (race.getName().equals(raceID)) {
toRemove.add(race);
}
RaceDefinition race = regatta.getRaceByName(raceID);
if (race != null) {
toRemove.add(race);
}
for (RaceDefinition raceToRemove : toRemove) {
regatta.removeRace(raceToRemove);
@@ -13,12 +13,13 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class LeaderboardDTOCalculationReuseCacheTest {
@Test
public void testNullWindCaching() {
final MillisecondsTimePoint now = MillisecondsTimePoint.now();
final TimePoint now = MillisecondsTimePoint.now();
final LeaderboardDTOCalculationReuseCache cache = new LeaderboardDTOCalculationReuseCache(now);
final Competitor competitor = mock(Competitor.class);
@SuppressWarnings("unchecked")
@@ -25,6 +25,7 @@ import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.tractracadapter.ReceiverType;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.tractrac.model.lib.api.event.CreateModelException;
@@ -43,7 +44,7 @@ public class LeaderboardForKielWeekTest extends OnlineTracTracBasedTest {
public void leaderboardWithOneRaceTest() throws URISyntaxException, NoWindException, IOException, InterruptedException, SubscriberInitializationException, CreateModelException {
leaderboard = new FlexibleLeaderboardImpl("Kiel Week 2011 505s", new ThresholdBasedResultDiscardingRuleImpl(new int[] { 3, 6 }),
new LowPoint(), null);
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
loadRace("event_20110609_KielerWoch-505_Race_2.txt", "event_20110609_KielerWoch-505_Race_2.mtb"); // 505 Race 2
Competitor kevin = getCompetitorByName("Kevin");
RaceColumn column = leaderboard.addRace(getTrackedRace(), "Test Race 1", /* medalRace */false);
@@ -120,7 +120,7 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
leaderboard.addRaceColumn(columnName, /* medalRace */ true);
leaderboard.addRace(testRaces.iterator().next(), columnName, /* medalRace */false);
final Set<String> emptySet = Collections.emptySet();
final MillisecondsTimePoint now = MillisecondsTimePoint.now();
final TimePoint now = MillisecondsTimePoint.now();
final TrackedRegattaRegistry trackedRegattaRegistry = new TrackedRegattaRegistry() {
@Override
public DynamicTrackedRegatta getOrCreateTrackedRegatta(Regatta regatta) {
@@ -23,7 +23,7 @@ import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class LineAnalysisTest extends TrackBasedTest {
private MillisecondsTimePoint now;
private TimePoint now;
private DynamicTrackedRace trackedRace;
@Before
@@ -34,6 +34,7 @@ import com.sap.sailing.domain.leaderboard.impl.FlexibleLeaderboardImpl;
import com.sap.sailing.domain.leaderboard.impl.LowPoint;
import com.sap.sailing.domain.leaderboard.impl.ThresholdBasedResultDiscardingRuleImpl;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
@@ -49,6 +50,7 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
* was <code>null</code> when {@link RaceColumn} was linked to {@link TrackedRace}.
*
* @author Alexander Ries (D062114)
* @author Axel Uhl (D043530)
*
*/
public class RaceExecutionOrderProvdiderAttachDetachTest extends TrackBasedTest {
@@ -69,7 +71,7 @@ public class RaceExecutionOrderProvdiderAttachDetachTest extends TrackBasedTest
private final String RACECOLUMN_FLEXIBLELEADERBOARD = "TestFlexibleLeaderboardRaceColumn";
@Test
public void testRaceExecutionOrderProviderAttachDetachWithRaceCollumn() {
public void testRaceExecutionOrderProviderAttachDetachWithRaceColumn() {
trackedRace = createTestTrackedRace(REGATTA, RACE, BOATCLASS, Collections.<Competitor> emptyList(),
MillisecondsTimePoint.now());
flexibleLeaderboard = new FlexibleLeaderboardImpl(FLEXIBLELEADERBOARD,
@@ -215,4 +217,63 @@ public class RaceExecutionOrderProvdiderAttachDetachTest extends TrackBasedTest
/* startDate */null, /* endDate */null, seriesSet, false, scoringScheme, UUID.randomUUID(), null,
OneDesignRankingMetric::new);
}
/**
* See bug 3173. When a race is linked more than once to a fleet in a series, the "previous race"
* definition's transitive closure may become cyclic, and a race may be considered its own
* direct or transitive predecessor. This can lead to endless recursion in the {@link TrackedRace#takesWindFix(Wind)}
* implementation which traverses a race's predecessors recursively.
*/
@Test
public void testCyclicPreviousRacesSequence() {
createTestSetupWithRegattaAndSeries(/* linkSeriesToRegatta */ true);
final RaceColumnInSeries r1 = series.addRaceColumn("R1", /* trackedRegattaRegistry */ null);
final RaceColumnInSeries r2 = series.addRaceColumn("R2", /* trackedRegattaRegistry */ null);
final RaceColumnInSeries r3 = series.addRaceColumn("R2", /* trackedRegattaRegistry */ null);
final RaceColumnInSeries r4 = series.addRaceColumn("R2", /* trackedRegattaRegistry */ null);
final TimePoint startOfFirstRace = MillisecondsTimePoint.now();
final TimePoint endOfFirstRace = startOfFirstRace.plus(Duration.ONE_MINUTE);
final TimePoint startOfSecondRace = endOfFirstRace.plus(Duration.ONE_MINUTE);
final TimePoint endOfSecondRace = startOfSecondRace.plus(Duration.ONE_MINUTE);
final TimePoint startOfThirdRace = endOfSecondRace.plus(Duration.ONE_MINUTE);
final TimePoint endOfThirdRace = startOfThirdRace.plus(Duration.ONE_MINUTE);
final TrackedRace tr1 = createTrackedRace("FirstRace", startOfFirstRace, endOfFirstRace);
final TrackedRace tr2 = createTrackedRace("SecondRace", startOfSecondRace, endOfSecondRace);
final TrackedRace tr3 = createTrackedRace("ThirdRace", startOfThirdRace, endOfThirdRace);
r1.setTrackedRace(fleet, tr3);
r2.setTrackedRace(fleet, tr1);
r3.setTrackedRace(fleet, tr2);
r4.setTrackedRace(fleet, tr3); // produce a cycle because now transitively tr1 has itself as a predecessor
assertTrue(tr3.takesWindFix(new WindImpl(new DegreePosition(12, 13),
startOfThirdRace.minus(TrackedRaceImpl.EXTRA_LONG_TIME_BEFORE_START_TO_TRACK_WIND_MILLIS.divide(2)),
new KnotSpeedWithBearingImpl(/* speedInKnots */18, new DegreeBearingImpl(185)))));
}
/**
* See bug 3173. When a race is linked more than once to a fleet in a series, the "previous race"
* definition's transitive closure may become cyclic, and a race may be considered its own
* direct or transitive predecessor. This can lead to endless recursion in the {@link TrackedRace#takesWindFix(Wind)}
* implementation which traverses a race's predecessors recursively.
*/
@Test
public void testCyclicPreviousRacesSequenceUsingSingleRace() {
createTestSetupWithRegattaAndSeries(/* linkSeriesToRegatta */ true);
final RaceColumnInSeries r1 = series.addRaceColumn("R1", /* trackedRegattaRegistry */ null);
final RaceColumnInSeries r2 = series.addRaceColumn("R2", /* trackedRegattaRegistry */ null);
final TimePoint startOfFirstRace = MillisecondsTimePoint.now();
final TimePoint endOfFirstRace = startOfFirstRace.plus(Duration.ONE_MINUTE);
final TrackedRace tr1 = createTrackedRace("FirstRace", startOfFirstRace, endOfFirstRace);
r1.setTrackedRace(fleet, tr1);
r2.setTrackedRace(fleet, tr1);
assertTrue(tr1.takesWindFix(new WindImpl(new DegreePosition(12, 13),
startOfFirstRace.minus(TrackedRaceImpl.EXTRA_LONG_TIME_BEFORE_START_TO_TRACK_WIND_MILLIS.divide(2)),
new KnotSpeedWithBearingImpl(/* speedInKnots */18, new DegreeBearingImpl(185)))));
}
private DynamicTrackedRace createTrackedRace(final String name, final TimePoint startOfRace, final TimePoint endOfRace) {
DynamicTrackedRace trackedRace = createTestTrackedRace(REGATTA, name, BOATCLASS, Collections.<Competitor> emptyList(), startOfRace);
trackedRace.setStartOfTrackingReceived(startOfRace);
trackedRace.setEndOfTrackingReceived(endOfRace);
return trackedRace;
}
}
@@ -3,7 +3,9 @@ package com.sap.sailing.domain.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.HashSet;
import java.util.Random;
@@ -11,12 +13,15 @@ import java.util.Set;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicInteger;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
import com.sap.sse.util.SmartFutureCache;
import com.sap.sse.util.SmartFutureCache.CacheUpdater;
@@ -24,6 +29,8 @@ import com.sap.sse.util.SmartFutureCache.EmptyUpdateInterval;
import com.sap.sse.util.SmartFutureCache.UpdateInterval;
public class SmartFutureCacheTest {
@Rule public Timeout AbstractTracTracLiveTestTimeout = new Timeout(2 * 60 * 1000);
@Test
public void testPerformanceOfGetAndCall() {
SmartFutureCache<String, String, EmptyUpdateInterval> sfc = new SmartFutureCache<String, String, SmartFutureCache.EmptyUpdateInterval>(
@@ -42,6 +49,34 @@ public class SmartFutureCacheTest {
}
System.out.println("testPerformanceOfGetAndCall took "+(System.currentTimeMillis()-start)+"ms");
}
@Test
public void testExceptionInComputeCacheUpdate() {
final boolean[] throwException = new boolean[1];
throwException[0] = true;
SmartFutureCache<String, String, EmptyUpdateInterval> sfc = new SmartFutureCache<String, String, SmartFutureCache.EmptyUpdateInterval>(
new SmartFutureCache.AbstractCacheUpdater<String, String, SmartFutureCache.EmptyUpdateInterval>() {
@Override
public String computeCacheUpdate(String key, EmptyUpdateInterval updateInterval) {
if (throwException[0]) {
throw new NullPointerException("Humba");
} else {
return "Humba";
}
}
}, "SmartFutureCacheTest.testExceptionInComputeCacheUpdate");
sfc.triggerUpdate("humba", /* update interval */ null);
try {
// during the first call, expecting exception
sfc.get("humba", /* waitForLatest */ true);
fail("Expected RuntimeException because computeCacheUpdate threw one");
} catch (RuntimeException expected) {
assertSame(ExecutionException.class, expected.getCause().getClass());
}
throwException[0] = false;
sfc.triggerUpdate("humba", /* update interval */ null);
assertEquals("Humba", sfc.get("humba", /* waitForLatest */ true));
}
@Test
public void testSuspendAndResume() {
@@ -34,7 +34,7 @@ import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class StarbordSideOfStartLineRecognitionTest {
private MillisecondsTimePoint now;
private TimePoint now;
@Before
public void setUp() {
@@ -25,6 +25,7 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sse.common.Color;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
@@ -56,7 +57,7 @@ public class TackTest extends StoredTrackBasedTestWithTrackedRace {
@Test
public void testStarboardTack() throws NoWindException {
DynamicGPSFixTrack<Competitor, GPSFixMoving> hassosTrack = getTrackedRace().getTrack(competitor);
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
hassosTrack.addGPSFix(new GPSFixMovingImpl(new DegreePosition(54.4680424, 10.234451), now,
new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(45))));
assertEquals(Tack.PORT, getTrackedRace().getTack(competitor, now));
@@ -65,7 +66,7 @@ public class TackTest extends StoredTrackBasedTestWithTrackedRace {
@Test
public void testPortTack() throws NoWindException {
DynamicGPSFixTrack<Competitor, GPSFixMoving> hassosTrack = getTrackedRace().getTrack(competitor);
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
hassosTrack.addGPSFix(new GPSFixMovingImpl(new DegreePosition(54.4680424, 10.234451), now,
new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(270))));
assertEquals(Tack.STARBOARD, getTrackedRace().getTack(competitor, now));
@@ -74,7 +75,7 @@ public class TackTest extends StoredTrackBasedTestWithTrackedRace {
@Test
public void testStarboardTackForZeroDifference() throws NoWindException {
DynamicGPSFixTrack<Competitor, GPSFixMoving> hassosTrack = getTrackedRace().getTrack(competitor);
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
hassosTrack.addGPSFix(new GPSFixMovingImpl(new DegreePosition(54.4680424, 10.234451), now,
new KnotSpeedWithBearingImpl(10, new DegreeBearingImpl(180))));
assertEquals(Tack.PORT, getTrackedRace().getTack(competitor, now));
@@ -0,0 +1,81 @@
package com.sap.sailing.domain.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import org.junit.Test;
import com.sap.sailing.domain.tractracadapter.TracTracRaceTracker;
import com.sap.sailing.domain.tractracadapter.impl.TracTracRaceTrackerImpl;
/**
* See bug 3174: the addition of the "random" element in the params_url around summer 2015 has killed our
* comparison of {@link TracTracRaceTracker#getID()} objects. Although two URLs differ in this "random" parameter,
* they still refer to the same race.<p>
*
* This test asserts that two TracTrac race tracker IDs differing only in the "random" URL parameter are still considered
* equal.
*
* @author Axel Uhl (d043530)
*
*/
public class TracTracIgnoreRandomInParamsURLTest {
@Test
public void testEqualRaceTrackerIDsThatOnlyDifferInRandomParam() throws MalformedURLException, URISyntaxException {
Object id1 = TracTracRaceTrackerImpl.createID(new URL("http://event.tractrac.com/events/event_20150707_erEuropean/clientparams.php?event=event_20150707_erEuropean&race=777b1420-07c1-0133-0cec-60a44ce903c3&random=769909699"),
new URI("tcp://event.tractrac.com:4400"), new URI("tcp://event.tractrac.com:4401"));
Object id2 = TracTracRaceTrackerImpl.createID(new URL("http://event.tractrac.com/events/event_20150707_erEuropean/clientparams.php?event=event_20150707_erEuropean&race=777b1420-07c1-0133-0cec-60a44ce903c3&random=1794448899"),
new URI("tcp://event.tractrac.com:4400"), new URI("tcp://event.tractrac.com:4401"));
assertEquals(id1, id2);
}
@Test
public void testEqualRaceTrackerIDsThatOnlyDifferInRandomParamAndNotLastParameter() throws MalformedURLException, URISyntaxException {
Object id1 = TracTracRaceTrackerImpl.createID(new URL("http://event.tractrac.com/events/event_20150707_erEuropean/clientparams.php?event=event_20150707_erEuropean&race=777b1420-07c1-0133-0cec-60a44ce903c3&random=769909699&a=b"),
new URI("tcp://event.tractrac.com:4400"), new URI("tcp://event.tractrac.com:4401"));
Object id2 = TracTracRaceTrackerImpl.createID(new URL("http://event.tractrac.com/events/event_20150707_erEuropean/clientparams.php?event=event_20150707_erEuropean&race=777b1420-07c1-0133-0cec-60a44ce903c3&random=1794448899&a=b"),
new URI("tcp://event.tractrac.com:4400"), new URI("tcp://event.tractrac.com:4401"));
assertEquals(id1, id2);
}
@Test
public void testEqualRaceTrackerIDsThatOnlyDifferInRandomParamWithFragment() throws MalformedURLException, URISyntaxException {
Object id1 = TracTracRaceTrackerImpl.createID(new URL("http://event.tractrac.com/events/event_20150707_erEuropean/clientparams.php?event=event_20150707_erEuropean&race=777b1420-07c1-0133-0cec-60a44ce903c3&random=769909699&a=b#HumbaHumba"),
new URI("tcp://event.tractrac.com:4400"), new URI("tcp://event.tractrac.com:4401"));
Object id2 = TracTracRaceTrackerImpl.createID(new URL("http://event.tractrac.com/events/event_20150707_erEuropean/clientparams.php?event=event_20150707_erEuropean&race=777b1420-07c1-0133-0cec-60a44ce903c3&random=1794448899&a=b#HumbaHumba"),
new URI("tcp://event.tractrac.com:4400"), new URI("tcp://event.tractrac.com:4401"));
assertEquals(id1, id2);
}
@Test
public void testEqualRaceTrackerIDsThatOnlyDifferInRandomParamAndFragment() throws MalformedURLException, URISyntaxException {
Object id1 = TracTracRaceTrackerImpl.createID(new URL("http://event.tractrac.com/events/event_20150707_erEuropean/clientparams.php?event=event_20150707_erEuropean&race=777b1420-07c1-0133-0cec-60a44ce903c3&random=769909699&a=b#HumbaTrala"),
new URI("tcp://event.tractrac.com:4400"), new URI("tcp://event.tractrac.com:4401"));
Object id2 = TracTracRaceTrackerImpl.createID(new URL("http://event.tractrac.com/events/event_20150707_erEuropean/clientparams.php?event=event_20150707_erEuropean&race=777b1420-07c1-0133-0cec-60a44ce903c3&random=1794448899&a=b#HumbaHumba"),
new URI("tcp://event.tractrac.com:4400"), new URI("tcp://event.tractrac.com:4401"));
assertFalse(id1.equals(id2));
}
@Test
public void testEqualRaceTrackerIDsThatDifferInRaceIDAndRandomParam() throws MalformedURLException, URISyntaxException {
Object id1 = TracTracRaceTrackerImpl.createID(new URL("http://event.tractrac.com/events/event_20150707_erEuropean/clientparams.php?event=event_20150707_erEuropean&race=777b1420-07c1-0132-0cec-60a44ce903c3&random=769909699"),
new URI("tcp://event.tractrac.com:4400"), new URI("tcp://event.tractrac.com:4401"));
Object id2 = TracTracRaceTrackerImpl.createID(new URL("http://event.tractrac.com/events/event_20150707_erEuropean/clientparams.php?event=event_20150707_erEuropean&race=777b1420-07c1-0133-0cec-60a44ce903c3&random=1794448899"),
new URI("tcp://event.tractrac.com:4400"), new URI("tcp://event.tractrac.com:4401"));
assertFalse(id1.equals(id2));
}
@Test
public void testEqualRaceTrackerIDsThatDifferInLiveURI() throws MalformedURLException, URISyntaxException {
Object id1 = TracTracRaceTrackerImpl.createID(new URL("http://event.tractrac.com/events/event_20150707_erEuropean/clientparams.php?event=event_20150707_erEuropean&race=777b1420-07c1-0132-0cec-60a44ce903c3&random=769909699"),
new URI("tcp://event.tractrac.com:4400"), new URI("tcp://event.tractrac.com:4401"));
Object id2 = TracTracRaceTrackerImpl.createID(new URL("http://event.tractrac.com/events/event_20150707_erEuropean/clientparams.php?event=event_20150707_erEuropean&race=777b1420-07c1-0132-0cec-60a44ce903c3&random=769909699"),
new URI("tcp://event.tractrac.com:4412"), new URI("tcp://event.tractrac.com:4413"));
assertFalse(id1.equals(id2));
}
}
@@ -758,7 +758,7 @@ public class TrackTest {
Bearing bearing = new DegreeBearingImpl(123);
Speed speed = new KnotSpeedImpl(7);
Position p = new DegreePosition(0, 0);
final MillisecondsTimePoint now = MillisecondsTimePoint.now();
final TimePoint now = MillisecondsTimePoint.now();
TimePoint start = now;
final int steps = 10;
TimePoint next = null;
@@ -858,7 +858,7 @@ public class TrackTest {
Bearing bearing = new DegreeBearingImpl(123);
Speed speed = new KnotSpeedImpl(7);
Position p = new DegreePosition(0, 0);
final MillisecondsTimePoint now = MillisecondsTimePoint.now();
final TimePoint now = MillisecondsTimePoint.now();
TimePoint start = now;
final int steps = 10;
TimePoint next = null;
@@ -916,7 +916,7 @@ public class TrackTest {
Bearing bearing = new DegreeBearingImpl(123);
Speed speed = new KnotSpeedImpl(7);
Position p = new DegreePosition(0, 0);
final MillisecondsTimePoint now = MillisecondsTimePoint.now();
final TimePoint now = MillisecondsTimePoint.now();
TimePoint start = now;
final int steps = 10;
TimePoint next = null;
@@ -67,7 +67,7 @@ public class TrackedRaceCenterTest {
@Test
public void testSimpleAverage() {
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
Position mark1Pos = new DegreePosition(10, 10);
mark1Track.add(new GPSFixImpl(mark1Pos, now));
Position mark2Pos = new DegreePosition(20, 10);
@@ -85,7 +85,7 @@ public class TrackedRaceCenterTest {
DynamicGPSFixTrackImpl<Mark> mark3Track = new DynamicGPSFixTrackImpl<Mark>(mark3, /* millisecondsOverWhichToAverage */ 10);
when(trackedRace.getOrCreateTrack(mark3)).thenReturn(mark3Track);
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
Position mark1Pos = new DegreePosition(0, 0);
mark1Track.add(new GPSFixImpl(mark1Pos, now));
Position mark2Pos = new DegreePosition(0, 10);
@@ -100,7 +100,7 @@ public class WindEstimationLockingUnderLoadTest {
} else {
double directionInDegrees = 360*Math.random();
double confidence = Math.random();
final MillisecondsTimePoint now = MillisecondsTimePoint.now();
final TimePoint now = MillisecondsTimePoint.now();
result = new WindWithConfidenceImpl<TimePoint>(new WindImpl(new DegreePosition(49, 8), now, new KnotSpeedWithBearingImpl(speedInKnots, new DegreeBearingImpl(directionInDegrees))),
confidence, now, /* useSpeed */ true);
}
@@ -137,7 +137,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
@Test
public void testCombinedWindTrack() throws NoWindException {
initRace(4, new int[] { 1, 1, 2, 2 }, new MillisecondsTimePoint(new GregorianCalendar(2011, 05, 23).getTime()));
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
setBearingForCompetitor(competitors.get(0), now, 315);
setBearingForCompetitor(competitors.get(1), now, 45); // on the same tack, should give no read-out
setBearingForCompetitor(competitors.get(2), now, 135);
@@ -253,7 +253,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
@Test
public void testWindEstimationForSimpleTracks() throws NoWindException {
initRace(2, new int[] { 1, 1 }, new MillisecondsTimePoint(new GregorianCalendar(2011, 05, 23).getTime()));
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
setBearingForCompetitor(competitors.get(0), now, 320);
setBearingForCompetitor(competitors.get(1), now, 50);
Wind estimatedWindDirection = getTrackedRace().getEstimatedWindDirection(now);
@@ -263,7 +263,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
@Test
public void testWindEstimationForSimpleTracksWithOneAncientFixToBeSuppressedByLowConfidence() throws NoWindException {
initRace(3, new int[] { 1, 1, 1 }, new MillisecondsTimePoint(new GregorianCalendar(2011, 05, 23).getTime()));
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
setBearingForCompetitor(competitors.get(0), now, 320);
setBearingForCompetitor(competitors.get(1), now, 50);
setBearingForCompetitor(competitors.get(2), new MillisecondsTimePoint(0), 100); // this shouldn't disturb the estimation because it's too old
@@ -275,7 +275,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
public void testWindEstimationForSimpleTracksWithOneFixNearMarkPassingToBeSuppressedByLowConfidence() throws NoWindException {
TimePoint markPassingTimePoint = new MillisecondsTimePoint(new GregorianCalendar(2011, 05, 23).getTime());
initRace(3, new int[] { 1, 1, 1 }, markPassingTimePoint);
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
setBearingForCompetitor(competitors.get(0), now, 320);
setBearingForCompetitor(competitors.get(1), now, 50);
setBearingForCompetitor(competitors.get(2), markPassingTimePoint, 100); // this shouldn't disturb the estimation because it's too old
@@ -286,7 +286,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
@Test
public void testWindEstimationForTwoBoatsOnSameTack() throws NoWindException {
initRace(2, new int[] { 1, 1 }, new MillisecondsTimePoint(new GregorianCalendar(2011, 05, 23).getTime()));
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
setBearingForCompetitor(competitors.get(0), now, 320);
setBearingForCompetitor(competitors.get(1), now, 330); // on the same tack, should give no read-out
Wind nullWind = getTrackedRace().getEstimatedWindDirection(now);
@@ -298,7 +298,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
@Test
public void testWindEstimationForFourBoatsOnSameTack() throws NoWindException {
initRace(4, new int[] { 1, 1, 2, 2 }, new MillisecondsTimePoint(new GregorianCalendar(2011, 05, 23).getTime()));
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
setBearingForCompetitor(competitors.get(0), now, 320);
setBearingForCompetitor(competitors.get(1), now, 330); // on the same tack, should give no read-out
setBearingForCompetitor(competitors.get(2), now, 135);
@@ -312,7 +312,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
@Test
public void testWindEstimationForFourBoatsWithUpwindOnSameTack() throws NoWindException {
initRace(4, new int[] { 1, 1, 2, 2 }, new MillisecondsTimePoint(new GregorianCalendar(2011, 05, 23).getTime()));
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
setBearingForCompetitor(competitors.get(0), now, 320);
setBearingForCompetitor(competitors.get(1), now, 330); // on the same tack, should give no read-out
setBearingForCompetitor(competitors.get(2), now, 135);
@@ -324,7 +324,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
@Test
public void testWindEstimationForFourBoats() throws NoWindException {
initRace(4, new int[] { 1, 1, 2, 2 }, new MillisecondsTimePoint(new GregorianCalendar(2011, 05, 23).getTime()));
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
setBearingForCompetitor(competitors.get(0), now, 315);
setBearingForCompetitor(competitors.get(1), now, 45); // on the same tack, should give no read-out
setBearingForCompetitor(competitors.get(2), now, 135);
@@ -1,15 +1,6 @@
package com.sap.sailing.domain.tractracadapter;
import java.net.URI;
import java.net.URL;
import com.sap.sailing.domain.tracking.RaceTracker;
import com.sap.sse.common.Util;
public interface TracTracRaceTracker extends RaceTracker {
/**
* returns the paramURL, liveURI and storedURI for the TracTrac connection maintained by this tracker
*/
Util.Triple<URL, URI, URI> getID();
}
@@ -16,6 +16,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -122,7 +123,7 @@ public class DomainFactoryImpl implements DomainFactory {
* Maps from the TracTrac race UUIDs to the domain model's {@link RaceDefinition} objects that represent the race
* identified by that UUID
*/
private final Map<UUID, RaceDefinition> raceCache = new HashMap<>();
private final ConcurrentHashMap<UUID, RaceDefinition> raceCache = new ConcurrentHashMap<>();
private final MetadataParser metadataParser;
@@ -328,11 +329,11 @@ public class DomainFactoryImpl implements DomainFactory {
@Override
public RaceDefinition getAndWaitForRaceDefinition(UUID raceId, long timeoutInMilliseconds) {
long start = System.currentTimeMillis();
synchronized (raceCache) {
RaceDefinition result = raceCache.get(raceId);
boolean interrupted = false;
while ((timeoutInMilliseconds == -1 || System.currentTimeMillis()-start < timeoutInMilliseconds) && !interrupted && result == null) {
try {
RaceDefinition result = raceCache.get(raceId);
boolean interrupted = false;
while ((timeoutInMilliseconds == -1 || System.currentTimeMillis()-start < timeoutInMilliseconds) && !interrupted && result == null) {
try {
synchronized (raceCache) {
if (timeoutInMilliseconds == -1) {
raceCache.wait();
} else {
@@ -341,13 +342,13 @@ public class DomainFactoryImpl implements DomainFactory {
raceCache.wait(timeToWait);
}
}
result = raceCache.get(raceId);
} catch (InterruptedException e) {
interrupted = true;
}
result = raceCache.get(raceId);
} catch (InterruptedException e) {
interrupted = true;
}
return result;
}
return result;
}
@Override
@@ -84,7 +84,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
}
@Override
public com.sap.sse.common.Util.Triple<URL, URI, URI> getTrackerID() {
public Object getTrackerID() {
return TracTracRaceTrackerImpl.createID(paramURL, liveURI, storedURI);
}
@@ -12,7 +12,9 @@ import java.net.URISyntaxException;
import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.StringJoiner;
import java.util.UUID;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
@@ -194,12 +196,12 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
private final Set<RaceDefinition> races;
private final DynamicTrackedRegatta trackedRegatta;
private TrackedRaceStatus lastStatus;
private HashMap<Util.Triple<URL, URI, URI>, Util.Pair<Integer, Float>> lastProgressPerID;
private Map<Object, Util.Pair<Integer, Float>> lastProgressPerID;
/**
* paramURL, liveURI and storedURI for TracTrac connection
* paramURL, liveURI and storedURI for TracTrac connection, voided of "random" part
*/
private final Util.Triple<URL, URI, URI> urls;
private final Object id;
/**
* Tells if this tracker was created with a valid live URI. If not, the tracker will stop and unregister itself
@@ -315,12 +317,12 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
this.trackedRegattaRegistry = trackedRegattaRegistry;
this.tractracRace = tractracRace;
this.tractracEvent = tractracRace.getEvent();
urls = createID(paramURL, liveURI, storedURI);
this.id = createID(paramURL, liveURI, storedURI);
isLiveTracking = liveURI != null;
this.races = new HashSet<RaceDefinition>();
this.gpsFixStore = gpsFixStore;
this.domainFactory = domainFactory;
this.lastProgressPerID = new HashMap<Util.Triple<URL, URI, URI>, Util.Pair<Integer, Float>>();
this.lastProgressPerID = new HashMap<>();
if (simulateWithStartTimeNow) {
simulator = new Simulator(windStore);
// don't write the transformed wind fixes into the DB again... see also bug 1974
@@ -461,13 +463,48 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
return trackedRegatta;
}
static Util.Triple<URL, URI, URI> createID(URL paramURL, URI liveURI, URI storedURI) {
return new Util.Triple<URL, URI, URI>(paramURL, liveURI, storedURI);
public static Object createID(URL paramURL, URI liveURI, URI storedURI) {
URL paramURLStrippedOfRandomParam;
if (paramURL == null) {
paramURLStrippedOfRandomParam = null;
} else {
final String query = paramURL.getQuery();
if (query == null) {
paramURLStrippedOfRandomParam = paramURL;
} else {
final StringJoiner stringJoiner = new StringJoiner("&", "?", "");
stringJoiner.setEmptyValue("");
String[] queryParams = query.split("&");
for (String queryParam : queryParams) {
String[] nameValue = queryParam.split("=");
if (!"random".equalsIgnoreCase(nameValue[0])) {
final StringBuilder param = new StringBuilder();
param.append(nameValue[0]);
if (nameValue.length > 1) {
param.append('=');
param.append(nameValue[1]);
}
stringJoiner.add(param.toString());
}
}
try {
paramURLStrippedOfRandomParam = new URL(paramURL.getProtocol(), paramURL.getHost(), paramURL.getPort(),
paramURL.getPath()+stringJoiner.toString()+(paramURL.getRef() == null || paramURL.getRef().isEmpty() ?
"" : ("#"+paramURL.getRef())));
} catch (MalformedURLException e) {
// this is pretty strange as we only removed one parameter; log and continue with original URL as a default
logger.log(Level.SEVERE, "Error trying to strip the \"random\" parameter from the TracTrac params_url "+
paramURL, e);
paramURLStrippedOfRandomParam = paramURL;
}
}
}
return new Util.Triple<URL, URI, URI>(paramURLStrippedOfRandomParam, liveURI, storedURI);
}
@Override
public Util.Triple<URL, URI, URI> getID() {
return urls;
public Object getID() {
return id;
}
@Override
@@ -5,6 +5,7 @@ import java.io.ObjectInputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.base.Competitor;
@@ -20,6 +21,8 @@ import com.sap.sse.common.Util;
public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implements RaceColumn {
private static final long serialVersionUID = -7801617988982540470L;
private static final Logger logger = Logger.getLogger(AbstractRaceColumn.class.getName());
private TrackedRaces trackedRaces;
private Map<Fleet, RaceIdentifier> raceIdentifiers;
@@ -70,6 +73,8 @@ public abstract class AbstractRaceColumn extends SimpleAbstractRaceColumn implem
if (trackedRace != null) {
this.trackedRaces.put(fleet, trackedRace);
this.setRaceIdentifier(fleet, trackedRace.getRaceIdentifier());
logger.info(String.format("Linked race column %s with tracked race %s.", this.getName(),
trackedRace.getRace() == null ? "null" : trackedRace.getRace().getName()));
}
}
if (trackedRace != null) {
@@ -6,11 +6,14 @@ import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
@@ -77,7 +80,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
private static final Logger logger = Logger.getLogger(RegattaImpl.class.getName());
private static final long serialVersionUID = 6509564189552478869L;
private Set<RaceDefinition> races;
private ConcurrentHashMap<String, RaceDefinition> races;
private final BoatClass boatClass;
private transient Set<RegattaListener> regattaListeners;
private List<? extends Series> series;
@@ -178,7 +181,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
this.useStartTimeInference = useStartTimeInference;
this.id = id;
this.raceLogStore = raceLogStore;
races = new HashSet<RaceDefinition>();
races = new ConcurrentHashMap<>();
regattaListeners = new HashSet<RegattaListener>();
raceColumnListeners = new RaceColumnListeners();
this.boatClass = boatClass;
@@ -256,7 +259,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
MasterDataImportInformation masterDataImportInformation = ongoingMasterDataImportInformation.get();
if (masterDataImportInformation != null) {
raceLogStore = masterDataImportInformation.getRaceLogStore();
races = new HashSet<RaceDefinition>();
races = new ConcurrentHashMap<>();
} else {
raceLogStore = EmptyRaceLogStore.INSTANCE;
}
@@ -309,9 +312,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
@Override
public Iterable<RaceDefinition> getAllRaces() {
synchronized (races) {
return new ArrayList<RaceDefinition>(races);
}
return races.values();
}
@Override
@@ -326,12 +327,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
@Override
public RaceDefinition getRaceByName(String raceName) {
for (RaceDefinition r : getAllRaces()) {
if (r.getName().equals(raceName)) {
return r;
}
}
return null;
return races.get(raceName);
}
@Override
@@ -340,9 +336,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
if (getBoatClass() != null && race.getBoatClass() != getBoatClass()) {
throw new IllegalArgumentException("Boat class "+race.getBoatClass()+" doesn't match regatta's boat class "+getBoatClass());
}
synchronized (races) {
races.add(race);
}
races.put(race.getName(), race);
synchronized (regattaListeners) {
for (RegattaListener l : regattaListeners) {
l.raceAdded(this, race);
@@ -352,10 +346,8 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
@Override
public void removeRace(RaceDefinition race) {
synchronized (races) {
logger.info("Removing race "+race.getName()+" from regatta "+getName()+" ("+hashCode()+")");
races.remove(race);
}
logger.info("Removing race "+race.getName()+" from regatta "+getName()+" ("+hashCode()+")");
races.remove(race.getName());
synchronized (regattaListeners) {
for (RegattaListener l : regattaListeners) {
l.raceRemoved(this, race);
@@ -678,15 +670,27 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
final Map<Fleet, Iterable<? extends RaceColumn>> result = new HashMap<>();
final Iterable<? extends Series> mySeries = getSeries();
if (mySeries != null) {
for (Series currentSeries : mySeries) {
if (currentSeries.getFleets() != null) {
for (Fleet fleet : currentSeries.getFleets()) {
if (currentSeries.getRaceColumns() != null) {
result.put(fleet, currentSeries.getRaceColumns());
boolean concurrentlyModified = false;
do {
try {
for (Series currentSeries : mySeries) {
if (currentSeries.getFleets() != null) {
for (Fleet fleet : currentSeries.getFleets()) {
if (currentSeries.getRaceColumns() != null) {
result.put(fleet, currentSeries.getRaceColumns());
}
}
}
}
} catch (ConcurrentModificationException e) {
// getSeries() returns a live collection, and Series.getRaceColumns() does so, too.
// In the unlikely event of a modification is applied to either of these structures while iterating, an exception
// will be thrown. We catch and log it here and try again.
logger.log(Level.INFO,
"Got a ConcurrentModificationException while trying to update the RaceExecutionOrderCache", e);
concurrentlyModified = true;
}
}
} while (concurrentlyModified);
}
return result;
}
@@ -74,7 +74,7 @@ public class LeaderboardDTOCache implements LeaderboardCache {
private static final Executor computeLeadearboardByNameExecutor = new ThreadPoolExecutor(/* corePoolSize */ THREAD_POOL_SIZE,
/* maximumPoolSize */ THREAD_POOL_SIZE,
/* keepAliveTime */ 60, TimeUnit.SECONDS,
/* workQueue */ new LinkedBlockingQueue<Runnable>(), new ThreadFactoryWithPriority(Thread.NORM_PRIORITY-1));
/* workQueue */ new LinkedBlockingQueue<Runnable>(), new ThreadFactoryWithPriority(Thread.NORM_PRIORITY-1, /* daemon */ true));
private final LeaderboardCacheManager leaderboardCacheManager;
@@ -279,7 +279,7 @@ public class LiveLeaderboardUpdater implements Runnable {
+ leaderboard.getName());
// interrupt the current thread if not producing a single result within the overall timeout
while (true) {
MillisecondsTimePoint now = MillisecondsTimePoint.now();
TimePoint now = MillisecondsTimePoint.now();
final Long delayToLiveInMillis = getLeaderboard().getDelayToLiveInMillis();
TimePoint timePoint = delayToLiveInMillis == null ? now : now.minus(delayToLiveInMillis);
synchronized (this) {
@@ -341,7 +341,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
executor = new ThreadPoolExecutor(/* corePoolSize */ THREAD_POOL_SIZE,
/* maximumPoolSize */ THREAD_POOL_SIZE,
/* keepAliveTime */ 60, TimeUnit.SECONDS,
/* workQueue */ new LinkedBlockingQueue<Runnable>(), new ThreadFactoryWithPriority(Thread.NORM_PRIORITY-1));
/* workQueue */ new LinkedBlockingQueue<Runnable>(), new ThreadFactoryWithPriority(Thread.NORM_PRIORITY-1, /* daemon */ true));
}
@Override
@@ -1148,7 +1148,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
@Override
public TimePoint getNowMinusDelay() {
final MillisecondsTimePoint now = MillisecondsTimePoint.now();
final TimePoint now = MillisecondsTimePoint.now();
final Long delayToLiveInMillis = getDelayToLiveInMillis();
TimePoint timePoint = delayToLiveInMillis == null ? now : now.minus(delayToLiveInMillis);
return timePoint;
@@ -145,7 +145,7 @@ public class FlexibleLeaderboardImpl extends AbstractLeaderboardImpl implements
private FlexibleRaceColumn addRaceColumn(String name, boolean medalRace, boolean logAlreadyExistingColumn) {
FlexibleRaceColumn column = getRaceColumnByName(name);
if (column != null) {
final String msg = "Trying to create race column with duplicate name "+name+" in leaderboard +"+getName();
final String msg = "Trying to create race column with duplicate name "+name+" in leaderboard "+getName();
logger.severe(msg);
} else {
column = createRaceColumn(name, medalRace);
@@ -54,7 +54,7 @@ public class MarkPassingCalculator {
.getRuntime().availableProcessors() - 1, 3),
/* maximumPoolSize */Math.max(Runtime.getRuntime().availableProcessors() - 1, 3),
/* keepAliveTime */60, TimeUnit.SECONDS,
/* workQueue */new LinkedBlockingQueue<Runnable>(), new ThreadFactoryWithPriority(Thread.NORM_PRIORITY - 1));
/* workQueue */new LinkedBlockingQueue<Runnable>(), new ThreadFactoryWithPriority(Thread.NORM_PRIORITY - 1, /* daemon */ true));
private boolean suspended = false;
@@ -23,6 +23,8 @@ import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.configuration.DeviceConfiguration;
import com.sap.sailing.domain.base.configuration.DeviceConfigurationMatcher;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaIdentifier;
@@ -62,10 +64,11 @@ public class TopLevelMasterData implements Serializable {
private final Set<WindTrackMasterData> windTrackMasterData;
private final Map<LeaderboardGroup, Set<Event>> eventForLeaderboardGroup;
private final Map<DeviceIdentifier, Set<GPSFix>> raceLogTrackingFixes;
private final Map<DeviceConfigurationMatcher, DeviceConfiguration> deviceConfigurations;
public TopLevelMasterData(final Set<LeaderboardGroup> groupsToExport, final Iterable<Event> allEvents,
final Map<String, Regatta> regattaForRaceIdString, final Collection<MediaTrack> allMediaTracks,
GPSFixStore gpsFixStore, boolean exportWind) {
GPSFixStore gpsFixStore, boolean exportWind, Map<DeviceConfigurationMatcher, DeviceConfiguration> deviceConfigurations) {
this.raceIdStringsForRegatta = convertToRaceIdStringsForRegattaMap(regattaForRaceIdString);
this.leaderboardGroups = groupsToExport;
this.raceLogTrackingFixes = getAllRelevantRaceLogTrackingFixes(gpsFixStore);
@@ -74,6 +77,7 @@ public class TopLevelMasterData implements Serializable {
} else {
this.windTrackMasterData = new HashSet<WindTrackMasterData>();
}
this.deviceConfigurations = deviceConfigurations;
this.eventForLeaderboardGroup = createEventMap(groupsToExport, allEvents);
this.filteredMediaTracks = new HashSet<MediaTrack>();
filterMediaTracks(allMediaTracks, this.filteredMediaTracks);
@@ -344,4 +348,8 @@ public class TopLevelMasterData implements Serializable {
public Map<DeviceIdentifier, Set<GPSFix>> getRaceLogTrackingFixes() {
return raceLogTrackingFixes;
}
public Map<DeviceConfigurationMatcher, DeviceConfiguration> getDeviceConfigurations() {
return deviceConfigurations;
}
}
@@ -8,6 +8,7 @@ import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence;
import com.sap.sailing.domain.base.SpeedWithConfidence;
import com.sap.sailing.domain.common.Bearing;
@@ -51,36 +52,6 @@ public interface PolarDataService {
SpeedWithConfidence<Void> getSpeed(BoatClass boatClass, Speed windSpeed, Bearing trueWindAngle)
throws NotEnoughDataHasBeenAddedException;
/**
*
* @param boatClass
* @param windSpeed
* @param legType
* Should be {@link LegType#UPWIND} or {@link LegType#DOWNWIND}, there is no information for other
* courses yet. Use getSpeed for the desired angle to get rawer information on other courses for now.
* @param tack
* Polar data can vary depending on the tack the boat is on.
* @param useRegressionForSpeed TODO
* @return The estimated average speed of a boat for the supplied parameters with the estimated average bearing to
* the true wind and a confidence which consists of the confidences of the wind speed, and boat speed sources (50%)
* and a confidence calculated using the amount of underlying fixes (50%). 0 <= confidence < 1<br/>
* A value with zero confidence doesn't have any significance!<br/>
* <br/>
*
* The bearing is somewhere between -179 to +180<br/>
* <br/>
*
* Get the speed using returnValue.getObject()<br/>
* <br/>
*
* Returns null if the leg type is not up or downwind.
*
* @throws NotEnoughDataHasBeenAddedException
* If there is not enough data to supply a value with some kind of significance.
*/
SpeedWithBearingWithConfidence<Void> getAverageSpeedWithBearing(BoatClass boatClass, Speed windSpeed,
LegType legType, Tack tack, boolean useRegressionForSpeed) throws NotEnoughDataHasBeenAddedException;
/**
* Generates a polar sheet for given races and settings using the provided executor for the worker threads. This
@@ -242,5 +213,9 @@ public interface PolarDataService {
*/
void unregisterListener(BoatClass boatClass, PolarsChangedListener listener);
void registerDomainFactory(DomainFactory domainFactory);
void unregisterDomainFactory(DomainFactory domainFactory);
}
@@ -471,7 +471,7 @@ public abstract class AbstractRankingMetric implements RankingMetric {
* started at <code>timePoint</code> yet, or <code>who</code>'s tracked leg for the last leg if <code>who</code> has
* already finished the race at <code>timePoint</code>.
*/
private TrackedLegOfCompetitor getCurrentLegOrLastLegIfAlreadyFinished(Competitor who, TimePoint timePoint) {
protected TrackedLegOfCompetitor getCurrentLegOrLastLegIfAlreadyFinished(Competitor who, TimePoint timePoint) {
TrackedLegOfCompetitor currentLegWho = getTrackedRace().getCurrentLeg(who, timePoint);
if (currentLegWho == null) { // already finished or not yet started; if already finished, use last leg
final Waypoint lastWaypoint = getTrackedRace().getRace().getCourse().getLastWaypoint();
@@ -42,7 +42,66 @@ public class OneDesignRankingMetric extends AbstractRankingMetric {
@Override
public Duration getGapToLeaderInOwnTime(RankingMetric.RankingInfo rankingInfo, Competitor competitor, WindLegTypeAndLegBearingCache cache) {
return rankingInfo.getCompetitorRankingInfo().apply(competitor).getEstimatedActualDurationFromTimePointToCompetitorFarthestAhead();
// When the competitor is in the same leg as the leader (which in this ranking metric is always the boat
// farthest ahead) then the competitor's duration to reach the current leader's position is estimated based on the
// competitor's average VMG on the current leg.
// If the leader has already finished the competitor's current leg, the time when the competitor will probably
// finish the leg is compared to the time when the leader finished the leg.
// When the competitor has already finished the current leg this can only mean the competitor finished the race
// because otherwise the competitor would not be considered in this leg anymore. In this case, the mark passing
// times for finishing the leg are compared between competitor and leader.
final Duration result;
final TrackedLegOfCompetitor currentLegWho = getCurrentLegOrLastLegIfAlreadyFinished(competitor,
rankingInfo.getTimePoint());
final Competitor to = rankingInfo.getCompetitorFarthestAhead();
final TrackedLegOfCompetitor currentLegTo = getCurrentLegOrLastLegIfAlreadyFinished(to, rankingInfo.getTimePoint());
if (currentLegTo == null) {
result = null;
} else {
final TimePoint tosLegFinishingTime = currentLegTo.getFinishTime();
// If who is on the last leg, to will also be considered on the last leg, even if to already finished that leg.
// If to finished the last leg and who is on the last leg, getEstimatedActualDurationFromTimePointToCompetitorFarthestAhead will
// not work properly because it considers both on the same leg and therefore doesn't restrict the time to when to finished
// the leg and looks at the excess time.
final Waypoint lastWaypoint = getTrackedRace().getRace().getCourse().getLastWaypoint();
if (tosLegFinishingTime != null && !tosLegFinishingTime.after(rankingInfo.getTimePoint()) &&
currentLegTo.getTrackedLeg().getLeg().getTo() == lastWaypoint) {
// to has finished the race
if (currentLegWho == null) {
result = null;
} else {
final TimePoint whosLegFinishingTime = currentLegWho.getFinishTime();
if (whosLegFinishingTime != null && !whosLegFinishingTime.after(rankingInfo.getTimePoint())) {
// both have finished their leg; this means both have finished the race because otherwise this wouldn't be
// who's current leg
result = tosLegFinishingTime.until(whosLegFinishingTime);
} else {
final Duration etaNextMark = currentLegWho.getEstimatedTimeToNextMark(rankingInfo.getTimePoint(), WindPositionMode.EXACT);
if (etaNextMark == null) {
result = null;
} else {
final Duration timeToTookToFinishRaceStartingAtNextMark;
final Waypoint whosNextMark = currentLegWho.getTrackedLeg().getLeg().getTo();
if (whosNextMark == lastWaypoint) {
// who is in the last leg; the ETA to who's next mark is the solution
timeToTookToFinishRaceStartingAtNextMark = Duration.NULL;
} else {
// after passing the next mark, who still needs to travel about the same time that to
// traveled after passing the next mark until finishing the race
timeToTookToFinishRaceStartingAtNextMark =
getTrackedRace().getMarkPassing(to, whosNextMark).getTimePoint().
until(tosLegFinishingTime);
}
final TimePoint etaAtFinish = rankingInfo.getTimePoint().plus(etaNextMark.plus(timeToTookToFinishRaceStartingAtNextMark));
result = tosLegFinishingTime.until(etaAtFinish);
}
}
}
} else {
result = rankingInfo.getCompetitorRankingInfo().apply(competitor).getEstimatedActualDurationFromTimePointToCompetitorFarthestAhead();
}
}
return result;
}
@Override
@@ -7,6 +7,7 @@ import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
import com.sap.sailing.domain.common.confidence.Weigher;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.common.tracking.GPSFix;
@@ -151,4 +152,19 @@ public interface GPSFixTrack<ItemType, FixType extends GPSFix> extends Track<Fix
*/
Iterator<Position> getEstimatedPositions(Iterable<Timed> timeds, boolean extrapolate);
/**
* When a {@link TrackedRace} moves into state {@link TrackedRaceStatusEnum#LOADING}, it shall call
* this method on all its tracks to allow them to skip validity cache updates which, when done at massive
* scale, are too expensive because they keep invalidating neighbors' validity and need some time to
* find those neighbors. When leading state LOADING, {@link #resumeValidityCaching()} must be called.
*/
void suspendValidityCaching();
/**
* When a {@link TrackedRace} moves out of state {@link TrackedRaceStatusEnum#LOADING}, it shall call
* this method on all its tracks to allow them to invalidate all validity caching so far in order to
* have everything re-calculated when needed.
*/
void resumeValidityCaching();
}
@@ -302,6 +302,10 @@ public interface TrackedRace extends Serializable, IsManagedByCache<SharedDomain
return true;
}
default boolean takesWindFixRecursively(Wind wind, Set<TrackedRace> visited) {
return true;
}
/**
* Same as {@link #getWind(Position, TimePoint, Set) getWind(p, at, Collections.emptyList())}
*/
@@ -859,4 +863,5 @@ public interface TrackedRace extends Serializable, IsManagedByCache<SharedDomain
default RaceLogResolver getRaceLogResolver() {
return null;
}
}
@@ -474,10 +474,13 @@ public class CrossTrackErrorCache extends AbstractRaceChangeListener {
}
public void suspend() {
owner.removeListener(this);
cachePerCompetitor.suspend();
}
public void resume() {
owner.addListener(this);
invalidate();
cachePerCompetitor.resume();
}
}
@@ -560,11 +560,6 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
}
}
@Override
public Iterable<MarkPassing> getMarkPassingsInOrder(Waypoint waypoint) {
return (NavigableSet<MarkPassing>) super.getMarkPassingsInOrder(waypoint);
}
@Override
public void lockForRead(Iterable<MarkPassing> markPassings) {
getRace().getCourse().lockForRead();
@@ -26,6 +26,7 @@ import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
import com.sap.sailing.domain.common.confidence.BearingWithConfidenceCluster;
import com.sap.sailing.domain.common.confidence.ConfidenceBasedAverager;
@@ -40,6 +41,7 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.WithValidityCache;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.GPSTrackListener;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange;
import com.sap.sse.common.Timed;
@@ -58,7 +60,12 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl
private final GPSTrackListeners<ItemType, FixType> listeners;
/**
* When the owning {@link TrackedRace} is still {@link TrackedRaceStatusEnum#LOADING loading}, validity cache updates are
* suspended.
*/
private boolean validityCachingSuspended;
private static class GPSTrackListeners<I, F extends GPSFix> implements Serializable {
private static final long serialVersionUID = -7117842092078781722L;
private Set<GPSTrackListener<I, F>> listeners;
@@ -186,6 +193,25 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl
this.listeners = new GPSTrackListeners<ItemType, FixType>();
this.distanceCache = new DistanceCache(trackedItem==null?"null":trackedItem.toString());
this.maxSpeedCache = createMaxSpeedCache();
this.validityCachingSuspended = false;
}
@Override
public void suspendValidityCaching() {
validityCachingSuspended = true;
}
@Override
public void resumeValidityCaching() {
lockForWrite();
try {
this.validityCachingSuspended = false;
for (FixType fix : getRawFixes()) {
fix.invalidateCache();
}
} finally {
unlockAfterWrite();
}
}
protected MaxSpeedCache<ItemType, FixType> createMaxSpeedCache() {
@@ -958,7 +984,7 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl
* of the <code>gpsFix</code> "upwards." However, if the adjacent earlier fixes have changed their validity by the addition
* of <code>gpsFix</code>, the distance cache must be invalidated starting with the first fix whose validity changed.
*/
protected void invalidateValidityAndEstimatedSpeedAndDistanceCaches(FixType gpsFix) {
private void invalidateValidityAndEstimatedSpeedAndDistanceCaches(FixType gpsFix) {
assertWriteLock();
TimePoint distanceCacheInvalidationStart = gpsFix.getTimePoint();
// see also bug 968: cache entries for intervals ending after the last fix need to be removed because they are
@@ -1058,7 +1084,9 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl
try {
firstFixInTrack = getRawFixes().isEmpty();
result = addWithoutLocking(fix);
invalidateValidityAndEstimatedSpeedAndDistanceCaches(fix);
if (!validityCachingSuspended) {
invalidateValidityAndEstimatedSpeedAndDistanceCaches(fix);
}
} finally {
unlockAfterWrite();
}
@@ -105,7 +105,7 @@ public class ShortTimeWindCache {
*/
private void ensureTimerIsRunning() {
if (timer == null) {
timer = new Timer(getClass().getSimpleName()+" for "+trackedRace.getRace().getName());
timer = new Timer(getClass().getSimpleName()+" for "+trackedRace.getRace().getName(), /* isDaemon */ true);
timer.scheduleAtFixedRate(new CacheInvalidator(), /* delay */ preserveHowManyMilliseconds, preserveHowManyMilliseconds);
}
}
@@ -590,6 +590,17 @@ public class TrackBasedEstimationWindTrackImpl extends VirtualWindTrackImpl {
*/
private class CacheInvalidationRaceChangeListener extends AbstractRaceChangeListener implements Serializable {
private static final long serialVersionUID = -6623310087193133466L;
private boolean suspended;
public CacheInvalidationRaceChangeListener() {
if (getTrackedRace().getStatus().getStatus() == TrackedRaceStatusEnum.LOADING) {
suspended = true;
clearCache();
} else {
suspended = false;
}
}
@Override
protected void defaultAction() {
@@ -598,7 +609,9 @@ public class TrackBasedEstimationWindTrackImpl extends VirtualWindTrackImpl {
@Override
public void windDataReceived(Wind wind, WindSource windSource) {
invalidateForNewWind(wind, windSource);
if (!suspended) {
invalidateForNewWind(wind, windSource);
}
}
@Override
@@ -654,48 +667,61 @@ public class TrackBasedEstimationWindTrackImpl extends VirtualWindTrackImpl {
@Override
public void windDataRemoved(Wind wind, WindSource windSource) {
invalidateForNewWind(wind, windSource);
if (!suspended) {
invalidateForNewWind(wind, windSource);
}
}
@Override
public void competitorPositionChanged(GPSFixMoving fix, Competitor competitor) {
long averagingInterval = getTrackedRace().getMillisecondsOverWhichToAverageSpeed();
WindWithConfidence<TimePoint> startOfInvalidation = getDummyFixWithConfidence(new MillisecondsTimePoint(fix
.getTimePoint().asMillis() - averagingInterval));
TimePoint endOfInvalidation = new MillisecondsTimePoint(fix.getTimePoint().asMillis() + averagingInterval);
scheduleCacheRefresh(startOfInvalidation, endOfInvalidation);
if (!suspended) {
long averagingInterval = getTrackedRace().getMillisecondsOverWhichToAverageSpeed();
WindWithConfidence<TimePoint> startOfInvalidation = getDummyFixWithConfidence(new MillisecondsTimePoint(fix
.getTimePoint().asMillis() - averagingInterval));
TimePoint endOfInvalidation = new MillisecondsTimePoint(fix.getTimePoint().asMillis() + averagingInterval);
scheduleCacheRefresh(startOfInvalidation, endOfInvalidation);
}
}
@Override
public void statusChanged(TrackedRaceStatus newStatus, TrackedRaceStatus oldStatus) {
if (oldStatus.getStatus() == TrackedRaceStatusEnum.LOADING) {
if (newStatus.getStatus() != TrackedRaceStatusEnum.LOADING) {
suspended = false;
}
} else if (newStatus.getStatus() == TrackedRaceStatusEnum.LOADING) {
suspended = true;
clearCache();
}
// This virtual wind track's cache can cope with an empty cache after the LOADING phase and populates the
// cache
// upon request. Invalidation happens also during the LOADING phase, preserving the cache's invariant.
// cache upon request. Invalidation during the LOADING phase happens by clearing the entire cache.
}
@Override
public void markPassingReceived(Competitor competitor, Map<Waypoint, MarkPassing> oldMarkPassings,
Iterable<MarkPassing> markPassings) {
long averagingInterval = getTrackedRace().getMillisecondsOverWhichToAverageSpeed();
WindWithConfidence<TimePoint> startOfInvalidation;
TimePoint endOfInvalidation;
for (MarkPassing markPassing : markPassings) {
MarkPassing oldMarkPassing = oldMarkPassings.get(markPassing.getWaypoint());
if (oldMarkPassing != markPassing) {
if (oldMarkPassing == null) {
startOfInvalidation = getDummyFixWithConfidence(new MillisecondsTimePoint(markPassing
.getTimePoint().asMillis() - averagingInterval));
endOfInvalidation = new MillisecondsTimePoint(markPassing.getTimePoint().asMillis()
+ averagingInterval);
} else {
TimePoint[] interval = new TimePoint[] { oldMarkPassing.getTimePoint(),
markPassing.getTimePoint() };
Arrays.sort(interval);
startOfInvalidation = getDummyFixWithConfidence(new MillisecondsTimePoint(
interval[0].asMillis() - averagingInterval));
endOfInvalidation = new MillisecondsTimePoint(interval[1].asMillis() + averagingInterval);
if (!suspended) {
long averagingInterval = getTrackedRace().getMillisecondsOverWhichToAverageSpeed();
WindWithConfidence<TimePoint> startOfInvalidation;
TimePoint endOfInvalidation;
for (MarkPassing markPassing : markPassings) {
MarkPassing oldMarkPassing = oldMarkPassings.get(markPassing.getWaypoint());
if (oldMarkPassing != markPassing) {
if (oldMarkPassing == null) {
startOfInvalidation = getDummyFixWithConfidence(new MillisecondsTimePoint(markPassing
.getTimePoint().asMillis() - averagingInterval));
endOfInvalidation = new MillisecondsTimePoint(markPassing.getTimePoint().asMillis()
+ averagingInterval);
} else {
TimePoint[] interval = new TimePoint[] { oldMarkPassing.getTimePoint(),
markPassing.getTimePoint() };
Arrays.sort(interval);
startOfInvalidation = getDummyFixWithConfidence(new MillisecondsTimePoint(
interval[0].asMillis() - averagingInterval));
endOfInvalidation = new MillisecondsTimePoint(interval[1].asMillis() + averagingInterval);
}
scheduleCacheRefresh(startOfInvalidation, endOfInvalidation);
}
scheduleCacheRefresh(startOfInvalidation, endOfInvalidation);
}
}
}
@@ -703,13 +729,15 @@ public class TrackBasedEstimationWindTrackImpl extends VirtualWindTrackImpl {
@Override
public void markPositionChanged(GPSFix fix, Mark mark, boolean firstInTrack) {
assert fix != null && fix.getTimePoint() != null;
// A mark position change can mean a leg type change. The interval over which the wind estimation is
// affected
// depends on how the GPS track computes the estimated mark position. Ask it:
TimeRange interval = getTrackedRace().getOrCreateTrack(mark).getEstimatedPositionTimePeriodAffectedBy(fix);
WindWithConfidence<TimePoint> startOfInvalidation = getDummyFixWithConfidence(interval.from());
TimePoint endOfInvalidation = interval.to();
scheduleCacheRefresh(startOfInvalidation, endOfInvalidation);
if (!suspended) {
// A mark position change can mean a leg type change. The interval over which the wind estimation is
// affected
// depends on how the GPS track computes the estimated mark position. Ask it:
TimeRange interval = getTrackedRace().getOrCreateTrack(mark).getEstimatedPositionTimePeriodAffectedBy(fix);
WindWithConfidence<TimePoint> startOfInvalidation = getDummyFixWithConfidence(interval.from());
TimePoint endOfInvalidation = interval.to();
scheduleCacheRefresh(startOfInvalidation, endOfInvalidation);
}
}
}
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.tracking.impl;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
@@ -17,6 +18,7 @@ import java.util.logging.Logger;
import com.sap.sailing.domain.base.BoatClass;
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.SpeedWithBearingWithConfidence;
import com.sap.sailing.domain.base.SpeedWithConfidence;
@@ -77,6 +79,16 @@ public class TrackedLegImpl implements TrackedLeg {
competitorTracksOrderedByRank = new ConcurrentHashMap<>();
}
private void writeObject(ObjectOutputStream oos) throws IOException {
final Course course = trackedRace.getRace().getCourse();
course.lockForRead();
try {
oos.defaultWriteObject();
} finally {
course.unlockAfterRead();
}
}
@Override
public Leg getLeg() {
return leg;
@@ -350,7 +350,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
if (startPos != null) {
Distance d = getTrackedLeg().getAbsoluteWindwardDistance(startPos, endPos, to,
WindPositionMode.EXACT, cache);
result = d.inTime(to.asMillis() - start.getTimePoint().asMillis());
result = d == null ? null : d.inTime(to.asMillis() - start.getTimePoint().asMillis());
}
}
}
@@ -273,6 +273,17 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
private transient Timer cacheInvalidationTimer;
private transient Object cacheInvalidationTimerLock;
/**
* handled by {@link #suspendAllCachesNotUpdatingWhileLoading()} and {@link #resumeAllCachesNotUpdatingWhileLoading()}.
*/
private boolean cachesSuspended;
/**
* Whether during {@link #cachesSuspended suspended caches mode} the maneuver re-calculation was triggered; will lead
* to triggering the maneuver re-calculation when caches are {@link #resumeAllCachesNotUpdatingWhileLoading() resumed}.
*/
private boolean triggerManeuverCacheInvalidationForAllCompetitors;
/**
* Keys are the {@link RaceLog#getId() IDs} of the race logs that are stored as values.
@@ -605,11 +616,11 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
crossTrackErrorCache = new CrossTrackErrorCache(this);
crossTrackErrorCache.invalidate();
maneuverCache = createManeuverCache();
triggerManeuverCacheRecalculationForAllCompetitors();
logger.info("Deserialized race " + getRace().getName());
// considering the unlikely possibility that the course and this tracked race's internal structures
// may be inconsistent, e.g., due to non-atomic serialization of course and tracked race; see bug 2223
adjustStructureToCourse();
triggerManeuverCacheRecalculationForAllCompetitors();
logger.info("Deserialized race " + getRace().getName());
}
/**
@@ -625,6 +636,10 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
private void adjustStructureToCourse() throws PatchFailedException {
final TrackedRaceAsWaypointList trackedRaceAsWaypointList = new TrackedRaceAsWaypointList(this);
Patch<Waypoint> diff = DiffUtils.diff(trackedRaceAsWaypointList, getRace().getCourse().getWaypoints());
if (!diff.isEmpty()) {
logger.warning("Found inconsistency between race's course ("+getRace().getCourse()+
") and TrackedRace's structures in "+this+"; fixing");
}
diff.applyToInPlace(trackedRaceAsWaypointList);
}
@@ -684,7 +699,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
}
@Override
public Iterable<MarkPassing> getMarkPassingsInOrder(Waypoint waypoint) {
public NavigableSet<MarkPassing> getMarkPassingsInOrder(Waypoint waypoint) {
return getMarkPassingsInOrderAsNavigableSet(waypoint);
}
@@ -798,12 +813,13 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
TimePoint passingTime = null;
final Waypoint lastWaypoint = getRace().getCourse().getLastWaypoint();
if (lastWaypoint != null) {
Iterable<MarkPassing> markPassingsInOrder = getMarkPassingsInOrder(lastWaypoint);
NavigableSet<MarkPassing> markPassingsInOrder = getMarkPassingsInOrder(lastWaypoint);
if (markPassingsInOrder != null) {
lockForRead(markPassingsInOrder);
try {
for (MarkPassing passingFinishLine : markPassingsInOrder) {
passingTime = passingFinishLine.getTimePoint();
final MarkPassing last = markPassingsInOrder.isEmpty() ? null : markPassingsInOrder.last();
if (last != null) {
passingTime = last.getTimePoint();
}
} finally {
unlockAfterRead(markPassingsInOrder);
@@ -1525,6 +1541,18 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
*/
@Override
public boolean takesWindFix(Wind wind) {
final Set<TrackedRace> visited = new HashSet<>();
visited.add(this);
return takesWindFixRecursively(wind, visited);
}
/**
* @param visited
* used to avoid endless recursion if cyclic predecessor relations are delivered by a
* {@link RaceExecutionOrderProvider}
*/
@Override
public boolean takesWindFixRecursively(Wind wind, Set<TrackedRace> visited) {
final boolean result;
final TimePoint earliestStartTimePoint = Util.getEarliestOfTimePoints(getStartOfRace(), getStartOfTracking());
final TimePoint latestEndTimePoint = Util.getLatestOfTimePoints(getEndOfRace(), getEndOfTracking());
@@ -1546,7 +1574,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
// the fix is in the critical interval between EXTRA_LONG_TIME_BEFORE_START_TO_TRACK_WIND_MILLIS and
// TIME_BEFORE_START_TO_TRACK_WIND_MILLIS before the earliestStartTimePoint; the fix shall only be accepted
// if no previous race exists that accepts it
result = noPreviousRaceTakesWind(wind);
result = noPreviousRaceTakesWind(wind, visited);
}
}
} else {
@@ -1558,10 +1586,11 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
return result;
}
private boolean noPreviousRaceTakesWind(Wind wind) {
private boolean noPreviousRaceTakesWind(Wind wind, Set<TrackedRace> visited) {
final boolean result;
Set<TrackedRace> previousRacesInExecutionOrder = getPreviousRacesFromAttachedRaceExecutionOrderProviders();
if (previousRacesInExecutionOrder == null || !previousRacesInExecutionOrder.stream().filter(tr -> tr.takesWindFix(wind) == true).findAny().isPresent()) {
if (previousRacesInExecutionOrder == null || !previousRacesInExecutionOrder.stream().filter(tr ->
visited.add(tr) && tr.takesWindFixRecursively(wind, visited)).findAny().isPresent()) {
result = true;
} else {
result = false;
@@ -2332,18 +2361,26 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
}
protected void triggerManeuverCacheRecalculationForAllCompetitors() {
final List<Competitor> shuffledCompetitors = new ArrayList<>();
for (Competitor competitor : (getRace().getCompetitors())) {
shuffledCompetitors.add(competitor);
}
Collections.shuffle(shuffledCompetitors);
for (Competitor competitor : shuffledCompetitors) {
triggerManeuverCacheRecalculation(competitor);
if (cachesSuspended) {
triggerManeuverCacheInvalidationForAllCompetitors = true;
} else {
final List<Competitor> shuffledCompetitors = new ArrayList<>();
for (Competitor competitor : (getRace().getCompetitors())) {
shuffledCompetitors.add(competitor);
}
Collections.shuffle(shuffledCompetitors);
for (Competitor competitor : shuffledCompetitors) {
triggerManeuverCacheRecalculation(competitor);
}
}
}
protected void triggerManeuverCacheRecalculation(final Competitor competitor) {
maneuverCache.triggerUpdate(competitor, /* updateInterval */null);
if (cachesSuspended) {
triggerManeuverCacheInvalidationForAllCompetitors = true;
} else {
maneuverCache.triggerUpdate(competitor, /* updateInterval */null);
}
}
private com.sap.sse.common.Util.Triple<TimePoint, TimePoint, List<Maneuver>> computeManeuvers(Competitor competitor) throws NoWindException {
@@ -3018,6 +3055,13 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
}
private void suspendAllCachesNotUpdatingWhileLoading() {
cachesSuspended = true;
for (GPSFixTrack<Competitor, GPSFixMoving> competitorTrack : tracks.values()) {
competitorTrack.suspendValidityCaching();
}
for (GPSFixTrack<Mark, GPSFix> markTrack : markTracks.values()) {
markTrack.suspendValidityCaching();
}
if (markPassingCalculator != null) {
markPassingCalculator.suspend();
}
@@ -3026,10 +3070,20 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
}
private void resumeAllCachesNotUpdatingWhileLoading() {
cachesSuspended = false;
for (GPSFixTrack<Competitor, GPSFixMoving> competitorTrack : tracks.values()) {
competitorTrack.resumeValidityCaching();
}
for (GPSFixTrack<Mark, GPSFix> markTrack : markTracks.values()) {
markTrack.resumeValidityCaching();
}
if (markPassingCalculator != null) {
markPassingCalculator.resume();
}
crossTrackErrorCache.resume();
if (triggerManeuverCacheInvalidationForAllCompetitors) {
triggerManeuverCacheRecalculationForAllCompetitors();
}
maneuverCache.resume();
}
@@ -3365,10 +3419,8 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
final TrackedLeg legDeterminingDirection = getLegDeterminingDirectionInWhichToPassWaypoint(waypoint);
final Mark portMarkWhileApproachingLine = marksAndPositions.getPortMarkWhileApproachingLine();
final Mark starboardMarkWhileApproachingLine = marksAndPositions.getStarboardMarkWhileApproachingLine();
final Position portMarkPositionWhileApproachingLine = marksAndPositions
.getPortMarkPositionWhileApproachingLine();
final Position starboardMarkPositionWhileApproachingLine = marksAndPositions
.getStarboardMarkPositionWhileApproachingLine();
final Position portMarkPositionWhileApproachingLine = marksAndPositions.getPortMarkPositionWhileApproachingLine();
final Position starboardMarkPositionWhileApproachingLine = marksAndPositions.getStarboardMarkPositionWhileApproachingLine();
final Bearing differenceToCombinedWind;
final NauticalSide advantageousSideWhileApproachingLine;
final Distance distanceAdvantage;
@@ -3425,14 +3477,13 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
*/
private LineMarksWithPositions getLineMarksAndPositions(TimePoint timePoint, Waypoint waypoint) {
final LineMarksWithPositions result;
List<Position> markPositions = new ArrayList<Position>();
List<Position> markPositions = new ArrayList<>();
int numberOfMarks = 0;
boolean allMarksHavePositions = true;
if (waypoint != null) {
for (Mark lineMark : waypoint.getMarks()) {
numberOfMarks++;
final Position estimatedMarkPosition = getOrCreateTrack(lineMark).getEstimatedPosition(timePoint, /* extrapolate */
false);
final Position estimatedMarkPosition = getOrCreateTrack(lineMark).getEstimatedPosition(timePoint, /* extrapolate */ false);
if (estimatedMarkPosition != null) {
markPositions.add(estimatedMarkPosition);
} else {
@@ -3482,12 +3533,10 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
private TrackedLeg getLegDeterminingDirectionInWhichToPassWaypoint(Waypoint waypoint) {
final TrackedLeg legDeterminingDirection;
{
final int indexOfWaypoint2 = getRace().getCourse().getIndexOfWaypoint(waypoint);
final boolean isStartLine2 = indexOfWaypoint2 == 0;
legDeterminingDirection = getTrackedLeg(getRace().getCourse().getLegs().get(isStartLine2 ? 0
: indexOfWaypoint2 - 1));
}
final int indexOfWaypoint = getRace().getCourse().getIndexOfWaypoint(waypoint);
final boolean isStartLine = indexOfWaypoint == 0;
legDeterminingDirection = getTrackedLeg(getRace().getCourse().getLegs().get(isStartLine ? 0
: indexOfWaypoint - 1));
return legDeterminingDirection;
}
@@ -110,4 +110,11 @@
version="0.0.0"
unpack="false"/>
<plugin
id="lz4-java"
download-size="0"
install-size="0"
version="0.0.0"
unpack="false"/>
</feature>
-2
View File
@@ -28,9 +28,7 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al
<import plugin="com.google.gwt.osgi" version="2.7.0" match="greaterOrEqual"/>
<import plugin="org.json.simple" version="1.1.0" match="greaterOrEqual"/>
<import plugin="org.moxieapps.gwt.highcharts" version="1.6.0" match="greaterOrEqual"/>
<import plugin="org.apache.geronimo.specs.geronimo-jms_1.1_spec"/>
<import plugin="com.rabbitmq.client"/>
<import plugin="org.apache.geronimo.specs.geronimo-j2ee-management_1.1_spec"/>
</requires>
<plugin
@@ -17,7 +17,6 @@ Require-Bundle: com.sap.sailing.server,
com.sap.sse.filestorage
Bundle-ClassPath: .
Import-Package: com.sap.sailing.domain.racelogtracking.impl,
javax.jms;version="1.1.0",
javax.servlet;version="2.6.0",
javax.servlet.http;version="2.6.0",
junit.framework;version="4.8.1",
@@ -11,6 +11,6 @@
</listAttribute>
<stringAttribute key="projectName" value="com.sap.sailing.gwt.ui"/>
<booleanAttribute key="showLaunchOutput" value="false"/>
<stringAttribute key="url" value="http://127.0.0.1:8888/gwt/AdminConsole.html"/>
<stringAttribute key="url" value="http://127.0.0.1:8888/gwt/Home.html"/>
<stringAttribute key="urlQueryParams" value=""/>
</launchConfiguration>
@@ -51,8 +51,7 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.news
Bundle-Activator: com.sap.sailing.gwt.ui.server.Activator
Bundle-ActivationPolicy: lazy
Import-Package: javax.jms;version="1.1.0",
javax.servlet;version="3.1.0",
Import-Package: javax.servlet;version="3.1.0",
javax.servlet.descriptor;version="3.1.0",
javax.servlet.http;version="3.1.0",
org.apache.http.client;version="4.2.5",
+76 -76
View File
@@ -1,76 +1,76 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="com.google.gdt.eclipse.suite.webapp">
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/>
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/>
<booleanAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT_AUTO" value="true"/>
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
<listEntry value="com.sap.sailing.gwt.ui.RaceBoard"/>
<listEntry value="com.sap.sailing.gwt.ui.DataMining"/>
<listEntry value="com.sap.sailing.gwt.ui.LeaderboardEditing"/>
<listEntry value="com.sap.sailing.gwt.ui.Spectator"/>
<listEntry value="com.sap.sailing.gwt.regattaoverview.RegattaOverview"/>
<listEntry value="com.sap.sailing.gwt.ui.PolarSheets"/>
<listEntry value="com.sap.sailing.gwt.ui.Leaderboard"/>
<listEntry value="com.sap.sailing.gwt.ui.Simulator"/>
<listEntry value="com.sap.sailing.gwt.ui.YoutubePopup"/>
<listEntry value="com.sap.sailing.gwt.AutoPlay"/>
<listEntry value="com.sap.sailing.gwt.ui.AdminConsole"/>
<listEntry value="com.sap.sailing.gwt.home.Home"/>
<listEntry value="com.sap.sailing.gwt.ui.VideoPopup"/>
</listAttribute>
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/gwt/Home.html"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.gwt.ui"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="false"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6&quot; javaProject=&quot;com.sap.sailing.gwt.ui&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt.theme/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.googlecode.java-diff-utils/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.json.simple/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.server/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.tractracadapter/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.expeditionconnector/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.declination/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.declination/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.udpconnector/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.mongodb/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.swisstimingadapter/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.swisstimingadapter.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.operationaltransformation/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.replication/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.tractracadapter.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.moxieapps.gwt.highcharts/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.geocoding/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.google.gwt.osgi/lib/gwt-user.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#13;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;com.sap.sailing.gwt.ui&quot;/&gt;&#13;&#10;&lt;/runtimeClasspathEntry&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt.adminconsole/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -incremental -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort auto -startupUrl /gwt/Home.html com.sap.sailing.gwt.home.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.regattaoverview.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.PolarSheets com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.RaceBoard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -Dgwt-usearchives=false -Dgwt.persistentunitcache=false"/>
</launchConfiguration>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="com.google.gdt.eclipse.suite.webapp">
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/>
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/>
<booleanAttribute key="com.google.gwt.eclipse.core.CODE_SERVER_PORT_AUTO" value="true"/>
<listAttribute key="com.google.gwt.eclipse.core.ENTRY_POINT_MODULES">
<listEntry value="com.sap.sailing.gwt.ui.RaceBoard"/>
<listEntry value="com.sap.sailing.gwt.ui.DataMining"/>
<listEntry value="com.sap.sailing.gwt.ui.LeaderboardEditing"/>
<listEntry value="com.sap.sailing.gwt.ui.Spectator"/>
<listEntry value="com.sap.sailing.gwt.regattaoverview.RegattaOverview"/>
<listEntry value="com.sap.sailing.gwt.ui.PolarSheets"/>
<listEntry value="com.sap.sailing.gwt.ui.Leaderboard"/>
<listEntry value="com.sap.sailing.gwt.ui.Simulator"/>
<listEntry value="com.sap.sailing.gwt.ui.YoutubePopup"/>
<listEntry value="com.sap.sailing.gwt.AutoPlay"/>
<listEntry value="com.sap.sailing.gwt.ui.AdminConsole"/>
<listEntry value="com.sap.sailing.gwt.home.Home"/>
<listEntry value="com.sap.sailing.gwt.ui.VideoPopup"/>
</listAttribute>
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/gwt/Home.html"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.gwt.ui"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="false"/>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6&quot; javaProject=&quot;com.sap.sailing.gwt.ui&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt.theme/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.googlecode.java-diff-utils/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.json.simple/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.server/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.tractracadapter/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.expeditionconnector/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.declination/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.declination/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.udpconnector/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.mongodb/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.swisstimingadapter/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.swisstimingadapter.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.operationaltransformation/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.replication/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.tractracadapter.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.moxieapps.gwt.highcharts/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.geocoding/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.google.gwt.osgi/lib/gwt-user.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#13;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;com.sap.sailing.gwt.ui&quot;/&gt;&#13;&#10;&lt;/runtimeClasspathEntry&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt.adminconsole/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -incremental -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort auto -startupUrl /gwt/Home.html com.sap.sailing.gwt.home.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.regattaoverview.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.PolarSheets com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.RaceBoard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -Dgwt-usearchives=false -Dgwt.persistentunitcache=false"/>
</launchConfiguration>
@@ -133,12 +133,6 @@ public abstract class AbstractEventActivity<PLACE extends AbstractEventPlace> ex
return getRaceViewerURL(leaderboard.name, raceIdentifier);
}
@Override
public String getRaceViewerURL(String regattaName, String trackedRaceName) {
return EntryPointLinkFactory
.createRaceBoardLink(createRaceBoardLinkParameters(regattaName, regattaName, trackedRaceName));
}
public String getRaceViewerURL(String leaderboardName, RegattaAndRaceIdentifier raceIdentifier) {
return EntryPointLinkFactory
.createRaceBoardLink(createRaceBoardLinkParameters(leaderboardName, raceIdentifier.getRegattaName(), raceIdentifier.getRaceName()));
@@ -314,7 +308,14 @@ public abstract class AbstractEventActivity<PLACE extends AbstractEventPlace> ex
String url = "RegattaOverview.html?ignoreLocalSettings=true&onlyrunningraces=false&event=" + getCtx().getEventId();
url += "&onlyracesofsameday=" + getCtx().getEventDTO().isRunning();
if(showRegattaMetadata()) {
url += "&regatta=" + getCtx().getRegattaId();
if(getRegattaMetadata().isFlexibleLeaderboard()) {
String defaultCourseAreaId = getRegattaMetadata().getDefaultCourseAreaId();
if(defaultCourseAreaId != null && !defaultCourseAreaId.isEmpty()) {
url += "&coursearea=" + defaultCourseAreaId;
}
} else {
url += "&regatta=" + getCtx().getRegattaId();
}
}
return url;
}
@@ -325,9 +326,6 @@ public abstract class AbstractEventActivity<PLACE extends AbstractEventPlace> ex
if(ctx.getRegatta().getState() == RegattaState.RUNNING) {
return true;
}
if(ctx.getRegatta().getState() != RegattaState.UNKNOWN) {
return false;
}
}
return ctx.getEventDTO().getState() == EventState.RUNNING;
}
@@ -6,6 +6,7 @@ import com.google.gwt.place.shared.Place;
import com.google.gwt.safehtml.shared.SafeUri;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.IsWidget;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.dto.RaceDTO;
import com.sap.sailing.gwt.common.client.controls.tabbar.TabView;
import com.sap.sailing.gwt.home.client.place.event.regatta.AbstractEventRegattaPlace;
@@ -56,7 +57,7 @@ public interface EventView<PLACE extends AbstractEventPlace, PRES extends EventV
String getRaceViewerURL(StrippedLeaderboardDTO leaderboard, RaceDTO race);
String getRaceViewerURL(String regattaName, String trackedRaceName);
String getRaceViewerURL(String leaderboardName, RegattaAndRaceIdentifier raceIdentifier);
PlaceNavigation<RegattaRacesPlace> getRegattaRacesNavigation(String regattaName);
PlaceNavigation<AbstractEventRegattaPlace> getRegattaNavigation(String regattaName);
@@ -157,8 +157,8 @@ public class MultiregattaOverviewTabView extends Composite implements Multiregat
private class MultiregattaOverviewRegattasTabViewRegattaFilterList implements
DropdownFilterList<String>, RefreshableWidget<SortedSetResult<RegattaWithProgressDTO>> {
@Override
public void setData(SortedSetResult<RegattaWithProgressDTO> data, long nextUpdate, int updateNo) {
regattaListUi.setData(data, nextUpdate, updateNo);
public void setData(SortedSetResult<RegattaWithProgressDTO> data) {
regattaListUi.setData(data);
boatCategoryFilterUi.updateFilterValues();
}
@Override
@@ -155,8 +155,8 @@ public class MultiregattaRegattasTabView extends Composite implements Multiregat
DropdownFilterList<String>, RefreshableWidget<SortedSetResult<RegattaWithProgressDTO>> {
@Override
public void setData(SortedSetResult<RegattaWithProgressDTO> data, long nextUpdate, int updateNo) {
regattaListUi.setData(data, nextUpdate, updateNo);
public void setData(SortedSetResult<RegattaWithProgressDTO> data) {
regattaListUi.setData(data);
boatCategoryFilterUi.updateFilterValues();
}
@@ -172,4 +172,4 @@ public class MultiregattaRegattasTabView extends Composite implements Multiregat
}
}
}
@@ -33,13 +33,13 @@ public class EventOverviewStage extends Composite {
private final RefreshableWidget<EventOverviewStageDTO> refreshable = new RefreshableWidget<EventOverviewStageDTO>() {
@Override
public void setData(EventOverviewStageDTO data, long nextUpdate, int updateNo) {
public void setData(EventOverviewStageDTO data) {
setStageData(data);
}
};
private final RefreshableWidget<ListResult<NewsEntryDTO>> newsRefreshable = new RefreshableWidget<ListResult<NewsEntryDTO>>() {
@Override
public void setData(ListResult<NewsEntryDTO> data, long nextUpdate, int updateNo) {
public void setData(ListResult<NewsEntryDTO> data) {
setNews(data.getValues());
}
};
@@ -32,7 +32,7 @@ public class MultiRegattaList extends Composite implements RefreshableWidget<Sor
}
@Override
public void setData(SortedSetResult<RegattaWithProgressDTO> data, long nextUpdate, int updateNo) {
public void setData(SortedSetResult<RegattaWithProgressDTO> data) {
this.setListData(data == null ? Collections.<RegattaWithProgressDTO>emptySet() : data.getValues());
}
@@ -21,15 +21,15 @@ public class MultiRegattaListItem extends Composite {
@UiField(provided = true) MultiRegattaListSteps regattaStepsUi;
private final RegattaWithProgressDTO regattaWithProgress;
public MultiRegattaListItem(RegattaWithProgressDTO regattaWithProgress) {
public MultiRegattaListItem(RegattaWithProgressDTO regattaWithProgress, boolean showStateMarker) {
this.regattaWithProgress = regattaWithProgress;
regattaHeaderUi = new RegattaHeader(regattaWithProgress);
regattaHeaderUi = new RegattaHeader(regattaWithProgress, showStateMarker);
regattaStepsUi = new MultiRegattaListSteps(regattaWithProgress.getProgress());
initWidget(uiBinder.createAndBindUi(this));
}
public MultiRegattaListItem(RegattaWithProgressDTO regattaWithProgress, Presenter presenter) {
this(regattaWithProgress);
this(regattaWithProgress, presenter.getCtx().getEventDTO().isRunning());
regattaHeaderUi.setRegattaNavigation(presenter.getRegattaNavigation(regattaWithProgress.getId()));
PlaceNavigation<?> leaderboardNavigation = presenter.getRegattaLeaderboardNavigation(regattaWithProgress.getId());
regattaStepsUi.setLeaderboardNavigation(regattaWithProgress.getState(), leaderboardNavigation);
@@ -38,7 +38,7 @@ public class RegattaCompetitionSeries extends Composite {
RegattaCompetitionFleet competitionFleet = new RegattaCompetitionFleet(fleet);
for (RaceListRaceDTO race : fleet.getRaces()) {
boolean tracked = race.getTrackingState() == RaceTrackingState.TRACKED_VALID_DATA;
String raceViewerUrl = tracked ? presenter.getRaceViewerURL(race.getRegattaName(), race.getTrackedRaceName()) : null;
String raceViewerUrl = tracked ? presenter.getRaceViewerURL(race.getRegattaName(), race.getRegattaAndRaceIdentifier()) : null;
competitionFleet.addRace(race, raceViewerUrl);
}
containerUi.add(competitionFleet);
@@ -72,20 +72,31 @@
min-width: 13.333333333333334em;
max-width: 16.666666666666668em;
}
.race_item_flag {
max-width: 1.6em;
max-height: 1.6em;
img.race_item_flag {
vertical-align: middle;
height: 1em;
width: auto;
}
.race_item_position {
margin-right: 0.333333333333333em;
}
.race_item_winner {
.race_item_sailid {
margin-left: 5px;
vertical-align: middle;
font-weight: bold;
font-size: 0.8em;
}
.race_item_winner {
vertical-align: middle;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.race_itemwinner {
padding: 0.733333333333333em 1.333333333333333em;
padding: 0 1.333333333333333em;
}
.race_itemwinner > div {
vertical-align: middle;
}
.race_itemcenter {
text-align: center;
@@ -211,4 +222,13 @@
.racesListIcon > div {
vertical-align: middle;
display: inline-block;
}
.racesListHideColumn {
width: 0 !important;
padding: 0 !important;
margin: 0 !important;
border: medium none !important;
}
.racesListHideColumn > * {
display: none !important;
}
@@ -24,6 +24,7 @@ public interface RacesListLiveResources extends ClientBundle {
String race_itemwinner();
String race_item_flag();
String race_item_position();
String race_item_sailid();
String race_item_winner();
String race_itemcenter();
String race_itemright();
@@ -49,5 +50,6 @@ public interface RacesListLiveResources extends ClientBundle {
String raceslistlive();
String racesListIcon();
String racesListHideColumn();
}
}
@@ -1,6 +1,9 @@
package com.sap.sailing.gwt.home.client.place.event.partials.racelist;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import com.google.gwt.user.cellview.client.Column;
import com.google.gwt.user.cellview.client.ColumnSortList;
@@ -33,33 +36,23 @@ public abstract class AbstractRaceList<T extends RaceMetadataDTO<? extends Abstr
protected final SortableRaceListColumn<T, ?> windDirectionColumn = RaceListColumnFactory.getWindDirectionColumn();
protected final SortableRaceListColumn<T, ?> raceViewerButtonColumn;
private SortedCellTable<T> cellTable;
private SortedCellTable<T> cellTable = new SortedCellTable<T>(0, CleanCellTableResources.INSTANCE);
private boolean tableColumnsInitialized = false;
protected AbstractRaceList(EventView.Presenter presenter) {
CSS.ensureInjected();
this.raceViewerButtonColumn = RaceListColumnFactory.getRaceViewerButtonColumn(presenter);
this.cellTableContainer.setWidget(this.cellTable);
this.initTableStyle();
this.initWidget(cellTableContainer);
}
@SuppressWarnings("unchecked")
protected void setTableData(Collection<T> data) {
ColumnSortList sortList = this.cellTable == null ? null : this.cellTable.getColumnSortList();
this.cellTable = new SortedCellTable<T>(data.size(), CleanCellTableResources.INSTANCE);
this.cellTableContainer.setWidget(this.cellTable);
this.initTableStyle();
this.initTableColumns();
this.ensureInitTableColumns();
this.updateColumnVisibility();
this.cellTable.setPageSize(data.size());
this.cellTable.setList(data);
if (sortList != null && sortList.size() > 0) {
for (int i = sortList.size() - 1; i >= 0; i--) {
ColumnSortInfo sortInfo = sortList.get(i);
Column<T, ?> column = (Column<T, ?>) sortInfo.getColumn();
if(this.cellTable.getColumnIndex(column) >= 0) {
this.cellTable.sortColumn(column, sortInfo.isAscending());
}
}
} else {
this.cellTable.sortColumn(startTimeColumn);
}
this.restoreColumnSortInfos();
}
private void initTableStyle() {
@@ -73,24 +66,52 @@ public abstract class AbstractRaceList<T extends RaceMetadataDTO<? extends Abstr
});
}
private void ensureInitTableColumns() {
if (tableColumnsInitialized) return;
tableColumnsInitialized = true;
this.initTableColumns();
}
protected abstract void initTableColumns();
protected void add(SortableRaceListColumn<T, ?> column) {
if (column.isShowDetails()) {
if (column.getColumnStyle() != null) {
column.setCellStyleNames(column.getColumnStyle());
}
private void updateColumnVisibility() {
for (int i = 0; i < this.cellTable.getColumnCount(); i++) {
SortableRaceListColumn<T, ?> column = (SortableRaceListColumn<T, ?>) this.cellTable.getColumn(i);
Header<?> header = column.getHeader();
if (header != null && column.getHeaderStyle() != null) {
header.setHeaderStyleNames(column.getHeaderStyle());
if (header != null) {
header.setHeaderStyleNames(column.getCurrentHeaderStyle());
}
boolean ascending = column.getPreferredSortingOrder().isAscending();
InvertibleComparator<T> comperator = column.getComparator();
if (comperator != null) {
comperator.setAscending(ascending);
}
this.cellTable.addColumn(column, header, comperator, ascending);
column.setCellStyleNames(column.getCurrentColumnStyle());
}
}
@SuppressWarnings("unchecked")
private void restoreColumnSortInfos() {
ColumnSortList sortList = this.cellTable.getColumnSortList();
List<ColumnSortInfo> oldSortInfos;
if (sortList.size() == 0) {
boolean ascending = startTimeColumn.getPreferredSortingOrder().isAscending();
oldSortInfos = Collections.singletonList(new ColumnSortInfo(startTimeColumn, ascending));
} else {
oldSortInfos = new ArrayList<ColumnSortList.ColumnSortInfo>(sortList.size());
for (int i = sortList.size() - 1; i >= 0; i--) {
oldSortInfos.add(sortList.get(i));
}
}
for (ColumnSortInfo sortInfo : oldSortInfos) {
Column<T, ?> column = (Column<T, ?>) sortInfo.getColumn();
this.cellTable.sortColumn(column);
}
}
protected void add(SortableRaceListColumn<T, ?> column) {
Header<?> header = column.getHeader();
boolean ascending = column.getPreferredSortingOrder().isAscending();
InvertibleComparator<T> comperator = column.getComparator();
if (comperator != null) {
comperator.setAscending(ascending);
}
this.cellTable.addColumn(column, header, comperator, ascending);
}
}
@@ -76,8 +76,8 @@ public class RaceListColumnFactory {
@Template("<a href=\"{2}\" class=\"{0}\" target=\"_blank\">{1}</a>")
SafeHtml raceViewerLinkButton(String styleNames, String text, String link);
@Template("<img src=\"{2}\" class=\"{0}\" /><span class=\"{1}\">{3}</span>")
SafeHtml winner(String styleNamesFlag, String styleNamesText, SafeUri flagImageURL, String name);
@Template("<img src=\"{3}\" class=\"{0}\" /><span class=\"{1}\">{4}</span><div class=\"{2}\" title=\"{5}\">{5}</div>")
SafeHtml winner(String styleNamesFlag, String styleNamesSailId, String styleNamesText, SafeUri flagImageURL, String sailId, String name);
@Template("<img src=\"{1}\" class=\"{0}\" />")
SafeHtml imageHeader(String styleNames, SafeUri imageURL);
@@ -470,7 +470,8 @@ public class RaceListColumnFactory {
public void render(Context context, SimpleCompetitorDTO value, SafeHtmlBuilder sb) {
if (value != null) {
SafeUri flagImageUri = FlagImageResolver.getFlagImageUri(value.getFlagImageURL(), value.getTwoLetterIsoCountryCode());
sb.append(TEMPLATE.winner(CSS.race_item_flag(), CSS.race_item_winner(), flagImageUri, value.getName()));
String flagStyle = CSS.race_item_flag(), sailIdStyle = CSS.race_item_sailid(), nameStyle = CSS.race_item_winner();
sb.append(TEMPLATE.winner(flagStyle, sailIdStyle, nameStyle, flagImageUri, value.getSailID(), value.getName()));
}
}
};
@@ -514,7 +515,7 @@ public class RaceListColumnFactory {
} else {
String styleNames = data.getViewState() == RaceViewState.FINISHED ? analyseRaceStyle : watchNowStyle;
String text = data.getViewState() == RaceViewState.FINISHED ? I18N.analyseRace() : I18N_UBI.watchNow();
String raceViewerURL = presenter.getRaceViewerURL(data.getRegattaName(), data.getTrackedRaceName());
String raceViewerURL = presenter.getRaceViewerURL(data.getLeaderboardName(), data.getRegattaAndRaceIdentifier());
sb.append(TEMPLATE.raceViewerLinkButton(styleNames, text, raceViewerURL));
}
}
@@ -43,7 +43,7 @@ public class RaceListContainer<T extends RaceMetadataDTO<?>> extends Composite i
}
@Override
public void setData(CollectionResult<T> data, long nextUpdate, int updateNo) {
public void setData(CollectionResult<T> data) {
setRaceListData(data == null ? null : data.getValues());
}
@@ -1,10 +1,12 @@
package com.sap.sailing.gwt.home.client.place.event.partials.racelist;
import com.google.gwt.cell.client.Cell;
import com.google.gwt.safehtml.shared.SafeHtmlUtils;
import com.google.gwt.user.cellview.client.Header;
import com.google.gwt.user.cellview.client.TextHeader;
import com.google.gwt.user.cellview.client.SafeHtmlHeader;
import com.sap.sailing.domain.common.InvertibleComparator;
import com.sap.sailing.domain.common.SortingOrder;
import com.sap.sailing.gwt.home.client.place.event.partials.raceListLive.RacesListLiveResources;
import com.sap.sailing.gwt.ui.client.shared.controls.SortableColumn;
import com.sap.sse.common.Util;
@@ -16,11 +18,11 @@ public abstract class SortableRaceListColumn<T, C> extends SortableColumn<T, C>
private boolean showDetails = true;
protected SortableRaceListColumn(String headerText, Cell<C> cell, InvertibleComparator<T> comparator) {
this(new TextHeader(headerText), cell, comparator);
this(new WrappedTextHeader(headerText), cell, comparator);
}
protected SortableRaceListColumn(String headerText, Cell<C> cell, InvertibleComparator<T> comparator, SortingOrder preferredSortingOrder) {
this(new TextHeader(headerText), cell, comparator, preferredSortingOrder);
this(new WrappedTextHeader(headerText), cell, comparator, preferredSortingOrder);
}
protected SortableRaceListColumn(Header<?> header, Cell<C> cell, InvertibleComparator<T> comparator) {
@@ -54,4 +56,25 @@ public abstract class SortableRaceListColumn<T, C> extends SortableColumn<T, C>
protected final String getStyleNamesString(String... styleNames) {
return Util.join(" ", styleNames);
}
private static final String COLUMN_HIDDEN_STYLE = RacesListLiveResources.INSTANCE.css().racesListHideColumn();
protected final String getCurrentHeaderStyle() {
return getShowOrHiddenStyle(getHeaderStyle());
}
protected final String getCurrentColumnStyle() {
return getShowOrHiddenStyle(getColumnStyle());
}
private String getShowOrHiddenStyle(String showStyle) {
showStyle = showStyle == null ? "" : showStyle;
return showDetails ? showStyle : getStyleNamesString(showStyle, COLUMN_HIDDEN_STYLE);
}
private static class WrappedTextHeader extends SafeHtmlHeader {
public WrappedTextHeader(String headerText) {
super(SafeHtmlUtils.fromTrustedString("<div>" + headerText + "</div>"));
}
}
}
@@ -19,9 +19,9 @@ public class RegattaHeader extends Composite {
@UiField AnchorElement headerBodyUi;
@UiField AnchorElement headerArrowUi;
public RegattaHeader(RegattaMetadataDTO regattaMetadata) {
public RegattaHeader(RegattaMetadataDTO regattaMetadata, boolean showStateMarker) {
initWidget(uiBinder.createAndBindUi(this));
headerBodyUi.appendChild(new RegattaHeaderBody(regattaMetadata).getElement());
headerBodyUi.appendChild(new RegattaHeaderBody(regattaMetadata, showStateMarker).getElement());
}
public void setRegattaNavigation(PlaceNavigation<?> placeNavigation) {
@@ -12,7 +12,9 @@ import com.google.gwt.user.client.ui.UIObject;
import com.sap.sailing.gwt.common.client.BoatClassImageResolver;
import com.sap.sailing.gwt.home.client.shared.LabelTypeUtil;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.eventview.HasRegattaMetadata.RegattaState;
import com.sap.sailing.gwt.ui.shared.eventview.RegattaMetadataDTO;
import com.sap.sailing.gwt.ui.shared.general.LabelType;
public class RegattaHeaderBody extends UIObject {
@@ -27,13 +29,14 @@ public class RegattaHeaderBody extends UIObject {
@UiField protected DivElement labelUi;
@UiField protected DivElement detailsItemContainerUi;
public RegattaHeaderBody(RegattaMetadataDTO regattaMetadata) {
public RegattaHeaderBody(RegattaMetadataDTO regattaMetadata, boolean showStateMarker) {
RegattaHeaderResources.INSTANCE.css().ensureInjected();
setElement(uiBinder.createAndBindUi(this));
ImageResource logo = BoatClassImageResolver.getBoatClassIconResource(regattaMetadata.getBoatClass());
logoUi.getStyle().setBackgroundImage("url('" + logo.getSafeUri().asString() + "')");
nameUi.setInnerText(regattaMetadata.getDisplayName());
LabelTypeUtil.renderLabelTypeOrHide(labelUi, regattaMetadata.getState().getStateMarker());
RegattaState state = regattaMetadata.getState();
LabelTypeUtil.renderLabelTypeOrHide(labelUi, showStateMarker ? state.getStateMarker() : LabelType.NONE);
addDetailsItem(regattaMetadata.getCompetitorsCount(), I18N.competitorsCount(regattaMetadata.getCompetitorsCount()));
addDetailsItem(regattaMetadata.getRaceCount(), I18N.racesCount(regattaMetadata.getRaceCount()));
String defaultCourseAreaName = regattaMetadata.getDefaultCourseAreaName();
@@ -75,8 +75,8 @@ public class UpdatesBoxItem extends Widget {
placeNavigation = presenter.getRegattaLeaderboardNavigation(((LeaderboardNewsEntryDTO) entry).getLeaderboardName());
} else if(entry instanceof AbstractRaceNewsEntryDTO) {
AbstractRaceNewsEntryDTO raceEntry = (AbstractRaceNewsEntryDTO) entry;
if(raceEntry.getTrackedRaceName() != null) {
directLink = presenter.getRaceViewerURL(raceEntry.getRegattaName(), raceEntry.getTrackedRaceName());
if(raceEntry.getRegattaAndRaceIdentfier() != null) {
directLink = presenter.getRaceViewerURL(raceEntry.getLeaderboardName(), raceEntry.getRegattaAndRaceIdentfier());
}
}
if(placeNavigation != null) {
@@ -132,8 +132,8 @@ public class RegattaRacesTabView extends Composite implements RegattaTabView<Reg
refreshManager.add(liveRacesListUi.getRefreshable(), new GetLiveRacesForRegattaAction(myPlace.getCtx().getEventDTO().getId(), myPlace.getRegattaId()));
refreshManager.add(new RefreshableWidget<RegattaWithProgressDTO>() {
@Override
public void setData(RegattaWithProgressDTO data, long nextUpdate, int updateNo) {
regattaInfoContainerUi.setWidget(new MultiRegattaListItem(data));
public void setData(RegattaWithProgressDTO data) {
regattaInfoContainerUi.setWidget(new MultiRegattaListItem(data, true));
}
}, new GetRegattaWithProgressAction(myPlace.getCtx().getEventDTO().getId(), myPlace.getRegattaId()));
refreshManager.add(raceListContainerUi, new GetFinishedRacesAction(myPlace.getCtx().getEventDTO().getId(), myPlace.getRegattaId()));
@@ -2,6 +2,8 @@ package com.sap.sailing.gwt.home.client.place.event.regatta.tabs.reload;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
@@ -17,6 +19,7 @@ import com.sap.sailing.gwt.ui.shared.dispatch.DTO;
import com.sap.sailing.gwt.ui.shared.dispatch.ResultWithTTL;
public class RefreshManager {
private static final Logger LOG = Logger.getLogger(RefreshManager.class.getName());
private static final long PAUSE_ON_ERROR = 1000 * 30;
private List<RefreshHolder<DTO, Action<ResultWithTTL<DTO>>>> refreshables = new ArrayList<>();
@@ -47,16 +50,14 @@ public class RefreshManager {
});
}
private int updateNo;
private void update() {
updateNo++;
for (final RefreshHolder<DTO, Action<ResultWithTTL<DTO>>> refreshable : refreshables) {
// Everything that needs refresh within the next 5000ms will be refreshed now.
// This makes it possible to use batching resulting in less requests.
if (!refreshable.callRunning && refreshable.timeout < System.currentTimeMillis() + 5000) {
refreshable.callRunning = true;
actionExecutor.execute(refreshable.provider.getAction(), new AsyncCallback<ResultWithTTL<DTO>>() {
final Action<ResultWithTTL<DTO>> action = refreshable.provider.getAction();
actionExecutor.execute(action, new AsyncCallback<ResultWithTTL<DTO>>() {
@Override
public void onFailure(Throwable caught) {
refreshable.callRunning = false;
@@ -68,7 +69,11 @@ public class RefreshManager {
public void onSuccess(ResultWithTTL<DTO> result) {
refreshable.callRunning = false;
refreshable.timeout = System.currentTimeMillis() + result.getTtl();
refreshable.widget.setData(result.getDto(), refreshable.timeout, updateNo);
try {
refreshable.widget.setData(result.getDto());
} catch(Throwable error) {
LOG.log(Level.SEVERE, "Error while refreshing content with action " + action.getClass().getName(), error);
}
reschedule();
}
});
@@ -3,5 +3,5 @@ package com.sap.sailing.gwt.home.client.place.event.regatta.tabs.reload;
import com.sap.sailing.gwt.ui.shared.dispatch.DTO;
public interface RefreshableWidget<D extends DTO> {
void setData(D data, long nextUpdate, int updateNo);
void setData(D data);
}
@@ -1,6 +1,6 @@
package com.sap.sailing.gwt.home.client.shared.media;
import java.util.List;
import java.util.Collection;
import com.google.gwt.dom.client.Style.Overflow;
import com.google.gwt.event.dom.client.KeyCodes;
@@ -27,7 +27,7 @@ public class SailingFullscreenViewer implements FullscreenViewer<SailingImageDTO
};
};
public void show(SailingImageDTO selected, List<SailingImageDTO> images) {
public void show(SailingImageDTO selected, Collection<SailingImageDTO> images) {
final SailingGalleryPlayer viewer = new SailingGalleryPlayer(selected, images);
Window.addResizeHandler(new ResizeHandler() {

Some files were not shown because too many files have changed in this diff Show More