mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-14 06:18:12 +00:00
Merge remote-tracking branch 'origin/master' into bug2822_boatcolors_merged_with_eol_changes
# Conflicts: # java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CompetitorProviderCacheInvalidationTest.java # java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/CompetitorProviderFromRaceColumnsAndRegattaLike.java
This commit is contained in:
commit
b3aba4bb73
241 files changed
+4260
-2797
No files matched your search
+2
-2
@@ -56,7 +56,7 @@ public class BravoFixTrackWithContext implements HasBravoFixTrackContext {
|
||||
end = endOfRace;
|
||||
}
|
||||
return bravoFixTrack.getDistanceSpentFoiling(
|
||||
getRaceOfCompetitorContext().getTrackedRaceContext().getTrackedRace().getTrack(getRaceOfCompetitorContext().getCompetitor()),
|
||||
startOfRace, end);
|
||||
startOfRace,
|
||||
end);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<projectDescription>
|
||||
<name>com.sap.sailing.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>
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
eclipse.preferences.version=1
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
|
||||
org.eclipse.jdt.core.compiler.source=1.8
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
eclipse.preferences.version=1
|
||||
pluginProject.extensions=false
|
||||
resolve.requirebundle=false
|
||||
@@ -0,0 +1,14 @@
|
||||
Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: 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
@@ -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>
|
||||
java/com.sap.sailing.domain.bravoadapter/src/com/sap/sailing/domain/bravoadapter/impl/Activator.java
Executable
+17
@@ -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 {
|
||||
}
|
||||
}
|
||||
+50
@@ -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";
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -25,6 +25,8 @@ 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),
|
||||
@@ -64,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),
|
||||
|
||||
+43
-2
@@ -141,7 +141,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;
|
||||
@@ -162,7 +162,7 @@ public class LeaderboardEntryDTO implements Serializable {
|
||||
return result;
|
||||
}
|
||||
|
||||
public Distance getDistanceTraveledInMeters() {
|
||||
public Distance getDistanceTraveled() {
|
||||
Distance result = null;
|
||||
if (legDetails != null) {
|
||||
for (LegEntryDTO legDetail : legDetails) {
|
||||
@@ -179,6 +179,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()) {
|
||||
|
||||
+41
-3
@@ -27,6 +27,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() {
|
||||
@@ -40,6 +42,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());
|
||||
@@ -94,6 +100,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;
|
||||
@@ -127,11 +143,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;
|
||||
}
|
||||
@@ -142,7 +158,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;
|
||||
}
|
||||
|
||||
+14
@@ -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;
|
||||
|
||||
+4
@@ -42,6 +42,10 @@ public enum BravoExtendedSensorDataMetadata implements ColumnMetadata {
|
||||
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;
|
||||
|
||||
+48
-86
@@ -59,6 +59,8 @@ 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;
|
||||
@@ -84,6 +86,7 @@ import com.sap.sailing.domain.racelogtracking.impl.fixtracker.FixLoaderAndTracke
|
||||
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;
|
||||
@@ -95,6 +98,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;
|
||||
@@ -186,18 +190,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);
|
||||
}
|
||||
|
||||
@@ -205,18 +203,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);
|
||||
}
|
||||
|
||||
@@ -224,18 +216,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);
|
||||
}
|
||||
|
||||
@@ -243,18 +229,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);
|
||||
}
|
||||
|
||||
@@ -262,18 +242,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);
|
||||
}
|
||||
|
||||
@@ -283,19 +257,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);
|
||||
}
|
||||
|
||||
@@ -305,18 +273,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);
|
||||
}
|
||||
|
||||
@@ -327,18 +289,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);
|
||||
}
|
||||
|
||||
@@ -350,23 +306,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);
|
||||
}
|
||||
|
||||
@@ -379,25 +328,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);
|
||||
}
|
||||
|
||||
@@ -422,20 +362,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);
|
||||
}
|
||||
|
||||
@@ -445,21 +379,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);
|
||||
}
|
||||
|
||||
@@ -469,18 +396,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());
|
||||
@@ -488,11 +409,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);
|
||||
}
|
||||
|
||||
@@ -509,7 +428,6 @@ public class SensorFixStoreAndLoadTest {
|
||||
}
|
||||
|
||||
private FixLoaderAndTracker createFixLoaderAndTracker() {
|
||||
|
||||
return new FixLoaderAndTracker(trackedRace, store, new SensorFixMapperFactory() {
|
||||
private TestDataFixMapper testDataFixMapper = new TestDataFixMapper();
|
||||
private BravoDataFixMapper bravoDataFixMapper = new BravoDataFixMapper();
|
||||
@@ -756,6 +674,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
|
||||
|
||||
+2
-1
@@ -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 BravoFixTrackImpl<Competitor>(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ false,
|
||||
race.getTrack(key)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-1
@@ -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 BravoFixTrackImpl<Competitor>(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ true,
|
||||
race.getTrack(key)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-1
@@ -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 BravoFixTrackImpl<Competitor>(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ true,
|
||||
race.getTrack(key)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+8
@@ -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);
|
||||
}
|
||||
}
|
||||
+5
-1
@@ -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
|
||||
|
||||
+27
@@ -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();
|
||||
|
||||
}
|
||||
+221
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+59
@@ -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();
|
||||
|
||||
+9
-7
@@ -213,27 +213,29 @@ public class CompetitorProviderCacheInvalidationTest extends AbstractLeaderboard
|
||||
for (Competitor c : compLists[1]) {
|
||||
raceLog.add(new RaceLogRegisterCompetitorAndBoatEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), 1, c, boats.get(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 RaceLogUseCompetitorsAndBoatsFromRaceLogEventImpl(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);
|
||||
|
||||
@@ -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;
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
+16
@@ -711,6 +711,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) {
|
||||
@@ -1196,4 +1206,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;
|
||||
}
|
||||
}
|
||||
+17
@@ -66,6 +66,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;
|
||||
@@ -373,6 +374,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;
|
||||
@@ -724,4 +735,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;
|
||||
}
|
||||
}
|
||||
+27
-1
@@ -277,7 +277,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
|
||||
@@ -489,6 +489,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
|
||||
@@ -576,4 +596,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);
|
||||
}
|
||||
+45
-6
@@ -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;
|
||||
@@ -662,8 +663,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() {
|
||||
@@ -841,6 +846,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();
|
||||
@@ -878,7 +889,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);
|
||||
@@ -1102,18 +1113,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
|
||||
@@ -1138,4 +1168,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;
|
||||
}
|
||||
}
|
||||
+2
-2
@@ -426,13 +426,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);
|
||||
|
||||
+4
-8
@@ -201,9 +201,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);
|
||||
@@ -212,12 +210,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 Map<Competitor, Boat> regattaLogProvidedCompetitorsAndBoats = new CompetitorsAndBoatsInLogAnalyzer<>(regattaLog).analyze();
|
||||
result.addAll(regattaLogProvidedCompetitorsAndBoats.keySet());
|
||||
}
|
||||
// If no race exists, the regatta log-provided competitor registrations will not have
|
||||
// been considered yet; add them:
|
||||
final Map<Competitor, Boat> regattaLogProvidedCompetitorsAndBoats = new CompetitorsAndBoatsInLogAnalyzer<>(regattaLog).analyze();
|
||||
result.addAll(regattaLogProvidedCompetitorsAndBoats.keySet());
|
||||
// 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.
|
||||
|
||||
+1
-1
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
+10
-3
@@ -177,9 +177,16 @@ public abstract class AbstractMetaLeaderboard extends AbstractSimpleLeaderboardI
|
||||
|
||||
@Override
|
||||
public int getTrackedRank(Competitor competitor, RaceColumn race, TimePoint timePoint) {
|
||||
final List<Competitor> competitorsFromBestToWorst = ((MetaLeaderboardColumn) race).getLeaderboard().getCompetitorsFromBestToWorst(timePoint);
|
||||
Util.removeAll(getSuppressedCompetitors(), competitorsFromBestToWorst);
|
||||
return competitorsFromBestToWorst.indexOf(competitor)+1;
|
||||
final Leaderboard leaderboard = ((MetaLeaderboardColumn) race).getLeaderboard();
|
||||
final int result;
|
||||
if (leaderboard.hasScores(competitor, timePoint)) {
|
||||
final List<Competitor> competitorsFromBestToWorst = leaderboard.getCompetitorsFromBestToWorst(timePoint);
|
||||
Util.removeAll(getSuppressedCompetitors(), competitorsFromBestToWorst);
|
||||
result = competitorsFromBestToWorst.indexOf(competitor)+1;
|
||||
} else {
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -2,12 +2,10 @@ package com.sap.sailing.domain.tracking;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.tracking.BravoExtendedFix;
|
||||
import com.sap.sailing.domain.common.tracking.BravoFix;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.WithID;
|
||||
@@ -73,7 +71,7 @@ public interface BravoFixTrack<ItemType extends WithID & Serializable> extends S
|
||||
/**
|
||||
* The distance sailed foiling during the time range provided
|
||||
*/
|
||||
Distance getDistanceSpentFoiling(GPSFixTrack<Competitor, GPSFixMoving> gpsFixTrack, TimePoint from, TimePoint to);
|
||||
Distance getDistanceSpentFoiling(TimePoint from, TimePoint to);
|
||||
|
||||
/**
|
||||
* Returns {@code true}, if the track contains {@link BravoExtendedFix} instances instead of simple {@link BravoFix BravoFixes}.
|
||||
|
||||
+6
-2
@@ -214,7 +214,7 @@ public interface TrackedLegOfCompetitor extends Serializable {
|
||||
* Same as {@link #getVelocityMadeGood(TimePoint, WindPositionMode)}, only that a cache for wind data and leg type and bearing
|
||||
* is passed.
|
||||
*/
|
||||
Speed getVelocityMadeGood(TimePoint at, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache);
|
||||
SpeedWithBearing getVelocityMadeGood(TimePoint at, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache);
|
||||
|
||||
|
||||
/**
|
||||
@@ -244,6 +244,11 @@ public interface TrackedLegOfCompetitor extends Serializable {
|
||||
Bearing getHeel(TimePoint at);
|
||||
|
||||
Bearing getPitch(TimePoint at);
|
||||
|
||||
Distance getDistanceFoiled(TimePoint at);
|
||||
|
||||
Duration getDurationFoiled(TimePoint at);
|
||||
|
||||
/**
|
||||
* Computes the distance along the wind track to the wind-projected position of the race's overall leader. If leader
|
||||
* and competitor are in the same leg, this is simply the windward distance. If the leader is already one or more
|
||||
@@ -264,7 +269,6 @@ public interface TrackedLegOfCompetitor extends Serializable {
|
||||
/**
|
||||
* Same as {@link #getWindwardDistanceToCompetitorFarthestAhead(TimePoint, WindPositionMode, RankingInfo)}, only that a cache for leg type
|
||||
* calculation is passed.
|
||||
* @param rankingInfo TODO
|
||||
*/
|
||||
Distance getWindwardDistanceToCompetitorFarthestAhead(TimePoint timePoint, WindPositionMode windPositionMode, RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache);
|
||||
|
||||
|
||||
@@ -47,6 +47,7 @@ import com.sap.sailing.domain.common.racelog.RacingProcedureType;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.common.tracking.SensorFix;
|
||||
import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache;
|
||||
import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator;
|
||||
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
|
||||
import com.sap.sailing.domain.polars.PolarDataService;
|
||||
@@ -612,7 +613,7 @@ public interface TrackedRace extends Serializable, IsManagedByCache<SharedDomain
|
||||
boolean notifyAboutGPSFixesAlreadyLoaded);
|
||||
|
||||
void removeListener(RaceChangeListener listener);
|
||||
|
||||
|
||||
/**
|
||||
* @return <code>null</code> if there are no mark passings for the <code>competitor</code> in this race
|
||||
* or if the competitor has not finished one of the legs in the race.
|
||||
@@ -624,6 +625,18 @@ public interface TrackedRace extends Serializable, IsManagedByCache<SharedDomain
|
||||
*/
|
||||
Distance getDistanceTraveledIncludingGateStart(Competitor competitor, TimePoint timePoint);
|
||||
|
||||
/**
|
||||
* @return <code>null</code> if there are no mark passings for the <code>competitor</code> in this race
|
||||
* or if the competitor has not finished one of the legs in the race.
|
||||
*/
|
||||
Distance getDistanceFoiled(Competitor competitor, TimePoint timePoint);
|
||||
|
||||
/**
|
||||
* @return <code>null</code> if there are no mark passings for the <code>competitor</code> in this race
|
||||
* or if the competitor has not finished one of the legs in the race.
|
||||
*/
|
||||
Duration getDurationFoiled(Competitor competitor, TimePoint timePoint);
|
||||
|
||||
/**
|
||||
* See {@link TrackedLegOfCompetitor#getWindwardDistanceToCompetitorFarthestAhead(TimePoint, WindPositionMode, RankingInfo)}
|
||||
*/
|
||||
@@ -1024,4 +1037,42 @@ public interface TrackedRace extends Serializable, IsManagedByCache<SharedDomain
|
||||
* time point up and until to compute the speed
|
||||
*/
|
||||
Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint);
|
||||
|
||||
/**
|
||||
* Computes the competitor's speed projected onto the wind (if wind data is available and the competitor is not
|
||||
* between start and finish (not racing) or not on a {@link LegType#REACHING reaching} leg; if outside a race and no
|
||||
* wind information is available, {@code null} is returned. Otherwise, the speed at {@code timePoint} is projected
|
||||
* onto the course. The wind direction at the {@link WindPositionMode#EXACT exact} competitor position at
|
||||
* {@code timePoint} is used for the calculation.
|
||||
*/
|
||||
default SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint) {
|
||||
return getVelocityMadeGood(competitor, timePoint, new LeaderboardDTOCalculationReuseCache(timePoint));
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #getVelocityMadeGood(Competitor, TimePoint)}, but allowing callers to specify a {@link WindPositionMode}
|
||||
* other than the default {@link WindPositionMode#EXACT}. If {@link WindPositionMode#LEG_MIDDLE} is used and the
|
||||
* competitor is not currently sailing on a leg (hasn't started or has already finished), {@code null} is returned.
|
||||
*/
|
||||
default SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) {
|
||||
return getVelocityMadeGood(competitor, timePoint, windPositionMode, new LeaderboardDTOCalculationReuseCache(timePoint));
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #getVelocityMadeGood(Competitor, TimePoint)}, but allowing callers to specify a cache that can
|
||||
* accelerate requests for wind directions, the leg type and the competitor's current leg's bearing.
|
||||
*/
|
||||
default SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
|
||||
return getVelocityMadeGood(competitor, timePoint, WindPositionMode.EXACT, cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #getVelocityMadeGood(Competitor, TimePoint)}, but allowing callers to specify a
|
||||
* {@link WindPositionMode} other than the default {@link WindPositionMode#EXACT} as well as a cache that can
|
||||
* accelerate requests for wind directions, the leg type and the competitor's current leg's bearing. If
|
||||
* {@link WindPositionMode#LEG_MIDDLE} is used and the competitor is not currently sailing on a leg (hasn't started
|
||||
* or has already finished), {@code null} is returned.
|
||||
*/
|
||||
SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode,
|
||||
WindLegTypeAndLegBearingCache cache);
|
||||
}
|
||||
+149
-51
@@ -1,9 +1,10 @@
|
||||
package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.io.Serializable;
|
||||
import java.util.function.Function;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.confidence.impl.ScalableDouble;
|
||||
@@ -15,6 +16,8 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.BravoFixTrack;
|
||||
import com.sap.sailing.domain.tracking.DynamicBravoFixTrack;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.GPSTrackListener;
|
||||
import com.sap.sailing.domain.tracking.Track;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
@@ -31,6 +34,37 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
|
||||
private static final long serialVersionUID = 460944392510182976L;
|
||||
|
||||
private final boolean hasExtendedFixes;
|
||||
|
||||
private transient TimeRangeCache<Duration> foilingTimeCache;
|
||||
|
||||
private transient TimeRangeCache<Distance> foilingDistanceCache;
|
||||
|
||||
private transient TimeRangeCache<Distance> averageRideHeightCache;
|
||||
|
||||
/**
|
||||
* If a GPS track was provided at construction time, remember it non-transiently. It is needed when restoring
|
||||
* the object after de-serialization, so the cache invalidation listener can be re-established.
|
||||
*/
|
||||
private GPSFixTrack<ItemType, GPSFixMoving> gpsTrack;
|
||||
|
||||
private class CacheInvalidationGpsTrackListener implements GPSTrackListener<ItemType, GPSFixMoving> {
|
||||
private static final long serialVersionUID = 6395529765232404414L;
|
||||
@Override
|
||||
public boolean isTransient() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void gpsFixReceived(GPSFixMoving fix, ItemType item, boolean firstFixInTrack) {
|
||||
assert item == getTrackedItem();
|
||||
foilingDistanceCache.invalidateAllAtOrLaterThan(fix.getTimePoint());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void speedAveragingChanged(long oldMillisecondsOverWhichToAverage,
|
||||
long newMillisecondsOverWhichToAverage) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param trackedItem
|
||||
@@ -39,8 +73,55 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
|
||||
* the name of the track by which it can be obtained from the {@link TrackedRace}.
|
||||
*/
|
||||
public BravoFixTrackImpl(ItemType trackedItem, String trackName, boolean hasExtendedFixes) {
|
||||
this(trackedItem, trackName, hasExtendedFixes, /* listen to GPS track for distance cache invalidation */ null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param gpsTrack
|
||||
* if not {@code null}, this track will listen on that track for changes in order to invalidate this
|
||||
* track's caches; for example, if a GPS fix is added, this track may need to invalidate its
|
||||
* {@link #foilingDistanceCache} accordingly because the distance traveled between the fixes may have
|
||||
* changed.
|
||||
*/
|
||||
public BravoFixTrackImpl(ItemType trackedItem, String trackName, boolean hasExtendedFixes, GPSFixTrack<ItemType, GPSFixMoving> gpsTrack) {
|
||||
super(trackedItem, trackName, BravoFixTrack.TRACK_NAME + " for " + trackedItem);
|
||||
this.hasExtendedFixes = hasExtendedFixes;
|
||||
this.foilingTimeCache = createFoilingTimeCache(trackedItem);
|
||||
this.foilingDistanceCache = createFoilingDistanceCache(trackedItem);
|
||||
this.averageRideHeightCache = createAverageRideHeightCache(trackedItem);
|
||||
this.gpsTrack = gpsTrack;
|
||||
if (gpsTrack != null) {
|
||||
gpsTrack.addListener(new CacheInvalidationGpsTrackListener());
|
||||
}
|
||||
}
|
||||
|
||||
private TimeRangeCache<Distance> createAverageRideHeightCache(ItemType trackedItem) {
|
||||
return createTimeRangeCache(trackedItem, "averageRideHeightCache");
|
||||
}
|
||||
|
||||
private <T> TimeRangeCache<T> createTimeRangeCache(ItemType trackedItem, final String cacheName) {
|
||||
return new TimeRangeCache<>(cacheName+" for "+trackedItem);
|
||||
}
|
||||
|
||||
private TimeRangeCache<Distance> createFoilingDistanceCache(ItemType trackedItem) {
|
||||
return createTimeRangeCache(trackedItem, "foilingDistanceCache");
|
||||
}
|
||||
|
||||
private TimeRangeCache<Duration> createFoilingTimeCache(ItemType trackedItem) {
|
||||
return createTimeRangeCache(trackedItem, "foilingTimeCache");
|
||||
}
|
||||
|
||||
/**
|
||||
* After reading this object from an {@link ObjectInputStream}, initialize the caches properly.
|
||||
*/
|
||||
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
|
||||
ois.defaultReadObject();
|
||||
this.foilingTimeCache = createFoilingTimeCache(getTrackedItem());
|
||||
this.foilingDistanceCache = createFoilingDistanceCache(getTrackedItem());
|
||||
this.averageRideHeightCache = createAverageRideHeightCache(getTrackedItem());
|
||||
if (gpsTrack != null) {
|
||||
gpsTrack.addListener(new CacheInvalidationGpsTrackListener());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -67,29 +148,43 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
|
||||
return rideHeight != null && rideHeight.compareTo(BravoFix.MIN_FOILING_HEIGHT_THRESHOLD) >= 0;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean add(BravoFix fix, boolean replace) {
|
||||
final boolean added = super.add(fix, replace);
|
||||
if (added) {
|
||||
final TimePoint fixTimePoint = fix.getTimePoint();
|
||||
averageRideHeightCache.invalidateAllAtOrLaterThan(fixTimePoint);
|
||||
foilingDistanceCache.invalidateAllAtOrLaterThan(fixTimePoint);
|
||||
foilingTimeCache.invalidateAllAtOrLaterThan(fixTimePoint);
|
||||
}
|
||||
return added;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageRideHeight(TimePoint from, TimePoint to) {
|
||||
final Distance result;
|
||||
lockForRead();
|
||||
try {
|
||||
Distance sum = Distance.NULL;
|
||||
int count = 0;
|
||||
for (final BravoFix fix : getFixes(from, true, to, true)) {
|
||||
final Distance rideHeight = fix.getRideHeight();
|
||||
if (rideHeight != null) {
|
||||
sum = sum.add(rideHeight);
|
||||
count++;
|
||||
return getValueSum(from, to, /* nullElement */ Distance.NULL, Distance::add, averageRideHeightCache,
|
||||
/* valueCalculator */ new Track.TimeRangeValueCalculator<Distance>() {
|
||||
@Override
|
||||
public Distance calculate(TimePoint from, TimePoint to) {
|
||||
Distance result;
|
||||
Distance sum = Distance.NULL;
|
||||
int count = 0;
|
||||
for (final BravoFix fix : getFixes(from, true, to, true)) {
|
||||
final Distance rideHeight = fix.getRideHeight();
|
||||
if (rideHeight != null) {
|
||||
sum = sum.add(rideHeight);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
if (count > 0) {
|
||||
result = sum.scale(1./count);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
if (count > 0) {
|
||||
result = sum.scale(1./count);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
} finally {
|
||||
unlockAfterRead();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
private boolean isFoiling(BravoFix fix) {
|
||||
@@ -98,44 +193,47 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
|
||||
|
||||
@Override
|
||||
public Duration getTimeSpentFoiling(TimePoint from, TimePoint to) {
|
||||
Duration result = Duration.NULL;
|
||||
lockForRead();
|
||||
try {
|
||||
TimePoint last = from;
|
||||
boolean isFoiling = false;
|
||||
for (final BravoFix fix : getFixes(from, true, to, true)) {
|
||||
final boolean fixFoils = isFoiling(fix);
|
||||
if (isFoiling && fixFoils) {
|
||||
result = result.plus(last.until(fix.getTimePoint()));
|
||||
return getValueSum(from, to, /* nullElement */ Duration.NULL, Duration::plus, foilingTimeCache,
|
||||
/* valueCalculator */ new Track.TimeRangeValueCalculator<Duration>() {
|
||||
@Override
|
||||
public Duration calculate(TimePoint from, TimePoint to) {
|
||||
Duration result = Duration.NULL;
|
||||
TimePoint last = from;
|
||||
boolean isFoiling = false;
|
||||
for (final BravoFix fix : getFixes(from, true, to, true)) {
|
||||
final boolean fixFoils = isFoiling(fix);
|
||||
if (isFoiling && fixFoils) {
|
||||
result = result.plus(last.until(fix.getTimePoint()));
|
||||
}
|
||||
last = fix.getTimePoint();
|
||||
isFoiling = fixFoils;
|
||||
}
|
||||
last = fix.getTimePoint();
|
||||
isFoiling = fixFoils;
|
||||
return result;
|
||||
}
|
||||
} finally {
|
||||
unlockAfterRead();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getDistanceSpentFoiling(GPSFixTrack<Competitor, GPSFixMoving> gpsFixTrack, TimePoint from, TimePoint to) {
|
||||
Distance result = Distance.NULL;
|
||||
lockForRead();
|
||||
try {
|
||||
TimePoint last = from;
|
||||
boolean isFoiling = false;
|
||||
for (final BravoFix fix : getFixes(from, true, to, true)) {
|
||||
final boolean fixFoils = isFoiling(fix);
|
||||
if (isFoiling && fixFoils) {
|
||||
result = result.add(gpsFixTrack.getDistanceTraveled(last, fix.getTimePoint()));
|
||||
public Distance getDistanceSpentFoiling(TimePoint from, TimePoint to) {
|
||||
assert gpsTrack != null;
|
||||
return getValueSum(from, to, /* nullElement */ Distance.NULL, Distance::add, foilingDistanceCache,
|
||||
/* valueCalculator */ new Track.TimeRangeValueCalculator<Distance>() {
|
||||
@Override
|
||||
public Distance calculate(TimePoint from, TimePoint to) {
|
||||
Distance result = Distance.NULL;
|
||||
TimePoint last = from;
|
||||
boolean isFoiling = false;
|
||||
for (final BravoFix fix : getFixes(from, true, to, true)) {
|
||||
final boolean fixFoils = isFoiling(fix);
|
||||
if (isFoiling && fixFoils) {
|
||||
result = result.add(gpsTrack.getDistanceTraveled(last, fix.getTimePoint()));
|
||||
}
|
||||
last = fix.getTimePoint();
|
||||
isFoiling = fixFoils;
|
||||
}
|
||||
last = fix.getTimePoint();
|
||||
isFoiling = fixFoils;
|
||||
return result;
|
||||
}
|
||||
} finally {
|
||||
unlockAfterRead();
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
-237
@@ -1,237 +0,0 @@
|
||||
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.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Computing {@link #getDistanceTraveled(TimePoint, TimePoint)} is more expensive the longer the track is and the
|
||||
* higher the tracking frequency. It is called regularly, usually for the same start time. Keeping to re-evaluate
|
||||
* all distances between all GPS fixes, even if they remain constant, is a waste of resources, particularly when we
|
||||
* assume that usually GPS fixes are appended to a track and not usually randomly inserted, even though this can
|
||||
* happen.
|
||||
* <p>
|
||||
*
|
||||
* This cache looks "backwards." It contains pairs whose first component represents a <code>to</code> parameter used
|
||||
* in {@link #getDistanceTraveled(TimePoint, TimePoint)}. 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
|
||||
* {@link #getDistanceTraveled(TimePoint, TimePoint)} and the second pair component represents the result of
|
||||
* {@link #getDistanceTraveled(TimePoint, TimePoint)} for this parameter combination. Note that the cache does only
|
||||
* store distances for <code>from</code> time points actually passed to
|
||||
* {@link #getDistanceTraveled(TimePoint, TimePoint)} and does not cache all interim combinations collected while
|
||||
* computing a result. As such, it differs from a typical "dynamic programming" algorithm which would store all
|
||||
* in-between values too. Assuming that <code>from</code> values are sparse (leg start, race start, ...), there
|
||||
* doesn't seem to be much use in storing all the other in-between values too.
|
||||
* <p>
|
||||
*
|
||||
* For implementation efficiency in combination with using a {@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 distances 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 this track, all distance 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 #getEarlierFixesWhoseValidityMayBeAffected(GPSFix) previous fix's} validity (track smoothing) and
|
||||
* therefore on its selection for distance 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 {@link #getDistanceTraveled(TimePoint, TimePoint)} is called, it first looks 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 distance values that is at or after the requested <code>from</code> time
|
||||
* point is determined. If such an entry exists, the distance 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 {@link #getDistanceTraveled(TimePoint, TimePoint)} 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 distance is computed by iterating the smoothened fixes.
|
||||
* <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 distance 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 distance is
|
||||
* computed by iterating backwards all the way to <code>from</code>.
|
||||
* <p>
|
||||
*
|
||||
* Once the {@link #getDistanceTraveled(TimePoint, TimePoint)} has computed its value, it adds the result to the
|
||||
* cache.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*/
|
||||
public class DistanceCache {
|
||||
public static final int MAX_SIZE = 100;
|
||||
|
||||
private final NavigableSet<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, Distance>>>> distanceCache;
|
||||
|
||||
/**
|
||||
* 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 #getEarliestFromAndDistanceAtOrAfterFrom(TimePoint, TimePoint)} or
|
||||
* having been added by {@link #cache(TimePoint, TimePoint, Distance)}.
|
||||
* <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 #distanceCache} 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 #distanceCache} 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 DistanceCache(String nameForLockLogging) {
|
||||
lock = new NamedReentrantReadWriteLock("lock for DistanceCache for "+nameForLockLogging, /* fair */ true);
|
||||
this.distanceCache = new ArrayListNavigableSet<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, Distance>>>>(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, Distance>>> entryForTo = distanceCache.floor(createDummy(to));
|
||||
if (entryForTo.getA().equals(to)) {
|
||||
Pair<TimePoint, Distance> entryForFrom = entryForTo.getB().ceiling(new Util.Pair<TimePoint, Distance>(from, null));
|
||||
if (entryForFrom.getA().equals(from)) {
|
||||
entryForTo.getB().remove(entryForFrom);
|
||||
if (entryForTo.getB().isEmpty()) {
|
||||
distanceCache.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/distance 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, Distance>> getEarliestFromAndDistanceAtOrAfterFrom(TimePoint from, TimePoint to) {
|
||||
LockUtil.lockForRead(lock);
|
||||
try {
|
||||
Util.Pair<TimePoint, Util.Pair<TimePoint, Distance>> result = null;
|
||||
Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, Distance>>> entryForTo = distanceCache.floor(createDummy(to));
|
||||
if (entryForTo != null) {
|
||||
final Util.Pair<TimePoint, Distance> fromCeiling = entryForTo.getB().ceiling(new Util.Pair<TimePoint, Distance>(from, null));
|
||||
if (fromCeiling != null) {
|
||||
result = new Util.Pair<TimePoint, Util.Pair<TimePoint, Distance>>(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, Distance>>> dummy = createDummy(timePoint);
|
||||
NavigableSet<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, Distance>>>> toRemove = distanceCache.tailSet(dummy, /* inclusive */ true);
|
||||
for (Iterator<Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, Distance>>>> i=toRemove.iterator(); i.hasNext(); ) {
|
||||
Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, Distance>>> entryToRemove = i.next();
|
||||
assert entryToRemove.getA().compareTo(timePoint) >= 0;
|
||||
for (Pair<TimePoint, Distance> fromAndDistance : entryToRemove.getB()) {
|
||||
lruCache.remove(new Util.Pair<>(fromAndDistance.getA(), entryToRemove.getA()));
|
||||
}
|
||||
i.remove();
|
||||
}
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(lock);
|
||||
}
|
||||
}
|
||||
|
||||
private NavigableSet<Util.Pair<TimePoint, Distance>> getEntryForTo(TimePoint to) {
|
||||
NavigableSet<Util.Pair<TimePoint, Distance>> result = null;
|
||||
Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, Distance>>> dummyForTo = createDummy(to);
|
||||
Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, Distance>>> entryForTo = distanceCache.floor(dummyForTo);
|
||||
if (entryForTo != null && entryForTo.getA().equals(to)) {
|
||||
result = entryForTo.getB();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public void cache(TimePoint from, TimePoint to, Distance distance) {
|
||||
LockUtil.lockForWrite(lock);
|
||||
try {
|
||||
NavigableSet<Util.Pair<TimePoint, Distance>> entryForTo = getEntryForTo(to);
|
||||
if (entryForTo == null) {
|
||||
entryForTo = new ArrayListNavigableSet<Util.Pair<TimePoint, Distance>>(timePointInPairComparator);
|
||||
Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, Distance>>> pairForTo = new Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, Distance>>>(
|
||||
to, entryForTo);
|
||||
distanceCache.add(pairForTo);
|
||||
}
|
||||
entryForTo.add(new Util.Pair<TimePoint, Distance>(from, distance));
|
||||
lruCache.put(new Util.Pair<TimePoint, TimePoint>(from, to), null);
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(lock);
|
||||
}
|
||||
}
|
||||
|
||||
private Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, Distance>>> createDummy(TimePoint to) {
|
||||
return new Util.Pair<TimePoint, NavigableSet<Util.Pair<TimePoint, Distance>>>(to, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all contents from this cache
|
||||
*/
|
||||
public void clear() {
|
||||
LockUtil.lockForWrite(lock);
|
||||
try {
|
||||
distanceCache.clear();
|
||||
lruCache.clear();
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(lock);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
+18
-60
@@ -38,6 +38,7 @@ import com.sap.sailing.domain.common.tracking.WithValidityCache;
|
||||
import com.sap.sailing.domain.common.tracking.impl.CompactPositionHelper;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.GPSTrackListener;
|
||||
import com.sap.sailing.domain.tracking.Track;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.TimeRange;
|
||||
@@ -116,7 +117,7 @@ public abstract class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends
|
||||
* Once the {@link #getDistanceTraveled(TimePoint, TimePoint)} has computed its value, it adds the result to the
|
||||
* cache.
|
||||
*/
|
||||
private transient DistanceCache distanceCache;
|
||||
private transient TimeRangeCache<Distance> distanceCache;
|
||||
|
||||
private transient MaxSpeedCache<ItemType, FixType> maxSpeedCache;
|
||||
|
||||
@@ -155,7 +156,7 @@ public abstract class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends
|
||||
this.millisecondsOverWhichToAverage = millisecondsOverWhichToAverage;
|
||||
this.maxSpeedForSmoothing = maxSpeedForSmoothening;
|
||||
this.listeners = new TrackListenerCollection<ItemType, FixType, GPSTrackListener<ItemType,FixType>>();
|
||||
this.distanceCache = new DistanceCache(trackedItem==null?"null":trackedItem.toString());
|
||||
this.distanceCache = new TimeRangeCache<>(trackedItem==null?"null":trackedItem.toString());
|
||||
this.maxSpeedCache = createMaxSpeedCache();
|
||||
this.validityCachingSuspended = false;
|
||||
this.losslessCompaction = losslessCompaction;
|
||||
@@ -190,7 +191,7 @@ public abstract class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends
|
||||
|
||||
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
|
||||
ois.defaultReadObject();
|
||||
distanceCache = new DistanceCache(getTrackedItem().toString());
|
||||
distanceCache = new TimeRangeCache<>(getTrackedItem().toString());
|
||||
maxSpeedCache = createMaxSpeedCache();
|
||||
}
|
||||
|
||||
@@ -506,76 +507,33 @@ public abstract class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends
|
||||
|
||||
@Override
|
||||
public Distance getDistanceTraveled(TimePoint from, TimePoint to) {
|
||||
return getDistanceTraveledRecursively(from, to, 0);
|
||||
}
|
||||
|
||||
private Distance getDistanceTraveledRecursively(TimePoint from, TimePoint to, int recursionDepth) {
|
||||
Distance result;
|
||||
if (!from.before(to)) {
|
||||
result = Distance.NULL;
|
||||
} else {
|
||||
boolean perfectCacheHit = false;
|
||||
lockForRead();
|
||||
try {
|
||||
Util.Pair<TimePoint, Util.Pair<TimePoint, Distance>> bestCacheEntry = getDistanceCache()
|
||||
.getEarliestFromAndDistanceAtOrAfterFrom(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"
|
||||
Distance distanceFromFromToBeginningOfCacheEntry = Distance.NULL;
|
||||
Distance distanceFromEndOfCacheEntryToTo = Distance.NULL;
|
||||
if (!bestCacheEntry.getB().getA().equals(from)) {
|
||||
assert bestCacheEntry.getB().getA().after(from);
|
||||
perfectCacheHit = false;
|
||||
distanceFromFromToBeginningOfCacheEntry = getDistanceTraveledRecursively(from, bestCacheEntry
|
||||
.getB().getA(), recursionDepth + 1);
|
||||
}
|
||||
if (!bestCacheEntry.getA().equals(to)) {
|
||||
assert bestCacheEntry.getA().before(to);
|
||||
perfectCacheHit = false;
|
||||
distanceFromEndOfCacheEntryToTo = getDistanceTraveledRecursively(bestCacheEntry.getA(), to,
|
||||
recursionDepth + 1);
|
||||
}
|
||||
result = distanceFromFromToBeginningOfCacheEntry.add(bestCacheEntry.getB().getB()).add(
|
||||
distanceFromEndOfCacheEntryToTo);
|
||||
} else {
|
||||
double distanceInNauticalMiles = 0;
|
||||
if (from.compareTo(to) < 0) {
|
||||
return getValueSum(from, to, /* nullElement */ Distance.NULL, Distance::add, getDistanceCache(),
|
||||
/* valueCalculator */ new Track.TimeRangeValueCalculator<Distance>() {
|
||||
@Override
|
||||
public Distance calculate(TimePoint from, TimePoint to) {
|
||||
// getEstimatedPosition's current implementation returns a position equal to that of a fix at
|
||||
// "from" if there is one with exactly that time stamp
|
||||
Distance distance = Distance.NULL;
|
||||
Position fromPos = getEstimatedPosition(from, /* extrapolate */false);
|
||||
if (fromPos == null) {
|
||||
result = Distance.NULL;
|
||||
} else {
|
||||
if (fromPos != null) {
|
||||
// TODO bug 4122: idea: use the subset to determine fromPos and toPos in one go, eliminating two expensive getEstimatedPosition calls
|
||||
NavigableSet<GPSFix> subset = getGPSFixes().subSet(new DummyGPSFix(from),
|
||||
/* fromInclusive */false, new DummyGPSFix(to),
|
||||
/* toInclusive */false);
|
||||
for (GPSFix fix : subset) {
|
||||
double distanceBetweenAdjacentFixesInNauticalMiles = fromPos.getDistance(
|
||||
fix.getPosition()).getNauticalMiles();
|
||||
distanceInNauticalMiles += distanceBetweenAdjacentFixesInNauticalMiles;
|
||||
Distance distanceBetweenAdjacentFixes = fromPos.getDistance(
|
||||
fix.getPosition());
|
||||
distance = distance.add(distanceBetweenAdjacentFixes);
|
||||
fromPos = fix.getPosition();
|
||||
}
|
||||
Position toPos = getEstimatedPosition(to, false);
|
||||
distanceInNauticalMiles += fromPos.getDistance(toPos).getNauticalMiles();
|
||||
result = new NauticalMileDistance(distanceInNauticalMiles);
|
||||
distance = distance.add(fromPos.getDistance(toPos));
|
||||
}
|
||||
} else {
|
||||
result = Distance.NULL;
|
||||
return distance;
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
unlockAfterRead();
|
||||
}
|
||||
if (!perfectCacheHit && recursionDepth == 0) {
|
||||
getDistanceCache().cache(from, to, result);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Distance getRawDistanceTraveled(TimePoint from, TimePoint to) {
|
||||
lockForRead();
|
||||
@@ -1065,7 +1023,7 @@ public abstract class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends
|
||||
}
|
||||
}
|
||||
|
||||
protected DistanceCache getDistanceCache() {
|
||||
protected TimeRangeCache<Distance> getDistanceCache() {
|
||||
return distanceCache;
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -44,7 +44,6 @@ import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackedRaceStatus;
|
||||
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
|
||||
import com.sap.sailing.domain.tracking.WindPositionMode;
|
||||
@@ -98,7 +97,7 @@ public class TrackedLegImpl implements TrackedLeg {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackedRace getTrackedRace() {
|
||||
public TrackedRaceImpl getTrackedRace() {
|
||||
return trackedRace;
|
||||
}
|
||||
|
||||
|
||||
+51
-18
@@ -30,7 +30,6 @@ import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
|
||||
import com.sap.sailing.domain.tracking.WindPositionMode;
|
||||
import com.sap.sse.common.Duration;
|
||||
@@ -76,7 +75,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
return trackedLeg.getLeg();
|
||||
}
|
||||
|
||||
private TrackedRace getTrackedRace() {
|
||||
private TrackedRaceImpl getTrackedRace() {
|
||||
return getTrackedLeg().getTrackedRace();
|
||||
}
|
||||
|
||||
@@ -276,29 +275,28 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
|
||||
/**
|
||||
* Projects <code>speed</code> onto the wind direction for upwind/downwind legs to see how fast a boat travels
|
||||
* "along the wind's direction." For reaching legs (neither upwind nor downwind), the speed is projected onto
|
||||
* the leg's direction.
|
||||
* @param windPositionMode see {@link #getWind(Position, TimePoint, Set)}
|
||||
* "along the wind's direction." For reaching legs (neither upwind nor downwind), the speed is projected onto the
|
||||
* leg's direction.
|
||||
*
|
||||
* @throws NoWindException in case the wind direction is not known
|
||||
* @param speed
|
||||
* if {@code null} then {@code null} will be returned
|
||||
* @param windPositionMode
|
||||
* see {@link #getWind(Position, TimePoint, Set)}
|
||||
*
|
||||
* @throws NoWindException
|
||||
* in case the wind direction is not known
|
||||
*/
|
||||
private SpeedWithBearing getWindwardSpeed(SpeedWithBearing speed, final TimePoint at, WindPositionMode windPositionMode,
|
||||
WindLegTypeAndLegBearingCache cache) {
|
||||
SpeedWithBearing result = null;
|
||||
final SpeedWithBearing result;
|
||||
if (speed != null) {
|
||||
Bearing projectToBearing;
|
||||
try {
|
||||
if (cache.getLegType(getTrackedLeg(), at) != LegType.REACHING) {
|
||||
final Wind wind;
|
||||
if (windPositionMode == WindPositionMode.EXACT) {
|
||||
wind = cache.getWind(getTrackedRace(), getCompetitor(), at);
|
||||
} else {
|
||||
wind = getTrackedRace().getWind(
|
||||
getTrackedLeg().getEffectiveWindPosition(
|
||||
() -> getTrackedRace().getTrack(getCompetitor())
|
||||
.getEstimatedPosition(at, false), at, windPositionMode), at);
|
||||
}
|
||||
final Wind wind = getTrackedRace().getWind(windPositionMode, getTrackedLeg(), getCompetitor(), at, cache);
|
||||
if (wind == null) {
|
||||
// This is not really likely to happen because wind==null would have let the call
|
||||
// to cache.getLegType(...) fail with a NoWindException
|
||||
throw new NoWindException("Need at least wind direction to determine windward speed");
|
||||
}
|
||||
projectToBearing = wind.getBearing();
|
||||
@@ -315,7 +313,11 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
projectToBearing = projectToBearing.reverse();
|
||||
}
|
||||
result = new KnotSpeedWithBearingImpl(Math.abs(speed.getKnots() * cos), projectToBearing);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -653,7 +655,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Speed getVelocityMadeGood(TimePoint at, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) {
|
||||
public SpeedWithBearing getVelocityMadeGood(TimePoint at, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) {
|
||||
if (hasStartedLeg(at)) {
|
||||
TimePoint timePoint;
|
||||
if (hasFinishedLeg(at)) {
|
||||
@@ -717,7 +719,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
public Distance getRideHeight(TimePoint at) {
|
||||
final Distance result;
|
||||
if (hasStartedLeg(at)) {
|
||||
TimePoint timePoint =hasFinishedLeg(at) ? getMarkPassingForLegEnd().getTimePoint() : at;
|
||||
TimePoint timePoint = hasFinishedLeg(at) ? getMarkPassingForLegEnd().getTimePoint() : at;
|
||||
BravoFixTrack<Competitor> track = getTrackedRace()
|
||||
.<BravoFix, BravoFixTrack<Competitor>> getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
|
||||
result = track == null ? null : track.getRideHeight(timePoint);
|
||||
@@ -727,6 +729,37 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getDistanceFoiled(TimePoint at) {
|
||||
final Distance result;
|
||||
if (hasStartedLeg(at)) {
|
||||
TimePoint timePoint = hasFinishedLeg(at) ? getMarkPassingForLegEnd().getTimePoint() : at;
|
||||
BravoFixTrack<Competitor> track = getTrackedRace()
|
||||
.<BravoFix, BravoFixTrack<Competitor>> getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
|
||||
result = track == null ? null
|
||||
: track.getDistanceSpentFoiling(getMarkPassingForLegStart().getTimePoint(),
|
||||
timePoint);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getDurationFoiled(TimePoint at) {
|
||||
final Duration result;
|
||||
if (hasStartedLeg(at)) {
|
||||
TimePoint timePoint = hasFinishedLeg(at) ? getMarkPassingForLegEnd().getTimePoint() : at;
|
||||
BravoFixTrack<Competitor> track = getTrackedRace()
|
||||
.<BravoFix, BravoFixTrack<Competitor>> getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
|
||||
result = track == null ? null
|
||||
: track.getTimeSpentFoiling(getMarkPassingForLegStart().getTimePoint(), timePoint);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getEstimatedTimeToNextMark(TimePoint timePoint, WindPositionMode windPositionMode) {
|
||||
return getEstimatedTimeToNextMark(timePoint, windPositionMode, new LeaderboardDTOCalculationReuseCache(timePoint));
|
||||
|
||||
+105
-11
@@ -1213,7 +1213,50 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
}
|
||||
|
||||
private Distance getDistanceTraveled(Competitor competitor, TimePoint timePoint, boolean considerGateStart) {
|
||||
final Distance result;
|
||||
return getValueFromStartToTimePointOrEnd(competitor, timePoint,
|
||||
(from, to)->{
|
||||
final Distance result;
|
||||
final Distance preResult = getTrack(competitor).getDistanceTraveled(from, to);
|
||||
if (considerGateStart && preResult != null) {
|
||||
result = preResult.add(getAdditionalGateStartDistance(competitor, timePoint));
|
||||
} else {
|
||||
result = preResult;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getDistanceFoiled(Competitor competitor, TimePoint timePoint) {
|
||||
return getBravoValue(competitor, timePoint, BravoFixTrack::getDistanceSpentFoiling);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getDurationFoiled(Competitor competitor, TimePoint timePoint) {
|
||||
return getBravoValue(competitor, timePoint, BravoFixTrack::getTimeSpentFoiling);
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
private static interface BravoFromToValueCalculator<T> {
|
||||
T getValue(BravoFixTrack<Competitor> bravoFixTrack, TimePoint from, TimePoint to);
|
||||
}
|
||||
|
||||
private <T> T getBravoValue(Competitor competitor, TimePoint timePoint, BravoFromToValueCalculator<T> bravoValueCalculator) {
|
||||
return getValueFromStartToTimePointOrEnd(competitor, timePoint,
|
||||
(from, to)->{
|
||||
final T result;
|
||||
final BravoFixTrack<Competitor> bravoFixTrack = getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
|
||||
if (bravoFixTrack != null) {
|
||||
result = bravoValueCalculator.getValue(bravoFixTrack, from, to);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}
|
||||
|
||||
private <T> T getValueFromStartToTimePointOrEnd(Competitor competitor, TimePoint timePoint, Track.TimeRangeValueCalculator<T> valueCalculator) {
|
||||
final T result;
|
||||
NavigableSet<MarkPassing> markPassings = getMarkPassings(competitor);
|
||||
try {
|
||||
lockForRead(markPassings);
|
||||
@@ -1240,12 +1283,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
if (end == null) {
|
||||
result = null;
|
||||
} else {
|
||||
final Distance preResult = getTrack(competitor).getDistanceTraveled(markPassings.first().getTimePoint(), end);
|
||||
if (considerGateStart && preResult != null) {
|
||||
result = preResult.add(getAdditionalGateStartDistance(competitor, timePoint));
|
||||
} else {
|
||||
result = preResult;
|
||||
}
|
||||
result = valueCalculator.calculate(markPassings.first().getTimePoint(), end);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -3576,12 +3614,14 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
|
||||
@Override
|
||||
public Speed getSpeed(Competitor competitor, long millisecondsBeforeRaceStart) {
|
||||
final Speed result;
|
||||
if (getStartOfRace() == null) {
|
||||
return null;
|
||||
result = null;
|
||||
} else {
|
||||
TimePoint beforeStart = new MillisecondsTimePoint(getStartOfRace().asMillis() - millisecondsBeforeRaceStart);
|
||||
result = getTrack(competitor).getEstimatedSpeed(beforeStart);
|
||||
}
|
||||
|
||||
TimePoint beforeStart = new MillisecondsTimePoint(getStartOfRace().asMillis() - millisecondsBeforeRaceStart);
|
||||
return getTrack(competitor).getEstimatedSpeed(beforeStart);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -4213,4 +4253,58 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint,
|
||||
WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) {
|
||||
TrackedLegOfCompetitor trackedLeg = getTrackedLeg(competitor, timePoint);
|
||||
final SpeedWithBearing result;
|
||||
if (trackedLeg != null) {
|
||||
result = trackedLeg.getVelocityMadeGood(timePoint, windPositionMode, cache);
|
||||
} else {
|
||||
// check if wind information is available; if so, compute a VMG only based on wind data:
|
||||
if (windPositionMode == WindPositionMode.LEG_MIDDLE) {
|
||||
result = null;
|
||||
} else {
|
||||
final Wind wind = getWind(windPositionMode, /* trackedLeg */ null, competitor, timePoint, cache);
|
||||
result = projectOnto(getTrack(competitor).getEstimatedSpeed(timePoint), wind.getBearing());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
SpeedWithBearing projectOnto(SpeedWithBearing speed, Bearing projectToBearing) {
|
||||
final SpeedWithBearing result;
|
||||
if (speed != null && speed.getBearing() != null && projectToBearing != null) {
|
||||
double cos = Math.cos(speed.getBearing().getRadians() - projectToBearing.getRadians());
|
||||
if (cos < 0) {
|
||||
projectToBearing = projectToBearing.reverse();
|
||||
}
|
||||
result = new KnotSpeedWithBearingImpl(Math.abs(speed.getKnots() * cos), projectToBearing);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param trackedLeg
|
||||
* The caller is expected to obtain the {@link #getTrackedLeg(Competitor, TimePoint) tracked leg} the
|
||||
* {@code competitor} is sailing in at time point {@code at}. If {@code null}, any
|
||||
* non-{@link WindPositionMode#EXACT exact} wind position mode will use {@code null} for the wind
|
||||
* position, defaulting to the "COMBINED" wind source at the middle of the course. In particular, it then
|
||||
* obviously makes no real sense to request {@link WindPositionMode#LEG_MIDDLE} because no leg is known.
|
||||
*/
|
||||
Wind getWind(WindPositionMode windPositionMode, TrackedLegImpl trackedLeg, Competitor competitor, TimePoint at, WindLegTypeAndLegBearingCache cache) {
|
||||
final Wind wind;
|
||||
if (windPositionMode == WindPositionMode.EXACT) {
|
||||
wind = cache.getWind(this, competitor, at);
|
||||
} else {
|
||||
wind = getWind(trackedLeg == null ? null :
|
||||
trackedLeg.getEffectiveWindPosition(
|
||||
() -> getTrack(competitor)
|
||||
.getEstimatedPosition(at, false), at, windPositionMode), at);
|
||||
}
|
||||
return wind;
|
||||
}
|
||||
}
|
||||
@@ -37,6 +37,7 @@
|
||||
<plugin id="com.sap.sailing.barbados.resultimport" autoStart="true" startLevel="3" />
|
||||
<plugin id="com.sap.sailing.dashboards.gwt" autoStart="true" startLevel="5" />
|
||||
<plugin id="com.sap.sailing.datamining" autoStart="true" startLevel="4" />
|
||||
<plugin id="com.sap.sailing.domain.bravoadapter" autoStart="true" startLevel="4" />
|
||||
<plugin id="com.sap.sailing.domain.deckmanadapter" autoStart="true" startLevel="4" />
|
||||
<plugin id="com.sap.sailing.domain.expeditionadapter" autoStart="true" startLevel="4" />
|
||||
<plugin id="com.sap.sailing.domain.igtimiadapter" autoStart="true" startLevel="3" />
|
||||
|
||||
@@ -416,4 +416,11 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al
|
||||
version="0.0.0"
|
||||
unpack="false"/>
|
||||
|
||||
<plugin
|
||||
id="com.sap.sailing.domain.bravoadapter"
|
||||
download-size="0"
|
||||
install-size="0"
|
||||
version="0.0.0"
|
||||
unpack="false"/>
|
||||
|
||||
</feature>
|
||||
+17
@@ -71,6 +71,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.WindTrackImpl;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.IsManagedByCache;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
@@ -407,6 +408,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) {
|
||||
return null;
|
||||
@@ -890,4 +901,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;
|
||||
}
|
||||
}
|
||||
@@ -42,6 +42,7 @@
|
||||
<inherits name='com.sap.sailing.gwt.ui.SourceActions'/>
|
||||
<inherits name='com.sap.sailing.gwt.ui.SourceSimulator'/>
|
||||
<inherits name="com.sap.sailing.gwt.ui.SourceAdminConsole"/>
|
||||
<!-- TODO: probably only used for flag images. Move RegattaRaceStatesFlagsResources to shared -->
|
||||
<inherits name="com.sap.sailing.gwt.regattaoverview.SourceRegattaOverview"/>
|
||||
<inherits name="com.sap.sailing.gwt.ui.SourceLeaderboard"/>
|
||||
<inherits name="com.sap.sailing.gwt.ui.SourceRaceboard"/>
|
||||
|
||||
+11
@@ -13,6 +13,17 @@
|
||||
state of the currently opened page to get lost.
|
||||
After a sign out (and another page reload) the language preference will stop to take effect.
|
||||
</li>
|
||||
<li>There are additional column types in the leaderboard that show per leg, per race and overall statistics
|
||||
for distance foiled and duration foiled. This allows for quick competitor comparisons regarding these
|
||||
figures which are so important for any foiling boat class. Note, that this feature is also available
|
||||
on overall leaderboards, providing foiling stats across an entire season or event series.
|
||||
</li>
|
||||
<li>When a leaderboard belongs to a leaderboard group with an overall (series) leaderboard and the
|
||||
leaderboard has no scores at all yet for a competitor, that competitor won't be assigned a score
|
||||
in the overall (series) leaderboard either. This way, when an act or event has its leaderboard already
|
||||
linked to the series leaderboard group, no scores will show in the series leaderboard before the
|
||||
act/event starts producing scores for the competitors.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h5 class="articleSubheadline">October 2017</h5>
|
||||
|
||||
+3
-3
@@ -48,17 +48,17 @@ public abstract class LeaderboardSettings extends AbstractGenericSerializableSet
|
||||
@Override
|
||||
protected void addChildSettings() {
|
||||
isShowCompetitorNationality = new BooleanSetting("showCompetitorNationality", this, false);
|
||||
List<DetailType> maneuverDetails = new ArrayList<DetailType>();
|
||||
List<DetailType> maneuverDetails = new ArrayList<>();
|
||||
maneuverDetails.add(DetailType.TACK);
|
||||
maneuverDetails.add(DetailType.JIBE);
|
||||
maneuverDetails.add(DetailType.PENALTY_CIRCLE);
|
||||
maneuverDetailsToShow = new EnumSetSetting<>("maneuverDetailsToShow", this, maneuverDetails, DetailType::valueOf);
|
||||
List<DetailType> legDetails = new ArrayList<DetailType>();
|
||||
List<DetailType> legDetails = new ArrayList<>();
|
||||
legDetails.add(DetailType.DISTANCE_TRAVELED);
|
||||
legDetails.add(DetailType.AVERAGE_SPEED_OVER_GROUND_IN_KNOTS);
|
||||
legDetails.add(DetailType.RANK_GAIN);
|
||||
legDetailsToShow = new EnumSetSetting<>("legDetailsToShow", this, legDetails, DetailType::valueOf);
|
||||
List<DetailType> raceDetails = new ArrayList<DetailType>();
|
||||
List<DetailType> raceDetails = new ArrayList<>();
|
||||
raceDetails.add(DetailType.DISPLAY_LEGS);
|
||||
raceDetailsToShow = new EnumSetSetting<>("raceDetailsToShow", this, raceDetails, DetailType::valueOf);
|
||||
List<DetailType> overallDetails = new ArrayList<>();
|
||||
|
||||
+7
@@ -12,6 +12,7 @@ import com.google.gwt.uibinder.client.UiHandler;
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.CheckBox;
|
||||
import com.google.gwt.user.client.ui.Composite;
|
||||
import com.google.gwt.user.client.ui.FileUpload;
|
||||
import com.google.gwt.user.client.ui.FormPanel;
|
||||
@@ -35,6 +36,8 @@ public abstract class AbstractFileImportWidget extends Composite {
|
||||
@UiField FileUpload fileUploadUi;
|
||||
@UiField ListBox preferredImporterUi;
|
||||
@UiField Button importButtonUi;
|
||||
@UiField
|
||||
CheckBox downsampleUi;
|
||||
@UiField Image loadingImageUi;
|
||||
@UiField FormPanel formPanelUi;
|
||||
private final TrackFileImportDeviceIdentifierTableWrapper table;
|
||||
@@ -72,6 +75,10 @@ public abstract class AbstractFileImportWidget extends Composite {
|
||||
formPanelUi.submit();
|
||||
}
|
||||
|
||||
protected void setDownsampleOptionVisible(boolean visible) {
|
||||
downsampleUi.setVisible(visible);
|
||||
}
|
||||
|
||||
@UiHandler("formPanelUi")
|
||||
void onFileImportComplete(SubmitCompleteEvent event) {
|
||||
if (shouldClearListOnNewUploadComplete) {
|
||||
|
||||
+1
@@ -22,6 +22,7 @@
|
||||
<g:HTMLPanel addStyleNames="{style.formPanelContent}">
|
||||
<g:FileUpload name="file" ui:field="fileUploadUi"/>
|
||||
<g:ListBox name="preferredImporter" ui:field="preferredImporterUi" height="21px"/>
|
||||
<g:CheckBox name="downsample" checked="true" ui:field="downsampleUi">Downsample</g:CheckBox>
|
||||
<g:Button styleName="" ui:field="importButtonUi"/>
|
||||
<g:Image resource="{res.transparentGif}" addStyleNames="{style.loadingImage}" ui:field="loadingImageUi"/>
|
||||
</g:HTMLPanel>
|
||||
|
||||
+4
@@ -51,6 +51,10 @@ interface AdminConsoleResources extends ClientBundle {
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/clock.png")
|
||||
ImageResource clockIcon();
|
||||
|
||||
// Smaller variant of RegattaRaceStatesFlagsResources.flagBlue to solve layouting issues
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/blue_small.png")
|
||||
ImageResource blueSmall();
|
||||
|
||||
@Source("com/sap/sailing/gwt/ui/client/images/flag_blue.png")
|
||||
ImageResource flagIcon();
|
||||
|
||||
+47
-15
@@ -61,9 +61,11 @@ import com.sap.sailing.gwt.ui.client.shared.controls.SelectionCheckboxColumn;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardEntryPoint;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.ScoringSchemeTypeFormatter;
|
||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceLogSetFinishingAndFinishTimeDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceLogSetStartTimeAndProcedureDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.common.util.NaturalComparator;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.async.MarkedAsyncCallback;
|
||||
@@ -473,6 +475,8 @@ TrackedRaceChangedListener, LeaderboardsDisplayer {
|
||||
refreshRaceLog(object.getA(), object.getB(), true);
|
||||
} else if (LeaderboardRaceConfigImagesBarCell.ACTION_SET_STARTTIME.equals(value)) {
|
||||
setStartTime(object.getA(), object.getB());
|
||||
} else if (LeaderboardRaceConfigImagesBarCell.ACTION_SET_FINISHING_AND_FINISH_TIME.equals(value)) {
|
||||
setEndTime(object.getA(), object.getB());
|
||||
} else if (LeaderboardRaceConfigImagesBarCell.ACTION_SHOW_RACELOG.equals(value)) {
|
||||
showRaceLog(object.getA(), object.getB());
|
||||
} else if (LeaderboardRaceConfigImagesBarCell.ACTION_EDIT_COMPETITOR_TO_BOAT_MAPPINGS.equals(value)) {
|
||||
@@ -615,28 +619,56 @@ TrackedRaceChangedListener, LeaderboardsDisplayer {
|
||||
}
|
||||
|
||||
private void setStartTime(RaceColumnDTO raceColumnDTO, FleetDTO fleetDTO) {
|
||||
new SetStartTimeDialog(sailingService, errorReporter, getSelectedLeaderboardName(), raceColumnDTO.getName(),
|
||||
new SetStartTimeDialog(sailingService, errorReporter, getSelectedLeaderboardName(), raceColumnDTO.getName(),
|
||||
fleetDTO.getName(), stringMessages, new DialogCallback<RaceLogSetStartTimeAndProcedureDTO>() {
|
||||
@Override
|
||||
public void ok(RaceLogSetStartTimeAndProcedureDTO editedObject) {
|
||||
sailingService.setStartTimeAndProcedure(editedObject, new AsyncCallback<Boolean>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(caught.getMessage());
|
||||
public void ok(RaceLogSetStartTimeAndProcedureDTO editedObject) {
|
||||
sailingService.setStartTimeAndProcedure(editedObject, new AsyncCallback<Boolean>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(caught.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Boolean result) {
|
||||
if (!result) {
|
||||
Window.alert(stringMessages.failedToSetNewStartTime());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Boolean result) {
|
||||
if (!result) {
|
||||
Window.alert(stringMessages.failedToSetNewStartTime());
|
||||
}
|
||||
public void cancel() {
|
||||
}
|
||||
});
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void setEndTime(RaceColumnDTO raceColumnDTO, FleetDTO fleetDTO) {
|
||||
new SetFinishingAndFinishedTimeDialog(sailingService, errorReporter, getSelectedLeaderboardName(), raceColumnDTO.getName(),
|
||||
fleetDTO.getName(), stringMessages, new DialogCallback<RaceLogSetFinishingAndFinishTimeDTO>() {
|
||||
@Override
|
||||
public void ok(RaceLogSetFinishingAndFinishTimeDTO editedObject) {
|
||||
sailingService.setFinishingAndEndTime(editedObject, new AsyncCallback<Pair<Boolean, Boolean>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(caught.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() { }
|
||||
}).show();
|
||||
@Override
|
||||
public void onSuccess(Pair<Boolean, Boolean> result) {
|
||||
if (!result.getA() || !result.getB()) {
|
||||
Window.alert(stringMessages.failedToSetNewFinishingAndFinishTime());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void removeRaceColumn(final RaceColumnDTO raceColumnDTO) {
|
||||
|
||||
+2
@@ -15,6 +15,7 @@ public class LeaderboardRaceConfigImagesBarCell extends ImagesBarCell {
|
||||
public final static String ACTION_EDIT = "ACTION_EDIT";
|
||||
public final static String ACTION_REFRESH_RACELOG = "ACTION_REFRESH_RACE_LOG";
|
||||
public final static String ACTION_SET_STARTTIME = "ACTION_SET_STARTTIME";
|
||||
public final static String ACTION_SET_FINISHING_AND_FINISH_TIME = "ACTION_SET_FINISHING_AND_FINISH_TIME";
|
||||
public final static String ACTION_SHOW_RACELOG = "ACTION_SHOW_RACELOG";
|
||||
public final static String ACTION_EDIT_COMPETITOR_TO_BOAT_MAPPINGS = "ACTION_EDIT_COMPETITOR_TO_BOAT_MAPPINGS";
|
||||
|
||||
@@ -40,6 +41,7 @@ public class LeaderboardRaceConfigImagesBarCell extends ImagesBarCell {
|
||||
}
|
||||
result.add(new ImageSpec(ACTION_REFRESH_RACELOG, stringMessages.refreshRaceLog(), makeImagePrototype(resources.reloadIcon())));
|
||||
result.add(new ImageSpec(ACTION_SET_STARTTIME, stringMessages.setStartTime(), makeImagePrototype(resources.clockIcon())));
|
||||
result.add(new ImageSpec(ACTION_SET_FINISHING_AND_FINISH_TIME, stringMessages.setFinishingAndFinishTime(), makeImagePrototype(resources.blueSmall())));
|
||||
result.add(new ImageSpec(ACTION_SHOW_RACELOG, stringMessages.raceLog(), makeImagePrototype(resources.flagIcon())));
|
||||
|
||||
if (selectedLeaderboardProvider.getSelectedLeaderboard().canBoatsOfCompetitorsChangePerRace) {
|
||||
|
||||
+2
@@ -19,6 +19,7 @@ public class RaceLogTrackingEventManagementRaceImagesBarCell extends ImagesBarCe
|
||||
public final static String ACTION_EDIT = "ACTION_EDIT";
|
||||
public final static String ACTION_REFRESH_RACELOG = "ACTION_REFRESH_RACE_LOG";
|
||||
public final static String ACTION_SET_STARTTIME = "ACTION_SET_STARTTIME";
|
||||
public final static String ACTION_SET_FINISHING_AND_FINISH_TIME = "ACTION_SET_FINISHING_AND_FINISH_TIME";
|
||||
public final static String ACTION_SHOW_RACELOG = "ACTION_SHOW_RACELOG";
|
||||
public final static String ACTION_SET_TRACKING_TIMES = "ACTION_SET_TRACKING_TIMES";
|
||||
public final static String ACTION_STOP_TRACKING = "ACTION_STOP_TRACKING";
|
||||
@@ -50,6 +51,7 @@ public class RaceLogTrackingEventManagementRaceImagesBarCell extends ImagesBarCe
|
||||
result.add(new ImageSpec(ACTION_EDIT, stringMessages.actionEdit(), makeImagePrototype(resources.editIcon())));
|
||||
result.add(new ImageSpec(ACTION_REFRESH_RACELOG, stringMessages.refreshRaceLog(), makeImagePrototype(resources.reloadIcon())));
|
||||
result.add(new ImageSpec(ACTION_SET_STARTTIME, stringMessages.setStartTime(), makeImagePrototype(resources.clockIcon())));
|
||||
result.add(new ImageSpec(ACTION_SET_FINISHING_AND_FINISH_TIME, stringMessages.setFinishingAndFinishTime(), makeImagePrototype(resources.blueSmall())));
|
||||
result.add(new ImageSpec(ACTION_SHOW_RACELOG, stringMessages.raceLog(), makeImagePrototype(resources.flagIcon())));
|
||||
result.add(new ImageSpec(ACTION_SET_TRACKING_TIMES, stringMessages.setTrackingTimes(), makeImagePrototype(resources.setTrackingTimes())));
|
||||
|
||||
|
||||
+1
@@ -15,6 +15,7 @@ public class SensorDataImportWidget extends AbstractFileImportWidget implements
|
||||
super("/sailingserver/sensordata/import", stringMessages, table, sailingService, errorReporter);
|
||||
setMultipleFileUploadEnabled(true);
|
||||
setShouldClearListOnNewUploadComplete(false);
|
||||
setDownsampleOptionVisible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.google.gwt.i18n.client.DateTimeFormat;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.shared.BetterDateTimeBox;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceLogSetFinishingAndFinishTimeDTO;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
|
||||
public class SetFinishingAndFinishedTimeDialog extends SetTimeDialog<RaceLogSetFinishingAndFinishTimeDTO> {
|
||||
private BetterDateTimeBox finishTimeBox;
|
||||
private Label currentFinishTimeLabel;
|
||||
|
||||
public SetFinishingAndFinishedTimeDialog(SailingServiceAsync service, ErrorReporter errorReporter, String leaderboardName,
|
||||
String raceColumnName, String fleetName, StringMessages stringMessages,
|
||||
DataEntryDialog.DialogCallback<RaceLogSetFinishingAndFinishTimeDTO> callback) {
|
||||
super(service, errorReporter, stringMessages.setFinishingAndFinishTime(), stringMessages.setFinishingAndFinishTimeDescription(),
|
||||
stringMessages.setFinishingAndFinishTime(), stringMessages.cancel(), leaderboardName, raceColumnName, fleetName,
|
||||
stringMessages, new EndTimeValidator(stringMessages), callback);
|
||||
this.ensureDebugId("SetFinishingAndFinishedTimeDialog");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RaceLogSetFinishingAndFinishTimeDTO getResult() {
|
||||
RaceLogSetFinishingAndFinishTimeDTO dto = new RaceLogSetFinishingAndFinishTimeDTO();
|
||||
dto.leaderboardName = leaderboardName;
|
||||
dto.raceColumnName = raceColumnName;
|
||||
dto.fleetName = fleetName;
|
||||
dto.authorName = authorNameBox.getValue();
|
||||
dto.authorPriority = authorPriorityBox.getValue();
|
||||
dto.finishTime = finishTimeBox.getValue();
|
||||
dto.finishingTime = timeBox.getValue();
|
||||
dto.passId = currentPassId;
|
||||
return dto;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void refreshCurrentTime() {
|
||||
service.getFinishingAndFinishTime(leaderboardName, raceColumnName, fleetName,
|
||||
new AsyncCallback<com.sap.sse.common.Util.Triple<Date, Date, Integer>>() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(com.sap.sse.common.Util.Triple<Date, Date, Integer> result) {
|
||||
if (result == null) {
|
||||
currentFinishTimeLabel.setText(stringMessages.notAvailable());
|
||||
currentPassIdBox.setText(stringMessages.notAvailable());
|
||||
} else {
|
||||
setDate(result.getA(), currentStartOrFinishingTimeLabel, timeBox);
|
||||
setDate(result.getB(), currentFinishTimeLabel, finishTimeBox);
|
||||
currentPassId = result.getC().intValue();
|
||||
currentPassIdBox.setText(result.getC().toString());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(caught.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void setDate(Date date, Label currentValueLabel, BetterDateTimeBox dateTimeBox) {
|
||||
if (date == null) {
|
||||
currentValueLabel.setText(stringMessages.unknown());
|
||||
} else {
|
||||
dateTimeBox.setValue(date);
|
||||
currentValueLabel.setText(DateTimeFormat
|
||||
.getFormat(DateTimeFormat.PredefinedFormat.DATE_TIME_MEDIUM).format(date));
|
||||
}
|
||||
}
|
||||
|
||||
private static class EndTimeValidator implements Validator<RaceLogSetFinishingAndFinishTimeDTO> {
|
||||
|
||||
private final StringMessages stringMessages;
|
||||
|
||||
public EndTimeValidator(StringMessages stringMessages) {
|
||||
this.stringMessages = stringMessages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorMessage(RaceLogSetFinishingAndFinishTimeDTO dto) {
|
||||
if (dto.authorName == null || dto.authorName.isEmpty() || dto.authorPriority == null
|
||||
|| dto.finishTime == null || dto.finishingTime == null) {
|
||||
return stringMessages.pleaseEnterAValue();
|
||||
}
|
||||
// both times are != null at this point
|
||||
if (dto.finishTime.before(dto.finishingTime)) {
|
||||
return stringMessages.finishTimeMustBeAtOrAfterFinishingTime();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTimeLabel() {
|
||||
return stringMessages.finishingTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void additionalCurrentTimeLabel(Grid content) {
|
||||
currentFinishTimeLabel = new Label("");
|
||||
content.setWidget(1, 0, createLabel(stringMessages.finishTimeString()));
|
||||
content.setWidget(1, 1, currentFinishTimeLabel);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addAdditionalInput(Grid content) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void additionalInput(Grid content) {
|
||||
finishTimeBox = createDateTimeBox(new Date());
|
||||
finishTimeBox.setFormat("dd/mm/yyyy hh:ii:ss");
|
||||
finishTimeBox.ensureDebugId("FinishTimeBox");
|
||||
content.setWidget(1, 0, createLabel(stringMessages.finishTimeString()));
|
||||
content.setWidget(1, 1, finishTimeBox);
|
||||
}
|
||||
}
|
||||
Executable → Regular
+77
-153
@@ -2,136 +2,30 @@ package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.i18n.client.DateTimeFormat;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.CaptionPanel;
|
||||
import com.google.gwt.user.client.ui.CheckBox;
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.HasVerticalAlignment;
|
||||
import com.google.gwt.user.client.ui.HorizontalPanel;
|
||||
import com.google.gwt.user.client.ui.Image;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.ListBox;
|
||||
import com.google.gwt.user.client.ui.PushButton;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
|
||||
import com.sap.sailing.gwt.ui.client.DataEntryDialogWithBootstrap;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.shared.BetterDateTimeBox;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceLogSetStartTimeAndProcedureDTO;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
|
||||
public class SetStartTimeDialog extends DataEntryDialogWithBootstrap<RaceLogSetStartTimeAndProcedureDTO> {
|
||||
public class SetStartTimeDialog extends SetTimeDialog<RaceLogSetStartTimeAndProcedureDTO> {
|
||||
protected ListBox racingProcedureSelection;
|
||||
protected CheckBox advancePassIdCheckbox;
|
||||
|
||||
private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
|
||||
|
||||
private final SailingServiceAsync service;
|
||||
private final ErrorReporter errorReporter;
|
||||
private final String leaderboardName;
|
||||
private final String raceColumnName;
|
||||
private final String fleetName;
|
||||
private final StringMessages stringMessages;
|
||||
|
||||
private int currentPassId = -1;
|
||||
private Label currentStartTimeLabel;
|
||||
private Label currentPassIdBox;
|
||||
private BetterDateTimeBox timeBox;
|
||||
private TextBox authorNameBox;
|
||||
private com.sap.sse.gwt.client.controls.IntegerBox authorPriorityBox;
|
||||
private ListBox racingProcedureSelection;
|
||||
private CheckBox advancePassIdCheckbox;
|
||||
|
||||
public SetStartTimeDialog(SailingServiceAsync service, ErrorReporter errorReporter, String leaderboardName,
|
||||
String raceColumnName, String fleetName, StringMessages stringMessages,
|
||||
public SetStartTimeDialog(SailingServiceAsync service, ErrorReporter errorReporter, String leaderboardName,
|
||||
String raceColumnName, String fleetName, StringMessages stringMessages,
|
||||
DataEntryDialog.DialogCallback<RaceLogSetStartTimeAndProcedureDTO> callback) {
|
||||
super(stringMessages.setStartTime(), stringMessages.setStartTimeDescription(), stringMessages.setStartTime(),
|
||||
stringMessages.cancel(), new StartTimeValidator(stringMessages), callback);
|
||||
this.service = service;
|
||||
this.errorReporter = errorReporter;
|
||||
this.stringMessages = stringMessages;
|
||||
this.leaderboardName = leaderboardName;
|
||||
this.raceColumnName = raceColumnName;
|
||||
this.fleetName = fleetName;
|
||||
super(service, errorReporter, stringMessages.setStartTime(), stringMessages.setStartTimeDescription(),
|
||||
stringMessages.setStartTime(), stringMessages.cancel(), leaderboardName, raceColumnName, fleetName,
|
||||
stringMessages, new StartTimeValidator(stringMessages), callback);
|
||||
this.ensureDebugId("SetStartTimeDialog");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Widget getAdditionalWidget() {
|
||||
VerticalPanel content = new VerticalPanel();
|
||||
content.add(createCurrentPanel());
|
||||
content.add(createInputPanel());
|
||||
refreshCurrentStartTime();
|
||||
return content;
|
||||
}
|
||||
|
||||
private Widget createCurrentPanel() {
|
||||
CaptionPanel current = new CaptionPanel(stringMessages.liveData());
|
||||
HorizontalPanel currentPanel = new HorizontalPanel();
|
||||
currentPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
|
||||
Grid grid = new Grid(2,2);
|
||||
currentStartTimeLabel = new Label("");
|
||||
grid.setWidget(0, 0, createLabel(stringMessages.startTime()));
|
||||
grid.setWidget(0, 1, currentStartTimeLabel);
|
||||
|
||||
currentPassIdBox = new Label("");
|
||||
grid.setWidget(1, 0, createLabel(stringMessages.currentPass()));
|
||||
grid.setWidget(1, 1, currentPassIdBox);
|
||||
currentPanel.add(grid);
|
||||
|
||||
PushButton refreshButton = new PushButton(new Image(resources.reloadIcon()));
|
||||
refreshButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
refreshCurrentStartTime();
|
||||
}
|
||||
});
|
||||
currentPanel.add(refreshButton);
|
||||
|
||||
current.add(currentPanel);
|
||||
return current;
|
||||
}
|
||||
|
||||
private Widget createInputPanel() {
|
||||
Grid content = new Grid(5, 2);
|
||||
timeBox = createDateTimeBox(new Date());
|
||||
timeBox.setFormat("dd/mm/yyyy hh:ii:ss");
|
||||
timeBox.ensureDebugId("StartTimeTimeBox");
|
||||
content.setWidget(0, 0, createLabel(stringMessages.startTime()));
|
||||
content.setWidget(0, 1, timeBox);
|
||||
|
||||
|
||||
authorNameBox = createTextBox("Shore");
|
||||
authorNameBox.ensureDebugId("AuthorNameTextBox");
|
||||
content.setWidget(1, 0, createLabel(stringMessages.authorName()));
|
||||
content.setWidget(1, 1, authorNameBox);
|
||||
authorPriorityBox = createIntegerBox(4, 2);
|
||||
authorPriorityBox.ensureDebugId("AuthorPriorityIntegerBox");
|
||||
content.setWidget(2, 0, createLabel(stringMessages.authorPriority()));
|
||||
content.setWidget(2, 1, authorPriorityBox);
|
||||
|
||||
racingProcedureSelection = createListBox(false);
|
||||
ListBoxUtils.setupRacingProcedureTypeListBox(racingProcedureSelection, RacingProcedureType.RRS26, stringMessages.no());
|
||||
int racingCounter = 0;
|
||||
for (RacingProcedureType racingType : RacingProcedureType.values()) {
|
||||
racingProcedureSelection.setValue(racingCounter++, racingType.name());
|
||||
}
|
||||
racingProcedureSelection.ensureDebugId("RacingProcedureListBox");
|
||||
content.setWidget(3, 0, createLabel(stringMessages.racingProcedure()));
|
||||
content.setWidget(3, 1, racingProcedureSelection);
|
||||
|
||||
advancePassIdCheckbox = createCheckbox(stringMessages.advancePassId());
|
||||
advancePassIdCheckbox.setValue(false);
|
||||
advancePassIdCheckbox.ensureDebugId("AnvancePassIdCheckBox");
|
||||
content.setWidget(4, 1, advancePassIdCheckbox);
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RaceLogSetStartTimeAndProcedureDTO getResult() {
|
||||
@@ -143,64 +37,94 @@ public class SetStartTimeDialog extends DataEntryDialogWithBootstrap<RaceLogSetS
|
||||
dto.authorPriority = authorPriorityBox.getValue();
|
||||
dto.logicalTimePoint = new Date();
|
||||
dto.startTime = timeBox.getValue();
|
||||
dto.passId = advancePassIdCheckbox.getValue() ? currentPassId+1 : currentPassId;
|
||||
dto.passId = advancePassIdCheckbox.getValue() ? currentPassId + 1 : currentPassId;
|
||||
dto.racingProcedure = RacingProcedureType.values()[racingProcedureSelection.getSelectedIndex()];
|
||||
return dto;
|
||||
}
|
||||
|
||||
private void refreshCurrentStartTime() {
|
||||
service.getStartTimeAndProcedure(leaderboardName, raceColumnName, fleetName, new AsyncCallback<com.sap.sse.common.Util.Triple<Date,Integer, RacingProcedureType>>() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(com.sap.sse.common.Util.Triple<Date, Integer, RacingProcedureType> result) {
|
||||
if (result == null) {
|
||||
currentStartTimeLabel.setText(stringMessages.notAvailable());
|
||||
currentPassIdBox.setText(stringMessages.notAvailable());
|
||||
} else {
|
||||
Date startTime = result.getA();
|
||||
if (startTime == null) {
|
||||
currentStartTimeLabel.setText(stringMessages.unknown());
|
||||
} else {
|
||||
currentStartTimeLabel.setText(DateTimeFormat.getFormat(
|
||||
DateTimeFormat.PredefinedFormat.DATE_TIME_MEDIUM).format(startTime));
|
||||
}
|
||||
currentPassId = result.getB().intValue();
|
||||
currentPassIdBox.setText(result.getB().toString());
|
||||
int racingProcedureIndex = 0;
|
||||
for (RacingProcedureType racingProcedureSelect : RacingProcedureType.values()) {
|
||||
if (racingProcedureSelect.equals(result.getC())) {
|
||||
break;
|
||||
@Override
|
||||
protected void refreshCurrentTime() {
|
||||
service.getStartTimeAndProcedure(leaderboardName, raceColumnName, fleetName,
|
||||
new AsyncCallback<com.sap.sse.common.Util.Triple<Date, Integer, RacingProcedureType>>() {
|
||||
|
||||
@Override
|
||||
public void onSuccess(com.sap.sse.common.Util.Triple<Date, Integer, RacingProcedureType> result) {
|
||||
if (result == null) {
|
||||
currentStartOrFinishingTimeLabel.setText(stringMessages.notAvailable());
|
||||
currentPassIdBox.setText(stringMessages.notAvailable());
|
||||
} else {
|
||||
Date startTime = result.getA();
|
||||
if (startTime == null) {
|
||||
currentStartOrFinishingTimeLabel.setText(stringMessages.unknown());
|
||||
} else {
|
||||
currentStartOrFinishingTimeLabel.setText(DateTimeFormat
|
||||
.getFormat(DateTimeFormat.PredefinedFormat.DATE_TIME_MEDIUM).format(startTime));
|
||||
}
|
||||
currentPassId = result.getB().intValue();
|
||||
currentPassIdBox.setText(result.getB().toString());
|
||||
int racingProcedureIndex = 0;
|
||||
for (RacingProcedureType racingProcedureSelect : RacingProcedureType.values()) {
|
||||
if (racingProcedureSelect.equals(result.getC())) {
|
||||
break;
|
||||
}
|
||||
racingProcedureIndex++;
|
||||
}
|
||||
racingProcedureSelection.setSelectedIndex(racingProcedureIndex);
|
||||
}
|
||||
racingProcedureIndex++;
|
||||
}
|
||||
racingProcedureSelection.setSelectedIndex(racingProcedureIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(caught.getMessage());
|
||||
}
|
||||
});
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(caught.getMessage());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static class StartTimeValidator implements Validator<RaceLogSetStartTimeAndProcedureDTO> {
|
||||
|
||||
|
||||
private final StringMessages stringMessages;
|
||||
|
||||
|
||||
public StartTimeValidator(StringMessages stringMessages) {
|
||||
this.stringMessages = stringMessages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorMessage(RaceLogSetStartTimeAndProcedureDTO dto) {
|
||||
if (dto.authorName == null || dto.authorName.isEmpty() ||
|
||||
dto.authorPriority == null || dto.startTime == null) {
|
||||
if (dto.authorName == null || dto.authorName.isEmpty() || dto.authorPriority == null
|
||||
|| dto.startTime == null) {
|
||||
return stringMessages.pleaseEnterAValue();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getTimeLabel() {
|
||||
return stringMessages.startTime();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void addAdditionalInput(Grid content) {
|
||||
racingProcedureSelection = createListBox(false);
|
||||
ListBoxUtils.setupRacingProcedureTypeListBox(racingProcedureSelection, RacingProcedureType.RRS26,
|
||||
stringMessages.no());
|
||||
int racingCounter = 0;
|
||||
for (RacingProcedureType racingType : RacingProcedureType.values()) {
|
||||
racingProcedureSelection.setValue(racingCounter++, racingType.name());
|
||||
}
|
||||
racingProcedureSelection.ensureDebugId("RacingProcedureListBox");
|
||||
content.setWidget(4, 0, createLabel(stringMessages.racingProcedure()));
|
||||
content.setWidget(4, 1, racingProcedureSelection);
|
||||
|
||||
advancePassIdCheckbox = createCheckbox(stringMessages.advancePassId());
|
||||
advancePassIdCheckbox.setValue(false);
|
||||
advancePassIdCheckbox.ensureDebugId("AnvancePassIdCheckBox");
|
||||
content.setWidget(5, 0, advancePassIdCheckbox);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void additionalInput(Grid content) {
|
||||
}
|
||||
|
||||
}
|
||||
Executable
+135
@@ -0,0 +1,135 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.user.client.ui.CaptionPanel;
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.HasVerticalAlignment;
|
||||
import com.google.gwt.user.client.ui.HorizontalPanel;
|
||||
import com.google.gwt.user.client.ui.Image;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.PushButton;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.gwt.ui.client.DataEntryDialogWithBootstrap;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.shared.BetterDateTimeBox;
|
||||
import com.sap.sse.gwt.client.ErrorReporter;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
|
||||
public abstract class SetTimeDialog<T> extends DataEntryDialogWithBootstrap<T> {
|
||||
|
||||
private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
|
||||
|
||||
protected final SailingServiceAsync service;
|
||||
protected final ErrorReporter errorReporter;
|
||||
protected final String leaderboardName;
|
||||
protected final String raceColumnName;
|
||||
protected final String fleetName;
|
||||
protected final StringMessages stringMessages;
|
||||
|
||||
protected int currentPassId = -1;
|
||||
protected Label currentStartOrFinishingTimeLabel;
|
||||
protected Label currentPassIdBox;
|
||||
protected BetterDateTimeBox timeBox;
|
||||
protected TextBox authorNameBox;
|
||||
protected com.sap.sse.gwt.client.controls.IntegerBox authorPriorityBox;
|
||||
|
||||
public SetTimeDialog(SailingServiceAsync service, ErrorReporter errorReporter, String title, String message,
|
||||
String okButtonName, String cancelButtonName, String leaderboardName, String raceColumnName,
|
||||
String fleetName, StringMessages stringMessages,
|
||||
com.sap.sse.gwt.client.dialog.DataEntryDialog.Validator<T> validator,
|
||||
DataEntryDialog.DialogCallback<T> callback) {
|
||||
super(title, message, okButtonName, cancelButtonName, validator, callback);
|
||||
this.service = service;
|
||||
this.errorReporter = errorReporter;
|
||||
this.stringMessages = stringMessages;
|
||||
this.leaderboardName = leaderboardName;
|
||||
this.raceColumnName = raceColumnName;
|
||||
this.fleetName = fleetName;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Widget getAdditionalWidget() {
|
||||
VerticalPanel content = new VerticalPanel();
|
||||
content.add(createCurrentPanel());
|
||||
content.add(createInputPanel());
|
||||
refreshCurrentTime();
|
||||
return content;
|
||||
}
|
||||
|
||||
private Widget createCurrentPanel() {
|
||||
CaptionPanel current = new CaptionPanel(stringMessages.liveData());
|
||||
HorizontalPanel currentPanel = new HorizontalPanel();
|
||||
currentPanel.setVerticalAlignment(HasVerticalAlignment.ALIGN_MIDDLE);
|
||||
Grid grid = new Grid(3, 2);
|
||||
currentStartOrFinishingTimeLabel = new Label("");
|
||||
grid.setWidget(0, 0, createLabel(getTimeLabel()));
|
||||
grid.setWidget(0, 1, currentStartOrFinishingTimeLabel);
|
||||
|
||||
grid.setText(1, 0, "");
|
||||
grid.setText(1, 1, "");
|
||||
|
||||
additionalCurrentTimeLabel(grid);
|
||||
|
||||
currentPassIdBox = new Label("");
|
||||
grid.setWidget(2, 0, createLabel(stringMessages.currentPass()));
|
||||
grid.setWidget(2, 1, currentPassIdBox);
|
||||
currentPanel.add(grid);
|
||||
|
||||
PushButton refreshButton = new PushButton(new Image(resources.reloadIcon()));
|
||||
refreshButton.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
refreshCurrentTime();
|
||||
}
|
||||
});
|
||||
currentPanel.add(refreshButton);
|
||||
|
||||
current.add(currentPanel);
|
||||
return current;
|
||||
}
|
||||
|
||||
protected abstract String getTimeLabel();
|
||||
|
||||
private Widget createInputPanel() {
|
||||
Grid content = new Grid(7, 2);
|
||||
timeBox = createDateTimeBox(new Date());
|
||||
timeBox.setFormat("dd/mm/yyyy hh:ii:ss");
|
||||
timeBox.ensureDebugId("StartTimeTimeBox");
|
||||
content.setWidget(0, 0, createLabel(getTimeLabel()));
|
||||
content.setWidget(0, 1, timeBox);
|
||||
|
||||
content.setText(1, 0, "");
|
||||
content.setText(1, 1, "");
|
||||
|
||||
additionalInput(content);
|
||||
|
||||
authorNameBox = createTextBox("Shore");
|
||||
authorNameBox.ensureDebugId("AuthorNameTextBox");
|
||||
content.setWidget(2, 0, createLabel(stringMessages.authorName()));
|
||||
content.setWidget(2, 1, authorNameBox);
|
||||
content.setWidget(3, 0, createLabel(stringMessages.authorPriority()));
|
||||
authorPriorityBox = createIntegerBox(4, 2);
|
||||
authorPriorityBox.ensureDebugId("AuthorPriorityIntegerBox");
|
||||
content.setWidget(3, 1, authorPriorityBox);
|
||||
|
||||
addAdditionalInput(content);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
protected void additionalCurrentTimeLabel(Grid content) {
|
||||
}
|
||||
|
||||
protected abstract void additionalInput(Grid content);
|
||||
|
||||
protected abstract void addAdditionalInput(Grid content);
|
||||
|
||||
protected abstract void refreshCurrentTime();
|
||||
}
|
||||
+31
@@ -56,6 +56,7 @@ import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapSettings;
|
||||
import com.sap.sailing.gwt.ui.shared.ControlPointDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.DeviceConfigurationDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceLogSetFinishingAndFinishTimeDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceLogSetStartTimeAndProcedureDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
|
||||
@@ -383,6 +384,8 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC
|
||||
refreshRaceLog(raceColumnDTOAndFleetDTO.getA(), raceColumnDTOAndFleetDTO.getB(), true);
|
||||
} else if (RaceLogTrackingEventManagementRaceImagesBarCell.ACTION_SET_STARTTIME.equals(value)) {
|
||||
setStartTime(getSelectedRaceColumnWithFleet().getA(), getSelectedRaceColumnWithFleet().getB());
|
||||
} else if (RaceLogTrackingEventManagementRaceImagesBarCell.ACTION_SET_FINISHING_AND_FINISH_TIME.equals(value)) {
|
||||
setEndTime(getSelectedRaceColumnWithFleet().getA(), getSelectedRaceColumnWithFleet().getB());
|
||||
} else if (LeaderboardRaceConfigImagesBarCell.ACTION_SHOW_RACELOG.equals(value)) {
|
||||
showRaceLog(raceColumnDTOAndFleetDTO.getA(), raceColumnDTOAndFleetDTO.getB());
|
||||
} else if (RaceLogTrackingEventManagementRaceImagesBarCell.ACTION_SET_TRACKING_TIMES.equals(value)) {
|
||||
@@ -761,6 +764,34 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void setEndTime(RaceColumnDTO raceColumnDTO, FleetDTO fleetDTO) {
|
||||
new SetFinishingAndFinishedTimeDialog(sailingService, errorReporter, getSelectedLeaderboardName(), raceColumnDTO.getName(),
|
||||
fleetDTO.getName(), stringMessages, new DialogCallback<RaceLogSetFinishingAndFinishTimeDTO>() {
|
||||
@Override
|
||||
public void ok(RaceLogSetFinishingAndFinishTimeDTO editedObject) {
|
||||
sailingService.setFinishingAndEndTime(editedObject, new AsyncCallback<Pair<Boolean, Boolean>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(caught.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Pair<Boolean, Boolean> result) {
|
||||
if (!result.getA() || !result.getB()) {
|
||||
Window.alert(stringMessages.failedToSetNewFinishingAndFinishTime());
|
||||
} else {
|
||||
trackedRacesListComposite.regattaRefresher.fillRegattas();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void cancel() {
|
||||
}
|
||||
}).show();
|
||||
}
|
||||
|
||||
private void refreshTrackingActionButtons(){
|
||||
leaderboardSelectionChanged();
|
||||
}
|
||||
|
||||
+1
@@ -14,6 +14,7 @@ public class TrackFileImportWidget extends AbstractFileImportWidget implements I
|
||||
public TrackFileImportWidget(TrackFileImportDeviceIdentifierTableWrapper table, StringMessages stringMessages,
|
||||
final SailingServiceAsync sailingService, final ErrorReporter errorReporter) {
|
||||
super("/sailingserver/trackfiles/import", stringMessages, table, sailingService, errorReporter);
|
||||
setDownsampleOptionVisible(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+30
-2
@@ -40,12 +40,20 @@ public class DetailTypeFormatter {
|
||||
return stringMessages.currentSpeedOverGroundInKnots();
|
||||
case CURRENT_RIDE_HEIGHT_IN_METERS:
|
||||
return stringMessages.currentOrAverageRideHeightInMeters();
|
||||
case CURRENT_DISTANCE_FOILED_IN_METERS:
|
||||
return stringMessages.currentDistanceFoiledInMeters();
|
||||
case CURRENT_DURATION_FOILED_IN_SECONDS:
|
||||
return stringMessages.currentDurationFoiled();
|
||||
case CURRENT_HEEL_IN_DEGREES:
|
||||
return stringMessages.currentHeelInDegree();
|
||||
case CURRENT_PITCH_IN_DEGREES:
|
||||
return stringMessages.currentPitchInDegree();
|
||||
case RACE_CURRENT_RIDE_HEIGHT_IN_METERS:
|
||||
return stringMessages.currentRideHeightInMeters();
|
||||
case RACE_CURRENT_DURATION_FOILED_IN_SECONDS:
|
||||
return stringMessages.currentDurationFoiled();
|
||||
case RACE_CURRENT_DISTANCE_FOILED_IN_METERS:
|
||||
return stringMessages.currentDistanceFoiledInMeters();
|
||||
case CURRENT_PORT_DAGGERBOARD_RAKE:
|
||||
return stringMessages.currentPortDaggerboardRake();
|
||||
case CURRENT_STBD_DAGGERBOARD_RAKE:
|
||||
@@ -158,6 +166,10 @@ public class DetailTypeFormatter {
|
||||
return stringMessages.totalTimeSailedInSeconds();
|
||||
case TOTAL_AVERAGE_SPEED_OVER_GROUND:
|
||||
return stringMessages.totalAverageSpeedOverGround();
|
||||
case TOTAL_DURATION_FOILED_IN_SECONDS:
|
||||
return stringMessages.totalDurationFoiledInSeconds();
|
||||
case TOTAL_DISTANCE_FOILED_IN_METERS:
|
||||
return stringMessages.totalDistanceFoiledInMeters();
|
||||
case AVERAGE_MANEUVER_LOSS_IN_METERS:
|
||||
return stringMessages.averageManeuverLossInMeters();
|
||||
case AVERAGE_TACK_LOSS_IN_METERS:
|
||||
@@ -188,8 +200,6 @@ public class DetailTypeFormatter {
|
||||
return stringMessages.beatAngle();
|
||||
case COURSE_OVER_GROUND_TRUE_DEGREES:
|
||||
return stringMessages.courseOverGroundTrueDegrees();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -241,7 +251,10 @@ public class DetailTypeFormatter {
|
||||
case DISTANCE_TO_START_LINE:
|
||||
case CURRENT_RIDE_HEIGHT_IN_METERS:
|
||||
case RACE_CURRENT_RIDE_HEIGHT_IN_METERS:
|
||||
case RACE_CURRENT_DISTANCE_FOILED_IN_METERS:
|
||||
case CURRENT_DEPTH_IN_METERS:
|
||||
case CURRENT_DISTANCE_FOILED_IN_METERS:
|
||||
case TOTAL_DISTANCE_FOILED_IN_METERS:
|
||||
return stringMessages.metersUnit();
|
||||
|
||||
case CURRENT_DEFLECTOR_IN_MILLIMETERS:
|
||||
@@ -296,6 +309,9 @@ public class DetailTypeFormatter {
|
||||
case RACE_TIME_TRAVELED_REACHING:
|
||||
case RACE_TIME_TRAVELED_UPWIND:
|
||||
case TIME_TRAVELED:
|
||||
case CURRENT_DURATION_FOILED_IN_SECONDS:
|
||||
case RACE_CURRENT_DURATION_FOILED_IN_SECONDS:
|
||||
case TOTAL_DURATION_FOILED_IN_SECONDS:
|
||||
return stringMessages.hhmmssUnit();
|
||||
|
||||
// Cases for detail types without unit, so that an empty string is returned.
|
||||
@@ -385,8 +401,16 @@ public class DetailTypeFormatter {
|
||||
return stringMessages.currentSpeedOverGroundInKnotsTooltip();
|
||||
case CURRENT_RIDE_HEIGHT_IN_METERS:
|
||||
return stringMessages.currentOrAverageRideHeightInMetersTooltip();
|
||||
case CURRENT_DISTANCE_FOILED_IN_METERS:
|
||||
return stringMessages.currentDistanceFoiledInMetersTooltip();
|
||||
case CURRENT_DURATION_FOILED_IN_SECONDS:
|
||||
return stringMessages.currentDurationFoiledTooltip();
|
||||
case RACE_CURRENT_RIDE_HEIGHT_IN_METERS:
|
||||
return stringMessages.currentRideHeightInMetersTooltip();
|
||||
case RACE_CURRENT_DURATION_FOILED_IN_SECONDS:
|
||||
return stringMessages.currentDurationFoiledTooltip();
|
||||
case RACE_CURRENT_DISTANCE_FOILED_IN_METERS:
|
||||
return stringMessages.currentDistanceFoiledInMetersTooltip();
|
||||
case CURRENT_PORT_DAGGERBOARD_RAKE:
|
||||
return stringMessages.currentPortDaggerboardRakeTooltip();
|
||||
case CURRENT_STBD_DAGGERBOARD_RAKE:
|
||||
@@ -483,6 +507,10 @@ public class DetailTypeFormatter {
|
||||
return stringMessages.totalDistanceTraveledTooltip();
|
||||
case TOTAL_AVERAGE_SPEED_OVER_GROUND:
|
||||
return stringMessages.totalAverageSpeedOverGroundTooltip();
|
||||
case TOTAL_DURATION_FOILED_IN_SECONDS:
|
||||
return stringMessages.totalDurationFoiledInSecondsTooltip();
|
||||
case TOTAL_DISTANCE_FOILED_IN_METERS:
|
||||
return stringMessages.totalDistanceFoiledInMetersTooltip();
|
||||
case TIME_ON_TIME_FACTOR:
|
||||
return stringMessages.timeOnTimeFactorTooltip();
|
||||
case TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE:
|
||||
|
||||
+5
@@ -74,6 +74,7 @@ import com.sap.sailing.gwt.ui.shared.MarkDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceCourseDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceGroupDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceLogDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceLogSetFinishingAndFinishTimeDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceLogSetStartTimeAndProcedureDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceboardDataDTO;
|
||||
@@ -472,7 +473,11 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
|
||||
|
||||
boolean setStartTimeAndProcedure(RaceLogSetStartTimeAndProcedureDTO dto);
|
||||
|
||||
Pair<Boolean, Boolean> setFinishingAndEndTime(RaceLogSetFinishingAndFinishTimeDTO dto);
|
||||
|
||||
Util.Triple<Date, Integer, RacingProcedureType> getStartTimeAndProcedure(String leaderboardName, String raceColumnName, String fleetName);
|
||||
|
||||
Util.Triple<Date, Date, Integer> getFinishingAndFinishTime(String leaderboardName, String raceColumnName, String fleetName);
|
||||
|
||||
Iterable<String> getAllIgtimiAccountEmailAddresses();
|
||||
|
||||
|
||||
+17
@@ -63,6 +63,7 @@ import com.sap.sailing.gwt.ui.shared.MarkDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceCourseDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceGroupDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceLogDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceLogSetFinishingAndFinishTimeDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceLogSetStartTimeAndProcedureDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceboardDataDTO;
|
||||
@@ -639,6 +640,15 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan
|
||||
*/
|
||||
void setStartTimeAndProcedure(RaceLogSetStartTimeAndProcedureDTO dto, AsyncCallback<Boolean> callback);
|
||||
|
||||
/**
|
||||
* Sets the a new finishing and end time.
|
||||
*
|
||||
* @param dto
|
||||
* {@link RaceLogSetFinishingAndFinishTimeDTO} identifying the race and the new finishing and
|
||||
* end time.
|
||||
*/
|
||||
void setFinishingAndEndTime(RaceLogSetFinishingAndFinishTimeDTO editedObject, AsyncCallback<Pair<Boolean, Boolean>> asyncCallback);
|
||||
|
||||
/**
|
||||
* Gets the race's current start time, current pass identifier and racing procedure. If no start time is set, the
|
||||
* pass identifier will still be returned, but the start time field will be <code>null</code>.
|
||||
@@ -646,6 +656,13 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan
|
||||
void getStartTimeAndProcedure(String leaderboardName, String raceColumnName, String fleetName,
|
||||
AsyncCallback<Util.Triple<Date, Integer, RacingProcedureType>> callback);
|
||||
|
||||
/**
|
||||
* Gets the race's current finishing and finish times as well as the current pass identifier. If no finishing or finish time is set, the
|
||||
* pass identifier will still be returned, but the finishing/finish time field will be <code>null</code>.
|
||||
*/
|
||||
void getFinishingAndFinishTime(String leaderboardName, String raceColumnName, String fleetName,
|
||||
AsyncCallback<Util.Triple<Date, Date, Integer>> asyncCallback);
|
||||
|
||||
void getAllIgtimiAccountEmailAddresses(AsyncCallback<Iterable<String>> callback);
|
||||
|
||||
void getIgtimiAuthorizationUrl(String redirectProtocol, String redirectHostname, String redirectPort, AsyncCallback<String> callback);
|
||||
|
||||
+14
@@ -1029,8 +1029,12 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
|
||||
String overwriteRacingProceduresConfigurationHelpText();
|
||||
|
||||
String setStartTime();
|
||||
String finishTimeString();
|
||||
String setFinishingAndFinishTime();
|
||||
String finishTimeMustBeAtOrAfterFinishingTime();
|
||||
String currentPass();
|
||||
String setStartTimeDescription();
|
||||
String setFinishingAndFinishTimeDescription();
|
||||
String setTrackingTimesDescription();
|
||||
String hasAdditionalGolfDownTime();
|
||||
String additionalGolfDownTimeHelpText();
|
||||
@@ -1056,6 +1060,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
|
||||
String doYouReallyWantToRemoveLeaderboard(String name);
|
||||
String metaLeaderboardCannotBeChanged();
|
||||
String failedToSetNewStartTime();
|
||||
String failedToSetNewFinishingAndFinishTime();
|
||||
String doYouReallyWantToRemoveIgtimiAccount(String accountEmail);
|
||||
String igtimiWindImport();
|
||||
String seeIgtimiTabForAccountSettings();
|
||||
@@ -1945,6 +1950,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
|
||||
String currentStbdRudderRakeTooltip();
|
||||
String currentMastRotationInDegree();
|
||||
String currentMastRotationInDegreeTooltip();
|
||||
String finishingTime();
|
||||
String boatNameMustNotBeEmpty();
|
||||
String errorUpdatingExpeditionDeviceConfiguration(String name, String message);
|
||||
String doYouReallyWantToRemoveExpeditionDeviceConfiguration(String name);
|
||||
@@ -1984,4 +1990,12 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
|
||||
String currentDriftInDegreesTooltip();
|
||||
String currentDepthInMetersTooltip();
|
||||
String tonsUnit();
|
||||
String currentDistanceFoiledInMeters();
|
||||
String currentDurationFoiled();
|
||||
String currentDistanceFoiledInMetersTooltip();
|
||||
String currentDurationFoiledTooltip();
|
||||
String totalDurationFoiledInSeconds();
|
||||
String totalDistanceFoiledInMeters();
|
||||
String totalDurationFoiledInSecondsTooltip();
|
||||
String totalDistanceFoiledInMetersTooltip();
|
||||
}
|
||||
+15
-1
@@ -1002,6 +1002,7 @@ overwriteRacingProceduresConfiguration=Activate regatta configuration
|
||||
overwriteRacingProceduresConfigurationHelpText=Enabling this option will provide a base regatta configuration for all options unconfigured in the regatta-specific configuration.
|
||||
setStartTime=Set start time
|
||||
currentPass=Current pass
|
||||
setFinishingAndFinishTimeDescription=Select new finishing and finish times for the race.
|
||||
setStartTimeDescription=Select a new start time for the race. If you want to set the start time to a timepoint in the past then you have to enable the checkbox to update the pass count. Please be aware of the fact that changing the pass count will have you lose all information from the previous pass (like flag events).
|
||||
setTrackingTimesDescription=Set the start and end of tracking for the race.
|
||||
hasAdditionalGolfDownTime=Activate additional golf down time
|
||||
@@ -1030,6 +1031,7 @@ searchTag=Search tag
|
||||
doYouReallyWantToRemoveLeaderboard=Do you really want to remove the leaderboard: ''{0}''?
|
||||
metaLeaderboardCannotBeChanged=This is a meta leaderboard. It can''t be changed here.
|
||||
failedToSetNewStartTime=Failed to set new start time. This can happen when the same start time is already present in the race log or someone has added a start time that is newer (in terms of a pass id and logical time) than the one that you just added.
|
||||
failedToSetNewFinishingAndFinishTime=Failed to set new finishing and finished time.
|
||||
doYouReallyWantToRemoveIgtimiAccount=Do you really want to remove Igtimi account granting access to data of user {0}?
|
||||
igtimiWindImport=Import Wind from Igtimi
|
||||
seeIgtimiTabForAccountSettings=See the ''Igtimi'' tab for account settings determining which data from Igtimi will be considered.
|
||||
@@ -1930,6 +1932,10 @@ currentStbdRudderRake=Stbd rudder rake
|
||||
currentStbdRudderRakeTooltip=The current starboard rudder rake
|
||||
currentMastRotationInDegree=Mast rotation
|
||||
currentMastRotationInDegreeTooltip=The current mast rotation in degrees
|
||||
finishingTime=Finishing time
|
||||
finishTimeString=Finish time
|
||||
setFinishingAndFinishTime=Set finishing and finish time
|
||||
finishTimeMustBeAtOrAfterFinishingTime=Finish time must be at or after finishing time
|
||||
boatNameMustNotBeEmpty=Please provide a non-empty boat name
|
||||
errorUpdatingExpeditionDeviceConfiguration=Error updating Expedition device configuration "{0}": {1}
|
||||
doYouReallyWantToRemoveExpeditionDeviceConfiguration=Do you really want to remove Expedition device configuration "{0}"?
|
||||
@@ -1968,4 +1974,12 @@ currentRudderInDegreesTooltip=Rudder angle; negative values mean to port, positi
|
||||
currentLeewayInDegreesTooltip=Leeway
|
||||
currentDriftInDegreesTooltip=Angle between heading and course over ground
|
||||
currentDepthInMetersTooltip=Water depth
|
||||
tonsUnit=t
|
||||
tonsUnit=t
|
||||
currentDistanceFoiledInMeters=Distance Foiled
|
||||
currentDurationFoiled=Time Foiled
|
||||
currentDistanceFoiledInMetersTooltip=Distance spent foiling in meters
|
||||
currentDurationFoiledTooltip=Time spent foiling
|
||||
totalDurationFoiledInSeconds=Total Time Foiled
|
||||
totalDistanceFoiledInMeters=Total Distance Foiled
|
||||
totalDurationFoiledInSecondsTooltip=Total time foiled, across the entire leaderboard
|
||||
totalDistanceFoiledInMetersTooltip=Total distance foiled, across the entire leaderboard
|
||||
+17
-3
@@ -1019,6 +1019,7 @@ doYouReallyWantToRemoveTrackedRaces=Möchten Sie die getrackten Rennen wirklich
|
||||
doYouReallyWantToRemoveNonVisibleTrackedRaces=Sie entfernen auch getrackte Rennen, die während des Filterns nicht angezeigt werden. Die folgenden getrackten Rennen werden entfernt. Möchten Sie fortfahren?\n\n{0}
|
||||
metaLeaderboardCannotBeChanged=Dies ist eine Meta-Rangliste, das hier nicht geändert werden kann.
|
||||
failedToSetNewStartTime=Neue Startzeit konnte nicht gesetzt werden.
|
||||
failedToSetNewFinishingAndFinishTime=Neue Zieleinlaufzeit und Endzeit konnte nicht gesetzt werden.
|
||||
doYouReallyWantToRemoveIgtimiAccount=Möchten Sie wirklich den Zugang zu den Daten des Benutzers {0} löschen?
|
||||
igtimiWindImport=Wind von Igtimi importieren
|
||||
seeIgtimiTabForAccountSettings=Siehe den ''Igtimi'' Reiter für die Einstellungen, von welchen Igtimi-Konten Winddaten geladen werden sollen.
|
||||
@@ -1924,6 +1925,11 @@ elements={0} Elemente
|
||||
chooseDifferentDimensionTitle=Andere Dimension auswählen
|
||||
chooseDifferentDimensionMessage=Zum Gruppieren eine andere Dimension auswählen
|
||||
pleaseSelectADimension=Bitte eine Dimension auswählen
|
||||
finishingTime=Zieleinlaufzeit
|
||||
finishTimeString=Endzeitpunkt
|
||||
setFinishingAndFinishTime=Setzte Zieleinlaufzeit und Endzeitpunkt
|
||||
finishTimeMustBeAtOrAfterFinishingTime=Der Endzeitpunkt muss nach der Zieleinlaufzeit liegen oder gleich sein
|
||||
setFinishingAndFinishTimeDescription=Wähle neue Werte für Zieleinlaufzeit und Endzeitpunkt aus.
|
||||
boatNameMustNotBeEmpty=Bitte einen nicht leeren Bootsnamen angeben
|
||||
errorUpdatingExpeditionDeviceConfiguration=Fehler beim Speichern der Expedition Gerätekonfiguration "{0}": {1}
|
||||
doYouReallyWantToRemoveExpeditionDeviceConfiguration=Möchten Sie wirklich die Expedition Gerätekonfiguration "{0}" löschen?
|
||||
@@ -1954,12 +1960,20 @@ currentTargetBoatspeedPercentageTooltip=Anteil der erreichten berechneten Zielge
|
||||
currentRakeInDegreesTooltip=Mastneigung
|
||||
currentForestayLoadTooltip=Gemessene Vorstagsspannung
|
||||
currentForestayPressureTooltip=Druck am Vorstag
|
||||
currentDeflectorPercentageTooltip=Prozentualer Anteil Abweiser (???)
|
||||
currentDeflectorInMillimetersTooltip=Abweiser Abstand (???)
|
||||
currentDeflectorPercentageTooltip=Prozentualer Anteil Deflektor
|
||||
currentDeflectorInMillimetersTooltip=Deflektor-Abstand
|
||||
currentTackAngleInDegreesTooltip=Berechneter optimaler Wendewinkel
|
||||
currentSetTooltip=Set (???)
|
||||
currentRudderInDegreesTooltip=Ruderwinkel; negative Werte bedeuten Ruder nach Backbord, positive Ruder nach Steuerbord
|
||||
currentLeewayInDegreesTooltip=Leeway (???)
|
||||
currentDriftInDegreesTooltip=Winkel zwischen Kurs durchs Wasser und Kurs über Grund
|
||||
currentDepthInMetersTooltip=Wassertiefe
|
||||
tonsUnit=t
|
||||
tonsUnit=t
|
||||
currentDistanceFoiledInMeters=Gefoilte Strecke
|
||||
currentDurationFoiled=Gefoilte Zeit
|
||||
currentDistanceFoiledInMetersTooltip=Gefoilte Strecke in Metern
|
||||
currentDurationFoiledTooltip=Gefoilte Zeit
|
||||
totalDurationFoiledInSeconds=Gesamt gefoilte Zeit
|
||||
totalDistanceFoiledInMeters=Gesamt gefoilte Strecke
|
||||
totalDurationFoiledInSecondsTooltip=Insgesamt gefoilte Zeit, über die gesamte Ergebnisliste kumuliert
|
||||
totalDistanceFoiledInMetersTooltip=Insgesamt gefoilte Strecke, über die gesamte Ergebnisliste kumuliert
|
||||
+56
-2
@@ -1178,6 +1178,7 @@ public abstract class LeaderboardPanel<LS extends LeaderboardSettings> extends A
|
||||
DetailType.RACE_CALCULATED_TIME_TRAVELED,
|
||||
DetailType.RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD,
|
||||
DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, DetailType.RACE_CURRENT_RIDE_HEIGHT_IN_METERS,
|
||||
DetailType.RACE_CURRENT_DISTANCE_FOILED_IN_METERS, DetailType.RACE_CURRENT_DURATION_FOILED_IN_SECONDS,
|
||||
DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS, DetailType.NUMBER_OF_MANEUVERS,
|
||||
DetailType.DISPLAY_LEGS, DetailType.CURRENT_LEG,
|
||||
DetailType.RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS,
|
||||
@@ -1198,6 +1199,7 @@ public abstract class LeaderboardPanel<LS extends LeaderboardSettings> extends A
|
||||
public static DetailType[] getAvailableOverallDetailColumnTypes() {
|
||||
return new DetailType[] { DetailType.REGATTA_RANK, DetailType.TOTAL_DISTANCE_TRAVELED,
|
||||
DetailType.TOTAL_AVERAGE_SPEED_OVER_GROUND, DetailType.TOTAL_TIME_SAILED_IN_SECONDS,
|
||||
DetailType.TOTAL_DURATION_FOILED_IN_SECONDS, DetailType.TOTAL_DISTANCE_FOILED_IN_METERS,
|
||||
DetailType.MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS, DetailType.TIME_ON_TIME_FACTOR,
|
||||
DetailType.TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE };
|
||||
}
|
||||
@@ -1266,6 +1268,14 @@ public abstract class LeaderboardPanel<LS extends LeaderboardSettings> extends A
|
||||
new RideHeightColumn(DetailType.RACE_CURRENT_RIDE_HEIGHT_IN_METERS,
|
||||
new RaceCurrentRideHeightInMeters(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE,
|
||||
LeaderboardPanel.this));
|
||||
result.put(DetailType.RACE_CURRENT_DISTANCE_FOILED_IN_METERS,
|
||||
new FormattedDoubleDetailTypeColumn(DetailType.RACE_CURRENT_DISTANCE_FOILED_IN_METERS,
|
||||
new RaceDistanceFoiledInMeters(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE,
|
||||
LeaderboardPanel.this));
|
||||
result.put(DetailType.RACE_CURRENT_DURATION_FOILED_IN_SECONDS,
|
||||
new TotalTimeColumn(DetailType.RACE_CURRENT_DURATION_FOILED_IN_SECONDS,
|
||||
new RaceDurationFoiledInSeconds(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE,
|
||||
LeaderboardPanel.this));
|
||||
result.put(DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS,
|
||||
new FormattedDoubleDetailTypeColumn(DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS,
|
||||
new RaceDistanceToCompetitorFarthestAheadInMeters(), LEG_COLUMN_HEADER_STYLE,
|
||||
@@ -1407,7 +1417,7 @@ public abstract class LeaderboardPanel<LS extends LeaderboardSettings> extends A
|
||||
private class RaceAverageSpeedInKnots implements LegDetailField<Double> {
|
||||
@Override
|
||||
public Double get(LeaderboardRowDTO row) {
|
||||
final Distance distanceTraveledInMeters = row.getDistanceTraveledInMeters(getRaceColumnName());
|
||||
final Distance distanceTraveledInMeters = row.getDistanceTraveled(getRaceColumnName());
|
||||
final Duration time = row.getTimeSailed(getRaceColumnName());
|
||||
final Double result;
|
||||
if (distanceTraveledInMeters != null && time != null) {
|
||||
@@ -1597,11 +1607,37 @@ public abstract class LeaderboardPanel<LS extends LeaderboardSettings> extends A
|
||||
private class RaceDistanceTraveledInMeters implements LegDetailField<Double> {
|
||||
@Override
|
||||
public Double get(LeaderboardRowDTO row) {
|
||||
Distance distanceForRaceColumn = row.getDistanceTraveledInMeters(getRaceColumnName());
|
||||
Distance distanceForRaceColumn = row.getDistanceTraveled(getRaceColumnName());
|
||||
return distanceForRaceColumn == null ? null : distanceForRaceColumn.getMeters();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accumulates the distance foiled over all legs of a race
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*/
|
||||
private class RaceDistanceFoiledInMeters implements LegDetailField<Double> {
|
||||
@Override
|
||||
public Double get(LeaderboardRowDTO row) {
|
||||
Distance distanceForRaceColumn = row.getDistanceFoiled(getRaceColumnName());
|
||||
return distanceForRaceColumn == null ? null : distanceForRaceColumn.getMeters();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Accumulates the duration foiled over all legs of a race
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*/
|
||||
private class RaceDurationFoiledInSeconds implements LegDetailField<Double> {
|
||||
@Override
|
||||
public Double get(LeaderboardRowDTO row) {
|
||||
Duration durationForRaceColumn = row.getDurationFoiled(getRaceColumnName());
|
||||
return durationForRaceColumn == null ? null : durationForRaceColumn.asSeconds();
|
||||
}
|
||||
}
|
||||
|
||||
private class RaceCalculatedTimeTraveledInSeconds implements LegDetailField<Double> {
|
||||
@Override
|
||||
public Double get(LeaderboardRowDTO row) {
|
||||
@@ -2115,6 +2151,20 @@ public abstract class LeaderboardPanel<LS extends LeaderboardSettings> extends A
|
||||
}
|
||||
}
|
||||
|
||||
private static class TotalDistanceFoiledInMetersField implements LegDetailField<Double> {
|
||||
@Override
|
||||
public Double get(LeaderboardRowDTO row) {
|
||||
return row.totalDistanceFoiledInMeters;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TotalDurationFoiledInSecondsField implements LegDetailField<Double> {
|
||||
@Override
|
||||
public Double get(LeaderboardRowDTO row) {
|
||||
return row.totalDurationFoiledInSeconds;
|
||||
}
|
||||
}
|
||||
|
||||
private static class TimeOnTimeFactorColumn implements LegDetailField<Double> {
|
||||
@Override
|
||||
public Double get(LeaderboardRowDTO row) {
|
||||
@@ -2156,6 +2206,10 @@ public abstract class LeaderboardPanel<LS extends LeaderboardSettings> extends A
|
||||
result.put(DetailType.MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS,
|
||||
new MaxSpeedOverallColumn(RACE_COLUMN_HEADER_STYLE, RACE_COLUMN_STYLE, this));
|
||||
result.put(DetailType.TOTAL_TIME_SAILED_IN_SECONDS, createOverallTimeTraveledColumn());
|
||||
result.put(DetailType.TOTAL_DURATION_FOILED_IN_SECONDS, new TotalTimeColumn(DetailType.TOTAL_DURATION_FOILED_IN_SECONDS,
|
||||
new TotalDurationFoiledInSecondsField(), RACE_COLUMN_HEADER_STYLE, RACE_COLUMN_STYLE, this));
|
||||
result.put(DetailType.TOTAL_DISTANCE_FOILED_IN_METERS, new FormattedDoubleDetailTypeColumn(DetailType.TOTAL_DISTANCE_FOILED_IN_METERS,
|
||||
new TotalDistanceFoiledInMetersField(), RACE_COLUMN_HEADER_STYLE, RACE_COLUMN_STYLE, this));
|
||||
result.put(DetailType.TIME_ON_TIME_FACTOR, new FormattedDoubleDetailTypeColumn(DetailType.TIME_ON_TIME_FACTOR,
|
||||
new TimeOnTimeFactorColumn(), RACE_COLUMN_HEADER_STYLE, RACE_COLUMN_STYLE, this));
|
||||
result.put(DetailType.TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE,
|
||||
|
||||
+19
@@ -122,6 +122,20 @@ public class LegColumn extends ExpandableSortableColumn<String> {
|
||||
}
|
||||
}
|
||||
|
||||
private class CurrentDistanceFoiledInMeters extends AbstractLegDetailField<Double> {
|
||||
@Override
|
||||
protected Double getFromNonNullEntry(LegEntryDTO entry) {
|
||||
return entry.currentDistanceFoiledInMeters;
|
||||
}
|
||||
}
|
||||
|
||||
private class CurrentDurationFoiled extends AbstractLegDetailField<Double> {
|
||||
@Override
|
||||
protected Double getFromNonNullEntry(LegEntryDTO entry) {
|
||||
return entry.currentDurationFoiledInSeconds;
|
||||
}
|
||||
}
|
||||
|
||||
private class EstimatedTimeToNextWaypointInSeconds extends AbstractLegDetailField<Double> {
|
||||
@Override
|
||||
protected Double getFromNonNullEntry(LegEntryDTO entry) {
|
||||
@@ -272,6 +286,7 @@ public class LegColumn extends ExpandableSortableColumn<String> {
|
||||
DetailType.GAP_CHANGE_SINCE_LEG_START_IN_SECONDS, DetailType.SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED,
|
||||
DetailType.CURRENT_SPEED_OVER_GROUND_IN_KNOTS, DetailType.CURRENT_HEEL_IN_DEGREES,
|
||||
DetailType.CURRENT_PITCH_IN_DEGREES, DetailType.CURRENT_RIDE_HEIGHT_IN_METERS,
|
||||
DetailType.CURRENT_DISTANCE_FOILED_IN_METERS, DetailType.CURRENT_DURATION_FOILED_IN_SECONDS,
|
||||
DetailType.WINDWARD_DISTANCE_TO_GO_IN_METERS, DetailType.NUMBER_OF_MANEUVERS,
|
||||
DetailType.ESTIMATED_TIME_TO_NEXT_WAYPOINT_IN_SECONDS, DetailType.VELOCITY_MADE_GOOD_IN_KNOTS,
|
||||
DetailType.TIME_TRAVELED, DetailType.CORRECTED_TIME_TRAVELED, DetailType.AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS,
|
||||
@@ -299,6 +314,10 @@ public class LegColumn extends ExpandableSortableColumn<String> {
|
||||
detailHeaderStyle, detailColumnStyle, leaderboardPanel));
|
||||
result.put(DetailType.CURRENT_RIDE_HEIGHT_IN_METERS, new RideHeightColumn(DetailType.CURRENT_RIDE_HEIGHT_IN_METERS,
|
||||
new CurrentRideHeightInMeters(), detailHeaderStyle, detailColumnStyle, leaderboardPanel));
|
||||
result.put(DetailType.CURRENT_DISTANCE_FOILED_IN_METERS, new FormattedDoubleDetailTypeColumn(DetailType.CURRENT_DISTANCE_FOILED_IN_METERS,
|
||||
new CurrentDistanceFoiledInMeters(), detailHeaderStyle, detailColumnStyle, leaderboardPanel));
|
||||
result.put(DetailType.CURRENT_DURATION_FOILED_IN_SECONDS, new TotalTimeColumn(DetailType.CURRENT_DURATION_FOILED_IN_SECONDS,
|
||||
new CurrentDurationFoiled(), detailHeaderStyle, detailColumnStyle, leaderboardPanel));
|
||||
result.put(DetailType.ESTIMATED_TIME_TO_NEXT_WAYPOINT_IN_SECONDS,
|
||||
new FormattedDoubleDetailTypeColumn(DetailType.ESTIMATED_TIME_TO_NEXT_WAYPOINT_IN_SECONDS, new EstimatedTimeToNextWaypointInSeconds(),
|
||||
detailHeaderStyle, detailColumnStyle, leaderboardPanel));
|
||||
|
||||
+6
@@ -4,6 +4,12 @@ import com.sap.sailing.domain.common.DetailType;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardRowDTO;
|
||||
import com.sap.sailing.gwt.ui.client.DurationAsHoursMinutesSecondsFormatter;
|
||||
|
||||
/**
|
||||
* Displays a {@code double} value that is assumed to represent seconds as a time in hours, minutes and seconds.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public class TotalTimeColumn extends FormattedDoubleDetailTypeColumn {
|
||||
|
||||
protected TotalTimeColumn(DetailType detailType, LegDetailField<Double> field, String headerStyle,
|
||||
|
||||
+35
-4
@@ -363,6 +363,7 @@ import com.sap.sailing.gwt.ui.shared.RaceInfoDTO.LineStartInfoDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceInfoDTO.RaceInfoExtensionDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceLogDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceLogEventDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceLogSetFinishingAndFinishTimeDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceLogSetStartTimeAndProcedureDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceMapDataDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
|
||||
@@ -484,7 +485,6 @@ import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.common.Util.Triple;
|
||||
import com.sap.sse.common.WithID;
|
||||
import com.sap.sse.common.impl.MillisecondsDurationImpl;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.common.impl.TimeRangeImpl;
|
||||
import com.sap.sse.common.mail.MailException;
|
||||
@@ -2898,10 +2898,14 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
}
|
||||
break;
|
||||
case VELOCITY_MADE_GOOD_IN_KNOTS:
|
||||
final Speed velocityMadeGood;
|
||||
if (trackedLeg != null) {
|
||||
Speed velocityMadeGood = trackedLeg.getVelocityMadeGood(timePoint, WindPositionMode.EXACT, cache);
|
||||
result = (velocityMadeGood == null) ? null : velocityMadeGood.getKnots();
|
||||
velocityMadeGood = trackedLeg.getVelocityMadeGood(timePoint, WindPositionMode.EXACT, cache);
|
||||
} else {
|
||||
// check if wind information is available; if so, compute a VMG only based on wind data:
|
||||
velocityMadeGood = trackedRace.getVelocityMadeGood(competitor, timePoint, cache);
|
||||
}
|
||||
result = (velocityMadeGood == null) ? null : velocityMadeGood.getKnots();
|
||||
break;
|
||||
case DISTANCE_TRAVELED:
|
||||
if (trackedLeg != null) {
|
||||
@@ -3097,7 +3101,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
TimePoint newestEvent = trackedRace.getTimePointOfNewestEvent();
|
||||
final TimePoint startTime = from == null ? trackedRace.getStartOfTracking() : new MillisecondsTimePoint(from);
|
||||
final TimePoint endTime = (to == null || to.after(newestEvent.asDate())) ? newestEvent : new MillisecondsTimePoint(to);
|
||||
final long adjustedStepSizeInMillis = (long) Math.max((double) stepSizeInMillis, startTime.until(endTime).divide(new MillisecondsDurationImpl(stepSizeInMillis)));
|
||||
final long adjustedStepSizeInMillis = (long) Math.max((double) stepSizeInMillis, startTime.until(endTime).divide(SailingServiceConstants.MAX_NUMBER_OF_FIXES_TO_QUERY).asMillis());
|
||||
result = new CompetitorsRaceDataDTO(detailType, startTime==null?null:startTime.asDate(), endTime==null?null:endTime.asDate());
|
||||
final int MAX_CACHE_SIZE = SailingServiceConstants.MAX_NUMBER_OF_FIXES_TO_QUERY;
|
||||
final ConcurrentHashMap<TimePoint, WindLegTypeAndLegBearingCache> cachesByTimePoint = new ConcurrentHashMap<>();
|
||||
@@ -5342,6 +5346,20 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
dto.racingProcedure);
|
||||
return new MillisecondsTimePoint(dto.startTime).equals(newStartTime);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<Boolean, Boolean> setFinishingAndEndTime(RaceLogSetFinishingAndFinishTimeDTO dto) {
|
||||
TimePoint newFinsihingTime = getService().setFinishingTime(dto.leaderboardName, dto.raceColumnName,
|
||||
dto.fleetName, dto.authorName, dto.authorPriority,
|
||||
dto.passId, new MillisecondsTimePoint(dto.finishingTime));
|
||||
|
||||
TimePoint newEndTime = getService().setEndTime(dto.leaderboardName, dto.raceColumnName,
|
||||
dto.fleetName, dto.authorName, dto.authorPriority,
|
||||
dto.passId, new MillisecondsTimePoint(dto.finishTime));
|
||||
|
||||
return new Pair<Boolean, Boolean>(new MillisecondsTimePoint(dto.finishingTime).equals(newFinsihingTime),
|
||||
new MillisecondsTimePoint(dto.finishTime).equals(newEndTime));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTrackingTimes(RaceLogSetTrackingTimesDTO dto) throws NotFoundException {
|
||||
@@ -5419,6 +5437,17 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
}
|
||||
return new com.sap.sse.common.Util.Triple<Date, Integer, RacingProcedureType>(result.getA() == null ? null : result.getA().asDate(), result.getB(), result.getC());
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public com.sap.sse.common.Util.Triple<Date, Date, Integer> getFinishingAndFinishTime(String leaderboardName, String raceColumnName, String fleetName) {
|
||||
com.sap.sse.common.Util.Triple<TimePoint, TimePoint, Integer> result = getService().getFinishingAndFinishTime(leaderboardName, raceColumnName, fleetName);
|
||||
if (result == null) {
|
||||
return null;
|
||||
}
|
||||
return new com.sap.sse.common.Util.Triple<>(result.getA() == null ? null : result.getA().asDate(),
|
||||
result.getB() == null ? null : result.getB().asDate(), result.getC());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<String> getAllIgtimiAccountEmailAddresses() {
|
||||
@@ -6876,6 +6905,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
}
|
||||
if (hasBravoTrack) {
|
||||
availableDetailsTypes.add(DetailType.RACE_CURRENT_RIDE_HEIGHT_IN_METERS);
|
||||
availableDetailsTypes.add(DetailType.RACE_CURRENT_DURATION_FOILED_IN_SECONDS);
|
||||
availableDetailsTypes.add(DetailType.RACE_CURRENT_DISTANCE_FOILED_IN_METERS);
|
||||
availableDetailsTypes.add(DetailType.CURRENT_HEEL_IN_DEGREES);
|
||||
availableDetailsTypes.add(DetailType.CURRENT_PITCH_IN_DEGREES);
|
||||
}
|
||||
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
package com.sap.sailing.gwt.ui.shared;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.google.gwt.user.client.rpc.IsSerializable;
|
||||
|
||||
/**
|
||||
* DTO holding all information to set a new start time on a specific race.
|
||||
*/
|
||||
public class RaceLogSetFinishingAndFinishTimeDTO implements IsSerializable {
|
||||
public String leaderboardName;
|
||||
public String raceColumnName;
|
||||
public String fleetName;
|
||||
public int passId;
|
||||
public String authorName;
|
||||
public Integer authorPriority;
|
||||
public Date finishTime;
|
||||
public Date finishingTime;
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "RaceLogSetEndTimeDTO [leaderboardName=" + leaderboardName + ", raceColumnName=" + raceColumnName
|
||||
+ ", fleetName=" + fleetName + ", passId=" + passId + ", authorName=" + authorName + ", authorPriority="
|
||||
+ authorPriority + ", finishTime=" + finishTime + ", endTime=" + finishingTime + "]";
|
||||
}
|
||||
}
|
||||
BIN
Binary file not shown.
|
After Width: | Height: | Size: 160 B |
@@ -66,4 +66,5 @@ Bundle-ClassPath: .
|
||||
Web-ContextPath: /sailingserver
|
||||
Export-Package: com.sap.sailing.server.gateway,
|
||||
com.sap.sailing.server.gateway.jaxrs,
|
||||
com.sap.sailing.server.gateway.jaxrs.spi
|
||||
com.sap.sailing.server.gateway.jaxrs.spi,
|
||||
com.sap.sailing.server.gateway.windimport.bravo
|
||||
+16
-11
@@ -52,14 +52,14 @@ public class SensorDataImportServlet extends AbstractFileUploadServlet {
|
||||
*
|
||||
* @param files
|
||||
* the file items together with the names of the importer to use for importing the respective file's
|
||||
* contents; the importer names are matched against {@link DoubleVectorFixImporter#getType()} for
|
||||
* all importers found registered in the OSGi registry. The first matching importer is used for the
|
||||
* file. The importer is selected on a per-file basis.
|
||||
* contents; the importer names are matched against {@link DoubleVectorFixImporter#getType()} for all
|
||||
* importers found registered in the OSGi registry. The first matching importer is used for the file. The
|
||||
* importer is selected on a per-file basis.
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private Iterable<TrackFileImportDeviceIdentifier> importFiles(Iterable<Pair<String, FileItem>> files)
|
||||
throws IOException {
|
||||
private Iterable<TrackFileImportDeviceIdentifier> importFiles(boolean enableDownsampler,
|
||||
Iterable<Pair<String, FileItem>> files) throws IOException {
|
||||
final Set<TrackFileImportDeviceIdentifier> deviceIds = new HashSet<>();
|
||||
final Map<DeviceIdentifier, TimePoint> from = new HashMap<>();
|
||||
final Map<DeviceIdentifier, TimePoint> to = new HashMap<>();
|
||||
@@ -102,7 +102,7 @@ public class SensorDataImportServlet extends AbstractFileUploadServlet {
|
||||
}
|
||||
}
|
||||
}
|
||||
}, fi.getName(), requestedImporterName, /* downsample */ true);
|
||||
}, fi.getName(), requestedImporterName, enableDownsampler);
|
||||
logger.log(Level.INFO, "Successfully imported file " + requestedImporterName);
|
||||
} catch (FormatNotSupportedException e) {
|
||||
logger.log(Level.INFO, "Failed to import file " + requestedImporterName);
|
||||
@@ -119,10 +119,14 @@ public class SensorDataImportServlet extends AbstractFileUploadServlet {
|
||||
protected void process(List<FileItem> fileItems, HttpServletRequest req, HttpServletResponse resp)
|
||||
throws IOException {
|
||||
String importerName = null;
|
||||
searchForPreferredImporter: for (FileItem fi : fileItems) {
|
||||
if ("preferredImporter".equalsIgnoreCase(fi.getFieldName())) {
|
||||
importerName = fi.getString();
|
||||
break searchForPreferredImporter;
|
||||
boolean enableDownsampler = false;
|
||||
for (FileItem fi : fileItems) {
|
||||
if (fi.isFormField()) {
|
||||
if ("preferredImporter".equalsIgnoreCase(fi.getFieldName())) {
|
||||
importerName = fi.getString();
|
||||
} else if ("downsample".equalsIgnoreCase(fi.getFieldName())) {
|
||||
enableDownsampler = "on".equalsIgnoreCase(fi.getString());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (importerName == null) {
|
||||
@@ -134,7 +138,8 @@ public class SensorDataImportServlet extends AbstractFileUploadServlet {
|
||||
filesAndImporterNames.add(new Pair<>(importerName, fi));
|
||||
}
|
||||
}
|
||||
final Iterable<TrackFileImportDeviceIdentifier> mappingList = importFiles(filesAndImporterNames);
|
||||
final Iterable<TrackFileImportDeviceIdentifier> mappingList = importFiles(enableDownsampler,
|
||||
filesAndImporterNames);
|
||||
resp.setContentType("text/html");
|
||||
for (TrackFileImportDeviceIdentifier mapping : mappingList) {
|
||||
String stringRep = mapping.getId().toString();
|
||||
|
||||
+12
-6
@@ -31,6 +31,8 @@ import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
|
||||
public class BravoWindImportServlet extends AbstractWindImportServlet {
|
||||
private static final String BRAVO_WIND_IMPORT = "Bravo Wind Import";
|
||||
private static final String GZIP_SUFFIX = ".gz";
|
||||
private static final long serialVersionUID = -4547876638456305135L;
|
||||
private static final Logger logger = Logger.getLogger(BravoWindImportServlet.class.getName());
|
||||
|
||||
@@ -42,7 +44,7 @@ public class BravoWindImportServlet extends AbstractWindImportServlet {
|
||||
if (uploadRequest.files != null && !uploadRequest.files.isEmpty()) {
|
||||
sourceName = uploadRequest.files.stream().map(f->f.getName()).collect(Collectors.joining(", "));
|
||||
} else {
|
||||
sourceName = "Bravo Wind Import";
|
||||
sourceName = BRAVO_WIND_IMPORT;
|
||||
}
|
||||
windSource = new WindSourceWithAdditionalID(WindSourceType.EXPEDITION, sourceName + "@" + MillisecondsTimePoint.now());
|
||||
return windSource;
|
||||
@@ -75,7 +77,7 @@ public class BravoWindImportServlet extends AbstractWindImportServlet {
|
||||
for (final Fields field : Fields.values()) {
|
||||
columnsMap.put(field.name(), field.ordinal());
|
||||
}
|
||||
final BaseBravoDataImporterImpl importer = new BaseBravoDataImporterImpl(columnsMap, "BRAVO_WIND");
|
||||
final BaseBravoDataImporterImpl importer = new BaseBravoDataImporterImpl(columnsMap, BRAVO_WIND_IMPORT);
|
||||
final Callback callback = new Callback() {
|
||||
@Override
|
||||
public void addFixes(Iterable<DoubleVectorFix> fixes, TrackFileImportDeviceIdentifier device) {
|
||||
@@ -91,22 +93,26 @@ public class BravoWindImportServlet extends AbstractWindImportServlet {
|
||||
}
|
||||
}
|
||||
};
|
||||
if (filename.toLowerCase().endsWith("zip")) {
|
||||
if (filename.toLowerCase().endsWith(".zip")) {
|
||||
logger.info("Bravo file "+filename+" is a ZIP file");
|
||||
try (final ZipInputStream zipInputStream = new ZipInputStream(inputStream)) {
|
||||
ZipEntry entry;
|
||||
while ((entry=zipInputStream.getNextEntry()) != null) {
|
||||
if (entry.getName().toLowerCase().endsWith(".txt")) {
|
||||
logger.info("Reading Bravo wind data from "+filename+"'s ZIP entry "+entry.getName());
|
||||
importer.importFixes(zipInputStream, callback, filename, filename, /* downsample */ false);
|
||||
importer.importFixes(zipInputStream, callback, entry.getName(), BRAVO_WIND_IMPORT, /* downsample */ false);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (filename.toLowerCase().endsWith("gz")) {
|
||||
final String actualFileName;
|
||||
if (filename.toLowerCase().endsWith(GZIP_SUFFIX)) {
|
||||
inputStream = new GZIPInputStream(inputStream);
|
||||
actualFileName = filename.substring(0, filename.length()-GZIP_SUFFIX.length());
|
||||
} else {
|
||||
actualFileName = filename;
|
||||
}
|
||||
importer.importFixes(inputStream, callback, filename, filename, /* downsample */ false);
|
||||
importer.importFixes(inputStream, callback, actualFileName, BRAVO_WIND_IMPORT, /* downsample */ false);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+2
-2
@@ -69,7 +69,7 @@ public class BaseBravoDataImporterImpl extends AbstractDoubleVectorFixImporter {
|
||||
try {
|
||||
LOG.fine("Import CSV from " + filename);
|
||||
final InputStreamReader isr;
|
||||
if (sourceName.endsWith("gz")) {
|
||||
if (filename.endsWith(".gz")) {
|
||||
LOG.fine("Using gzip stream reader " + filename);
|
||||
isr = new InputStreamReader(new GZIPInputStream(inputStream));
|
||||
} else {
|
||||
@@ -173,6 +173,6 @@ public class BaseBravoDataImporterImpl extends AbstractDoubleVectorFixImporter {
|
||||
}
|
||||
|
||||
private int getTrackColumnCount() {
|
||||
return columnNamesInFileAndTheirValueIndexInResultingDoubleVectorFix.size();
|
||||
return Collections.max(columnNamesInFileAndTheirValueIndexInResultingDoubleVectorFix.values())+1;
|
||||
}
|
||||
}
|
||||
@@ -36,5 +36,5 @@
|
||||
<booleanAttribute key="tracing" value="false"/>
|
||||
<booleanAttribute key="useCustomFeatures" value="false"/>
|
||||
<booleanAttribute key="useDefaultConfigArea" value="false"/>
|
||||
<stringAttribute key="workspace_bundles" value="com.google.gwt.servlet@default:default,com.googlecode.java-diff-utils@default:default,com.googlecode.mgwt@default:default,com.sap.sailing.barbados.resultimport@default:default,com.sap.sailing.competitorimport@default:default,com.sap.sailing.dashboards.gwt@5:true,com.sap.sailing.datamining.provider@default:default,com.sap.sailing.datamining.shared@default:default,com.sap.sailing.datamining@default:default,com.sap.sailing.declination@default:default,com.sap.sailing.domain.common@default:default,com.sap.sailing.domain.deckmanadapter@4:true,com.sap.sailing.domain.expeditionadapter@4:true,com.sap.sailing.domain.igtimiadapter.persistence@default:default,com.sap.sailing.domain.igtimiadapter@default:default,com.sap.sailing.domain.persistence@default:default,com.sap.sailing.domain.racelogtrackingadapter@3:default,com.sap.sailing.domain.shared.android@default:default,com.sap.sailing.domain.swisstimingadapter.persistence@default:default,com.sap.sailing.domain.swisstimingadapter@4:true,com.sap.sailing.domain.swisstimingreplayadapter@4:true,com.sap.sailing.domain.tractracadapter.persistence@default:default,com.sap.sailing.domain.tractracadapter@4:true,com.sap.sailing.domain@default:default,com.sap.sailing.ess40.resultimport@default:default,com.sap.sailing.expeditionconnector.common@default:default,com.sap.sailing.expeditionconnector.persistence@3:true,com.sap.sailing.expeditionconnector@default:default,com.sap.sailing.freg.resultimport@default:default,com.sap.sailing.geocoding@default:default,com.sap.sailing.grib@default:default,com.sap.sailing.gwt.ui@5:true,com.sap.sailing.kiworesultimport@default:default,com.sap.sailing.manage2sail.resultimport@default:default,com.sap.sailing.manage2sail@default:default,com.sap.sailing.monitoring@default:default,com.sap.sailing.news@default:default,com.sap.sailing.nmeaconnector@default:default,com.sap.sailing.polars.datamining.shared@default:default,com.sap.sailing.polars.datamining@default:default,com.sap.sailing.polars@4:true,com.sap.sailing.resultimport@default:default,com.sap.sailing.sailwave.resultimport@default:default,com.sap.sailing.server.gateway.serialization.shared.android@default:default,com.sap.sailing.server.gateway.serialization@default:default,com.sap.sailing.server.gateway@5:true,com.sap.sailing.server.trackfiles@default:default,com.sap.sailing.server@default:default,com.sap.sailing.simulator@default:default,com.sap.sailing.udpconnector@default:default,com.sap.sailing.velum.resultimport@default:default,com.sap.sailing.www@4:true,com.sap.sailing.xrr.resultimport@default:default,com.sap.sailing.xrr.schema@default:default,com.sap.sailing.xrr.structureimport@default:default,com.sap.sailing.yachtscoring.resultimport@default:default,com.sap.sse.common@default:default,com.sap.sse.datamining.annotations@default:default,com.sap.sse.datamining.shared@default:default,com.sap.sse.datamining@default:default,com.sap.sse.filestorage@default:default,com.sap.sse.gwt.adminconsole@default:default,com.sap.sse.gwt@default:default,com.sap.sse.mail@4:true,com.sap.sse.mongodb@default:default,com.sap.sse.operationaltransformation@default:default,com.sap.sse.replication@4:true,com.sap.sse.security.common@default:default,com.sap.sse.security.ui@6:true,com.sap.sse.security.userstore.mongodb@4:true,com.sap.sse.security@5:true,com.sap.sse.shared.android@default:default,com.sap.sse@default:default,com.tractrac.clientmodule@default:default,net.sf.marineapi@default:default,org.json.simple@default:default,org.moxieapps.gwt.highcharts@default:default"/>
|
||||
<stringAttribute key="workspace_bundles" value="com.google.gwt.servlet@default:default,com.googlecode.java-diff-utils@default:default,com.googlecode.mgwt@default:default,com.sap.sailing.barbados.resultimport@default:default,com.sap.sailing.competitorimport@default:default,com.sap.sailing.dashboards.gwt@5:true,com.sap.sailing.datamining.provider@default:default,com.sap.sailing.datamining.shared@default:default,com.sap.sailing.datamining@default:default,com.sap.sailing.declination@default:default,com.sap.sailing.domain.bravoadapter@4:true,com.sap.sailing.domain.common@default:default,com.sap.sailing.domain.deckmanadapter@4:true,com.sap.sailing.domain.expeditionadapter@4:true,com.sap.sailing.domain.igtimiadapter.persistence@default:default,com.sap.sailing.domain.igtimiadapter@default:default,com.sap.sailing.domain.persistence@default:default,com.sap.sailing.domain.racelogtrackingadapter@3:default,com.sap.sailing.domain.shared.android@default:default,com.sap.sailing.domain.swisstimingadapter.persistence@default:default,com.sap.sailing.domain.swisstimingadapter@4:true,com.sap.sailing.domain.swisstimingreplayadapter@4:true,com.sap.sailing.domain.tractracadapter.persistence@default:default,com.sap.sailing.domain.tractracadapter@4:true,com.sap.sailing.domain@default:default,com.sap.sailing.ess40.resultimport@default:default,com.sap.sailing.expeditionconnector.common@default:default,com.sap.sailing.expeditionconnector.persistence@3:true,com.sap.sailing.expeditionconnector@default:default,com.sap.sailing.freg.resultimport@default:default,com.sap.sailing.geocoding@default:default,com.sap.sailing.grib@default:default,com.sap.sailing.gwt.ui@5:true,com.sap.sailing.kiworesultimport@default:default,com.sap.sailing.manage2sail.resultimport@default:default,com.sap.sailing.manage2sail@default:default,com.sap.sailing.monitoring@default:default,com.sap.sailing.news@default:default,com.sap.sailing.nmeaconnector@default:default,com.sap.sailing.polars.datamining.shared@default:default,com.sap.sailing.polars.datamining@default:default,com.sap.sailing.polars@4:true,com.sap.sailing.resultimport@default:default,com.sap.sailing.sailwave.resultimport@default:default,com.sap.sailing.server.gateway.serialization.shared.android@default:default,com.sap.sailing.server.gateway.serialization@default:default,com.sap.sailing.server.gateway@5:true,com.sap.sailing.server.trackfiles@default:default,com.sap.sailing.server@default:default,com.sap.sailing.simulator@default:default,com.sap.sailing.udpconnector@default:default,com.sap.sailing.velum.resultimport@default:default,com.sap.sailing.www@4:true,com.sap.sailing.xrr.resultimport@default:default,com.sap.sailing.xrr.schema@default:default,com.sap.sailing.xrr.structureimport@default:default,com.sap.sailing.yachtscoring.resultimport@default:default,com.sap.sse.common@default:default,com.sap.sse.datamining.annotations@default:default,com.sap.sse.datamining.shared@default:default,com.sap.sse.datamining@default:default,com.sap.sse.filestorage@default:default,com.sap.sse.gwt.adminconsole@default:default,com.sap.sse.gwt@default:default,com.sap.sse.mail@4:true,com.sap.sse.mongodb@default:default,com.sap.sse.operationaltransformation@default:default,com.sap.sse.replication@4:true,com.sap.sse.security.common@default:default,com.sap.sse.security.ui@6:true,com.sap.sse.security.userstore.mongodb@4:true,com.sap.sse.security@5:true,com.sap.sse.shared.android@default:default,com.sap.sse@default:default,com.tractrac.clientmodule@default:default,net.sf.marineapi@default:default,org.json.simple@default:default,org.moxieapps.gwt.highcharts@default:default"/>
|
||||
</launchConfiguration>
|
||||
@@ -92,6 +92,7 @@ import com.sap.sse.common.TypeBasedServiceFinderFactory;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.common.Util.Triple;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.common.search.KeywordQuery;
|
||||
import com.sap.sse.common.search.Result;
|
||||
import com.sap.sse.common.search.Searchable;
|
||||
@@ -553,12 +554,47 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch
|
||||
*/
|
||||
TimePoint setStartTimeAndProcedure(String leaderboardName, String raceColumnName, String fleetName, String authorName,
|
||||
int authorPriority, int passId, TimePoint logicalTimePoint, TimePoint startTime, RacingProcedureType racingProcedure);
|
||||
|
||||
/**
|
||||
* Forces a new end time identified by the passed parameters.
|
||||
* @param leaderboardName name of the RaceLog's leaderboard.
|
||||
* @param raceColumnName name of the RaceLog's column
|
||||
* @param fleetName name of the RaceLog's fleet
|
||||
* @param authorName name of the {@link AbstractLogEventAuthor} the {@link RaceLogStartTimeEvent} will be created with
|
||||
* @param authorPriority priority of the author.
|
||||
* @param passId Pass identifier of the new start time event.
|
||||
* @param logicalTimePoint logical {@link TimePoint} of the new event.
|
||||
* @return
|
||||
*/
|
||||
TimePoint setEndTime(String leaderboardName, String raceColumnName, String fleetName, String authorName,
|
||||
int authorPriority, int passId, TimePoint logicalTimePoint);
|
||||
|
||||
/**
|
||||
* Forces a new finishing time identified by the passed parameters.
|
||||
* @param leaderboardName name of the RaceLog's leaderboard.
|
||||
* @param raceColumnName name of the RaceLog's column
|
||||
* @param fleetName name of the RaceLog's fleet
|
||||
* @param authorName name of the {@link AbstractLogEventAuthor} the {@link RaceLogStartTimeEvent} will be created with
|
||||
* @param authorPriority priority of the author.
|
||||
* @param passId Pass identifier of the new start time event.
|
||||
* @param logicalTimePoint logical {@link TimePoint} of the new event.
|
||||
* @return
|
||||
*/
|
||||
TimePoint setFinishingTime(String leaderboardName, String raceColumnName, String fleetName, String authorName,
|
||||
Integer authorPriority, int passId, MillisecondsTimePoint millisecondsTimePoint);
|
||||
|
||||
/**
|
||||
* Gets the start time, pass identifier and racing procedure for the queried race. Start time might be <code>null</code>.
|
||||
*/
|
||||
Util.Triple<TimePoint, Integer, RacingProcedureType> getStartTimeAndProcedure(String leaderboardName, String raceColumnName, String fleetName);
|
||||
|
||||
/**
|
||||
* Gets the finishing and finish times as well as the pass identifier for the queried race. The first TimePoint is the
|
||||
* finishing time, the second on is the finish time. Finishing and/or finish times might be <code>null</code>.
|
||||
*/
|
||||
com.sap.sse.common.Util.Triple<TimePoint, TimePoint, Integer> getFinishingAndFinishTime(
|
||||
String leaderboardName, String raceColumnName, String fleetName);
|
||||
|
||||
MongoObjectFactory getMongoObjectFactory();
|
||||
|
||||
DomainObjectFactory getDomainObjectFactory();
|
||||
|
||||
+53
@@ -54,7 +54,10 @@ 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.abstractlog.race.SimpleRaceLogIdentifier;
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.FinishedTimeFinder;
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.FinishingTimeFinder;
|
||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
|
||||
import com.sap.sailing.domain.abstractlog.race.impl.RaceLogRaceStatusEventImpl;
|
||||
import com.sap.sailing.domain.abstractlog.race.state.RaceState;
|
||||
import com.sap.sailing.domain.abstractlog.race.state.ReadonlyRaceState;
|
||||
import com.sap.sailing.domain.abstractlog.race.state.impl.RaceStateImpl;
|
||||
@@ -119,6 +122,7 @@ import com.sap.sailing.domain.common.dto.RegattaCreationParametersDTO;
|
||||
import com.sap.sailing.domain.common.dto.SeriesCreationParametersDTO;
|
||||
import com.sap.sailing.domain.common.impl.DataImportProgressImpl;
|
||||
import com.sap.sailing.domain.common.media.MediaTrack;
|
||||
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
|
||||
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
@@ -3507,6 +3511,38 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint setEndTime(String leaderboardName, String raceColumnName, String fleetName,
|
||||
String authorName, int authorPriority, int passId, TimePoint logicalTimePoint) {
|
||||
RaceLog raceLog = getRaceLog(leaderboardName, raceColumnName, fleetName);
|
||||
Leaderboard leaderboard = getLeaderboardByName(leaderboardName);
|
||||
final TimePoint result;
|
||||
if (leaderboard instanceof HasRegattaLike && raceLog != null) {
|
||||
LogEventAuthorImpl author = new LogEventAuthorImpl(authorName, authorPriority);
|
||||
raceLog.add(new RaceLogRaceStatusEventImpl(logicalTimePoint, author, raceLog.getCurrentPassId(), RaceLogRaceStatus.FINISHED));
|
||||
result = new FinishedTimeFinder(raceLog).analyze();
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint setFinishingTime(String leaderboardName, String raceColumnName, String fleetName,
|
||||
String authorName, Integer authorPriority, int passId, MillisecondsTimePoint logicalTimePoint) {
|
||||
RaceLog raceLog = getRaceLog(leaderboardName, raceColumnName, fleetName);
|
||||
Leaderboard leaderboard = getLeaderboardByName(leaderboardName);
|
||||
final TimePoint result;
|
||||
if (leaderboard instanceof HasRegattaLike && raceLog != null) {
|
||||
LogEventAuthorImpl author = new LogEventAuthorImpl(authorName, authorPriority);
|
||||
raceLog.add(new RaceLogRaceStatusEventImpl(logicalTimePoint, author, raceLog.getCurrentPassId(), RaceLogRaceStatus.FINISHING));
|
||||
result = new FinishingTimeFinder(raceLog).analyze();
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public RaceLog getRaceLog(String leaderboardName, String raceColumnName, String fleetName) {
|
||||
Leaderboard leaderboard = getLeaderboardByName(leaderboardName);
|
||||
@@ -3565,6 +3601,22 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public com.sap.sse.common.Util.Triple<TimePoint, TimePoint, Integer> getFinishingAndFinishTime(
|
||||
String leaderboardName, String raceColumnName, String fleetName) {
|
||||
RaceLog raceLog = getRaceLog(leaderboardName, raceColumnName, fleetName);
|
||||
Leaderboard leaderboard = getLeaderboardByName(leaderboardName);
|
||||
final Triple<TimePoint, TimePoint, Integer> result;
|
||||
if (leaderboard instanceof HasRegattaLike && raceLog != null) {
|
||||
ReadonlyRaceState state = ReadonlyRaceStateImpl.create(/* race log resolver */ this, raceLog);
|
||||
result = new com.sap.sse.common.Util.Triple<>(state.getFinishingTime(), state.getFinishedTime(),
|
||||
raceLog.getCurrentPassId());
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private Iterable<WindTrackerFactory> getWindTrackerFactories() {
|
||||
final Set<WindTrackerFactory> result;
|
||||
@@ -4149,4 +4201,5 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
|
||||
public AnniversaryRaceDeterminator getAnniversaryRaceDeterminator() {
|
||||
return anniversaryRaceDeterminator;
|
||||
}
|
||||
|
||||
}
|
||||
+17
@@ -60,6 +60,7 @@ import com.sap.sailing.domain.tracking.WindStore;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.IsManagedByCache;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
@@ -357,6 +358,16 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
||||
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) {
|
||||
@@ -691,4 +702,10 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
||||
public Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint,
|
||||
WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -22,6 +22,20 @@
|
||||
<h2 class="releaseHeadline">Release Notes - Administration Console</h2>
|
||||
<div class="innerContent">
|
||||
|
||||
<h2 class="articleSubheadline">November 2017</h2>
|
||||
<ul class="bulletList">
|
||||
<li>In addition to the "Set start time" dialog there's now a "Set finishing and end time dialog".
|
||||
This dialog makes it possible to set the finishing as well as the end time in the RaceLog.
|
||||
It is intended to be used in cases where a race wasn't correctly finished using the race management app.
|
||||
</li>
|
||||
<li>New GPS fix, wind and sensor data importers have been implemented: "Bravo" and "Expedition."
|
||||
</li>
|
||||
<li>An upgrade to TracAPI 3.8.0 is expected to have fixed the issue with static marks not providing
|
||||
a position fix within a race's tracking time interval when the tracking start is set after the
|
||||
static position was entered.
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h2 class="articleSubheadline">September 2017</h2>
|
||||
<ul class="bulletList">
|
||||
<li>Authentication for Igtimi accounts is now possible with an OAuth process.
|
||||
|
||||
@@ -2,7 +2,7 @@ Manifest-Version: 1.0
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: TracTrac Client module
|
||||
Bundle-SymbolicName: com.tractrac.clientmodule
|
||||
Bundle-Version: 3.7.7
|
||||
Bundle-Version: 3.8.0
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Bundle-ClassPath: .,
|
||||
lib/TracAPI.jar
|
||||
|
||||
@@ -16,6 +16,28 @@ It contains also some files:
|
||||
- test.sh -> script that compiles the code in the src folder, creates the test.jar library and execute the code of the example.
|
||||
- Manifest.txt -> manifest used to create the test.jar file
|
||||
|
||||
********************************************
|
||||
************* TracAPI 3.8.0 ****************
|
||||
********************************************
|
||||
This is a final version. It change the signature of a method, breaking the
|
||||
backward compatibility:
|
||||
|
||||
- com.tractrac.model.lib.api.event.RaceStatusType enum. UNKNOWN value of this enum type has been renamed to PENDING.
|
||||
|
||||
This version provides a new JavaDoc version.
|
||||
|
||||
Release date: 22/11/2017
|
||||
Build number: 14453
|
||||
|
||||
1) Features
|
||||
|
||||
- com.tractrac.model.lib.api.event.RaceStatusType enum. UNKNOWN value of this enum type has been renamed to PENDING.
|
||||
(Requested by Chris Terkelsen 24/10/2017)
|
||||
- Support for sensor data. IRaceSubscriber has been updated adding methods to retrieve sensor data (Requested by SAP)
|
||||
- When the tracking of a race is initialized, TracAPI also sends the positions of the controls using the tracking start time
|
||||
as timestamp (Requested by Axel Uhl, 14/11/2017)
|
||||
|
||||
|
||||
********************************************
|
||||
************* TracAPI 3.7.7 ****************
|
||||
********************************************
|
||||
@@ -23,7 +45,7 @@ This is a final version. It fixes bugs in the implementation
|
||||
It keeps the backward compatibility.
|
||||
|
||||
Release date: 29/10/2017
|
||||
Build number: 14418
|
||||
Build number: 14453
|
||||
|
||||
1) Bugs
|
||||
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<!-- NewPage -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Generated by javadoc (1.8.0_111) on Thu Jun 29 10:20:59 UTC 2017 -->
|
||||
<!-- Generated by javadoc (1.8.0_111) on Wed Nov 22 10:19:51 UTC 2017 -->
|
||||
<title>All Classes</title>
|
||||
<meta name="date" content="2017-06-29">
|
||||
<meta name="date" content="2017-11-22">
|
||||
<link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
|
||||
<script type="text/javascript" src="script.js"></script>
|
||||
</head>
|
||||
@@ -13,24 +13,15 @@
|
||||
<div class="indexContainer">
|
||||
<ul>
|
||||
<li><a href="com/tractrac/model/lib/api/attachment/AbstractAttachable.html" title="class in com.tractrac.model.lib.api.attachment" target="classFrame">AbstractAttachable</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/AbstractJSONTest.html" title="class in com.tractrac.model.lib.api.event" target="classFrame">AbstractJSONTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/AbstractModelTest.html" title="class in com.tractrac.model.lib.api" target="classFrame">AbstractModelTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/AbstractSubscriptionTest.html" title="class in com.tractrac.subscription.lib.api" target="classFrame">AbstractSubscriptionTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/attachment/AttachmentsTest.html" title="class in com.tractrac.model.lib.api.attachment" target="classFrame">AttachmentsTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/competitor/CompetitorsEventTest.html" title="class in com.tractrac.subscription.lib.api.competitor" target="classFrame">CompetitorsEventTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/competitor/ControlPassingsTest.html" title="class in com.tractrac.subscription.lib.api.competitor" target="classFrame">ControlPassingsTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/route/ControlRouteTest.html" title="class in com.tractrac.model.lib.api.route" target="classFrame">ControlRouteTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/ControlsEventTest.html" title="class in com.tractrac.subscription.lib.api.control" target="classFrame">ControlsEventTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/CreateModelException.html" title="class in com.tractrac.model.lib.api.event" target="classFrame">CreateModelException</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/DataSource.html" title="enum in com.tractrac.model.lib.api.event" target="classFrame">DataSource</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/EventsFromClubsJSONTest.html" title="class in com.tractrac.model.lib.api.event" target="classFrame">EventsFromClubsJSONTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/EventsFromJSONTest.html" title="class in com.tractrac.model.lib.api.event" target="classFrame">EventsFromJSONTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/EventType.html" title="enum in com.tractrac.model.lib.api.event" target="classFrame">EventType</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/attachment/IAttachable.html" title="interface in com.tractrac.model.lib.api.attachment" target="classFrame"><span class="interfaceName">IAttachable</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/attachment/IAttachmentKey.html" title="interface in com.tractrac.model.lib.api.attachment" target="classFrame"><span class="interfaceName">IAttachmentKey</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/attachment/IAttachmentManager.html" title="interface in com.tractrac.model.lib.api.attachment" target="classFrame"><span class="interfaceName">IAttachmentManager</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/ICompetitor.html" title="interface in com.tractrac.model.lib.api.event" target="classFrame"><span class="interfaceName">ICompetitor</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/ICompetitorClass.html" title="interface in com.tractrac.model.lib.api.event" target="classFrame"><span class="interfaceName">ICompetitorClass</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/competitor/ICompetitorSensorDataListener.html" title="interface in com.tractrac.subscription.lib.api.competitor" target="classFrame"><span class="interfaceName">ICompetitorSensorDataListener</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/competitor/ICompetitorsListener.html" title="interface in com.tractrac.subscription.lib.api.competitor" target="classFrame"><span class="interfaceName">ICompetitorsListener</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/event/IConnectionStatusListener.html" title="interface in com.tractrac.subscription.lib.api.event" target="classFrame"><span class="interfaceName">IConnectionStatusListener</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/route/IControl.html" title="interface in com.tractrac.model.lib.api.route" target="classFrame"><span class="interfaceName">IControl</span></a></li>
|
||||
@@ -39,6 +30,7 @@
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/IControlPassingsListener.html" title="interface in com.tractrac.subscription.lib.api.control" target="classFrame"><span class="interfaceName">IControlPassingsListener</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/route/IControlPoint.html" title="interface in com.tractrac.model.lib.api.route" target="classFrame"><span class="interfaceName">IControlPoint</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/IControlPointPositionListener.html" title="interface in com.tractrac.subscription.lib.api.control" target="classFrame"><span class="interfaceName">IControlPointPositionListener</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/IControlPointSensorDataListener.html" title="interface in com.tractrac.subscription.lib.api.control" target="classFrame"><span class="interfaceName">IControlPointSensorDataListener</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/route/IControlRoute.html" title="interface in com.tractrac.model.lib.api.route" target="classFrame"><span class="interfaceName">IControlRoute</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/IControlRouteChangeListener.html" title="interface in com.tractrac.subscription.lib.api.control" target="classFrame"><span class="interfaceName">IControlRouteChangeListener</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/IControlsListener.html" title="interface in com.tractrac.subscription.lib.api.control" target="classFrame"><span class="interfaceName">IControlsListener</span></a></li>
|
||||
@@ -53,8 +45,6 @@
|
||||
<li><a href="com/tractrac/model/lib/api/IIdentifiable.html" title="interface in com.tractrac.model.lib.api" target="classFrame"><span class="interfaceName">IIdentifiable</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/event/ILiveDataEvent.html" title="interface in com.tractrac.subscription.lib.api.event" target="classFrame"><span class="interfaceName">ILiveDataEvent</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/event/ILiveDataEvent.StatusType.html" title="enum in com.tractrac.subscription.lib.api.event" target="classFrame">ILiveDataEvent.StatusType</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/ILocalDataserver.html" title="interface in com.tractrac.subscription.lib.api" target="classFrame"><span class="interfaceName">ILocalDataserver</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/ILocalDataServerFactory.html" title="interface in com.tractrac.subscription.lib.api" target="classFrame"><span class="interfaceName">ILocalDataServerFactory</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/event/IMessage.html" title="interface in com.tractrac.subscription.lib.api.event" target="classFrame"><span class="interfaceName">IMessage</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/data/IMessageData.html" title="interface in com.tractrac.model.lib.api.data" target="classFrame"><span class="interfaceName">IMessageData</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/metadata/IMetadata.html" title="interface in com.tractrac.model.lib.api.metadata" target="classFrame"><span class="interfaceName">IMetadata</span></a></li>
|
||||
@@ -83,6 +73,7 @@
|
||||
<li><a href="com/tractrac/model/lib/api/route/IRoute.html" title="interface in com.tractrac.model.lib.api.route" target="classFrame"><span class="interfaceName">IRoute</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/route/IRoutesListener.html" title="interface in com.tractrac.subscription.lib.api.route" target="classFrame"><span class="interfaceName">IRoutesListener</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/route/ISegment.html" title="interface in com.tractrac.model.lib.api.route" target="classFrame"><span class="interfaceName">ISegment</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/sensor/ISensorData.html" title="interface in com.tractrac.model.lib.api.sensor" target="classFrame"><span class="interfaceName">ISensorData</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/event/IServerTimeListener.html" title="interface in com.tractrac.subscription.lib.api.event" target="classFrame"><span class="interfaceName">IServerTimeListener</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/data/IStartStopData.html" title="interface in com.tractrac.model.lib.api.data" target="classFrame"><span class="interfaceName">IStartStopData</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/race/IStartStopTimesChangeListener.html" title="interface in com.tractrac.subscription.lib.api.race" target="classFrame"><span class="interfaceName">IStartStopTimesChangeListener</span></a></li>
|
||||
@@ -93,41 +84,14 @@
|
||||
<li><a href="com/tractrac/subscription/lib/api/ISubscriberListener.html" title="interface in com.tractrac.subscription.lib.api" target="classFrame"><span class="interfaceName">ISubscriberListener</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/ITeam.html" title="interface in com.tractrac.model.lib.api.event" target="classFrame"><span class="interfaceName">ITeam</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/data/ITimeData.html" title="interface in com.tractrac.model.lib.api.data" target="classFrame"><span class="interfaceName">ITimeData</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/metadata/MetadataTest.html" title="class in com.tractrac.model.lib.api.metadata" target="classFrame">MetadataTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/ModelLocator.html" title="class in com.tractrac.model.lib.api" target="classFrame">ModelLocator</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/ModelLocatorTest.html" title="class in com.tractrac.model.lib.api" target="classFrame">ModelLocatorTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/competitor/PositionEventTest.html" title="class in com.tractrac.subscription.lib.api.competitor" target="classFrame">PositionEventTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/RaceCompetitorStatusType.html" title="enum in com.tractrac.model.lib.api.event" target="classFrame">RaceCompetitorStatusType</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/RaceCompetitorTest.html" title="class in com.tractrac.model.lib.api.event" target="classFrame">RaceCompetitorTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/race/RaceCompetitorTest.html" title="class in com.tractrac.subscription.lib.api.race" target="classFrame">RaceCompetitorTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/RaceFromParameterFileTest.html" title="class in com.tractrac.model.lib.api.event" target="classFrame">RaceFromParameterFileTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/RaceInitializedTest.html" title="class in com.tractrac.model.lib.api.event" target="classFrame">RaceInitializedTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/RaceLoadingException.html" title="class in com.tractrac.model.lib.api.event" target="classFrame">RaceLoadingException</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/RaceSerieTest.html" title="class in com.tractrac.model.lib.api.event" target="classFrame">RaceSerieTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/race/RacesEventTest.html" title="class in com.tractrac.subscription.lib.api.race" target="classFrame">RacesEventTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/RacesFromJSONTest.html" title="class in com.tractrac.model.lib.api.event" target="classFrame">RacesFromJSONTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/race/RacesListener.html" title="class in com.tractrac.subscription.lib.api.race" target="classFrame">RacesListener</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/RaceStatusType.html" title="enum in com.tractrac.model.lib.api.event" target="classFrame">RaceStatusType</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/RaceVisibilityType.html" title="enum in com.tractrac.model.lib.api.event" target="classFrame">RaceVisibilityType</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/ReadHDRaceTest.html" title="class in com.tractrac.subscription.lib.api" target="classFrame">ReadHDRaceTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/ReadParameterFileTest.html" title="class in com.tractrac.model.lib.api" target="classFrame">ReadParameterFileTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/route/ReadRouteTest.html" title="class in com.tractrac.model.lib.api.route" target="classFrame">ReadRouteTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/route/ReadTextPathRouteTests.html" title="class in com.tractrac.model.lib.api.route" target="classFrame">ReadTextPathRouteTests</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/ReconnectAddingControlsTest.html" title="class in com.tractrac.subscription.lib.api.control" target="classFrame">ReconnectAddingControlsTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/competitor/ReconnectAddingEntriesTest.html" title="class in com.tractrac.subscription.lib.api.competitor" target="classFrame">ReconnectAddingEntriesTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/ReconnectWithStaticControlsTest.html" title="class in com.tractrac.subscription.lib.api.control" target="classFrame">ReconnectWithStaticControlsTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/SameEventTest.html" title="class in com.tractrac.model.lib.api.event" target="classFrame">SameEventTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/StartTimeType.html" title="enum in com.tractrac.model.lib.api.event" target="classFrame">StartTimeType</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/StartTimeTypeTest.html" title="class in com.tractrac.model.lib.api.event" target="classFrame">StartTimeTypeTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/StaticControlInSecondRace.html" title="class in com.tractrac.subscription.lib.api.control" target="classFrame">StaticControlInSecondRace</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/StaticControlTest.html" title="class in com.tractrac.subscription.lib.api.control" target="classFrame">StaticControlTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/race/StaticRaceStartStopTimesChangeTest.html" title="class in com.tractrac.subscription.lib.api.race" target="classFrame">StaticRaceStartStopTimesChangeTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/StaticRoutesChangesTest.html" title="class in com.tractrac.subscription.lib.api.control" target="classFrame">StaticRoutesChangesTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/SubscriberInitializationException.html" title="class in com.tractrac.subscription.lib.api" target="classFrame">SubscriberInitializationException</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/SubscriptionLocator.html" title="class in com.tractrac.subscription.lib.api" target="classFrame">SubscriptionLocator</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/SubscriptionLocatorTest.html" title="class in com.tractrac.subscription.lib.api" target="classFrame">SubscriptionLocatorTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/SubscriptionTestLocator.html" title="class in com.tractrac.subscription.lib.api" target="classFrame">SubscriptionTestLocator</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/race/UnsubscriptionTest.html" title="class in com.tractrac.subscription.lib.api.race" target="classFrame">UnsubscriptionTest</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<!-- NewPage -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Generated by javadoc (1.8.0_111) on Thu Jun 29 10:20:59 UTC 2017 -->
|
||||
<!-- Generated by javadoc (1.8.0_111) on Wed Nov 22 10:19:51 UTC 2017 -->
|
||||
<title>All Classes</title>
|
||||
<meta name="date" content="2017-06-29">
|
||||
<meta name="date" content="2017-11-22">
|
||||
<link rel="stylesheet" type="text/css" href="stylesheet.css" title="Style">
|
||||
<script type="text/javascript" src="script.js"></script>
|
||||
</head>
|
||||
@@ -13,24 +13,15 @@
|
||||
<div class="indexContainer">
|
||||
<ul>
|
||||
<li><a href="com/tractrac/model/lib/api/attachment/AbstractAttachable.html" title="class in com.tractrac.model.lib.api.attachment">AbstractAttachable</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/AbstractJSONTest.html" title="class in com.tractrac.model.lib.api.event">AbstractJSONTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/AbstractModelTest.html" title="class in com.tractrac.model.lib.api">AbstractModelTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/AbstractSubscriptionTest.html" title="class in com.tractrac.subscription.lib.api">AbstractSubscriptionTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/attachment/AttachmentsTest.html" title="class in com.tractrac.model.lib.api.attachment">AttachmentsTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/competitor/CompetitorsEventTest.html" title="class in com.tractrac.subscription.lib.api.competitor">CompetitorsEventTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/competitor/ControlPassingsTest.html" title="class in com.tractrac.subscription.lib.api.competitor">ControlPassingsTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/route/ControlRouteTest.html" title="class in com.tractrac.model.lib.api.route">ControlRouteTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/ControlsEventTest.html" title="class in com.tractrac.subscription.lib.api.control">ControlsEventTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/CreateModelException.html" title="class in com.tractrac.model.lib.api.event">CreateModelException</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/DataSource.html" title="enum in com.tractrac.model.lib.api.event">DataSource</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/EventsFromClubsJSONTest.html" title="class in com.tractrac.model.lib.api.event">EventsFromClubsJSONTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/EventsFromJSONTest.html" title="class in com.tractrac.model.lib.api.event">EventsFromJSONTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/EventType.html" title="enum in com.tractrac.model.lib.api.event">EventType</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/attachment/IAttachable.html" title="interface in com.tractrac.model.lib.api.attachment"><span class="interfaceName">IAttachable</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/attachment/IAttachmentKey.html" title="interface in com.tractrac.model.lib.api.attachment"><span class="interfaceName">IAttachmentKey</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/attachment/IAttachmentManager.html" title="interface in com.tractrac.model.lib.api.attachment"><span class="interfaceName">IAttachmentManager</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/ICompetitor.html" title="interface in com.tractrac.model.lib.api.event"><span class="interfaceName">ICompetitor</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/ICompetitorClass.html" title="interface in com.tractrac.model.lib.api.event"><span class="interfaceName">ICompetitorClass</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/competitor/ICompetitorSensorDataListener.html" title="interface in com.tractrac.subscription.lib.api.competitor"><span class="interfaceName">ICompetitorSensorDataListener</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/competitor/ICompetitorsListener.html" title="interface in com.tractrac.subscription.lib.api.competitor"><span class="interfaceName">ICompetitorsListener</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/event/IConnectionStatusListener.html" title="interface in com.tractrac.subscription.lib.api.event"><span class="interfaceName">IConnectionStatusListener</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/route/IControl.html" title="interface in com.tractrac.model.lib.api.route"><span class="interfaceName">IControl</span></a></li>
|
||||
@@ -39,6 +30,7 @@
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/IControlPassingsListener.html" title="interface in com.tractrac.subscription.lib.api.control"><span class="interfaceName">IControlPassingsListener</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/route/IControlPoint.html" title="interface in com.tractrac.model.lib.api.route"><span class="interfaceName">IControlPoint</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/IControlPointPositionListener.html" title="interface in com.tractrac.subscription.lib.api.control"><span class="interfaceName">IControlPointPositionListener</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/IControlPointSensorDataListener.html" title="interface in com.tractrac.subscription.lib.api.control"><span class="interfaceName">IControlPointSensorDataListener</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/route/IControlRoute.html" title="interface in com.tractrac.model.lib.api.route"><span class="interfaceName">IControlRoute</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/IControlRouteChangeListener.html" title="interface in com.tractrac.subscription.lib.api.control"><span class="interfaceName">IControlRouteChangeListener</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/IControlsListener.html" title="interface in com.tractrac.subscription.lib.api.control"><span class="interfaceName">IControlsListener</span></a></li>
|
||||
@@ -53,8 +45,6 @@
|
||||
<li><a href="com/tractrac/model/lib/api/IIdentifiable.html" title="interface in com.tractrac.model.lib.api"><span class="interfaceName">IIdentifiable</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/event/ILiveDataEvent.html" title="interface in com.tractrac.subscription.lib.api.event"><span class="interfaceName">ILiveDataEvent</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/event/ILiveDataEvent.StatusType.html" title="enum in com.tractrac.subscription.lib.api.event">ILiveDataEvent.StatusType</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/ILocalDataserver.html" title="interface in com.tractrac.subscription.lib.api"><span class="interfaceName">ILocalDataserver</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/ILocalDataServerFactory.html" title="interface in com.tractrac.subscription.lib.api"><span class="interfaceName">ILocalDataServerFactory</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/event/IMessage.html" title="interface in com.tractrac.subscription.lib.api.event"><span class="interfaceName">IMessage</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/data/IMessageData.html" title="interface in com.tractrac.model.lib.api.data"><span class="interfaceName">IMessageData</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/metadata/IMetadata.html" title="interface in com.tractrac.model.lib.api.metadata"><span class="interfaceName">IMetadata</span></a></li>
|
||||
@@ -83,6 +73,7 @@
|
||||
<li><a href="com/tractrac/model/lib/api/route/IRoute.html" title="interface in com.tractrac.model.lib.api.route"><span class="interfaceName">IRoute</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/route/IRoutesListener.html" title="interface in com.tractrac.subscription.lib.api.route"><span class="interfaceName">IRoutesListener</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/route/ISegment.html" title="interface in com.tractrac.model.lib.api.route"><span class="interfaceName">ISegment</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/sensor/ISensorData.html" title="interface in com.tractrac.model.lib.api.sensor"><span class="interfaceName">ISensorData</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/event/IServerTimeListener.html" title="interface in com.tractrac.subscription.lib.api.event"><span class="interfaceName">IServerTimeListener</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/data/IStartStopData.html" title="interface in com.tractrac.model.lib.api.data"><span class="interfaceName">IStartStopData</span></a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/race/IStartStopTimesChangeListener.html" title="interface in com.tractrac.subscription.lib.api.race"><span class="interfaceName">IStartStopTimesChangeListener</span></a></li>
|
||||
@@ -93,41 +84,14 @@
|
||||
<li><a href="com/tractrac/subscription/lib/api/ISubscriberListener.html" title="interface in com.tractrac.subscription.lib.api"><span class="interfaceName">ISubscriberListener</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/ITeam.html" title="interface in com.tractrac.model.lib.api.event"><span class="interfaceName">ITeam</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/data/ITimeData.html" title="interface in com.tractrac.model.lib.api.data"><span class="interfaceName">ITimeData</span></a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/metadata/MetadataTest.html" title="class in com.tractrac.model.lib.api.metadata">MetadataTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/ModelLocator.html" title="class in com.tractrac.model.lib.api">ModelLocator</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/ModelLocatorTest.html" title="class in com.tractrac.model.lib.api">ModelLocatorTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/competitor/PositionEventTest.html" title="class in com.tractrac.subscription.lib.api.competitor">PositionEventTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/RaceCompetitorStatusType.html" title="enum in com.tractrac.model.lib.api.event">RaceCompetitorStatusType</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/RaceCompetitorTest.html" title="class in com.tractrac.model.lib.api.event">RaceCompetitorTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/race/RaceCompetitorTest.html" title="class in com.tractrac.subscription.lib.api.race">RaceCompetitorTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/RaceFromParameterFileTest.html" title="class in com.tractrac.model.lib.api.event">RaceFromParameterFileTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/RaceInitializedTest.html" title="class in com.tractrac.model.lib.api.event">RaceInitializedTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/RaceLoadingException.html" title="class in com.tractrac.model.lib.api.event">RaceLoadingException</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/RaceSerieTest.html" title="class in com.tractrac.model.lib.api.event">RaceSerieTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/race/RacesEventTest.html" title="class in com.tractrac.subscription.lib.api.race">RacesEventTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/RacesFromJSONTest.html" title="class in com.tractrac.model.lib.api.event">RacesFromJSONTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/race/RacesListener.html" title="class in com.tractrac.subscription.lib.api.race">RacesListener</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/RaceStatusType.html" title="enum in com.tractrac.model.lib.api.event">RaceStatusType</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/RaceVisibilityType.html" title="enum in com.tractrac.model.lib.api.event">RaceVisibilityType</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/ReadHDRaceTest.html" title="class in com.tractrac.subscription.lib.api">ReadHDRaceTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/ReadParameterFileTest.html" title="class in com.tractrac.model.lib.api">ReadParameterFileTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/route/ReadRouteTest.html" title="class in com.tractrac.model.lib.api.route">ReadRouteTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/route/ReadTextPathRouteTests.html" title="class in com.tractrac.model.lib.api.route">ReadTextPathRouteTests</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/ReconnectAddingControlsTest.html" title="class in com.tractrac.subscription.lib.api.control">ReconnectAddingControlsTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/competitor/ReconnectAddingEntriesTest.html" title="class in com.tractrac.subscription.lib.api.competitor">ReconnectAddingEntriesTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/ReconnectWithStaticControlsTest.html" title="class in com.tractrac.subscription.lib.api.control">ReconnectWithStaticControlsTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/SameEventTest.html" title="class in com.tractrac.model.lib.api.event">SameEventTest</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/StartTimeType.html" title="enum in com.tractrac.model.lib.api.event">StartTimeType</a></li>
|
||||
<li><a href="com/tractrac/model/lib/api/event/StartTimeTypeTest.html" title="class in com.tractrac.model.lib.api.event">StartTimeTypeTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/StaticControlInSecondRace.html" title="class in com.tractrac.subscription.lib.api.control">StaticControlInSecondRace</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/StaticControlTest.html" title="class in com.tractrac.subscription.lib.api.control">StaticControlTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/race/StaticRaceStartStopTimesChangeTest.html" title="class in com.tractrac.subscription.lib.api.race">StaticRaceStartStopTimesChangeTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/control/StaticRoutesChangesTest.html" title="class in com.tractrac.subscription.lib.api.control">StaticRoutesChangesTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/SubscriberInitializationException.html" title="class in com.tractrac.subscription.lib.api">SubscriberInitializationException</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/SubscriptionLocator.html" title="class in com.tractrac.subscription.lib.api">SubscriptionLocator</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/SubscriptionLocatorTest.html" title="class in com.tractrac.subscription.lib.api">SubscriptionLocatorTest</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/SubscriptionTestLocator.html" title="class in com.tractrac.subscription.lib.api">SubscriptionTestLocator</a></li>
|
||||
<li><a href="com/tractrac/subscription/lib/api/race/UnsubscriptionTest.html" title="class in com.tractrac.subscription.lib.api.race">UnsubscriptionTest</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
+4
-4
@@ -2,9 +2,9 @@
|
||||
<!-- NewPage -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Generated by javadoc (1.8.0_111) on Thu Jun 29 10:20:59 UTC 2017 -->
|
||||
<!-- Generated by javadoc (1.8.0_111) on Wed Nov 22 10:19:51 UTC 2017 -->
|
||||
<title>IIdentifiable</title>
|
||||
<meta name="date" content="2017-06-29">
|
||||
<meta name="date" content="2017-11-22">
|
||||
<link rel="stylesheet" type="text/css" href="../../../../../stylesheet.css" title="Style">
|
||||
<script type="text/javascript" src="../../../../../script.js"></script>
|
||||
</head>
|
||||
@@ -48,7 +48,7 @@ var activeTableTab = "activeTableTab";
|
||||
</div>
|
||||
<div class="subNav">
|
||||
<ul class="navList">
|
||||
<li><a href="../../../../../com/tractrac/model/lib/api/AbstractModelTest.html" title="class in com.tractrac.model.lib.api"><span class="typeNameLink">Prev Class</span></a></li>
|
||||
<li>Prev Class</li>
|
||||
<li><a href="../../../../../com/tractrac/model/lib/api/INamed.html" title="interface in com.tractrac.model.lib.api"><span class="typeNameLink">Next Class</span></a></li>
|
||||
</ul>
|
||||
<ul class="navList">
|
||||
@@ -198,7 +198,7 @@ extends <a href="../../../../../com/tractrac/model/lib/api/attachment/IAttachabl
|
||||
</div>
|
||||
<div class="subNav">
|
||||
<ul class="navList">
|
||||
<li><a href="../../../../../com/tractrac/model/lib/api/AbstractModelTest.html" title="class in com.tractrac.model.lib.api"><span class="typeNameLink">Prev Class</span></a></li>
|
||||
<li>Prev Class</li>
|
||||
<li><a href="../../../../../com/tractrac/model/lib/api/INamed.html" title="interface in com.tractrac.model.lib.api"><span class="typeNameLink">Next Class</span></a></li>
|
||||
</ul>
|
||||
<ul class="navList">
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<!-- NewPage -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Generated by javadoc (1.8.0_111) on Thu Jun 29 10:20:59 UTC 2017 -->
|
||||
<!-- Generated by javadoc (1.8.0_111) on Wed Nov 22 10:19:51 UTC 2017 -->
|
||||
<title>INamed</title>
|
||||
<meta name="date" content="2017-06-29">
|
||||
<meta name="date" content="2017-11-22">
|
||||
<link rel="stylesheet" type="text/css" href="../../../../../stylesheet.css" title="Style">
|
||||
<script type="text/javascript" src="../../../../../script.js"></script>
|
||||
</head>
|
||||
|
||||
@@ -2,9 +2,9 @@
|
||||
<!-- NewPage -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Generated by javadoc (1.8.0_111) on Thu Jun 29 10:20:59 UTC 2017 -->
|
||||
<!-- Generated by javadoc (1.8.0_111) on Wed Nov 22 10:19:51 UTC 2017 -->
|
||||
<title>ModelLocator</title>
|
||||
<meta name="date" content="2017-06-29">
|
||||
<meta name="date" content="2017-11-22">
|
||||
<link rel="stylesheet" type="text/css" href="../../../../../stylesheet.css" title="Style">
|
||||
<script type="text/javascript" src="../../../../../script.js"></script>
|
||||
</head>
|
||||
@@ -49,7 +49,7 @@ var activeTableTab = "activeTableTab";
|
||||
<div class="subNav">
|
||||
<ul class="navList">
|
||||
<li><a href="../../../../../com/tractrac/model/lib/api/INamed.html" title="interface in com.tractrac.model.lib.api"><span class="typeNameLink">Prev Class</span></a></li>
|
||||
<li><a href="../../../../../com/tractrac/model/lib/api/ModelLocatorTest.html" title="class in com.tractrac.model.lib.api"><span class="typeNameLink">Next Class</span></a></li>
|
||||
<li>Next Class</li>
|
||||
</ul>
|
||||
<ul class="navList">
|
||||
<li><a href="../../../../../index.html?com/tractrac/model/lib/api/ModelLocator.html" target="_top">Frames</a></li>
|
||||
@@ -452,7 +452,7 @@ extends java.lang.Object</pre>
|
||||
<div class="subNav">
|
||||
<ul class="navList">
|
||||
<li><a href="../../../../../com/tractrac/model/lib/api/INamed.html" title="interface in com.tractrac.model.lib.api"><span class="typeNameLink">Prev Class</span></a></li>
|
||||
<li><a href="../../../../../com/tractrac/model/lib/api/ModelLocatorTest.html" title="class in com.tractrac.model.lib.api"><span class="typeNameLink">Next Class</span></a></li>
|
||||
<li>Next Class</li>
|
||||
</ul>
|
||||
<ul class="navList">
|
||||
<li><a href="../../../../../index.html?com/tractrac/model/lib/api/ModelLocator.html" target="_top">Frames</a></li>
|
||||
|
||||
+4
-4
@@ -2,9 +2,9 @@
|
||||
<!-- NewPage -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Generated by javadoc (1.8.0_111) on Thu Jun 29 10:20:58 UTC 2017 -->
|
||||
<!-- Generated by javadoc (1.8.0_111) on Wed Nov 22 10:19:50 UTC 2017 -->
|
||||
<title>AbstractAttachable</title>
|
||||
<meta name="date" content="2017-06-29">
|
||||
<meta name="date" content="2017-11-22">
|
||||
<link rel="stylesheet" type="text/css" href="../../../../../../stylesheet.css" title="Style">
|
||||
<script type="text/javascript" src="../../../../../../script.js"></script>
|
||||
</head>
|
||||
@@ -49,7 +49,7 @@ var activeTableTab = "activeTableTab";
|
||||
<div class="subNav">
|
||||
<ul class="navList">
|
||||
<li>Prev Class</li>
|
||||
<li><a href="../../../../../../com/tractrac/model/lib/api/attachment/AttachmentsTest.html" title="class in com.tractrac.model.lib.api.attachment"><span class="typeNameLink">Next Class</span></a></li>
|
||||
<li><a href="../../../../../../com/tractrac/model/lib/api/attachment/IAttachable.html" title="interface in com.tractrac.model.lib.api.attachment"><span class="typeNameLink">Next Class</span></a></li>
|
||||
</ul>
|
||||
<ul class="navList">
|
||||
<li><a href="../../../../../../index.html?com/tractrac/model/lib/api/attachment/AbstractAttachable.html" target="_top">Frames</a></li>
|
||||
@@ -284,7 +284,7 @@ implements <a href="../../../../../../com/tractrac/model/lib/api/attachment/IAtt
|
||||
<div class="subNav">
|
||||
<ul class="navList">
|
||||
<li>Prev Class</li>
|
||||
<li><a href="../../../../../../com/tractrac/model/lib/api/attachment/AttachmentsTest.html" title="class in com.tractrac.model.lib.api.attachment"><span class="typeNameLink">Next Class</span></a></li>
|
||||
<li><a href="../../../../../../com/tractrac/model/lib/api/attachment/IAttachable.html" title="interface in com.tractrac.model.lib.api.attachment"><span class="typeNameLink">Next Class</span></a></li>
|
||||
</ul>
|
||||
<ul class="navList">
|
||||
<li><a href="../../../../../../index.html?com/tractrac/model/lib/api/attachment/AbstractAttachable.html" target="_top">Frames</a></li>
|
||||
|
||||
+4
-4
@@ -2,9 +2,9 @@
|
||||
<!-- NewPage -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Generated by javadoc (1.8.0_111) on Thu Jun 29 10:20:58 UTC 2017 -->
|
||||
<!-- Generated by javadoc (1.8.0_111) on Wed Nov 22 10:19:50 UTC 2017 -->
|
||||
<title>IAttachable</title>
|
||||
<meta name="date" content="2017-06-29">
|
||||
<meta name="date" content="2017-11-22">
|
||||
<link rel="stylesheet" type="text/css" href="../../../../../../stylesheet.css" title="Style">
|
||||
<script type="text/javascript" src="../../../../../../script.js"></script>
|
||||
</head>
|
||||
@@ -48,7 +48,7 @@ var activeTableTab = "activeTableTab";
|
||||
</div>
|
||||
<div class="subNav">
|
||||
<ul class="navList">
|
||||
<li><a href="../../../../../../com/tractrac/model/lib/api/attachment/AttachmentsTest.html" title="class in com.tractrac.model.lib.api.attachment"><span class="typeNameLink">Prev Class</span></a></li>
|
||||
<li><a href="../../../../../../com/tractrac/model/lib/api/attachment/AbstractAttachable.html" title="class in com.tractrac.model.lib.api.attachment"><span class="typeNameLink">Prev Class</span></a></li>
|
||||
<li><a href="../../../../../../com/tractrac/model/lib/api/attachment/IAttachmentKey.html" title="interface in com.tractrac.model.lib.api.attachment"><span class="typeNameLink">Next Class</span></a></li>
|
||||
</ul>
|
||||
<ul class="navList">
|
||||
@@ -226,7 +226,7 @@ var activeTableTab = "activeTableTab";
|
||||
</div>
|
||||
<div class="subNav">
|
||||
<ul class="navList">
|
||||
<li><a href="../../../../../../com/tractrac/model/lib/api/attachment/AttachmentsTest.html" title="class in com.tractrac.model.lib.api.attachment"><span class="typeNameLink">Prev Class</span></a></li>
|
||||
<li><a href="../../../../../../com/tractrac/model/lib/api/attachment/AbstractAttachable.html" title="class in com.tractrac.model.lib.api.attachment"><span class="typeNameLink">Prev Class</span></a></li>
|
||||
<li><a href="../../../../../../com/tractrac/model/lib/api/attachment/IAttachmentKey.html" title="interface in com.tractrac.model.lib.api.attachment"><span class="typeNameLink">Next Class</span></a></li>
|
||||
</ul>
|
||||
<ul class="navList">
|
||||
|
||||
+2
-2
@@ -2,9 +2,9 @@
|
||||
<!-- NewPage -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Generated by javadoc (1.8.0_111) on Thu Jun 29 10:20:58 UTC 2017 -->
|
||||
<!-- Generated by javadoc (1.8.0_111) on Wed Nov 22 10:19:50 UTC 2017 -->
|
||||
<title>IAttachmentKey</title>
|
||||
<meta name="date" content="2017-06-29">
|
||||
<meta name="date" content="2017-11-22">
|
||||
<link rel="stylesheet" type="text/css" href="../../../../../../stylesheet.css" title="Style">
|
||||
<script type="text/javascript" src="../../../../../../script.js"></script>
|
||||
</head>
|
||||
|
||||
+2
-2
@@ -2,9 +2,9 @@
|
||||
<!-- NewPage -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Generated by javadoc (1.8.0_111) on Thu Jun 29 10:20:58 UTC 2017 -->
|
||||
<!-- Generated by javadoc (1.8.0_111) on Wed Nov 22 10:19:50 UTC 2017 -->
|
||||
<title>IAttachmentManager</title>
|
||||
<meta name="date" content="2017-06-29">
|
||||
<meta name="date" content="2017-11-22">
|
||||
<link rel="stylesheet" type="text/css" href="../../../../../../stylesheet.css" title="Style">
|
||||
<script type="text/javascript" src="../../../../../../script.js"></script>
|
||||
</head>
|
||||
|
||||
+2
-3
@@ -2,9 +2,9 @@
|
||||
<!-- NewPage -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Generated by javadoc (1.8.0_111) on Thu Jun 29 10:20:59 UTC 2017 -->
|
||||
<!-- Generated by javadoc (1.8.0_111) on Wed Nov 22 10:19:51 UTC 2017 -->
|
||||
<title>com.tractrac.model.lib.api.attachment</title>
|
||||
<meta name="date" content="2017-06-29">
|
||||
<meta name="date" content="2017-11-22">
|
||||
<link rel="stylesheet" type="text/css" href="../../../../../../stylesheet.css" title="Style">
|
||||
<script type="text/javascript" src="../../../../../../script.js"></script>
|
||||
</head>
|
||||
@@ -20,7 +20,6 @@
|
||||
<h2 title="Classes">Classes</h2>
|
||||
<ul title="Classes">
|
||||
<li><a href="AbstractAttachable.html" title="class in com.tractrac.model.lib.api.attachment" target="classFrame">AbstractAttachable</a></li>
|
||||
<li><a href="AttachmentsTest.html" title="class in com.tractrac.model.lib.api.attachment" target="classFrame">AttachmentsTest</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</body>
|
||||
|
||||
+2
-9
@@ -2,9 +2,9 @@
|
||||
<!-- NewPage -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Generated by javadoc (1.8.0_111) on Thu Jun 29 10:20:59 UTC 2017 -->
|
||||
<!-- Generated by javadoc (1.8.0_111) on Wed Nov 22 10:19:51 UTC 2017 -->
|
||||
<title>com.tractrac.model.lib.api.attachment</title>
|
||||
<meta name="date" content="2017-06-29">
|
||||
<meta name="date" content="2017-11-22">
|
||||
<link rel="stylesheet" type="text/css" href="../../../../../../stylesheet.css" title="Style">
|
||||
<script type="text/javascript" src="../../../../../../script.js"></script>
|
||||
</head>
|
||||
@@ -124,13 +124,6 @@
|
||||
<div class="block">Abstract implementation for the <a href="../../../../../../com/tractrac/model/lib/api/attachment/IAttachable.html" title="interface in com.tractrac.model.lib.api.attachment"><code>IAttachable</code></a> interface.</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="rowColor">
|
||||
<td class="colFirst"><a href="../../../../../../com/tractrac/model/lib/api/attachment/AttachmentsTest.html" title="class in com.tractrac.model.lib.api.attachment">AttachmentsTest</a></td>
|
||||
<td class="colLast">
|
||||
<div class="block">
|
||||
This test checks the attachments.</div>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</li>
|
||||
|
||||
+2
-11
@@ -2,9 +2,9 @@
|
||||
<!-- NewPage -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Generated by javadoc (1.8.0_111) on Thu Jun 29 10:20:59 UTC 2017 -->
|
||||
<!-- Generated by javadoc (1.8.0_111) on Wed Nov 22 10:19:51 UTC 2017 -->
|
||||
<title>com.tractrac.model.lib.api.attachment Class Hierarchy</title>
|
||||
<meta name="date" content="2017-06-29">
|
||||
<meta name="date" content="2017-11-22">
|
||||
<link rel="stylesheet" type="text/css" href="../../../../../../stylesheet.css" title="Style">
|
||||
<script type="text/javascript" src="../../../../../../script.js"></script>
|
||||
</head>
|
||||
@@ -81,15 +81,6 @@
|
||||
<li type="circle">java.lang.Object
|
||||
<ul>
|
||||
<li type="circle">com.tractrac.model.lib.api.attachment.<a href="../../../../../../com/tractrac/model/lib/api/attachment/AbstractAttachable.html" title="class in com.tractrac.model.lib.api.attachment"><span class="typeNameLink">AbstractAttachable</span></a> (implements com.tractrac.model.lib.api.attachment.<a href="../../../../../../com/tractrac/model/lib/api/attachment/IAttachable.html" title="interface in com.tractrac.model.lib.api.attachment">IAttachable</a>)</li>
|
||||
<li type="circle">junit.framework.Assert
|
||||
<ul>
|
||||
<li type="circle">junit.framework.TestCase (implements junit.framework.Test)
|
||||
<ul>
|
||||
<li type="circle">com.tractrac.model.lib.api.attachment.<a href="../../../../../../com/tractrac/model/lib/api/attachment/AttachmentsTest.html" title="class in com.tractrac.model.lib.api.attachment"><span class="typeNameLink">AttachmentsTest</span></a></li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
+2
-2
@@ -2,9 +2,9 @@
|
||||
<!-- NewPage -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Generated by javadoc (1.8.0_111) on Thu Jun 29 10:20:59 UTC 2017 -->
|
||||
<!-- Generated by javadoc (1.8.0_111) on Wed Nov 22 10:19:51 UTC 2017 -->
|
||||
<title>IControlPassing</title>
|
||||
<meta name="date" content="2017-06-29">
|
||||
<meta name="date" content="2017-11-22">
|
||||
<link rel="stylesheet" type="text/css" href="../../../../../../stylesheet.css" title="Style">
|
||||
<script type="text/javascript" src="../../../../../../script.js"></script>
|
||||
</head>
|
||||
|
||||
+2
-2
@@ -2,9 +2,9 @@
|
||||
<!-- NewPage -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Generated by javadoc (1.8.0_111) on Thu Jun 29 10:20:59 UTC 2017 -->
|
||||
<!-- Generated by javadoc (1.8.0_111) on Wed Nov 22 10:19:51 UTC 2017 -->
|
||||
<title>IControlPassings</title>
|
||||
<meta name="date" content="2017-06-29">
|
||||
<meta name="date" content="2017-11-22">
|
||||
<link rel="stylesheet" type="text/css" href="../../../../../../stylesheet.css" title="Style">
|
||||
<script type="text/javascript" src="../../../../../../script.js"></script>
|
||||
</head>
|
||||
|
||||
+2
-2
@@ -2,9 +2,9 @@
|
||||
<!-- NewPage -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<!-- Generated by javadoc (1.8.0_111) on Thu Jun 29 10:20:59 UTC 2017 -->
|
||||
<!-- Generated by javadoc (1.8.0_111) on Wed Nov 22 10:19:51 UTC 2017 -->
|
||||
<title>IMessageData</title>
|
||||
<meta name="date" content="2017-06-29">
|
||||
<meta name="date" content="2017-11-22">
|
||||
<link rel="stylesheet" type="text/css" href="../../../../../../stylesheet.css" title="Style">
|
||||
<script type="text/javascript" src="../../../../../../script.js"></script>
|
||||
</head>
|
||||
|
||||
Loaded 100 of 241 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user