Merge branch 'master' into bug4350

This commit is contained in:
Kai Boernert committed 2017-12-12 13:14:27 +01:00
commit 6b279c7910
1539 files changed
+9530 -5650

No files matched your search

@@ -1,4 +1,4 @@
eclipse.preferences.version=1
entryPointModules=
filesCopiedToWebInfLib=
gwtCompileSettings=PGd3dC1jb21waWxlLXNldHRpbmdzPjxsb2ctbGV2ZWw+SU5GTzwvbG9nLWxldmVsPjxvdXRwdXQtc3R5bGU+T0JGVVNDQVRFRDwvb3V0cHV0LXN0eWxlPjxleHRyYS1hcmdzPjwhW0NEQVRBWy13YXIgLiAtbG9jYWxXb3JrZXJzIDMgLXN0cmljdCAtbG9nTGV2ZWwgVFJBQ0VdXT48L2V4dHJhLWFyZ3M+PHZtLWFyZ3M+PCFbQ0RBVEFbLVhteDIwMjRtXV0+PC92bS1hcmdzPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmRhc2hib2FyZHMuZ3d0LlJpYkRhc2hib2FyZDwvZW50cnktcG9pbnQtbW9kdWxlPjwvZ3d0LWNvbXBpbGUtc2V0dGluZ3M+
gwtCompileSettings=PGd3dC1jb21waWxlLXNldHRpbmdzPjxsb2ctbGV2ZWw+SU5GTzwvbG9nLWxldmVsPjxvdXRwdXQtc3R5bGU+T0JGVVNDQVRFRDwvb3V0cHV0LXN0eWxlPjxleHRyYS1hcmdzPjwhW0NEQVRBWy13YXIgLiAtbG9jYWxXb3JrZXJzIDMgLXN0cmljdCAtbG9nTGV2ZWwgVFJBQ0UgLVhmcmFnbWVudENvdW50IDRdXT48L2V4dHJhLWFyZ3M+PHZtLWFyZ3M+PCFbQ0RBVEFbLVhteDIwMjRtXV0+PC92bS1hcmdzPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmRhc2hib2FyZHMuZ3d0LlJpYkRhc2hib2FyZDwvZW50cnktcG9pbnQtbW9kdWxlPjwvZ3d0LWNvbXBpbGUtc2V0dGluZ3M+
@@ -28,6 +28,15 @@
<url-pattern>/service/sailing</url-pattern>
</servlet-mapping>
<filter>
<filter-name>LocaleInjectionFilter</filter-name>
<filter-class>com.sap.sse.security.ui.server.LocaleInjectionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>LocaleInjectionFilter</filter-name>
<url-pattern>*.html</url-pattern>
</filter-mapping>
<servlet>
<display-name>Dashboard Dispatch Service</display-name>
<servlet-name>dashboardDispatch</servlet-name>
@@ -23,12 +23,6 @@
<version>1.0.0.GA</version>
<classifier>sources</classifier>
</dependency>
<dependency>
<groupId>com.github.gwtbootstrap</groupId>
<artifactId>gwt-bootstrap</artifactId>
<version>2.3.2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<!-- This dependency is required because the GWT Maven Plugin searches
other GWT modules only in Maven dependencies -->
@@ -56,7 +56,7 @@ public class BravoFixTrackWithContext implements HasBravoFixTrackContext {
end = endOfRace;
}
return bravoFixTrack.getDistanceSpentFoiling(
getRaceOfCompetitorContext().getTrackedRaceContext().getTrackedRace().getTrack(getRaceOfCompetitorContext().getCompetitor()),
startOfRace, end);
startOfRace,
end);
}
}
@@ -96,7 +96,7 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti
public Distance getDistanceSpentFoiling() {
return getSomethingForLegTrackingInterval((start, end) -> {
BravoFixTrack<Competitor> bravoFixTrack = getTrackedLegContext().getTrackedLeg().getTrackedRace().getSensorTrack(getCompetitor(), BravoFixTrack.TRACK_NAME);
return bravoFixTrack == null ? Distance.NULL : bravoFixTrack.getDistanceSpentFoiling(getTrackedLegContext().getTrackedLeg().getTrackedRace().getTrack(getCompetitor()), start, end);
return bravoFixTrack == null ? Distance.NULL : bravoFixTrack.getDistanceSpentFoiling(start, end);
});
}
@@ -8,6 +8,8 @@ import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.xml.parsers.DocumentBuilder;
@@ -30,6 +32,7 @@ import com.sap.sse.common.TimePoint;
*
*/
public class NOAAImporter {
private static final Logger logger = Logger.getLogger(NOAAImporter.class.getName());
private static final String QUERY_URL = "https://www.ngdc.noaa.gov/geomag-web/calculators/calculateDeclination";
private static final String REGEXP_DECLINATION = "<p class=\"indent\"><b>Declination</b> = ([0-9]*)&deg; ([0-9]*)' *([EW])";
private static final String REGEXP_ANNUAL_CHANGE = "changing by *([0-9]*)&deg; *([0-9]*)' ([EW])/year *</p>";
@@ -102,7 +105,7 @@ public class NOAAImporter {
result.notifyAll();
}
} catch (IOException | ParserConfigurationException | SAXException e) {
throw new RuntimeException(e);
logger.log(Level.FINE, "Exception while trying to load magnetic declination online", e);
}
}
};
+7
View File
@@ -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
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.sap.sailing.domain.bravoadapter</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>
@@ -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
@@ -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: Bravoadapter
Bundle-SymbolicName: com.sap.sailing.domain.bravoadapter
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: org.osgi.framework;version="1.8.0"
Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.server.trackfiles,
com.sap.sailing.domain.common,
com.sap.sse.common,
com.sap.sailing.server.gateway
Bundle-Activator: com.sap.sailing.domain.bravoadapter.impl.Activator
@@ -0,0 +1,4 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.
+12
View File
@@ -0,0 +1,12 @@
<?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.domain.bravoadapter</artifactId>
<packaging>eclipse-plugin</packaging>
</project>
@@ -0,0 +1,17 @@
package com.sap.sailing.domain.bravoadapter.impl;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import com.sap.sailing.server.trackfiles.common.GPSFixImporterRegistration;
public class Activator implements BundleActivator {
@Override
public void start(BundleContext context) throws Exception {
GPSFixImporterRegistration.register(new BravoGPSFixImporter(), context);
}
@Override
public void stop(BundleContext context) throws Exception {
}
}
@@ -0,0 +1,50 @@
package com.sap.sailing.domain.bravoadapter.impl;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Date;
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.sensordata.BravoExtendedSensorDataMetadata;
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifier;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifierImpl;
import com.sap.sailing.domain.trackimport.FormatNotSupportedException;
import com.sap.sailing.domain.trackimport.GPSFixImporter;
import com.sap.sailing.server.gateway.windimport.bravo.FunnyDegreeConverter;
import com.sap.sailing.server.trackfiles.impl.BravoExtendedDataImporterImpl;
public class BravoGPSFixImporter implements GPSFixImporter {
@Override
public void importFixes(InputStream inputStream, Callback callback, boolean inferSpeedAndBearing, final String filename)
throws FormatNotSupportedException, IOException {
TrackFileImportDeviceIdentifier device = new TrackFileImportDeviceIdentifierImpl(filename, getType() + "@" + new Date());
new BravoExtendedDataImporterImpl().importFixes(inputStream,
(Iterable<DoubleVectorFix> fixes, TrackFileImportDeviceIdentifier deviceIdentifier)->{
for (final DoubleVectorFix fix : fixes) {
GPSFixMoving gpsFix = new GPSFixMovingImpl(
new DegreePosition(FunnyDegreeConverter.funnyLatLng(fix.get(BravoExtendedSensorDataMetadata.LAT.getColumnIndex())),
FunnyDegreeConverter.funnyLatLng(fix.get(BravoExtendedSensorDataMetadata.LON.getColumnIndex()))),
fix.getTimePoint(),
new KnotSpeedWithBearingImpl(fix.get(BravoExtendedSensorDataMetadata.SOG.getColumnIndex()),
new DegreeBearingImpl(fix.get(BravoExtendedSensorDataMetadata.COG.getColumnIndex()))));
callback.addFix(gpsFix, device);
}
}, filename, /* sourceName */ getType(), /* downsample */ false);
}
@Override
public Iterable<String> getSupportedFileExtensions() {
return Arrays.asList(new String[] { "csv", "log", "txt" });
}
@Override
public String getType() {
return "Bravo";
}
}
@@ -112,6 +112,7 @@ public enum BoatClassMasterdata {
TORNADO ("Tornado Catamaran", true, 6.10, 3.02, BoatHullType.CATAMARAN, true, "Tornado", "Tornado Cat"),
TOM_28_MAX ("Tom 28 MAX", true, 8.48, 2.48, BoatHullType.MONOHULL, true, "Tom 28"),
TRIAS ("Trias", true, 9.20, 2.12, BoatHullType.MONOHULL, true),
TP52 ("TP52", true, 15.85, 4.35, BoatHullType.MONOHULL, true, "TP 52", "Transpac 52", "Transpac52"),
VIPER_640 ("Viper 640", true, 6.43, 2.49, BoatHullType.MONOHULL, true),
VO60 ("VO60", true, 19.5, 5.25, BoatHullType.MONOHULL, true, "Volvo Ocean 60", "VolvoOcean60", "W60", "Whitbread60", "Whitbread 60"),
VX_ONE ("VX ONE", true, 5.79, 2.19, BoatHullType.MONOHULL, true, "VX-ONE"),
@@ -25,11 +25,26 @@ public enum DetailType implements Serializable {
CURRENT_HEEL_IN_DEGREES(2,DESCENDING),
CURRENT_PITCH_IN_DEGREES(2,DESCENDING),
CURRENT_RIDE_HEIGHT_IN_METERS(2,DESCENDING),
CURRENT_DISTANCE_FOILED_IN_METERS(0, DESCENDING),
CURRENT_DURATION_FOILED_IN_SECONDS(0, DESCENDING),
CURRENT_PORT_DAGGERBOARD_RAKE(2,DESCENDING),
CURRENT_STBD_DAGGERBOARD_RAKE(2,DESCENDING),
CURRENT_PORT_RUDDER_RAKE(2,DESCENDING),
CURRENT_STBD_RUDDER_RAKE(2,DESCENDING),
CURRENT_MAST_ROTATION_IN_DEGREES(2,DESCENDING),
CURRENT_LEEWAY_IN_DEGREES(1, ASCENDING),
CURRENT_SET(1, ASCENDING),
CURRENT_DRIFT_IN_DEGREES(1, ASCENDING),
CURRENT_DEPTH_IN_METERS(1, ASCENDING),
CURRENT_RUDDER_IN_DEGREES(2, ASCENDING),
CURRENT_TACK_ANGLE_IN_DEGREES(2, ASCENDING),
CURRENT_DEFLECTOR_PERCENTAGE(2, ASCENDING),
CURRENT_DEFLECTOR_IN_MILLIMETERS(2, ASCENDING),
CURRENT_RAKE_IN_DEGREES(2, ASCENDING),
CURRENT_TARGET_HEEL_ANGLE_IN_DEGREES(2, ASCENDING),
CURRENT_FORESTAY_LOAD(2, ASCENDING),
CURRENT_FORESTAY_PRESSURE(2, ASCENDING),
CURRENT_TARGET_BOATSPEED_PERCENTAGE(2, ASCENDING),
ESTIMATED_TIME_TO_NEXT_WAYPOINT_IN_SECONDS(1, ASCENDING), VELOCITY_MADE_GOOD_IN_KNOTS(2, DESCENDING),
GAP_TO_LEADER_IN_SECONDS(0, ASCENDING), GAP_CHANGE_SINCE_LEG_START_IN_SECONDS(0, ASCENDING),
SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED(0, ASCENDING), WINDWARD_DISTANCE_TO_GO_IN_METERS(0, ASCENDING),
@@ -51,8 +66,10 @@ public enum DetailType implements Serializable {
MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS(1, DESCENDING),
TIME_ON_TIME_FACTOR(4, DESCENDING), TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE(0, ASCENDING),
TOTAL_DISTANCE_TRAVELED(0, ASCENDING), TOTAL_AVERAGE_SPEED_OVER_GROUND(2, DESCENDING),
TOTAL_TIME_SAILED_IN_SECONDS(1, ASCENDING), RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS(2,
DESCENDING), RACE_CURRENT_RIDE_HEIGHT_IN_METERS(2, DESCENDING),
TOTAL_TIME_SAILED_IN_SECONDS(1, ASCENDING),
TOTAL_DURATION_FOILED_IN_SECONDS(0, DESCENDING), TOTAL_DISTANCE_FOILED_IN_METERS(0, DESCENDING),
RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING), RACE_CURRENT_RIDE_HEIGHT_IN_METERS(2, DESCENDING),
RACE_CURRENT_DISTANCE_FOILED_IN_METERS(0, DESCENDING), RACE_CURRENT_DURATION_FOILED_IN_SECONDS(0, DESCENDING),
RACE_NET_POINTS(2, ASCENDING), REGATTA_NET_POINTS(2, ASCENDING), REGATTA_NET_POINTS_SUM(2, ASCENDING),
RACE_RATIO_BETWEEN_TIME_SINCE_LAST_POSITION_FIX_AND_AVERAGE_SAMPLING_INTERVAL(1, ASCENDING), RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START(1, ASCENDING),
RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START(2, DESCENDING),
@@ -1,9 +1,7 @@
package com.sap.sailing.domain.racelogtracking;
package com.sap.sailing.domain.common;
import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
/**
* Identifies any kind of tracking device. Should be implemented accordingly for different tracking
* adapters, e.g. for smartphones, Igtimi trackers etc.<p>
@@ -37,6 +35,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog;
* {@link Object#equals(Object)} and {@link Object#hashCode()} should be implemented so that multiple
* deserializations of same device identifier (conceptually) result in objects that are be identified
* as being equal through these two methods.
*
* @author Fredrik Teschke
*
*/
@@ -138,7 +138,7 @@ public class LeaderboardEntryDTO implements Serializable {
return totalPointsCorrected || (reasonForMaxPoints != null && reasonForMaxPoints != MaxPointsReason.NONE);
}
public Duration getTimeSailedInMillis() {
public Duration getTimeSailed() {
final Duration result;
if (legDetails != null) {
long timeInMilliseconds = 0;
@@ -159,7 +159,7 @@ public class LeaderboardEntryDTO implements Serializable {
return result;
}
public Distance getDistanceTraveledInMeters() {
public Distance getDistanceTraveled() {
Distance result = null;
if (legDetails != null) {
for (LegEntryDTO legDetail : legDetails) {
@@ -176,6 +176,47 @@ public class LeaderboardEntryDTO implements Serializable {
return result;
}
public Duration getDurationFoiled() {
final Duration result;
if (legDetails != null) {
Double acc = null;
for (LegEntryDTO legDetail : legDetails) {
if (legDetail != null) {
if (legDetail.currentDurationFoiledInSeconds != null) {
if (acc == null) {
acc = 0.0;
}
acc += legDetail.currentDurationFoiledInSeconds;
} else {
acc = null;
break;
}
}
}
result = acc == null ? null : new MillisecondsDurationImpl((long) (acc*1000.));
} else {
result = null;
}
return result;
}
public Distance getDistanceFoiled() {
Distance result = null;
if (legDetails != null) {
for (LegEntryDTO legDetail : legDetails) {
if (legDetail != null) {
if (legDetail.currentDistanceFoiledInMeters != null) {
if (result == null) {
result = Distance.NULL;
}
result = result.add(new MeterDistance(legDetail.currentDistanceFoiledInMeters));
}
}
}
}
return result;
}
public int getOneBasedCurrentLegNumber() {
int result = 0;
if (legDetails != null && !legDetails.isEmpty()) {
@@ -26,6 +26,8 @@ public class LeaderboardRowDTO implements Serializable {
public Double totalTimeSailedUpwindInSeconds;
public Double totalTimeSailedReachingInSeconds;
public Double totalDistanceTraveledInMeters;
public Double totalDurationFoiledInSeconds;
public Double totalDistanceFoiledInMeters;
@Override
public int hashCode() {
@@ -38,6 +40,10 @@ public class LeaderboardRowDTO implements Serializable {
+ ((maximumSpeedOverGroundInKnots == null) ? 0 : maximumSpeedOverGroundInKnots.hashCode());
result = prime * result
+ ((totalDistanceTraveledInMeters == null) ? 0 : totalDistanceTraveledInMeters.hashCode());
result = prime * result
+ ((totalDurationFoiledInSeconds == null) ? 0 : totalDurationFoiledInSeconds.hashCode());
result = prime * result
+ ((totalDistanceFoiledInMeters == null) ? 0 : totalDistanceFoiledInMeters.hashCode());
result = prime * result + ((netPoints == null) ? 0 : netPoints.hashCode());
result = prime * result
+ ((totalTimeSailedDownwindInSeconds == null) ? 0 : totalTimeSailedDownwindInSeconds.hashCode());
@@ -87,6 +93,16 @@ public class LeaderboardRowDTO implements Serializable {
return false;
} else if (!totalDistanceTraveledInMeters.equals(other.totalDistanceTraveledInMeters))
return false;
if (totalDurationFoiledInSeconds == null) {
if (other.totalDurationFoiledInSeconds != null)
return false;
} else if (!totalDurationFoiledInSeconds.equals(other.totalDurationFoiledInSeconds))
return false;
if (totalDistanceFoiledInMeters == null) {
if (other.totalDistanceFoiledInMeters != null)
return false;
} else if (!totalDistanceFoiledInMeters.equals(other.totalDistanceFoiledInMeters))
return false;
if (netPoints == null) {
if (other.netPoints != null)
return false;
@@ -120,11 +136,11 @@ public class LeaderboardRowDTO implements Serializable {
return true;
}
public Distance getDistanceTraveledInMeters(String raceColumnName) {
public Distance getDistanceTraveled(String raceColumnName) {
final Distance result;
LeaderboardEntryDTO fieldsForRace = fieldsByRaceColumnName.get(raceColumnName);
if (fieldsForRace != null) {
result = fieldsForRace.getDistanceTraveledInMeters();
result = fieldsForRace.getDistanceTraveled();
} else {
result = null;
}
@@ -135,7 +151,29 @@ public class LeaderboardRowDTO implements Serializable {
final Duration result;
LeaderboardEntryDTO fieldsForRace = fieldsByRaceColumnName.get(raceColumnName);
if (fieldsForRace != null) {
result = fieldsForRace.getTimeSailedInMillis();
result = fieldsForRace.getTimeSailed();
} else {
result = null;
}
return result;
}
public Distance getDistanceFoiled(String raceColumnName) {
final Distance result;
LeaderboardEntryDTO fieldsForRace = fieldsByRaceColumnName.get(raceColumnName);
if (fieldsForRace != null) {
result = fieldsForRace.getDistanceFoiled();
} else {
result = null;
}
return result;
}
public Duration getDurationFoiled(String raceColumnName) {
final Duration result;
LeaderboardEntryDTO fieldsForRace = fieldsByRaceColumnName.get(raceColumnName);
if (fieldsForRace != null) {
result = fieldsForRace.getDurationFoiled();
} else {
result = null;
}
@@ -44,6 +44,8 @@ public class LegEntryDTO implements Serializable {
public Double currentHeelInDegrees;
public Double currentPitchInDegrees;
public Double currentDistanceFoiledInMeters;
public Double currentDurationFoiledInSeconds;
/**
* If the competitor has already finished the leg at the time point for which this entry was created, contains the
@@ -81,6 +83,8 @@ public class LegEntryDTO implements Serializable {
result = prime * result + ((currentHeelInDegrees == null) ? 0 : currentHeelInDegrees.hashCode());
result = prime * result + ((currentPitchInDegrees == null) ? 0 : currentPitchInDegrees.hashCode());
result = prime * result + ((currentRideHeightInMeters == null) ? 0 : currentRideHeightInMeters.hashCode());
result = prime * result + ((currentDurationFoiledInSeconds == null) ? 0 : currentDurationFoiledInSeconds.hashCode());
result = prime * result + ((currentDistanceFoiledInMeters == null) ? 0 : currentDistanceFoiledInMeters.hashCode());
result = prime * result
+ ((currentSpeedOverGroundInKnots == null) ? 0 : currentSpeedOverGroundInKnots.hashCode());
result = prime * result + ((distanceTraveledInMeters == null) ? 0 : distanceTraveledInMeters.hashCode());
@@ -150,6 +154,16 @@ public class LegEntryDTO implements Serializable {
return false;
} else if (!currentRideHeightInMeters.equals(other.currentRideHeightInMeters))
return false;
if (currentDurationFoiledInSeconds == null) {
if (other.currentDurationFoiledInSeconds != null)
return false;
} else if (!currentDurationFoiledInSeconds.equals(other.currentDurationFoiledInSeconds))
return false;
if (currentDistanceFoiledInMeters == null) {
if (other.currentDistanceFoiledInMeters != null)
return false;
} else if (!currentDistanceFoiledInMeters.equals(other.currentDistanceFoiledInMeters))
return false;
if (currentSpeedOverGroundInKnots == null) {
if (other.currentSpeedOverGroundInKnots != null)
return false;
@@ -9,7 +9,7 @@ public class DegreeBearingImpl extends AbstractBearing implements Bearing {
private final double bearingDeg;
/**
*
* @param bearingDeg if a mount degrees over 360, then it will be mod 360 degrees
* @param bearingDeg if amount degrees over 360, then it will be mod 360 degrees
*/
public DegreeBearingImpl(double bearingDeg) {
super();
@@ -35,12 +35,17 @@ public enum BravoExtendedSensorDataMetadata implements ColumnMetadata {
DEPTH(null, /* expectedInLogfile */ false), //
RUDDER(null, /* expectedInLogfile */ false), //
FORESTAY_LOAD(null, /* expectedInLogfile */ false), //
FORESTAY_PRESSURE(null, /* expectedInLogfile */ false), //
TACK_ANGLE(null, /* expectedInLogfile */ false), //
RAKE_DEG(null, /* expectedInLogfile */ false), //
DEFLECTOR_PERCENTAGE(null, /* expectedInLogfile */ false), //
TARGET_HEEL(null, /* expectedInLogfile */ false), //
DEFLECTOR_MILLIMETERS(null, /* expectedInLogfile */ false), //
TARGET_BOATSPEED_P(null, /* expectedInLogfile */ false), //
LAT("Lat", /* expectedInLogfile */ true), //
LON("Lon", /* expectedInLogfile */ true), //
COG("COG", /* expectedInLogfile */ true), //
SOG("SOG", /* expectedInLogfile */ true), //
;
private final String columnName;
@@ -9,28 +9,31 @@ import java.util.Objects;
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
/**
* Metadata that defines the column structure of BravoExtended fixes when imported as {@link com.sap.sailing.domain.common.tracking.DoubleVectorFix}.
* Metadata that defines the column structure of BravoExtended fixes when imported as
* {@link com.sap.sailing.domain.common.tracking.DoubleVectorFix}.
* <p>
*
* The current implementation only stores a subset of the information available during the import.
* <p>
*
* The first columns need to be consistent with {@link BravoSensorDataMetadata}.
*/
public enum ExpeditionExtendedSensorDataMetadata {
HEEL("Heel", BravoExtendedSensorDataMetadata.HEEL), //
TRIM("TRIM", BravoExtendedSensorDataMetadata.PITCH), //
LEEWAY(null, BravoExtendedSensorDataMetadata.LEEWAY), //
SET(null, BravoExtendedSensorDataMetadata.SET), //
DRIFT(null, BravoExtendedSensorDataMetadata.DRIFT), //
DEPTH(null, BravoExtendedSensorDataMetadata.DEPTH), //
RUDDER(null, BravoExtendedSensorDataMetadata.RUDDER), //
FORESTAY_LOAD(null, BravoExtendedSensorDataMetadata.FORESTAY_LOAD), //
TRIM("Trim", BravoExtendedSensorDataMetadata.PITCH), //
LEEWAY("Leeway", BravoExtendedSensorDataMetadata.LEEWAY, /* expected in logfile */ true), //
SET("Set", BravoExtendedSensorDataMetadata.SET, /* expected in logfile */ true), //
DRIFT("Drift", BravoExtendedSensorDataMetadata.DRIFT, /* expected in logfile */ true), //
DEPTH("Depth", BravoExtendedSensorDataMetadata.DEPTH, /* expected in logfile */ true), //
RUDDER("Rudder", BravoExtendedSensorDataMetadata.RUDDER, /* expected in logfile */ true), //
FORESTAY_LOAD("Forestay", BravoExtendedSensorDataMetadata.FORESTAY_LOAD, /* expected in logfile */ true), //
FORESTAY_PRESSURE("ForestayPres", BravoExtendedSensorDataMetadata.FORESTAY_PRESSURE, /* expected in logfile */ true), //
TACK_ANGLE(null, BravoExtendedSensorDataMetadata.TACK_ANGLE), //
RAKE_DEG(null, BravoExtendedSensorDataMetadata.RAKE_DEG), //
DEFLECTOR_PERCENTAGE(null, BravoExtendedSensorDataMetadata.DEFLECTOR_PERCENTAGE), //
TARGET_HEEL(null, BravoExtendedSensorDataMetadata.TARGET_HEEL), //
DEFLECTOR_MILLIMETERS(null, BravoExtendedSensorDataMetadata.DEFLECTOR_MILLIMETERS), //
TARGET_BOATSPEED_P(null, BravoExtendedSensorDataMetadata.TARGET_BOATSPEED_P), //
RAKE_DEG("RakeDeg", BravoExtendedSensorDataMetadata.RAKE_DEG, /* expected in logfile */ true), //
DEFLECTOR_PERCENTAGE("DflectrPP", BravoExtendedSensorDataMetadata.DEFLECTOR_PERCENTAGE, /* expected in logfile */ true), //
TARGET_HEEL("TG Heell", BravoExtendedSensorDataMetadata.TARGET_HEEL, /* expected in logfile */ true), //
DEFLECTOR_MILLIMETERS("DflectrMM", BravoExtendedSensorDataMetadata.DEFLECTOR_MILLIMETERS, /* expected in logfile */ true), //
TARGET_BOATSPEED_P("BspTr", BravoExtendedSensorDataMetadata.TARGET_BOATSPEED_P, /* expected in logfile */ true), //
;
private String columnName;
@@ -44,10 +47,17 @@ public enum ExpeditionExtendedSensorDataMetadata {
* the Bravo metadata.
*/
private final BravoExtendedSensorDataMetadata mappedToBravoField;
private final boolean expectedInLogfile;
private ExpeditionExtendedSensorDataMetadata(String columnName, BravoExtendedSensorDataMetadata mappedToBravoField) {
this(columnName, mappedToBravoField, mappedToBravoField.isExpectedInLogFile());
}
private ExpeditionExtendedSensorDataMetadata(String columnName, BravoExtendedSensorDataMetadata mappedToBravoField, boolean expectedInLogfile) {
this.columnName = columnName;
this.mappedToBravoField = mappedToBravoField;
this.expectedInLogfile = expectedInLogfile;
}
public String getColumnName() {
@@ -62,7 +72,7 @@ public enum ExpeditionExtendedSensorDataMetadata {
}
public boolean isExpectedInLogFile() {
return mappedToBravoField.isExpectedInLogFile();
return expectedInLogfile;
}
public static ExpeditionExtendedSensorDataMetadata byColumnName(String valueName) {
@@ -12,12 +12,13 @@ public interface BravoExtendedFix extends BravoFix {
Double getPortRudderRake();
Double getStbdRudderRake();
Bearing getMastRotation();
Double getLeeway();
Bearing getLeeway();
Double getSet();
Double getDrift();
Bearing getDrift();
Distance getDepth();
Bearing getRudder();
Double getForestayLoad();
Double getForestayPressure();
Bearing getTackAngle();
Bearing getRake();
Double getDeflectorPercentage();
@@ -52,8 +52,9 @@ public class BravoExtendedFixImpl extends BravoFixImpl implements BravoExtendedF
}
@Override
public Double getLeeway() {
return fix.get(BravoExtendedSensorDataMetadata.LEEWAY.getColumnIndex());
public Bearing getLeeway() {
final Double leeway = fix.get(BravoExtendedSensorDataMetadata.LEEWAY.getColumnIndex());
return leeway == null ? null : new DegreeBearingImpl(leeway);
}
@Override
@@ -62,8 +63,9 @@ public class BravoExtendedFixImpl extends BravoFixImpl implements BravoExtendedF
}
@Override
public Double getDrift() {
return fix.get(BravoExtendedSensorDataMetadata.DRIFT.getColumnIndex());
public Bearing getDrift() {
final Double drift = fix.get(BravoExtendedSensorDataMetadata.DRIFT.getColumnIndex());
return drift == null ? null : new DegreeBearingImpl(drift);
}
@Override
@@ -83,6 +85,11 @@ public class BravoExtendedFixImpl extends BravoFixImpl implements BravoExtendedF
return fix.get(BravoExtendedSensorDataMetadata.FORESTAY_LOAD.getColumnIndex());
}
@Override
public Double getForestayPressure() {
return fix.get(BravoExtendedSensorDataMetadata.FORESTAY_PRESSURE.getColumnIndex());
}
@Override
public Bearing getTackAngle() {
final Double tackAngleDeg = fix.get(BravoExtendedSensorDataMetadata.TACK_ANGLE.getColumnIndex());
@@ -27,10 +27,15 @@ public class BravoFixImpl extends SensorFixImpl implements BravoFix {
@Override
public Distance getRideHeight() {
double rideHeightPortHullasDouble = fix.get(BravoSensorDataMetadata.RIDE_HEIGHT_PORT_HULL.getColumnIndex());
double rideHeightStarboardHullasDouble = fix
Double rideHeightPortHullasDouble = fix.get(BravoSensorDataMetadata.RIDE_HEIGHT_PORT_HULL.getColumnIndex());
Double rideHeightStarboardHullAsDouble = fix
.get(BravoSensorDataMetadata.RIDE_HEIGHT_STBD_HULL.getColumnIndex());
return new MeterDistance(Math.min(rideHeightPortHullasDouble, rideHeightStarboardHullasDouble));
return rideHeightPortHullasDouble == null ?
rideHeightStarboardHullAsDouble == null ? null :
new MeterDistance(rideHeightStarboardHullAsDouble) :
rideHeightStarboardHullAsDouble == null ?
new MeterDistance(rideHeightPortHullasDouble) :
new MeterDistance(Math.min(rideHeightPortHullasDouble, rideHeightStarboardHullAsDouble));
}
@Override
@@ -23,8 +23,8 @@ import com.sap.sailing.server.trackfiles.impl.ExpeditionExtendedDataImporterImpl
public class ExpeditionGPSFixImporter implements GPSFixImporter {
private static final String LAT_COLUMN_HEADING = ExpeditionExtendedDataImporterImpl.COL_NAME_LAT;
private static final String LON_COLUMN_HEADING = ExpeditionExtendedDataImporterImpl.COL_NAME_LON;
private static final String COG_COLUMN_HEADING = "Ext_COG";
private static final String SOG_COLUMN_HEADING = "Ext_SOG";
private static final String COG_COLUMN_HEADING = "Cog";
private static final String SOG_COLUMN_HEADING = "Sog";
@Override
public void importFixes(InputStream inputStream, Callback callback, boolean inferSpeedAndBearing, final String sourceName)
throws FormatNotSupportedException, IOException {
@@ -0,0 +1,46 @@
package com.sap.sailing.domain.igtimiadapter.test;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.List;
import org.json.simple.JSONObject;
import org.json.simple.parser.JSONParser;
import org.json.simple.parser.ParseException;
import org.junit.Before;
import org.junit.Test;
import com.sap.sailing.domain.igtimiadapter.datatypes.Fix;
import com.sap.sailing.domain.igtimiadapter.impl.FixFactory;
import com.sap.sse.common.Util;
public class FixFactoryTest {
private FixFactory fixFactory;
@Before
public void setUp() {
fixFactory = new FixFactory();
}
@Test
public void testUnknownFixTypes() throws ParseException {
final String[] messages = new String[] {
"{\"DC-FE-AAKG\":{\"1\":{\"t\":[1510174025500],\"1\":[170.5044476],\"2\":[-45.87697]}}}",
"{\"DC-FE-AAKG\":{\"2\":{\"t\":[1510174025500],\"1\":[1]}}}",
"{\"DC-FE-AAKG\":{\"3\":{\"t\":[1510174025500],\"1\":[10]}}}",
"{\"DC-FE-AAKG\":{\"7\":{\"t\":[1510174025500],\"1\":[187.7]}}}",
"{\"DC-FE-AAKG\":{\"11\":{\"t\":[1510174025500],\"1\":[54]}}}",
"{\"DC-FE-AAKG\":{\"12\":{\"t\":[1510174025500],\"1\":[1.852]}}}",
"{\"DC-FE-AAKG\":{\"22\":{\"t\":[1510174025500],\"1\":[263.4]}}}",
"{\"DC-FE-AAKG\":{\"23\":{\"t\":[1510174025500],\"1\":[1.852]}}}",
"{\"DC-FE-AAKG\":{\"6\":{\"t\":[1510174025500],\"1\":[0]}}}",
"{\"DC-FE-AAKG\":{\"9\":{\"t\":[1510174025500],\"1\":[0]}}}" };
List<Fix> fixes = new ArrayList<>();
for (final String message : messages) {
JSONObject jsonObject = (JSONObject) new JSONParser().parse(message);
Util.addAll(fixFactory.createFixes(jsonObject), fixes);
}
assertTrue(!Util.isEmpty(fixes)); // this largely asserts that no exception was thrown although there were unknown fix types (22/23)
}
}
@@ -54,9 +54,12 @@ public class FixFactory {
Sensor sensor = new SensorImpl(deviceSerialNumber,
fixTypeAndOptionalColonSeparatedSensorsSubId.length < 2 ? 0
: Long.valueOf(fixTypeAndOptionalColonSeparatedSensorsSubId[1]));
Fix fix = createFix(sensor, Type.valueOf(fixType), timePoint, valuesPerSubindex);
result.add(fix);
fixIndex++;
final Type ft = Type.valueOf(fixType);
if (ft != null) {
Fix fix = createFix(sensor, ft, timePoint, valuesPerSubindex);
result.add(fix);
fixIndex++;
}
}
}
return result;
@@ -39,6 +39,7 @@ import com.mongodb.WriteResult;
import com.mongodb.util.JSON;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
import com.sap.sailing.domain.abstractlog.race.CompetitorResult.MergeState;
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogCourseAreaChangedEvent;
@@ -155,6 +156,7 @@ import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.base.impl.VenueImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl;
import com.sap.sailing.domain.common.CourseDesignerMode;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.MarkType;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.PassingInstruction;
@@ -168,8 +170,8 @@ import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.dto.EventType;
import com.sap.sailing.domain.common.dto.AnniversaryType;
import com.sap.sailing.domain.common.dto.EventType;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
@@ -206,7 +208,6 @@ import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongo
import com.sap.sailing.domain.persistence.racelog.tracking.impl.PlaceHolderDeviceIdentifierMongoHandler;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.racelog.RaceLogStore;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.impl.PlaceHolderDeviceIdentifierSerializationHandler;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.ranking.RankingMetricConstructor;
@@ -1601,8 +1602,15 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
final Long finishingTimePointAsMillis = (Long) dbObject.get(FieldNames.RACE_LOG_FINISHING_TIME_AS_MILLIS.name());
final TimePoint finishingTime = finishingTimePointAsMillis == null ? null : new MillisecondsTimePoint(finishingTimePointAsMillis);
final String comment = (String) dbObject.get(FieldNames.LEADERBOARD_SCORE_CORRECTION_COMMENT.name());
final String mergeStateAsString = (String) dbObject.get(FieldNames.LEADERBOARD_SCORE_CORRECTION_MERGE_STATE.name());
final MergeState mergeState;
if (mergeStateAsString == null) {
mergeState = MergeState.OK;
} else {
mergeState = MergeState.valueOf(mergeStateAsString);
}
CompetitorResultImpl positionedCompetitor = new CompetitorResultImpl(
competitorId, competitorDisplayName, rank == null ? rankCounter : rank, maxPointsReason, score, finishingTime, comment);
competitorId, competitorDisplayName, rank == null ? rankCounter : rank, maxPointsReason, score, finishingTime, comment, mergeState);
positionedCompetitors.add(positionedCompetitor);
rankCounter++;
}
@@ -80,7 +80,8 @@ public enum FieldNames {
LEADERBOARD_IS_MEDAL_RACE_COLUMN, LEADERBOARD_CARRIED_POINTS, LEADERBOARD_CARRIED_POINTS_BY_ID,
LEADERBOARD_SCORE_CORRECTIONS, LEADERBOARD_DISCARDING_THRESHOLDS,
LEADERBOARD_SCORE_CORRECTION_MAX_POINTS_REASON, LEADERBOARD_CORRECTED_SCORE, LEADERBOARD_SCORE_CORRECTION_TIMESTAMP,
LEADERBOARD_RANK, LEADERBOARD_SCORE_CORRECTION_COMMENT, LEADERBOARD_COLUMN_FACTORS, WRAPPED_REGATTA_LEADERBOARD_NAME,
LEADERBOARD_RANK, LEADERBOARD_SCORE_CORRECTION_COMMENT, LEADERBOARD_SCORE_CORRECTION_MERGE_STATE,
LEADERBOARD_COLUMN_FACTORS, WRAPPED_REGATTA_LEADERBOARD_NAME,
ELMINATED_COMPETITORS,
// leaderboard group
@@ -86,6 +86,7 @@ import com.sap.sailing.domain.base.configuration.RegattaConfiguration;
import com.sap.sailing.domain.base.configuration.impl.DeviceConfigurationMatcherSingle;
import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.common.Positioned;
@@ -108,7 +109,6 @@ import com.sap.sailing.domain.persistence.MongoObjectFactory;
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
import com.sap.sailing.domain.persistence.racelog.tracking.impl.PlaceHolderDeviceIdentifierMongoHandler;
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParametersHandler;
@@ -1239,6 +1239,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
result.put(FieldNames.RACE_LOG_FINISHING_TIME_AS_MILLIS.name(), competitorResult.getFinishingTime() == null ? null : competitorResult.getFinishingTime().asMillis());
result.put(FieldNames.LEADERBOARD_RANK.name(), competitorResult.getOneBasedRank());
result.put(FieldNames.LEADERBOARD_SCORE_CORRECTION_COMMENT.name(), competitorResult.getComment());
result.put(FieldNames.LEADERBOARD_SCORE_CORRECTION_MERGE_STATE.name(), competitorResult.getMergeState().name());
return result;
}
@@ -20,6 +20,7 @@ import com.mongodb.DBCollection;
import com.mongodb.DBCursor;
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.persistence.DomainObjectFactory;
import com.sap.sailing.domain.persistence.MongoObjectFactory;
@@ -30,7 +31,6 @@ import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongo
import com.sap.sailing.domain.persistence.racelog.tracking.FixMongoHandler;
import com.sap.sailing.domain.persistence.racelog.tracking.MongoSensorFixStore;
import com.sap.sailing.domain.racelog.tracking.FixReceivedListener;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sse.common.Duration;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.TimePoint;
@@ -1,8 +1,8 @@
package com.sap.sailing.domain.persistence.racelog.tracking.impl;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.impl.PlaceHolderDeviceIdentifierSerializationHandler;
public class PlaceHolderDeviceIdentifierMongoHandler extends PlaceHolderDeviceIdentifierSerializationHandler
@@ -24,6 +24,7 @@ import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Sideline;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
@@ -38,7 +39,6 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelog.tracking.test.mock.MockDeviceAndSessionIdentifierWithGPSFixesDeserializer;
import com.sap.sailing.domain.racelog.tracking.test.mock.MockSmartphoneImeiServiceFinderFactory;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
@@ -36,6 +36,7 @@ import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.impl.FleetImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.SeriesImpl;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
@@ -47,7 +48,6 @@ import com.sap.sailing.domain.leaderboard.impl.HighPoint;
import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelog.tracking.test.mock.MockSmartphoneImeiServiceFinderFactory;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter;
import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory;
import com.sap.sailing.domain.racelogtracking.impl.fixtracker.RaceLogFixTrackerManager;
@@ -16,9 +16,9 @@ import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCo
import com.sap.sailing.domain.abstractlog.regatta.impl.RegattaLogImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.DomainFactory;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.tracking.impl.DoubleVectorFixImpl;
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.RegattaLogDeviceMappings;
import com.sap.sailing.domain.racelogtracking.test.AbstractGPSFixStoreTest;
import com.sap.sse.common.MultiTimeRange;
@@ -9,10 +9,10 @@ import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.Timeout;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.persistence.racelog.tracking.impl.MongoSensorFixStoreImpl;
import com.sap.sailing.domain.racelog.tracking.FixReceivedListener;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.test.AbstractGPSFixStoreTest;
public class GPSFixStoreListenerTest extends AbstractGPSFixStoreTest {
@@ -31,6 +31,7 @@ import com.sap.sailing.domain.base.impl.CourseImpl;
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
@@ -45,7 +46,6 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelog.tracking.test.mock.MockSmartphoneImeiServiceFinderFactory;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
import com.sap.sailing.domain.racelogsensortracking.SensorFixMapperFactory;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.impl.fixtracker.RaceLogFixTrackerManager;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
@@ -54,7 +54,10 @@ import com.sap.sailing.domain.base.impl.CourseImpl;
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
@@ -75,12 +78,12 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelog.tracking.test.mock.MockSmartphoneImeiServiceFinderFactory;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
import com.sap.sailing.domain.racelogsensortracking.SensorFixMapperFactory;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.impl.BravoDataFixMapper;
import com.sap.sailing.domain.racelogtracking.impl.fixtracker.FixLoaderAndTracker;
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
import com.sap.sailing.domain.regattalog.impl.EmptyRegattaLogStore;
import com.sap.sailing.domain.tracking.BravoFixTrack;
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;
@@ -92,6 +95,7 @@ import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sailing.domain.tracking.impl.SensorFixTrackImpl;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Timed;
import com.sap.sse.common.Util.Pair;
@@ -178,18 +182,12 @@ public class SensorFixStoreAndLoadTest {
public void testLoadAlreadyAddedFixes() throws InterruptedException {
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
addBravoFixes();
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
trackedRace.waitForLoadingToFinish();
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 2);
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -197,18 +195,12 @@ public class SensorFixStoreAndLoadTest {
public void testAddFixesWhileTracking() throws InterruptedException {
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
addBravoFixes();
trackedRace.waitForLoadingToFinish();
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 2);
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -216,18 +208,12 @@ public class SensorFixStoreAndLoadTest {
public void testNoFixesAreLoadedIfNoStoredFixIsInTimeRange() throws InterruptedException {
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(AFTER_LAST_FIX), new MillisecondsTimePoint(END_OF_TRACKING)));
addBravoFixes();
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
trackedRace.waitForLoadingToFinish();
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 0);
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -235,18 +221,12 @@ public class SensorFixStoreAndLoadTest {
public void testFixesNotInMappedTimeRangeAreIgnoredWhileTracking() throws InterruptedException {
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(AFTER_LAST_FIX), new MillisecondsTimePoint(END_OF_TRACKING)));
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
addBravoFixes();
trackedRace.waitForLoadingToFinish();
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 0);
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -254,18 +234,12 @@ public class SensorFixStoreAndLoadTest {
public void testCompetitorWithoutMappingHasNoTrack() throws InterruptedException {
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
addBravoFixes();
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
trackedRace.waitForLoadingToFinish();
assertNull(trackedRace.getSensorTrack(comp2, BravoFixTrack.TRACK_NAME));
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -275,19 +249,13 @@ public class SensorFixStoreAndLoadTest {
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp2,
device, new MillisecondsTimePoint(MID_OF_TRACKING + 1), new MillisecondsTimePoint(END_OF_TRACKING)));
addBravoFixes();
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
trackedRace.waitForLoadingToFinish();
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 2);
testNumberOfRawFixes(trackedRace.getSensorTrack(comp2, BravoFixTrack.TRACK_NAME), 1);
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -297,18 +265,12 @@ public class SensorFixStoreAndLoadTest {
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(MID_OF_TRACKING + 1), new MillisecondsTimePoint(END_OF_TRACKING)));
addBravoFixes();
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
trackedRace.waitForLoadingToFinish();
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 3);
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -319,18 +281,12 @@ public class SensorFixStoreAndLoadTest {
new MillisecondsTimePoint(MID_OF_TRACKING));
regattaLog.add(mappingEvent);
regattaLog.add(new RegattaLogRevokeEventImpl(author, mappingEvent, "Test purposes"));
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
addBravoFixes();
trackedRace.waitForLoadingToFinish();
assertNull(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME));
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -342,23 +298,16 @@ public class SensorFixStoreAndLoadTest {
new MillisecondsTimePoint(END_OF_TRACKING));
regattaLog.add(mappingEvent);
regattaLog.add(new RegattaLogRevokeEventImpl(author, mappingEvent, "Test purposes"));
// non-revoked mapping timestamp 100-200
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
addBravoFixes();
trackedRace.waitForLoadingToFinish();
// Only Fixes from 100 to 200 may be included
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 2);
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -371,25 +320,16 @@ public class SensorFixStoreAndLoadTest {
RegattaLogCloseOpenEndedDeviceMappingEventImpl closeEvent = new RegattaLogCloseOpenEndedDeviceMappingEventImpl(
new MillisecondsTimePoint(4), author, mappingEventId, new MillisecondsTimePoint(MID_OF_TRACKING));
regattaLog.add(closeEvent);
addBravoFixes();
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
trackedRace.waitForLoadingToFinish();
BravoFixTrack<Competitor> bravoFixTrack = trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME);
testNumberOfRawFixes(bravoFixTrack, 2);
regattaLog.add(new RegattaLogRevokeEventImpl(author, closeEvent, "Test purposes"));
trackedRace.waitForLoadingToFinish();
testNumberOfRawFixes(bravoFixTrack, 3);
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -414,20 +354,14 @@ public class SensorFixStoreAndLoadTest {
deviceTest, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
addTestFixes();
addBravoFixes();
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
trackedRace.waitForLoadingToFinish();
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 2);
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, TestFixTrackImpl.TRACK_NAME), 1);
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -437,21 +371,14 @@ public class SensorFixStoreAndLoadTest {
deviceTest, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
addTestFixes();
addBravoFixes();
trackedRace.waitForLoadingToFinish();
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, TestFixTrackImpl.TRACK_NAME), 1);
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 2);
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -461,18 +388,12 @@ public class SensorFixStoreAndLoadTest {
deviceTest, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(END_OF_TRACKING)));
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(END_OF_TRACKING)));
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
addTestFixes();
addBravoFixes();
trackedRace.waitForLoadingToFinish();
BravoFixTrack<Competitor> bravoFixTrack = trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME);
assertEquals(FIX_RIDE_HEIGHT,
bravoFixTrack.getFirstFixAtOrAfter(new MillisecondsTimePoint(FIX_TIMESTAMP)).getRideHeight());
@@ -480,11 +401,9 @@ public class SensorFixStoreAndLoadTest {
bravoFixTrack.getFirstFixAtOrAfter(new MillisecondsTimePoint(FIX_TIMESTAMP2)).getRideHeight());
assertEquals(FIX_RIDE_HEIGHT3,
bravoFixTrack.getFirstFixAtOrAfter(new MillisecondsTimePoint(FIX_TIMESTAMP3)).getRideHeight());
TestFixTrackImpl<Competitor> testFixTrack = trackedRace.getSensorTrack(comp, TestFixTrackImpl.TRACK_NAME);
assertEquals(FIX_TEST_VALUE,
testFixTrack.getFirstFixAtOrAfter(new MillisecondsTimePoint(FIX_TIMESTAMP)).getTestValue());
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
}
@@ -501,7 +420,6 @@ public class SensorFixStoreAndLoadTest {
}
private FixLoaderAndTracker createFixLoaderAndTracker() {
return new FixLoaderAndTracker(trackedRace, store, new SensorFixMapperFactory() {
private TestDataFixMapper testDataFixMapper = new TestDataFixMapper();
private BravoDataFixMapper bravoDataFixMapper = new BravoDataFixMapper();
@@ -747,6 +665,50 @@ public class SensorFixStoreAndLoadTest {
// only the initial 3 fixes are available
testNumberOfRawFixes(trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME), 3);
fixLoaderAndTracker.stop(true, /* willBeRemoved */ false);
final DynamicGPSFixTrack<Competitor, GPSFixMoving> gpsFixTrack = trackedRace.getTrack(comp);
final DegreePosition pos1 = new DegreePosition(0, 0);
final DegreeBearingImpl course = new DegreeBearingImpl(0);
final KnotSpeedWithBearingImpl speed = new KnotSpeedWithBearingImpl(10, course);
final MillisecondsTimePoint timePoint = new MillisecondsTimePoint(FIX_TIMESTAMP);
final MillisecondsTimePoint timePoint2 = new MillisecondsTimePoint(FIX_TIMESTAMP2);
final MillisecondsTimePoint timePoint3 = new MillisecondsTimePoint(FIX_TIMESTAMP3);
final TimePoint timePointBetween2And3 = new MillisecondsTimePoint((FIX_TIMESTAMP2+FIX_TIMESTAMP3)/2);
final GPSFixMoving fix1 = new GPSFixMovingImpl(pos1, timePoint, speed);
final Position pos2 = pos1.translateGreatCircle(course, speed.travel(timePoint.until(timePoint2)));
final GPSFixMoving fix2 = new GPSFixMovingImpl(pos2, timePoint2, speed);
final Position pos3 = pos2.translateGreatCircle(course, speed.travel(timePoint2.until(timePoint3)));
final GPSFixMoving fix3 = new GPSFixMovingImpl(pos3, timePoint3, speed);
gpsFixTrack.add(fix1);
gpsFixTrack.add(fix2);
gpsFixTrack.add(fix3);
final BravoFixTrack<Competitor> bravoFixTrack = trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME);
final Distance foilingDistance = bravoFixTrack.getDistanceSpentFoiling(timePoint, timePoint3);
final Distance distanceTraveled = gpsFixTrack.getDistanceTraveled(timePoint, timePoint3);
assertEquals(distanceTraveled.getMeters(), foilingDistance.getMeters(), 0.001);
final Duration foilingDuration = bravoFixTrack.getTimeSpentFoiling(timePoint, timePoint3);
assertEquals(timePoint.until(timePoint3).asMillis(), foilingDuration.asMillis());
// now insert a GPS fix which leads to an extended distance traveled as it is not exactly on the previous track
final DegreeBearingImpl temporaryCourse = new DegreeBearingImpl(90);
final GPSFixMoving fixBetween2And3 = new GPSFixMovingImpl(
pos2.translateGreatCircle(temporaryCourse, speed.add(new KnotSpeedWithBearingImpl(5, temporaryCourse)).travel(timePoint2.until(timePointBetween2And3))),
timePointBetween2And3, speed);
gpsFixTrack.add(fixBetween2And3);
final Distance distanceTraveledDifferently = gpsFixTrack.getDistanceTraveled(timePoint, timePoint3);
assertTrue(distanceTraveledDifferently.compareTo(distanceTraveled) > 0);
final Distance foilingDistanceDifferent = bravoFixTrack.getDistanceSpentFoiling(timePoint, timePoint3);
assertEquals(distanceTraveledDifferently.getMeters(), foilingDistanceDifferent.getMeters(), 0.001);
// now overwrite fix3 by a new one with increased speed, therefore extended distance:
final SpeedWithBearing doubledSpeed = speed.add(speed);
final GPSFixMoving fix3Faster = new GPSFixMovingImpl(
pos2.translateGreatCircle(course, doubledSpeed.travel(timePoint2.until(timePoint3))),
timePoint3, doubledSpeed);
gpsFixTrack.add(fix3Faster, /* replace */ true);
final Distance distanceTraveledFaster = gpsFixTrack.getDistanceTraveled(timePoint, timePoint3);
assertTrue(distanceTraveledFaster.compareTo(distanceTraveled) > 0);
final Distance foilingDistanceFaster = bravoFixTrack.getDistanceSpentFoiling(timePoint, timePoint3);
assertEquals(distanceTraveledFaster.getMeters(), foilingDistanceFaster.getMeters(), 0.001);
}
@Test
@@ -14,6 +14,7 @@ import org.mockito.Mockito;
import com.mongodb.DB;
import com.mongodb.MongoException;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
@@ -30,7 +31,6 @@ import com.sap.sailing.domain.racelog.tracking.FixReceivedListener;
import com.sap.sailing.domain.racelog.tracking.SensorFixStore;
import com.sap.sailing.domain.racelog.tracking.test.mock.MockSmartphoneImeiServiceFinderFactory;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.Timed;
import com.sap.sse.common.impl.MillisecondsTimePoint;
@@ -24,11 +24,11 @@ import com.sap.sailing.domain.base.RaceDefinition;
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.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.common.tracking.GPSFix;
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;
@@ -1,6 +1,6 @@
package com.sap.sailing.domain.racelog.tracking.test.mock;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.common.DeviceIdentifier;
public class SmartphoneImeiIdentifier implements DeviceIdentifier {
@@ -1,7 +1,7 @@
package com.sap.sailing.domain.racelog.tracking.test.mock;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler;
public class SmartphoneImeiJsonHandler extends SmartphoneImeiSerializationHandler
@@ -1,8 +1,8 @@
package com.sap.sailing.domain.racelog.tracking.test.mock;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
public class SmartphoneImeiMongoHandler extends SmartphoneImeiSerializationHandler
implements DeviceIdentifierMongoHandler {
@@ -1,7 +1,7 @@
package com.sap.sailing.domain.racelog.tracking.test.mock;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sse.common.Util;
public class SmartphoneImeiSerializationHandler {
@@ -2,6 +2,8 @@ package com.sap.sailing.domain.racelogtracking;
import java.util.UUID;
import com.sap.sailing.domain.common.DeviceIdentifier;
/**
* A device identifier used to identify a non-existent, virtual device, which is used only once as an
* identifier to add a single fix to a track (i.e. pinging the location of that track's item).
@@ -10,7 +10,7 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
import com.sap.sailing.domain.tracking.BravoFixTrack;
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl;
import com.sap.sailing.domain.tracking.impl.CompetitorBravoFixTrackImpl;
/**
* {@link SensorFixMapper} implementation to handle {@link BravoFix}es.
@@ -20,7 +20,8 @@ public class BravoDataFixMapper implements SensorFixMapper<BravoFix, DynamicSens
@Override
public DynamicSensorFixTrack<Competitor, BravoFix> getTrack(DynamicTrackedRace race, Competitor key) {
return race.getOrCreateSensorTrack(key, BravoFixTrack.TRACK_NAME,
() -> new BravoFixTrackImpl<Competitor>(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ false));
() -> new CompetitorBravoFixTrackImpl(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ false,
race.getTrack(key)));
}
@Override
@@ -11,7 +11,7 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
import com.sap.sailing.domain.tracking.BravoFixTrack;
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl;
import com.sap.sailing.domain.tracking.impl.CompetitorBravoFixTrackImpl;
/**
* {@link SensorFixMapper} implementation to handle {@link BravoExtendedFix}es.
@@ -21,7 +21,8 @@ public class BravoExtendedDataFixMapper implements SensorFixMapper<BravoFix, Dyn
@Override
public DynamicSensorFixTrack<Competitor, BravoFix> getTrack(DynamicTrackedRace race, Competitor key) {
return race.getOrCreateSensorTrack(key, BravoFixTrack.TRACK_NAME,
() -> new BravoFixTrackImpl<Competitor>(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ true));
() -> new CompetitorBravoFixTrackImpl(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ true,
race.getTrack(key)));
}
@Override
@@ -11,7 +11,7 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
import com.sap.sailing.domain.tracking.BravoFixTrack;
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl;
import com.sap.sailing.domain.tracking.impl.CompetitorBravoFixTrackImpl;
/**
* {@link SensorFixMapper} implementation to product {@link BravoExtendedFix}es from {@link DoubleVectorFix}es
@@ -25,7 +25,8 @@ public class ExpeditionExtendedDataFixMapper implements SensorFixMapper<BravoFix
@Override
public DynamicSensorFixTrack<Competitor, BravoFix> getTrack(DynamicTrackedRace race, Competitor key) {
return race.getOrCreateSensorTrack(key, BravoFixTrack.TRACK_NAME,
() -> new BravoFixTrackImpl<Competitor>(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ true));
() -> new CompetitorBravoFixTrackImpl(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ true,
race.getTrack(key)));
}
@Override
@@ -1,7 +1,7 @@
package com.sap.sailing.domain.racelogtracking.impl;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler;
public class PingDeviceIdentifierJsonHandler extends PingDeviceIdentifierSerializationHandler
@@ -1,8 +1,8 @@
package com.sap.sailing.domain.racelogtracking.impl;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
public class PingDeviceIdentifierMongoHandler extends PingDeviceIdentifierSerializationHandler
implements DeviceIdentifierMongoHandler {
@@ -2,8 +2,8 @@ package com.sap.sailing.domain.racelogtracking.impl;
import java.util.UUID;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.PingDeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.PingDeviceIdentifierImpl;
import com.sap.sse.common.Util;
@@ -1,8 +1,8 @@
package com.sap.sailing.domain.racelogtracking.impl;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.SmartphoneUUIDSerializationHandler;
public class SmartphoneUUIDMongoHandler extends SmartphoneUUIDSerializationHandler
@@ -4,8 +4,8 @@ import java.util.UUID;
import org.json.simple.JSONObject;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifier;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifierImpl;
import com.sap.sailing.server.gateway.serialization.racelog.tracking.DeviceIdentifierJsonHandler;
@@ -4,9 +4,9 @@ import java.util.UUID;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.persistence.racelog.tracking.DeviceIdentifierMongoHandler;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifier;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifierImpl;
import com.sap.sse.common.TimePoint;
@@ -2,8 +2,8 @@ package com.sap.sailing.domain.racelogtracking.impl;
import java.util.UUID;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifierStringSerializationHandler;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifier;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifierImpl;
@@ -23,6 +23,7 @@ import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMap
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
@@ -33,7 +34,6 @@ import com.sap.sailing.domain.racelog.tracking.FixReceivedListener;
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.DeviceMappingWithRegattaLogEvent;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
@@ -22,8 +22,8 @@ import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMap
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.regatta.impl.BaseRegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceMappingFinder;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.DoesNotHaveRegattaLogException;
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;
@@ -12,6 +12,7 @@ import org.junit.Test;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.race.CompetitorResult;
import com.sap.sailing.domain.abstractlog.race.CompetitorResult.MergeState;
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
@@ -56,10 +57,10 @@ public class ConfirmedFinishPositioningListFinderTest extends
public void testMostRecent() {
RaceLogFinishPositioningConfirmedEvent event1 = createEvent(RaceLogFinishPositioningConfirmedEvent.class, 1);
RaceLogFinishPositioningConfirmedEvent event2 = createEvent(RaceLogFinishPositioningConfirmedEvent.class, 2);
final CompetitorResultImpl oldResultC1 = new CompetitorResultImpl("Comp1", "Comp 1", 1, MaxPointsReason.NONE, 1.0, /* finishing time */ null, /* comment */ null);
final CompetitorResultImpl oldResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.NONE, 2.0, /* finishing time */ null, /* comment */ null);
final CompetitorResultImpl oldResultC1 = new CompetitorResultImpl("Comp1", "Comp 1", 1, MaxPointsReason.NONE, 1.0, /* finishing time */ null, /* comment */ null, MergeState.OK);
final CompetitorResultImpl oldResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.NONE, 2.0, /* finishing time */ null, /* comment */ null, MergeState.OK);
final CompetitorResults olderResults = mockCompetitorResults(oldResultC1, oldResultC2);
final CompetitorResultImpl newResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.DNF, 3.0, /* finishing time */ null, /* comment */ null);
final CompetitorResultImpl newResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.DNF, 3.0, /* finishing time */ null, /* comment */ null, MergeState.OK);
final CompetitorResults newerResults = mockCompetitorResults(newResultC2);
when(event1.getPositionedCompetitorsIDsNamesMaxPointsReasons()).thenReturn(olderResults);
when(event2.getPositionedCompetitorsIDsNamesMaxPointsReasons()).thenReturn(newerResults);
@@ -74,16 +75,16 @@ public class ConfirmedFinishPositioningListFinderTest extends
RaceLogFinishPositioningConfirmedEvent event2 = createEvent(/* priority */ 1, RaceLogFinishPositioningConfirmedEvent.class, 2);
RaceLogFinishPositioningConfirmedEvent event3 = createEvent(/* priority */ 2, RaceLogFinishPositioningConfirmedEvent.class, 3);
RaceLogFinishPositioningConfirmedEvent event4 = createEvent(/* priority */ 1, RaceLogFinishPositioningConfirmedEvent.class, 4);
final CompetitorResultImpl e1ResultC1 = new CompetitorResultImpl("Comp1", "Comp 1", 1, MaxPointsReason.DNC, 6.0, /* finishing time */ null, /* comment */ null);
final CompetitorResultImpl e1ResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.NONE, 2.0, /* finishing time */ null, /* comment */ null);
final CompetitorResultImpl e1ResultC1 = new CompetitorResultImpl("Comp1", "Comp 1", 1, MaxPointsReason.DNC, 6.0, /* finishing time */ null, /* comment */ null, MergeState.OK);
final CompetitorResultImpl e1ResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.NONE, 2.0, /* finishing time */ null, /* comment */ null, MergeState.OK);
final CompetitorResults e1Results = mockCompetitorResults(e1ResultC1, e1ResultC2);
final CompetitorResultImpl e2ResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.DNF, 3.0, /* finishing time */ null, /* comment */ null);
final CompetitorResultImpl e2ResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.DNF, 3.0, /* finishing time */ null, /* comment */ null, MergeState.OK);
final CompetitorResults e2Results = mockCompetitorResults(e2ResultC2);
final CompetitorResultImpl e3ResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.DSQ, 6.0, /* finishing time */ null, /* comment */ null);
final CompetitorResultImpl e3ResultC3 = new CompetitorResultImpl("Comp3", "Comp 3", 2, MaxPointsReason.DNF, 3.0, /* finishing time */ null, /* comment */ null);
final CompetitorResultImpl e3ResultC2 = new CompetitorResultImpl("Comp2", "Comp 2", 2, MaxPointsReason.DSQ, 6.0, /* finishing time */ null, /* comment */ null, MergeState.OK);
final CompetitorResultImpl e3ResultC3 = new CompetitorResultImpl("Comp3", "Comp 3", 2, MaxPointsReason.DNF, 3.0, /* finishing time */ null, /* comment */ null, MergeState.OK);
final CompetitorResults e3Results = mockCompetitorResults(e3ResultC2, e3ResultC3);
final CompetitorResultImpl e4ResultC1 = new CompetitorResultImpl("Comp1", "Comp 1", 1, /* maxPointsReason */ null, /* score */ null, /* finishing time */ null, /* comment */ null);
final CompetitorResultImpl e4ResultC4 = new CompetitorResultImpl("Comp4", "Comp 4", 2, MaxPointsReason.DNF, 3.0, /* finishing time */ null, /* comment */ null);
final CompetitorResultImpl e4ResultC1 = new CompetitorResultImpl("Comp1", "Comp 1", 1, /* maxPointsReason */ null, /* score */ null, /* finishing time */ null, /* comment */ null, MergeState.OK);
final CompetitorResultImpl e4ResultC4 = new CompetitorResultImpl("Comp4", "Comp 4", 2, MaxPointsReason.DNF, 3.0, /* finishing time */ null, /* comment */ null, MergeState.OK);
final CompetitorResults e4Results = mockCompetitorResults(e4ResultC1, e4ResultC4);
when(event1.getPositionedCompetitorsIDsNamesMaxPointsReasons()).thenReturn(e1Results);
when(event2.getPositionedCompetitorsIDsNamesMaxPointsReasons()).thenReturn(e2Results);
@@ -17,9 +17,9 @@ import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCo
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceCompetitorMappingFinder;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.impl.CompetitorImpl;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
import com.sap.sailing.domain.racelog.tracking.test.mock.SmartphoneImeiIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
@@ -14,6 +14,30 @@ import com.sap.sse.common.TimePoint;
*
*/
public interface CompetitorResult extends Serializable {
public static enum MergeState {
/**
* No merge was necessary at competitor level.
*/
OK,
/**
* A field-wise merge was necessary because an update was received that affected this competitor result
* while locally this competitor result was in a "dirty" (modified) state. The merge was carried out
* automatically because the fields changed in the dirty state did not overlap with the fields changed
* in the version of the object received.
*/
WARNING,
/**
* A field-wise merge was necessary because an update was received that affected this competitor result while
* locally this competitor result was in a "dirty" (modified) state. The fields changed in the dirty state
* overlap with the fields changed in the version of the object received. Field values from the local dirty state
* have been preferred or have been appended in case of {@link String}-typed fields and need manual
* intervention.
*/
ERROR
};
Serializable getCompetitorId();
String getCompetitorDisplayName();
@@ -27,4 +51,9 @@ public interface CompetitorResult extends Serializable {
TimePoint getFinishingTime();
String getComment();
/**
* Always returns a valid, non-{@code null} result. See {@link MergeState} for details.
*/
MergeState getMergeState();
}
@@ -23,8 +23,11 @@ public class CompetitorResultImpl implements CompetitorResult {
private final String comment;
private final MergeState mergeState;
public CompetitorResultImpl(Serializable competitorId, String competitorDisplayName, int oneBasedRank,
MaxPointsReason maxPointsReason, Double score, TimePoint finishingTime, String comment) {
MaxPointsReason maxPointsReason, Double score, TimePoint finishingTime, String comment,
MergeState mergeState) {
super();
this.competitorId = competitorId;
this.competitorDisplayName = competitorDisplayName;
@@ -33,6 +36,7 @@ public class CompetitorResultImpl implements CompetitorResult {
this.score = score;
this.finishingTime = finishingTime;
this.comment = comment;
this.mergeState = mergeState;
}
@Override
@@ -70,6 +74,11 @@ public class CompetitorResultImpl implements CompetitorResult {
return comment;
}
@Override
public MergeState getMergeState() {
return mergeState == null ? MergeState.OK : mergeState; // default in case of having de-serialized an old version
}
@Override
public int hashCode() {
final int prime = 31;
@@ -81,6 +90,7 @@ public class CompetitorResultImpl implements CompetitorResult {
result = prime * result + ((maxPointsReason == null) ? 0 : maxPointsReason.hashCode());
result = prime * result + oneBasedRank;
result = prime * result + ((score == null) ? 0 : score.hashCode());
result = prime * result + ((mergeState == null) ? 0 : mergeState.hashCode());
return result;
}
@@ -122,6 +132,11 @@ public class CompetitorResultImpl implements CompetitorResult {
return false;
} else if (!score.equals(other.score))
return false;
if (mergeState == null) {
if (other.mergeState != null)
return false;
} else if (!mergeState.equals(other.mergeState))
return false;
return true;
}
@@ -129,7 +144,7 @@ public class CompetitorResultImpl implements CompetitorResult {
public String toString() {
return "CompetitorResultImpl [competitorId=" + competitorId + ", competitorName=" + competitorDisplayName + ", rank="
+ oneBasedRank + ", maxPointsReason=" + maxPointsReason + ", score=" + score + ", finishingTime="
+ finishingTime + ", comment=" + comment + "]";
+ finishingTime + ", comment=" + comment + ", mergeState=" + mergeState + "]";
}
}
@@ -15,6 +15,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogEventVisitor;
import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
import com.sap.sailing.domain.tracking.impl.TimeRangeCache;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util.Function;
@@ -263,4 +264,11 @@ public class NoAddingRaceLogWrapper implements RaceLog {
public int size() {
return innerRaceLog.size();
}
@Override
public <T> T getValueSum(TimePoint from, TimePoint to, T nullElement,
com.sap.sailing.domain.tracking.Track.Adder<T> adder, TimeRangeCache<T> cache,
com.sap.sailing.domain.tracking.Track.TimeRangeValueCalculator<T> valueCalculator) {
return innerRaceLog.getValueSum(from, to, nullElement, adder, cache, valueCalculator);
}
}
@@ -158,6 +158,7 @@ public class RaceStateImpl extends ReadonlyRaceStateImpl implements RaceState {
public void forceNewDependentStartTime(TimePoint now, final Duration startTimeDifference, final SimpleRaceLogIdentifier dependentOnRace) {
raceLog.add(new RaceLogDependentStartTimeEventImpl(now, author, raceLog.getCurrentPassId(), dependentOnRace, startTimeDifference));
}
@Override
public void setFinishingTime(TimePoint timePoint) {
@@ -166,7 +167,10 @@ public class RaceStateImpl extends ReadonlyRaceStateImpl implements RaceState {
@Override
public void setFinishedTime(TimePoint timePoint) {
raceLog.add(new RaceLogRaceStatusEventImpl(timePoint, author, raceLog.getCurrentPassId(), RaceLogRaceStatus.FINISHED));
raceLog.add(new RaceLogRaceStatusEventImpl(timePoint, author, raceLog.getCurrentPassId(),
RaceLogRaceStatus.FINISHED));
// ensure caches are synched
forceUpdate();
}
@Override
@@ -6,7 +6,7 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDefinedMarkAnalyzer;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange;
@@ -5,7 +5,7 @@ import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.impl.AbstractLogEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.WithID;
@@ -8,7 +8,7 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompetitorSensorDataMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEventImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
/**
@@ -4,7 +4,7 @@ import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
/**
@@ -4,7 +4,7 @@ import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
/**
@@ -4,7 +4,7 @@ import java.io.Serializable;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
/**
@@ -8,7 +8,7 @@ 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.RegattaLogDeviceMappingEventImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
public class RegattaLogDeviceCompetitorMappingEventImpl extends RegattaLogDeviceMappingEventImpl<Competitor> implements
@@ -8,7 +8,7 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEventVisitor;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEventImpl;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
public class RegattaLogDeviceMarkMappingEventImpl extends RegattaLogDeviceMappingEventImpl<Mark> implements
@@ -10,8 +10,8 @@ import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.abstractlog.NotRevokableException;
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;
@@ -8,7 +8,7 @@ import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceCompeti
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceCompetitorMappingEventImpl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
@@ -13,7 +13,7 @@ import com.sap.sailing.domain.abstractlog.regatta.RegattaLog;
import com.sap.sailing.domain.abstractlog.regatta.RegattaLogEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogCloseOpenEndedDeviceMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
import com.sap.sailing.domain.racelogtracking.impl.DeviceMappingWithRegattaLogEventImpl;
@@ -8,7 +8,7 @@ import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMapping
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMarkMappingEvent;
import com.sap.sailing.domain.abstractlog.regatta.events.impl.RegattaLogDeviceMarkMappingEventImpl;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
@@ -1,5 +1,6 @@
package com.sap.sailing.domain.racelogtracking;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sse.common.Util;
@@ -4,6 +4,7 @@ import java.io.Serializable;
import java.util.List;
import com.sap.sailing.domain.abstractlog.regatta.tracking.analyzing.impl.RegattaLogDeviceMappingFinder;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sse.common.TimeRange;
import com.sap.sse.common.Timed;
import com.sap.sse.common.WithID;
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.racelogtracking;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.common.DeviceIdentifier;
@@ -2,6 +2,8 @@ package com.sap.sailing.domain.racelogtracking;
import java.util.UUID;
import com.sap.sailing.domain.common.DeviceIdentifier;
public interface SmartphoneUUIDIdentifier extends DeviceIdentifier {
String TYPE = "smartphoneUUID";
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.racelogtracking;
import java.util.UUID;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.racelogtracking.impl.SmartphoneUUIDIdentifierImpl;
import com.sap.sse.common.Util;
@@ -5,7 +5,7 @@ import java.util.ArrayList;
import java.util.List;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMapping;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.TimeRange;
@@ -4,7 +4,7 @@ import java.io.Serializable;
import java.util.List;
import com.sap.sailing.domain.abstractlog.regatta.events.RegattaLogDeviceMappingEvent;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.DeviceMappingWithRegattaLogEvent;
import com.sap.sse.common.TimeRange;
import com.sap.sse.common.WithID;
@@ -1,7 +1,7 @@
package com.sap.sailing.domain.racelogtracking.impl;
import com.sap.sailing.domain.common.DeviceIdentifier;
import com.sap.sailing.domain.common.racelog.tracking.TransformationException;
import com.sap.sailing.domain.racelogtracking.DeviceIdentifier;
import com.sap.sailing.domain.racelogtracking.PlaceHolderDeviceIdentifier;
import com.sap.sse.common.Util;
@@ -5,6 +5,7 @@ import java.util.Iterator;
import java.util.concurrent.locks.ReadWriteLock;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.impl.TimeRangeCache;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Timed;
@@ -31,6 +32,29 @@ import com.sap.sse.common.scalablevalue.ScalableValue;
* @author Axel Uhl (d043530)
*/
public interface Track<FixType extends Timed> extends Serializable {
/**
* An adding function to be used together with {@link Track#getValueSum(TimePoint, TimePoint, Object, Adder, TimeRangeCache, TimeRangeValueCalculator)}.
*
* @author Axel Uhl (D043530)
*
* @param <T>
*/
static interface Adder<T> {
/**
* Adds two elements of type {@code T}. Neither argument must be {@code null}.
*/
T add(T t1, T t2);
}
static interface TimeRangeValueCalculator<T> {
/**
* Calculates a value for fixes across a time range. When the method is called,
* a read lock will previously have been {@link Track#lockForRead obtained} before,
* so an implementing class does not need to worry about acquiring the lock.
*/
T calculate(TimePoint from, TimePoint to);
}
/**
* Locks this track for reading by the calling thread. If the thread already holds the lock for this track,
* the hold count will be incremented. Make sure to call {@link #unlockAfterRead()} in a <code>finally</code>
@@ -209,6 +233,8 @@ public interface Track<FixType extends Timed> extends Serializable {
*/
Duration getAverageIntervalBetweenRawFixes();
<T> T getValueSum(TimePoint from, TimePoint to, T nullElement, Adder<T> adder, TimeRangeCache<T> cache, TimeRangeValueCalculator<T> valueCalculator);
/**
* @return the number of raw fixes contained in the Track.
*/
@@ -218,4 +244,5 @@ public interface Track<FixType extends Timed> extends Serializable {
* Tells whether the collection of {@link #getRawFixes() raw fixes} (no outliers removed) is empty
*/
boolean isEmpty();
}
@@ -0,0 +1,221 @@
package com.sap.sailing.domain.tracking.impl;
import java.util.Comparator;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map.Entry;
import java.util.NavigableSet;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.concurrent.LockUtil;
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
import com.sap.sse.util.impl.ArrayListNavigableSet;
/**
* This cache looks "backwards." It contains pairs whose first component represents a <code>to</code> parameter used in
* a calculation for a time range. It is ordered by this component. The second component is a navigable, ordered set of
* pairs where the first pair component represents a <code>from</code> parameter used in the calculation's time range
* and the second pair component represents the result of the calculation for this parameter combination.
* <p>
*
* For implementation efficiency in combination with using an {@link ArrayListNavigableSet} for the values and in order
* to be able to efficiently extend a cache entry for a single <code>to</code> fix, the navigable sets containing the
* <code>from</code> fixes and results are ordered such that earlier fixes come later in the set. This way, extending
* the cache entry for a <code>to</code> fix to an earlier <code>from</code> fix only requires appending to the set.
* <p>
*
* <b>Invalidation</b>: When a new fix is added to the track, all cache entries for fixes at or later than the new fix's
* time point are removed from this cache. Additionally, the fix insertion may have an impact on the
* {@link #getEarliestFromAndResultAtOrAfterFrom(TimePoint, TimePoint) previous fix's} validity (track smoothing) and
* therefore on its selection for result aggregation. Therefore, if fix addition turned the previous fix invalid, the
* cache entries for the time points at or after the previous fix also need to be removed.
* <p>
*
* <b>Cache use</b>: When a result across a time range is to be computed the calculating method should first look for a
* cache entry for the <code>to</code> parameter. If one is found, the earliest entry in the navigable set for the
* navigable set of <code>from</code> and result values that is at or after the requested <code>from</code> time point
* is determined. If such an entry exists, the result is remembered and the algorithm is repeated recursively, using the
* <code>from</code> value found in the cache as the new <code>to</code> value, and the <code>from</code> value
* originally passed to the calculating method as <code>from</code> again. If no entry is found in the cache entry for
* <code>to</code> that is at or after the requested <code>from</code> time, the result has to be computed "from
* scratch."
* <p>
*
* If a cache entry for <code>to</code> is not found, the latest cache entry before it is looked up. If one is found,
* the result for the time range between the <code>to</code> time point requested and the <code>to</code> time point
* found in the cache is computed by iterating the smoothened fixes for this interval. If none is found, the result is
* computed by iterating backwards all the way to <code>from</code>.
* <p>
*
* Once the calculating method has computed its value, it should {@link #cache(TimePoint, TimePoint, Object) add} the
* result to the cache.
*
* @author Axel Uhl (D043530)
*/
public class TimeRangeCache<T> {
public static final int MAX_SIZE = 100;
private final NavigableSet<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>> timeRangeCache;
/**
* The cache is to have limited size. Eviction shall happen based on a least-recently-used strategy. Usage is
* defined as having been returned by {@link #getEarliestFromAndResultAtOrAfterFrom(TimePoint, TimePoint)} or
* having been added by {@link #cache(TimePoint, TimePoint, Object)}.
* <p>
*
* When an eldest entry is asked to be expunged from this map and the map has more than {@link #MAX_SIZE} elements,
* the expunging will be admitted, and the entry is removed from the {@link #timeRangeCache} core structure. Reading
* and writing this structure must happen under the {@link #lock write lock} because also reading the linked hash
* map that counts access as "use" has a modifying effect on its internal structures.
* <p>
*
* The key pairs are from/to pairs. Note that this is in some sense "the opposite direction" compared to the
* alignment of the {@link #timeRangeCache} structure which has as its outer keys the "to" time point.<p>
*
* Read access is to be <code>synchronized<code> using this field's mutex; write access only happens under the
* {@link #lock write lock} and therefore will have no contenders.
*/
private final LinkedHashMap<Util.Pair<TimePoint, TimePoint>, Void> lruCache;
private final NamedReentrantReadWriteLock lock;
private static final Comparator<Util.Pair<TimePoint, ?>> timePointInPairComparator = new Comparator<Util.Pair<TimePoint, ?>>() {
@Override
public int compare(Util.Pair<TimePoint, ?> o1, Util.Pair<TimePoint, ?> o2) {
return o1.getA().compareTo(o2.getA());
}
};
public TimeRangeCache(String nameForLockLogging) {
lock = new NamedReentrantReadWriteLock("lock for TimeRangeCache for "+nameForLockLogging, /* fair */ true);
this.timeRangeCache = new ArrayListNavigableSet<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>>(timePointInPairComparator);
this.lruCache = new LinkedHashMap<Util.Pair<TimePoint, TimePoint>, Void>(/* initial capacity */ 10, /* load factor */ 0.75f,
/* access-based ordering */ true) {
private static final long serialVersionUID = -6568235517111733193L;
@Override
protected boolean removeEldestEntry(Entry<Pair<TimePoint, TimePoint>, Void> eldest) {
final boolean expunge = size() > MAX_SIZE;
if (expunge) {
removeCacheEntry(eldest.getKey().getA(), eldest.getKey().getB());
}
return expunge;
}
};
}
public int size() {
return lruCache.size();
}
private void removeCacheEntry(TimePoint from, TimePoint to) {
assert lock.getWriteHoldCount() == 1; // we can be sure we are alone here; this only happens when adding a new entry, holding the write lock
Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryForTo = timeRangeCache.floor(createDummy(to));
if (entryForTo.getA().equals(to)) {
Pair<TimePoint, T> entryForFrom = entryForTo.getB().ceiling(new Util.Pair<TimePoint, T>(from, null));
if (entryForFrom.getA().equals(from)) {
entryForTo.getB().remove(entryForFrom);
if (entryForTo.getB().isEmpty()) {
timeRangeCache.remove(entryForTo);
}
}
}
}
/**
* Looks up the entry closest to but no later than <code>to</code>. If not found, <code>null</code> is returned. If
* found, the earliest pair of from/result that is at or after <code>from</code> will be returned, together with
* the <code>to</code> value of the entry. If there is no entry that is at or after <code>from</code>,
* <code>null</code> is returned.
*/
public Util.Pair<TimePoint, Util.Pair<TimePoint, T>> getEarliestFromAndResultAtOrAfterFrom(TimePoint from, TimePoint to) {
LockUtil.lockForRead(lock);
try {
Util.Pair<TimePoint, Util.Pair<TimePoint, T>> result = null;
Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryForTo = timeRangeCache.floor(createDummy(to));
if (entryForTo != null) {
final Util.Pair<TimePoint, T> fromCeiling = entryForTo.getB().ceiling(new Util.Pair<TimePoint, T>(from, null));
if (fromCeiling != null) {
result = new Util.Pair<TimePoint, Util.Pair<TimePoint, T>>(entryForTo.getA(), fromCeiling);
}
}
// no writer can be active because we're holding the read lock; read access on the lruCache is synchronized using
// the lruCache's mutex; this is necessary because we're using access-based LRU pinging where even getting an entry
// modifies the internal parts of the data structure which is not thread safe.
synchronized (lruCache) {
lruCache.get(new Util.Pair<TimePoint, TimePoint>(from, to));
}
return result;
} finally {
LockUtil.unlockAfterRead(lock);
}
}
/**
* Removes all cache entries that have a <code>to</code> time point that is at or after <code>timePoint</code>.
*/
public void invalidateAllAtOrLaterThan(TimePoint timePoint) {
LockUtil.lockForWrite(lock);
try {
Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> dummy = createDummy(timePoint);
NavigableSet<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>> toRemove = timeRangeCache.tailSet(dummy, /* inclusive */ true);
for (Iterator<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>> i=toRemove.iterator(); i.hasNext(); ) {
Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryToRemove = i.next();
assert entryToRemove.getA().compareTo(timePoint) >= 0;
for (Pair<TimePoint, T> fromAndResult : entryToRemove.getB()) {
lruCache.remove(new Util.Pair<>(fromAndResult.getA(), entryToRemove.getA()));
}
i.remove();
}
} finally {
LockUtil.unlockAfterWrite(lock);
}
}
private NavigableSet<Util.Pair<TimePoint, T>> getEntryForTo(TimePoint to) {
NavigableSet<Util.Pair<TimePoint, T>> result = null;
Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> dummyForTo = createDummy(to);
Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> entryForTo = timeRangeCache.floor(dummyForTo);
if (entryForTo != null && entryForTo.getA().equals(to)) {
result = entryForTo.getB();
}
return result;
}
public void cache(TimePoint from, TimePoint to, T result) {
LockUtil.lockForWrite(lock);
try {
NavigableSet<Util.Pair<TimePoint, T>> entryForTo = getEntryForTo(to);
if (entryForTo == null) {
entryForTo = new ArrayListNavigableSet<Util.Pair<TimePoint, T>>(timePointInPairComparator);
Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> pairForTo = new Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>(
to, entryForTo);
timeRangeCache.add(pairForTo);
}
entryForTo.add(new Util.Pair<TimePoint, T>(from, result));
lruCache.put(new Util.Pair<TimePoint, TimePoint>(from, to), null);
} finally {
LockUtil.unlockAfterWrite(lock);
}
}
private Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>> createDummy(TimePoint to) {
return new Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, T>>>(to, null);
}
/**
* Removes all contents from this cache
*/
public void clear() {
LockUtil.lockForWrite(lock);
try {
timeRangeCache.clear();
lruCache.clear();
} finally {
LockUtil.unlockAfterWrite(lock);
}
}
}
@@ -11,6 +11,7 @@ import com.sap.sailing.domain.tracking.Track;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Timed;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Function;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.scalablevalue.ScalableValue;
@@ -459,6 +460,64 @@ public class TrackImpl<FixType extends Timed> implements Track<FixType> {
}
}
@Override
public <T> T getValueSum(TimePoint from, TimePoint to, T nullElement, Adder<T> adder, TimeRangeCache<T> cache, TimeRangeValueCalculator<T> valueCalculator) {
return getValueSumRecursively(from, to, /* recursionLevel */ 0, nullElement, adder, cache, valueCalculator);
}
private <T> T getValueSumRecursively(TimePoint from, TimePoint to, int recursionDepth, T nullElement,
Adder<T> adder, TimeRangeCache<T> cache, TimeRangeValueCalculator<T> valueCalculator) {
T result;
if (!from.before(to)) {
result = nullElement;
} else {
boolean perfectCacheHit = false;
lockForRead();
try {
Util.Pair<TimePoint, Util.Pair<TimePoint, T>> bestCacheEntry = cache.getEarliestFromAndResultAtOrAfterFrom(from, to);
if (bestCacheEntry != null) {
perfectCacheHit = true; // potentially a cache hit; but if it doesn't span the full interval, it's not perfect; see below
// compute the missing stretches between best cache entry's "from" and our "from" and the cache
// entry's "to" and our "to"
T valueFromFromToBeginningOfCacheEntry = nullElement;
T valueFromEndOfCacheEntryToTo = nullElement;
if (!bestCacheEntry.getB().getA().equals(from)) {
assert bestCacheEntry.getB().getA().after(from);
perfectCacheHit = false;
valueFromFromToBeginningOfCacheEntry = getValueSumRecursively(from, bestCacheEntry
.getB().getA(), recursionDepth + 1, nullElement, adder, cache, valueCalculator);
}
if (!bestCacheEntry.getA().equals(to)) {
assert bestCacheEntry.getA().before(to);
perfectCacheHit = false;
valueFromEndOfCacheEntryToTo = getValueSumRecursively(bestCacheEntry.getA(), to,
recursionDepth + 1, nullElement, adder, cache, valueCalculator);
}
if (valueFromEndOfCacheEntryToTo == null || bestCacheEntry.getB().getB() == null) {
result = null;
} else {
result = adder.add(adder.add(valueFromFromToBeginningOfCacheEntry, bestCacheEntry.getB().getB()),
valueFromEndOfCacheEntryToTo);
}
} else {
if (from.compareTo(to) < 0) {
result = valueCalculator.calculate(from, to);
} else {
result = nullElement;
}
}
} finally {
unlockAfterRead();
}
if (!perfectCacheHit && recursionDepth == 0) {
cache.cache(from, to, result);
}
}
return result;
}
@Override
public int size() {
return fixes.size();
@@ -46,6 +46,16 @@ public class BearingTest {
assertNull(new BearingWithConfidenceCluster<Void>(null).getAverage(null));
}
@Test
public void testNegativeAngle() {
assertEquals(-10., new DegreeBearingImpl(-10.).getDegrees(), 0.000001);
}
@Test
public void testNegativeAngleOverflow() {
assertEquals(-10., new DegreeBearingImpl(-370.).getDegrees(), 0.000001);
}
@Test
public void testZeroConfidenceLeadsToNullBearingAverage() {
BearingWithConfidenceCluster<Void> cluster = new BearingWithConfidenceCluster<Void>(null);
@@ -207,27 +207,29 @@ public class CompetitorProviderCacheInvalidationTest extends AbstractLeaderboard
for (Competitor c : compLists[1]) {
raceLog.add(new RaceLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), 1, c));
}
Set<Competitor> allCompetitors = new HashSet<>();
allCompetitors.addAll(compLists[0]);
allCompetitors.addAll(compLists[1]);
// expected are only the competitors in the RaceLog, because only one RaceColumn is
// registered, which has the competitors registered in the RaceLog.
assertRegattaAndRaceCompetitors(new HashSet<>(compLists[1]));
assertRegattaAndRaceCompetitors(allCompetitors, new HashSet<>(compLists[1]));
// Now we revoke that the race log provides the competitors for R1; the competitors should then
// snap back to those competitors taken from the regatta log for both, the rentire leaderboard
// as well as for the race column
raceLog.revokeEvent(author, usesCompetitorsFromRaceLogEvent);
assertRegattaAndRaceCompetitors(new HashSet<>(compLists[0]));
assertRegattaAndRaceCompetitors(new HashSet<>(compLists[0]), new HashSet<>(compLists[0]));
// And now re-introduce per-race competitors and validate again that the cache adjusts properly:
raceLog.add(new RaceLogUseCompetitorsFromRaceLogEventImpl(MillisecondsTimePoint.now(), author, MillisecondsTimePoint.now(), UUID.randomUUID(), passId));
assertRegattaAndRaceCompetitors(new HashSet<>(compLists[1]));
assertRegattaAndRaceCompetitors(allCompetitors, new HashSet<>(compLists[1]));
}
private void assertRegattaAndRaceCompetitors(Set<Competitor> expected) {
private void assertRegattaAndRaceCompetitors(Set<Competitor> expectedAllCompetitors, Set<Competitor> expectedForRace) {
Set<Competitor> actual = new HashSet<>();
Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(), actual);
assertEquals(expected, actual);
assertEquals(expectedAllCompetitors, actual);
Set<Competitor> actualForRace = new HashSet<>();
//For the race only the competitors registered on RaceLog as RaceLogUseCompetitorsFromRaceLogEvent is present
Set<Competitor> expectedForRace = new HashSet<>(expected);
// For the race only the competitors registered on RaceLog as RaceLogUseCompetitorsFromRaceLogEvent is present
Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(
flexibleLeaderboard.getRaceColumnByName("R1"),
flexibleLeaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), actualForRace);
@@ -24,6 +24,7 @@ import org.junit.Test;
import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor;
import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl;
import com.sap.sailing.domain.abstractlog.race.CompetitorResult;
import com.sap.sailing.domain.abstractlog.race.CompetitorResult.MergeState;
import com.sap.sailing.domain.abstractlog.race.CompetitorResults;
import com.sap.sailing.domain.abstractlog.race.RaceLog;
import com.sap.sailing.domain.abstractlog.race.RaceLogFinishPositioningConfirmedEvent;
@@ -153,7 +154,7 @@ public class MarkPassingUpdateFromRaceLogFinishPositioingListTest extends Abstra
int rank = 1;
for (Pair<Competitor, TimePoint> finishingTime : finishingTimes) {
results.add(new CompetitorResultImpl(finishingTime.getA().getId(), finishingTime.getA().getName(), rank++, /* maxPointsReason */
null, /* score */null, finishingTime.getB(), /* comment */null));
null, /* score */null, finishingTime.getB(), /* comment */null, MergeState.OK));
}
return results;
}
@@ -51,7 +51,7 @@ import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.impl.DistanceCache;
import com.sap.sailing.domain.tracking.impl.TimeRangeCache;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl;
import com.sap.sailing.domain.tracking.impl.GPSFixTrackImpl;
@@ -304,7 +304,7 @@ public class TrackTest {
// The following getMaximumSpeedOverGround call will trigger a computeMaxSpeed(...) and a cache(...) call
new Thread(()->
assertEquals(1., track.getMaximumSpeedOverGround(new MillisecondsTimePoint(0), new MillisecondsTimePoint(7200000)).
getB().getKnots(), 0.001)).start(); // produces a cache entry that ends
getB().getKnots(), 0.01)).start(); // produces a cache entry that ends
// now don't release the cacheBarrier as yet but add more fixes
new Thread(() -> {
GPSFixMoving fix2 = new GPSFixMovingImpl(new DegreePosition(0, 0), new MillisecondsTimePoint(3600000),
@@ -442,7 +442,7 @@ public class TrackTest {
@Test
public void testDistanceTraveledBackwardsQuery() {
final Set<com.sap.sse.common.Util.Triple<TimePoint, TimePoint, Distance>> cacheEntries = new HashSet<>();
final DistanceCache distanceCache = new DistanceCache("test-DistanceCache") {
final TimeRangeCache<Distance> distanceCache = new TimeRangeCache<Distance>("test-DistanceCache") {
@Override
public void cache(TimePoint from, TimePoint to, Distance distance) {
super.cache(from, to, distance);
@@ -453,7 +453,7 @@ public class TrackTest {
DynamicGPSFixTrack<Object, GPSFix> track = new DynamicGPSFixTrackImpl<Object>(new Object(), /* millisecondsOverWhichToAverage */ 30000l) {
private static final long serialVersionUID = -7277196393160609503L;
@Override
protected DistanceCache getDistanceCache() {
protected TimeRangeCache<Distance> getDistanceCache() {
return distanceCache;
}
};
@@ -779,7 +779,7 @@ public class TrackTest {
@Test
public void testDistanceTraveledOnSmoothenedTrackThenAddingOutlier() {
final Set<TimePoint> invalidationCalls = new HashSet<TimePoint>();
final DistanceCache distanceCache = new DistanceCache("test-DistanceCache") {
final TimeRangeCache<Distance> distanceCache = new TimeRangeCache<Distance>("test-DistanceCache") {
@Override
public void invalidateAllAtOrLaterThan(TimePoint timePoint) {
super.invalidateAllAtOrLaterThan(timePoint);
@@ -789,7 +789,7 @@ public class TrackTest {
DynamicGPSFixTrack<Object, GPSFix> track = new DynamicGPSFixTrackImpl<Object>(new Object(), /* millisecondsOverWhichToAverage */ 30000l) {
private static final long serialVersionUID = -7277196393160609503L;
@Override
protected DistanceCache getDistanceCache() {
protected TimeRangeCache<Distance> getDistanceCache() {
return distanceCache;
}
};
@@ -811,7 +811,7 @@ public class TrackTest {
}
invalidationCalls.clear();
assertEquals(speed.getMetersPerSecond()*(steps-1), track.getDistanceTraveled(now, start).getMeters(), 0.02);
final com.sap.sse.common.Util.Pair<TimePoint, com.sap.sse.common.Util.Pair<TimePoint, Distance>> fullIntervalCacheEntry = distanceCache.getEarliestFromAndDistanceAtOrAfterFrom(now, start);
final com.sap.sse.common.Util.Pair<TimePoint, com.sap.sse.common.Util.Pair<TimePoint, Distance>> fullIntervalCacheEntry = distanceCache.getEarliestFromAndResultAtOrAfterFrom(now, start);
assertNotNull(fullIntervalCacheEntry); // no more entry for "to"-value start in cache
assertEquals(start, fullIntervalCacheEntry.getA());
assertEquals(now, fullIntervalCacheEntry.getB().getA());
@@ -822,13 +822,13 @@ public class TrackTest {
assertEquals(1, invalidationCalls.size());
TimePoint timePointOfLastFixBeforeOutlier = track.getLastFixBefore(timePointForOutlier).getTimePoint();
assertTrue(invalidationCalls.iterator().next().after(timePointOfLastFixBeforeOutlier)); // outlier doesn't turn its preceding element into an outlier
assertNull(distanceCache.getEarliestFromAndDistanceAtOrAfterFrom(now, start)); // no more entry for "to"-value start in cache
assertNull(distanceCache.getEarliestFromAndResultAtOrAfterFrom(now, start)); // no more entry for "to"-value start in cache
invalidationCalls.clear();
final TimePoint timePointOfLastOriginalFix = track.getLastRawFix().getTimePoint();
assertEquals(speed.getMetersPerSecond() * (steps - 1),
track.getDistanceTraveled(now, timePointOfLastOriginalFix).getMeters(), 0.02);
final com.sap.sse.common.Util.Pair<TimePoint, com.sap.sse.common.Util.Pair<TimePoint, Distance>> newFullIntervalCacheEntry = distanceCache
.getEarliestFromAndDistanceAtOrAfterFrom(now, timePointOfLastOriginalFix);
.getEarliestFromAndResultAtOrAfterFrom(now, timePointOfLastOriginalFix);
assertNotNull(newFullIntervalCacheEntry); // no more entry for "to"-value start in cache
assertEquals(timePointOfLastOriginalFix, newFullIntervalCacheEntry.getA());
assertEquals(now, newFullIntervalCacheEntry.getB().getA());
@@ -845,7 +845,7 @@ public class TrackTest {
invalidationCalls.clear();
// expect the invalidation to have started after the single cache entry, so the cache entry still has to be there:
final com.sap.sse.common.Util.Pair<TimePoint, com.sap.sse.common.Util.Pair<TimePoint, Distance>> stillPresentFullIntervalCacheEntry = distanceCache
.getEarliestFromAndDistanceAtOrAfterFrom(now, timePointOfLastOriginalFix);
.getEarliestFromAndResultAtOrAfterFrom(now, timePointOfLastOriginalFix);
assertNull(stillPresentFullIntervalCacheEntry); // no more entry for "to"-value start in cache because new temporary outlier lies in previously cached interval
GPSFix polishedLastFix = track.getLastFixBefore(new MillisecondsTimePoint(Long.MAX_VALUE)); // get the last smoothened fix...
// ...which now is expected to be two fixes before lateOutlier because lateOutlier has previous fixes within the time range
@@ -870,7 +870,7 @@ public class TrackTest {
assertTrue(timePointForLateOutlier.compareTo(fix.getTimePoint()) < 0);
// expect the invalidation to have started at the fix before the outlier, leaving the previous result ending at the fix right before the outlier intact
final com.sap.sse.common.Util.Pair<TimePoint, com.sap.sse.common.Util.Pair<TimePoint, Distance>> stillStillPresentFullIntervalCacheEntry = distanceCache
.getEarliestFromAndDistanceAtOrAfterFrom(now, timePointOfLastOriginalFix);
.getEarliestFromAndResultAtOrAfterFrom(now, timePointOfLastOriginalFix);
assertNull(stillStillPresentFullIntervalCacheEntry); // still nothing in the cache
track.lockForRead();
try {
@@ -955,11 +955,11 @@ public class TrackTest {
final Duration raceDuration = Duration.ONE_HOUR.times(48);
final Speed speed = new KnotSpeedImpl(12);
final Distance distancePerSample = speed.travel(fixTime, fixTime.plus(samplingInterval));
final DistanceCache distanceCache = new DistanceCache("test-DistanceCache");
final TimeRangeCache<Distance> distanceCache = new TimeRangeCache<>("test-DistanceCache");
DynamicGPSFixTrack<Object, GPSFix> track = new DynamicGPSFixTrackImpl<Object>(new Object(), /* millisecondsOverWhichToAverage */ 30000l) {
private static final long serialVersionUID = -7277196393160609503L;
@Override
protected DistanceCache getDistanceCache() {
protected TimeRangeCache<Distance> getDistanceCache() {
return distanceCache;
}
};
@@ -980,13 +980,13 @@ public class TrackTest {
queryTime = queryTime.plus(queryInterval);
}
}
assertTrue(distanceCache.size() <= DistanceCache.MAX_SIZE);
assertTrue(distanceCache.size() <= TimeRangeCache.MAX_SIZE);
}
@Test
public void testDistanceCacheAccessForPartialStrip() {
final Set<TimePoint> invalidationCalls = new HashSet<TimePoint>();
final DistanceCache distanceCache = new DistanceCache("test-DistanceCache") {
final TimeRangeCache<Distance> distanceCache = new TimeRangeCache<Distance>("test-DistanceCache") {
@Override
public void invalidateAllAtOrLaterThan(TimePoint timePoint) {
super.invalidateAllAtOrLaterThan(timePoint);
@@ -996,7 +996,7 @@ public class TrackTest {
DynamicGPSFixTrack<Object, GPSFix> track = new DynamicGPSFixTrackImpl<Object>(new Object(), /* millisecondsOverWhichToAverage */ 30000l) {
private static final long serialVersionUID = -7277196393160609503L;
@Override
protected DistanceCache getDistanceCache() {
protected TimeRangeCache<Distance> getDistanceCache() {
return distanceCache;
}
};
@@ -1024,11 +1024,11 @@ public class TrackTest {
assertEquals(speed.getMetersPerSecond()*(steps-1), track.getDistanceTraveled(now, start).getMeters(), 0.02);
assertTrue(invalidationCalls.isEmpty());
// expect a cache entry exactly for the strip's boundaries
com.sap.sse.common.Util.Pair<TimePoint, com.sap.sse.common.Util.Pair<TimePoint, Distance>> stripCacheEntry = distanceCache.getEarliestFromAndDistanceAtOrAfterFrom(stripFrom, stripTo);
com.sap.sse.common.Util.Pair<TimePoint, com.sap.sse.common.Util.Pair<TimePoint, Distance>> stripCacheEntry = distanceCache.getEarliestFromAndResultAtOrAfterFrom(stripFrom, stripTo);
assertEquals(stripTo, stripCacheEntry.getA());
assertEquals(stripFrom, stripCacheEntry.getB().getA());
// expect a cache entry exactly for the full boundaries
com.sap.sse.common.Util.Pair<TimePoint, com.sap.sse.common.Util.Pair<TimePoint, Distance>> fullCacheEntry = distanceCache.getEarliestFromAndDistanceAtOrAfterFrom(now, start);
com.sap.sse.common.Util.Pair<TimePoint, com.sap.sse.common.Util.Pair<TimePoint, Distance>> fullCacheEntry = distanceCache.getEarliestFromAndResultAtOrAfterFrom(now, start);
assertEquals(start, fullCacheEntry.getA());
assertEquals(now, fullCacheEntry.getB().getA());
}
@@ -674,6 +674,16 @@ public class MockedTrackedRace implements DynamicTrackedRace {
return null;
}
@Override
public Distance getDistanceFoiled(Competitor competitor, TimePoint timePoint) {
return null;
}
@Override
public Duration getDurationFoiled(Competitor competitor, TimePoint timePoint) {
return null;
}
@Override
public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint,
WindPositionMode windPositionMode) {
@@ -1155,4 +1165,10 @@ public class MockedTrackedRace implements DynamicTrackedRace {
public Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint) {
return null;
}
@Override
public SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint,
WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) {
return null;
}
}
@@ -60,6 +60,7 @@ import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
import com.sap.sse.common.Duration;
import com.sap.sse.common.IsManagedByCache;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
@@ -360,6 +361,16 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
return null;
}
@Override
public Distance getDistanceFoiled(Competitor competitor, TimePoint timePoint) {
return null;
}
@Override
public Duration getDurationFoiled(Competitor competitor, TimePoint timePoint) {
return null;
}
@Override
public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) {
return null;
@@ -707,4 +718,10 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
public Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint) {
return null;
}
@Override
public SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint,
WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) {
return null;
}
}
@@ -269,7 +269,7 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns {
* competitor doesn't appear in any of the race column's attached tracked races. A 0.0 score is returned
* if the competitor's result for <code>race</code> is discarded.
*/
Double getNetPoints(Competitor competitor, RaceColumn race, TimePoint timePoint) throws NoWindException;
Double getNetPoints(Competitor competitor, RaceColumn race, TimePoint timePoint);
/**
* Tells whether the contribution of <code>raceColumn</code> is discarded in the current leaderboard's standings for
@@ -481,6 +481,26 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns {
* leaderboard
*/
Distance getTotalDistanceTraveled(Competitor competitor, TimePoint timePoint);
/**
* Computes the distance the <code>competitor</code> has been foiling in the tracked races in this leaderboard, starting
* to count in each race when the competitor passes the start line, aggregating up to <code>timePoint</code> or the
* end of the last race, whichever is first.
*
* @return <code>null</code> if the <code>competitor</code> hasn't foiled any distance in any tracked race in this
* leaderboard
*/
Distance getTotalDistanceFoiled(Competitor competitor, TimePoint timePoint);
/**
* Computes the duration the <code>competitor</code> has foiled in the tracked races in this leaderboard, starting
* to count in each race when the competitor passes the start line, aggregating up to <code>timePoint</code> or the
* end of the last race, whichever is first.
*
* @return <code>null</code> if the <code>competitor</code> hasn't foiled at any time in any tracked race in this
* leaderboard
*/
Duration getTotalDurationFoiled(Competitor competitor, TimePoint timePoint);
/**
* Same as {@link #getNetPoints(Competitor, RaceColumn, TimePoint)}, only that for determining the discarded
@@ -568,4 +588,10 @@ public interface Leaderboard extends LeaderboardBase, HasRaceColumns {
BoatClass getBoatClass();
LeaderboardType getLeaderboardType();
/**
* Tells if there is at least one non-{@code null} score for the given {@code competitor} in the
* leaderboard.
*/
boolean hasScores(Competitor competitor, TimePoint timePoint);
}
@@ -19,6 +19,7 @@ import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Future;
import java.util.concurrent.RunnableFuture;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -642,8 +643,12 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard {
row.totalTimeSailedReachingInSeconds = totalTimeSailedReaching==null?null:totalTimeSailedReaching.asSeconds();
final Duration totalTimeSailed = this.getTotalTimeSailed(competitor, timePoint);
row.totalTimeSailedInSeconds = totalTimeSailed==null?null:totalTimeSailed.asSeconds();
final Distance totalDistanceTraveledInMeters = this.getTotalDistanceTraveled(competitor, timePoint);
row.totalDistanceTraveledInMeters = totalDistanceTraveledInMeters==null?null:totalDistanceTraveledInMeters.getMeters();
final Distance totalDistanceTraveled = this.getTotalDistanceTraveled(competitor, timePoint);
row.totalDistanceTraveledInMeters = totalDistanceTraveled==null?null:totalDistanceTraveled.getMeters();
final Distance totalDistanceFoiled = this.getTotalDistanceFoiled(competitor, timePoint);
row.totalDistanceFoiledInMeters = totalDistanceFoiled==null?null:totalDistanceFoiled.getMeters();
final Duration totalDurationFoiled = this.getTotalDurationFoiled(competitor, timePoint);
row.totalDurationFoiledInSeconds = totalDurationFoiled==null?null:totalDurationFoiled.asSeconds();
}
private LeaderboardDTOCache getLeaderboardDTOCache() {
@@ -821,6 +826,12 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard {
result.currentHeelInDegrees = heel == null ? null : heel.getDegrees();
Bearing pitch = trackedLeg.getPitch(timePoint);
result.currentPitchInDegrees = pitch == null ? null : pitch.getDegrees();
Distance distanceFoiled = trackedLeg.getDistanceFoiled(timePoint);
result.currentDistanceFoiledInMeters = distanceFoiled == null ? null : distanceFoiled.getMeters();
Duration durationFoiled = trackedLeg.getDurationFoiled(timePoint);
result.currentDurationFoiledInSeconds = durationFoiled == null ? null : durationFoiled.asSeconds();
result.currentSpeedOverGroundInKnots = speedOverGroundInKnots == null ? null : speedOverGroundInKnots;
Distance distanceTraveled = trackedLeg.getDistanceTraveled(timePoint);
result.distanceTraveledInMeters = distanceTraveled == null ? null : distanceTraveled.getMeters();
@@ -858,7 +869,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard {
if (trackedLeg.hasFinishedLeg(timePoint)) {
velocityMadeGood = trackedLeg.getAverageVelocityMadeGood(timePoint);
} else {
velocityMadeGood = trackedLeg.getVelocityMadeGood(timePoint, WindPositionMode.EXACT);
velocityMadeGood = trackedLeg.getVelocityMadeGood(timePoint, WindPositionMode.EXACT, cache);
}
result.velocityMadeGoodInKnots = velocityMadeGood == null ? null : velocityMadeGood.getKnots();
Distance windwardDistanceToGo = trackedLeg.getWindwardDistanceToGo(timePoint, WindPositionMode.LEG_MIDDLE);
@@ -1082,18 +1093,37 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard {
@Override
public Distance getTotalDistanceTraveled(Competitor competitor, TimePoint timePoint) {
Distance result = null;
return getTotals(competitor, timePoint, Distance::add, TrackedRace::getDistanceTraveled);
}
@Override
public Distance getTotalDistanceFoiled(Competitor competitor, TimePoint timePoint) {
return getTotals(competitor, timePoint, Distance::add, TrackedRace::getDistanceFoiled);
}
@Override
public Duration getTotalDurationFoiled(Competitor competitor, TimePoint timePoint) {
return getTotals(competitor, timePoint, Duration::plus, TrackedRace::getDurationFoiled);
}
@FunctionalInterface
private static interface ValueFromRaceGetter<T> {
T get(TrackedRace trackedRace, Competitor competitor, TimePoint timePoint);
}
private <T> T getTotals(Competitor competitor, TimePoint timePoint, BiFunction<T, T, T> adder, ValueFromRaceGetter<T> valueGetter) {
T result = null;
for (TrackedRace trackedRace : getTrackedRaces()) {
TimePoint startOfRace;
if (Util.contains(trackedRace.getRace().getCompetitors(), competitor) &&
(startOfRace=trackedRace.getStartOfRace()) != null &&
!startOfRace.after(timePoint)) {
Distance distanceSailedInRace = trackedRace.getDistanceTraveled(competitor, timePoint);
T distanceSailedInRace = valueGetter.get(trackedRace, competitor, timePoint);
if (distanceSailedInRace != null) {
if (result == null) {
result = distanceSailedInRace;
} else {
result = result.add(distanceSailedInRace);
result = adder.apply(result, distanceSailedInRace);
}
} else {
// if competitor has not finished one single race in the whole
@@ -1118,4 +1148,13 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard {
}
}
@Override
public boolean hasScores(Competitor competitor, TimePoint timePoint) {
for (final RaceColumn raceColumn : getRaceColumns()) {
if (getTotalPoints(competitor, raceColumn, timePoint) != null) {
return true;
}
}
return false;
}
}
@@ -425,13 +425,13 @@ public abstract class AbstractSimpleLeaderboardImpl extends AbstractLeaderboardW
}
@Override
public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) throws NoWindException {
public Double getNetPoints(Competitor competitor, RaceColumn raceColumn, TimePoint timePoint) {
return getNetPoints(competitor, raceColumn, getRaceColumns(), timePoint);
}
@Override
public Double getNetPoints(Competitor competitor, RaceColumn raceColumn,
Iterable<RaceColumn> raceColumnsToConsider, TimePoint timePoint) throws NoWindException {
Iterable<RaceColumn> raceColumnsToConsider, TimePoint timePoint) {
final Set<RaceColumn> discardedRaceColumns = getResultDiscardingRule()
.getDiscardedRaceColumns(competitor, this, raceColumnsToConsider, timePoint);
return getNetPoints(competitor, raceColumn, timePoint, discardedRaceColumns);
@@ -181,9 +181,7 @@ public class CompetitorProviderFromRaceColumnsAndRegattaLike {
if (allCompetitorsCache == null) {
final Set<Competitor> result = new HashSet<>();
final Set<RaceDefinition> raceDefinitions = new HashSet<>();
boolean hasRaceColumns = false;
for (RaceColumn rc : provider.getRaceColumns()) {
hasRaceColumns = true;
final Pair<Iterable<RaceDefinition>, Iterable<Competitor>> allCompetitorsInRaceColumnWithRaceDefinitionsConsidered = rc.getAllCompetitorsWithRaceDefinitionsConsidered();
Util.addAll(allCompetitorsInRaceColumnWithRaceDefinitionsConsidered.getB(), result);
Util.addAll(allCompetitorsInRaceColumnWithRaceDefinitionsConsidered.getA(), raceDefinitions);
@@ -192,12 +190,10 @@ public class CompetitorProviderFromRaceColumnsAndRegattaLike {
}
}
final RegattaLog regattaLog = provider.getRegattaLike().getRegattaLog();
if (!hasRaceColumns) {
// If no race exists, the regatta log-provided competitor registrations will not have
// been considered yet; add them:
final Set<Competitor> regattaLogProvidedCompetitors = new CompetitorsInLogAnalyzer<>(regattaLog).analyze();
result.addAll(regattaLogProvidedCompetitors);
}
// If no race exists, the regatta log-provided competitor registrations will not have
// been considered yet; add them:
final Set<Competitor> regattaLogProvidedCompetitors = new CompetitorsInLogAnalyzer<>(regattaLog).analyze();
result.addAll(regattaLogProvidedCompetitors);
// else, don't add regatta log competitors because they have been added in each column already.
// The competitors are collected from the races. Those, however, will be the regatta log
// competitors if the race does not define its own.
@@ -280,7 +280,7 @@ public class DelegatingRegattaLeaderboardWithCompetitorElimination extends Abstr
return getFullLeaderboard().getMaxPointsReason(competitor, race, timePoint);
}
public Double getNetPoints(Competitor competitor, RaceColumn race, TimePoint timePoint) throws NoWindException {
public Double getNetPoints(Competitor competitor, RaceColumn race, TimePoint timePoint) {
return getFullLeaderboard().getNetPoints(competitor, race, timePoint);
}
Loaded 100 of 1539 files, more files were not shown because too many files have changed in this diff. Show more