mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-08 03:52:18 +00:00
Merge branch 'master' into bug3529
This commit is contained in:
commit
0fa0e3cb6a
275 files changed
+9096
-1388
No files matched your search
@@ -1,55 +1,54 @@
|
||||
#!/bin/bash
|
||||
|
||||
# You need an installation of the Neon release of "Eclipse IDE for Eclipse Committers" matching you OS and JDK (32 vs 64 Bit):
|
||||
# http://www.eclipse.org/downloads/packages/eclipse-ide-eclipse-committers/neonr
|
||||
|
||||
if [[ $1 == "" ]]; then
|
||||
echo "You need to specify the Eclipse installation directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
installPath="$1"
|
||||
|
||||
installPlugins() {
|
||||
"$installPath/eclipse" -application org.eclipse.equinox.p2.director -noSplash -roaming -repository $1 -installIU $2
|
||||
echo ""
|
||||
}
|
||||
|
||||
updatePlugins() {
|
||||
"$installPath/eclipse" -application org.eclipse.equinox.p2.director -noSplash -roaming -repository $1 -uninstallIU $2 -installIU $2
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Not necessary if using Eclipse for JEE developers
|
||||
echo "Installing webtools (WTP/WST/JPT) and m2e that are required by the GWT plugin..."
|
||||
installPlugins http://download.eclipse.org/releases/neon org.eclipse.wst.web_ui.feature.feature.group,org.eclipse.jst.web_ui.feature.feature.group,org.eclipse.wst.xml_ui.feature.feature.group,org.eclipse.jpt.common.feature.feature.group,org.eclipse.jpt.jpa.feature.feature.group,org.eclipse.m2e.feature.feature.group,org.eclipse.m2e.wtp.feature.feature.group
|
||||
|
||||
echo "Installing GWT plugin..."
|
||||
installPlugins http://storage.googleapis.com/gwt-eclipse-plugin/release com.google.gdt.eclipse.suite.e44.feature.feature.group
|
||||
|
||||
echo "Installing Android Tools..."
|
||||
installPlugins https://dl-ssl.google.com/android/eclipse com.android.ide.eclipse.adt.feature.feature.group
|
||||
|
||||
echo "Installing GWT SDM debug bridge..."
|
||||
installPlugins http://p2.sapsailing.com/p2/sdbg com.github.sdbg.feature.feature.group
|
||||
|
||||
# EasyShell is currently not Neon compatible, but 2.0 is coming: https://github.com/anb0s/EasyShell/issues/29
|
||||
# echo "Installing EasyShell..."
|
||||
# installPlugins http://anb0s.github.io/EasyShell com.tetrade.eclipse.plugins.easyshell.feature.feature.group
|
||||
|
||||
echo "Installing BIRT charts (requirement for MAT)..."
|
||||
installPlugins http://download.eclipse.org/birt/update-site/4.6 org.eclipse.birt.chart.feature.group
|
||||
|
||||
echo "Installing Memory Analyzer..."
|
||||
installPlugins http://download.eclipse.org/mat/1.6/update-site/ org.eclipse.mat.feature.feature.group,org.eclipse.mat.chart.feature.feature.group
|
||||
|
||||
echo "Installing latest version of Code Recommenders (Neon release version has bugs)..."
|
||||
updatePlugins http://download.eclipse.org/recommenders/updates/stable/ org.eclipse.recommenders.rcp.feature.feature.group,org.eclipse.recommenders.mylyn.rcp.feature.feature.group,org.eclipse.recommenders.snipmatch.rcp.feature.feature.group,org.eclipse.recommenders.news.rcp.feature.feature.group
|
||||
|
||||
echo "Installing latest version of EGit (Neon release version has bugs)..."
|
||||
updatePlugins http://download.eclipse.org/egit/updates org.eclipse.jgit.feature.group,org.eclipse.jgit.http.apache.feature.group,org.eclipse.egit.feature.group,org.eclipse.egit.mylyn.feature.group
|
||||
|
||||
echo "Installing latest version of Mylyn ..."
|
||||
updatePlugins http://download.eclipse.org/mylyn/releases/latest org.eclipse.mylyn_feature.feature.group,org.eclipse.mylyn.bugzilla_feature.feature.group,org.eclipse.mylyn.builds.feature.group,org.eclipse.mylyn.commons.feature.group,org.eclipse.mylyn.commons.identity.feature.group,org.eclipse.mylyn.commons.notifications.feature.group,org.eclipse.mylyn.commons.repositories.feature.group,org.eclipse.mylyn.commons.repositories.http.feature.group,org.eclipse.mylyn.context_feature.feature.group,org.eclipse.mylyn.discovery.feature.group,org.eclipse.mylyn.gerrit.feature.feature.group,org.eclipse.mylyn.git.feature.group,org.eclipse.mylyn.hudson.feature.group,org.eclipse.mylyn.java_feature.feature.group,org.eclipse.mylyn.monitor.feature.group,org.eclipse.mylyn.reviews.feature.feature.group,org.eclipse.mylyn.team_feature.feature.group,org.eclipse.mylyn.versions.feature.group,org.eclipse.mylyn.wikitext_feature.feature.group
|
||||
|
||||
echo "Installation completed!"
|
||||
#!/bin/bash
|
||||
|
||||
# You need an installation of the Neon SR2 release of "Eclipse IDE for Eclipse Committers" matching you OS and JDK (32 vs 64 Bit):
|
||||
# http://www.eclipse.org/downloads/packages/eclipse-ide-eclipse-committers/neon2
|
||||
|
||||
if [[ $1 == "" ]]; then
|
||||
echo "You need to specify the Eclipse installation directory"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
installPath="$1"
|
||||
|
||||
installPlugins() {
|
||||
"$installPath/eclipse" -application org.eclipse.equinox.p2.director -noSplash -roaming -repository $1 -installIU $2
|
||||
echo ""
|
||||
}
|
||||
|
||||
updatePlugins() {
|
||||
"$installPath/eclipse" -application org.eclipse.equinox.p2.director -noSplash -roaming -repository $1 -uninstallIU $2 -installIU $2
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Not necessary if using Eclipse for JEE developers
|
||||
echo "Installing webtools (WTP/WST/JPT) and m2e that are required by the GWT plugin..."
|
||||
installPlugins http://download.eclipse.org/releases/neon org.eclipse.wst.web_ui.feature.feature.group,org.eclipse.jst.web_ui.feature.feature.group,org.eclipse.wst.xml_ui.feature.feature.group,org.eclipse.jpt.common.feature.feature.group,org.eclipse.jpt.jpa.feature.feature.group,org.eclipse.m2e.feature.feature.group,org.eclipse.m2e.wtp.feature.feature.group
|
||||
|
||||
echo "Installing GWT plugin..."
|
||||
installPlugins http://storage.googleapis.com/gwt-eclipse-plugin/release com.google.gdt.eclipse.suite.e44.feature.feature.group
|
||||
|
||||
echo "Installing Android Tools..."
|
||||
installPlugins https://dl-ssl.google.com/android/eclipse com.android.ide.eclipse.adt.feature.feature.group
|
||||
|
||||
echo "Installing GWT SDM debug bridge..."
|
||||
installPlugins http://p2.sapsailing.com/p2/sdbg com.github.sdbg.feature.feature.group
|
||||
|
||||
echo "Installing EasyShell..."
|
||||
installPlugins http://anb0s.github.io/EasyShell de.anbos.eclipse.easyshell.feature.feature.group
|
||||
|
||||
echo "Installing BIRT charts (requirement for MAT)..."
|
||||
installPlugins http://download.eclipse.org/birt/update-site/4.6 org.eclipse.birt.chart.feature.group
|
||||
|
||||
echo "Installing Memory Analyzer..."
|
||||
installPlugins http://download.eclipse.org/mat/1.6/update-site/ org.eclipse.mat.feature.feature.group,org.eclipse.mat.chart.feature.feature.group
|
||||
|
||||
echo "Installing latest version of Code Recommenders (Neon release version has bugs)..."
|
||||
updatePlugins http://download.eclipse.org/recommenders/updates/stable/ org.eclipse.recommenders.rcp.feature.feature.group,org.eclipse.recommenders.mylyn.rcp.feature.feature.group,org.eclipse.recommenders.snipmatch.rcp.feature.feature.group,org.eclipse.recommenders.news.rcp.feature.feature.group
|
||||
|
||||
echo "Installing latest version of EGit (Neon release version has bugs)..."
|
||||
updatePlugins http://download.eclipse.org/egit/updates org.eclipse.jgit.feature.group,org.eclipse.jgit.http.apache.feature.group,org.eclipse.egit.feature.group,org.eclipse.egit.mylyn.feature.group
|
||||
|
||||
echo "Installing latest version of Mylyn ..."
|
||||
updatePlugins http://download.eclipse.org/mylyn/releases/latest org.eclipse.mylyn_feature.feature.group,org.eclipse.mylyn.bugzilla_feature.feature.group,org.eclipse.mylyn.builds.feature.group,org.eclipse.mylyn.commons.feature.group,org.eclipse.mylyn.commons.identity.feature.group,org.eclipse.mylyn.commons.notifications.feature.group,org.eclipse.mylyn.commons.repositories.feature.group,org.eclipse.mylyn.commons.repositories.http.feature.group,org.eclipse.mylyn.context_feature.feature.group,org.eclipse.mylyn.discovery.feature.group,org.eclipse.mylyn.gerrit.feature.feature.group,org.eclipse.mylyn.git.feature.group,org.eclipse.mylyn.hudson.feature.group,org.eclipse.mylyn.java_feature.feature.group,org.eclipse.mylyn.monitor.feature.group,org.eclipse.mylyn.reviews.feature.feature.group,org.eclipse.mylyn.team_feature.feature.group,org.eclipse.mylyn.versions.feature.group,org.eclipse.mylyn.wikitext_feature.feature.group
|
||||
|
||||
echo "Installation completed!"
|
||||
Executable
BIN
Binary file not shown.
+7
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>com.sap.sailing.datamining,provider</name>
|
||||
<comment></comment>
|
||||
<projects>
|
||||
</projects>
|
||||
<buildSpec>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.ManifestBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
<buildCommand>
|
||||
<name>org.eclipse.pde.SchemaBuilder</name>
|
||||
<arguments>
|
||||
</arguments>
|
||||
</buildCommand>
|
||||
</buildSpec>
|
||||
<natures>
|
||||
<nature>org.eclipse.pde.PluginNature</nature>
|
||||
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||
</natures>
|
||||
</projectDescription>
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.source=1.8
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
eclipse.preferences.version=1
|
||||
pluginProject.extensions=false
|
||||
resolve.requirebundle=false
|
||||
@@ -0,0 +1,14 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: Datamining_provider
|
||||
Bundle-SymbolicName: com.sap.sailing.datamining.provider
|
||||
Bundle-Version: 1.0.0.qualifier
|
||||
Bundle-Vendor: SAP
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Require-Bundle: com.sap.sse.datamining,
|
||||
com.sap.sailing.server,
|
||||
com.sap.sse.common
|
||||
Import-Package: org.osgi.framework;version="1.8.0",
|
||||
org.osgi.util.tracker
|
||||
Export-Package: com.sap.sailing.datamining.provider
|
||||
Bundle-ActivationPolicy: lazy
|
||||
@@ -0,0 +1,4 @@
|
||||
source.. = src/
|
||||
output.. = bin/
|
||||
bin.includes = META-INF/,\
|
||||
.
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"
|
||||
xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<artifactId>root</artifactId>
|
||||
<groupId>com.sap.sailing</groupId>
|
||||
<version>1.0.0-SNAPSHOT</version>
|
||||
</parent>
|
||||
<artifactId>com.sap.sailing.datamining.provider</artifactId>
|
||||
<packaging>eclipse-plugin</packaging>
|
||||
</project>
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.sap.sailing.datamining;
|
||||
package com.sap.sailing.datamining.provider;
|
||||
|
||||
import org.osgi.framework.BundleContext;
|
||||
import org.osgi.util.tracker.ServiceTracker;
|
||||
@@ -1,5 +0,0 @@
|
||||
eclipse.preferences.version=1
|
||||
encoding//resources/stringmessages/Sailing_StringMessages_ja.properties=UTF-8
|
||||
encoding//resources/stringmessages/Sailing_StringMessages_ru.properties=UTF-8
|
||||
encoding//resources/stringmessages/Sailing_StringMessages_zh.properties=UTF-8
|
||||
encoding//resources/stringmessages/Sailing_StringMessages_zh_CN.properties=UTF-8
|
||||
@@ -17,7 +17,8 @@ Require-Bundle: com.sap.sailing.domain,
|
||||
com.sap.sse.datamining.shared,
|
||||
com.sap.sse,
|
||||
com.sap.sse.common,
|
||||
org.eclipse.osgi
|
||||
org.eclipse.osgi,
|
||||
com.sap.sailing.datamining.provider
|
||||
Bundle-ClassPath: .
|
||||
Bundle-Activator: com.sap.sailing.datamining.Activator
|
||||
Bundle-ActivationPolicy: lazy
|
||||
+14
-3
@@ -20,7 +20,8 @@ CompetitorSearchTag=Competitor Search Tag
|
||||
LeaderboardSailingDomainRetrieverChain=Leaderboards
|
||||
RaceSailingDomainRetrieverChain=Races
|
||||
LegSailingDomainRetrieverChain=Legs
|
||||
GPSFixSailingDomainRetrieverChain=GPS-Fixes
|
||||
GPSFixSailingDomainRetrieverChain=GPS Fixes
|
||||
WindFixSailingDomainRetrieverChain=Wind Fixes
|
||||
MarkPassingSailingDomainRetrieverChain=Mark Passings
|
||||
RaceResultSailingDomainRetrieverChain=Race Results
|
||||
RaceOfCompetitorSailingDomainRetrieverChain=Races of Competitors
|
||||
@@ -30,7 +31,8 @@ Leaderboard=Leaderboard
|
||||
Leg=Leg
|
||||
MarkPassing=Mark Passing
|
||||
LegOfCompetitor=Leg of competitor
|
||||
GpsFix=GPS-Fix
|
||||
GpsFix=GPS Fix
|
||||
WindFix=Wind Fix
|
||||
Maneuver=Maneuver
|
||||
WindStrengthInBeaufort=Wind Strength in Beaufort
|
||||
Bft0=Calm
|
||||
@@ -72,6 +74,8 @@ Tack=Tack
|
||||
SpeedBefore=Speed Before Maneuver
|
||||
SpeedAfter=Speed After Maneuver
|
||||
DirectionChange=Direction Change in Degrees
|
||||
AbsoluteDirectionChange=Absolute Direction Change in Degrees (Positive)
|
||||
ToSide=To Side (Port / Starboard)
|
||||
ManeuverLoss=Maneuver Loss
|
||||
Waypoint=Waypoint
|
||||
PassingSide=Passing Side
|
||||
@@ -82,4 +86,11 @@ WindwardDistanceToAdvantageousEndOfLine=Windward Distance to Advantageous End of
|
||||
RankAfterHalfOfTheFirstLeg=Rank after the half of the first leg
|
||||
AdvantageousEndOfLine=Advantageous End of Line
|
||||
TimeSpentInSeconds=Time Spent in Seconds
|
||||
MedalRace=Medal Race
|
||||
MedalRace=Medal Race
|
||||
WindSourceType=Wind Source Type
|
||||
WindSourceName=Wind Source Name
|
||||
windFrom=True Wind Direction (TWD)
|
||||
windSpeedKnots=True Wind Speed (kts)
|
||||
speedInKnots=Speed (kts)
|
||||
bearing=Course over Ground (°T)
|
||||
WindTrack=Wind Track
|
||||
+12
-1
@@ -19,6 +19,7 @@ LeaderboardSailingDomainRetrieverChain=Leaderboards
|
||||
RaceSailingDomainRetrieverChain=Rennen
|
||||
LegSailingDomainRetrieverChain=Schenkel
|
||||
GPSFixSailingDomainRetrieverChain=GPS-Fixes
|
||||
WindFixSailingDomainRetrieverChain=Windmessungen
|
||||
MarkPassingSailingDomainRetrieverChain=Tonnenrundungen
|
||||
RaceResultSailingDomainRetrieverChain=Rennergebnisse
|
||||
RaceOfCompetitorSailingDomainRetrieverChain=Rennen eines Teilnehmers
|
||||
@@ -29,6 +30,7 @@ Leg=Schenkel
|
||||
MarkPassing=Tonnenrundung
|
||||
LegOfCompetitor=Schenkel eines Teilnehmers
|
||||
GpsFix=GPS-Fix
|
||||
WindFix=Windmessung
|
||||
Maneuver=Manöver
|
||||
Speed=Geschwindigkeit
|
||||
SpeedTenSecondsBefore=Geschwindigkeit 10s vorher
|
||||
@@ -72,6 +74,8 @@ Tack=Bug
|
||||
SpeedBefore=Geschwindigkeit bevor dem Manöver
|
||||
SpeedAfter=Geschwindigkeit nach dem Manöver
|
||||
DirectionChange=Richtungsänderung in Grad
|
||||
AbsoluteDirectionChange=Absolute Richtungsänderung in Grad (positiv)
|
||||
ToSide=In Richtung (Backbord / Steuerbord)
|
||||
ManeuverLoss=Manöververlust
|
||||
Waypoint=Wegpunkt
|
||||
PassingSide=Passierende Seite
|
||||
@@ -82,4 +86,11 @@ WindwardDistanceToAdvantageousEndOfLine=Windw
|
||||
RankAfterHalfOfTheFirstLeg=Platzierung nach der Hälfte des ersten Schenkels
|
||||
AdvantageousEndOfLine=Bevorteiltes Ende der Startlinie
|
||||
TimeSpentInSeconds=Benötigte Zeit in Sekunden
|
||||
MedalRace=Medaillenrennen
|
||||
MedalRace=Medaillenrennen
|
||||
WindSourceType=Art der Windmessung
|
||||
WindSourceName=Name der Windmessreihe
|
||||
windFrom=Wind aus Richtung (TWD)
|
||||
windSpeedKnots=Wahre Windgeschwindigkeit (kn)
|
||||
speedInKnots=Fahrt (kn)
|
||||
bearing=Kurs über Grund (°)
|
||||
WindTrack=Windmessreihe
|
||||
@@ -17,12 +17,16 @@ 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.data.HasWindFixContext;
|
||||
import com.sap.sailing.datamining.data.HasWindTrackContext;
|
||||
import com.sap.sailing.datamining.impl.components.aggregators.ParallelBearingAverageDegreesAggregationProcessor;
|
||||
import com.sap.sailing.datamining.impl.components.aggregators.ParallelBoolSumAggregationProcessor;
|
||||
import com.sap.sailing.datamining.impl.components.aggregators.ParallelDistanceAverageAggregationProcessor;
|
||||
import com.sap.sailing.datamining.impl.components.aggregators.ParallelDistanceMaxAggregationProcessor;
|
||||
import com.sap.sailing.datamining.impl.components.aggregators.ParallelDistanceMedianAggregationProcessor;
|
||||
import com.sap.sailing.datamining.impl.components.aggregators.ParallelDistanceMinAggregationProcessor;
|
||||
import com.sap.sailing.datamining.impl.components.aggregators.ParallelDistanceSumAggregationProcessor;
|
||||
import com.sap.sailing.datamining.provider.RacingEventServiceProvider;
|
||||
import com.sap.sse.datamining.DataSourceProvider;
|
||||
import com.sap.sse.datamining.components.AggregationProcessorDefinition;
|
||||
import com.sap.sse.datamining.components.DataRetrieverChainDefinition;
|
||||
@@ -81,6 +85,8 @@ public class Activator extends AbstractDataMiningActivatorWithPredefinedQueries
|
||||
internalClasses.add(HasTrackedLegContext.class);
|
||||
internalClasses.add(HasTrackedLegOfCompetitorContext.class);
|
||||
internalClasses.add(HasGPSFixContext.class);
|
||||
internalClasses.add(HasWindTrackContext.class);
|
||||
internalClasses.add(HasWindFixContext.class);
|
||||
internalClasses.add(HasManeuverContext.class);
|
||||
internalClasses.add(HasMarkPassingContext.class);
|
||||
internalClasses.add(HasRaceOfCompetitorContext.class);
|
||||
@@ -112,6 +118,7 @@ public class Activator extends AbstractDataMiningActivatorWithPredefinedQueries
|
||||
aggregators.add(ParallelDistanceMaxAggregationProcessor.getDefinition());
|
||||
aggregators.add(ParallelDistanceMinAggregationProcessor.getDefinition());
|
||||
aggregators.add(ParallelDistanceMedianAggregationProcessor.getDefinition());
|
||||
aggregators.add(ParallelBearingAverageDegreesAggregationProcessor.getDefinition());
|
||||
return aggregators;
|
||||
}
|
||||
|
||||
@@ -133,7 +140,6 @@ public class Activator extends AbstractDataMiningActivatorWithPredefinedQueries
|
||||
if (INSTANCE == null) {
|
||||
INSTANCE = new Activator(); // probably non-OSGi case, as in test execution
|
||||
}
|
||||
|
||||
return INSTANCE;
|
||||
}
|
||||
|
||||
|
||||
+11
@@ -12,6 +12,8 @@ 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.data.HasWindFixContext;
|
||||
import com.sap.sailing.datamining.data.HasWindTrackContext;
|
||||
import com.sap.sailing.datamining.impl.components.CompetitorOfRaceInLeaderboardRetrievalProcessor;
|
||||
import com.sap.sailing.datamining.impl.components.GPSFixRetrievalProcessor;
|
||||
import com.sap.sailing.datamining.impl.components.LeaderboardGroupRetrievalProcessor;
|
||||
@@ -22,6 +24,8 @@ import com.sap.sailing.datamining.impl.components.RaceOfCompetitorRetrievalProce
|
||||
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.components.WindFixRetrievalProcessor;
|
||||
import com.sap.sailing.datamining.impl.components.WindTrackRetrievalProcessor;
|
||||
import com.sap.sailing.datamining.impl.data.LeaderboardGroupWithContext;
|
||||
import com.sap.sailing.server.RacingEventService;
|
||||
import com.sap.sse.datamining.components.DataRetrieverChainDefinition;
|
||||
@@ -69,6 +73,13 @@ public class SailingDataRetrievalChainDefinitions {
|
||||
HasGPSFixContext.class, "GpsFix");
|
||||
dataRetrieverChainDefinitions.add(gpsFixRetrieverChainDefinition);
|
||||
|
||||
final DataRetrieverChainDefinition<RacingEventService, HasWindFixContext> windFixRetrieverChainDefinition = new SimpleDataRetrieverChainDefinition<>(
|
||||
trackedRaceRetrieverChainDefinition, HasWindFixContext.class, "WindFixSailingDomainRetrieverChain");
|
||||
windFixRetrieverChainDefinition.addAfter(TrackedRaceRetrievalProcessor.class, WindTrackRetrievalProcessor.class, HasWindTrackContext.class, "WindTrack");
|
||||
windFixRetrieverChainDefinition.endWith(WindTrackRetrievalProcessor.class, WindFixRetrievalProcessor.class,
|
||||
HasWindFixContext.class, "WindFix");
|
||||
dataRetrieverChainDefinitions.add(windFixRetrieverChainDefinition);
|
||||
|
||||
final DataRetrieverChainDefinition<RacingEventService, HasManeuverContext> maneuverRetrieverChainDefinition = new SimpleDataRetrieverChainDefinition<>(
|
||||
legOfCompetitorRetrieverChainDefinition, HasManeuverContext.class, "ManeuverSailingDomainRetrieverChain");
|
||||
maneuverRetrieverChainDefinition.endWith(TrackedLegOfCompetitorRetrievalProcessor.class, ManeuverRetrievalProcessor.class,
|
||||
|
||||
+12
-10
@@ -1,22 +1,24 @@
|
||||
package com.sap.sailing.datamining.data;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.datamining.annotations.Connector;
|
||||
import com.sap.sse.datamining.annotations.Dimension;
|
||||
import com.sap.sse.datamining.shared.impl.dto.ClusterDTO;
|
||||
import com.sap.sse.i18n.ResourceBundleStringMessages;
|
||||
|
||||
public interface HasGPSFixContext {
|
||||
|
||||
public interface HasGPSFixContext extends HasWindOnTrackedLeg {
|
||||
@Connector(scanForStatistics=false)
|
||||
public HasTrackedLegOfCompetitorContext getTrackedLegOfCompetitorContext();
|
||||
|
||||
@Connector(ordinal=1)
|
||||
public GPSFixMoving getGPSFix();
|
||||
|
||||
@Dimension(messageKey="WindStrengthInBeaufort", ordinal=11)
|
||||
public ClusterDTO getWindStrengthAsBeaufortCluster(Locale locale, ResourceBundleStringMessages stringMessages);
|
||||
|
||||
@Override
|
||||
default TimePoint getTimePoint() {
|
||||
return getGPSFix().getTimePoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
default Position getPosition() {
|
||||
return getGPSFix().getPosition();
|
||||
}
|
||||
}
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
package com.sap.sailing.datamining.data;
|
||||
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Positioned;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Timed;
|
||||
|
||||
public interface HasManeuver extends Positioned, Timed {
|
||||
Maneuver getManeuver();
|
||||
|
||||
@Override
|
||||
default TimePoint getTimePoint() {
|
||||
return getManeuver().getTimePoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
default Position getPosition() {
|
||||
return getManeuver().getPosition();
|
||||
}
|
||||
}
|
||||
+9
-6
@@ -2,20 +2,17 @@ package com.sap.sailing.datamining.data;
|
||||
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sse.datamining.annotations.Connector;
|
||||
import com.sap.sse.datamining.annotations.Dimension;
|
||||
import com.sap.sse.datamining.annotations.Statistic;
|
||||
|
||||
public interface HasManeuverContext {
|
||||
|
||||
public interface HasManeuverContext extends HasWindOnTrackedLeg, HasManeuver, HasTrackedLegOfCompetitor {
|
||||
@Connector(scanForStatistics=false)
|
||||
HasTrackedLegOfCompetitorContext getTrackedLegOfCompetitorContext();
|
||||
|
||||
Maneuver getManeuver();
|
||||
|
||||
|
||||
//TODO Clean-Up:
|
||||
// Move Dimensions and Statistics to Maneuver and connect to it
|
||||
@Dimension(messageKey="ManeuverType", ordinal=12)
|
||||
@@ -31,6 +28,12 @@ public interface HasManeuverContext {
|
||||
|
||||
@Statistic(messageKey="DirectionChange", resultDecimals=2, ordinal=2)
|
||||
Double getDirectionChangeInDegrees();
|
||||
|
||||
@Statistic(messageKey="AbsoluteDirectionChange", resultDecimals=2, ordinal=2)
|
||||
Double getAbsoluteDirectionChangeInDegrees();
|
||||
|
||||
@Dimension(messageKey="ToSide")
|
||||
NauticalSide getToSide();
|
||||
|
||||
@Statistic(messageKey="ManeuverLoss", resultDecimals=0, ordinal=3)
|
||||
Distance getManeuverLoss();
|
||||
|
||||
+2
-6
@@ -4,18 +4,14 @@ import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.tracking.MarkPassingManeuver;
|
||||
import com.sap.sse.datamining.annotations.Connector;
|
||||
import com.sap.sse.datamining.annotations.Dimension;
|
||||
import com.sap.sse.datamining.annotations.Statistic;
|
||||
|
||||
public interface HasMarkPassingContext {
|
||||
|
||||
public interface HasMarkPassingContext extends HasWindOnTrackedLeg, HasManeuver, HasTrackedLegOfCompetitor {
|
||||
@Connector(scanForStatistics=false)
|
||||
HasTrackedLegOfCompetitorContext getTrackedLegOfCompetitorContext();
|
||||
|
||||
MarkPassingManeuver getManeuver();
|
||||
|
||||
|
||||
//TODO Clean-Up:
|
||||
// Move Dimensions and Statistics to Maneuver/MarkPassingManeuver and connect to it
|
||||
// Find a way to implement HasManeuverContext and HasMarkPassingManeuver with a base class
|
||||
|
||||
+1
@@ -16,6 +16,7 @@ public interface HasRaceOfCompetitorContext {
|
||||
public HasTrackedRaceContext getTrackedRaceContext();
|
||||
|
||||
@Connector(messageKey="Competitor")
|
||||
@Statistic(messageKey="")
|
||||
public Competitor getCompetitor();
|
||||
|
||||
@Dimension(messageKey="CompetitorSearchTag", ordinal=11) // TODO Clean me: Move Dimension to Competitor when possible
|
||||
|
||||
+4
@@ -1,5 +1,6 @@
|
||||
package com.sap.sailing.datamining.data;
|
||||
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sse.datamining.annotations.Connector;
|
||||
import com.sap.sse.datamining.annotations.Dimension;
|
||||
@@ -12,6 +13,9 @@ public interface HasRaceResultOfCompetitorContext {
|
||||
|
||||
@Connector(messageKey="Competitor")
|
||||
public Competitor getCompetitor();
|
||||
|
||||
@Connector(messageKey="BoatClass")
|
||||
BoatClass getBoatClass();
|
||||
|
||||
@Dimension(messageKey="Regatta")
|
||||
String getRegattaName();
|
||||
|
||||
Executable
+5
@@ -0,0 +1,5 @@
|
||||
package com.sap.sailing.datamining.data;
|
||||
|
||||
public interface HasTrackedLegOfCompetitor {
|
||||
HasTrackedLegOfCompetitorContext getTrackedLegOfCompetitorContext();
|
||||
}
|
||||
+1
-2
@@ -8,8 +8,7 @@ import com.sap.sse.datamining.annotations.Dimension;
|
||||
import com.sap.sse.datamining.annotations.Statistic;
|
||||
import com.sap.sse.datamining.shared.impl.dto.ClusterDTO;
|
||||
|
||||
public interface HasTrackedLegOfCompetitorContext {
|
||||
|
||||
public interface HasTrackedLegOfCompetitorContext extends HasWindOnTrackedLeg {
|
||||
@Connector(scanForStatistics=false)
|
||||
public HasTrackedLegContext getTrackedLegContext();
|
||||
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
package com.sap.sailing.datamining.data;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import com.sap.sailing.datamining.Activator;
|
||||
import com.sap.sailing.domain.common.Positioned;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sse.common.Timed;
|
||||
import com.sap.sse.datamining.annotations.Dimension;
|
||||
import com.sap.sse.datamining.data.Cluster;
|
||||
import com.sap.sse.datamining.shared.impl.dto.ClusterDTO;
|
||||
import com.sap.sse.i18n.ResourceBundleStringMessages;
|
||||
|
||||
public interface HasWind extends Positioned, Timed {
|
||||
Wind getWind();
|
||||
|
||||
@Dimension(messageKey="WindStrengthInBeaufort", ordinal=11)
|
||||
default ClusterDTO getWindStrengthAsBeaufortCluster(Locale locale, ResourceBundleStringMessages stringMessages) {
|
||||
Wind wind = getWind();
|
||||
Cluster<?> cluster = Activator.getClusterGroups().getWindStrengthInBeaufortClusterGroup().getClusterFor(wind);
|
||||
return new ClusterDTO(cluster.asLocalizedString(locale, stringMessages));
|
||||
}
|
||||
}
|
||||
Executable
+35
@@ -0,0 +1,35 @@
|
||||
package com.sap.sailing.datamining.data;
|
||||
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.datamining.annotations.Connector;
|
||||
import com.sap.sse.datamining.annotations.Dimension;
|
||||
import com.sap.sse.datamining.annotations.Statistic;
|
||||
|
||||
public interface HasWindFixContext extends HasWind {
|
||||
@Connector(scanForStatistics=false)
|
||||
public HasTrackedRaceContext getTrackedRaceContext();
|
||||
|
||||
@Statistic(messageKey="")
|
||||
public Wind getWind();
|
||||
|
||||
@Statistic(messageKey="windFrom", resultDecimals=1)
|
||||
double getWindFromDegrees();
|
||||
|
||||
@Statistic(messageKey="windSpeedKnots", resultDecimals=1)
|
||||
double getWindSpeedInKnots();
|
||||
|
||||
@Override
|
||||
default TimePoint getTimePoint() {
|
||||
return getWind().getTimePoint();
|
||||
}
|
||||
|
||||
@Override
|
||||
default Position getPosition() {
|
||||
return getWind().getPosition();
|
||||
}
|
||||
|
||||
@Dimension(messageKey="WindSourceType")
|
||||
String getWindSourceType();
|
||||
}
|
||||
Executable
+17
@@ -0,0 +1,17 @@
|
||||
package com.sap.sailing.datamining.data;
|
||||
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
|
||||
public interface HasWindOnTrackedLeg extends HasTrackedLegOfCompetitor, HasWind {
|
||||
default Wind getWind() {
|
||||
if (getWindInternal() == null) {
|
||||
setWindInternal(getTrackedLegOfCompetitorContext().getTrackedLegContext().getTrackedRaceContext().getTrackedRace()
|
||||
.getWind(getPosition(), getTimePoint()));
|
||||
}
|
||||
return getWindInternal();
|
||||
}
|
||||
|
||||
Wind getWindInternal();
|
||||
|
||||
void setWindInternal(Wind wind);
|
||||
}
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
package com.sap.sailing.datamining.data;
|
||||
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sse.datamining.annotations.Connector;
|
||||
import com.sap.sse.datamining.annotations.Dimension;
|
||||
|
||||
public interface HasWindTrackContext {
|
||||
@Connector(scanForStatistics=false)
|
||||
public HasTrackedRaceContext getTrackedRaceContext();
|
||||
|
||||
@Dimension(messageKey="WindSourceType")
|
||||
String getWindSourceType();
|
||||
|
||||
@Dimension(messageKey="WindSourceName")
|
||||
String getWindSourceName();
|
||||
|
||||
WindTrack getWindTrack();
|
||||
}
|
||||
+1
-2
@@ -2,7 +2,6 @@ package com.sap.sailing.datamining.impl.components;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import com.sap.sailing.datamining.data.HasManeuverContext;
|
||||
@@ -26,7 +25,7 @@ public class ManeuverRetrievalProcessor extends AbstractRetrievalProcessor<HasTr
|
||||
TimePoint finishTime = element.getTrackedLegOfCompetitor().getFinishTime();
|
||||
if (finishTime != null) {
|
||||
try {
|
||||
List<Maneuver> maneuvers = element.getTrackedLegOfCompetitor().getManeuvers(finishTime, false);
|
||||
Iterable<Maneuver> maneuvers = element.getTrackedLegOfCompetitor().getManeuvers(finishTime, false);
|
||||
for (Maneuver maneuver : maneuvers) {
|
||||
maneuversWithContext.add(new ManeuverWithContext(element, maneuver));
|
||||
}
|
||||
|
||||
+1
-2
@@ -2,7 +2,6 @@ package com.sap.sailing.datamining.impl.components;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import com.sap.sailing.datamining.data.HasMarkPassingContext;
|
||||
@@ -29,7 +28,7 @@ public class MarkPassingRetrievalProcessor extends AbstractRetrievalProcessor<Ha
|
||||
TimePoint finishTime = element.getTrackedLegOfCompetitor().getFinishTime();
|
||||
if (finishTime != null) {
|
||||
try {
|
||||
List<Maneuver> maneuvers = element.getTrackedLegOfCompetitor().getManeuvers(finishTime, false);
|
||||
Iterable<Maneuver> maneuvers = element.getTrackedLegOfCompetitor().getManeuvers(finishTime, false);
|
||||
for (Maneuver maneuver : maneuvers) {
|
||||
if (maneuver.getType() == ManeuverType.MARK_PASSING) {
|
||||
maneuversWithContext.add(new MarkPassingWithContext(element, (MarkPassingManeuver) maneuver));
|
||||
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
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.HasWindFixContext;
|
||||
import com.sap.sailing.datamining.data.HasWindTrackContext;
|
||||
import com.sap.sailing.datamining.impl.data.WindFixWithContext;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sse.datamining.components.Processor;
|
||||
import com.sap.sse.datamining.impl.components.AbstractRetrievalProcessor;
|
||||
|
||||
/**
|
||||
* Retrieves all wind fixes from a {@link TrackedRace} that are provided by wind sources that are not currently
|
||||
* {@link TrackedRace#getWindSourcesToExclude() excluded}.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class WindFixRetrievalProcessor extends AbstractRetrievalProcessor<HasWindTrackContext, HasWindFixContext> {
|
||||
|
||||
public WindFixRetrievalProcessor(ExecutorService executor, Collection<Processor<HasWindFixContext, ?>> resultReceivers, int retrievalLevel) {
|
||||
super(HasWindTrackContext.class, HasWindFixContext.class, executor, resultReceivers, retrievalLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<HasWindFixContext> retrieveData(HasWindTrackContext element) {
|
||||
Collection<HasWindFixContext> windFixesWithContext = new ArrayList<>();
|
||||
final WindTrack windTrack = element.getWindTrack();
|
||||
windTrack.lockForRead();
|
||||
try {
|
||||
for (final Wind wind : windTrack.getFixes()) {
|
||||
windFixesWithContext.add(new WindFixWithContext(element.getTrackedRaceContext(), wind, element.getWindSourceType()));
|
||||
}
|
||||
} finally {
|
||||
windTrack.unlockAfterRead();
|
||||
}
|
||||
return windFixesWithContext;
|
||||
}
|
||||
|
||||
}
|
||||
+42
@@ -0,0 +1,42 @@
|
||||
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.HasTrackedRaceContext;
|
||||
import com.sap.sailing.datamining.data.HasWindTrackContext;
|
||||
import com.sap.sailing.datamining.impl.data.WindTrackWithContext;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sse.datamining.components.Processor;
|
||||
import com.sap.sse.datamining.impl.components.AbstractRetrievalProcessor;
|
||||
|
||||
/**
|
||||
* Retrieves all wind fixes from a {@link TrackedRace} that are provided by wind sources that are not currently
|
||||
* {@link TrackedRace#getWindSourcesToExclude() excluded}.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class WindTrackRetrievalProcessor extends AbstractRetrievalProcessor<HasTrackedRaceContext, HasWindTrackContext> {
|
||||
|
||||
public WindTrackRetrievalProcessor(ExecutorService executor, Collection<Processor<HasWindTrackContext, ?>> resultReceivers, int retrievalLevel) {
|
||||
super(HasTrackedRaceContext.class, HasWindTrackContext.class, executor, resultReceivers, retrievalLevel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Iterable<HasWindTrackContext> retrieveData(HasTrackedRaceContext element) {
|
||||
Collection<HasWindTrackContext> windTracksWithContext = new ArrayList<>();
|
||||
final TrackedRace trackedRace = element.getTrackedRace();
|
||||
for (final WindSource windSource : trackedRace.getWindSources()) {
|
||||
if (!trackedRace.getWindSourcesToExclude().contains(windSource)) {
|
||||
final WindTrack windTrack = trackedRace.getOrCreateWindTrack(windSource);
|
||||
windTracksWithContext.add(new WindTrackWithContext(element, windTrack, windSource));
|
||||
}
|
||||
}
|
||||
return windTracksWithContext;
|
||||
}
|
||||
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package com.sap.sailing.datamining.impl.components.aggregators;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.BearingCluster;
|
||||
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 ParallelBearingAverageDegreesAggregationProcessor
|
||||
extends AbstractParallelGroupedDataStoringAggregationProcessor<Bearing, Double> {
|
||||
|
||||
private static final AggregationProcessorDefinition<Bearing, Double> DEFINITION =
|
||||
new SimpleAggregationProcessorDefinition<>(Bearing.class, Double.class, "Average", ParallelBearingAverageDegreesAggregationProcessor.class);
|
||||
|
||||
private final Map<GroupKey, BearingCluster> results;
|
||||
|
||||
public static AggregationProcessorDefinition<Bearing, Double> getDefinition() {
|
||||
return DEFINITION;
|
||||
}
|
||||
|
||||
public ParallelBearingAverageDegreesAggregationProcessor(ExecutorService executor,
|
||||
Collection<Processor<Map<GroupKey, Double>, ?>> resultReceivers) {
|
||||
super(executor, resultReceivers, "Average");
|
||||
results = new HashMap<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void storeElement(GroupedDataEntry<Bearing> element) {
|
||||
BearingCluster cluster = results.get(element.getKey());
|
||||
if (cluster == null) {
|
||||
cluster = new BearingCluster();
|
||||
results.put(element.getKey(), cluster);
|
||||
}
|
||||
cluster.add(element.getDataEntry());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Map<GroupKey, Double> aggregateResult() {
|
||||
Map<GroupKey, Double> result = new HashMap<>();
|
||||
for (Entry<GroupKey, BearingCluster> clusterEntry : results.entrySet()) {
|
||||
GroupKey key = clusterEntry.getKey();
|
||||
result.put(key, clusterEntry.getValue().getAverage().getDegrees());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
+1
-2
@@ -7,7 +7,6 @@ import java.util.Map.Entry;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.impl.MeterDistance;
|
||||
import com.sap.sse.datamining.components.AggregationProcessorDefinition;
|
||||
import com.sap.sse.datamining.components.Processor;
|
||||
import com.sap.sse.datamining.impl.components.GroupedDataEntry;
|
||||
@@ -56,7 +55,7 @@ public class ParallelDistanceAverageAggregationProcessor
|
||||
Map<GroupKey, Distance> sumAggregation = sumAggregationProcessor.getResult();
|
||||
for (Entry<GroupKey, Distance> sumAggregationEntry : sumAggregation.entrySet()) {
|
||||
GroupKey key = sumAggregationEntry.getKey();
|
||||
result.put(key, new MeterDistance(sumAggregationEntry.getValue().getMeters() / elementAmountPerKey.get(key).doubleValue()));
|
||||
result.put(key, sumAggregationEntry.getValue().scale(1. / elementAmountPerKey.get(key).doubleValue()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
-1
@@ -44,5 +44,4 @@ public class ParallelDistanceSumAggregationProcessor extends
|
||||
protected Map<GroupKey, Distance> getResult() {
|
||||
return results;
|
||||
}
|
||||
|
||||
}
|
||||
+9
-20
@@ -1,18 +1,13 @@
|
||||
package com.sap.sailing.datamining.impl.data;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
import com.sap.sailing.datamining.Activator;
|
||||
import com.sap.sailing.datamining.data.HasGPSFixContext;
|
||||
import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sse.datamining.data.Cluster;
|
||||
import com.sap.sse.datamining.shared.impl.dto.ClusterDTO;
|
||||
import com.sap.sse.i18n.ResourceBundleStringMessages;
|
||||
|
||||
public class GPSFixWithContext implements HasGPSFixContext {
|
||||
|
||||
private static final long serialVersionUID = -4537126043228674949L;
|
||||
|
||||
private final HasTrackedLegOfCompetitorContext trackedLegOfCompetitorContext;
|
||||
|
||||
private final GPSFixMoving gpsFix;
|
||||
@@ -32,20 +27,14 @@ public class GPSFixWithContext implements HasGPSFixContext {
|
||||
public GPSFixMoving getGPSFix() {
|
||||
return gpsFix;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClusterDTO getWindStrengthAsBeaufortCluster(Locale locale, ResourceBundleStringMessages stringMessages) {
|
||||
Wind wind = getWind();
|
||||
Cluster<?> cluster = Activator.getClusterGroups().getWindStrengthInBeaufortClusterGroup().getClusterFor(wind);
|
||||
return new ClusterDTO(cluster.asLocalizedString(locale, stringMessages));
|
||||
}
|
||||
|
||||
private Wind getWind() {
|
||||
if (wind == null) {
|
||||
wind = getTrackedLegOfCompetitorContext().getTrackedLegContext().getTrackedRaceContext().getTrackedRace()
|
||||
.getWind(gpsFix.getPosition(), gpsFix.getTimePoint());
|
||||
}
|
||||
@Override
|
||||
public Wind getWindInternal() {
|
||||
return wind;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setWindInternal(Wind wind) {
|
||||
this.wind = wind;
|
||||
}
|
||||
}
|
||||
+23
-1
@@ -4,14 +4,17 @@ import com.sap.sailing.datamining.data.HasManeuverContext;
|
||||
import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
|
||||
public class ManeuverWithContext implements HasManeuverContext {
|
||||
|
||||
private static final long serialVersionUID = 7717196485074392156L;
|
||||
private final HasTrackedLegOfCompetitorContext trackedLegOfCompetitor;
|
||||
private final Maneuver maneuver;
|
||||
private Wind wind;
|
||||
|
||||
public ManeuverWithContext(HasTrackedLegOfCompetitorContext trackedLegOfCompetitor, Maneuver maneuver) {
|
||||
this.trackedLegOfCompetitor = trackedLegOfCompetitor;
|
||||
@@ -53,9 +56,28 @@ public class ManeuverWithContext implements HasManeuverContext {
|
||||
return getManeuver().getDirectionChangeInDegrees();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getAbsoluteDirectionChangeInDegrees() {
|
||||
return Math.abs(getManeuver().getDirectionChangeInDegrees());
|
||||
}
|
||||
|
||||
@Override
|
||||
public NauticalSide getToSide() {
|
||||
return getManeuver().getDirectionChangeInDegrees() >= 0 ? NauticalSide.STARBOARD : NauticalSide.PORT;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getManeuverLoss() {
|
||||
return getManeuver().getManeuverLoss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Wind getWindInternal() {
|
||||
return wind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindInternal(Wind wind) {
|
||||
this.wind = wind;
|
||||
}
|
||||
}
|
||||
+12
-1
@@ -7,18 +7,20 @@ import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.tracking.MarkPassingManeuver;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public class MarkPassingWithContext implements HasMarkPassingContext {
|
||||
|
||||
private static final long serialVersionUID = -337042113749307686L;
|
||||
private final HasTrackedLegOfCompetitorContext trackedLegOfCompetitor;
|
||||
private final MarkPassingManeuver maneuver;
|
||||
|
||||
private Double absoluteRank;
|
||||
private boolean rankHasBeenInitialized;
|
||||
private Wind wind;
|
||||
|
||||
public MarkPassingWithContext(HasTrackedLegOfCompetitorContext trackedLegOfCompetitor, MarkPassingManeuver maneuver) {
|
||||
this.trackedLegOfCompetitor = trackedLegOfCompetitor;
|
||||
@@ -84,4 +86,13 @@ public class MarkPassingWithContext implements HasMarkPassingContext {
|
||||
return absoluteRank;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Wind getWindInternal() {
|
||||
return wind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindInternal(Wind wind) {
|
||||
this.wind = wind;
|
||||
}
|
||||
}
|
||||
+6
@@ -7,6 +7,7 @@ import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.datamining.data.HasLeaderboardContext;
|
||||
import com.sap.sailing.datamining.data.HasRaceResultOfCompetitorContext;
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Leg;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
@@ -61,6 +62,11 @@ public class RaceResultOfCompetitorWithContext implements HasRaceResultOfCompeti
|
||||
return competitor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public BoatClass getBoatClass() {
|
||||
return getLeaderboard().getBoatClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRegattaName() {
|
||||
Leaderboard leaderboard = getLeaderboard();;
|
||||
|
||||
+52
@@ -6,7 +6,10 @@ 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.common.Distance;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
@@ -15,6 +18,7 @@ import com.sap.sse.datamining.data.Cluster;
|
||||
import com.sap.sse.datamining.shared.impl.dto.ClusterDTO;
|
||||
|
||||
public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompetitorContext {
|
||||
private static final long serialVersionUID = 5944904146286262768L;
|
||||
|
||||
private final HasTrackedLegContext trackedLegContext;
|
||||
|
||||
@@ -25,6 +29,7 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti
|
||||
private boolean isRankAtStartInitialized;
|
||||
private Double rankAtFinish;
|
||||
private boolean isRankAtFinishInitialized;
|
||||
private Wind wind;
|
||||
|
||||
public TrackedLegOfCompetitorWithContext(HasTrackedLegContext trackedLegContext, TrackedLegOfCompetitor trackedLegOfCompetitor) {
|
||||
this.trackedLegContext = trackedLegContext;
|
||||
@@ -120,5 +125,52 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti
|
||||
|
||||
return (finishTime.asMillis() - startTime.asMillis()) / 1000;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Wind getWindInternal() {
|
||||
return wind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setWindInternal(Wind wind) {
|
||||
this.wind = wind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getPosition() {
|
||||
final TrackedLeg trackedLeg = getTrackedLegContext().getTrackedLeg();
|
||||
final TrackedRace trackedRace = trackedLeg.getTrackedRace();
|
||||
final TimePoint timepoint = getTimePointBetweenLegStartAndLegFinish(trackedRace);
|
||||
final Position result;
|
||||
if (timepoint == null) {
|
||||
result = null;
|
||||
} else {
|
||||
result = trackedLeg.getMiddleOfLeg(timepoint);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private TimePoint getTimePointBetweenLegStartAndLegFinish(final TrackedRace trackedRace) {
|
||||
final TimePoint competitorLegStartTime = getTrackedLegOfCompetitor().getStartTime();
|
||||
final TimePoint competitorLegEndTime = getTrackedLegOfCompetitor().getFinishTime();
|
||||
final TimePoint startTime = competitorLegStartTime != null ? competitorLegStartTime :
|
||||
trackedRace.getStartOfRace() != null ? trackedRace.getStartOfRace() : trackedRace.getStartOfTracking();
|
||||
final TimePoint endTime = competitorLegEndTime != null ? competitorLegEndTime :
|
||||
trackedRace.getEndOfRace() != null ? trackedRace.getEndOfRace() : trackedRace.getEndOfTracking();
|
||||
final TimePoint timepoint = endTime == null ? startTime : startTime == null ? null : startTime.plus(startTime.until(endTime).divide(2));
|
||||
return timepoint;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getTimePoint() {
|
||||
final TrackedLeg trackedLeg = getTrackedLegContext().getTrackedLeg();
|
||||
final TrackedRace trackedRace = trackedLeg.getTrackedRace();
|
||||
return getTimePointBetweenLegStartAndLegFinish(trackedRace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasTrackedLegOfCompetitorContext getTrackedLegOfCompetitorContext() {
|
||||
return this;
|
||||
}
|
||||
|
||||
}
|
||||
+7
-4
@@ -85,12 +85,15 @@ public class TrackedRaceWithContext implements HasTrackedRaceContext {
|
||||
private Integer calculateYear() {
|
||||
TimePoint startOfRace = getTrackedRace().getStartOfRace();
|
||||
TimePoint time = startOfRace != null ? startOfRace : getTrackedRace().getStartOfTracking();
|
||||
final Integer result;
|
||||
if (time == null) {
|
||||
year = 0;
|
||||
result = 0;
|
||||
} else {
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(time.asDate());
|
||||
result = calendar.get(Calendar.YEAR);
|
||||
}
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
calendar.setTime(time.asDate());
|
||||
return calendar.get(Calendar.YEAR);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Executable
+52
@@ -0,0 +1,52 @@
|
||||
package com.sap.sailing.datamining.impl.data;
|
||||
|
||||
import com.sap.sailing.datamining.data.HasTrackedRaceContext;
|
||||
import com.sap.sailing.datamining.data.HasWindFixContext;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
|
||||
public class WindFixWithContext implements HasWindFixContext {
|
||||
private static final long serialVersionUID = -4537126043228674949L;
|
||||
|
||||
private final HasTrackedRaceContext trackedRaceContext;
|
||||
|
||||
private final String windSourceType;
|
||||
|
||||
private final Wind wind;
|
||||
|
||||
public WindFixWithContext(HasTrackedRaceContext trackedRaceContext, Wind wind, String windSourceType) {
|
||||
this.trackedRaceContext = trackedRaceContext;
|
||||
this.wind = wind;
|
||||
this.windSourceType = windSourceType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasTrackedRaceContext getTrackedRaceContext() {
|
||||
return trackedRaceContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Wind getWind() {
|
||||
return wind;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getWindFromDegrees() {
|
||||
return getWind().getFrom().getDegrees();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getWindSpeedInKnots() {
|
||||
return getWind().getKnots();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWindSourceType() {
|
||||
return windSourceType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WindFixWithContext [trackedRaceContext=" + trackedRaceContext + ", windSourceType=" + windSourceType
|
||||
+ ", wind=" + wind + "]";
|
||||
}
|
||||
}
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
package com.sap.sailing.datamining.impl.data;
|
||||
|
||||
import com.sap.sailing.datamining.data.HasTrackedRaceContext;
|
||||
import com.sap.sailing.datamining.data.HasWindTrackContext;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
|
||||
public class WindTrackWithContext implements HasWindTrackContext {
|
||||
|
||||
private final HasTrackedRaceContext trackedRaceContext;
|
||||
|
||||
private final String windSourceType;
|
||||
|
||||
private final String windSourceName;
|
||||
|
||||
private final WindTrack windTrack;
|
||||
|
||||
public WindTrackWithContext(HasTrackedRaceContext trackedRaceContext, WindTrack windTrack, WindSource windSource) {
|
||||
this.trackedRaceContext = trackedRaceContext;
|
||||
this.windTrack = windTrack;
|
||||
this.windSourceType = windSource.getType().name();
|
||||
this.windSourceName = windSource.getType().name()+(windSource.getId()==null?"":" ("+windSource.getId()+")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public HasTrackedRaceContext getTrackedRaceContext() {
|
||||
return trackedRaceContext;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWindSourceType() {
|
||||
return windSourceType;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "WindTrackWithContext [trackedRaceContext=" + trackedRaceContext + ", windSourceType=" + windSourceType
|
||||
+ ", windSourceName=" + windSourceName + "]";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getWindSourceName() {
|
||||
return windSourceName;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindTrack getWindTrack() {
|
||||
return windTrack;
|
||||
}
|
||||
|
||||
}
|
||||
+5
-1
@@ -57,7 +57,11 @@ public abstract class AbstractBearing implements Bearing {
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
return object != null && object instanceof Bearing && getDegrees() == ((Bearing) object).getDegrees();
|
||||
if (this == object) {
|
||||
return true;
|
||||
} else {
|
||||
return object != null && object instanceof Bearing && getDegrees() == ((Bearing) object).getDegrees();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
@@ -16,6 +16,8 @@ public class AbstractPosition implements Position {
|
||||
public boolean equals(Object o) {
|
||||
if (o == null) {
|
||||
return false;
|
||||
} if (this == o) {
|
||||
return true;
|
||||
} else {
|
||||
return o instanceof Position && getLatRad() == ((Position) o).getLatRad()
|
||||
&& getLngRad() == ((Position) o).getLngRad();
|
||||
|
||||
+6
@@ -58,6 +58,12 @@ public enum DataImportSubProgress {
|
||||
return messages.importWindTracks();
|
||||
}
|
||||
},
|
||||
IMPORT_SENSOR_FIXES {
|
||||
@Override
|
||||
public String getMessage(CommonStringMessages messages) {
|
||||
return messages.importSensorFixes();
|
||||
}
|
||||
},
|
||||
UPDATE_EVENT_LEADERBOARD_GROUP_LINKS {
|
||||
@Override
|
||||
public String getMessage(CommonStringMessages messages) {
|
||||
|
||||
@@ -3,6 +3,6 @@ package com.sap.sailing.domain.common;
|
||||
import com.sap.sse.datamining.annotations.Connector;
|
||||
|
||||
public interface Moving {
|
||||
@Connector(messageKey="Speed")
|
||||
@Connector(messageKey="")
|
||||
SpeedWithBearing getSpeed();
|
||||
}
|
||||
@@ -20,7 +20,7 @@ public interface Position extends Serializable {
|
||||
Distance getDistance(Position p);
|
||||
|
||||
/**
|
||||
* The bearing from this position towards <code>p</code> on a great circle.
|
||||
* The bearing from this position towards <code>p</code> on a great circle, using the shortest path.
|
||||
*
|
||||
* @return {@code null} if {@code p} is {@code null}, otherwise the bearing from {@code this} {@link Position} to
|
||||
* {@code p} along a great circle
|
||||
|
||||
@@ -67,7 +67,7 @@ public interface Speed extends Comparable<Speed>, Serializable {
|
||||
}
|
||||
};
|
||||
|
||||
@Statistic(messageKey="", resultDecimals=2)
|
||||
@Statistic(messageKey="speedInKnots", resultDecimals=2)
|
||||
double getKnots();
|
||||
|
||||
double getMetersPerSecond();
|
||||
|
||||
+2
@@ -1,10 +1,12 @@
|
||||
package com.sap.sailing.domain.common;
|
||||
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.datamining.annotations.Statistic;
|
||||
|
||||
|
||||
|
||||
public interface SpeedWithBearing extends Speed {
|
||||
@Statistic(messageKey="bearing", resultDecimals=1)
|
||||
Bearing getBearing();
|
||||
|
||||
/**
|
||||
|
||||
+1
@@ -12,6 +12,7 @@ public interface CommonStringMessages {
|
||||
String importWait();
|
||||
String importLeaderboardGroups();
|
||||
String importWindTracks();
|
||||
String importSensorFixes();
|
||||
String updateEventLeaderboardGroupLinks();
|
||||
|
||||
}
|
||||
+12
@@ -15,4 +15,16 @@ public class KnotSpeedImpl extends AbstractSpeedImpl implements Speed {
|
||||
public double getKnots() {
|
||||
return knots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Saves extra work by only converting the other object's speed to knots; useful in particular
|
||||
* if both speeds are knot-based speeds
|
||||
*/
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
if (object == null || !(object instanceof Speed)) {
|
||||
return false;
|
||||
}
|
||||
return getKnots() == ((Speed) object).getKnots();
|
||||
}
|
||||
}
|
||||
+33
@@ -32,6 +32,39 @@ public class WindImpl extends KnotSpeedWithBearingImpl implements Wind {
|
||||
return getBearing().reverse();
|
||||
}
|
||||
|
||||
public static int hashCode(double latDeg, double lngDeg, long timePointAsMillis) {
|
||||
return (31 * (int) (timePointAsMillis & Integer.MAX_VALUE)) ^ (int) (latDeg*lngDeg);
|
||||
}
|
||||
/**
|
||||
* Wind hash is determined based on time point and position only to speed this up a little.
|
||||
*/
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return hashCode(position.getLatDeg(), position.getLngDeg(), timepoint==null?0:timepoint.asMillis());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (!super.equals(obj))
|
||||
return false;
|
||||
if (!(obj instanceof Wind))
|
||||
return false;
|
||||
Wind other = (Wind) obj;
|
||||
if (position == null) {
|
||||
if (other.getPosition() != null)
|
||||
return false;
|
||||
} else if (!position.equals(other.getPosition()))
|
||||
return false;
|
||||
if (timepoint == null) {
|
||||
if (other.getTimePoint() != null)
|
||||
return false;
|
||||
} else if (!timepoint.equals(other.getTimePoint()))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ""+getTimePoint()+"@"+getPosition()+": "+getKnots()+"kn from "+getFrom();
|
||||
|
||||
+7
@@ -1,5 +1,6 @@
|
||||
package com.sap.sailing.domain.common.racelog.tracking;
|
||||
|
||||
import com.sap.sse.common.TimeRange;
|
||||
import com.sap.sse.common.util.UrlHelper;
|
||||
|
||||
/**
|
||||
@@ -35,6 +36,12 @@ public class DeviceMappingConstants {
|
||||
public static final String JSON_DEVICE_TYPE = "deviceType";
|
||||
public static final String JSON_PUSH_DEVICE_ID = "pushDeviceId";
|
||||
public static final String JSON_FROM_MILLIS = "fromMillis";
|
||||
|
||||
/**
|
||||
* The parameter to specify the end of the mapping interval, inclusively. Note that when asking a {@link TimeRange} describing
|
||||
* the interval, the {@link TimeRange#to()} method has to return a value "infinitesimally" later than the time point provided here
|
||||
* because {@link TimeRange}s have an <em>exclusive</em> end.
|
||||
*/
|
||||
public static final String JSON_TO_MILLIS = "toMillis";
|
||||
public static final String JSON_TEAM_IMAGE_URI = "teamImageUri";
|
||||
|
||||
|
||||
+1
@@ -37,6 +37,7 @@ public enum Permission implements com.sap.sse.security.shared.Permission {
|
||||
LEADERBOARD,
|
||||
LEADERBOARD_GROUP,
|
||||
TRACKED_RACE,
|
||||
DATA_MINING,
|
||||
;
|
||||
|
||||
// TODO once we can use Java8 here, move this up into a "default" method on the Permission interface
|
||||
|
||||
+29
@@ -6,7 +6,9 @@ import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Wind;
|
||||
import com.sap.sailing.domain.common.impl.AbstractSpeedWithAbstractBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.WindImpl;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.AbstractTimePoint;
|
||||
|
||||
public class CompactWindImpl extends AbstractSpeedWithAbstractBearingImpl implements Wind {
|
||||
@@ -32,6 +34,11 @@ public class CompactWindImpl extends AbstractSpeedWithAbstractBearingImpl implem
|
||||
public double getLngDeg() {
|
||||
return lngDeg;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return this==o || o instanceof Position && getLatDeg() == ((Position) o).getLatDeg() && getLngDeg() == ((Position) o).getLngDeg();
|
||||
}
|
||||
}
|
||||
|
||||
private class CompactBearing extends AbstractBearing {
|
||||
@@ -46,6 +53,11 @@ public class CompactWindImpl extends AbstractSpeedWithAbstractBearingImpl implem
|
||||
public double getRadians() {
|
||||
return degBearing / 180. * Math.PI;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object object) {
|
||||
return this==object || object instanceof Bearing && getDegrees() == ((Bearing) object).getDegrees();
|
||||
}
|
||||
}
|
||||
|
||||
private class CompactTimePoint extends AbstractTimePoint implements TimePoint {
|
||||
@@ -125,6 +137,23 @@ public class CompactWindImpl extends AbstractSpeedWithAbstractBearingImpl implem
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return WindImpl.hashCode(latDeg, lngDeg, timePointIsNull?0:timePointAsMillis);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (this == obj)
|
||||
return true;
|
||||
if (!super.equals(obj))
|
||||
return false;
|
||||
if (!(obj instanceof Wind))
|
||||
return false;
|
||||
Wind other = (Wind) obj;
|
||||
return Util.equalsWithNull(getPosition(), other.getPosition()) && Util.equalsWithNull(getTimePoint(), other.getTimePoint());
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return ""+getTimePoint()+"@"+getPosition()+": "+getKnots()+"kn from "+getFrom();
|
||||
|
||||
@@ -8,8 +8,8 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Require-Bundle: org.json.simple;bundle-version="1.1.0",
|
||||
com.sun.jersey;bundle-version="1.17.0",
|
||||
org.objectweb.asm;bundle-version="3.3.1",
|
||||
org.apache.httpcomponents.httpclient;bundle-version="4.2.5",
|
||||
org.apache.httpcomponents.httpcore;bundle-version="4.0.0",
|
||||
org.apache.httpcomponents.httpclient;bundle-version="[4.2.5,4.3.0)",
|
||||
org.apache.httpcomponents.httpcore;bundle-version="[4.0.0,4.3.0)",
|
||||
com.sap.sailing.domain.common,
|
||||
com.sap.sailing.domain.igtimiadapter.persistence,
|
||||
com.sap.sailing.domain.shared.android,
|
||||
|
||||
+1
-1
@@ -37,7 +37,7 @@ public class ClientImpl implements Client {
|
||||
|
||||
@Override
|
||||
public String getRedirectUri(String redirectProtocol, String redirectHost, String redirectPort) {
|
||||
return redirectProtocol+"://"+redirectHost+(redirectPort==null?"":(":"+redirectPort))+
|
||||
return redirectProtocol+"://"+redirectHost+(redirectPort==null||redirectPort.isEmpty()?"":(":"+redirectPort))+
|
||||
(redirectUriPath.startsWith("/")?redirectUriPath:"/"+redirectUriPath);
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -1761,9 +1761,9 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
AbstractLogEventAuthor author, TimePoint logicalTimePoint, Serializable id, DBObject dbObject) {
|
||||
Serializable deviceMappingEventId = Helpers.tryUuidConversion((Serializable) dbObject.get(
|
||||
FieldNames.REGATTA_LOG_DEVICE_MAPPING_EVENT_ID.name()));
|
||||
TimePoint closingTimePoint = loadTimePoint(dbObject, FieldNames.REGATTA_LOG_CLOSING_TIMEPOINT);
|
||||
TimePoint closingTimePointInclusive = loadTimePoint(dbObject, FieldNames.REGATTA_LOG_CLOSING_TIMEPOINT);
|
||||
return new RegattaLogCloseOpenEndedDeviceMappingEventImpl(createdAt, author, logicalTimePoint, id,
|
||||
deviceMappingEventId, closingTimePoint);
|
||||
deviceMappingEventId, closingTimePointInclusive);
|
||||
}
|
||||
|
||||
private RegattaLogDeviceMarkMappingEvent loadRegattaLogDeviceMarkMappingEvent(TimePoint createdAt, AbstractLogEventAuthor author,
|
||||
|
||||
+3
-3
@@ -975,8 +975,8 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
if (event.getFrom() != null) {
|
||||
storeTimePoint(event.getFrom(), result, fromField);
|
||||
}
|
||||
if (event.getTo() != null) {
|
||||
storeTimePoint(event.getTo(), result, toField);
|
||||
if (event.getToInclusive() != null) {
|
||||
storeTimePoint(event.getToInclusive(), result, toField);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1445,7 +1445,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
DBObject result = createBasicRegattaLogEventDBObject(event);
|
||||
result.put(FieldNames.REGATTA_LOG_EVENT_CLASS.name(), RegattaLogCloseOpenEndedDeviceMappingEvent.class.getSimpleName());
|
||||
result.put(FieldNames.REGATTA_LOG_DEVICE_MAPPING_EVENT_ID.name(), event.getDeviceMappingEventId());
|
||||
storeTimePoint(event.getClosingTimePoint(), result, FieldNames.REGATTA_LOG_CLOSING_TIMEPOINT);
|
||||
storeTimePoint(event.getClosingTimePointInclusive(), result, FieldNames.REGATTA_LOG_CLOSING_TIMEPOINT);
|
||||
storeRegattaLogEvent(regattaLikeId, result);
|
||||
}
|
||||
|
||||
|
||||
+22
-3
@@ -9,6 +9,7 @@ import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.UnaryOperator;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -87,10 +88,25 @@ public class MongoSensorFixStoreImpl implements MongoSensorFixStore {
|
||||
DBObject fixObject = (DBObject) object.get(FieldNames.GPSFIX.name());
|
||||
return this.<T> findService(type).transformBack(fixObject);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public <FixT extends Timed> boolean loadOldestFix(Consumer<FixT> consumer, DeviceIdentifier device, TimeRange timeRangeToLoad) throws NoCorrespondingServiceRegisteredException, TransformationException {
|
||||
return loadFixes(consumer, device, timeRangeToLoad.from(), timeRangeToLoad.to(), false, cursor -> cursor.sort(new BasicDBObject(FieldNames.TIME_AS_MILLIS.name(), /* ascending */ 1)).limit(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <FixT extends Timed> boolean loadYoungestFix(Consumer<FixT> consumer, DeviceIdentifier device, TimeRange timeRangeToLoad) throws NoCorrespondingServiceRegisteredException, TransformationException {
|
||||
return loadFixes(consumer, device, timeRangeToLoad.from(), timeRangeToLoad.to(), false, cursor -> cursor.sort(new BasicDBObject(FieldNames.TIME_AS_MILLIS.name(), /* descending */ -1)).limit(1));
|
||||
}
|
||||
|
||||
@Override
|
||||
public <FixT extends Timed> void loadFixes(Consumer<FixT> consumer, DeviceIdentifier device, TimePoint from,
|
||||
TimePoint to, boolean inclusive) throws NoCorrespondingServiceRegisteredException, TransformationException {
|
||||
loadFixes(consumer, device, from, to, inclusive, UnaryOperator.identity());
|
||||
}
|
||||
|
||||
private <FixT extends Timed> boolean loadFixes(Consumer<FixT> consumer, DeviceIdentifier device, TimePoint from,
|
||||
TimePoint to, boolean inclusive, UnaryOperator<DBCursor> dbCursorCallback) throws NoCorrespondingServiceRegisteredException, TransformationException {
|
||||
final TimePoint loadFixesFrom = from == null ? TimePoint.BeginningOfTime : from;
|
||||
final TimePoint loadFixesTo = to == null ? TimePoint.EndOfTime : to;
|
||||
|
||||
@@ -101,15 +117,17 @@ public class MongoSensorFixStoreImpl implements MongoSensorFixStore {
|
||||
queryBuilder.greaterThanEquals(loadFixesFrom.asMillis()).and(FieldNames.TIME_AS_MILLIS.name())
|
||||
.lessThanEquals(loadFixesTo.asMillis());
|
||||
} else {
|
||||
queryBuilder.greaterThan(loadFixesFrom.asMillis()).and(FieldNames.TIME_AS_MILLIS.name()).lessThan(loadFixesTo.asMillis());
|
||||
queryBuilder.greaterThanEquals(loadFixesFrom.asMillis()).and(FieldNames.TIME_AS_MILLIS.name()).lessThan(loadFixesTo.asMillis());
|
||||
}
|
||||
DBObject query = queryBuilder.get();
|
||||
DBCursor result = fixesCollection.find(query);
|
||||
DBCursor result = dbCursorCallback.apply(fixesCollection.find(query));
|
||||
boolean fixLoaded = false;
|
||||
for (DBObject fixObject : result) {
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
FixT fix = (FixT) loadFix(fixObject);
|
||||
consumer.accept(fix);
|
||||
fixLoaded = true;
|
||||
} catch (TransformationException e) {
|
||||
logger.log(Level.WARNING, "Could not read fix from MongoDB: " + fixObject);
|
||||
} catch (ClassCastException e) {
|
||||
@@ -118,6 +136,7 @@ public class MongoSensorFixStoreImpl implements MongoSensorFixStore {
|
||||
"Unexpected fix type (" + type + ") encountered when trying to load track for " + device);
|
||||
}
|
||||
}
|
||||
return fixLoaded;
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+7
@@ -30,7 +30,9 @@ import com.sap.sailing.domain.base.impl.RegattaImpl;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
|
||||
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
|
||||
import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl;
|
||||
import com.sap.sailing.domain.persistence.racelog.tracking.impl.MongoSensorFixStoreImpl;
|
||||
@@ -70,6 +72,11 @@ public class AbstractGPSFixStoreTest {
|
||||
return new GPSFixMovingImpl(new DegreePosition(lat, lng),
|
||||
new MillisecondsTimePoint(millis), new KnotSpeedWithBearingImpl(knots, new DegreeBearingImpl(degrees)));
|
||||
}
|
||||
|
||||
protected GPSFix createFix(long millis, double lat, double lng) {
|
||||
return new GPSFixImpl(new DegreePosition(lat, lng),
|
||||
new MillisecondsTimePoint(millis));
|
||||
}
|
||||
|
||||
@Before
|
||||
public void setServiceAndRaceLog() {
|
||||
|
||||
+48
-12
@@ -32,6 +32,7 @@ import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifie
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sse.common.Timed;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.common.impl.TimeRangeImpl;
|
||||
|
||||
public class SensorFixStoreTest {
|
||||
private static final long FIX_TIMESTAMP = 110;
|
||||
@@ -114,10 +115,26 @@ public class SensorFixStoreTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFixesOnExclusiveBoundsArentLoaded() throws Exception {
|
||||
addBravoFix(device, FIX_TIMESTAMP, FIX_RIDE_HEIGHT);
|
||||
public void testFixesOnExclusiveEndBoundsArentLoaded() throws Exception {
|
||||
DoubleVectorFix fix = addBravoFix(device, FIX_TIMESTAMP, FIX_RIDE_HEIGHT);
|
||||
addBravoFix(device, FIX_TIMESTAMP2, FIX_RIDE_HEIGHT2);
|
||||
verifyNoFix(FIX_TIMESTAMP, FIX_TIMESTAMP2, device, false);
|
||||
verifySingleFix(fix, FIX_TIMESTAMP, FIX_TIMESTAMP2, device, false);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testYoungestFix() throws Exception {
|
||||
addBravoFix(device, FIX_TIMESTAMP, FIX_RIDE_HEIGHT);
|
||||
addBravoFix(device, FIX_TIMESTAMP + 2, FIX_RIDE_HEIGHT2);
|
||||
DoubleVectorFix youngestFix = addBravoFix(device, FIX_TIMESTAMP + 5, FIX_RIDE_HEIGHT2);
|
||||
verifyYoungestFix(youngestFix, FIX_TIMESTAMP, FIX_TIMESTAMP2, device);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOldestFix() throws Exception {
|
||||
DoubleVectorFix oldestFix = addBravoFix(device, FIX_TIMESTAMP, FIX_RIDE_HEIGHT);
|
||||
addBravoFix(device, FIX_TIMESTAMP + 2, FIX_RIDE_HEIGHT2);
|
||||
addBravoFix(device, FIX_TIMESTAMP + 5, FIX_RIDE_HEIGHT2);
|
||||
verifyOldestFix(oldestFix, FIX_TIMESTAMP, FIX_TIMESTAMP2, device);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -268,24 +285,43 @@ public class SensorFixStoreTest {
|
||||
return listener;
|
||||
}
|
||||
|
||||
private void verifySingleFix(Timed expectedFix, long start, long end, DeviceIdentifier device, boolean inclusive) throws Exception {
|
||||
List<Timed> loadedFixes = loadFixes(start, end, device, inclusive);
|
||||
private void verifySingleFix(Timed expectedFix, long start, long end, DeviceIdentifier device, boolean endIsInclusive) throws Exception {
|
||||
List<Timed> loadedFixes = loadFixes(start, end, device, endIsInclusive);
|
||||
assertEquals(1, loadedFixes.size());
|
||||
assertEquals(expectedFix, loadedFixes.get(0));
|
||||
}
|
||||
|
||||
private void verifyNoFix(long start, long end, DeviceIdentifier device, boolean inclusive) throws Exception {
|
||||
List<Timed> loadedFixes = loadFixes(start, end, device, inclusive);
|
||||
assertEquals(0, loadedFixes.size());
|
||||
}
|
||||
|
||||
private List<Timed> loadFixes(long start, long end, DeviceIdentifier device, boolean inclusive)
|
||||
private List<Timed> loadFixes(long start, long end, DeviceIdentifier device, boolean endIsInclusive)
|
||||
throws TransformationException {
|
||||
List<Timed> loadedFixes = new ArrayList<>();
|
||||
|
||||
store.loadFixes(loadedFixes::add, device, new MillisecondsTimePoint(start), new MillisecondsTimePoint(end), inclusive);
|
||||
store.loadFixes(loadedFixes::add, device, new MillisecondsTimePoint(start), new MillisecondsTimePoint(end), endIsInclusive);
|
||||
return loadedFixes;
|
||||
}
|
||||
|
||||
private void verifyYoungestFix(Timed expectedFix, long start, long end, DeviceIdentifier device) throws Exception {
|
||||
assertEquals(expectedFix, loadYoungestFix(start, end, device));
|
||||
}
|
||||
|
||||
private Timed loadYoungestFix(long start, long end, DeviceIdentifier device)
|
||||
throws TransformationException {
|
||||
List<Timed> loadedFixes = new ArrayList<>();
|
||||
store.loadYoungestFix(loadedFixes::add, device, new TimeRangeImpl(new MillisecondsTimePoint(start), new MillisecondsTimePoint(end)));
|
||||
assert(loadedFixes.size() <= 1);
|
||||
return loadedFixes.isEmpty() ? null : loadedFixes.get(0);
|
||||
}
|
||||
|
||||
private void verifyOldestFix(Timed expectedFix, long start, long end, DeviceIdentifier device) throws Exception {
|
||||
assertEquals(expectedFix, loadOldestFix(start, end, device));
|
||||
}
|
||||
|
||||
private Timed loadOldestFix(long start, long end, DeviceIdentifier device)
|
||||
throws TransformationException {
|
||||
List<Timed> loadedFixes = new ArrayList<>();
|
||||
store.loadOldestFix(loadedFixes::add, device, new TimeRangeImpl(new MillisecondsTimePoint(start), new MillisecondsTimePoint(end)));
|
||||
assert(loadedFixes.size() <= 1);
|
||||
return loadedFixes.isEmpty() ? null : loadedFixes.get(0);
|
||||
}
|
||||
|
||||
private DoubleVectorFix addBravoFix(DeviceIdentifier device, long timestamp, double rideHeight) {
|
||||
DoubleVectorFix fix = createBravoDoubleVectorFixWithRideHeight(timestamp, rideHeight);
|
||||
|
||||
+176
-41
@@ -25,11 +25,13 @@ import com.sap.sailing.domain.base.impl.CourseImpl;
|
||||
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
|
||||
import com.sap.sailing.domain.base.impl.WaypointImpl;
|
||||
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.racelogtracking.impl.fixtracker.FixLoaderAndTracker;
|
||||
import com.sap.sailing.domain.racelogtracking.test.AbstractGPSFixStoreTest;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.GPSTrackListener;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
|
||||
import com.sap.sse.common.Timed;
|
||||
@@ -60,9 +62,9 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
store.storeFix(device, createFix(100, 10, 20, 30, 40)); // before
|
||||
store.storeFix(device, createFix(1100, 10, 20, 30, 40)); // in
|
||||
store.storeFix(device, createFix(2100, 10, 20, 30, 40)); // after
|
||||
store.storeFix(markDevice, createFix(100, 10, 20, 30, 40));
|
||||
store.storeFix(markDevice, createFix(1100, 10, 20, 30, 40));
|
||||
store.storeFix(markDevice, createFix(2100, 10, 20, 30, 40));
|
||||
store.storeFix(markDevice, createFix(100, 10, 20));
|
||||
store.storeFix(markDevice, createFix(1100, 10, 20));
|
||||
store.storeFix(markDevice, createFix(2100, 10, 20));
|
||||
|
||||
final DynamicTrackedRaceImpl trackedRace = createDynamicTrackedRace(boatClass, raceDefinition);
|
||||
trackedRace.setStartOfTrackingReceived(new MillisecondsTimePoint(1000));
|
||||
@@ -100,8 +102,8 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
store.storeFix(device, createFix(100, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(200, 10, 20, 30, 40));
|
||||
store.storeFix(device2, createFix(100, 10, 20, 30, 40));
|
||||
store.storeFix(device3, createFix(100, 10, 20, 30, 40));
|
||||
store.storeFix(device3, createFix(100, 10, 20, 30, 40));
|
||||
store.storeFix(device3, createFix(100, 10, 20));
|
||||
store.storeFix(device3, createFix(100, 10, 20));
|
||||
|
||||
for (int i = 0; i < 10000; i++) {
|
||||
store.storeFix(device, createFix(i + 1000, 10, 20, 30, 40));
|
||||
@@ -155,6 +157,26 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
store.storeFix(device, createFix(250, 10, 20, 30, 40)); // outside both mappings
|
||||
store.storeFix(device, createFix(350, 10, 20, 30, 40)); // in second mapping
|
||||
}, /* tests and expectations */ trackedRace -> {
|
||||
final int[] fixInsertionCount = new int[1];
|
||||
trackedRace.getTrack(comp).addListener(new GPSTrackListener<Competitor, GPSFixMoving>() {
|
||||
private static final long serialVersionUID = 1933664210766279523L;
|
||||
|
||||
@Override
|
||||
public boolean isTransient() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gpsFixReceived(GPSFixMoving fix, Competitor item, boolean firstFixInTrack) {
|
||||
fixInsertionCount[0]++;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void speedAveragingChanged(long oldMillisecondsOverWhichToAverage,
|
||||
long newMillisecondsOverWhichToAverage) {
|
||||
// no-op
|
||||
}
|
||||
});
|
||||
testNumberOfRawFixes(trackedRace.getTrack(comp), 3);
|
||||
map(comp, device, 0, 500); // covers fix at 250ms; assert that only that fix is loaded
|
||||
try {
|
||||
@@ -163,6 +185,7 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
logger.log(Level.SEVERE, "Exception waiting for loading to finish", e);
|
||||
}
|
||||
testNumberOfRawFixes(trackedRace.getTrack(comp), 4);
|
||||
assertEquals(1, fixInsertionCount[0]);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -187,14 +210,23 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
});
|
||||
}
|
||||
|
||||
/** Regression test for bug 4008 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4008 */
|
||||
/**
|
||||
* Regression test for bug 4008 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4008 <br>
|
||||
* Updated for bug 4009 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4009
|
||||
*/
|
||||
@Test
|
||||
public void testFixesForMarkAreLoadedIfMappingDoesNotIntersectWithTrackingInterval() throws InterruptedException {
|
||||
testFixes(/* start of tracking */ 200, /* end of tracking */ 300, /* mappings and fixes */ () -> {
|
||||
final long startOfTracking = 200;
|
||||
final long bestFixOffset = 20;
|
||||
testFixes(/* start of tracking */ startOfTracking, /* end of tracking */ 300, /* mappings and fixes */ () -> {
|
||||
map(mark, device, 0, 100);
|
||||
store.storeFix(device, createFix(10, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(20, 10, 20, 30, 40));
|
||||
}, /* tests and expectations */ trackedRace -> testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 2));
|
||||
store.storeFix(device, createFix(10, 10, 20));
|
||||
store.storeFix(device, createFix(bestFixOffset, 10, 20));
|
||||
}, /* tests and expectations */ trackedRace -> {
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
|
||||
assertEquals(bestFixOffset, trackedRace.getOrCreateTrack(mark)
|
||||
.getLastFixBefore(new MillisecondsTimePoint(startOfTracking)).getTimePoint().asMillis());
|
||||
});
|
||||
}
|
||||
|
||||
/** Regression test for bug 4008 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4008 */
|
||||
@@ -203,9 +235,9 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
throws InterruptedException {
|
||||
testFixes(/* start of tracking */ 400, /* end of tracking */ 500, /* mappings and fixes */ () -> {
|
||||
map(mark, device, 0, 100);
|
||||
store.storeFix(device, createFix(50, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(50, 10, 20));
|
||||
map(mark2, device, 200, 300);
|
||||
store.storeFix(device, createFix(250, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(250, 10, 20));
|
||||
}, /* tests and expectations */ trackedRace -> {
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark2), 1);
|
||||
@@ -218,31 +250,131 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
throws InterruptedException {
|
||||
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings and fixes */ () -> {
|
||||
map(mark, device, 0, 200);
|
||||
store.storeFix(device, createFix(50, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(150, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(250, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(50, 10, 20));
|
||||
store.storeFix(device, createFix(150, 10, 20));
|
||||
store.storeFix(device, createFix(250, 10, 20));
|
||||
|
||||
map(mark, device, 350, 500);
|
||||
store.storeFix(device, createFix(400, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(450, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(400, 10, 20));
|
||||
store.storeFix(device, createFix(450, 10, 20));
|
||||
}, /* tests and expectations */ trackedRace -> {
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
|
||||
});
|
||||
}
|
||||
|
||||
/** Regression test for bug 4008 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4008 */
|
||||
/**
|
||||
* Regression test for bug 4008 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4008 <br>
|
||||
* Updated for bug 4009 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4009
|
||||
*/
|
||||
@Test
|
||||
public void testFixesForMarkAreLoadedIfMappingDoesIntersectWithTrackingIntervalAllFixesBecauseOfNoFixesWithIntersection()
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException, InterruptedException {
|
||||
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings and fixes */ () -> {
|
||||
final long bestFixBeforeStartOfTrackingOffset = 50;
|
||||
final long bestFixAfterEndOfTrackingOffset = 400;
|
||||
final long startOfTracking = 100;
|
||||
final long endOfTracking = 300;
|
||||
testFixes(/* start of tracking */ startOfTracking, /* end of tracking */ endOfTracking, /* mappings and fixes */ () -> {
|
||||
map(mark, device, 0, 200);
|
||||
store.storeFix(device, createFix(50, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(250, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(bestFixBeforeStartOfTrackingOffset, 10, 20));
|
||||
store.storeFix(device, createFix(250, 10, 20));
|
||||
map(mark, device, 350, 500);
|
||||
store.storeFix(device, createFix(400, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(450, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(bestFixAfterEndOfTrackingOffset, 10, 20));
|
||||
store.storeFix(device, createFix(450, 10, 20));
|
||||
}, /* tests and expectations */ trackedRace -> {
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 3);
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 2);
|
||||
assertEquals(bestFixBeforeStartOfTrackingOffset, trackedRace.getOrCreateTrack(mark)
|
||||
.getLastFixBefore(new MillisecondsTimePoint(startOfTracking)).getTimePoint().asMillis());
|
||||
assertEquals(bestFixAfterEndOfTrackingOffset, trackedRace.getOrCreateTrack(mark)
|
||||
.getFirstFixAfter(new MillisecondsTimePoint(endOfTracking)).getTimePoint().asMillis());
|
||||
});
|
||||
}
|
||||
|
||||
/** Test for changes of bug 4009 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4009 */
|
||||
@Test
|
||||
public void testThatOnlyTheBestFixesAreLoadedForMarksIfMultipleMappingsAreAvailable()
|
||||
throws InterruptedException {
|
||||
final long bestFixBeforeStartOfTrackingOffset = 140;
|
||||
final long bestFixAfterEndOfTrackingOffset = 360;
|
||||
final long startOfTracking = 200;
|
||||
final long endOfTracking = 300;
|
||||
testFixes(/* start of tracking */ startOfTracking, /* end of tracking */ endOfTracking, /* mappings and fixes */ () -> {
|
||||
map(mark, device, 0, 80);
|
||||
store.storeFix(device, createFix(50, 10, 20));
|
||||
store.storeFix(device, createFix(70, 10, 20));
|
||||
map(mark, device, 100, 150);
|
||||
store.storeFix(device, createFix(120, 10, 20));
|
||||
store.storeFix(device, createFix(bestFixBeforeStartOfTrackingOffset, 10, 20));
|
||||
|
||||
map(mark, device, 400, 500);
|
||||
store.storeFix(device, createFix(420, 10, 20));
|
||||
store.storeFix(device, createFix(450, 10, 20));
|
||||
store.storeFix(device, createFix(480, 10, 20));
|
||||
map(mark, device, 350, 390);
|
||||
store.storeFix(device, createFix(bestFixAfterEndOfTrackingOffset, 10, 20));
|
||||
store.storeFix(device, createFix(380, 10, 20));
|
||||
}, /* tests and expectations */ trackedRace -> {
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 2);
|
||||
assertEquals(bestFixBeforeStartOfTrackingOffset, trackedRace.getOrCreateTrack(mark)
|
||||
.getLastFixBefore(new MillisecondsTimePoint(startOfTracking)).getTimePoint().asMillis());
|
||||
assertEquals(bestFixAfterEndOfTrackingOffset, trackedRace.getOrCreateTrack(mark)
|
||||
.getFirstFixAfter(new MillisecondsTimePoint(endOfTracking)).getTimePoint().asMillis());
|
||||
});
|
||||
}
|
||||
|
||||
/** Test for changes of bug 4009 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4009 */
|
||||
@Test
|
||||
public void testYoungestFixIsAccurateOnSeveralAdditionsOfFixes()
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException, InterruptedException {
|
||||
final long bestFix1 = 50;
|
||||
final long bestFix2 = 100;
|
||||
final long startOfTracking = 200;
|
||||
final long endOfTracking = 300;
|
||||
testFixes(/* start of tracking */ startOfTracking, /* end of tracking */ endOfTracking, /* mappings and fixes */ () -> {
|
||||
map(mark, device, bestFix1 - 20, bestFix1 + 10);
|
||||
store.storeFix(device, createFix(bestFix1 - 10, 10, 20));
|
||||
store.storeFix(device, createFix(bestFix1, 10, 20));
|
||||
}, /* tests and expectations */ trackedRace -> {
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
|
||||
assertEquals(bestFix1, trackedRace.getOrCreateTrack(mark)
|
||||
.getLastFixBefore(new MillisecondsTimePoint(startOfTracking)).getTimePoint().asMillis());
|
||||
store.storeFix(device, createFix(bestFix2 - 10, 10, 20));
|
||||
store.storeFix(device, createFix(bestFix2, 10, 20));
|
||||
map(mark, device, bestFix2 - 20, bestFix2 + 10);
|
||||
}, /* tests and expectations */ trackedRace -> {
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 2);
|
||||
assertEquals(bestFix2, trackedRace.getOrCreateTrack(mark)
|
||||
.getLastFixBefore(new MillisecondsTimePoint(startOfTracking)).getTimePoint().asMillis());
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for changes of bug 4009 - https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=4009 <br>
|
||||
*/
|
||||
@Test
|
||||
public void testLoadingOfFixesOutsideOfTrackingIntervalsEndsIfFixInIntervalIsFound()
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException, InterruptedException {
|
||||
final long bestFix1 = 50;
|
||||
final long bestFix2 = 100;
|
||||
final long startOfTracking = 200;
|
||||
final long endOfTracking = 300;
|
||||
testFixes(/* start of tracking */ startOfTracking, /* end of tracking */ endOfTracking, /* mappings and fixes */ () -> {
|
||||
map(mark, device, bestFix1 - 10, bestFix1 + 10);
|
||||
store.storeFix(device, createFix(bestFix1, 10, 20));
|
||||
}, /* tests and expectations */ trackedRace -> {
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
|
||||
assertEquals(bestFix1, trackedRace.getOrCreateTrack(mark)
|
||||
.getLastFixBefore(new MillisecondsTimePoint(startOfTracking)).getTimePoint().asMillis());
|
||||
store.storeFix(device, createFix(startOfTracking + 10, 10, 20));
|
||||
map(mark, device, startOfTracking, startOfTracking + 20);
|
||||
}, /* tests and expectations */ trackedRace -> {
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 2);
|
||||
// would be the new best fix if there wasn't a fix in the tracking interval
|
||||
store.storeFix(device, createFix(bestFix2, 10, 20));
|
||||
map(mark, device, bestFix2 - 10, bestFix2 + 10);
|
||||
}, /* tests and expectations */ trackedRace -> {
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 2);
|
||||
assertEquals(bestFix1, trackedRace.getOrCreateTrack(mark)
|
||||
.getLastFixBefore(new MillisecondsTimePoint(startOfTracking)).getTimePoint().asMillis());
|
||||
});
|
||||
}
|
||||
|
||||
@@ -251,7 +383,7 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException, InterruptedException {
|
||||
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings */ () -> map(mark, device, 0, 200),
|
||||
/* fixes and tests/expectations */ trackedRace -> {
|
||||
store.storeFix(device, createFix(50, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(50, 10, 20));
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
|
||||
});
|
||||
}
|
||||
@@ -260,7 +392,7 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
public void testFixForMarkIsAddedIfAfterTrackingIntervalAndNoOtherFixExists()
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException, InterruptedException {
|
||||
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings */ () -> map(mark, device, 0, 400), /* fixes and tests/expectations */ trackedRace -> {
|
||||
store.storeFix(device, createFix(350, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(350, 10, 20));
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
|
||||
});
|
||||
}
|
||||
@@ -270,8 +402,8 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException, InterruptedException {
|
||||
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings */ () -> map(mark, device, 0, 400),
|
||||
/* fixes and tests/expectations */ trackedRace -> {
|
||||
store.storeFix(device, createFix(50, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(75, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(50, 10, 20));
|
||||
store.storeFix(device, createFix(75, 10, 20));
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 2);
|
||||
});
|
||||
}
|
||||
@@ -281,8 +413,8 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException, InterruptedException {
|
||||
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings */ () -> map(mark, device, 0, 400),
|
||||
/* fixes and tests/expectations */ trackedRace -> {
|
||||
store.storeFix(device, createFix(75, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(50, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(75, 10, 20));
|
||||
store.storeFix(device, createFix(50, 10, 20));
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
|
||||
});
|
||||
}
|
||||
@@ -292,8 +424,8 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException, InterruptedException {
|
||||
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings */ () -> map(mark, device, 0, 400),
|
||||
/* fixes and tests/expectations */ trackedRace -> {
|
||||
store.storeFix(device, createFix(375, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(350, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(375, 10, 20));
|
||||
store.storeFix(device, createFix(350, 10, 20));
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 2);
|
||||
});
|
||||
}
|
||||
@@ -303,8 +435,8 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException, InterruptedException {
|
||||
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings */ () -> map(mark, device, 0, 400),
|
||||
/* fixes and tests/expectations */ trackedRace -> {
|
||||
store.storeFix(device, createFix(350, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(375, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(350, 10, 20));
|
||||
store.storeFix(device, createFix(375, 10, 20));
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
|
||||
});
|
||||
}
|
||||
@@ -314,15 +446,16 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
throws TransformationException, NoCorrespondingServiceRegisteredException, InterruptedException {
|
||||
testFixes(/* start of tracking */ 100, /* end of tracking */ 300, /* mappings */ () -> map(mark, device, 0, 400),
|
||||
/* fixes and tests/expectations */ trackedRace -> {
|
||||
store.storeFix(device, createFix(200, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(50, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(350, 10, 20, 30, 40));
|
||||
store.storeFix(device, createFix(200, 10, 20));
|
||||
store.storeFix(device, createFix(50, 10, 20));
|
||||
store.storeFix(device, createFix(350, 10, 20));
|
||||
testNumberOfRawFixes(trackedRace.getOrCreateTrack(mark), 1);
|
||||
});
|
||||
}
|
||||
|
||||
private void testFixes(long startOfTracking, long endOfTracking, Runnable beforeTrackingStarted,
|
||||
Consumer<DynamicTrackedRace> afterTrackingStarted) throws InterruptedException {
|
||||
@SafeVarargs
|
||||
private final void testFixes(long startOfTracking, long endOfTracking, Runnable beforeTrackingStarted,
|
||||
Consumer<DynamicTrackedRace>... afterTrackingStartedConsumers) throws InterruptedException {
|
||||
defineMarksOnRegattaLog(mark, mark2);
|
||||
raceLog.add(new RaceLogStartOfTrackingEventImpl(new MillisecondsTimePoint(startOfTracking), author, 0));
|
||||
raceLog.add(new RaceLogEndOfTrackingEventImpl(new MillisecondsTimePoint(endOfTracking), author, 0));
|
||||
@@ -331,8 +464,10 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
trackedRace.attachRaceLog(raceLog);
|
||||
trackedRace.attachRegattaLog(regattaLog);
|
||||
new FixLoaderAndTracker(trackedRace, store, null);
|
||||
trackedRace.waitForLoadingToFinish();
|
||||
afterTrackingStarted.accept(trackedRace);
|
||||
for(Consumer<DynamicTrackedRace> afterTrackingStarted : afterTrackingStartedConsumers) {
|
||||
trackedRace.waitForLoadingToFinish();
|
||||
afterTrackingStarted.accept(trackedRace);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+3
-3
@@ -64,13 +64,13 @@ public class RaceLogConnectivityParams extends AbstractRaceTrackingConnectivityP
|
||||
|
||||
@Override
|
||||
public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore,
|
||||
RaceLogResolver raceLogResolver) {
|
||||
return createRaceTracker(regatta, trackedRegattaRegistry, windStore, raceLogResolver);
|
||||
RaceLogResolver raceLogResolver, long timeoutInMilliseconds) {
|
||||
return createRaceTracker(regatta, trackedRegattaRegistry, windStore, raceLogResolver, timeoutInMilliseconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry,
|
||||
WindStore windStore, RaceLogResolver raceLogResolver) {
|
||||
WindStore windStore, RaceLogResolver raceLogResolver, long timeoutInMilliseconds) {
|
||||
if (regatta == null) {
|
||||
BoatClass boatClass = new RaceInformationFinder(getRaceLog()).analyze().getBoatClass();
|
||||
regatta = service.getOrCreateDefaultRegatta(
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ public class TrackFileImportDeviceIdentifierJsonHandler implements DeviceIdentif
|
||||
String fileName = (String) json.get(Fields.FILE_NAME.name());
|
||||
String trackName = (String) json.get(Fields.TRACK_NAME.name());
|
||||
TimePoint uploaded = loadTimePoint(json.get(Fields.UPLOADED_MILLIS.name()));
|
||||
return new TrackFileImportDeviceIdentifierImpl(uuid, fileName, trackName, uploaded);
|
||||
return new TrackFileImportDeviceIdentifierImpl(uuid, stringRepresentation, fileName, trackName, uploaded);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -24,7 +24,7 @@ public class TrackFileImportDeviceIdentifierMongoHandler implements DeviceIdenti
|
||||
String trackName = (String) dbObject.get(Fields.TRACK_NAME.name());
|
||||
TimePoint uploaded = TrackFileImportDeviceIdentifierJsonHandler.
|
||||
loadTimePoint(dbObject.get(Fields.UPLOADED_MILLIS.name()));
|
||||
return new TrackFileImportDeviceIdentifierImpl(uuid, fileName, trackName, uploaded);
|
||||
return new TrackFileImportDeviceIdentifierImpl(uuid, stringRepresentation, fileName, trackName, uploaded);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Executable → Regular
+146
-112
@@ -1,5 +1,8 @@
|
||||
package com.sap.sailing.domain.racelogtracking.impl.fixtracker;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.TreeSet;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.logging.Level;
|
||||
@@ -7,9 +10,9 @@ import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.domain.abstractlog.regatta.MappingEventVisitor;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
@@ -19,23 +22,20 @@ import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.common.tracking.SensorFix;
|
||||
import com.sap.sailing.domain.racelog.impl.GPSFixStoreImpl;
|
||||
import com.sap.sailing.domain.racelog.tracking.FixReceivedListener;
|
||||
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
|
||||
import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
|
||||
import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
|
||||
import com.sap.sailing.domain.racelogsensortracking.SensorFixMapperFactory;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
|
||||
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrack;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
||||
import com.sap.sailing.domain.tracking.TrackingDataLoader;
|
||||
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
|
||||
import com.sap.sailing.domain.tracking.impl.TrackedRaceStatusImpl;
|
||||
import com.sap.sse.common.MultiTimeRange;
|
||||
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.TimeRange;
|
||||
@@ -56,11 +56,14 @@ import com.sap.sse.util.ThreadPoolUtil;
|
||||
* </ul>
|
||||
*
|
||||
*/
|
||||
/**
|
||||
* @author sschaefe
|
||||
*
|
||||
*/
|
||||
public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
private static final Logger logger = Logger.getLogger(FixLoaderAndTracker.class.getName());
|
||||
protected final DynamicTrackedRace trackedRace;
|
||||
private final SensorFixStore sensorFixStore;
|
||||
private final GPSFixStore gpsFixStore;
|
||||
private RegattaLogDeviceMappings<WithID> deviceMappings;
|
||||
private final AtomicInteger activeLoaders = new AtomicInteger();
|
||||
private final SensorFixMapperFactory sensorFixMapperFactory;
|
||||
@@ -196,7 +199,6 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
public FixLoaderAndTracker(DynamicTrackedRace trackedRace, SensorFixStore sensorFixStore,
|
||||
SensorFixMapperFactory sensorFixMapperFactory) {
|
||||
this.sensorFixStore = sensorFixStore;
|
||||
this.gpsFixStore = new GPSFixStoreImpl(sensorFixStore);
|
||||
this.sensorFixMapperFactory = sensorFixMapperFactory;
|
||||
this.trackedRace = trackedRace;
|
||||
|
||||
@@ -207,37 +209,50 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
* Loading fixes for {@link Mark}s needs special handling compared with {@link Competitor}s. If there are no fixes
|
||||
* available in the tracking interval, it is necessary to load other available fixes before/after the tracking
|
||||
* interval. this is due to the buoy pinger app can be used in the morning to ping some marks. The resulting fixes
|
||||
* need to also be available for races later on the day. This method ensures that if available, fixes are initially
|
||||
* loaded when starting tracking. In contrast to that, mappings for {@link Competitor}s have no special handling.
|
||||
*
|
||||
* @param mappings
|
||||
* the added mappings
|
||||
* @param item
|
||||
* the item the mappings belong to
|
||||
* need to also be available for races later on the day. This method ensures that if available, the best available
|
||||
* fixes are initially loaded when starting tracking. In contrast to that, mappings for {@link Competitor}s have no
|
||||
* special handling.
|
||||
*/
|
||||
private void loadFixesForNewMappings(Iterable<DeviceMappingWithRegattaLogEvent<WithID>> mappings, WithID item) {
|
||||
private void loadFixesForNewlyCoveredTimeRanges(WithID item,
|
||||
Map<RegattaLogDeviceMappingEvent<WithID>, MultiTimeRange> newlyCoveredTimeRanges) {
|
||||
TimeRange trackingTimeRange = getTrackingTimeRange();
|
||||
loadFixesInTrackingTimeRange(newlyCoveredTimeRanges, trackingTimeRange);
|
||||
if (item instanceof Mark) {
|
||||
Mark mark = (Mark) item;
|
||||
if (containsMappingThatIntersectsTimeRange(mappings, trackingTimeRange)) {
|
||||
loadFixesInTimeRange(mappings, trackingTimeRange);
|
||||
}
|
||||
DynamicGPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(mark);
|
||||
// load all mapped fixes if there was no fix in the tracking TimeRange
|
||||
final boolean loadAllMappedFixes = (track.getFirstRawFix() == null);
|
||||
if (loadAllMappedFixes) {
|
||||
// either got an empty track of there is no mapping for the TimeRange of the race at all.
|
||||
// try again without constraining the mapping interval by start/end of tracking to at
|
||||
// least attempt to get fixes at all in case there were any within the device mapping interval specified
|
||||
mappings.forEach(mapping -> loadFixes(mapping.getTimeRange(), mapping));
|
||||
GPSFix firstFixAfterStartOfTracking = track.getFirstFixAfter(trackingTimeRange.from());
|
||||
if (firstFixAfterStartOfTracking == null
|
||||
|| firstFixAfterStartOfTracking.getTimePoint().after(trackingTimeRange.to())) {
|
||||
// There is no fix in the tracking interval -> looking for better fixes before start of tracking and
|
||||
// after end of tracking
|
||||
newlyCoveredTimeRanges.forEach((event, timeRange) -> {
|
||||
loadBetterFixesIfAvailable(trackingTimeRange, timeRange, event);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
loadFixesInTimeRange(mappings, trackingTimeRange);
|
||||
}
|
||||
}
|
||||
|
||||
private void loadFixesInTimeRange(Iterable<DeviceMappingWithRegattaLogEvent<WithID>> mappings, TimeRange trackingTimeRange) {
|
||||
mappings.forEach(mapping -> loadFixes(trackingTimeRange.intersection(mapping.getTimeRange()), mapping));
|
||||
/**
|
||||
* Loads fixes defined by the given mapping and {@link MultiTimeRange}. Only those fixes that are in the mapping
|
||||
* time range are being loaded.
|
||||
*/
|
||||
private void loadFixesInTrackingTimeRange(
|
||||
Map<RegattaLogDeviceMappingEvent<WithID>, MultiTimeRange> newlyCoveredTimeRanges,
|
||||
TimeRange trackingTimeRange) {
|
||||
newlyCoveredTimeRanges.forEach((event, timeRange) -> {
|
||||
loadFixesForMultiTimeRange(timeRange.intersection(trackingTimeRange), event);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads fixes for the parts of the given {@link MultiTimeRange} using the given mapping event.
|
||||
*/
|
||||
private void loadFixesForMultiTimeRange(MultiTimeRange effectiveRangeToLoad,
|
||||
RegattaLogDeviceMappingEvent<WithID> event) {
|
||||
if (!effectiveRangeToLoad.isEmpty()) {
|
||||
effectiveRangeToLoad.forEach(timeRange -> loadFixes(timeRange, event));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -254,14 +269,17 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
return false;
|
||||
}
|
||||
|
||||
private void loadFixes(TimeRange timeRangeToLoad, DeviceMappingWithRegattaLogEvent<? extends WithID> mapping) {
|
||||
if(timeRangeToLoad == null) {
|
||||
/**
|
||||
* Loads the fixes in the specified {@link TimeRange} using a visitor of the given mapping event.
|
||||
*/
|
||||
private void loadFixes(TimeRange timeRangeToLoad, RegattaLogDeviceMappingEvent<? extends WithID> mappingEvent) {
|
||||
if (timeRangeToLoad == null) {
|
||||
return;
|
||||
}
|
||||
if (preemptiveStopRequested.get()) {
|
||||
return;
|
||||
}
|
||||
mapping.getRegattaLogEvent().accept(new MappingEventVisitor() {
|
||||
mappingEvent.accept(new MappingEventVisitor() {
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
|
||||
SensorFixMapper<Timed, DynamicTrack<Timed>, Competitor> mapper = sensorFixMapperFactory
|
||||
@@ -272,11 +290,11 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
// for the trackedRace because the competitors may not compete in it; in this case, the
|
||||
// competitor retrieved from the mapping event does not have a track in trackedRace
|
||||
try {
|
||||
sensorFixStore.loadFixes((DoubleVectorFix fix) -> mapper.addFix(track, fix), mapping.getDevice(),
|
||||
timeRangeToLoad.from(), timeRangeToLoad.to(), true);
|
||||
sensorFixStore.<DoubleVectorFix> loadFixes(fix -> mapper.addFix(track, fix), event.getDevice(),
|
||||
timeRangeToLoad.from(), timeRangeToLoad.to(), /* toIsInclusive */ false);
|
||||
} catch (NoCorrespondingServiceRegisteredException | TransformationException e) {
|
||||
logger.log(Level.WARNING, "Could not load track for competitor: " + mapping.getMappedTo()
|
||||
+ "; device: " + mapping.getDevice());
|
||||
logger.log(Level.WARNING, "Could not load track for competitor: " + event.getMappedTo()
|
||||
+ "; device: " + event.getDevice());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -289,12 +307,11 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
// for the trackedRace because the competitors may not compete in it; in this case, the
|
||||
// competitor retrieved from the mapping event does not have a track in trackedRace
|
||||
try {
|
||||
@SuppressWarnings({ "unchecked" })
|
||||
DeviceMapping<Competitor> competitorMapping = (DeviceMapping<Competitor>) mapping;
|
||||
gpsFixStore.loadCompetitorTrack(track, competitorMapping, timeRangeToLoad.from(),
|
||||
timeRangeToLoad.to());
|
||||
sensorFixStore.<GPSFixMoving> loadFixes(fix -> track.add(fix, true), event.getDevice(),
|
||||
timeRangeToLoad.from(), timeRangeToLoad.to(), /* toIsInclusive */ false);
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not load competitor track " + mapping.getMappedTo());
|
||||
logger.log(Level.WARNING, "Could not load competitor track " + event.getMappedTo() + "; device "
|
||||
+ event.getDevice());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -303,19 +320,87 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
public void visit(RegattaLogDeviceMarkMappingEvent event) {
|
||||
DynamicGPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(event.getMappedTo());
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
DeviceMapping<Mark> markMapping = (DeviceMapping<Mark>) mapping;
|
||||
gpsFixStore.loadMarkTrack(track, markMapping, timeRangeToLoad.from(), timeRangeToLoad.to());
|
||||
sensorFixStore.<GPSFix> loadFixes(fix -> track.add(fix, true), event.getDevice(),
|
||||
timeRangeToLoad.from(), timeRangeToLoad.to(), /* toIsInclusive */ false);
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not load mark track " + mapping.getMappedTo());
|
||||
logger.log(Level.WARNING, "Could not load mark track " + event.getMappedTo());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads better fallback fixes if there is no fix in the tracking interval found.
|
||||
*/
|
||||
private void loadBetterFixesIfAvailable(TimeRange trackingTimeRange, MultiTimeRange coveredTimeRanges,
|
||||
RegattaLogDeviceMappingEvent<? extends WithID> mappingEvent) {
|
||||
if (preemptiveStopRequested.get()) {
|
||||
return;
|
||||
}
|
||||
mappingEvent.accept(new MappingEventVisitor() {
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceCompetitorSensorDataMappingEvent event) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceCompetitorMappingEvent event) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visit(RegattaLogDeviceMarkMappingEvent event) {
|
||||
DynamicGPSFixTrack<Mark, GPSFix> track = trackedRace.getOrCreateTrack(event.getMappedTo());
|
||||
|
||||
final GPSFix lastFixAtOrBeforeStartOfTracking = track.getLastFixAtOrBefore(trackingTimeRange.from());
|
||||
// A better fix before start of tracking must be after the current best fix
|
||||
final MultiTimeRange beforeRange = coveredTimeRanges
|
||||
.intersection(new TimeRangeImpl(
|
||||
lastFixAtOrBeforeStartOfTracking != null
|
||||
? lastFixAtOrBeforeStartOfTracking.getTimePoint() : TimePoint.BeginningOfTime,
|
||||
trackingTimeRange.from()));
|
||||
// starting to load newer ranges to make the first found fix the best available fix
|
||||
Collection<TimeRange> inverseTimeRanges = Util.addAll(beforeRange,
|
||||
new TreeSet<>((timeRange1, timeRange2) -> -timeRange1.from().compareTo(timeRange2.from())));
|
||||
for (TimeRange timeRange : inverseTimeRanges) {
|
||||
try {
|
||||
if (sensorFixStore.<GPSFix> loadYoungestFix(fix -> track.add(fix, true),
|
||||
event.getDevice(), timeRange)) {
|
||||
// new best fix before start of tracking found
|
||||
break;
|
||||
}
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not load better fix for mark track " + event.getMappedTo());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
final GPSFix firstFixAtOrAfterEndOfTracking = track.getFirstFixAtOrAfter(trackingTimeRange.to());
|
||||
// A better fix after end of tracking must be before the current best fix
|
||||
MultiTimeRange afterRange = coveredTimeRanges.intersection(new TimeRangeImpl(trackingTimeRange.to(),
|
||||
firstFixAtOrAfterEndOfTracking != null ? firstFixAtOrAfterEndOfTracking.getTimePoint()
|
||||
: TimePoint.EndOfTime));
|
||||
for (TimeRange timeRange : afterRange) {
|
||||
try {
|
||||
if (sensorFixStore.<GPSFix> loadOldestFix(fix -> track.add(fix, true), event.getDevice(),
|
||||
timeRange)) {
|
||||
// new best fix after end of tracking found
|
||||
break;
|
||||
}
|
||||
} catch (TransformationException | NoCorrespondingServiceRegisteredException e) {
|
||||
logger.log(Level.WARNING, "Could not load better fix for mark track " + event.getMappedTo());
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private TimeRange getTrackingTimeRange() {
|
||||
return new TimeRangeImpl(getStartOfTracking() == null ? TimePoint.BeginningOfTime : getStartOfTracking(),
|
||||
getEndOfTracking() == null ? TimePoint.EndOfTime : getEndOfTracking());
|
||||
final TimePoint startOfTracking = trackedRace.getStartOfTracking();
|
||||
final TimePoint endOfTracking = trackedRace.getEndOfTracking();
|
||||
return new TimeRangeImpl(startOfTracking == null ? TimePoint.BeginningOfTime : startOfTracking,
|
||||
endOfTracking == null ? TimePoint.EndOfTime : endOfTracking);
|
||||
}
|
||||
|
||||
public void stop(boolean preemptive) {
|
||||
@@ -334,7 +419,7 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
}
|
||||
}
|
||||
|
||||
protected void startTracking() {
|
||||
private void startTracking() {
|
||||
trackedRace.addListener(raceChangeListener);
|
||||
this.deviceMappings = new FixLoaderDeviceMappings(trackedRace.getAttachedRegattaLogs(),
|
||||
trackedRace.getRace().getName());
|
||||
@@ -350,19 +435,9 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
}
|
||||
}
|
||||
|
||||
protected TimePoint getStartOfTracking() {
|
||||
return trackedRace.getStartOfTracking();
|
||||
}
|
||||
|
||||
protected TimePoint getEndOfTracking() {
|
||||
return trackedRace.getEndOfTracking();
|
||||
}
|
||||
|
||||
protected void loadFixesForExtendedTimeRange(TimePoint loadFixesFrom, TimePoint loadFixesTo) {
|
||||
private void loadFixesForExtendedTimeRange(TimePoint loadFixesFrom, TimePoint loadFixesTo) {
|
||||
final TimeRangeImpl extendedTimeRange = new TimeRangeImpl(loadFixesFrom, loadFixesTo);
|
||||
deviceMappings.forEachMapping((mapping) -> {
|
||||
loadFixes(extendedTimeRange.intersection(mapping.getTimeRange()), mapping);
|
||||
});
|
||||
deviceMappings.forEachItemAndCoveredTimeRanges((item, mappingsAndCoveredTimeRanges) -> loadFixesInTrackingTimeRange(mappingsAndCoveredTimeRanges, extendedTimeRange));
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -390,6 +465,8 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
}
|
||||
updateCallback.run();
|
||||
}
|
||||
} catch(Throwable t) {
|
||||
logger.log(Level.SEVERE, "Error while updating device mappings and loading fixes for race: " + trackedRace.getRaceIdentifier(), t);
|
||||
} finally {
|
||||
try {
|
||||
synchronized (FixLoaderAndTracker.this) {
|
||||
@@ -408,14 +485,6 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if currently the race is loading GPS fixes from the {@link GPSFixStore}. Clients may {@link Object#wait()}
|
||||
* on <code>this</code> object and will be notified whenever a change of this flag's value occurs.
|
||||
*/
|
||||
public boolean isLoadingFromGPSFixStore() {
|
||||
return activeLoaders.get() > 0;
|
||||
}
|
||||
|
||||
private void setStatusAndProgress(TrackedRaceStatusEnum status, double progress) {
|
||||
trackedRace.onStatusChanged(this, new TrackedRaceStatusImpl(status, progress));
|
||||
}
|
||||
@@ -429,56 +498,21 @@ public class FixLoaderAndTracker implements TrackingDataLoader {
|
||||
protected void updateMappings() {
|
||||
updateAsyncInternal(FixLoaderDeviceMappings.super::updateMappings);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void mappingRemoved(DeviceMappingWithRegattaLogEvent<WithID> mapping) {
|
||||
final DeviceIdentifier device = mapping.getDevice();
|
||||
if (!hasMappingForDevice(device)) {
|
||||
sensorFixStore.removeListener(listener, device);
|
||||
}
|
||||
// TODO if tracks are always associated to only one device mapping, we could remove tracks here
|
||||
protected void deviceIdAdded(DeviceIdentifier deviceIdentifier) {
|
||||
sensorFixStore.addListener(listener, deviceIdentifier);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
protected void mappingsAdded(Iterable<DeviceMappingWithRegattaLogEvent<WithID>> mappings, WithID item) {
|
||||
// The listener is first added. This causes that no fix is lost but potentially fixes are being processes twice.
|
||||
// There are the following cases:
|
||||
// 1. A fix is added before the mapping update started
|
||||
// -> the fix is dropped but during mapping update the whole mapping is being loaded including this fix.
|
||||
// 2. A fix is added while the write lock is being held for the mappings
|
||||
// -> The fix will be processed after the write lock is removed. In this state
|
||||
// the mappings are up-to-date and in a consequence the fix is loaded into the track.
|
||||
// In addition, the fix is loaded on mapping update, but both fix instances consistently have the same state.
|
||||
// 3. A fix is added after a successful mapping update
|
||||
// -> The fix is only added once, because the initial loading during mapping update is finished
|
||||
mappings.forEach(mapping -> sensorFixStore.addListener(listener, mapping.getDevice()));
|
||||
loadFixesForNewMappings(mappings, item);
|
||||
protected void deviceIdRemoved(DeviceIdentifier deviceIdentifier) {
|
||||
sensorFixStore.removeListener(listener, deviceIdentifier);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link #loadFixes loads fixes} that are captured by {@code newMapping} but not by {@code oldMapping}. This
|
||||
* may require up to two {@link #loadFixes} calls: one for the fixes after and one for the fixes before the old
|
||||
* mapping.
|
||||
*/
|
||||
|
||||
@Override
|
||||
protected void mappingChanged(DeviceMappingWithRegattaLogEvent<WithID> oldMapping,
|
||||
DeviceMappingWithRegattaLogEvent<WithID> newMapping) {
|
||||
final TimeRange newTimeRange = newMapping.getTimeRange();
|
||||
final TimeRange oldTimeRange = oldMapping.getTimeRange();
|
||||
if (newTimeRange.endsAfter(oldTimeRange)) {
|
||||
final TimePoint oldTo = oldTimeRange.to();
|
||||
final TimePoint newFrom = newTimeRange.from();
|
||||
final TimePoint from = oldTo.after(newFrom) ? oldTo : newFrom;
|
||||
final TimeRange rangeToLoad = new TimeRangeImpl(from, newTimeRange.to());
|
||||
loadFixes(rangeToLoad, newMapping);
|
||||
}
|
||||
if (newTimeRange.startsBefore(oldTimeRange)) {
|
||||
final TimePoint oldFrom = oldTimeRange.from();
|
||||
final TimePoint newTo = newTimeRange.to();
|
||||
final TimePoint to = oldFrom.after(newTo) ? oldFrom : newTo;
|
||||
final TimeRange rangeToLoad = new TimeRangeImpl(newTimeRange.from(), to);
|
||||
loadFixes(rangeToLoad, newMapping);
|
||||
}
|
||||
protected void newTimeRangesCovered(WithID item,
|
||||
Map<RegattaLogDeviceMappingEvent<WithID>, MultiTimeRange> newlyCoveredTimeRanges) {
|
||||
loadFixesForNewlyCoveredTimeRanges(item, newlyCoveredTimeRanges);
|
||||
}
|
||||
}
|
||||
}
|
||||
+153
-127
@@ -4,7 +4,6 @@ import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
@@ -18,6 +17,7 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorMappingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent;
|
||||
import com.sap.sailing.domain.abstractlog.regatta.impl.BaseRegattaLogEventVisitor;
|
||||
@@ -27,9 +27,14 @@ import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrack;
|
||||
import com.sap.sse.common.MultiTimeRange;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.TimeRange;
|
||||
import com.sap.sse.common.Timed;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.common.WithID;
|
||||
import com.sap.sse.common.impl.MultiTimeRangeImpl;
|
||||
import com.sap.sse.concurrent.LockUtil;
|
||||
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
|
||||
|
||||
@@ -125,9 +130,8 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
|
||||
public void stop() {
|
||||
LockUtil.executeWithWriteLock(knownRegattaLogsLock, () -> {
|
||||
knownRegattaLogs.forEach((log) -> log.removeListener(regattaLogEventVisitor));
|
||||
knownRegattaLogs.clear();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
protected void updateMappings() {
|
||||
try {
|
||||
@@ -137,40 +141,6 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the given callback for every known mapping that's currently known.
|
||||
*
|
||||
* @param callback the callback to call for every known mapping
|
||||
*/
|
||||
public void forEachMapping(Consumer<DeviceMappingWithRegattaLogEvent<ItemT>> callback) {
|
||||
forEachMapping((item, mapping) -> callback.accept(mapping));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the given callback for every device mapped by at least one of the known mappings.
|
||||
*
|
||||
* @param callback the callback to call for every mapped device
|
||||
*/
|
||||
public void forEachDevice(Consumer<DeviceIdentifier> callback) {
|
||||
LockUtil.executeWithReadLock(mappingsLock, () -> mappingsByDevice.keySet().forEach(callback::accept));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the given callback for every known mapping that's currently known.
|
||||
*
|
||||
* @param callback the callback to call for every known mapping
|
||||
*/
|
||||
public void forEachMapping(BiConsumer<ItemT, DeviceMappingWithRegattaLogEvent<ItemT>> callback) {
|
||||
LockUtil.executeWithReadLock(mappingsLock, () -> {
|
||||
for (Map.Entry<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> entry : mappings.entrySet()) {
|
||||
ItemT item = entry.getKey();
|
||||
for (DeviceMappingWithRegattaLogEvent<ItemT> mapping : entry.getValue()) {
|
||||
callback.accept(item, mapping);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Calls the given callback for every DeviceMapping that is known for the given {@link DeviceIdentifier} that
|
||||
* includes the given {@link TimePoint}.
|
||||
@@ -197,16 +167,31 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
|
||||
});
|
||||
}
|
||||
|
||||
public void forEachItemAndCoveredTimeRanges(final BiConsumer<ItemT, Map<RegattaLogDeviceMappingEvent<ItemT>, MultiTimeRange>> consumer) {
|
||||
HashMap<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> allMappings = LockUtil.executeWithReadLockAndResult(mappingsLock, () -> new HashMap<>(mappings));
|
||||
allMappings.forEach((item, mappings) -> {
|
||||
final Map<RegattaLogDeviceMappingEvent<ItemT>, MultiTimeRange> coveredTimeRanges = calculateCoveredTimeRanges(mappings);
|
||||
consumer.accept(item, coveredTimeRanges);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if there is at least one mapping for the given {@link DeviceIdentifier}, false otherwise
|
||||
* Calculates an association of mapping events to the covered {@link MultiTimeRange}.
|
||||
*/
|
||||
public boolean hasMappingForDevice(DeviceIdentifier device) {
|
||||
LockUtil.lockForRead(mappingsLock);
|
||||
try {
|
||||
return mappingsByDevice.containsKey(device);
|
||||
} finally {
|
||||
LockUtil.unlockAfterRead(mappingsLock);
|
||||
private Map<RegattaLogDeviceMappingEvent<ItemT>, MultiTimeRange> calculateCoveredTimeRanges(
|
||||
final List<DeviceMappingWithRegattaLogEvent<ItemT>> mappingsForItem) {
|
||||
final Map<RegattaLogDeviceMappingEvent<ItemT>, MultiTimeRange> coveredTimeRanges = new HashMap<>();
|
||||
if(mappingsForItem != null) {
|
||||
Map<Pair<DeviceIdentifier, Class<?>>, Iterable<DeviceMappingWithRegattaLogEvent<ItemT>>> groupedMappings = groupMappingsByDeviceIdAndMappingType(mappingsForItem);
|
||||
groupedMappings.entrySet().forEach(entry -> {
|
||||
Iterable<DeviceMappingWithRegattaLogEvent<ItemT>> mappingsForDeviceIdAndMappingType = entry.getValue();
|
||||
final MultiTimeRange coveredTimeRange = getCoveredTimeRange(mappingsForDeviceIdAndMappingType);
|
||||
if(!coveredTimeRange.isEmpty()) {
|
||||
coveredTimeRanges.put(Util.get(mappingsForDeviceIdAndMappingType, 0).getRegattaLogEvent(), coveredTimeRange);
|
||||
}
|
||||
});
|
||||
}
|
||||
return coveredTimeRanges;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -237,11 +222,15 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
|
||||
* @throws DoesNotHaveRegattaLogException
|
||||
*/
|
||||
private final <FixT extends Timed, TrackT extends DynamicTrack<FixT>> void updateMappingsInternal() {
|
||||
final Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> newMappings = calculateMappings();
|
||||
final Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> newMappings;
|
||||
final Map<ItemT, List<DeviceMappingWithRegattaLogEvent<ItemT>>> oldMappings = new HashMap<>();
|
||||
final Set<DeviceIdentifier> oldDeviceIds = new HashSet<>();
|
||||
final Set<DeviceIdentifier> newDeviceIds = new HashSet<>();
|
||||
LockUtil.lockForWrite(mappingsLock);
|
||||
try {
|
||||
newMappings = calculateMappings();
|
||||
oldMappings.putAll(mappings);
|
||||
oldDeviceIds.addAll(mappingsByDevice.keySet());
|
||||
mappings.clear();
|
||||
mappings.putAll(newMappings);
|
||||
mappingsByDevice.clear();
|
||||
@@ -255,10 +244,11 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
|
||||
list.add(mapping);
|
||||
}
|
||||
}
|
||||
newDeviceIds.addAll(mappingsByDevice.keySet());
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(mappingsLock);
|
||||
}
|
||||
calculateDiff(oldMappings, newMappings);
|
||||
calculateDiff(oldMappings, newMappings, oldDeviceIds, newDeviceIds);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -266,105 +256,141 @@ public abstract class RegattaLogDeviceMappings<ItemT extends WithID> {
|
||||
* and by loading and adding the fixes for extended or added mappings.
|
||||
*/
|
||||
private void calculateDiff(Map<ItemT, ? extends Iterable<DeviceMappingWithRegattaLogEvent<ItemT>>> previousMappings,
|
||||
Map<ItemT, ? extends Iterable<DeviceMappingWithRegattaLogEvent<ItemT>>> newMappings) {
|
||||
Set<ItemT> itemsToProcess = new HashSet<ItemT>(previousMappings.keySet());
|
||||
itemsToProcess.addAll(newMappings.keySet());
|
||||
for (ItemT item : itemsToProcess) {
|
||||
if (!newMappings.containsKey(item)) {
|
||||
previousMappings.get(item).forEach(this::mappingRemovedInternal);
|
||||
} else {
|
||||
final Iterable<DeviceMappingWithRegattaLogEvent<ItemT>> oldMappings = previousMappings.containsKey(item)
|
||||
? previousMappings.get(item) : Collections.emptyList();
|
||||
final List<DeviceMappingWithRegattaLogEvent<ItemT>> addedMappings = new ArrayList<>();
|
||||
for (DeviceMappingWithRegattaLogEvent<ItemT> newMapping : newMappings.get(item)) {
|
||||
DeviceMappingWithRegattaLogEvent<ItemT> oldMapping = findAndRemoveMapping(newMapping, oldMappings);
|
||||
if (oldMapping == null) {
|
||||
addedMappings.add(newMapping);
|
||||
} else if (!newMapping.getTimeRange().equals(oldMapping.getTimeRange())) {
|
||||
mappingChangedInternal(oldMapping, newMapping);
|
||||
}
|
||||
}
|
||||
mappingsAddedInternal(addedMappings, item);
|
||||
// oldMappings now still contains those mappings that were not entirely substituted by the new mappings
|
||||
oldMappings.forEach(this::mappingRemovedInternal);
|
||||
Map<ItemT, ? extends Iterable<DeviceMappingWithRegattaLogEvent<ItemT>>> newMappings,
|
||||
Set<DeviceIdentifier> oldDeviceIds, Set<DeviceIdentifier> newDeviceIds) {
|
||||
final Set<DeviceIdentifier> removedDeviceIds = new HashSet<>(oldDeviceIds);
|
||||
removedDeviceIds.removeAll(newDeviceIds);
|
||||
removedDeviceIds.forEach(this::deviceIdRemovedInternal);
|
||||
|
||||
final Set<DeviceIdentifier> addedDeviceIds = new HashSet<>(newDeviceIds);
|
||||
addedDeviceIds.removeAll(oldDeviceIds);
|
||||
addedDeviceIds.forEach(this::deviceIdAddedInternal);
|
||||
|
||||
// Only deviceIdentifier and mappingTypes are covered that are contained in newMappings.
|
||||
// Those that are only found in oldMappings won't lead to new covered TimeRanges.
|
||||
// DeviceIdentifiers, that aren't needed at all are already handled above.
|
||||
newMappings.forEach((item, mappingsForItem) -> {
|
||||
final Map<RegattaLogDeviceMappingEvent<ItemT>, MultiTimeRange> newlyCoveredTimeRanges = new HashMap<>();
|
||||
|
||||
// The mappings are processes grouped by DeviceIdentifier and mapping type
|
||||
// to build a consistent overall mapping update without the risk of fixes
|
||||
// to get loaded multiple times
|
||||
this.processNewAndChangedMappingsByDeviceIdAndEventType(previousMappings.get(item), mappingsForItem,
|
||||
(deviceIdentifier, mappingType, oldMappingsForDeviceIdAndMappingType,
|
||||
newMappingsForDeviceIdAndMappingType) -> {
|
||||
assert (newMappingsForDeviceIdAndMappingType != null);
|
||||
assert (!Util.isEmpty(newMappingsForDeviceIdAndMappingType));
|
||||
|
||||
final MultiTimeRange newCoveredTimeRanges = getCoveredTimeRange(
|
||||
newMappingsForDeviceIdAndMappingType)
|
||||
.subtract(getCoveredTimeRange(oldMappingsForDeviceIdAndMappingType));
|
||||
if (!newCoveredTimeRanges.isEmpty()) {
|
||||
newlyCoveredTimeRanges.put(
|
||||
Util.get(newMappingsForDeviceIdAndMappingType, 0).getRegattaLogEvent(),
|
||||
newCoveredTimeRanges);
|
||||
}
|
||||
});
|
||||
if (!newlyCoveredTimeRanges.isEmpty()) {
|
||||
// all updates for one item are handled at once
|
||||
newTimeRangesCoveredInternal(item, newlyCoveredTimeRanges);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Called when a {@link DeviceMapping} was removed.
|
||||
*
|
||||
* @param mapping the removed mapping
|
||||
* Processes the old and new mappings for mapping update by {@link DeviceIdentifier} and mapping type (class of the
|
||||
* mapping event). the old and new mappings are first grouped by the already mentioned criteria. For each group in
|
||||
* the new mappings, the associated group in the old mappings is identified. For these pairs, the given callback is
|
||||
* called. Groups only found in the old mappings are explicitly not handled because these can't lead to new covered
|
||||
* time ranges.
|
||||
*/
|
||||
protected abstract void mappingRemoved(DeviceMappingWithRegattaLogEvent<ItemT> mapping);
|
||||
private void processNewAndChangedMappingsByDeviceIdAndEventType(
|
||||
Iterable<DeviceMappingWithRegattaLogEvent<ItemT>> oldMappings,
|
||||
Iterable<DeviceMappingWithRegattaLogEvent<ItemT>> newMappings,
|
||||
GroupedOldAndNewMappingsCallback<ItemT> callback) {
|
||||
|
||||
final Map<Pair<DeviceIdentifier, Class<?>>, Iterable<DeviceMappingWithRegattaLogEvent<ItemT>>> groupedOldMappings = groupMappingsByDeviceIdAndMappingType(
|
||||
oldMappings != null ? oldMappings : Collections.emptySet());
|
||||
final Map<Pair<DeviceIdentifier, Class<?>>, Iterable<DeviceMappingWithRegattaLogEvent<ItemT>>> groupedNewMappings = groupMappingsByDeviceIdAndMappingType(
|
||||
newMappings);
|
||||
|
||||
groupedNewMappings.forEach((key, newMappingsForDeviceIdAndMappingType) -> {
|
||||
Iterable<DeviceMappingWithRegattaLogEvent<ItemT>> oldMappingsForDeviceIdAndMappingType = groupedOldMappings
|
||||
.get(key);
|
||||
callback.process(
|
||||
key.getA(), key.getB(), oldMappingsForDeviceIdAndMappingType != null
|
||||
? oldMappingsForDeviceIdAndMappingType : Collections.emptySet(),
|
||||
newMappingsForDeviceIdAndMappingType);
|
||||
});
|
||||
}
|
||||
|
||||
private void mappingRemovedInternal(DeviceMappingWithRegattaLogEvent<ItemT> mapping) {
|
||||
/**
|
||||
* Groups the given {@link DeviceMapping}s by pairs of {@link DeviceIdentifier} and mapping type (class of the
|
||||
* mapping event).
|
||||
*/
|
||||
private Map<Pair<DeviceIdentifier, Class<?>>, Iterable<DeviceMappingWithRegattaLogEvent<ItemT>>> groupMappingsByDeviceIdAndMappingType(
|
||||
Iterable<DeviceMappingWithRegattaLogEvent<ItemT>> mappings) {
|
||||
return Util.group(mappings, value -> new Pair<>(value.getDevice(), value.getEventType()), HashSet::new);
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the {@link MultiTimeRange} as union of the {@link TimeRange}s of the given mappings.
|
||||
*/
|
||||
private MultiTimeRange getCoveredTimeRange(Iterable<DeviceMappingWithRegattaLogEvent<ItemT>> mappings) {
|
||||
MultiTimeRange result = new MultiTimeRangeImpl();
|
||||
for (DeviceMappingWithRegattaLogEvent<ItemT> mapping : mappings) {
|
||||
result = result.union(mapping.getTimeRange());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Internal callback interface for {@link #processNewAndChangedMappingsByDeviceIdAndEventType}.
|
||||
*/
|
||||
private interface GroupedOldAndNewMappingsCallback<ItemT extends WithID> {
|
||||
void process(DeviceIdentifier deviceIdentifier, Class<?> mappingType,
|
||||
Iterable<DeviceMappingWithRegattaLogEvent<ItemT>> oldMappings,
|
||||
Iterable<DeviceMappingWithRegattaLogEvent<ItemT>> newMappings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when at least one mapping for a previously not available {@link DeviceIdentifier} was added.
|
||||
*/
|
||||
protected abstract void deviceIdAdded(DeviceIdentifier deviceIdentifier);
|
||||
|
||||
private void deviceIdAddedInternal(DeviceIdentifier deviceIdentifier) {
|
||||
try {
|
||||
mappingRemoved(mapping);
|
||||
} catch(Exception e) {
|
||||
logger.log(Level.SEVERE, "error while removing mapping " + mapping, e);
|
||||
deviceIdAdded(deviceIdentifier);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "error while adding deviceIdentifier " + deviceIdentifier, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a {@link DeviceMapping} was added.
|
||||
*
|
||||
* @param mappings the new mapping
|
||||
* Called when the last available mapping for a {@link DeviceIdentifier} was removed.
|
||||
*/
|
||||
protected abstract void mappingsAdded(Iterable<DeviceMappingWithRegattaLogEvent<ItemT>> mappings, ItemT item);
|
||||
|
||||
private void mappingsAddedInternal(Iterable<DeviceMappingWithRegattaLogEvent<ItemT>> mappings, ItemT item) {
|
||||
protected abstract void deviceIdRemoved(DeviceIdentifier deviceIdentifier);
|
||||
|
||||
private void deviceIdRemovedInternal(DeviceIdentifier deviceIdentifier) {
|
||||
try {
|
||||
mappingsAdded(mappings, item);
|
||||
} catch(Exception e) {
|
||||
logger.log(Level.SEVERE, "error while adding mapping " + mappings, e);
|
||||
deviceIdRemoved(deviceIdentifier);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "error while removing deviceIdentifier " + deviceIdentifier, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when a {@link DeviceMapping} was changed regarding its mapped time range.
|
||||
* This can occur if an open ended mapping is being closed or a close event gets revoked
|
||||
* or a new mapping is added that may partly overlap with an old mapping such that only
|
||||
* parts of the fixes covered by the new mapping need to be loaded (the ones not already
|
||||
* covered by the old mapping).
|
||||
*
|
||||
* @param oldMapping the old mapping
|
||||
* @param newMapping the new mapping
|
||||
* Called when a new MultiTimeRange could be identified that is now covered by the mappings for a item,
|
||||
* DeviceIdentifier and mappingType.
|
||||
*/
|
||||
protected abstract void mappingChanged(DeviceMappingWithRegattaLogEvent<ItemT> oldMapping,
|
||||
DeviceMappingWithRegattaLogEvent<ItemT> newMapping);
|
||||
|
||||
private void mappingChangedInternal(DeviceMappingWithRegattaLogEvent<ItemT> oldMapping,
|
||||
DeviceMappingWithRegattaLogEvent<ItemT> newMapping) {
|
||||
protected abstract void newTimeRangesCovered(ItemT item, Map<RegattaLogDeviceMappingEvent<ItemT>, MultiTimeRange> newlyCoveredTimeRanges);
|
||||
|
||||
private void newTimeRangesCoveredInternal(ItemT item, Map<RegattaLogDeviceMappingEvent<ItemT>, MultiTimeRange> newlyCoveredTimeRanges) {
|
||||
try {
|
||||
mappingChanged(oldMapping, newMapping);
|
||||
} catch(Exception e) {
|
||||
logger.log(Level.SEVERE, "error while changing mapping old: " + oldMapping + "; new: " + newMapping, e);
|
||||
newTimeRangesCovered(item, newlyCoveredTimeRanges);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE,
|
||||
"error while calling newTimeRangeCovered for item " + item, e);
|
||||
}
|
||||
}
|
||||
|
||||
private DeviceMappingWithRegattaLogEvent<ItemT> findAndRemoveMapping(
|
||||
DeviceMappingWithRegattaLogEvent<ItemT> mappingToFind,
|
||||
Iterable<DeviceMappingWithRegattaLogEvent<ItemT>> newItemsToProcess) {
|
||||
for (Iterator<DeviceMappingWithRegattaLogEvent<ItemT>> iterator = newItemsToProcess.iterator(); iterator.hasNext();) {
|
||||
DeviceMappingWithRegattaLogEvent<ItemT> deviceMapping = iterator.next();
|
||||
if (isSame(mappingToFind, deviceMapping)) {
|
||||
iterator.remove();
|
||||
return deviceMapping;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares two device mappings based on their device, the item mapped to and the race/regatta log event type
|
||||
* that usually corresponds with the type of item to which the device is mapped. Note that in particular the
|
||||
* mappings' time ranges are ignored for this comparison.
|
||||
*/
|
||||
private boolean isSame(DeviceMappingWithRegattaLogEvent<ItemT> mapping1,
|
||||
DeviceMappingWithRegattaLogEvent<ItemT> mapping2) {
|
||||
return mapping1.getDevice().equals(mapping2.getDevice()) && mapping1.getMappedTo().equals(mapping2.getMappedTo())
|
||||
&& mapping1.getEventType().equals(mapping2.getEventType())
|
||||
&& mapping1.getRegattaLogEvent().getId().equals(mapping2.getRegattaLogEvent().getId());
|
||||
}
|
||||
}
|
||||
+20
-20
@@ -52,9 +52,9 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
|
||||
return mapping.getId();
|
||||
}
|
||||
|
||||
private void closeMapping(AbstractLogEventAuthor author, DeviceIdentifier device, Serializable mappingId, long millis) {
|
||||
private void closeMapping(AbstractLogEventAuthor author, DeviceIdentifier device, Serializable mappingId, long closingTimePointInclusiveAsMillis) {
|
||||
RegattaLogEvent mapping = new RegattaLogCloseOpenEndedDeviceMappingEventImpl(t(), author, t(), UUID.randomUUID(), mappingId,
|
||||
new MillisecondsTimePoint(millis));
|
||||
new MillisecondsTimePoint(closingTimePointInclusiveAsMillis));
|
||||
log.add(mapping);
|
||||
}
|
||||
|
||||
@@ -73,7 +73,7 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
|
||||
assertEquals(1, deviceMappings.size());
|
||||
final DeviceMappingWithRegattaLogEvent<Competitor> mapping = deviceMappings.iterator().next();
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
|
||||
assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to());
|
||||
assertEquals(new MillisecondsTimePoint(201) /* exclusive vs. inclusive */, mapping.getTimeRange().to());
|
||||
}
|
||||
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(150));
|
||||
{
|
||||
@@ -86,10 +86,10 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
|
||||
for (final DeviceMappingWithRegattaLogEvent<Competitor> mapping : deviceMappings) {
|
||||
if (mapping.getTimeRange().from().asMillis() == 100) {
|
||||
found100 = true;
|
||||
assertEquals(149, mapping.getTimeRange().to().asMillis());
|
||||
assertEquals(150 /* exclusive */, mapping.getTimeRange().to().asMillis());
|
||||
} else {
|
||||
assertEquals(151, mapping.getTimeRange().from().asMillis());
|
||||
assertEquals(200, mapping.getTimeRange().to().asMillis());
|
||||
assertEquals(201 /* exclusive vs. inclusive */, mapping.getTimeRange().to().asMillis());
|
||||
}
|
||||
}
|
||||
assertTrue(found100);
|
||||
@@ -109,7 +109,7 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
|
||||
assertEquals(1, deviceMappings.size());
|
||||
final DeviceMappingWithRegattaLogEvent<Competitor> mapping = deviceMappings.iterator().next();
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
|
||||
assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to());
|
||||
assertEquals(new MillisecondsTimePoint(201 /* exclusive vs. inclusive */), mapping.getTimeRange().to());
|
||||
}
|
||||
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(150));
|
||||
{
|
||||
@@ -120,10 +120,10 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
|
||||
assertEquals(2, deviceMappings.size());
|
||||
for (final DeviceMappingWithRegattaLogEvent<Competitor> mapping : deviceMappings) {
|
||||
if (mapping.getTimeRange().from().asMillis() == 100) {
|
||||
assertEquals(149, mapping.getTimeRange().to().asMillis());
|
||||
assertEquals(150 /* exclusive */, mapping.getTimeRange().to().asMillis());
|
||||
} else {
|
||||
assertEquals(151, mapping.getTimeRange().from().asMillis());
|
||||
assertEquals(200, mapping.getTimeRange().to().asMillis());
|
||||
assertEquals(201 /* exclusive vs. inclusive */, mapping.getTimeRange().to().asMillis());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -141,7 +141,7 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
|
||||
assertEquals(1, deviceMappings.size());
|
||||
final DeviceMappingWithRegattaLogEvent<Competitor> mapping = deviceMappings.iterator().next();
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
|
||||
assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to());
|
||||
assertEquals(new MillisecondsTimePoint(201) /* exclusive vs. inclusive */, mapping.getTimeRange().to());
|
||||
}
|
||||
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(100));
|
||||
{
|
||||
@@ -151,7 +151,7 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
|
||||
final List<DeviceMappingWithRegattaLogEvent<Competitor>> deviceMappings = mappings.get(competitor);
|
||||
assertEquals(1, deviceMappings.size());
|
||||
assertEquals(101, deviceMappings.get(0).getTimeRange().from().asMillis());
|
||||
assertEquals(200, deviceMappings.get(0).getTimeRange().to().asMillis());
|
||||
assertEquals(201 /* exclusive vs. inclusive */, deviceMappings.get(0).getTimeRange().to().asMillis());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -168,7 +168,7 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
|
||||
assertEquals(1, deviceMappings.size());
|
||||
final DeviceMappingWithRegattaLogEvent<Competitor> mapping = deviceMappings.iterator().next();
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
|
||||
assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to());
|
||||
assertEquals(new MillisecondsTimePoint(201) /* exclusive vs. inclusive */, mapping.getTimeRange().to());
|
||||
}
|
||||
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(100));
|
||||
{
|
||||
@@ -178,7 +178,7 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
|
||||
final List<DeviceMappingWithRegattaLogEvent<Competitor>> deviceMappings = mappings.get(competitor);
|
||||
assertEquals(1, deviceMappings.size());
|
||||
assertEquals(101, deviceMappings.get(0).getTimeRange().from().asMillis());
|
||||
assertEquals(200, deviceMappings.get(0).getTimeRange().to().asMillis());
|
||||
assertEquals(201 /* exclusive */, deviceMappings.get(0).getTimeRange().to().asMillis());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -194,7 +194,7 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
|
||||
assertEquals(1, deviceMappings.size());
|
||||
final DeviceMappingWithRegattaLogEvent<Competitor> mapping = deviceMappings.iterator().next();
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
|
||||
assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to());
|
||||
assertEquals(new MillisecondsTimePoint(201) /* exclusive vs. inclusive */, mapping.getTimeRange().to());
|
||||
}
|
||||
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(200));
|
||||
{
|
||||
@@ -204,7 +204,7 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
|
||||
final List<DeviceMappingWithRegattaLogEvent<Competitor>> deviceMappings = mappings.get(competitor);
|
||||
assertEquals(1, deviceMappings.size());
|
||||
assertEquals(100, deviceMappings.get(0).getTimeRange().from().asMillis());
|
||||
assertEquals(199, deviceMappings.get(0).getTimeRange().to().asMillis());
|
||||
assertEquals(200 /* exclusive */, deviceMappings.get(0).getTimeRange().to().asMillis());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,7 +221,7 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
|
||||
assertEquals(1, deviceMappings.size());
|
||||
final DeviceMappingWithRegattaLogEvent<Competitor> mapping = deviceMappings.iterator().next();
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
|
||||
assertEquals(new MillisecondsTimePoint(200), mapping.getTimeRange().to());
|
||||
assertEquals(new MillisecondsTimePoint(201) /* exclusive vs. inclusive */, mapping.getTimeRange().to());
|
||||
}
|
||||
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(200));
|
||||
{
|
||||
@@ -231,7 +231,7 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
|
||||
final List<DeviceMappingWithRegattaLogEvent<Competitor>> deviceMappings = mappings.get(competitor);
|
||||
assertEquals(1, deviceMappings.size());
|
||||
assertEquals(100, deviceMappings.get(0).getTimeRange().from().asMillis());
|
||||
assertEquals(199, deviceMappings.get(0).getTimeRange().to().asMillis());
|
||||
assertEquals(200 /* exclusive */, deviceMappings.get(0).getTimeRange().to().asMillis());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -247,7 +247,7 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
|
||||
assertEquals(1, deviceMappings.size());
|
||||
final DeviceMappingWithRegattaLogEvent<Competitor> mapping = deviceMappings.iterator().next();
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().to());
|
||||
assertEquals(new MillisecondsTimePoint(101) /* exclusive vs. inclusive */, mapping.getTimeRange().to());
|
||||
}
|
||||
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(100));
|
||||
{
|
||||
@@ -269,7 +269,7 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
|
||||
assertEquals(1, deviceMappings.size());
|
||||
final DeviceMappingWithRegattaLogEvent<Competitor> mapping = deviceMappings.iterator().next();
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().from());
|
||||
assertEquals(new MillisecondsTimePoint(100), mapping.getTimeRange().to());
|
||||
assertEquals(new MillisecondsTimePoint(101) /* exclusive vs. inclusive */, mapping.getTimeRange().to());
|
||||
}
|
||||
finder.removeTimePointFromMapping(competitor, new MillisecondsTimePoint(100));
|
||||
{
|
||||
@@ -301,7 +301,7 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
|
||||
assertEquals(1, mappings.size());
|
||||
DeviceMappingWithRegattaLogEvent<Competitor> mapping = mappings.get(0);
|
||||
assertEquals(10, mapping.getTimeRange().from().asMillis());
|
||||
assertEquals(20, mapping.getTimeRange().to().asMillis());
|
||||
assertEquals(21 /* exclusive vs. inclusive */, mapping.getTimeRange().to().asMillis());
|
||||
|
||||
//another independent range
|
||||
id = addMapping(author, device, 0L, null, competitor);
|
||||
@@ -312,7 +312,7 @@ public class DeviceMappingFinderTest extends AbstractRegattaLogTrackingTest {
|
||||
//another closing mapping, that should take precedence (because added later)
|
||||
closeMapping(author, device, id, 3);
|
||||
assertEquals(2, getMappings().size());
|
||||
assertEquals(3, getMappings().get(1).getTimeRange().to().asMillis());
|
||||
assertEquals(4 /* exclusive vs. inclusive */, getMappings().get(1).getTimeRange().to().asMillis());
|
||||
|
||||
//open-ended at end, not closed
|
||||
addMapping(author, device, 30L, null, competitor);
|
||||
|
||||
+4
-2
@@ -7,7 +7,7 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
|
||||
/**
|
||||
* Closes the time range of a {@link RegattaLogDeviceMappingEvent}. This means that the {@link #getClosingTimePoint() timepoint} provided
|
||||
* Closes the time range of a {@link RegattaLogDeviceMappingEvent}. This means that the {@link #getClosingTimePointInclusive() timepoint} provided
|
||||
* by this event is substituted for the missing timepoint in the {@code DeviceMappingEvent} it refers to.
|
||||
* @author Fredrik Teschke
|
||||
*
|
||||
@@ -15,8 +15,10 @@ import com.sap.sse.common.TimePoint;
|
||||
public interface RegattaLogCloseOpenEndedDeviceMappingEvent extends RegattaLogEvent, Revokable {
|
||||
/**
|
||||
* Returns the timepoint that shall substitute the missing one in the corresponding {@link RegattaLogDeviceMappingEvent}.
|
||||
* As with {@link RegattaLogDeviceMappingEvent#getToInclusive()}, this time point marks the <em>inclusive</em> end
|
||||
* of the interval.
|
||||
*/
|
||||
TimePoint getClosingTimePoint();
|
||||
TimePoint getClosingTimePointInclusive();
|
||||
|
||||
/**
|
||||
* Returns the {@code id} of the event for which this event provides the closing timepoint.
|
||||
|
||||
+9
-4
@@ -9,15 +9,16 @@ import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
|
||||
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.TimeRange;
|
||||
import com.sap.sse.common.WithID;
|
||||
|
||||
/**
|
||||
* Event type for mapping {@link DeviceIdentifier devices} to {@code items} ({@link Competitor competitors} or {@link Mark marks}).
|
||||
* <p>
|
||||
* Not both {@link #getFrom()} and {@link #getTo()} may be {@code null} at the same time (which would be a mapping for "all times",
|
||||
* Not both {@link #getFrom()} and {@link #getToInclusive()} may be {@code null} at the same time (which would be a mapping for "all times",
|
||||
* as the semantics of somehow later limiting this range would become unclear
|
||||
* <p>
|
||||
* If one end of the time range enclosed by {@link #getFrom()} and {@link #getTo()} is open, this can be closed
|
||||
* If one end of the time range enclosed by {@link #getFrom()} and {@link #getToInclusive()} is open, this can be closed
|
||||
* by a {@link RegattaLogCloseOpenEndedDeviceMappingEvent}.
|
||||
*
|
||||
* @see RegattaLogDefinedMarkAnalyzer for rules by which {@link DeviceMapping}s are derived from these events.
|
||||
@@ -29,15 +30,19 @@ public interface RegattaLogDeviceMappingEvent<ItemType extends WithID> extends R
|
||||
|
||||
/**
|
||||
* The {@code return} value may be {@code null}, symbolizing an open range.
|
||||
* In that case the device should be mapped to the item for any timepoint before that specified by {@link #getTo()}.
|
||||
* In that case the device should be mapped to the item for any timepoint before that specified by {@link #getToInclusive()}.
|
||||
*/
|
||||
TimePoint getFrom();
|
||||
|
||||
/**
|
||||
* The {@code return} value may be {@code null}, symbolizing an open range.
|
||||
* In that case the device should be mapped to the item for any timepoint after that specified by {@link #getFrom()}.
|
||||
* Otherwise, this marks the <em>inclusive</em> end of the interval, other than the {@link TimeRange#to()} semantics which
|
||||
* uses an <em>exclusive</em> interval end. To bridge this gap, whenever mapping this event's from/to interval to a
|
||||
* {@link TimeRange}, at least the resolution of the {@link TimePoint} representation must be added to the result of
|
||||
* this method to make it into a valid end of a {@link TimeRange} that includes the time interval covered by this mapping.
|
||||
*/
|
||||
TimePoint getTo();
|
||||
TimePoint getToInclusive();
|
||||
|
||||
/**
|
||||
* Visitor patter implementation for event mapping handling differentiation.
|
||||
|
||||
+20
-8
@@ -15,23 +15,35 @@ RegattaLogDeviceMappingEvent<ItemType> {
|
||||
|
||||
private final ItemType mappedTo;
|
||||
private final DeviceIdentifier device;
|
||||
|
||||
/**
|
||||
* inclusive start time point
|
||||
*/
|
||||
private final TimePoint from;
|
||||
private final TimePoint to;
|
||||
|
||||
/**
|
||||
* inclusive end time point
|
||||
*/
|
||||
private final TimePoint toInclusive;
|
||||
|
||||
/**
|
||||
* @param from inclusive
|
||||
* @param toInclusive inclusive
|
||||
*/
|
||||
public RegattaLogDeviceMappingEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
|
||||
AbstractLogEventAuthor author, Serializable id, ItemType mappedTo,
|
||||
DeviceIdentifier device, TimePoint from, TimePoint to) {
|
||||
DeviceIdentifier device, TimePoint from, TimePoint toInclusive) {
|
||||
super(createdAt, logicalTimePoint, author, id);
|
||||
this.mappedTo = mappedTo;
|
||||
this.device = device;
|
||||
this.from = from;
|
||||
this.to = to;
|
||||
this.toInclusive = toInclusive;
|
||||
}
|
||||
|
||||
public RegattaLogDeviceMappingEventImpl(TimePoint logicalTimePoint,
|
||||
AbstractLogEventAuthor author, ItemType mappedTo,
|
||||
DeviceIdentifier device, TimePoint from, TimePoint to) {
|
||||
this(now(), logicalTimePoint, author, randId(), mappedTo, device, from, to);
|
||||
DeviceIdentifier device, TimePoint from, TimePoint toInclusive) {
|
||||
this(now(), logicalTimePoint, author, randId(), mappedTo, device, from, toInclusive);
|
||||
}
|
||||
|
||||
public ItemType getMappedTo() {
|
||||
@@ -43,12 +55,12 @@ RegattaLogDeviceMappingEvent<ItemType> {
|
||||
public TimePoint getFrom() {
|
||||
return from;
|
||||
}
|
||||
public TimePoint getTo() {
|
||||
return to;
|
||||
public TimePoint getToInclusive() {
|
||||
return toInclusive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortInfo() {
|
||||
return "device: " + device + ", mapped to: " + mappedTo + ", from: " + from + ", to: " + to;
|
||||
return "device: " + device + ", mapped to: " + mappedTo + ", from: " + from + ", to: " + toInclusive;
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -13,18 +13,18 @@ public class RegattaLogCloseOpenEndedDeviceMappingEventImpl extends AbstractLogE
|
||||
private static final long serialVersionUID = -5114645637316367845L;
|
||||
|
||||
private final Serializable deviceMappingEventId;
|
||||
private final TimePoint closingTimePoint;
|
||||
private final TimePoint closingTimePointInclusive;
|
||||
|
||||
public RegattaLogCloseOpenEndedDeviceMappingEventImpl(TimePoint createdAt, AbstractLogEventAuthor author, TimePoint logicalTimePoint,
|
||||
Serializable pId, Serializable deviceMappingEventId, TimePoint closingTimePoint) {
|
||||
Serializable pId, Serializable deviceMappingEventId, TimePoint closingTimePointInclusive) {
|
||||
super(createdAt, logicalTimePoint, author, pId);
|
||||
this.deviceMappingEventId = deviceMappingEventId;
|
||||
this.closingTimePoint = closingTimePoint;
|
||||
this.closingTimePointInclusive = closingTimePointInclusive;
|
||||
}
|
||||
|
||||
public RegattaLogCloseOpenEndedDeviceMappingEventImpl(TimePoint logicalTimePoint, AbstractLogEventAuthor author,
|
||||
Serializable deviceMappingEventId, TimePoint closingTimePoint) {
|
||||
this( now(), author, logicalTimePoint, randId(), deviceMappingEventId, closingTimePoint);
|
||||
Serializable deviceMappingEventId, TimePoint closingTimePointInclusive) {
|
||||
this(now(), author, logicalTimePoint, randId(), deviceMappingEventId, closingTimePointInclusive);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -33,13 +33,13 @@ public class RegattaLogCloseOpenEndedDeviceMappingEventImpl extends AbstractLogE
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getClosingTimePoint() {
|
||||
return closingTimePoint;
|
||||
public TimePoint getClosingTimePointInclusive() {
|
||||
return closingTimePointInclusive;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortInfo() {
|
||||
return "closing mapping (id: " + deviceMappingEventId + ") at " + closingTimePoint;
|
||||
return "closing mapping (id: " + deviceMappingEventId + ") at " + closingTimePointInclusive;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+4
-4
@@ -15,13 +15,13 @@ public class RegattaLogDeviceCompetitorMappingEventImpl extends RegattaLogDevice
|
||||
RegattaLogDeviceCompetitorMappingEvent {
|
||||
public RegattaLogDeviceCompetitorMappingEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
|
||||
AbstractLogEventAuthor author, Serializable pId, Competitor mappedTo, DeviceIdentifier device,
|
||||
TimePoint from, TimePoint to) {
|
||||
super(createdAt, logicalTimePoint, author, pId, mappedTo, device, from, to);
|
||||
TimePoint from, TimePoint toInclusive) {
|
||||
super(createdAt, logicalTimePoint, author, pId, mappedTo, device, from, toInclusive);
|
||||
}
|
||||
|
||||
public RegattaLogDeviceCompetitorMappingEventImpl(TimePoint logicalTimePoint, AbstractLogEventAuthor author,
|
||||
Competitor mappedTo, DeviceIdentifier device, TimePoint from, TimePoint to) {
|
||||
super(logicalTimePoint, author, mappedTo, device, from, to);
|
||||
Competitor mappedTo, DeviceIdentifier device, TimePoint from, TimePoint toInclusive) {
|
||||
super(logicalTimePoint, author, mappedTo, device, from, toInclusive);
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = -1494030544804758753L;
|
||||
|
||||
+4
-4
@@ -17,13 +17,13 @@ public class RegattaLogDeviceMarkMappingEventImpl extends RegattaLogDeviceMappin
|
||||
|
||||
public RegattaLogDeviceMarkMappingEventImpl(TimePoint createdAt, TimePoint logicalTimePoint,
|
||||
AbstractLogEventAuthor author, Serializable pId, Mark mappedTo, DeviceIdentifier device, TimePoint from,
|
||||
TimePoint to) {
|
||||
super(createdAt, logicalTimePoint, author, pId, mappedTo, device, from, to);
|
||||
TimePoint toInclusive) {
|
||||
super(createdAt, logicalTimePoint, author, pId, mappedTo, device, from, toInclusive);
|
||||
}
|
||||
|
||||
public RegattaLogDeviceMarkMappingEventImpl(TimePoint logicalTimePoint, AbstractLogEventAuthor author,
|
||||
Mark mappedTo, DeviceIdentifier device, TimePoint from, TimePoint to) {
|
||||
super(logicalTimePoint, author, mappedTo, device, from, to);
|
||||
Mark mappedTo, DeviceIdentifier device, TimePoint from, TimePoint toInclusive) {
|
||||
super(logicalTimePoint, author, mappedTo, device, from, toInclusive);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+5
-5
@@ -51,8 +51,8 @@ public abstract class BaseRegattaLogDeviceMappingFinder<ItemT extends WithID>
|
||||
for (final RegattaLogDeviceMappingEvent<ItemT> event : events.get(item)) {
|
||||
final TimePoint from = event.getFrom();
|
||||
final RegattaLogCloseOpenEndedDeviceMappingEvent closingEvent = closingEvents.get(event.getId());
|
||||
final TimePoint to = closingEvent != null ? closingEvent.getClosingTimePoint() : event.getTo();
|
||||
final TimeRange mappingTimeRange = new TimeRangeImpl(from, to);
|
||||
final TimePoint toInclusive = closingEvent != null ? closingEvent.getClosingTimePointInclusive() : event.getToInclusive();
|
||||
final TimeRange mappingTimeRange = new TimeRangeImpl(from, toInclusive, /* inclusive */ true);
|
||||
if (mappingTimeRange.includes(fixTimePoint)) {
|
||||
if (closingEvent != null) {
|
||||
log.revokeEvent(closingEvent.getAuthor(), closingEvent,
|
||||
@@ -65,8 +65,8 @@ public abstract class BaseRegattaLogDeviceMappingFinder<ItemT extends WithID>
|
||||
if (!endOfFirstHalf.before(from)) {
|
||||
log.add(createDeviceMappingEvent(item, event.getAuthor(), from, endOfFirstHalf, event.getDevice()));
|
||||
}
|
||||
if (to == null || !to.before(startOfSecondHalf)) {
|
||||
log.add(createDeviceMappingEvent(item, event.getAuthor(), startOfSecondHalf, to,
|
||||
if (toInclusive == null || !toInclusive.before(startOfSecondHalf)) {
|
||||
log.add(createDeviceMappingEvent(item, event.getAuthor(), startOfSecondHalf, toInclusive,
|
||||
event.getDevice()));
|
||||
}
|
||||
}
|
||||
@@ -74,6 +74,6 @@ public abstract class BaseRegattaLogDeviceMappingFinder<ItemT extends WithID>
|
||||
}
|
||||
|
||||
protected abstract RegattaLogDeviceMappingEvent<ItemT> createDeviceMappingEvent(ItemT item,
|
||||
AbstractLogEventAuthor author, TimePoint plus, TimePoint to, DeviceIdentifier deviceId);
|
||||
AbstractLogEventAuthor author, TimePoint plus, TimePoint toInclusive, DeviceIdentifier deviceId);
|
||||
|
||||
}
|
||||
+2
-2
@@ -25,8 +25,8 @@ public class RegattaLogDeviceCompetitorMappingFinder extends BaseRegattaLogDevic
|
||||
|
||||
@Override
|
||||
protected RegattaLogDeviceCompetitorMappingEvent createDeviceMappingEvent(Competitor item, AbstractLogEventAuthor author,
|
||||
TimePoint from, TimePoint to, DeviceIdentifier deviceId) {
|
||||
TimePoint from, TimePoint toInclusive, DeviceIdentifier deviceId) {
|
||||
final TimePoint now = MillisecondsTimePoint.now();
|
||||
return new RegattaLogDeviceCompetitorMappingEventImpl(now, now, author, UUID.randomUUID(), item, deviceId, from, to);
|
||||
return new RegattaLogDeviceCompetitorMappingEventImpl(now, now, author, UUID.randomUUID(), item, deviceId, from, toInclusive);
|
||||
}
|
||||
}
|
||||
+8
-8
@@ -48,8 +48,8 @@ public class RegattaLogDeviceMappingFinder<ItemT extends WithID>
|
||||
}
|
||||
|
||||
protected DeviceMappingWithRegattaLogEvent<ItemT> createMapping(DeviceIdentifier device, ItemT item,
|
||||
TimePoint from, TimePoint to, Serializable originalEventId, RegattaLogDeviceMappingEvent<ItemT> event) {
|
||||
return new DeviceMappingWithRegattaLogEventImpl<ItemT>(item, device, new TimeRangeImpl(from, to),
|
||||
TimePoint from, TimePoint toInclusive, Serializable originalEventId, RegattaLogDeviceMappingEvent<ItemT> event) {
|
||||
return new DeviceMappingWithRegattaLogEventImpl<ItemT>(item, device, new TimeRangeImpl(from, toInclusive, /* inclusive */ true),
|
||||
Collections.singletonList(originalEventId), event);
|
||||
}
|
||||
|
||||
@@ -95,16 +95,16 @@ public class RegattaLogDeviceMappingFinder<ItemT extends WithID>
|
||||
List<DeviceMappingWithRegattaLogEvent<ItemT>> result = new ArrayList<>();
|
||||
for (final RegattaLogDeviceMappingEvent<ItemT> event : events) {
|
||||
TimePoint from = event.getFrom();
|
||||
TimePoint to = event.getTo();
|
||||
TimePoint closingTimePoint = closingEvents.containsKey(event.getId()) ? closingEvents.get(event.getId())
|
||||
.getClosingTimePoint() : null;
|
||||
TimePoint toInclusive = event.getToInclusive();
|
||||
TimePoint closingTimePointInclusive = closingEvents.containsKey(event.getId()) ? closingEvents.get(event.getId())
|
||||
.getClosingTimePointInclusive() : null;
|
||||
if (from == null) {
|
||||
logger.severe("No start time set for DeviceMappingEvent with ID: " + event.getId());
|
||||
}
|
||||
if (to == null) {
|
||||
to = closingTimePoint;
|
||||
if (toInclusive == null) {
|
||||
toInclusive = closingTimePointInclusive;
|
||||
}
|
||||
result.add(createMapping(event.getDevice(), item, from, to, event.getId(), event));
|
||||
result.add(createMapping(event.getDevice(), item, from, toInclusive, event.getId(), event));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+5
-5
@@ -24,20 +24,20 @@ public class RegattaLogOpenEndedDeviceMappingCloser extends
|
||||
RegattaLogAnalyzer<List<RegattaLogCloseOpenEndedDeviceMappingEvent>> {
|
||||
private final DeviceMapping<?> mapping;
|
||||
protected final AbstractLogEventAuthor author;
|
||||
protected final TimePoint closingTimePoint;
|
||||
protected final TimePoint closingTimePointInclusive;
|
||||
|
||||
public RegattaLogOpenEndedDeviceMappingCloser(RegattaLog log, DeviceMapping<?> mapping,
|
||||
AbstractLogEventAuthor author, TimePoint closingTimePoint) {
|
||||
AbstractLogEventAuthor author, TimePoint closingTimePointInclusive) {
|
||||
super(log);
|
||||
this.mapping = mapping;
|
||||
this.author = author;
|
||||
this.closingTimePoint = closingTimePoint;
|
||||
this.closingTimePointInclusive = closingTimePointInclusive;
|
||||
}
|
||||
|
||||
protected RegattaLogCloseOpenEndedDeviceMappingEvent createCloseEvent(TimePoint logicalTimePoint,
|
||||
Serializable eventToCloseId) {
|
||||
return new RegattaLogCloseOpenEndedDeviceMappingEventImpl(logicalTimePoint, author, eventToCloseId,
|
||||
closingTimePoint);
|
||||
closingTimePointInclusive);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -46,7 +46,7 @@ public class RegattaLogOpenEndedDeviceMappingCloser extends
|
||||
|
||||
for (Serializable eventId : mapping.getOriginalRaceLogEventIds()) {
|
||||
RegattaLogDeviceMappingEvent<?> event = (RegattaLogDeviceMappingEvent<?>) getLog().getEventById(eventId);
|
||||
if (event.getFrom() == null || event.getTo() == null) {
|
||||
if (event.getFrom() == null || event.getToInclusive() == null) {
|
||||
result.add(createCloseEvent(MillisecondsTimePoint.now(), event.getId()));
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -66,7 +66,8 @@ public class DeviceMappingImpl<ItemType extends WithID> implements DeviceMapping
|
||||
}
|
||||
|
||||
public static <T extends WithID> DeviceMapping<T> convertToDeviceMapping(RegattaLogDeviceMappingEvent<T> event) {
|
||||
return new DeviceMappingImpl<T>(event.getMappedTo(), event.getDevice(), new TimeRangeImpl(event.getFrom(), event.getTo()),
|
||||
return new DeviceMappingImpl<T>(event.getMappedTo(), event.getDevice(),
|
||||
new TimeRangeImpl(event.getFrom(), event.getToInclusive(), /* inclusive */ true),
|
||||
event.getId(), event.getClass());
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -58,7 +58,7 @@ public class SwissTimingTrackingConnectivityParameters extends AbstractRaceTrack
|
||||
|
||||
@Override
|
||||
public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, WindStore windStore,
|
||||
RaceLogResolver raceLogResolver) throws Exception {
|
||||
RaceLogResolver raceLogResolver, long timeoutInMilliseconds) throws Exception {
|
||||
return swissTimingFactory.createRaceTracker(raceID, raceName, raceDescription, boatClass, hostname, port,
|
||||
startList, delayToLiveInMillis, raceLogStore, regattaLogStore, windStore,
|
||||
useInternalMarkPassingAlgorithm,
|
||||
@@ -67,7 +67,7 @@ public class SwissTimingTrackingConnectivityParameters extends AbstractRaceTrack
|
||||
|
||||
@Override
|
||||
public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry,
|
||||
WindStore windStore, RaceLogResolver raceLogResolver) throws Exception {
|
||||
WindStore windStore, RaceLogResolver raceLogResolver, long timeoutInMilliseconds) throws Exception {
|
||||
return swissTimingFactory.createRaceTracker(regatta, raceID, raceName, raceDescription, boatClass, hostname,
|
||||
port, startList, delayToLiveInMillis, windStore, useInternalMarkPassingAlgorithm,
|
||||
domainFactory, trackedRegattaRegistry, raceLogResolver, raceLogStore, regattaLogStore, this);
|
||||
|
||||
+2
-2
@@ -132,7 +132,7 @@ public class SwissTimingReplayConnectivityParameters extends AbstractRaceTrackin
|
||||
|
||||
@Override
|
||||
public RaceTracker createRaceTracker(TrackedRegattaRegistry trackedRegattaRegistry, final WindStore windStore,
|
||||
RaceLogResolver raceLogResolver) throws Exception {
|
||||
RaceLogResolver raceLogResolver, long timeoutInMilliseconds) throws Exception {
|
||||
SwissTimingReplayToDomainAdapter listener = new SwissTimingReplayToDomainAdapter(/* regatta */ null, raceName,
|
||||
raceID, domainFactory.getBaseDomainFactory().getOrCreateBoatClass(boatClassName), domainFactory,
|
||||
trackedRegattaRegistry, useInternalMarkPassingAlgorithm, raceLogResolver, raceLogStore,
|
||||
@@ -143,7 +143,7 @@ public class SwissTimingReplayConnectivityParameters extends AbstractRaceTrackin
|
||||
|
||||
@Override
|
||||
public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry,
|
||||
WindStore windStore, RaceLogResolver raceLogResolver) throws Exception {
|
||||
WindStore windStore, RaceLogResolver raceLogResolver, long timeoutInMilliseconds) throws Exception {
|
||||
SwissTimingReplayToDomainAdapter listener = new SwissTimingReplayToDomainAdapter(regatta, raceName,
|
||||
raceID, domainFactory.getBaseDomainFactory().getOrCreateBoatClass(boatClassName),
|
||||
domainFactory, trackedRegattaRegistry, useInternalMarkPassingAlgorithm, raceLogResolver,
|
||||
|
||||
+1
-1
@@ -154,6 +154,6 @@ public class SwissTimingReplayServiceImpl implements SwissTimingReplayService {
|
||||
boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore)
|
||||
throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception {
|
||||
trackerManager.addRace(regattaToAddTo, new SwissTimingReplayConnectivityParameters(link, raceName, raceID,
|
||||
boatClassName, useInternalMarkPassingAlgorithm, domainFactory, this, raceLogStore, regattaLogStore), /* timeout */ 0);
|
||||
boatClassName, useInternalMarkPassingAlgorithm, domainFactory, this, raceLogStore, regattaLogStore), /* timeout */ -1);
|
||||
}
|
||||
}
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
package com.sap.sailing.domain.common.test;
|
||||
|
||||
import static com.sap.sailing.domain.common.test.TimeTestHelpers.create;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sse.common.MultiTimeRange;
|
||||
import com.sap.sse.common.TimeRange;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MultiTimeRangeImpl;
|
||||
|
||||
public class MultiTimeRangeTest {
|
||||
private MultiTimeRange createMulti(long... startAndEndSequence) {
|
||||
assert startAndEndSequence != null;
|
||||
assert startAndEndSequence.length % 2 == 0;
|
||||
List<TimeRange> list = new ArrayList<>();
|
||||
for (int i=0; i<startAndEndSequence.length; i+=2) {
|
||||
list.add(create(startAndEndSequence[i], startAndEndSequence[i+1]));
|
||||
}
|
||||
return new MultiTimeRangeImpl(list);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEmptyMultiTimeRange() {
|
||||
assertTrue(createMulti().isEmpty());
|
||||
assertTrue(createMulti(100, 200).intersection(create(200, 300)).isEmpty());
|
||||
assertTrue(createMulti(100, 100).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void equality() {
|
||||
assertEquals(createMulti(300, 400, 400, 500, 200, 350), createMulti(400, 500, 300, 400, 200, 350));
|
||||
assertEquals(createMulti(300, 400, 400, 500, 200, 350), createMulti(200, 500));
|
||||
assertFalse(createMulti(300, 400, 400, 500, 200, 350).equals(createMulti(300, 600)));
|
||||
assertFalse(createMulti(300, 400).equals(createMulti(70, 500)));
|
||||
assertEquals(createMulti(500, 600, 300, 400), createMulti(300, 400, 500, 600, 300, 400));
|
||||
assertEquals(createMulti(500, 600, 300, 400), createMulti(300, 400, 500, 600));
|
||||
}
|
||||
@Test
|
||||
public void testAdd() {
|
||||
MultiTimeRange mtr = createMulti(100, 200);
|
||||
MultiTimeRange multi = mtr.union(createMulti(300, 400));
|
||||
assertEquals(2, Util.size(multi));
|
||||
assertEquals(mtr.iterator().next(), multi.iterator().next());
|
||||
assertTrue(multi.includes(create(150)));
|
||||
assertTrue(multi.includes(create(350)));
|
||||
assertFalse(multi.includes(create(250)));
|
||||
assertFalse(multi.includes(create(200))); // exclusive first time range
|
||||
assertFalse(multi.includes(create(400))); // exclusive last time range
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMinimality() {
|
||||
assertEquals(1, Util.size(createMulti(300, 400, 400, 500, 200, 350)));
|
||||
assertEquals(create(200), Util.first(createMulti(300, 400, 400, 500, 200, 350)).from());
|
||||
assertEquals(create(500), Util.last(createMulti(300, 400, 400, 500, 200, 350)).to());
|
||||
|
||||
assertEquals(2, Util.size(createMulti(500, 600, 300, 400)));
|
||||
assertEquals(create(300), Util.first(createMulti(500, 600, 300, 400)).from());
|
||||
assertEquals(create(600), Util.last(createMulti(500, 600, 300, 400)).to());
|
||||
assertTrue(createMulti(500, 600, 300, 400).includes(create(350)));
|
||||
assertTrue(createMulti(500, 600, 300, 400).includes(create(550)));
|
||||
assertFalse(createMulti(500, 600, 300, 400).includes(create(450)));
|
||||
assertFalse(createMulti(500, 600, 300, 400).includes(create(400)));
|
||||
assertFalse(createMulti(500, 600, 300, 400).includes(create(600)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void intersection() {
|
||||
assertEquals(createMulti(100, 200, 300, 400), createMulti(100, 200, 300, 400).intersection(createMulti(100, 400)));
|
||||
assertEquals(createMulti(100, 400), createMulti(100, 350, 300, 400).intersection(createMulti(100, 400)));
|
||||
assertTrue(createMulti(100, 200).intersection(createMulti()).isEmpty());
|
||||
assertTrue(createMulti(100, 200).intersection(createMulti(300, 400)).isEmpty());
|
||||
assertTrue(createMulti(100, 200).intersection(createMulti(200, 400)).isEmpty());
|
||||
assertEquals(createMulti(100, 101), createMulti(100, 200).intersection(createMulti(50, 101)));
|
||||
assertEquals(createMulti(120, 200, 300, 380), createMulti(100, 380).intersection(createMulti(120, 200, 300, 400)));
|
||||
assertEquals(createMulti(100, 150, 160, 180, 190, 200, 300, 350, 370, 400), createMulti(100, 200, 300, 400).intersection(createMulti(50, 150, 160, 180, 190, 220, 280, 350, 370, 410)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void intersects() {
|
||||
assertTrue(createMulti(100, 200, 300, 400).intersects(createMulti(50, 150, 160, 180, 190, 220, 280, 350, 370, 410)));
|
||||
assertFalse(createMulti(100, 200).intersects(createMulti(220, 300)));
|
||||
assertFalse(createMulti(100, 200).intersects(createMulti(200, 300)));
|
||||
assertTrue(createMulti(100, 200, 300, 400).intersects(createMulti(150, 350)));
|
||||
assertTrue(createMulti(100, 200, 300, 400).intersects(createMulti(350, 370)));
|
||||
assertFalse(createMulti(100, 200, 300, 400).intersects(createMulti(250, 270)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void subtract() {
|
||||
assertEquals(createMulti(100, 120, 180, 200), createMulti(100, 200).subtract(createMulti(120, 180)));
|
||||
assertTrue(createMulti(100, 200).subtract(createMulti(100, 200)).isEmpty());
|
||||
assertEquals(createMulti(100, 120), createMulti(100, 200).subtract(createMulti(120, 200)));
|
||||
assertEquals(createMulti(100, 120, 300, 400), createMulti(100, 200, 300, 400).subtract(createMulti(120, 250)));
|
||||
assertEquals(createMulti(100, 120, 180, 200, 300, 400), createMulti(100, 200, 300, 400).subtract(createMulti(120, 180)));
|
||||
assertEquals(createMulti(100, 120, 300, 400), createMulti(100, 200, 300, 400).subtract(createMulti(120, 300)));
|
||||
assertEquals(createMulti(100, 120, 350, 400), createMulti(100, 200, 300, 400).subtract(createMulti(120, 350)));
|
||||
assertEquals(createMulti(100, 120, 350, 360, 370, 400), createMulti(100, 200, 300, 400).subtract(createMulti(120, 350, 360, 370)));
|
||||
}
|
||||
}
|
||||
+24
-1
@@ -11,6 +11,7 @@ import java.util.GregorianCalendar;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
@@ -39,12 +40,34 @@ public class TimePointTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualForEuqal() {
|
||||
public void testEqualForEqual() {
|
||||
TimePoint t1 = new MillisecondsTimePoint(Long.MIN_VALUE);
|
||||
TimePoint t2 = new MillisecondsTimePoint(Long.MIN_VALUE);
|
||||
assertThat(t1, is(t2));
|
||||
assertThat(t2, is(t1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAvoidOverflowBeyondEndOfTime() {
|
||||
TimePoint t1 = TimePoint.EndOfTime;
|
||||
assertEquals(TimePoint.EndOfTime, t1.plus(1));
|
||||
assertEquals(TimePoint.EndOfTime, t1.plus(100));
|
||||
assertEquals(TimePoint.EndOfTime, t1.plus(Duration.ONE_MINUTE));
|
||||
TimePoint t2 = TimePoint.EndOfTime.minus(100);
|
||||
assertEquals(TimePoint.EndOfTime, t2.plus(200));
|
||||
assertEquals(TimePoint.EndOfTime, t2.plus(Duration.ONE_HOUR));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAvoidUnderflowBeyondBeginningOfTime() {
|
||||
TimePoint t1 = TimePoint.BeginningOfTime;
|
||||
assertEquals(TimePoint.BeginningOfTime, t1.minus(1));
|
||||
assertEquals(TimePoint.BeginningOfTime, t1.minus(100));
|
||||
assertEquals(TimePoint.BeginningOfTime, t1.minus(Duration.ONE_MINUTE));
|
||||
TimePoint t2 = TimePoint.BeginningOfTime.plus(100);
|
||||
assertEquals(TimePoint.BeginningOfTime, t2.minus(200));
|
||||
assertEquals(TimePoint.BeginningOfTime, t2.minus(Duration.ONE_HOUR));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEqualForNull() {
|
||||
|
||||
+73
-22
@@ -1,27 +1,23 @@
|
||||
package com.sap.sailing.domain.common.test;
|
||||
|
||||
import static com.sap.sailing.domain.common.test.TimeTestHelpers.create;
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertNull;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import junit.framework.Assert;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sse.common.MultiTimeRange;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.TimeRange;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.common.impl.TimeRangeImpl;
|
||||
|
||||
import junit.framework.Assert;
|
||||
|
||||
public class TimeRangeTest {
|
||||
protected TimeRange create(long from, long to) {
|
||||
return new TimeRangeImpl(new MillisecondsTimePoint(from), new MillisecondsTimePoint(to));
|
||||
}
|
||||
|
||||
protected TimePoint create(long millis) {
|
||||
return new MillisecondsTimePoint(millis);
|
||||
}
|
||||
|
||||
private void liesWithin(long fromOuter, long toOuter, long fromInner, long toInner) {
|
||||
TimeRange outer = create(fromOuter, toOuter);
|
||||
TimeRange inner = create(fromInner, toInner);
|
||||
@@ -44,10 +40,18 @@ public class TimeRangeTest {
|
||||
TimeRange range = create(0, 100);
|
||||
Assert.assertTrue(range.includes(create(0)));
|
||||
Assert.assertTrue(range.includes(create(1)));
|
||||
Assert.assertTrue(range.includes(create(100)));
|
||||
Assert.assertTrue(range.includes(create(99)));
|
||||
Assert.assertFalse(range.includes(create(100))); // exclusive end
|
||||
Assert.assertFalse(range.includes(create(101)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void empty() {
|
||||
assertTrue(create(100, 100).isEmpty());
|
||||
assertFalse(create(100, 101).isEmpty());
|
||||
assertFalse(create(null, 100l).isEmpty());
|
||||
assertFalse(create(100l, null).isEmpty());
|
||||
}
|
||||
@Test
|
||||
public void intersects() {
|
||||
TimeRange one = create(0, 50);
|
||||
@@ -84,6 +88,11 @@ public class TimeRangeTest {
|
||||
two = create(11, 16);
|
||||
assertNull(one.union(two));
|
||||
|
||||
assertEquals(create(5, 20), one.union(create(10, 20)));
|
||||
assertEquals(create(5, 10), create(5, 10).union(create(5, 10)));
|
||||
assertEquals(create(5, 10), create(5, 10).union(create(6, 7)));
|
||||
assertEquals(create(5, 10), create(5, 10).union(create(6, 6)));
|
||||
|
||||
two = create(7, TimePoint.EndOfTime.asMillis());
|
||||
union = two.union(one);
|
||||
TimeRange union2 = one.union(two);
|
||||
@@ -122,6 +131,26 @@ public class TimeRangeTest {
|
||||
assertEquals(10, intersection.to().asMillis());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startAfterTest() {
|
||||
assertTrue(create(100, 200).startsAtOrAfter(create(100)));
|
||||
assertTrue(create(100, 200).startsAtOrAfter(create(99)));
|
||||
assertFalse(create(100, 200).startsAtOrAfter(create(101)));
|
||||
assertTrue(create(100, 200).startsAfter(create(50, 100)));
|
||||
assertFalse(create(100, 200).startsAfter(create(50, 150)));
|
||||
assertFalse(create(100, 200).startsAfter(create(50l, null))); // can't start after a range that extends until the end of time
|
||||
}
|
||||
|
||||
@Test
|
||||
public void touches() {
|
||||
assertTrue(create(100, 200).touches(create(100, 200))); // touches is reflexive
|
||||
assertTrue(create(100, 200).touches(create(50, 150)));
|
||||
assertTrue(create(100, 200).touches(create(50, 100)));
|
||||
assertTrue(create(100, 200).touches(create(200, 350)));
|
||||
assertFalse(create(100, 200).touches(create(201, 250)));
|
||||
assertFalse(create(100, 200).touches(create(50, 99)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void timeDifferenceTest() {
|
||||
TimeRange one = create(5, 10);
|
||||
@@ -138,17 +167,17 @@ public class TimeRangeTest {
|
||||
|
||||
@Test
|
||||
public void testSubtractionWithMinuendCompletelyWithin() {
|
||||
testSubtraction(5, 10, 7, 8, /* expected */ 5, 6, 9, 10);
|
||||
testSubtraction(5, 10, 7, 8, /* expected */ 5, 7, 8, 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubtractionWithMinuendStartingAtFrom() {
|
||||
testSubtraction(5, 10, 5, 8, /* expected */ 9, 10);
|
||||
testSubtraction(5, 10, 5, 8, /* expected */ 8, 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubtractionWithMinuendEndingAtTo() {
|
||||
testSubtraction(5, 10, 7, 10, /* expected */ 5, 6);
|
||||
testSubtraction(5, 10, 7, 10, /* expected */ 5, 7);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -158,24 +187,46 @@ public class TimeRangeTest {
|
||||
|
||||
@Test
|
||||
public void testSubtractionWithMinuendOverlappingAtFrom() {
|
||||
testSubtraction(5, 10, 4, 7, /* expected */ 8, 10);
|
||||
testSubtraction(5, 10, 4, 7, /* expected */ 7, 10);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSubtractionWithMinuendOverlappingAtto() {
|
||||
testSubtraction(5, 10, 7, 12, /* expected */ 5, 6);
|
||||
testSubtraction(5, 10, 7, 12, /* expected */ 5, 7);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExclusiveEnd() {
|
||||
assertFalse(create(100, 200).includes(create(200)));
|
||||
assertTrue(create(100, 200).includes(create(199)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEndsBeforeWithExclusiveEnd() {
|
||||
assertTrue(create(100, 200).endsBefore(create(200)));
|
||||
assertTrue(create(100, 200).endsBefore(create(201)));
|
||||
assertFalse(create(100, 200).endsBefore(create(199)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEndsAfterWithExclusiveEnd() {
|
||||
assertFalse(create(100, 200).endsAfter(create(100, 200)));
|
||||
assertFalse(create(100, 200).endsAfter(create(100, 201)));
|
||||
assertTrue(create(100, 200).endsAfter(create(100, 199)));
|
||||
}
|
||||
|
||||
private void testSubtraction(long allFrom, long allTo, long minuendFrom, long minuendTo, long... expected) {
|
||||
TimeRange all = create(allFrom, allTo);
|
||||
TimeRange minuend = create(minuendFrom, minuendTo);
|
||||
TimeRange[] diff = all.subtract(minuend);
|
||||
assertEquals("Expected to obtain "+expected.length/2+" time ranges but got "+diff.length, expected.length/2, diff.length);
|
||||
for (int i=0; i<expected.length/2; i++) {
|
||||
assertEquals("expected "+i+"-th time range's \"from\" time point with millis "+expected[2*i]+" but got "+diff[i].from().asMillis(),
|
||||
create(expected[2*i]), diff[i].from());
|
||||
assertEquals("expected "+i+"-th time range's \"to\" time point with millis "+expected[2*i+1]+" but got "+diff[i].to().asMillis(),
|
||||
create(expected[2*i+1]), diff[i].to());
|
||||
MultiTimeRange diff = all.subtract(minuend);
|
||||
assertEquals("Expected to obtain "+expected.length/2+" time ranges but got "+Util.size(diff), expected.length/2, Util.size(diff));
|
||||
int i=0;
|
||||
for (final TimeRange timeRangeFromDiff : diff) {
|
||||
assertEquals("expected "+i+"-th time range's \"from\" time point with millis "+expected[2*i]+" but got "+timeRangeFromDiff.from().asMillis(),
|
||||
create(expected[2*i]), timeRangeFromDiff.from());
|
||||
assertEquals("expected "+i+"-th time range's \"to\" time point with millis "+expected[2*i+1]+" but got "+timeRangeFromDiff.to().asMillis(),
|
||||
create(expected[2*i+1]), timeRangeFromDiff.to());
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
+30
@@ -0,0 +1,30 @@
|
||||
package com.sap.sailing.domain.common.test;
|
||||
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.TimeRange;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.common.impl.TimeRangeImpl;
|
||||
|
||||
public class TimeTestHelpers {
|
||||
|
||||
protected static TimeRange create(Long from, Long toExclusive) {
|
||||
return new TimeRangeImpl(from==null?null:new MillisecondsTimePoint(from), toExclusive==null?null:new MillisecondsTimePoint(toExclusive));
|
||||
}
|
||||
|
||||
protected static TimeRange create(int from, int toExclusive) {
|
||||
return create((long) from, (long) toExclusive);
|
||||
}
|
||||
|
||||
protected static TimeRange create(int from, long toExclusive) {
|
||||
return create((long) from, toExclusive);
|
||||
}
|
||||
|
||||
protected static TimeRange create(long from, int toExclusive) {
|
||||
return create(from, (long) toExclusive);
|
||||
}
|
||||
|
||||
protected static TimePoint create(long millis) {
|
||||
return new MillisecondsTimePoint(millis);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -5,12 +5,18 @@ import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
|
||||
public class UtilTest {
|
||||
private List<Integer> l;
|
||||
@@ -42,4 +48,21 @@ public class UtilTest {
|
||||
assertEquals(0, Util.indexOf(l, 1));
|
||||
assertEquals(2, Util.indexOf(l, null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testGroup() {
|
||||
Pair<String, String> a1 = new Pair<>("a", "a");
|
||||
Pair<String, String> a2 = new Pair<>("a", "b");
|
||||
Pair<String, String> a3 = new Pair<>("a", "c");
|
||||
Pair<String, String> b1 = new Pair<>("b", "a");
|
||||
Pair<String, String> b2 = new Pair<>("b", "b");
|
||||
Pair<String, String> c1 = new Pair<>("c", "a");
|
||||
|
||||
Map<String, Iterable<Pair<String, String>>> expected = new HashMap<>();
|
||||
expected.put("a", new HashSet<Pair<String, String>>(Arrays.asList(a1, a2, a3)));
|
||||
expected.put("b", new HashSet<Pair<String, String>>(Arrays.asList(b1, b2)));
|
||||
expected.put("c", new HashSet<Pair<String, String>>(Collections.singleton(c1)));
|
||||
|
||||
assertEquals(expected, Util.group(Arrays.asList(a1, b1, a2, c1, b2, a3), Pair::getA, HashSet::new));
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -41,7 +41,7 @@ public abstract class AbstractManeuverDetectionTestCase extends OnlineTracTracBa
|
||||
* @param toleranceInMillis
|
||||
* The tolerance of time, the maneuver should have happened in milliseconds.
|
||||
*/
|
||||
protected void assertManeuver(List<Maneuver> maneuverList, ManeuverType maneuverType,
|
||||
protected void assertManeuver(Iterable<Maneuver> maneuverList, ManeuverType maneuverType,
|
||||
TimePoint maneuverTimePoint, int toleranceInMillis) {
|
||||
for (Maneuver maneuver : maneuverList) {
|
||||
assertNotNull(maneuver.getTimePoint());
|
||||
|
||||
+2
-1
@@ -40,6 +40,7 @@ import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
|
||||
import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.RaceTracker;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
@@ -94,7 +95,7 @@ public class CourseUpdateTest extends AbstractTracTracLiveTest {
|
||||
},
|
||||
/* delayToLiveInMillis */0l, /* millisecondsOverWhichToAverageWind */30000, /* simulator */null, /* courseDesignUpdateURI */
|
||||
null, /* tracTracUsername */null, /* tracTracPassword */null, getEventSubscriber(), getRaceSubscriber(),
|
||||
/*ignoreTracTracMarkPassings*/false, mock(RaceLogResolver.class)) {
|
||||
/*ignoreTracTracMarkPassings*/false, mock(RaceLogResolver.class), RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS) {
|
||||
@Override
|
||||
protected void handleEvent(Triple<IControlRoute, Long, Void> event) {
|
||||
super.handleEvent(event);
|
||||
|
||||
+7
-4
@@ -27,6 +27,7 @@ import com.sap.sailing.domain.common.impl.WindImpl;
|
||||
import com.sap.sailing.domain.common.impl.WindSourceImpl;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tractracadapter.ReceiverType;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.tractrac.model.lib.api.event.CreateModelException;
|
||||
import com.tractrac.subscription.lib.api.SubscriberInitializationException;
|
||||
@@ -62,9 +63,10 @@ public class ManeuverAnalysis505Test extends AbstractManeuverDetectionTestCase {
|
||||
Date toDate = dateFormat.parse("06/23/2011-15:29:50");
|
||||
assertNotNull(fromDate);
|
||||
assertNotNull(toDate);
|
||||
List<Maneuver> maneuvers = getTrackedRace().getManeuvers(competitor, new MillisecondsTimePoint(fromDate),
|
||||
Iterable<Maneuver> maneuvers = getTrackedRace().getManeuvers(competitor, new MillisecondsTimePoint(fromDate),
|
||||
new MillisecondsTimePoint(toDate), /* waitForLatest */ true);
|
||||
maneuversInvalid = new ArrayList<Maneuver>(maneuvers);
|
||||
maneuversInvalid = new ArrayList<Maneuver>();
|
||||
Util.addAll(maneuvers, maneuversInvalid);
|
||||
|
||||
assertManeuver(maneuvers, ManeuverType.TACK,
|
||||
new MillisecondsTimePoint(dateFormat.parse("06/23/2011-15:28:24")), TACK_TOLERANCE);
|
||||
@@ -85,9 +87,10 @@ public class ManeuverAnalysis505Test extends AbstractManeuverDetectionTestCase {
|
||||
Date toDate = dateFormat.parse("06/23/2011-16:38:01");
|
||||
assertNotNull(fromDate);
|
||||
assertNotNull(toDate);
|
||||
List<Maneuver> maneuvers = getTrackedRace().getManeuvers(competitor, new MillisecondsTimePoint(fromDate),
|
||||
Iterable<Maneuver> maneuvers = getTrackedRace().getManeuvers(competitor, new MillisecondsTimePoint(fromDate),
|
||||
new MillisecondsTimePoint(toDate), /* waitForLatest */ true);
|
||||
maneuversInvalid = new ArrayList<Maneuver>(maneuvers);
|
||||
maneuversInvalid = new ArrayList<Maneuver>();
|
||||
Util.addAll(maneuvers, maneuversInvalid);
|
||||
|
||||
assertManeuver(maneuvers, ManeuverType.TACK,
|
||||
new MillisecondsTimePoint(dateFormat.parse("06/23/2011-15:28:24")), TACK_TOLERANCE);
|
||||
|
||||
+4
-2
@@ -38,6 +38,7 @@ import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.impl.TrackedLegImpl;
|
||||
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;
|
||||
import com.tractrac.subscription.lib.api.SubscriberInitializationException;
|
||||
@@ -100,9 +101,10 @@ public class ManeuverAnalysisIDMChampionsFinalTest extends AbstractManeuverDetec
|
||||
Date fromDate = new Date(toDate.getTime()-450000l);
|
||||
final SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ");
|
||||
TimePoint maneuverTime = new MillisecondsTimePoint(dateFormatter.parse("2011-10-03T15:52:30.000+0200"));
|
||||
List<Maneuver> maneuvers = getTrackedRace().getManeuvers(competitor, new MillisecondsTimePoint(fromDate),
|
||||
Iterable<Maneuver> maneuvers = getTrackedRace().getManeuvers(competitor, new MillisecondsTimePoint(fromDate),
|
||||
new MillisecondsTimePoint(toDate), /* waitForLatest */ true);
|
||||
maneuversInvalid = new ArrayList<Maneuver>(maneuvers);
|
||||
maneuversInvalid = new ArrayList<Maneuver>();
|
||||
Util.addAll(maneuvers, maneuversInvalid);
|
||||
assertManeuver(maneuvers, ManeuverType.PENALTY_CIRCLE, maneuverTime, PENALTYCIRCLE_TOLERANCE);
|
||||
List<ManeuverType> maneuverTypesFound = new ArrayList<ManeuverType>();
|
||||
maneuverTypesFound.add(ManeuverType.PENALTY_CIRCLE);
|
||||
|
||||
+4
-3
@@ -12,7 +12,6 @@ import java.net.URL;
|
||||
import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -27,6 +26,7 @@ import com.sap.sailing.domain.common.impl.WindImpl;
|
||||
import com.sap.sailing.domain.common.impl.WindSourceImpl;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tractracadapter.ReceiverType;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.tractrac.model.lib.api.event.CreateModelException;
|
||||
import com.tractrac.subscription.lib.api.SubscriberInitializationException;
|
||||
@@ -68,9 +68,10 @@ public class ManeuverAnalysisQingdao2014Test extends AbstractManeuverDetectionTe
|
||||
Date toDate = dateFormat.parse("05/01/2014-09:10:00");
|
||||
assertNotNull(fromDate);
|
||||
assertNotNull(toDate);
|
||||
List<Maneuver> maneuvers = getTrackedRace().getManeuvers(competitor, new MillisecondsTimePoint(fromDate),
|
||||
Iterable<Maneuver> maneuvers = getTrackedRace().getManeuvers(competitor, new MillisecondsTimePoint(fromDate),
|
||||
new MillisecondsTimePoint(toDate), /* waitForLatest */ true);
|
||||
maneuversInvalid = new ArrayList<Maneuver>(maneuvers);
|
||||
maneuversInvalid = new ArrayList<Maneuver>();
|
||||
Util.addAll(maneuvers, maneuversInvalid);
|
||||
|
||||
assertManeuver(maneuvers, ManeuverType.JIBE, new MillisecondsTimePoint(dateFormat.parse("05/01/2014-09:08:15")), 5000);
|
||||
assertManeuver(maneuvers, ManeuverType.TACK, new MillisecondsTimePoint(dateFormat.parse("05/01/2014-09:08:51")), 5000);
|
||||
|
||||
+2
-2
@@ -87,7 +87,7 @@ public class ManeuverDetectionOnKielerWoche505Race2DataTest extends OnlineTracTr
|
||||
public void testManeuversForHasso() throws NoWindException {
|
||||
Competitor hasso = getCompetitorByName("Dr.Plattner");
|
||||
NavigableSet<MarkPassing> hassosMarkPassings = getTrackedRace().getMarkPassings(hasso);
|
||||
List<Maneuver> maneuvers = getTrackedRace().getManeuvers(hasso, hassosMarkPassings.first().getTimePoint(),
|
||||
Iterable<Maneuver> maneuvers = getTrackedRace().getManeuvers(hasso, hassosMarkPassings.first().getTimePoint(),
|
||||
hassosMarkPassings.last().getTimePoint(), /* waitForLatest */ true);
|
||||
Calendar c = new GregorianCalendar(TimeZone.getTimeZone("Europe/Berlin"));
|
||||
c.clear();
|
||||
@@ -105,7 +105,7 @@ public class ManeuverDetectionOnKielerWoche505Race2DataTest extends OnlineTracTr
|
||||
assertManeuver(maneuvers, ManeuverType.JIBE, Tack.PORT, new MillisecondsTimePoint(c.getTime()), /* tolerance in milliseconds */ 3000);
|
||||
}
|
||||
|
||||
private void assertManeuver(List<Maneuver> maneuvers, ManeuverType type, Tack newTack,
|
||||
private void assertManeuver(Iterable<Maneuver> maneuvers, ManeuverType type, Tack newTack,
|
||||
MillisecondsTimePoint timePoint, int toleranceInMilliseconds) {
|
||||
for (Maneuver maneuver : maneuvers) {
|
||||
if (maneuver.getType() == type && (newTack == null || newTack == maneuver.getNewTack()) &&
|
||||
|
||||
+2
-3
@@ -9,7 +9,6 @@ import java.text.ParseException;
|
||||
import java.util.Calendar;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.TimeZone;
|
||||
@@ -77,7 +76,7 @@ public class ManeuverDetectionOnMdM2011SemifinalTest extends OnlineTracTracBased
|
||||
public void testManeuversForDennis() throws NoWindException {
|
||||
Competitor dennis = getCompetitorByName("Gehrlein");
|
||||
NavigableSet<MarkPassing> dennisMarkPassings = getTrackedRace().getMarkPassings(dennis);
|
||||
List<Maneuver> maneuvers = getTrackedRace().getManeuvers(dennis, dennisMarkPassings.first().getTimePoint(),
|
||||
Iterable<Maneuver> maneuvers = getTrackedRace().getManeuvers(dennis, dennisMarkPassings.first().getTimePoint(),
|
||||
dennisMarkPassings.last().getTimePoint(), /* waitForLatest */ true);
|
||||
Calendar c = new GregorianCalendar(TimeZone.getTimeZone("Europe/Berlin"));
|
||||
c.set(2011, 10-1, 30, 13, 32, 42);
|
||||
@@ -96,7 +95,7 @@ public class ManeuverDetectionOnMdM2011SemifinalTest extends OnlineTracTracBased
|
||||
assertManeuver(maneuvers, ManeuverType.JIBE, Tack.STARBOARD, new MillisecondsTimePoint(c.getTime()), /* tolerance in milliseconds */ 3000);
|
||||
}
|
||||
|
||||
private void assertManeuver(List<Maneuver> maneuvers, ManeuverType type, Tack newTack,
|
||||
private void assertManeuver(Iterable<Maneuver> maneuvers, ManeuverType type, Tack newTack,
|
||||
MillisecondsTimePoint timePoint, int toleranceInMilliseconds) {
|
||||
for (Maneuver maneuver : maneuvers) {
|
||||
if (maneuver.getType() == type && (newTack == null || newTack == maneuver.getNewTack()) &&
|
||||
|
||||
+33
-40
@@ -4,7 +4,6 @@ import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
@@ -18,13 +17,13 @@ import org.junit.Test;
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
|
||||
import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
|
||||
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
|
||||
import com.sap.sailing.domain.tracking.RaceTracker;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sailing.domain.tractracadapter.DomainFactory;
|
||||
import com.sap.sailing.domain.tractracadapter.TracTracConnectionConstants;
|
||||
import com.sap.sailing.domain.tractracadapter.TracTracRaceTracker;
|
||||
import com.sap.sailing.domain.tractracadapter.impl.DomainFactoryImpl;
|
||||
import com.tractrac.model.lib.api.event.CreateModelException;
|
||||
import com.tractrac.subscription.lib.api.SubscriberInitializationException;
|
||||
import com.sap.sailing.domain.tractracadapter.impl.RaceTrackingConnectivityParametersImpl;
|
||||
|
||||
public class MultipleClassesInRegattaTest {
|
||||
private static final boolean tractracTunnel = Boolean.valueOf(System.getProperty("tractrac.tunnel", "false"));
|
||||
@@ -41,7 +40,7 @@ public class MultipleClassesInRegattaTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadTwoRacesWithEqualEventNameButDifferentClasses() throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException, SubscriberInitializationException {
|
||||
public void testLoadTwoRacesWithEqualEventNameButDifferentClasses() throws Exception {
|
||||
String httpAndHost = "http://" + TracTracConnectionConstants.HOST_NAME;
|
||||
String liveURI = "tcp://" + TracTracConnectionConstants.HOST_NAME + ":" + TracTracConnectionConstants.PORT_LIVE;
|
||||
String storedURI = "tcp://" + TracTracConnectionConstants.HOST_NAME + ":" + TracTracConnectionConstants.PORT_STORED;
|
||||
@@ -54,48 +53,28 @@ public class MultipleClassesInRegattaTest {
|
||||
}
|
||||
kiwotest1 = domainFactory
|
||||
.createRaceTracker(
|
||||
new URL(
|
||||
httpAndHost
|
||||
+ "/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=cce678c8-97e6-11e0-9aed-406186cbf87c"),
|
||||
new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI),
|
||||
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
|
||||
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE,
|
||||
tracTracUsername, tracTracPassword,
|
||||
TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
|
||||
new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class), /* connectivityParams */ null);
|
||||
EmptyRaceLogStore.INSTANCE,
|
||||
EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE, new DummyTrackedRegattaRegistry(),
|
||||
mock(RaceLogResolver.class), createConnectivityParams(httpAndHost, liveURI, storedURI, courseDesignUpdateURI,
|
||||
tracTracUsername, tracTracPassword, "cce678c8-97e6-11e0-9aed-406186cbf87c"), RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS);
|
||||
kiwotest2 = domainFactory
|
||||
.createRaceTracker(
|
||||
new URL(
|
||||
httpAndHost
|
||||
+ "/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=11290bd6-97e7-11e0-9aed-406186cbf87c"),
|
||||
new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI),
|
||||
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
|
||||
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE,
|
||||
tracTracUsername, tracTracPassword,
|
||||
TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
|
||||
new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class), /* connectivityParams */ null);
|
||||
EmptyRaceLogStore.INSTANCE,
|
||||
EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE, new DummyTrackedRegattaRegistry(),
|
||||
mock(RaceLogResolver.class), createConnectivityParams(httpAndHost, liveURI, storedURI, courseDesignUpdateURI,
|
||||
tracTracUsername, tracTracPassword, "11290bd6-97e7-11e0-9aed-406186cbf87c"), RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS);
|
||||
kiwotest3 = domainFactory
|
||||
.createRaceTracker(
|
||||
new URL(
|
||||
httpAndHost
|
||||
+ "/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=39635b24-97e7-11e0-9aed-406186cbf87c"),
|
||||
new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI),
|
||||
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
|
||||
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE,
|
||||
tracTracUsername, tracTracPassword,
|
||||
TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
|
||||
new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class), /* connectivityParams */ null);
|
||||
EmptyRaceLogStore.INSTANCE,
|
||||
EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE, new DummyTrackedRegattaRegistry(),
|
||||
mock(RaceLogResolver.class), createConnectivityParams(httpAndHost, liveURI, storedURI, courseDesignUpdateURI,
|
||||
tracTracUsername, tracTracPassword, "39635b24-97e7-11e0-9aed-406186cbf87c"), RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS);
|
||||
weym470may112014_2 = domainFactory
|
||||
.createRaceTracker(
|
||||
new URL(
|
||||
httpAndHost
|
||||
+ "/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=04498426-7dfd-11e0-8236-406186cbf87c"),
|
||||
new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI),
|
||||
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
|
||||
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE,
|
||||
tracTracUsername, tracTracPassword,
|
||||
TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
|
||||
new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class), /* connectivityParams */ null);
|
||||
EmptyRaceLogStore.INSTANCE,
|
||||
EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE, new DummyTrackedRegattaRegistry(),
|
||||
mock(RaceLogResolver.class), createConnectivityParams(httpAndHost, liveURI, storedURI, courseDesignUpdateURI,
|
||||
tracTracUsername, tracTracPassword, "04498426-7dfd-11e0-8236-406186cbf87c"), RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS);
|
||||
|
||||
assertEquals("STG", kiwotest1.getRegatta().getBoatClass().getName());
|
||||
assertEquals("5O5", kiwotest2.getRegatta().getBoatClass().getName());
|
||||
@@ -105,6 +84,20 @@ public class MultipleClassesInRegattaTest {
|
||||
assertNotSame(kiwotest1.getRegatta(), kiwotest3.getRegatta());
|
||||
assertNotSame(kiwotest2.getRegatta(), kiwotest3.getRegatta());
|
||||
}
|
||||
|
||||
private RaceTrackingConnectivityParametersImpl createConnectivityParams(String httpAndHost, String liveURI,
|
||||
String storedURI, String courseDesignUpdateURI, String tracTracUsername, String tracTracPassword, String raceId)
|
||||
throws Exception, MalformedURLException, URISyntaxException {
|
||||
return new RaceTrackingConnectivityParametersImpl(new URL(
|
||||
httpAndHost
|
||||
+ "/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race="+raceId),
|
||||
new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI),
|
||||
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l,
|
||||
/* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
|
||||
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, domainFactory,
|
||||
tracTracUsername, tracTracPassword, TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
|
||||
/* trackWind */ false, /* correctWindDirectionByMagneticDeclination */ true, /* preferReplayIfAvailable */ false);
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws MalformedURLException, IOException, InterruptedException {
|
||||
|
||||
+2
-1
@@ -34,6 +34,7 @@ import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
|
||||
import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.RaceTracker;
|
||||
import com.sap.sailing.domain.tracking.TrackingDataLoader;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
|
||||
@@ -123,7 +124,7 @@ public abstract class OnlineTracTracBasedTest extends AbstractTracTracLiveTest i
|
||||
for (Receiver r : domainFactory.getUpdateReceivers(trackedRegatta, getTracTracRace(), EmptyWindStore.INSTANCE, /* delayToLiveInMillis */0l, /* simulator */null, createRaceDefinitionSet(),
|
||||
/* trackedRegattaRegistry */null,
|
||||
mock(RaceLogResolver.class), /* courseDesignUpdateURI */null, /* tracTracUsername */null, /* tracTracPassword */
|
||||
null, getEventSubscriber(), getRaceSubscriber(), /*ignoreTracTracMarkPassings*/ false, receiverTypes)) {
|
||||
null, getEventSubscriber(), getRaceSubscriber(), /*ignoreTracTracMarkPassings*/ false, RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, receiverTypes)) {
|
||||
receivers.add(r);
|
||||
}
|
||||
getRaceSubscriber().subscribeConnectionStatus(new IConnectionStatusListener() {
|
||||
|
||||
+4
-3
@@ -13,7 +13,6 @@ import java.text.ParseException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -28,6 +27,7 @@ import com.sap.sailing.domain.common.impl.WindImpl;
|
||||
import com.sap.sailing.domain.common.impl.WindSourceImpl;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tractracadapter.ReceiverType;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.tractrac.model.lib.api.event.CreateModelException;
|
||||
import com.tractrac.subscription.lib.api.SubscriberInitializationException;
|
||||
@@ -83,9 +83,10 @@ public class PhilippBuhlsDoublePenaltyCircleAtKielerWoche2014Test extends Abstra
|
||||
assertNotNull(fromDate);
|
||||
assertNotNull(toDate);
|
||||
assertNotNull(competitor);
|
||||
List<Maneuver> maneuvers = getTrackedRace().getManeuvers(competitor, new MillisecondsTimePoint(fromDate),
|
||||
Iterable<Maneuver> maneuvers = getTrackedRace().getManeuvers(competitor, new MillisecondsTimePoint(fromDate),
|
||||
new MillisecondsTimePoint(toDate), /* waitForLatest */ true);
|
||||
maneuversInvalid = new ArrayList<Maneuver>(maneuvers);
|
||||
maneuversInvalid = new ArrayList<Maneuver>();
|
||||
Util.addAll(maneuvers, maneuversInvalid);
|
||||
for (Maneuver maneuver : maneuvers) {
|
||||
if (maneuver.getType() == ManeuverType.PENALTY_CIRCLE) {
|
||||
assertTrue(Math.abs(maneuver.getDirectionChangeInDegrees()) < 700); // the second penalty has to count for its own
|
||||
|
||||
+3
-2
@@ -20,6 +20,7 @@ import com.sap.sailing.domain.racelog.impl.EmptyRaceLogStore;
|
||||
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
|
||||
import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.RaceTracker;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sailing.domain.tractracadapter.DomainFactory;
|
||||
@@ -109,8 +110,8 @@ public class ReceiveMarkPassingDataTest extends AbstractTracTracLiveTest {
|
||||
}
|
||||
}, /* trackedRegattaRegistry */null,
|
||||
mock(RaceLogResolver.class), /* courseDesignUpdateURI */null, /* tracTracUsername */null, /* tracTracPassword */
|
||||
null, getEventSubscriber(), getRaceSubscriber(), /*ignoreTracTracMarkPassings*/ false, ReceiverType.RACECOURSE, ReceiverType.MARKPOSITIONS,
|
||||
ReceiverType.RACESTARTFINISH, ReceiverType.RAWPOSITIONS)) {
|
||||
null, getEventSubscriber(), getRaceSubscriber(), /*ignoreTracTracMarkPassings*/ false, RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, ReceiverType.RACECOURSE,
|
||||
ReceiverType.MARKPOSITIONS, ReceiverType.RACESTARTFINISH, ReceiverType.RAWPOSITIONS)) {
|
||||
receivers.add(r);
|
||||
addReceiverToStopDuringTearDown(r);
|
||||
}
|
||||
|
||||
Loaded 100 of 275 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user