Merge branch 'master' into bug4374

This commit is contained in:
Steffen Schaefer committed 2017-12-04 08:55:54 +01:00
commit fb856dbe2b
54 files changed
+1070 -108

No files matched your search

+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="output" path="bin"/>
</classpath>
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.sap.sailing.domain.bravoadapter</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
@@ -0,0 +1,7 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
org.eclipse.jdt.core.compiler.compliance=1.8
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.source=1.8
@@ -0,0 +1,3 @@
eclipse.preferences.version=1
pluginProject.extensions=false
resolve.requirebundle=false
@@ -0,0 +1,14 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Bravoadapter
Bundle-SymbolicName: com.sap.sailing.domain.bravoadapter
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: org.osgi.framework;version="1.8.0"
Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.server.trackfiles,
com.sap.sailing.domain.common,
com.sap.sse.common,
com.sap.sailing.server.gateway
Bundle-Activator: com.sap.sailing.domain.bravoadapter.impl.Activator
@@ -0,0 +1,4 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>root</artifactId>
<groupId>com.sap.sailing</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.sap.sailing.domain.bravoadapter</artifactId>
<packaging>eclipse-plugin</packaging>
</project>
@@ -0,0 +1,17 @@
package com.sap.sailing.domain.bravoadapter.impl;
import org.osgi.framework.BundleActivator;
import org.osgi.framework.BundleContext;
import com.sap.sailing.server.trackfiles.common.GPSFixImporterRegistration;
public class Activator implements BundleActivator {
@Override
public void start(BundleContext context) throws Exception {
GPSFixImporterRegistration.register(new BravoGPSFixImporter(), context);
}
@Override
public void stop(BundleContext context) throws Exception {
}
}
@@ -0,0 +1,50 @@
package com.sap.sailing.domain.bravoadapter.impl;
import java.io.IOException;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Date;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.sensordata.BravoExtendedSensorDataMetadata;
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifier;
import com.sap.sailing.domain.trackfiles.TrackFileImportDeviceIdentifierImpl;
import com.sap.sailing.domain.trackimport.FormatNotSupportedException;
import com.sap.sailing.domain.trackimport.GPSFixImporter;
import com.sap.sailing.server.gateway.windimport.bravo.FunnyDegreeConverter;
import com.sap.sailing.server.trackfiles.impl.BravoExtendedDataImporterImpl;
public class BravoGPSFixImporter implements GPSFixImporter {
@Override
public void importFixes(InputStream inputStream, Callback callback, boolean inferSpeedAndBearing, final String filename)
throws FormatNotSupportedException, IOException {
TrackFileImportDeviceIdentifier device = new TrackFileImportDeviceIdentifierImpl(filename, getType() + "@" + new Date());
new BravoExtendedDataImporterImpl().importFixes(inputStream,
(Iterable<DoubleVectorFix> fixes, TrackFileImportDeviceIdentifier deviceIdentifier)->{
for (final DoubleVectorFix fix : fixes) {
GPSFixMoving gpsFix = new GPSFixMovingImpl(
new DegreePosition(FunnyDegreeConverter.funnyLatLng(fix.get(BravoExtendedSensorDataMetadata.LAT.getColumnIndex())),
FunnyDegreeConverter.funnyLatLng(fix.get(BravoExtendedSensorDataMetadata.LON.getColumnIndex()))),
fix.getTimePoint(),
new KnotSpeedWithBearingImpl(fix.get(BravoExtendedSensorDataMetadata.SOG.getColumnIndex()),
new DegreeBearingImpl(fix.get(BravoExtendedSensorDataMetadata.COG.getColumnIndex()))));
callback.addFix(gpsFix, device);
}
}, filename, /* sourceName */ getType(), /* downsample */ false);
}
@Override
public Iterable<String> getSupportedFileExtensions() {
return Arrays.asList(new String[] { "csv", "log", "txt" });
}
@Override
public String getType() {
return "Bravo";
}
}
@@ -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;
@@ -10,7 +10,7 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
import com.sap.sailing.domain.tracking.BravoFixTrack;
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl;
import com.sap.sailing.domain.tracking.impl.CompetitorBravoFixTrackImpl;
/**
* {@link SensorFixMapper} implementation to handle {@link BravoFix}es.
@@ -20,7 +20,7 @@ public class BravoDataFixMapper implements SensorFixMapper<BravoFix, DynamicSens
@Override
public DynamicSensorFixTrack<Competitor, BravoFix> getTrack(DynamicTrackedRace race, Competitor key) {
return race.getOrCreateSensorTrack(key, BravoFixTrack.TRACK_NAME,
() -> new BravoFixTrackImpl<Competitor>(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ false,
() -> new CompetitorBravoFixTrackImpl(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ false,
race.getTrack(key)));
}
@@ -11,7 +11,7 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
import com.sap.sailing.domain.tracking.BravoFixTrack;
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl;
import com.sap.sailing.domain.tracking.impl.CompetitorBravoFixTrackImpl;
/**
* {@link SensorFixMapper} implementation to handle {@link BravoExtendedFix}es.
@@ -21,7 +21,7 @@ public class BravoExtendedDataFixMapper implements SensorFixMapper<BravoFix, Dyn
@Override
public DynamicSensorFixTrack<Competitor, BravoFix> getTrack(DynamicTrackedRace race, Competitor key) {
return race.getOrCreateSensorTrack(key, BravoFixTrack.TRACK_NAME,
() -> new BravoFixTrackImpl<Competitor>(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ true,
() -> new CompetitorBravoFixTrackImpl(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ true,
race.getTrack(key)));
}
@@ -11,7 +11,7 @@ import com.sap.sailing.domain.racelog.tracking.SensorFixMapper;
import com.sap.sailing.domain.tracking.BravoFixTrack;
import com.sap.sailing.domain.tracking.DynamicSensorFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.impl.BravoFixTrackImpl;
import com.sap.sailing.domain.tracking.impl.CompetitorBravoFixTrackImpl;
/**
* {@link SensorFixMapper} implementation to product {@link BravoExtendedFix}es from {@link DoubleVectorFix}es
@@ -25,7 +25,7 @@ public class ExpeditionExtendedDataFixMapper implements SensorFixMapper<BravoFix
@Override
public DynamicSensorFixTrack<Competitor, BravoFix> getTrack(DynamicTrackedRace race, Competitor key) {
return race.getOrCreateSensorTrack(key, BravoFixTrack.TRACK_NAME,
() -> new BravoFixTrackImpl<Competitor>(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ true,
() -> new CompetitorBravoFixTrackImpl(key, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ true,
race.getTrack(key)));
}
@@ -479,22 +479,26 @@ public class TrackImpl<FixType extends Timed> implements Track<FixType> {
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 distanceFromFromToBeginningOfCacheEntry = nullElement;
T distanceFromEndOfCacheEntryToTo = nullElement;
T valueFromFromToBeginningOfCacheEntry = nullElement;
T valueFromEndOfCacheEntryToTo = nullElement;
if (!bestCacheEntry.getB().getA().equals(from)) {
assert bestCacheEntry.getB().getA().after(from);
perfectCacheHit = false;
distanceFromFromToBeginningOfCacheEntry = getValueSumRecursively(from, bestCacheEntry
valueFromFromToBeginningOfCacheEntry = getValueSumRecursively(from, bestCacheEntry
.getB().getA(), recursionDepth + 1, nullElement, adder, cache, valueCalculator);
}
if (!bestCacheEntry.getA().equals(to)) {
assert bestCacheEntry.getA().before(to);
perfectCacheHit = false;
distanceFromEndOfCacheEntryToTo = getValueSumRecursively(bestCacheEntry.getA(), to,
valueFromEndOfCacheEntryToTo = getValueSumRecursively(bestCacheEntry.getA(), to,
recursionDepth + 1, nullElement, adder, cache, valueCalculator);
}
result = adder.add(adder.add(distanceFromFromToBeginningOfCacheEntry, bestCacheEntry.getB().getB()),
distanceFromEndOfCacheEntryToTo);
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);
@@ -207,27 +207,29 @@ public class CompetitorProviderCacheInvalidationTest extends AbstractLeaderboard
for (Competitor c : compLists[1]) {
raceLog.add(new RaceLogRegisterCompetitorEventImpl(MillisecondsTimePoint.now(), new LogEventAuthorImpl("Me", 0), 1, c));
}
Set<Competitor> allCompetitors = new HashSet<>();
allCompetitors.addAll(compLists[0]);
allCompetitors.addAll(compLists[1]);
// expected are only the competitors in the RaceLog, because only one RaceColumn is
// registered, which has the competitors registered in the RaceLog.
assertRegattaAndRaceCompetitors(new HashSet<>(compLists[1]));
assertRegattaAndRaceCompetitors(allCompetitors, new HashSet<>(compLists[1]));
// Now we revoke that the race log provides the competitors for R1; the competitors should then
// snap back to those competitors taken from the regatta log for both, the rentire leaderboard
// as well as for the race column
raceLog.revokeEvent(author, usesCompetitorsFromRaceLogEvent);
assertRegattaAndRaceCompetitors(new HashSet<>(compLists[0]));
assertRegattaAndRaceCompetitors(new HashSet<>(compLists[0]), new HashSet<>(compLists[0]));
// And now re-introduce per-race competitors and validate again that the cache adjusts properly:
raceLog.add(new RaceLogUseCompetitorsFromRaceLogEventImpl(MillisecondsTimePoint.now(), author, MillisecondsTimePoint.now(), UUID.randomUUID(), passId));
assertRegattaAndRaceCompetitors(new HashSet<>(compLists[1]));
assertRegattaAndRaceCompetitors(allCompetitors, new HashSet<>(compLists[1]));
}
private void assertRegattaAndRaceCompetitors(Set<Competitor> expected) {
private void assertRegattaAndRaceCompetitors(Set<Competitor> expectedAllCompetitors, Set<Competitor> expectedForRace) {
Set<Competitor> actual = new HashSet<>();
Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(), actual);
assertEquals(expected, actual);
assertEquals(expectedAllCompetitors, actual);
Set<Competitor> actualForRace = new HashSet<>();
//For the race only the competitors registered on RaceLog as RaceLogUseCompetitorsFromRaceLogEvent is present
Set<Competitor> expectedForRace = new HashSet<>(expected);
// For the race only the competitors registered on RaceLog as RaceLogUseCompetitorsFromRaceLogEvent is present
Util.addAll(competitorProviderFlexibleLeaderboard.getAllCompetitors(
flexibleLeaderboard.getRaceColumnByName("R1"),
flexibleLeaderboard.getFleet(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), actualForRace);
@@ -181,9 +181,7 @@ public class CompetitorProviderFromRaceColumnsAndRegattaLike {
if (allCompetitorsCache == null) {
final Set<Competitor> result = new HashSet<>();
final Set<RaceDefinition> raceDefinitions = new HashSet<>();
boolean hasRaceColumns = false;
for (RaceColumn rc : provider.getRaceColumns()) {
hasRaceColumns = true;
final Pair<Iterable<RaceDefinition>, Iterable<Competitor>> allCompetitorsInRaceColumnWithRaceDefinitionsConsidered = rc.getAllCompetitorsWithRaceDefinitionsConsidered();
Util.addAll(allCompetitorsInRaceColumnWithRaceDefinitionsConsidered.getB(), result);
Util.addAll(allCompetitorsInRaceColumnWithRaceDefinitionsConsidered.getA(), raceDefinitions);
@@ -192,12 +190,10 @@ public class CompetitorProviderFromRaceColumnsAndRegattaLike {
}
}
final RegattaLog regattaLog = provider.getRegattaLike().getRegattaLog();
if (!hasRaceColumns) {
// If no race exists, the regatta log-provided competitor registrations will not have
// been considered yet; add them:
final Set<Competitor> regattaLogProvidedCompetitors = new CompetitorsInLogAnalyzer<>(regattaLog).analyze();
result.addAll(regattaLogProvidedCompetitors);
}
// If no race exists, the regatta log-provided competitor registrations will not have
// been considered yet; add them:
final Set<Competitor> regattaLogProvidedCompetitors = new CompetitorsInLogAnalyzer<>(regattaLog).analyze();
result.addAll(regattaLogProvidedCompetitors);
// else, don't add regatta log competitors because they have been added in each column already.
// The competitors are collected from the races. Those, however, will be the regatta log
// competitors if the race does not define its own.
@@ -13,5 +13,4 @@ import com.sap.sse.common.WithID;
*/
public interface DynamicSensorFixTrack<ItemType extends WithID & Serializable, FixT extends SensorFix> extends
SensorFixTrack<ItemType, FixT>, DynamicTrack<FixT> {
}
@@ -25,4 +25,10 @@ public interface DynamicTrack<FixType extends Timed> extends Track<FixType> {
* @return <code>true</code> if the element was added, <code>false</code> otherwise.
*/
boolean add(FixType fix, boolean replace);
/**
* Invoked when this track is added to a tracked race. The method is also invoked after de-serializing the
* containing {@link TrackedRace}. This default implementation does nothing.
*/
default void addedToTrackedRace(TrackedRace trackedRace) {}
}
@@ -45,7 +45,7 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
* 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 transient GPSFixTrack<ItemType, GPSFixMoving> gpsTrack;
private class CacheInvalidationGpsTrackListener implements GPSTrackListener<ItemType, GPSFixMoving> {
private static final long serialVersionUID = 6395529765232404414L;
@@ -89,6 +89,23 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
this.foilingTimeCache = createFoilingTimeCache(trackedItem);
this.foilingDistanceCache = createFoilingDistanceCache(trackedItem);
this.averageRideHeightCache = createAverageRideHeightCache(trackedItem);
setGpsTrack(gpsTrack);
}
public GPSFixTrack<ItemType, GPSFixMoving> getGpsTrack() {
return gpsTrack;
}
/**
* Sets the {@link #gpsTrack} field and registers a cache invalidation listener
* on the {@code gpsTrack} to ensure that the foiling distance is invalidated if needed.<p>
*
* Must be called only once for a non-{@code null} {@code gpsTrack}.
*/
protected void setGpsTrack(GPSFixTrack<ItemType, GPSFixMoving> gpsTrack) {
if (gpsTrack != null) {
assert this.gpsTrack == null;
}
this.gpsTrack = gpsTrack;
if (gpsTrack != null) {
gpsTrack.addListener(new CacheInvalidationGpsTrackListener());
@@ -119,9 +136,6 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
this.foilingTimeCache = createFoilingTimeCache(getTrackedItem());
this.foilingDistanceCache = createFoilingDistanceCache(getTrackedItem());
this.averageRideHeightCache = createAverageRideHeightCache(getTrackedItem());
if (gpsTrack != null) {
gpsTrack.addListener(new CacheInvalidationGpsTrackListener());
}
}
@Override
@@ -215,8 +229,8 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
@Override
public Distance getDistanceSpentFoiling(TimePoint from, TimePoint to) {
assert gpsTrack != null;
return getValueSum(from, to, /* nullElement */ Distance.NULL, Distance::add, foilingDistanceCache,
return getGpsTrack() == null ? null :
getValueSum(from, to, /* nullElement */ Distance.NULL, Distance::add, foilingDistanceCache,
/* valueCalculator */ new Track.TimeRangeValueCalculator<Distance>() {
@Override
public Distance calculate(TimePoint from, TimePoint to) {
@@ -226,7 +240,7 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
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()));
result = result.add(getGpsTrack().getDistanceTraveled(last, fix.getTimePoint()));
}
last = fix.getTimePoint();
isFoiling = fixFoils;
@@ -0,0 +1,26 @@
package com.sap.sailing.domain.tracking.impl;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.TrackedRace;
public class CompetitorBravoFixTrackImpl extends BravoFixTrackImpl<Competitor> {
private static final long serialVersionUID = 569731925060211875L;
public CompetitorBravoFixTrackImpl(Competitor trackedItem, String trackName, boolean hasExtendedFixes,
GPSFixTrack<Competitor, GPSFixMoving> gpsTrack) {
super(trackedItem, trackName, hasExtendedFixes, gpsTrack);
}
public CompetitorBravoFixTrackImpl(Competitor trackedItem, String trackName, boolean hasExtendedFixes) {
super(trackedItem, trackName, hasExtendedFixes);
}
@Override
public void addedToTrackedRace(TrackedRace trackedRace) {
if (this.getGpsTrack() == null) {
this.setGpsTrack(trackedRace.getTrack(getTrackedItem()));
}
}
}
@@ -13,6 +13,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.Optional;
import java.util.Set;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Level;
@@ -609,7 +610,7 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
notifyListeners(listener -> listener.markPassingReceived(competitor, oldMarkPassings, markPassings));
}
private void notifyListeners(DynamicSensorFixTrack<Competitor, ?> track) {
private void notifyListenersAboutSensorTrackAdded(DynamicSensorFixTrack<Competitor, ?> track) {
notifyListeners(listener -> listener.competitorSensorTrackAdded(track));
}
@@ -1206,7 +1207,7 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
}
@Override
protected <FixT extends SensorFix> void addSensorTrackInternal(Pair<Competitor, String> key,
protected <FixT extends SensorFix> Optional<Runnable> addSensorTrackInternal(Pair<Competitor, String> key,
DynamicSensorFixTrack<Competitor, FixT> track) {
super.addSensorTrackInternal(key, track);
track.addListener(new SensorFixTrackListener<Competitor, FixT>() {
@@ -1222,6 +1223,6 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
notifyListeners(item, trackName, fix);
}
});
notifyListeners(track);
return Optional.of(()->notifyListenersAboutSensorTrackAdded(track));
}
}
@@ -19,6 +19,7 @@ import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.NavigableSet;
import java.util.Optional;
import java.util.Set;
import java.util.Timer;
import java.util.TimerTask;
@@ -635,6 +636,9 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException, PatchFailedException {
ois.defaultReadObject();
getRace().getCourse().addCourseListener(this);
for (DynamicTrack<?> sensorTrack : sensorTracks.values()) {
sensorTrack.addedToTrackedRace(this);
}
raceStates = new WeakHashMap<>();
attachedRaceLogs = new ConcurrentHashMap<>();
attachedRegattaLogs = new ConcurrentHashMap<>();
@@ -4147,40 +4151,54 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
protected <FixT extends SensorFix, TrackT extends DynamicSensorFixTrack<Competitor, FixT>> TrackT getOrCreateSensorTrack(
Competitor competitor, String trackName, TrackFactory<TrackT> newTrackFactory) {
Pair<Competitor, String> key = new Pair<>(competitor, trackName);
Optional<Runnable> executeAfterReleasingLock = Optional.empty();
TrackT result;
LockUtil.lockForWrite(sensorTracksLock);
try {
TrackT result = getTrackInternal(key);
result = getTrackInternal(key);
if (result == null && tracks.containsKey(competitor)) {
// A track is only added if the given Competitor is known to participate in this race
result = newTrackFactory.get();
addSensorTrackInternal(key, result);
executeAfterReleasingLock = addSensorTrackInternal(key, result);
}
return result;
} finally {
LockUtil.unlockAfterWrite(sensorTracksLock);
}
executeAfterReleasingLock.ifPresent(r->r.run());
return result;
}
protected void addSensorTrack(Competitor competitor, String trackName, DynamicSensorFixTrack<Competitor, ?> track) {
Pair<Competitor, String> key = new Pair<>(competitor, trackName);
Optional<Runnable> executeAfterReleasingLock = Optional.empty();
LockUtil.lockForWrite(sensorTracksLock);
try {
if(getTrackInternal(key) != null) {
if (getTrackInternal(key) != null) {
if (logger != null && logger.getLevel() != null && logger.getLevel().equals(Level.WARNING)) {
logger.warning(SensorFixTrack.class.getName() + " already exists for competitor: "
+ competitor.getName() + "; trackName: " + trackName);
}
} else {
this.addSensorTrackInternal(key, track);
executeAfterReleasingLock = this.addSensorTrackInternal(key, track);
}
} finally {
LockUtil.unlockAfterWrite(sensorTracksLock);
}
executeAfterReleasingLock.ifPresent(r->r.run());
}
protected <FixT extends SensorFix> void addSensorTrackInternal(Pair<Competitor, String> key,
/**
* To call this method, the caller must have obtained the write lock of {@link #sensorTracksLock}.
* Optionally, the method may return a {@link Runnable} to execute after the lock has been released.
* This may, e.g., be a routine that notifies listeners. Callers are responsible for invoking this
* {@link Runnable} <em>after</em> releasing the write lock.
*/
protected <FixT extends SensorFix> Optional<Runnable> addSensorTrackInternal(Pair<Competitor, String> key,
DynamicSensorFixTrack<Competitor, FixT> track) {
assert sensorTracksLock.isWriteLockedByCurrentThread();
sensorTracks.put(key, track);
track.addedToTrackedRace(this);
return Optional.empty();
}
@SuppressWarnings("unchecked")
@@ -4269,7 +4287,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
SpeedWithBearing projectOnto(SpeedWithBearing speed, Bearing projectToBearing) {
final SpeedWithBearing result;
if (speed.getBearing() != null && projectToBearing != null) {
if (speed != null && speed.getBearing() != null && projectToBearing != null) {
double cos = Math.cos(speed.getBearing().getRadians() - projectToBearing.getRadians());
if (cos < 0) {
projectToBearing = projectToBearing.reverse();
@@ -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" />
+7
View File
@@ -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>
@@ -687,7 +687,7 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
* Used to determine for a Chart the available Detailtypes. This is for example used to only show the RideHeight as
* an option for charts if it actually recorded for the race.
*/
List<DetailType> determineDetailTypes(String leaderboardGroupName, RegattaAndRaceIdentifier identifier);
List<DetailType> determineDetailTypesForCompetitorChart(String leaderboardGroupName, RegattaAndRaceIdentifier identifier);
List<ExpeditionDeviceConfiguration> getExpeditionDeviceConfigurations();
@@ -862,7 +862,7 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan
* Used to determine for a Chart the available Detailtypes. This is for example used, to only show the RideHeight as
* an option for charts, if it actually recorded for the race.
*/
void determineDetailTypes(String leaderboardGroupName, RegattaAndRaceIdentifier identifier,
void determineDetailTypesForCompetitorChart(String leaderboardGroupName, RegattaAndRaceIdentifier identifier,
AsyncCallback<List<DetailType>> callback);
void getExpeditionDeviceConfigurations(AsyncCallback<List<ExpeditionDeviceConfiguration>> callback);
@@ -1933,7 +1933,7 @@ currentDepthInMeters=Wassertiefe
currentDriftInDegrees=Abdrift
currentLeewayInDegrees=Leeway (???)
currentRudderInDegrees=Ruder
currentSet=Set (???)
currentSet=Stromversatz
currentTackAngleInDegrees=Wendewinkel
currentDeflectorInMillimeters=Vorstags-Deflektor Abstand
currentDeflectorPercentage=Vorstags-Deflektor Anteil
@@ -183,6 +183,7 @@ in=en
secondsUnit=s
secondsPerNauticalMileUnit=s/NM
metersUnit=m
millimetersUnit=mm
degreesUnit=°
close=Cerrar
compareCompetitors=Comparar competidores
@@ -992,6 +993,7 @@ overwriteRacingProceduresConfiguration=Activar configuración de regata
overwriteRacingProceduresConfigurationHelpText=Si se activa esta opción, se proporcionará una configuración de regata base para todas las opciones no configuradas en la configuración específica de regata.
setStartTime=Fijar hora de salida
currentPass=Pase actual
setFinishingAndFinishTimeDescription=Seleccione las horas de llegada y de fin nuevas para la carrera.
setStartTimeDescription=Seleccione una hora de salida nueva para la prueba. Si desea fijar la hora de salida en el pasado, deberá habilitar la casilla de selección para actualizar el recuento de pases. Tenga en cuenta que si cambia el recuento de pases se perderá toda la información del pase anterior (como eventos de bandera)
setTrackingTimesDescription=Defina la salida y la llegada del rastreo para la carrera.
hasAdditionalGolfDownTime=Activar tiempo de descanso de golf adicional
@@ -1020,6 +1022,7 @@ searchTag=Etiqueta de búsqueda
doYouReallyWantToRemoveLeaderboard=¿Desea realmente eliminar la tabla de clasificación: "{0}"?
metaLeaderboardCannotBeChanged=Se trata de una metatabla de clasificación. No se puede modificar aquí.
failedToSetNewStartTime=No se ha podido fijar una nueva hora de salida. Esto puede ocurrir si ya existe la misma hora de salida en el registro de la carrera o si alguna persona ha añadido una hora de salida más reciente (en términos de un ID de paso y de una hora lógica) que la hora que acaba de añadir.
failedToSetNewFinishingAndFinishTime=Ha fallado la fijación de la hora de llegada y de fin nueva.
doYouReallyWantToRemoveIgtimiAccount=¿Desea realmente eliminar la cuenta de Igtimi otorgando acceso a los datos del usuario {0}?
igtimiWindImport=Importar viento desde Igtimi
seeIgtimiTabForAccountSettings=Consulte la pestaña "Igtimi" para conocer los ajustes de la cuenta que determinan qué datos de Igtimi deben tenerse en cuenta.
@@ -1915,6 +1918,10 @@ currentStbdRudderRake=Inclinación del timón a estribor
currentStbdRudderRakeTooltip=La inclinación actual del timón a estribor
currentMastRotationInDegree=Rotación del mástil
currentMastRotationInDegreeTooltip=La rotación actual del mástil en grados
finishingTime=Hora de llegada
finishTimeString=Hora de fin
setFinishingAndFinishTime=Fijar hora de llegada y de fin
finishTimeMustBeAtOrAfterFinishingTime=Hora de fin debe ser a la hora de llegada o posterior
boatNameMustNotBeEmpty=Proporcione un nombre de embarcación que no esté en blanco
errorUpdatingExpeditionDeviceConfiguration=Error al actualizar la configuración del dispositivo con la expedición "{0}": {1}
doYouReallyWantToRemoveExpeditionDeviceConfiguration=¿Desea realmente eliminar la configuración del dispositivo con la expedición "{0}"?
@@ -1926,3 +1933,39 @@ editExpeditionDeviceConfiguration=Tratar configuración de dispositivo con exped
expeditionBoatId=ID de embarcación (por ejemplo 0, 1, o 2)
doYouReallyWantToRemoveTheSelectedExpeditionDeviceConfigurations=¿Desea realmente eliminar las configuraciones del dispositivo con expedición seleccionadas?
doYouReallyWantToRemoveTheSelectedIgtimiAccounts=¿Desea realmente eliminar las cuentas Igtimi seleccionadas?
currentDepthInMeters=Profundidad
currentDriftInDegrees=Desviación
currentLeewayInDegrees=Deriva
currentRudderInDegrees=Timón
currentSet=Conjunto
currentTackAngleInDegrees=Ángulo de bordo
currentDeflectorInMillimeters=Distancia de deflector
currentDeflectorPercentage=Ratio de deflector
currentForestayLoad=Carga de estay de proa
currentForestayPressure=Presión de estay de proa
currentRakeInDegrees=Rastrillo
currentTargetBoatspeedPercentage=Velocidad de embarcación objetivo
currentTargetHeelAngleInDegrees=Escora objetivo
percent=%
currentTargetHeelAngleInDegreesTooltip=Ángulo de escora objetivo para mejor rendimiento
currentTargetBoatspeedPercentageTooltip=Porcentaje alcanzado de velocidad de embarcación objetivo calculada
currentRakeInDegreesTooltip=Ángulo del mástil
currentForestayLoadTooltip=Carga de estay de proa, medida por célula de carga
currentForestayPressureTooltip=Presión de estay de proa
currentDeflectorPercentageTooltip=Porcentaje de deflector
currentDeflectorInMillimetersTooltip=Distancia de deflector
currentTackAngleInDegreesTooltip=El ángulo de bordo óptimo calculado
currentSetTooltip=Conjunto (???)
currentRudderInDegreesTooltip=Ángulo de timón; valores negativos significan a puerto; positivos, a estribor
currentLeewayInDegreesTooltip=Deriva
currentDriftInDegreesTooltip=Ángulo entre dirección y rumbo sobre fondo
currentDepthInMetersTooltip=Profundidad del agua
tonsUnit=t
currentDistanceFoiledInMeters=Distancia con foils
currentDurationFoiled=Tiempo con foils
currentDistanceFoiledInMetersTooltip=Distancia con foils en metros
currentDurationFoiledTooltip=Tiempo empleado con foils
totalDurationFoiledInSeconds=Tiempo total con foils
totalDistanceFoiledInMeters=Distancia total con foils
totalDurationFoiledInSecondsTooltip=Tiempo total con foils, a lo largo de todas la clasificación
totalDistanceFoiledInMetersTooltip=Distancia total con foils, a lo largo de todas la clasificación
@@ -182,7 +182,8 @@ distance=Distance
in=en
secondsUnit=s
secondsPerNauticalMileUnit=s/NM
metersUnit=nm
metersUnit=m
millimetersUnit=mm
degreesUnit=°
close=Fermer
compareCompetitors=Comparer concurrents
@@ -992,7 +993,8 @@ overwriteRacingProceduresConfiguration=Activer configuration de la régate
overwriteRacingProceduresConfigurationHelpText=L''activation de cette option fournira une configuration de base de la régate pour toutes les options non configurées dans la configuration spécifique de la régate.
setStartTime=Définir heure de départ
currentPass=Passage actuel
setStartTimeDescription=Sélectionnez une nouvelle heure de départ pour la course. Si vous voulez définir une heure de départ qui corresponde à une heure du passé, vous devez cocher cette case pour mettre à jour le nombre de passages. Notez qu''en modifiant le nombre de passages, vous perdrez toutes les informations concernant le passage précédent (comme les pavillons et manifestations).
setFinishingAndFinishTimeDescription=Sélectionnez l''heure d''arrivée du premier et l''heure de fermeture de la ligne d''arrivée pour la course.
setStartTimeDescription=Sélectionnez une nouvelle heure de départ pour la course. Si vous voulez définir une heure de départ dans le passé, vous devez cocher cette case pour mettre à jour le nombre de passages. Notez qu''en modifiant le nombre de passages, vous perdrez toutes les informations concernant le passage précédent (comme les pavillons et manifestations).
setTrackingTimesDescription=Définissez le début et la fin du suivi pour la course.
hasAdditionalGolfDownTime=Activer temps d''arrêt supplémentaire
additionalGolfDownTimeHelpText=Activer temps d''arrêt supplémentaire
@@ -1020,6 +1022,7 @@ searchTag=Balise de recherche
doYouReallyWantToRemoveLeaderboard=Voulez-vous vraiment supprimer le palmarès "{0}" ?
metaLeaderboardCannotBeChanged=Ce palmarès est de type méta. Vous ne pouvez pas le modifier ici.
failedToSetNewStartTime=Définition de la nouvelle heure de départ échouée. Cela peut arriver lorsque la même heure de départ existe déjà dans le journal de course ou lorsque quelqu''un d''autre a ajouté une heure de départ plus récente (sous forme d''ID de passage et d''heure logique) que celle que vous venez d''ajouter.
failedToSetNewFinishingAndFinishTime=Échec de la définition des nouvelles heures d''arrivée du premier et de fermeture de la ligne d''arrivée.
doYouReallyWantToRemoveIgtimiAccount=Souhaitez-vous réellement supprimer le compte Igtimi qui permet à l''utilisateur {0} d''accéder aux données ?
igtimiWindImport=Importer le vent depuis Igtimi
seeIgtimiTabForAccountSettings=Afficher l''onglet "Igtimi" pour voir les options de compte qui déterminent quelles données Igtimi seront prises en compte.
@@ -1915,6 +1918,10 @@ currentStbdRudderRake=Inclinaison du gouvernail tribord
currentStbdRudderRakeTooltip=Inclinaison actuelle du gouvernail tribord
currentMastRotationInDegree=Rotation du mât
currentMastRotationInDegreeTooltip=Rotation actuelle du mât en degrés
finishingTime=Heure d''arrivée du premier
finishTimeString=Heure de fermeture de la ligne d''arrivée
setFinishingAndFinishTime=Définir heure d''arrivée du premier et de fermeture de la ligne d''arrivée
finishTimeMustBeAtOrAfterFinishingTime=L''heure de fermeture de la ligne d''arrivée doit être égale ou ultérieure à l''heure d''arrivée du premier.
boatNameMustNotBeEmpty=Veuillez indiquer un nom de bateau.
errorUpdatingExpeditionDeviceConfiguration=Erreur lors de la mise à jour de la configuration du terminal Expedition "{0}" : {1}
doYouReallyWantToRemoveExpeditionDeviceConfiguration=Voulez-vous vraiment supprimer la configuration du terminal Expedition "{0}" ?
@@ -1926,3 +1933,39 @@ editExpeditionDeviceConfiguration=Modifier configuration du terminal Expedition
expeditionBoatId=ID du bateau (par exemple 0, 1 ou 2)
doYouReallyWantToRemoveTheSelectedExpeditionDeviceConfigurations=Voulez-vous vraiment supprimer les configurations du terminal Expedition sélectionnées ?
doYouReallyWantToRemoveTheSelectedIgtimiAccounts=Voulez-vous vraiment supprimer les comptes lgtimi sélectionnés ?
currentDepthInMeters=Profondeur
currentDriftInDegrees=Vitesse du courant
currentLeewayInDegrees=Dérive
currentRudderInDegrees=Gouvernail
currentSet=Direction du courant
currentTackAngleInDegrees=Angle de virement de bord
currentDeflectorInMillimeters=Distance déflecteur
currentDeflectorPercentage=Ratio déflecteur
currentForestayLoad=Charge étai avant
currentForestayPressure=Pression étai avant
currentRakeInDegrees=Inclinaison
currentTargetBoatspeedPercentage=Vitesse cible du bateau
currentTargetHeelAngleInDegrees=Gîte cible
percent=%
currentTargetHeelAngleInDegreesTooltip=Angle de gîte cible pour obtenir de meilleures performances
currentTargetBoatspeedPercentageTooltip=Pourcentage de la vitesse cible du bateau calculée atteinte
currentRakeInDegreesTooltip=Inclinaison du mât
currentForestayLoadTooltip=Charge étai avant, mesurée par un capteur de force
currentForestayPressureTooltip=Pression étai avant
currentDeflectorPercentageTooltip=Pourcentage déflecteur
currentDeflectorInMillimetersTooltip=Distance déflecteur
currentTackAngleInDegreesTooltip=Angle de virement optimal calculé
currentSetTooltip=Direction du courant (???)
currentRudderInDegreesTooltip=Angle du gouvernail : valeurs négatives = à bâbord, valeurs positives = à tribord
currentLeewayInDegreesTooltip=Dérive
currentDriftInDegreesTooltip=Angle entre le cap et la route fond
currentDepthInMetersTooltip=Hauteur d''eau
tonsUnit=t
currentDistanceFoiledInMeters=Distance parcourue en planing
currentDurationFoiled=Temps de planing
currentDistanceFoiledInMetersTooltip=Distance parcourue en planing en mètres
currentDurationFoiledTooltip=Temps passé en planing
totalDurationFoiledInSeconds=Temps total passé en planing
totalDistanceFoiledInMeters=Distance totale parcourue en planing
totalDurationFoiledInSecondsTooltip=Temps total passé en planing, sur la totalité du palmarès
totalDistanceFoiledInMetersTooltip=Distance totale parcourue en planing, sur la totalité du palmarès
@@ -183,6 +183,7 @@ in=in
secondsUnit=s
secondsPerNauticalMileUnit=s/NM
metersUnit=m
millimetersUnit=mm
degreesUnit=°
close=閉じる
compareCompetitors=競技者比較
@@ -992,7 +993,8 @@ overwriteRacingProceduresConfiguration=レガッタ設定の有効化
overwriteRacingProceduresConfigurationHelpText=このオプションを有効化すると、レガッタ固有の設定で未設定のすべてのオプションに対して基本レガッタ設定が与えられます。
setStartTime=スタート時刻の設定
currentPass=現在のパス
setStartTimeDescription=レースの新規スタート時刻を選択してください。スタート時刻を過去の時点に設定する場合は、パスカウントを更新するチェックボックスを有効化する必要があります。パスカウントを変更すると (信号旗イベントなどの) 以前のパスからの情報がすべて解放されるということに注意してください。
setFinishingAndFinishTimeDescription=レースのフィニッシュ予定時刻とフィニッシュ時刻を新たに選択してください。
setStartTimeDescription=レースの新規スタート時刻を選択してください。スタート時刻を過去の時点に設定する場合は、パスカウントを更新するチェックボックスを有効化する必要があります。パスカウントを変更すると (信号旗イベントなどの) 以前のパスからの情報がすべて失われることに注意してください。
setTrackingTimesDescription=レース追跡の開始および終了を設定してください。
hasAdditionalGolfDownTime=追加ゴルフダウンタイムの有効化
additionalGolfDownTimeHelpText=追加ゴルフダウンタイムの有効化
@@ -1020,6 +1022,7 @@ searchTag=検索タグ
doYouReallyWantToRemoveLeaderboard=リーダーボード ''{0}'' を削除しますか。
metaLeaderboardCannotBeChanged=これはメタリーダーボードです。ここで変更することはできません。
failedToSetNewStartTime=新規スタート時刻の設定に失敗しました。これは、同じスタート時刻がすでにレースログに存在しているか、いま追加したものよりも (パス ID および論理時間の点で) 新しいスタート時間を誰かが追加している場合に発生することがあります。
failedToSetNewFinishingAndFinishTime=新規フィニッシュ時刻の設定に失敗しました。
doYouReallyWantToRemoveIgtimiAccount=ユーザ {0} のデータへのアクセスを付与する Igtimi アカウントを削除しますか。
igtimiWindImport=Igtimi から風をインポート
seeIgtimiTabForAccountSettings=''Igtimi'' タブで、Igtimi からのどのデータを考慮するかを指定するアカウント設定を参照してください。
@@ -1915,6 +1918,10 @@ currentStbdRudderRake=右舷ラダー傾斜角
currentStbdRudderRakeTooltip=現在の右舷ラダー傾斜角
currentMastRotationInDegree=マスト回転
currentMastRotationInDegreeTooltip=現在のマスト回転 (度)
finishingTime=フィニッシュ予定時刻
finishTimeString=フィニッシュ時刻
setFinishingAndFinishTime=フィニッシュ予定時刻とフィニッシュ時刻の設定
finishTimeMustBeAtOrAfterFinishingTime=フィニッシュ時刻はフィニッシュ予定時刻以降であることが必要
boatNameMustNotBeEmpty=空でない艇名を指定してください
errorUpdatingExpeditionDeviceConfiguration=Expedition デバイス設定 "{0}" の更新でエラーが発生: {1}
doYouReallyWantToRemoveExpeditionDeviceConfiguration=Expedition デバイス設定 "{0}" を削除しますか
@@ -1926,3 +1933,39 @@ editExpeditionDeviceConfiguration=Expedition デバイス設定の編集
expeditionBoatId=艇 ID (たとえば 0、1、2 など)
doYouReallyWantToRemoveTheSelectedExpeditionDeviceConfigurations=選択した Expedition デバイス設定を削除しますか
doYouReallyWantToRemoveTheSelectedIgtimiAccounts=選択した Igtimi アカウントを削除しますか
currentDepthInMeters=深さ
currentDriftInDegrees=ドリフト
currentLeewayInDegrees=リーウェイ
currentRudderInDegrees=ラダー
currentSet=セット
currentTackAngleInDegrees=タック角
currentDeflectorInMillimeters=ディフレクター距離
currentDeflectorPercentage=ディフレクター比
currentForestayLoad=フォアステイ負荷
currentForestayPressure=フォアステイ圧
currentRakeInDegrees=レーキ
currentTargetBoatspeedPercentage=目標艇速
currentTargetHeelAngleInDegrees=目標ヒール
percent=%
currentTargetHeelAngleInDegreesTooltip=最高パフォーマンスの目標ヒール角
currentTargetBoatspeedPercentageTooltip=計算による目標艇速に達した比率 (%)
currentRakeInDegreesTooltip=マストレーキ
currentForestayLoadTooltip=フォアステイ負荷 (ロードセルによる計測)
currentForestayPressureTooltip=フォアステイ圧
currentDeflectorPercentageTooltip=ディフレクター比 (%)
currentDeflectorInMillimetersTooltip=ディフレクター距離
currentTackAngleInDegreesTooltip=計算による最適タック角
currentSetTooltip=セット (???)
currentRudderInDegreesTooltip=ラダー角 (マイナス値は左舷、プラス値は右舷を意味)
currentLeewayInDegreesTooltip=リーウェイ
currentDriftInDegreesTooltip=針路と対地コースの間の角度
currentDepthInMetersTooltip=水深
tonsUnit=t
currentDistanceFoiledInMeters=フォイリング距離
currentDurationFoiled=フォイリング時間
currentDistanceFoiledInMetersTooltip=フォイリングに使われた距離 (m)
currentDurationFoiledTooltip=フォイリング時間
totalDurationFoiledInSeconds=合計フォイリング時間
totalDistanceFoiledInMeters=合計フォイリング距離
totalDurationFoiledInSecondsTooltip=合計フォイリング時間 (リーダーボード全体)
totalDistanceFoiledInMetersTooltip=合計フォイリング距離 (リーダーボード全体)
@@ -183,6 +183,7 @@ in=em
secondsUnit=s
secondsPerNauticalMileUnit=s/NM
metersUnit=m
millimetersUnit=mm
degreesUnit=°
close=Encerrar
compareCompetitors=Comparar competidores
@@ -992,7 +993,8 @@ overwriteRacingProceduresConfiguration=Ativar configuração de regata
overwriteRacingProceduresConfigurationHelpText=A ativação desta opção fornecerá uma configuração básica da regata para todas as opções não configuradas na configuração específica da regata.
setStartTime=Definir hora de partida
currentPass=Passagem atual
setStartTimeDescription=Selecione uma nova hora de início para a corrida. Se você quiser definir a hora de início para uma data/hora no passado, então precisa ativar o campo de seleção para atualizar a contagem das passagens. Tenha em atenção que a modificação da contagem das passagens fará com que sejam perdidas todas as informações da passagem anterior (como eventos de bandeira).
setFinishingAndFinishTimeDescription=Selecionar novas horas de chegada e conclusão para a corrida.
setStartTimeDescription=Selecione uma nova hora de partida para a corrida. Se você quiser definir a hora de partida para uma data/hora no passado, então precisa ativar o campo de seleção para atualizar a contagem das passagens. Tenha em atenção que a modificação da contagem das passagens fará com que sejam perdidas todas as informações da passagem anterior (como eventos de bandeira).
setTrackingTimesDescription=Definir o início e o fim do rastreamento para a corrida.
hasAdditionalGolfDownTime=Ativar pausa Golf adicional
additionalGolfDownTimeHelpText=Ativar pausa Golf adicional
@@ -1020,6 +1022,7 @@ searchTag=Etiqueta de pesquisa
doYouReallyWantToRemoveLeaderboard=Você tem a certeza que quer remover o painel de classificação: ''{0}''?
metaLeaderboardCannotBeChanged=Este é um painel de classificação meta. Não é possível modificá-lo aqui.
failedToSetNewStartTime=Falha ao definir nova hora de início. Isto pode suceder se a mesma hora de início já existir no registro de corridas ou se alguém tiver adicionado uma hora de início mais recente (em termos de um ID de passagem e hora lógica) que aquela que você acabou de adicionar.
failedToSetNewFinishingAndFinishTime=Falha ao definir nova hora de chegada e conclusão.
doYouReallyWantToRemoveIgtimiAccount=Você quer realmente remover a conta de Igtimi concedendo acesso a dados do usuário {0}?
igtimiWindImport=Importar vento de Igtimi
seeIgtimiTabForAccountSettings=Consulte a ficha ''Igtimi'' para as configurações da conta que determinam que dados de Igtimi serão considerados.
@@ -1915,6 +1918,10 @@ currentStbdRudderRake=Inclinação leme boreste
currentStbdRudderRakeTooltip=A inclinação atual do leme para boreste
currentMastRotationInDegree=Rotação do mastro
currentMastRotationInDegreeTooltip=A rotação atual do mastro em graus
finishingTime=Hora de chegada
finishTimeString=Hora de conclusão
setFinishingAndFinishTime=Definir hora de chegada e conclusão
finishTimeMustBeAtOrAfterFinishingTime=A hora de conclusão deve ser igual ou posterior à hora de chegada
boatNameMustNotBeEmpty=Forneça um nome de barco não vazio
errorUpdatingExpeditionDeviceConfiguration=Erro ao atualizar configuração de dispositivo Expedition "{0}": {1}
doYouReallyWantToRemoveExpeditionDeviceConfiguration=Você quer realmente remover a configuração do dispositivo Expedition "{0}"?
@@ -1926,3 +1933,39 @@ editExpeditionDeviceConfiguration=Processar configuração do dispositivo Expedi
expeditionBoatId=ID do barco (por ex., 0, 1 ou 2)
doYouReallyWantToRemoveTheSelectedExpeditionDeviceConfigurations=Você quer realmente remover as configurações do dispositivo Expedition selecionado?
doYouReallyWantToRemoveTheSelectedIgtimiAccounts=Você quer realmente remover as contas de Igtimi selecionadas?
currentDepthInMeters=Profundidade
currentDriftInDegrees=Deriva
currentLeewayInDegrees=Abatimento
currentRudderInDegrees=Leme
currentSet=Curso
currentTackAngleInDegrees=Ângulo de cambada
currentDeflectorInMillimeters=Distância de defletor
currentDeflectorPercentage=Proporção de defletor
currentForestayLoad=Carga de estai
currentForestayPressure=Pressão de estai
currentRakeInDegrees=Inclinação
currentTargetBoatspeedPercentage=Velocidade prevista do barco
currentTargetHeelAngleInDegrees=Inclinação prevista
percent=%
currentTargetHeelAngleInDegreesTooltip=Ângulo previsto de inclinação para melhor desempenho
currentTargetBoatspeedPercentageTooltip=Porcentagem da velocidade prevista do barco calculada atingida
currentRakeInDegreesTooltip=Inclinação do mastro
currentForestayLoadTooltip=Carga de estai, medida por uma célula de carga
currentForestayPressureTooltip=Pressão de estai
currentDeflectorPercentageTooltip=Porcentagem de defletor
currentDeflectorInMillimetersTooltip=Distância de defletor
currentTackAngleInDegreesTooltip=O ângulo de cambada ideal calculado
currentSetTooltip=Curso (???)
currentRudderInDegreesTooltip=Ângulo do leme; valores negativos indicam bombordo, positivos boreste
currentLeewayInDegreesTooltip=Abatimento
currentDriftInDegreesTooltip=Ângulo entre orientação e percurso no fundo
currentDepthInMetersTooltip=Profundidade da água
tonsUnit=t
currentDistanceFoiledInMeters=Distância com hidrofólio
currentDurationFoiled=Tempo com hidrofólio
currentDistanceFoiledInMetersTooltip=Distância percorrida em navegação com hidrofólio em metros
currentDurationFoiledTooltip=Tempo gasto em navegação com hidrofólio
totalDurationFoiledInSeconds=Total de tempo com hidrofólio
totalDistanceFoiledInMeters=Total de distância com hidrofólio
totalDurationFoiledInSecondsTooltip=Total de tempo com hidrofólio, em todo o painel de classificação
totalDistanceFoiledInMetersTooltip=Total de distância com hidrofólio, em todo o painel de classificação
@@ -183,6 +183,7 @@ in=в
secondsUnit=с
secondsPerNauticalMileUnit=с/NM
metersUnit=м
millimetersUnit=мм
degreesUnit=°
close=Закрыть
compareCompetitors=Сравнить участников
@@ -992,6 +993,7 @@ overwriteRacingProceduresConfiguration=Активировать конфигур
overwriteRacingProceduresConfigurationHelpText=Включение этого параметра предоставит базовую конфигурацию регаты для всех не настроенных параметров в конфигурации данной регаты.
setStartTime=Установить время старта
currentPass=Текущее прохождение
setFinishingAndFinishTimeDescription=Выберите новое время финиширования и финиша для гонки.
setStartTimeDescription=Выберите новое время старта для гонки. Если требуется назначить старт на время в прошлом, установите флажок для обновления счетчика прохождений. Учтите, что при изменении числа прохождений будут потеряны все сведения из предыдущих прохождений (такие как события флагов).
setTrackingTimesDescription=Установка времени начала и окончания отслеживания гонки.
hasAdditionalGolfDownTime=Активировать дополнительное время
@@ -1020,6 +1022,7 @@ searchTag=Тег поиска
doYouReallyWantToRemoveLeaderboard=Действительно удалить таблицу лидеров ''{0}''?
metaLeaderboardCannotBeChanged=Это метатаблица лидеров. Изменить ее здесь невозможно.
failedToSetNewStartTime=Не удалось установить новое время старта. Возможно, такое же время старта уже присутствует в журнале гонки или кто-то добавить более новое (ид. прохождения и логическое время) время старта, чем добавленное вами.
failedToSetNewFinishingAndFinishTime=Не удалось установить новое время финиширования и финиша.
doYouReallyWantToRemoveIgtimiAccount=Действительно удалить учетную запись Igtimi, предоставляющую доступ к данным пользователя {0}?
igtimiWindImport=Импортировать ветер из Igtimi
seeIgtimiTabForAccountSettings=Параметры учетной записи, определяющие учитываемые данные из Igtimi, см. на вкладке ''Igtimi''.
@@ -1915,6 +1918,10 @@ currentStbdRudderRake=Скос задней кромки руля на прав
currentStbdRudderRakeTooltip=Текущий скос задней кромки руля на правый борт
currentMastRotationInDegree=Вращение мачты
currentMastRotationInDegreeTooltip=Текущее значение вращения мачты в градусах
finishingTime=Время финиширования
finishTimeString=Время финиша
setFinishingAndFinishTime=Указать время финиширования и финиша
finishTimeMustBeAtOrAfterFinishingTime=Время финиша должно совпадать или быть после времени финиширования
boatNameMustNotBeEmpty=Укажите непустое имя лодки
errorUpdatingExpeditionDeviceConfiguration=Ошибка при обновлении конфигурации устройства Expedition "{0}": {1}
doYouReallyWantToRemoveExpeditionDeviceConfiguration=Действительно удалить конфигурацию устройства Expedition "{0}"?
@@ -1926,3 +1933,39 @@ editExpeditionDeviceConfiguration=Редактировать конфигура
expeditionBoatId=Ид. лодки (например, 0, 1, или 2)
doYouReallyWantToRemoveTheSelectedExpeditionDeviceConfigurations=Действительно удалить выбранные конфигурации устройства Expedition?
doYouReallyWantToRemoveTheSelectedIgtimiAccounts=Действительно удалить выбранные учетные записи Igtimi?
currentDepthInMeters=Глубина
currentDriftInDegrees=Скорость течения
currentLeewayInDegrees=Дрейф
currentRudderInDegrees=Перо руля
currentSet=Направление течения
currentTackAngleInDegrees=Галсовый угол
currentDeflectorInMillimeters=Дистанция для отражателя
currentDeflectorPercentage=Коэффициент отражателя
currentForestayLoad=Нагрузка форштага
currentForestayPressure=Давление форштага
currentRakeInDegrees=Продольный наклон мачты
currentTargetBoatspeedPercentage=Целевая скорость лодки
currentTargetHeelAngleInDegrees=Целевой крен
percent=%
currentTargetHeelAngleInDegreesTooltip=Целевой угол крена для улучшения эффективности
currentTargetBoatspeedPercentageTooltip=Процент достижения расчетной целевой скорости лодки
currentRakeInDegreesTooltip=Наклон мачты
currentForestayLoadTooltip=Нагрузка форштага, измеренная датчиком нагрузки
currentForestayPressureTooltip=Давление форштага
currentDeflectorPercentageTooltip=Процент отражателя
currentDeflectorInMillimetersTooltip=Дистанция для отражателя
currentTackAngleInDegreesTooltip=Расчетный оптимальный галсовый угол
currentSetTooltip=Направление течения (???)
currentRudderInDegreesTooltip=Угол отклонения руля; отрицательные значения означают отклонение по левому борту, положительные - отклонение по правому
currentLeewayInDegreesTooltip=Боковой снос
currentDriftInDegreesTooltip=Угол между направлением и курсом лодки относительно земли
currentDepthInMetersTooltip=Глубина воды
tonsUnit=т
currentDistanceFoiledInMeters=Дистанция в крыльевом режиме
currentDurationFoiled=Время в крыльевом режиме
currentDistanceFoiledInMetersTooltip=Дистанция в метрах, пройденная в крыльевом режиме
currentDurationFoiledTooltip=Время в крыльевом режиме
totalDurationFoiledInSeconds=Общее время в крыльевом режиме
totalDistanceFoiledInMeters=Общая дистанция в крыльевом режиме
totalDurationFoiledInSecondsTooltip=Общее время в крыльевом режиме, по всей таблице лидеров
totalDistanceFoiledInMetersTooltip=Общая дистанция в крыльевом режиме, по всей таблице лидеров
@@ -183,6 +183,7 @@ in=于
secondsUnit=s
secondsPerNauticalMileUnit=s/NM
metersUnit=m
millimetersUnit=mm
degreesUnit=°
close=关闭
compareCompetitors=比较参赛队
@@ -992,7 +993,8 @@ overwriteRacingProceduresConfiguration=激活比赛配置
overwriteRacingProceduresConfigurationHelpText=启用此选项将为比赛特定的配置中所有未配置的选项提供基本比赛配置。
setStartTime=设置开始时间
currentPass=当前绕标
setStartTimeDescription=为比赛轮次选择新开始时间。 如果要将开始时间设置为过去的时间点,需要启用此复选框更新场次计数。 请注意,更改场次计数将丢失先前场次的所有信息(例如旗标活动)。
setFinishingAndFinishTimeDescription=为比赛轮次选择新终点和结束时间。
setStartTimeDescription=为比赛轮次选择新开始时间。如果要将开始时间设置为过去的时间点,需要启用此复选框更新场次计数。请注意,更改场次计数将丢失先前场次的所有信息(例如旗标活动)。
setTrackingTimesDescription=设置比赛轮次跟踪的开始和结束。
hasAdditionalGolfDownTime=激活附加高尔夫故障时间
additionalGolfDownTimeHelpText=激活附加高尔夫故障时间
@@ -1020,6 +1022,7 @@ searchTag=搜索标记
doYouReallyWantToRemoveLeaderboard=是否确定移除积分榜:''''{0}''''?
metaLeaderboardCannotBeChanged=此为元积分榜。无法在此处更改。
failedToSetNewStartTime=设置新开始时间失败。 相同的开始时间可能已存在于比赛轮次日志中或某人已添加(场次编号和逻辑时间)更新的开始时间。
failedToSetNewFinishingAndFinishTime=设置新结束和完成时间失败。
doYouReallyWantToRemoveIgtimiAccount=是否确定移除授予用户{0}访问数据权限的 Igtimi 账户?
igtimiWindImport=从 Igtimi 导入风力数据
seeIgtimiTabForAccountSettings=请查看 "Igtimi" 选项卡了解账户设置,确定将考虑的 Igtimi 数据。
@@ -1915,6 +1918,10 @@ currentStbdRudderRake=右舵倾斜度
currentStbdRudderRakeTooltip=当前右舵倾斜度
currentMastRotationInDegree=桅杆旋转度
currentMastRotationInDegreeTooltip=当前桅杆旋转度数
finishingTime=结束时间
finishTimeString=终点线时间
setFinishingAndFinishTime=设置结束和终点线时间
finishTimeMustBeAtOrAfterFinishingTime=终点线时间必须与结束时间相同或在其之后
boatNameMustNotBeEmpty=请提供非空船只名称
errorUpdatingExpeditionDeviceConfiguration=更新导航图设备配置 "{0}" 出错:{1}
doYouReallyWantToRemoveExpeditionDeviceConfiguration=是否确定移除导航图设备配置 "{0}"?
@@ -1926,3 +1933,39 @@ editExpeditionDeviceConfiguration=编辑导航图设备配置
expeditionBoatId=船只编号(例如 0、1 或 2)
doYouReallyWantToRemoveTheSelectedExpeditionDeviceConfigurations=是否确定移除所选的导航图设备配置?
doYouReallyWantToRemoveTheSelectedIgtimiAccounts=是否确定移除所选的 Igtimi 账户?
currentDepthInMeters=深度
currentDriftInDegrees=漂移
currentLeewayInDegrees=航差角
currentRudderInDegrees=船舵
currentSet=扬帆
currentTackAngleInDegrees=迎风转向角度
currentDeflectorInMillimeters=转向器距离
currentDeflectorPercentage=转向器比率
currentForestayLoad=前桅支索负荷
currentForestayPressure=前桅支索压力
currentRakeInDegrees=倾斜度
currentTargetBoatspeedPercentage=目标船只速度
currentTargetHeelAngleInDegrees=目标侧倾
percent=%
currentTargetHeelAngleInDegreesTooltip=实现最佳性能的目标侧倾角
currentTargetBoatspeedPercentageTooltip=达到计算的目标船只速度的百分比
currentRakeInDegreesTooltip=桅倾度
currentForestayLoadTooltip=前桅支索负荷,通过负荷传感器测量
currentForestayPressureTooltip=前桅支索压力
currentDeflectorPercentageTooltip=转向器百分比
currentDeflectorInMillimetersTooltip=转向器距离
currentTackAngleInDegreesTooltip=计算的最佳迎风转向角度
currentSetTooltip=扬帆 (???)
currentRudderInDegreesTooltip=舵角;负值表示左舵,正值表示右舵
currentLeewayInDegreesTooltip=航差角
currentDriftInDegreesTooltip=航向与对地航向之间的角度
currentDepthInMetersTooltip=水深
tonsUnit=t
currentDistanceFoiledInMeters=水翼腾空距离
currentDurationFoiled=水翼腾空时间
currentDistanceFoiledInMetersTooltip=水翼腾空所用的距离(米)
currentDurationFoiledTooltip=水翼腾空所用时间
totalDurationFoiledInSeconds=水翼腾空总时间
totalDistanceFoiledInMeters=水翼腾空总距离
totalDurationFoiledInSecondsTooltip=水翼腾空总时间(在整个积分榜中)
totalDistanceFoiledInMetersTooltip=水翼腾空总距离(在整个积分榜中)
@@ -675,8 +675,6 @@ public abstract class LeaderboardPanel<LS extends LeaderboardSettings> extends A
public void updateSettings(final LS newSettings) {
this.currentSettings = newSettings;
boolean oldShallAddOverallDetails = shallAddOverallDetails();
if (newSettings.getOverallDetailsToShow() != null) {
setValuesWithReferenceOrder(newSettings.getOverallDetailsToShow(), getAvailableOverallDetailColumnTypes(),
selectedOverallDetailColumns);
@@ -701,17 +699,14 @@ public abstract class LeaderboardPanel<LS extends LeaderboardSettings> extends A
}
}
}
oldShallAddOverallDetails = applyDetailSettings(newSettings);
applyDetailSettings(newSettings);
addBusyTask();
Runnable doWhenNecessaryDetailHasBeenLoaded = new Runnable() {
@Override
public void run() {
try {
// avoid expansion during updateLeaderboard(...); will expand
// later
// if it was expanded before
// later if it was expanded before
applyRaceSelection(newSettings);
updateLeaderboard(leaderboard);
postApplySettings(newSettings, columnsToExpandAgain);
@@ -719,10 +714,9 @@ public abstract class LeaderboardPanel<LS extends LeaderboardSettings> extends A
removeBusyTask();
}
}
};
if (oldShallAddOverallDetails == shallAddOverallDetails() || oldShallAddOverallDetails
boolean newShallAddOverallDetails = shallAddOverallDetails();
if (oldShallAddOverallDetails == newShallAddOverallDetails || oldShallAddOverallDetails
|| getLeaderboard().hasOverallDetails()) {
doWhenNecessaryDetailHasBeenLoaded.run();
} else { // meaning that now the details need to be loaded from the server
@@ -742,8 +736,7 @@ public abstract class LeaderboardPanel<LS extends LeaderboardSettings> extends A
protected abstract void applyRaceSelection(final LeaderboardSettings newSettings);
private boolean applyDetailSettings(final LeaderboardSettings newSettings) {
boolean oldShallAddOverallDetails = shallAddOverallDetails();
private void applyDetailSettings(final LeaderboardSettings newSettings) {
if (newSettings.getOverallDetailsToShow() != null) {
setValuesWithReferenceOrder(newSettings.getOverallDetailsToShow(), getAvailableOverallDetailColumnTypes(),
selectedOverallDetailColumns);
@@ -771,7 +764,6 @@ public abstract class LeaderboardPanel<LS extends LeaderboardSettings> extends A
setValuesWithReferenceOrder(newSettings.getRaceDetailsToShow(), allRaceDetailsTypes.toArray(new DetailType[allRaceDetailsTypes.size()]),
selectedRaceDetails);
}
return oldShallAddOverallDetails;
}
protected abstract void setDefaultRaceColumnSelection(LS settings);
@@ -106,7 +106,7 @@ public class RaceBoardEntryPoint extends AbstractSailingEntryPoint {
final StoredSettingsLocation storageDefinition = StoredSettingsLocationFactory
.createStoredSettingsLocatorForRaceBoard(raceboardContextDefinition,
finalMode != null ? finalMode.name() : null);
sailingService.determineDetailTypes(raceboardContextDefinition.getLeaderboardGroupName(),
sailingService.determineDetailTypesForCompetitorChart(raceboardContextDefinition.getLeaderboardGroupName(),
raceboardData.getRace().getRaceIdentifier(), new AsyncCallback<List<DetailType>>() {
@Override
@@ -28,16 +28,16 @@ public class RaceBoardPerspectiveLifecycle extends AbstractPerspectiveLifecycle<
public static final String ID = "rb";
//constructor used by Standalone RaceBoard
public RaceBoardPerspectiveLifecycle(StringMessages stringMessages, List<DetailType> allowedDetailTypes) {
this(null, stringMessages, allowedDetailTypes);
public RaceBoardPerspectiveLifecycle(StringMessages stringMessages, List<DetailType> competitorChartAllowedDetailTypes) {
this(null, stringMessages, competitorChartAllowedDetailTypes);
}
public RaceBoardPerspectiveLifecycle(AbstractLeaderboardDTO leaderboard, StringMessages stringMessages, List<DetailType> allowedDetailTypes) {
public RaceBoardPerspectiveLifecycle(AbstractLeaderboardDTO leaderboard, StringMessages stringMessages, List<DetailType> competitorChartAllowedDetailTypes) {
this.stringMessages = stringMessages;
raceMapLifecycle = new RaceMapLifecycle(stringMessages);
windChartLifecycle = new WindChartLifecycle(stringMessages);
leaderboardPanelLifecycle = new SingleRaceLeaderboardPanelLifecycle(stringMessages);
multiCompetitorRaceChartLifecycle = new MultiCompetitorRaceChartLifecycle(stringMessages, allowedDetailTypes);
multiCompetitorRaceChartLifecycle = new MultiCompetitorRaceChartLifecycle(stringMessages, competitorChartAllowedDetailTypes);
mediaPlayerLifecycle = new MediaPlayerLifecycle(stringMessages);
raceTimePanelLifecycle = new RaceTimePanelLifecycle(stringMessages);
@@ -4815,7 +4815,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
URL base = createBaseUrl(urlAsString);
String path = "/sailingserver/spi/v1/masterdata/leaderboardgroups";
serverAddress = createUrl(base, path, query);
connection = HttpUrlConnectionHelper.redirectConnection(serverAddress);
// the response can take a very long time for MDI that include foiling data or such
connection = HttpUrlConnectionHelper.redirectConnection(serverAddress, Duration.ONE_HOUR.times(2));
getService().createOrUpdateDataImportProgressWithReplication(importOperationId, 0.02,
DataImportSubProgress.CONNECTION_ESTABLISH, 0.5);
if (compress) {
@@ -6710,7 +6711,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
@Override
public List<DetailType> determineDetailTypes(String leaderboardGroupName, RegattaAndRaceIdentifier identifier) {
public List<DetailType> determineDetailTypesForCompetitorChart(String leaderboardGroupName, RegattaAndRaceIdentifier identifier) {
List<DetailType> availableDetailsTypes = DetailType.getDefaultDetailTypesForChart();
final DynamicTrackedRace trackedRace = getService().getTrackedRace(identifier);
if (trackedRace != null) {
@@ -6725,8 +6726,6 @@ 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);
}
@@ -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
@@ -4,11 +4,9 @@ import java.io.BufferedInputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
@@ -30,7 +28,6 @@ import com.sap.sailing.domain.trackimport.GPSFixImporter;
import com.sap.sailing.domain.trackimport.GPSFixImporter.Callback;
import com.sap.sailing.server.gateway.impl.AbstractFileUploadServlet;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util.Pair;
/**
@@ -86,8 +83,6 @@ public class TrackFilesImportServlet extends AbstractFileUploadServlet {
protected Iterable<TrackFileImportDeviceIdentifier> importFiles(Iterable<Pair<String, FileItem>> files,
GPSFixImporter preferredImporter) throws IOException {
final Set<TrackFileImportDeviceIdentifier> deviceIds = new HashSet<>();
final Map<DeviceIdentifier, TimePoint> from = new HashMap<>();
final Map<DeviceIdentifier, TimePoint> to = new HashMap<>();
for (Pair<String, FileItem> pair : files) {
final String fileName = pair.getA();
final FileItem fileItem = pair.getB();
@@ -120,18 +115,6 @@ public class TrackFilesImportServlet extends AbstractFileUploadServlet {
public void addFix(GPSFix fix, TrackFileImportDeviceIdentifier device) {
deviceIds.add(device);
storeFix(fix, device);
TimePoint earliestFixSoFarFromCurrentDevice = from.get(device);
if (earliestFixSoFarFromCurrentDevice == null
|| earliestFixSoFarFromCurrentDevice.after(fix.getTimePoint())) {
earliestFixSoFarFromCurrentDevice = fix.getTimePoint();
from.put(device, earliestFixSoFarFromCurrentDevice);
}
TimePoint latestFixSoFarFromCurrentDevice = to.get(device);
if (latestFixSoFarFromCurrentDevice == null
|| latestFixSoFarFromCurrentDevice.before(fix.getTimePoint())) {
latestFixSoFarFromCurrentDevice = fix.getTimePoint();
to.put(device, latestFixSoFarFromCurrentDevice);
}
}
}, true, fileName);
succeeded = true;
@@ -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;
}
@@ -3,6 +3,7 @@ package com.sap.sailing.server.replication.test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNotSame;
import static org.junit.Assert.assertSame;
import java.net.UnknownHostException;
import java.util.ArrayList;
@@ -45,11 +46,13 @@ import com.sap.sailing.domain.persistence.MongoWindStore;
import com.sap.sailing.domain.persistence.MongoWindStoreFactory;
import com.sap.sailing.domain.persistence.PersistenceFactory;
import com.sap.sailing.domain.test.PositionAssert;
import com.sap.sailing.domain.tracking.BravoFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.impl.CompetitorBravoFixTrackImpl;
import com.sap.sailing.server.operationaltransformation.AddDefaultRegatta;
import com.sap.sailing.server.operationaltransformation.AddRaceDefinition;
import com.sap.sailing.server.operationaltransformation.CreateTrackedRace;
@@ -138,6 +141,25 @@ public class TrackedRaceContentsReplicationTest extends AbstractServerReplicatio
}
}
/**
* See also bug4387: test that after replicating the TrackedRace the CompetitorBravoFixTrackImpl's gpsTrack object
* points to the track from the TrackedRace to which the CompetitorBravoFixTrackImpl belongs.
*/
@Test
public void testBravoFixTrackReplication() throws InterruptedException {
trackedRace.getOrCreateSensorTrack(competitor, BravoFixTrack.TRACK_NAME, () -> new CompetitorBravoFixTrackImpl(competitor, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ false,
trackedRace.getTrack(competitor)));
Thread.sleep(1000);
DynamicTrackedRace replicaTrackedRace = replica.getTrackedRace(raceIdentifier);
Competitor replicaCompetitor = replicaTrackedRace.getRace().getCompetitors().iterator().next();
assertNotNull(replicaCompetitor);
GPSFixTrack<Competitor, GPSFixMoving> competitorTrack = replicaTrackedRace.getTrack(replicaCompetitor);
CompetitorBravoFixTrackImpl competitorSensorTrack = replicaTrackedRace.getOrCreateSensorTrack(
replicaCompetitor, BravoFixTrack.TRACK_NAME, () -> new CompetitorBravoFixTrackImpl(replicaCompetitor, BravoFixTrack.TRACK_NAME, /* hasExtendedFixes */ false,
replicaTrackedRace.getTrack(replicaCompetitor)));
assertSame(competitorTrack, competitorSensorTrack.getGpsTrack());
}
@Test
public void testMarkFixReplication() throws InterruptedException {
final GPSFixMovingImpl fix = new GPSFixMovingImpl(new DegreePosition(2, 3), new MillisecondsTimePoint(3456),
@@ -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>
@@ -27,7 +27,9 @@
<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>
<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.
+19
View File
@@ -24,6 +24,25 @@ body {
visibility: hidden;
}
.sapLogo {
float: left;
margin: 13px 0 0 12px;
}
.sapLogoImage {
max-height: 2em;
}
.sailingAnalyticsLabelPanel {
margin: 10px 0 0 0;
}
.sailingAnalyticsLabel {
float: left;
color: #fff;
font-size: 16px;
}
a {
color: #008fcc;
text-decoration: none;
@@ -318,6 +318,11 @@ public class UserStoreImpl implements UserStore {
return new ArrayList<User>(users.values());
}
@Override
public boolean hasUsers() {
return !users.isEmpty();
}
@Override
public User getUserByName(String name) {
final User result;
@@ -10,6 +10,8 @@ import com.sap.sse.security.shared.UserManagementException;
public interface UserStore extends Named {
Iterable<User> getUsers();
boolean hasUsers();
/**
* The user with that {@link User#getName() name} or {@code null} if no such user exists
@@ -191,7 +191,7 @@ public class SecurityServiceImpl implements ReplicableSecurityService, ClearStat
* is empty.
*/
private void initEmptyStore() {
if (Util.isEmpty(store.getUsers())) {
if (!store.hasUsers()) {
try {
logger.info("No users found, creating default user \"admin\" with password \"admin\"");
createSimpleUser("admin", "nobody@sapsailing.com", "admin",
@@ -10,7 +10,11 @@ import com.sap.sse.common.Duration;
public class HttpUrlConnectionHelper {
private static final int HTTP_MAX_REDIRECTS = 5;
public static HttpURLConnection redirectConnection(URL url) throws MalformedURLException, IOException {
/**
* Redirects the connection using the <code>Location</code> header. Make sure to set
* the timeout if you expect the response to take longer.
*/
public static HttpURLConnection redirectConnection(URL url, Duration timeout) throws MalformedURLException, IOException {
HttpURLConnection connection = null;
URL nextUrl = url;
for (int counterOfRedirects = 0; counterOfRedirects <= HTTP_MAX_REDIRECTS; counterOfRedirects++) {
@@ -21,8 +25,7 @@ public class HttpUrlConnectionHelper {
connection.setInstanceFollowRedirects(false);
connection.setRequestProperty("User-Agent", "Mozilla/5.0...");
connection.setDoOutput(true);
// Initial timeout needs to be big enough to allow the first parts of the response to reach this server
connection.setReadTimeout((int) Duration.ONE_MINUTE.times(10).asMillis());
connection.setReadTimeout((int) timeout.asMillis());
if (connection.getResponseCode() == HttpURLConnection.HTTP_MOVED_PERM
|| connection.getResponseCode() == HttpURLConnection.HTTP_MOVED_TEMP) {
String location = connection.getHeaderField("Location");
@@ -33,4 +36,8 @@ public class HttpUrlConnectionHelper {
}
return connection;
}
public static HttpURLConnection redirectConnection(URL url) throws MalformedURLException, IOException {
return redirectConnection(url, Duration.ONE_MINUTE.times(10));
}
}
+1
View File
@@ -104,6 +104,7 @@
<module>com.sap.sailing.datamining.shared</module>
<module>com.sap.sailing.domain.persistence</module>
<module>com.sap.sailing.domain.deckmanadapter</module>
<module>com.sap.sailing.domain.bravoadapter</module>
<module>com.sap.sailing.domain.expeditionadapter</module>
<module>com.sap.sailing.domain.tractracadapter</module>
<module>com.sap.sailing.domain.tractracadapter.persistence</module>