mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-24 14:38:45 +00:00
Merge branch 'master' into ubilabs--rcapp--develop
This commit is contained in:
+1
-1
@@ -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
-1
@@ -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> {
|
||||
|
||||
+13
-1
@@ -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
|
||||
+13
-1
@@ -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));
|
||||
|
||||
+9
-2
@@ -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");
|
||||
|
||||
+44
@@ -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();
|
||||
|
||||
}
|
||||
+7
-1
@@ -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();
|
||||
}
|
||||
|
||||
+3
@@ -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();
|
||||
|
||||
}
|
||||
+31
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+50
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+111
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+33
-3
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+9
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-1
@@ -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"),
|
||||
|
||||
-2
@@ -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";
|
||||
|
||||
+13
-3
@@ -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
|
||||
|
||||
+28
@@ -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
-1
@@ -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",
|
||||
|
||||
+2
-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");
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+3
-3
@@ -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)));
|
||||
|
||||
+1
-1
@@ -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));
|
||||
|
||||
+6
@@ -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);
|
||||
|
||||
+17
-1
@@ -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
|
||||
|
||||
+16
@@ -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();
|
||||
|
||||
+3
-4
@@ -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);
|
||||
|
||||
+2
-1
@@ -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")
|
||||
|
||||
+2
-1
@@ -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);
|
||||
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
+1
-1
@@ -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
|
||||
|
||||
+62
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
+35
@@ -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() {
|
||||
|
||||
+1
-1
@@ -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));
|
||||
|
||||
+81
@@ -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;
|
||||
|
||||
+2
-2
@@ -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);
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
|
||||
+8
-8
@@ -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);
|
||||
|
||||
-9
@@ -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();
|
||||
|
||||
}
|
||||
|
||||
+11
-10
@@ -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
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+46
-9
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
|
||||
+1
-1
@@ -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) {
|
||||
|
||||
+2
-2
@@ -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;
|
||||
|
||||
+1
-1
@@ -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);
|
||||
|
||||
+1
-1
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+3
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
-5
@@ -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();
|
||||
|
||||
+31
-3
@@ -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();
|
||||
}
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
Executable → Regular
+63
-35
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+12
@@ -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;
|
||||
|
||||
+79
-30
@@ -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>
|
||||
|
||||
@@ -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",
|
||||
|
||||
+8
-4
@@ -308,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 += "®atta=" + getCtx().getRegattaId();
|
||||
if(getRegattaMetadata().isFlexibleLeaderboard()) {
|
||||
String defaultCourseAreaId = getRegattaMetadata().getDefaultCourseAreaId();
|
||||
if(defaultCourseAreaId != null && !defaultCourseAreaId.isEmpty()) {
|
||||
url += "&coursearea=" + defaultCourseAreaId;
|
||||
}
|
||||
} else {
|
||||
url += "®atta=" + getCtx().getRegattaId();
|
||||
}
|
||||
}
|
||||
return url;
|
||||
}
|
||||
@@ -319,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;
|
||||
}
|
||||
|
||||
+2
-2
@@ -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
|
||||
|
||||
+3
-3
@@ -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
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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());
|
||||
}
|
||||
};
|
||||
|
||||
+1
-1
@@ -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());
|
||||
}
|
||||
|
||||
|
||||
+3
-3
@@ -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);
|
||||
|
||||
+25
-5
@@ -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;
|
||||
display: none !important;
|
||||
}
|
||||
.racesListHideColumn > * {
|
||||
display: none !important;
|
||||
}
|
||||
+2
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
+53
-32
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+4
-3
@@ -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()));
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
+1
-1
@@ -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
-2
@@ -131,7 +131,6 @@ public class RaceListDataUtil {
|
||||
});
|
||||
}
|
||||
|
||||
private static final long DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
|
||||
public static <T extends RaceMetadataDTO<?>> boolean hasDifferentStartDates(Collection<T> data) {
|
||||
return hasDifferentValues(data, new ValueProvider<T, Long>() {
|
||||
@Override
|
||||
@@ -140,7 +139,7 @@ public class RaceListDataUtil {
|
||||
if(start == null) {
|
||||
return null;
|
||||
}
|
||||
return start.getTime() / DAY_IN_MILLIS;
|
||||
return start.getTime() / Duration.ONE_DAY.asMillis();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
+26
-3
@@ -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>"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -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) {
|
||||
|
||||
+5
-2
@@ -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();
|
||||
|
||||
+2
-2
@@ -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()));
|
||||
|
||||
+13
-7
@@ -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;
|
||||
@@ -15,10 +17,12 @@ import com.sap.sailing.gwt.home.shared.dispatch.DispatchSystem;
|
||||
import com.sap.sailing.gwt.ui.shared.dispatch.Action;
|
||||
import com.sap.sailing.gwt.ui.shared.dispatch.DTO;
|
||||
import com.sap.sailing.gwt.ui.shared.dispatch.ResultWithTTL;
|
||||
import com.sap.sse.common.Duration;
|
||||
|
||||
public class RefreshManager {
|
||||
private static final Logger LOG = Logger.getLogger(RefreshManager.class.getName());
|
||||
|
||||
private static final long PAUSE_ON_ERROR = 1000 * 30;
|
||||
private static final long PAUSE_ON_ERROR = Duration.ONE_SECOND.times(30).asMillis();
|
||||
private List<RefreshHolder<DTO, Action<ResultWithTTL<DTO>>>> refreshables = new ArrayList<>();
|
||||
|
||||
private final Timer timer = new Timer() {
|
||||
@@ -47,16 +51,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;
|
||||
@@ -67,8 +69,12 @@ public class RefreshManager {
|
||||
@Override
|
||||
public void onSuccess(ResultWithTTL<DTO> result) {
|
||||
refreshable.callRunning = false;
|
||||
refreshable.timeout = System.currentTimeMillis() + result.getTtl();
|
||||
refreshable.widget.setData(result.getDto(), refreshable.timeout, updateNo);
|
||||
refreshable.timeout = System.currentTimeMillis() + result.getTtlMillis();
|
||||
try {
|
||||
refreshable.widget.setData(result.getDto());
|
||||
} catch(Throwable error) {
|
||||
LOG.log(Level.SEVERE, "Error while refreshing content with action " + action.getClass().getName(), error);
|
||||
}
|
||||
reschedule();
|
||||
}
|
||||
});
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
|
||||
+2
-2
@@ -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() {
|
||||
|
||||
+4
-3
@@ -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.core.client.GWT;
|
||||
import com.google.gwt.core.client.Scheduler;
|
||||
@@ -16,6 +16,7 @@ import com.google.gwt.user.client.Command;
|
||||
import com.google.gwt.user.client.ui.ResizeComposite;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.gwt.ui.shared.media.SailingImageDTO;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public class SailingGalleryPlayer extends ResizeComposite {
|
||||
private static MyBinder uiBinder = GWT.create(MyBinder.class);
|
||||
@@ -31,9 +32,9 @@ public class SailingGalleryPlayer extends ResizeComposite {
|
||||
|
||||
private int selectedIdx;
|
||||
|
||||
public SailingGalleryPlayer(SailingImageDTO selected, List<SailingImageDTO> images) {
|
||||
public SailingGalleryPlayer(SailingImageDTO selected, Collection<SailingImageDTO> images) {
|
||||
initWidget(uiBinder.createAndBindUi(this));
|
||||
selectedIdx = Math.max(selectedIdx, images.indexOf(selected));
|
||||
selectedIdx = Math.max(selectedIdx, Util.indexOf(images, selected));
|
||||
for (SailingImageDTO i : images) {
|
||||
mainSliderUi.appendChild(createMainImgElement(i));
|
||||
subSliderUi.appendChild(createThumbImgElement(i));
|
||||
|
||||
+1
@@ -44,6 +44,7 @@ public class VideoPlayer extends Composite {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
videoJSPlayer.play();
|
||||
playButton.setVisible(false);
|
||||
}
|
||||
}, ClickEvent.getType());
|
||||
panel.add(playButton);
|
||||
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
package com.sap.sailing.gwt.home.mobile.partials.impressions;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.uibinder.client.UiBinder;
|
||||
@@ -57,7 +57,7 @@ public class Impressions extends Composite {
|
||||
headerUi.setSubtitle(sb.toString());
|
||||
}
|
||||
|
||||
public void addImages(List<SailingImageDTO> images) {
|
||||
public void addImages(Collection<SailingImageDTO> images) {
|
||||
if (images.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
+1
-5
@@ -38,17 +38,13 @@ public class MinileaderboardBox extends Composite implements RefreshableWidget<G
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setData(final GetMiniLeaderboardDTO data, long nextUpdate, int updateNo) {
|
||||
setData(data);
|
||||
}
|
||||
|
||||
public void setAction(String infoText, final PlaceNavigation<?> placeNavigation) {
|
||||
headerUi.setInfoText(infoText);
|
||||
headerUi.setClickAction(placeNavigation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setData(final GetMiniLeaderboardDTO data) {
|
||||
itemContainerUi.clearContent();
|
||||
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ public class RegattaStatus extends Composite implements RefreshableWidget<Regatt
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setData(RegattasAndLiveRacesDTO data, long nextUpdate, int updateNo) {
|
||||
public void setData(RegattasAndLiveRacesDTO data) {
|
||||
regattaContainerUi.clearContent();
|
||||
collapsableContainerUi.clearContent();
|
||||
if (data.hasRegattasWithRaces()) {
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ public class StatisticsBox extends Composite implements RefreshableWidget<EventS
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setData(EventStatisticsDTO statistics, long nextUpdate, int updateNo) {
|
||||
public void setData(EventStatisticsDTO statistics) {
|
||||
itemContainerUi.clearContent();
|
||||
if (showRegattaInformation) {
|
||||
addItem(StatisticsBox.ICON_REGATTAS_FOUGHT, MSG.regattas(), statistics.getRegattasFoughtCount());
|
||||
|
||||
+1
-1
@@ -48,7 +48,7 @@ public class UpdatesBox extends Composite implements RefreshableWidget<ListResul
|
||||
|
||||
|
||||
@Override
|
||||
public void setData(final ListResult<NewsEntryDTO> data, long nextUpdate, int updateNo) {
|
||||
public void setData(final ListResult<NewsEntryDTO> data) {
|
||||
setData(data == null ? null :data.getValues());
|
||||
}
|
||||
|
||||
|
||||
+5
-3
@@ -11,7 +11,6 @@ import com.sap.sailing.gwt.ui.shared.general.EventReferenceDTO;
|
||||
public class QuickfinderPresenter {
|
||||
private static final StringMessages MSG = StringMessages.INSTANCE;
|
||||
|
||||
|
||||
public QuickfinderPresenter(Quickfinder quickfinder, RegattaLeaderboardNavigationProvider navigator, Collection<RegattaMetadataDTO> regattaMetadatas) {
|
||||
if (regattaMetadatas == null) {
|
||||
quickfinder.removeFromParent();
|
||||
@@ -19,7 +18,11 @@ public class QuickfinderPresenter {
|
||||
}
|
||||
quickfinder.addPlaceholderItem(MSG.resultsQuickfinder());
|
||||
for (RegattaMetadataDTO regattaMetadata : regattaMetadatas) {
|
||||
quickfinder.addItemToGroup(regattaMetadata.getBoatCategory(), regattaMetadata.getDisplayName(), navigator.getRegattaMiniLeaderboardNavigation(regattaMetadata.getId()));
|
||||
String boatCategory = regattaMetadata.getBoatCategory();
|
||||
if(boatCategory == null || boatCategory.isEmpty()) {
|
||||
boatCategory = MSG.regattas();
|
||||
}
|
||||
quickfinder.addItemToGroup(boatCategory, regattaMetadata.getDisplayName(), navigator.getRegattaMiniLeaderboardNavigation(regattaMetadata.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,5 +41,4 @@ public class QuickfinderPresenter {
|
||||
quickfinder.addItemToGroup(seriesName, displayName, navigator.getMiniLeaderboardNavigation(eventOfSeries.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
package com.sap.sailing.gwt.home.mobile.places.event;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.gwt.home.client.place.event.EventContext;
|
||||
@@ -28,7 +27,7 @@ public interface EventView {
|
||||
|
||||
void hideQuickfinder();
|
||||
|
||||
void setMediaForImpressions(int nrOfImages, int nrOfVideos, List<SailingImageDTO> images);
|
||||
void setMediaForImpressions(int nrOfImages, int nrOfVideos, Collection<SailingImageDTO> images);
|
||||
|
||||
public interface Presenter extends NewsItemLinkProvider, RegattaLeaderboardNavigationProvider, SeriesLeaderboardNavigationProvider {
|
||||
EventContext getCtx();
|
||||
|
||||
+1
-2
@@ -1,7 +1,6 @@
|
||||
package com.sap.sailing.gwt.home.mobile.places.event;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
@@ -129,7 +128,7 @@ public class EventViewImpl extends Composite implements EventView {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setMediaForImpressions(int nrOfImages, int nrOfVideos, List<SailingImageDTO> images) {
|
||||
public void setMediaForImpressions(int nrOfImages, int nrOfVideos, Collection<SailingImageDTO> images) {
|
||||
impressionsUi.getElement().getStyle().setDisplay(Display.BLOCK);
|
||||
impressionsUi.setStatistis(nrOfImages, nrOfVideos);
|
||||
impressionsUi.addImages(images);
|
||||
|
||||
+8
-8
@@ -15,16 +15,16 @@
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
</ui:style>
|
||||
<g:HTMLPanel>
|
||||
<div class="{style.headerWrapper}">
|
||||
<mp:eventheader.EventHeader ui:field="eventHeaderUi" />
|
||||
<mp:quickfinder.Quickfinder ui:field="quickFinderUi" />
|
||||
<mp:simpleinfoblock.SimpleInfoBlock ui:field="simpleInfoUi" />
|
||||
</div>
|
||||
<g:HTMLPanel>
|
||||
<div class="{style.headerWrapper}">
|
||||
<mp:eventheader.EventHeader ui:field="eventHeaderUi" />
|
||||
<mp:quickfinder.Quickfinder ui:field="quickFinderUi" />
|
||||
<mp:simpleinfoblock.SimpleInfoBlock ui:field="simpleInfoUi" />
|
||||
</div>
|
||||
<p:overview.EventOverviewStage ui:field="overviewStageUi" />
|
||||
<g:SimplePanel ui:field="listContentUi"/>
|
||||
<mp:updatesBox.UpdatesBox ui:field="updatesBoxUi" />
|
||||
<mp:impressions.Impressions ui:field="impressionsUi" />
|
||||
<mp:statisticsBox.StatisticsBox ui:field="statisticsBoxUi" />
|
||||
<mp:impressions.Impressions ui:field="impressionsUi" />
|
||||
<mp:statisticsBox.StatisticsBox ui:field="statisticsBoxUi" />
|
||||
</g:HTMLPanel>
|
||||
</ui:UiBinder>
|
||||
|
||||
+1
-1
@@ -36,7 +36,7 @@ public class EventOverviewStage extends Composite implements RefreshableWidget<E
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setData(EventOverviewStageDTO stageData, long nextUpdate, int updateNo) {
|
||||
public void setData(EventOverviewStageDTO stageData) {
|
||||
message.setMessage(stageData.getEventMessage());
|
||||
|
||||
EventOverviewStageContentDTO data = stageData.getStageContent();
|
||||
|
||||
+9
-7
@@ -13,8 +13,10 @@ import com.sap.sailing.gwt.home.mobile.partials.minileaderboard.MinileaderboardB
|
||||
import com.sap.sailing.gwt.home.mobile.partials.quickfinder.Quickfinder;
|
||||
import com.sap.sailing.gwt.home.mobile.partials.recents.EventsOverviewRecentYearEvent;
|
||||
import com.sap.sailing.gwt.home.mobile.partials.seriesheader.SeriesHeader;
|
||||
import com.sap.sailing.gwt.home.mobile.partials.statisticsBox.StatisticsBox;
|
||||
import com.sap.sailing.gwt.home.mobile.places.QuickfinderPresenter;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.shared.dispatch.event.GetEventStatisticsAction;
|
||||
import com.sap.sailing.gwt.ui.shared.dispatch.event.GetMiniOverallLeaderbordAction;
|
||||
import com.sap.sailing.gwt.ui.shared.fakeseries.EventSeriesViewDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.general.EventMetadataDTO;
|
||||
@@ -31,7 +33,7 @@ public class SeriesViewImpl extends Composite implements SeriesView {
|
||||
@UiField Quickfinder quickFinderUi;
|
||||
@UiField(provided = true) MinileaderboardBox leaderboardUi;
|
||||
@UiField FlowPanel eventsUi;
|
||||
// @UiField(provided = true) StatisticsBox statisticsBoxUi;
|
||||
@UiField(provided = true) StatisticsBox statisticsBoxUi;
|
||||
|
||||
private final Presenter currentPresenter;
|
||||
private final RefreshManager refreshManager;
|
||||
@@ -41,7 +43,7 @@ public class SeriesViewImpl extends Composite implements SeriesView {
|
||||
this.refreshManager = new RefreshManager(this, currentPresenter.getDispatch());
|
||||
EventSeriesViewDTO series = currentPresenter.getCtx().getSeriesDTO();
|
||||
eventHeaderUi = new SeriesHeader(series);
|
||||
// this.setupStatisticsBox(event);
|
||||
this.setupStatisticsBox(series);
|
||||
leaderboardUi = new MinileaderboardBox(true);
|
||||
initWidget(uiBinder.createAndBindUi(this));
|
||||
this.setupListContent(series);
|
||||
@@ -70,9 +72,9 @@ public class SeriesViewImpl extends Composite implements SeriesView {
|
||||
leaderboardUi.setAction(MSG.showAll(), currentPresenter.getMiniOverallLeaderboardNavigation());
|
||||
refreshManager.add(leaderboardUi, new GetMiniOverallLeaderbordAction(event.getId(), 3));
|
||||
}
|
||||
//
|
||||
// private void setupStatisticsBox(EventViewDTO event) {
|
||||
// statisticsBoxUi = new StatisticsBox(event.getType() == EventType.MULTI_REGATTA);
|
||||
// refreshManager.add(statisticsBoxUi, new GetEventStatisticsAction(event.getId()));
|
||||
// }
|
||||
|
||||
private void setupStatisticsBox(EventSeriesViewDTO series) {
|
||||
statisticsBoxUi = new StatisticsBox(true);
|
||||
refreshManager.add(statisticsBoxUi, new GetEventStatisticsAction(series.getId(), false));
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -22,6 +22,6 @@
|
||||
</div>
|
||||
<g:FlowPanel ui:field="eventsUi" />
|
||||
<mp:minileaderboard.MinileaderboardBox ui:field="leaderboardUi"/>
|
||||
<!-- <mp:statisticsBox.StatisticsBox ui:field="statisticsBoxUi" /> -->
|
||||
<mp:statisticsBox.StatisticsBox ui:field="statisticsBoxUi" />
|
||||
</g:HTMLPanel>
|
||||
</ui:UiBinder>
|
||||
|
||||
+14
-58
@@ -19,17 +19,12 @@ import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.LeaderboardGroupBase;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.common.dto.FleetDTO;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
|
||||
import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.gwt.ui.shared.eventlist.EventListEventDTO;
|
||||
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.EventMetadataDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.general.EventReferenceDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.general.EventState;
|
||||
@@ -40,7 +35,6 @@ import com.sap.sailing.gwt.ui.shared.start.StageEventType;
|
||||
import com.sap.sailing.server.RacingEventService;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.common.media.ImageDescriptor;
|
||||
import com.sap.sse.common.media.MediaDescriptor;
|
||||
@@ -86,22 +80,6 @@ public final class HomeServiceUtil {
|
||||
return true;
|
||||
}
|
||||
|
||||
public static RegattaState calculateRegattaState(RegattaMetadataDTO regatta) {
|
||||
Date now = new Date();
|
||||
Date startDate = regatta.getStartDate();
|
||||
Date endDate = regatta.getEndDate();
|
||||
if(startDate != null && now.compareTo(startDate) < 0) {
|
||||
return RegattaState.UPCOMING;
|
||||
}
|
||||
if(endDate != null && now.compareTo(endDate) > 0) {
|
||||
return RegattaState.FINISHED;
|
||||
}
|
||||
if(startDate != null && now.compareTo(startDate) >= 0 && endDate != null && now.compareTo(endDate) <= 0) {
|
||||
return RegattaState.RUNNING;
|
||||
}
|
||||
return RegattaState.UNKNOWN;
|
||||
}
|
||||
|
||||
public static EventState calculateEventState(EventBase event) {
|
||||
return calculateEventState(event.isPublic(), event.getStartDate().asDate(), event.getEndDate().asDate());
|
||||
}
|
||||
@@ -450,46 +428,11 @@ public final class HomeServiceUtil {
|
||||
return result;
|
||||
}
|
||||
|
||||
public static RegattaMetadataDTO toRegattaMetadataDTO(EventBase event, LeaderboardGroup leaderboardGroup, Leaderboard leaderboard) {
|
||||
RegattaMetadataDTO regattaDTO = new RegattaMetadataDTO();
|
||||
fillRegattaFields(event, leaderboardGroup, leaderboard, regattaDTO);
|
||||
|
||||
return regattaDTO;
|
||||
}
|
||||
|
||||
public static void fillRegattaFields(EventBase event, LeaderboardGroup leaderboardGroup, Leaderboard leaderboard,
|
||||
RegattaMetadataDTO regattaDTO) {
|
||||
regattaDTO.setId(leaderboard.getName());
|
||||
regattaDTO.setDisplayName(leaderboard.getDisplayName() != null ? leaderboard.getDisplayName() : leaderboard.getName());
|
||||
if(hasMultipleLeaderboardGroups(event)) {
|
||||
regattaDTO.setBoatCategory(leaderboardGroup.getDisplayName() != null ? leaderboardGroup.getDisplayName() : leaderboardGroup.getName());
|
||||
}
|
||||
regattaDTO.setCompetitorsCount(calculateCompetitorsCount(leaderboard));
|
||||
regattaDTO.setRaceCount(calculateRaceColumnCount(leaderboard));
|
||||
regattaDTO.setBoatClass(getBoatClassName(leaderboard));
|
||||
if(leaderboard instanceof RegattaLeaderboard) {
|
||||
Regatta regatta = ((RegattaLeaderboard) leaderboard).getRegatta();
|
||||
regattaDTO.setStartDate(regatta.getStartDate() != null ? regatta.getStartDate().asDate() : null);
|
||||
regattaDTO.setEndDate(regatta.getEndDate() != null ? regatta.getEndDate().asDate() : null);
|
||||
}
|
||||
regattaDTO.setState(calculateRegattaState(regattaDTO));
|
||||
regattaDTO.setDefaultCourseAreaName(getCourseAreaNameForRegattaIdThereIsMoreThanOne(event, leaderboard));
|
||||
}
|
||||
|
||||
private static boolean hasMultipleLeaderboardGroups(EventBase event) {
|
||||
return Util.size(event.getLeaderboardGroups()) > 1;
|
||||
}
|
||||
|
||||
public static boolean hasLiveRace(LeaderboardDTO leaderboard) {
|
||||
List<Pair<RaceColumnDTO, FleetDTO>> liveRaces = leaderboard.getLiveRaces(getLiveTimePointInMillis());
|
||||
return !liveRaces.isEmpty();
|
||||
}
|
||||
|
||||
public static TimePoint getLiveTimePoint() {
|
||||
return new MillisecondsTimePoint(getLiveTimePointInMillis());
|
||||
}
|
||||
|
||||
private static long getLiveTimePointInMillis() {
|
||||
public static long getLiveTimePointInMillis() {
|
||||
// TODO better solution
|
||||
long livePlayDelayInMillis = 15_000;
|
||||
return System.currentTimeMillis() - livePlayDelayInMillis;
|
||||
@@ -511,4 +454,17 @@ public final class HomeServiceUtil {
|
||||
}
|
||||
return courseArea == null ? null : courseArea.getName();
|
||||
}
|
||||
|
||||
public static String getCourseAreaIdForRegatta(EventBase event, Leaderboard leaderboard) {
|
||||
CourseArea courseArea = null;
|
||||
if (leaderboard instanceof FlexibleLeaderboard) {
|
||||
courseArea = ((FlexibleLeaderboard) leaderboard).getDefaultCourseArea();
|
||||
} else if(leaderboard instanceof RegattaLeaderboard) {
|
||||
Regatta regatta = ((RegattaLeaderboard) leaderboard).getRegatta();
|
||||
if (regatta != null) {
|
||||
courseArea = regatta.getDefaultCourseArea();
|
||||
}
|
||||
}
|
||||
return courseArea == null ? null : courseArea.getId().toString();
|
||||
}
|
||||
}
|
||||
|
||||
+54
@@ -36,6 +36,7 @@ import com.sap.sailing.domain.common.dto.AbstractLeaderboardDTO;
|
||||
import com.sap.sailing.domain.common.dto.CompetitorDTO;
|
||||
import com.sap.sailing.domain.common.dto.FleetDTO;
|
||||
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
|
||||
import com.sap.sailing.domain.common.impl.NaturalComparator;
|
||||
import com.sap.sailing.gwt.ui.adminconsole.DisablableCheckboxCell.IsEnabled;
|
||||
import com.sap.sailing.gwt.ui.client.EntryPointLinkFactory;
|
||||
import com.sap.sailing.gwt.ui.client.LeaderboardsDisplayer;
|
||||
@@ -156,7 +157,15 @@ TrackedRaceChangedListener, LeaderboardsDisplayer {
|
||||
return leaderboard.getDisplayName() != null ? leaderboard.getDisplayName() : "";
|
||||
}
|
||||
};
|
||||
leaderboardDisplayNameColumn.setSortable(true);
|
||||
leaderboardColumnListHandler.setComparator(leaderboardDisplayNameColumn, new Comparator<StrippedLeaderboardDTO>() {
|
||||
|
||||
@Override
|
||||
public int compare(StrippedLeaderboardDTO o1, StrippedLeaderboardDTO o2) {
|
||||
return new NaturalComparator().compare(o1.getDisplayName(), o2.getDisplayName());
|
||||
}
|
||||
});
|
||||
|
||||
TextColumn<StrippedLeaderboardDTO> discardingOptionsColumn = new TextColumn<StrippedLeaderboardDTO>() {
|
||||
@Override
|
||||
public String getValue(StrippedLeaderboardDTO leaderboard) {
|
||||
@@ -169,6 +178,25 @@ TrackedRaceChangedListener, LeaderboardsDisplayer {
|
||||
return result;
|
||||
}
|
||||
};
|
||||
discardingOptionsColumn.setSortable(true);
|
||||
leaderboardColumnListHandler.setComparator(discardingOptionsColumn, new Comparator<StrippedLeaderboardDTO>() {
|
||||
@Override
|
||||
public int compare(StrippedLeaderboardDTO o1, StrippedLeaderboardDTO o2) {
|
||||
String s1 = "";
|
||||
String s2 = "";
|
||||
if (o1.discardThresholds != null) {
|
||||
for (int i : o1.discardThresholds) {
|
||||
s1 += i;
|
||||
}
|
||||
}
|
||||
if (o2.discardThresholds != null) {
|
||||
for (int i : o2.discardThresholds) {
|
||||
s2 += i;
|
||||
}
|
||||
}
|
||||
return new NaturalComparator().compare(s1, s2);
|
||||
}
|
||||
});
|
||||
|
||||
TextColumn<StrippedLeaderboardDTO> leaderboardTypeColumn = new TextColumn<StrippedLeaderboardDTO>() {
|
||||
@Override
|
||||
@@ -180,6 +208,14 @@ TrackedRaceChangedListener, LeaderboardsDisplayer {
|
||||
return result;
|
||||
}
|
||||
};
|
||||
leaderboardTypeColumn.setSortable(true);
|
||||
leaderboardColumnListHandler.setComparator(leaderboardTypeColumn, new Comparator<StrippedLeaderboardDTO>() {
|
||||
@Override
|
||||
public int compare(StrippedLeaderboardDTO o1, StrippedLeaderboardDTO o2) {
|
||||
return o1.type.compareTo(o2.type);
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
TextColumn<StrippedLeaderboardDTO> scoringSystemColumn = new TextColumn<StrippedLeaderboardDTO>() {
|
||||
@Override
|
||||
@@ -187,6 +223,16 @@ TrackedRaceChangedListener, LeaderboardsDisplayer {
|
||||
return leaderboard.scoringScheme == null ? "" : ScoringSchemeTypeFormatter.format(leaderboard.scoringScheme, stringMessages);
|
||||
}
|
||||
};
|
||||
scoringSystemColumn.setSortable(true);
|
||||
leaderboardColumnListHandler.setComparator(scoringSystemColumn, new Comparator<StrippedLeaderboardDTO>() {
|
||||
|
||||
@Override
|
||||
public int compare(StrippedLeaderboardDTO o1, StrippedLeaderboardDTO o2) {
|
||||
String s1 = o1.scoringScheme == null ? null:o1.scoringScheme.toString();
|
||||
String s2 = o2.scoringScheme == null ? null:o2.scoringScheme.toString();
|
||||
return new NaturalComparator().compare(s1, s2);
|
||||
}
|
||||
});
|
||||
|
||||
TextColumn<StrippedLeaderboardDTO> courseAreaColumn = new TextColumn<StrippedLeaderboardDTO>() {
|
||||
@Override
|
||||
@@ -194,6 +240,14 @@ TrackedRaceChangedListener, LeaderboardsDisplayer {
|
||||
return leaderboard.defaultCourseAreaId == null ? "" : leaderboard.defaultCourseAreaName;
|
||||
}
|
||||
};
|
||||
courseAreaColumn.setSortable(true);
|
||||
leaderboardColumnListHandler.setComparator(courseAreaColumn, new Comparator<StrippedLeaderboardDTO>() {
|
||||
|
||||
@Override
|
||||
public int compare(StrippedLeaderboardDTO o1, StrippedLeaderboardDTO o2) {
|
||||
return new NaturalComparator().compare(o1.defaultCourseAreaName, o2.defaultCourseAreaName);
|
||||
}
|
||||
});
|
||||
|
||||
ImagesBarColumn<StrippedLeaderboardDTO, LeaderboardConfigImagesBarCell> leaderboardActionColumn = new ImagesBarColumn<StrippedLeaderboardDTO, LeaderboardConfigImagesBarCell>(
|
||||
new LeaderboardConfigImagesBarCell(stringMessages));
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user