Merge remote-tracking branch 'origin/master' into bug2610

This commit is contained in:
fmittag committed 2015-12-04 15:11:26 +01:00
commit 96ca0d37fa
38 files changed
+438 -179

No files matched your search

@@ -94,8 +94,8 @@ public class TracTrac {
new MillisecondsTimePoint(record.getTrackingStartTime().asMillis()),
new MillisecondsTimePoint(record.getTrackingEndTime().asMillis()),
raceLogStore, regattaLogStore,
RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, /* simulateWithStartTimeNow */
false, /*useTracTracMarkPassings*/ false,
RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, /* offsetToStartTimeOfSimulatedRace */
null, /*useTracTracMarkPassings*/ false,
/* tracTracUsername */"", /* tracTracPassword */"", record.getRaceStatus(),
record.getRaceVisibility());
} else {
@@ -21,7 +21,7 @@ public abstract class AbstractSpeedImpl implements Speed {
@Override
public Duration getDuration(Distance distance) {
return new MillisecondsDurationImpl((long) (1000 * distance.getMeters() / getMetersPerSecond()));
return distance == null ? null : new MillisecondsDurationImpl((long) (1000 * distance.getMeters() / getMetersPerSecond()));
}
@Override
@@ -60,7 +60,7 @@ public class MultipleClassesInRegattaTest {
httpAndHost
+ "/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=cce678c8-97e6-11e0-9aed-406186cbf87c"),
new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI),
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* simulateWithStartTimeNow */ false, /* ignoreTracTracMarkPassings*/
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE,
EmptyGPSFixStore.INSTANCE, tracTracUsername, tracTracPassword,
TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
@@ -71,7 +71,7 @@ public class MultipleClassesInRegattaTest {
httpAndHost
+ "/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=11290bd6-97e7-11e0-9aed-406186cbf87c"),
new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI),
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* simulateWithStartTimeNow */ false, /* ignoreTracTracMarkPassings*/
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE,
EmptyGPSFixStore.INSTANCE, tracTracUsername, tracTracPassword,
TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
@@ -82,7 +82,7 @@ public class MultipleClassesInRegattaTest {
httpAndHost
+ "/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=39635b24-97e7-11e0-9aed-406186cbf87c"),
new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI),
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* simulateWithStartTimeNow */ false, /* ignoreTracTracMarkPassings*/
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE,
EmptyGPSFixStore.INSTANCE, tracTracUsername, tracTracPassword,
TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
@@ -93,7 +93,7 @@ public class MultipleClassesInRegattaTest {
httpAndHost
+ "/events/event_20110505_SailingTea/clientparams.php?event=event_20110505_SailingTea&race=04498426-7dfd-11e0-8236-406186cbf87c"),
new URI(liveURI), new URI(storedURI), new URI(courseDesignUpdateURI),
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* simulateWithStartTimeNow */ false, /* ignoreTracTracMarkPassings*/
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, EmptyWindStore.INSTANCE,
EmptyGPSFixStore.INSTANCE, tracTracUsername, tracTracPassword,
TracTracConnectionConstants.ONLINE_STATUS, TracTracConnectionConstants.ONLINE_VISIBILITY,
@@ -67,7 +67,7 @@ public class UnicodeCharactersInCompetitorNamesTest {
+ TracTracConnectionConstants.PORT_STORED), new URI(
"http://tracms.traclive.dk/update_course"),
/* startOfTracking */null, /* endOfTracking */null, /* delayToLiveInMillis */0l,
/* simulateWithStartTimeNow */ false, /* ignoreTracTracMarkPassings*/
/* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings*/
false, EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE,
EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, "tracTest", "tracTest", "", "",
new DummyTrackedRegattaRegistry(), mock(RaceLogResolver.class));
@@ -44,6 +44,7 @@ import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sailing.domain.tractracadapter.impl.DomainFactoryImpl;
import com.sap.sailing.domain.tractracadapter.impl.RaceCourseReceiver;
import com.sap.sailing.domain.tractracadapter.impl.Simulator;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.tractrac.model.lib.api.data.IPosition;
@@ -95,7 +96,6 @@ public interface DomainFactory {
* Fetch a race definition previously created by a call to {@link #getOrCreateRaceDefinitionAndTrackedRace}. If no such
* race definition was created so far, the call blocks until such a definition is provided by a call to
* {@link #getOrCreateRaceDefinitionAndTrackedRace}.
* @param raceId TODO
*/
RaceDefinition getAndWaitForRaceDefinition(UUID raceId);
@@ -105,7 +105,6 @@ public interface DomainFactory {
* A new {@link com.sap.sailing.domain.base.Regatta} is created if no event by
* an equal name with a boat class with an equal name as the <code>event</code>'s
* boat class exists yet.
* @param trackedRegattaRegistry TODO
*/
com.sap.sailing.domain.base.Regatta getOrCreateDefaultRegatta(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
IRace race, TrackedRegattaRegistry trackedRegattaRegistry);
@@ -134,27 +133,30 @@ public interface DomainFactory {
* if <code>null</code>, all stored data until the "end of time" will be loaded that the event has
* to provide, particularly for the mark positions which are stored per event, not per race; otherwise,
* particularly the mark position loading will be constrained to this end time.
* @param offsetToStartTimeOfSimulatedRace
* if non-<code>null</code>, the {@link Simulator} will be used with this duration as start offset
* @param windStore
* Provides the capability to obtain the {@link WindTrack}s for the different wind sources. A trivial
* implementation is {@link EmptyWindStore} which simply provides new, empty tracks. This is always
* available but loses track of the wind, e.g., during server restarts.
* @param raceLogResolver TODO
*/
TracTracRaceTracker createRaceTracker(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI,
TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername, String tracTracPassword,
String raceStatus, String raceVisibility, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver)
throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException, SubscriberInitializationException;
/**
* Same as {@link #createRaceTracker(URL, URI, URI, URI, TimePoint, TimePoint, WindStore, TrackedRegattaRegistry)}, only that
* a predefined {@link Regatta} is used to hold the resulting races.
* @param raceLogResolver TODO
* Same as {@link #createRaceTracker(URL, URI, URI, URI, TimePoint, TimePoint, WindStore, TrackedRegattaRegistry)},
* only that a predefined {@link Regatta} is used to hold the resulting races.
*
* @param offsetToStartTimeOfSimulatedRace
* if non-<code>null</code>, the {@link Simulator} will be used with this duration as start offset
*/
RaceTracker createRaceTracker(Regatta regatta, URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI,
TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername, String tracTracPassword,
String raceStatus, String raceVisibility, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver)
throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException,
@@ -262,9 +264,13 @@ public interface DomainFactory {
Util.Pair<Iterable<com.sap.sailing.domain.base.Competitor>, BoatClass> getCompetitorsAndDominantBoatClass(IRace race);
/**
* @param offsetToStartTimeOfSimulatedRace
* if non-<code>null</code>, the {@link Simulator} will be used with this duration as start offset
*/
RaceTrackingConnectivityParameters createTrackingConnectivityParameters(URL paramURL, URI liveURI, URI storedURI,
URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility);
/**
* Removes all knowledge about <code>tractracRace</code> which includes removing it from the race cache, from the
@@ -17,6 +17,8 @@ import com.sap.sailing.domain.regattalog.RegattaLogStore;
import com.sap.sailing.domain.tracking.RaceHandle;
import com.sap.sailing.domain.tracking.TrackerManager;
import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tractracadapter.impl.Simulator;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
@@ -65,12 +67,13 @@ public interface TracTracAdapter {
* created, with a single default series and a single default fleet. If a valid {@link RegattaIdentifier}
* is specified, a regatta lookup is performed with that identifier; if the regatta is found, it is used
* to add the races to. Otherwise, a default regatta as described above will be created and used.
* @param raceStatus
* @param offsetToStartTimeOfSimulatedRace
* if non-<code>null</code>, the {@link Simulator} will be used with this duration as start offset
*/
RaceHandle addTracTracRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo, URL paramURL,
URI liveURI, URI storedURI, URI courseDesignUpdateURI, TimePoint trackingStartTime,
TimePoint trackingEndTime, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
long timeoutForReceivingRaceDefinitionInMilliseconds, boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm,
long timeoutForReceivingRaceDefinitionInMilliseconds, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm,
String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility)
throws MalformedURLException, FileNotFoundException, URISyntaxException, Exception;
@@ -74,6 +74,7 @@ import com.sap.sailing.domain.tractracadapter.ReceiverType;
import com.sap.sailing.domain.tractracadapter.TracTracConfiguration;
import com.sap.sailing.domain.tractracadapter.TracTracControlPoint;
import com.sap.sailing.domain.tractracadapter.TracTracRaceTracker;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
@@ -652,27 +653,27 @@ public class DomainFactoryImpl implements DomainFactory {
@Override
public TracTracRaceTracker createRaceTracker(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI,
TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername,
String tracTracPassword, String raceStatus, String raceVisibility,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws MalformedURLException, FileNotFoundException,
URISyntaxException, CreateModelException, SubscriberInitializationException {
return new TracTracRaceTrackerImpl(this, paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking,
endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore,
endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore,
gpsFixStore, tracTracUsername, tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, raceLogResolver);
}
@Override
public RaceTracker createRaceTracker(Regatta regatta, URL paramURL, URI liveURI, URI storedURI,
URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername,
String tracTracPassword, String raceStatus, String raceVisibility,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver)
throws MalformedURLException, FileNotFoundException, URISyntaxException, CreateModelException,
SubscriberInitializationException {
return new TracTracRaceTrackerImpl(regatta, this, paramURL, liveURI, storedURI, courseDesignUpdateURI,
startOfTracking, endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, useInternalMarkPassingAlgorithm, raceLogStore,
startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore,
regattaLogStore, windStore, gpsFixStore, tracTracUsername, tracTracPassword, raceStatus,
raceVisibility, trackedRegattaRegistry, raceLogResolver);
}
@@ -690,11 +691,11 @@ public class DomainFactoryImpl implements DomainFactory {
@Override
public RaceTrackingConnectivityParameters createTrackingConnectivityParameters(URL paramURL, URI liveURI,
URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking,
long delayToLiveInMillis, boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, String tracTracUsername, String tracTracPassword, String raceStatus,
String raceVisibility) {
return new RaceTrackingConnectivityParametersImpl(paramURL, liveURI, storedURI, courseDesignUpdateURI,
startOfTracking, endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, useInternalMarkPassingAlgorithm, raceLogStore,
startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore,
regattaLogStore, this, tracTracUsername, tracTracPassword, raceStatus, raceVisibility);
}
@@ -16,6 +16,7 @@ import com.sap.sailing.domain.tracking.RaceTrackingConnectivityParameters;
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tractracadapter.DomainFactory;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.tractrac.model.lib.api.event.CreateModelException;
import com.tractrac.subscription.lib.api.SubscriberInitializationException;
@@ -31,7 +32,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
private final RegattaLogStore regattaLogStore;
private final DomainFactory domainFactory;
private final long delayToLiveInMillis;
private final boolean simulateWithStartTimeNow;
private final Duration offsetToStartTimeOfSimulatedRace;
private final String tracTracUsername;
private final String tracTracPassword;
private final String raceStatus;
@@ -40,7 +41,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
public RaceTrackingConnectivityParametersImpl(URL paramURL, URI liveURI, URI storedURI, URI courseDesignUpdateURI,
TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
DomainFactory domainFactory, String tracTracUsername, String tracTracPassword, String raceStatus,
String raceVisibility) {
super();
@@ -52,7 +53,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
this.endOfTracking = endOfTracking;
this.delayToLiveInMillis = delayToLiveInMillis;
this.domainFactory = domainFactory;
this.simulateWithStartTimeNow = simulateWithStartTimeNow;
this.offsetToStartTimeOfSimulatedRace = offsetToStartTimeOfSimulatedRace;
this.raceLogStore = raceLogStore;
this.regattaLogStore = regattaLogStore;
this.tracTracUsername = tracTracUsername;
@@ -67,7 +68,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
GPSFixStore gpsFixStore, RaceLogResolver raceLogResolver) throws MalformedURLException, FileNotFoundException, URISyntaxException,
CreateModelException, SubscriberInitializationException {
RaceTracker tracker = domainFactory.createRaceTracker(paramURL, liveURI, storedURI, courseDesignUpdateURI,
startOfTracking, endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, useInternalMarkPassingAlgorithm, raceLogStore,
startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore,
regattaLogStore, windStore, gpsFixStore, tracTracUsername, tracTracPassword, raceStatus,
raceVisibility, trackedRegattaRegistry, raceLogResolver);
return tracker;
@@ -77,7 +78,7 @@ public class RaceTrackingConnectivityParametersImpl implements RaceTrackingConne
public RaceTracker createRaceTracker(Regatta regatta, TrackedRegattaRegistry trackedRegattaRegistry,
WindStore windStore, GPSFixStore gpsFixStore, RaceLogResolver raceLogResolver) throws Exception {
RaceTracker tracker = domainFactory.createRaceTracker(regatta, paramURL, liveURI, storedURI,
courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, useInternalMarkPassingAlgorithm,
courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm,
raceLogStore, regattaLogStore, windStore, gpsFixStore, tracTracUsername, tracTracPassword, raceStatus,
raceVisibility, trackedRegattaRegistry, raceLogResolver);
return tracker;
@@ -4,7 +4,6 @@ import java.util.logging.Logger;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
import com.sap.sailing.domain.tractracadapter.DomainFactory;
@@ -57,13 +56,13 @@ public class RawPositionReceiver extends AbstractReceiverWithQueue<IRaceCompetit
IRace race = event.getA().getRace();
DynamicTrackedRace trackedRace = getTrackedRace(race);
if (trackedRace != null) {
GPSFixMoving fix = getDomainFactory().createGPSFixMoving(event.getB());
if (getSimulator() != null) {
fix = new GPSFixMovingImpl(fix.getPosition(), getSimulator().delay(
fix.getTimePoint()), fix.getSpeed());
}
final GPSFixMoving fix = getDomainFactory().createGPSFixMoving(event.getB());
Competitor competitor = getDomainFactory().getOrCreateCompetitor(event.getA().getCompetitor());
trackedRace.recordFix(competitor, fix);
if (getSimulator() != null) {
getSimulator().scheduleCompetitorPosition(competitor, fix);
} else {
trackedRace.recordFix(competitor, fix);
}
} else {
logger.warning("Couldn't find tracked race for race " + race.getName()
+ ". Dropping raw position event " + event);
@@ -24,7 +24,9 @@ import com.sap.sailing.domain.tracking.WindStore;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsDurationImpl;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class Simulator {
@@ -33,13 +35,15 @@ public class Simulator {
private DynamicTrackedRace trackedRace;
private final WindStore windStore;
private boolean stopped;
private long advanceInMillis = -1;
private Duration advanceInMillis = Duration.NULL.minus(1);
private Timer timer = new Timer("Timer for TracTrac Simulator");
private final Duration offsetToStart;
public Simulator(WindStore windStore) {
public Simulator(WindStore windStore, Duration offsetToStart) {
super();
assert windStore != null;
this.windStore = windStore;
this.offsetToStart = offsetToStart;
}
/**
@@ -69,7 +73,7 @@ public class Simulator {
/**
* This is what everybody is waiting for :-). Notifies all waiters.
*/
public synchronized void setAdvanceInMillis(long advanceInMillis) {
public synchronized void setAdvanceInMillis(Duration advanceInMillis) {
this.advanceInMillis = advanceInMillis;
notifyAll();
}
@@ -114,7 +118,7 @@ public class Simulator {
* Waits until {@link #advanceInMillis} is set to something not equal to -1 which is its initial value. Unblocked by
* {@link #setAdvanceInMillis(long)}.
*/
private synchronized long getAdvanceInMillis() {
private synchronized Duration getAdvance() {
while (!isAdvanceInMillisSet()) {
try {
wait(2000); // wait for two seconds, then re-evaluate whether there is a start time
@@ -125,6 +129,16 @@ public class Simulator {
}
return advanceInMillis;
}
private Duration getOffsetToStart() {
final Duration result;
if (offsetToStart != null) {
result = offsetToStart;
} else {
result = Duration.NULL;
}
return result;
}
/**
* Transforms <code>timed</code>'s time point according to this simulator's delay and waits roughly until this time
@@ -157,12 +171,12 @@ public class Simulator {
* Like {@link #delay}, only that it doesn't wait until <code>timePoint</code> is reached in wall time.
*/
public TimePoint advance(TimePoint timePoint) {
return new MillisecondsTimePoint(timePoint.asMillis()+getAdvanceInMillis());
return timePoint.plus(getAdvance());
}
/**
* If {@link #advanceInMillis} is already set to a non-negative value, it is left alone, and
* {@link #delay(TimePoint)} is called. Otherwise, <code>time</code> is taken to be the original start time of the
* {@link #advance(TimePoint)} is called. Otherwise, <code>time</code> is taken to be the original start time of the
* race which is then used to compute {@link #advanceInMillis} such that
* <code>time.asMillis() + advanceInMillis == System.currentTimeMillis()</code>.
*/
@@ -183,13 +197,13 @@ public class Simulator {
if (isAdvanceInMillisSet()) {
return advance(time);
} else {
setAdvanceInMillis(System.currentTimeMillis() - time.asMillis());
setAdvanceInMillis(new MillisecondsDurationImpl(MillisecondsTimePoint.now().minus(time.asMillis()).plus(getOffsetToStart()).asMillis()));
return advance(time);
}
}
private boolean isAdvanceInMillisSet() {
return advanceInMillis != -1;
return !advanceInMillis.equals(Duration.NULL.minus(1));
}
/**
@@ -238,21 +252,35 @@ public class Simulator {
// deliver an empty list now
trackedRace.updateMarkPassings(competitor, markPassings);
}
}
public void scheduleCompetitorPosition(final Competitor competitor, GPSFixMoving competitorFix) {
final RecordGPSFix<Competitor> recorder = (c, f)->trackedRace.recordFix(c, f);
scheduleFixRecording(competitor, competitorFix, recorder);
}
@FunctionalInterface
private static interface RecordGPSFix<T> {
void recordFix(T objectForFix, GPSFixMoving fix);
}
public void scheduleMarkPosition(final Mark mark, GPSFixMoving markFix) {
final TimePoint transformedTimepoint = advance(markFix.getTimePoint());
final GPSFixMoving transformedMarkFix = new GPSFixMovingImpl(markFix.getPosition(), transformedTimepoint, markFix.getSpeed());
final RecordGPSFix<Mark> recorder = (m, f)->trackedRace.recordFix(mark, f);
scheduleFixRecording(mark, markFix, recorder);
}
private <T> void scheduleFixRecording(final T object, GPSFixMoving fix, final RecordGPSFix<T> recorder) {
final TimePoint transformedTimepoint = advance(fix.getTimePoint());
final GPSFixMoving transformedMarkFix = new GPSFixMovingImpl(fix.getPosition(), transformedTimepoint, fix.getSpeed());
long waitTime = getWaitTimeInMillisUntil(transformedMarkFix.getTimePoint());
if (waitTime <= 0) {
trackedRace.recordFix(mark, transformedMarkFix);
recorder.recordFix(object, transformedMarkFix);
} else {
timer.schedule(new TimerTask() {
@Override
public void run() {
try {
trackedRace.recordFix(mark, transformedMarkFix);
recorder.recordFix(object, transformedMarkFix);
} catch (Exception e) {
logger.throwing(Simulator.class.getName(), "scheduleMarkPosition", e);
}
@@ -21,6 +21,7 @@ import com.sap.sailing.domain.tractracadapter.JSONService;
import com.sap.sailing.domain.tractracadapter.RaceRecord;
import com.sap.sailing.domain.tractracadapter.TracTracAdapter;
import com.sap.sailing.domain.tractracadapter.TracTracConfiguration;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
@@ -56,7 +57,7 @@ public class TracTracAdapterImpl implements TracTracAdapter {
getTracTracDomainFactory().createTrackingConnectivityParameters(paramURL, liveURI, storedURI,
courseDesignUpdateURI,
/* startOfTracking */null,
/* endOfTracking */null, delayToLiveInMillis, /* simulateWithStartTimeNow */false, /* ignoreTracTracMarkPassings */ false,
/* endOfTracking */null, delayToLiveInMillis, /* offsetToStartTimeOfSimulatedRace */null, /* ignoreTracTracMarkPassings */ false,
raceLogStore, regattaLogStore, tracTracUsername, tracTracPassword, raceStatus, raceVisibility),
timeoutInMilliseconds);
}
@@ -65,13 +66,13 @@ public class TracTracAdapterImpl implements TracTracAdapter {
public RaceHandle addTracTracRace(TrackerManager trackerManager, RegattaIdentifier regattaToAddTo, URL paramURL,
URI liveURI, URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking,
RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, long timeoutInMilliseconds,
boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, String tracTracUsername, String tracTracPassword, String raceStatus,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, String tracTracUsername, String tracTracPassword, String raceStatus,
String raceVisibility) throws Exception {
return trackerManager.addRace(
regattaToAddTo,
getTracTracDomainFactory().createTrackingConnectivityParameters(paramURL, liveURI, storedURI,
courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis,
simulateWithStartTimeNow, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, tracTracUsername, tracTracPassword,
offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, tracTracUsername, tracTracPassword,
raceStatus, raceVisibility), timeoutInMilliseconds);
}
@@ -49,6 +49,7 @@ import com.sap.sailing.domain.tractracadapter.DomainFactory;
import com.sap.sailing.domain.tractracadapter.Receiver;
import com.sap.sailing.domain.tractracadapter.TracTracConnectionConstants;
import com.sap.sailing.domain.tractracadapter.TracTracRaceTracker;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.tractrac.model.lib.api.ModelLocator;
@@ -253,26 +254,26 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
*/
protected TracTracRaceTrackerImpl(DomainFactory domainFactory, URL paramURL, URI liveURI, URI storedURI,
URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking, long delayToLiveInMillis,
boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore, RegattaLogStore regattaLogStore,
WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername, String tracTracPassword,
String raceStatus, String raceVisibility, TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver)
throws URISyntaxException, MalformedURLException, FileNotFoundException, CreateModelException,
SubscriberInitializationException {
this(ModelLocator.getEventFactory().createRace(new URI(paramURL.toString())), domainFactory, paramURL, liveURI,
storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis,
simulateWithStartTimeNow, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore, gpsFixStore, tracTracUsername,
offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore, gpsFixStore, tracTracUsername,
tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, raceLogResolver);
}
private TracTracRaceTrackerImpl(IRace tractracRace, DomainFactory domainFactory, URL paramURL, URI liveURI,
URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking,
long delayToLiveInMillis, boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername,
String tracTracPassword, String raceStatus, String raceVisibility,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws URISyntaxException, MalformedURLException,
FileNotFoundException, SubscriberInitializationException {
this(tractracRace, null, domainFactory, paramURL, liveURI, storedURI, courseDesignUpdateURI, startOfTracking,
endOfTracking, delayToLiveInMillis, simulateWithStartTimeNow, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore,
endOfTracking, delayToLiveInMillis, offsetToStartTimeOfSimulatedRace, useInternalMarkPassingAlgorithm, raceLogStore, regattaLogStore, windStore,
gpsFixStore, tracTracUsername, tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, raceLogResolver);
}
@@ -284,14 +285,14 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
*/
protected TracTracRaceTrackerImpl(Regatta regatta, DomainFactory domainFactory, URL paramURL, URI liveURI,
URI storedURI, URI courseDesignUpdateURI, TimePoint startOfTracking, TimePoint endOfTracking,
long delayToLiveInMillis, boolean simulateWithStartTimeNow, boolean ignoreTracTracMarkPassings, RaceLogStore raceLogStore,
long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean ignoreTracTracMarkPassings, RaceLogStore raceLogStore,
RegattaLogStore regattaLogStore, WindStore windStore, GPSFixStore gpsFixStore, String tracTracUsername,
String tracTracPassword, String raceStatus, String raceVisibility,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws URISyntaxException,
MalformedURLException, FileNotFoundException, CreateModelException, SubscriberInitializationException {
this(ModelLocator.getEventFactory().createRace(new URI(paramURL.toString())), regatta, domainFactory, paramURL,
liveURI, storedURI, courseDesignUpdateURI, startOfTracking, endOfTracking, delayToLiveInMillis,
simulateWithStartTimeNow, ignoreTracTracMarkPassings, raceLogStore, regattaLogStore, windStore, gpsFixStore, tracTracUsername,
offsetToStartTimeOfSimulatedRace, ignoreTracTracMarkPassings, raceLogStore, regattaLogStore, windStore, gpsFixStore, tracTracUsername,
tracTracPassword, raceStatus, raceVisibility, trackedRegattaRegistry, raceLogResolver);
}
@@ -300,15 +301,15 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
* @param regatta
* if <code>null</code>, then <code>domainFactory.getOrCreateRegatta(tractracEvent)</code> will be used
* to obtain a default regatta
* @param simulateWithStartTimeNow
* if <code>true</code>, the connector will adjust the time stamps of all events received such that the
* @param offsetToStartTimeOfSimulatedRace
* if not <code>null</code>, the connector will adjust the time stamps of all events received such that the
* first mark passing for the first waypoint will be set to "now." It will delay the forwarding of all
* events received such that they seem to be sent in "real-time." So, more or less the time points
* events received such that they seem to be sent in "real-time" + <code>offsetToStartTimeOfSimulatedRace</code> So, more or less the time points
* attached to the events sent to the receivers will again approximate the wall time.
*/
private TracTracRaceTrackerImpl(IRace tractracRace, final Regatta regatta, DomainFactory domainFactory,
URL paramURL, URI liveURI, URI storedURI, URI tracTracUpdateURI, TimePoint startOfTracking,
TimePoint endOfTracking, long delayToLiveInMillis, boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm,
TimePoint endOfTracking, long delayToLiveInMillis, Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm,
RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, WindStore windStore, GPSFixStore gpsFixStore,
String tracTracUsername, String tracTracPassword, String raceStatus, String raceVisibility,
TrackedRegattaRegistry trackedRegattaRegistry, RaceLogResolver raceLogResolver) throws URISyntaxException, MalformedURLException,
@@ -323,8 +324,8 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl implements
this.gpsFixStore = gpsFixStore;
this.domainFactory = domainFactory;
this.lastProgressPerID = new HashMap<>();
if (simulateWithStartTimeNow) {
simulator = new Simulator(windStore);
if (offsetToStartTimeOfSimulatedRace != null) {
simulator = new Simulator(windStore, offsetToStartTimeOfSimulatedRace);
// don't write the transformed wind fixes into the DB again... see also bug 1974
this.windStore = EmptyWindStore.INSTANCE;
} else {
@@ -43,4 +43,6 @@ public interface Course extends CourseBase {
void update(Iterable<Util.Pair<ControlPoint, PassingInstruction>> newControlPoints, DomainFactory baseDomainFactory) throws PatchFailedException;
int getNumberOfWaypoints();
Leg getLeg(int zeroBasedIndexOfWaypoint);
}
@@ -246,6 +246,11 @@ public class CourseImpl extends NamedImpl implements Course {
return waypoints.size();
}
@Override
public Leg getLeg(int zeroBasedIndexOfWaypoint) {
return legs.get(zeroBasedIndexOfWaypoint);
}
@Override
public String toString() {
lockForRead();
@@ -13,7 +13,6 @@ import java.util.ConcurrentModificationException;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.IdentityHashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
@@ -154,6 +153,7 @@ import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sse.concurrent.LockUtil;
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
import com.sap.sse.util.IdentityWrapper;
import com.sap.sse.util.SmartFutureCache;
import com.sap.sse.util.SmartFutureCache.AbstractCacheUpdater;
import com.sap.sse.util.SmartFutureCache.EmptyUpdateInterval;
@@ -183,7 +183,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
* By default, all wind sources are used, none are excluded. However, e.g., for performance reasons, particular wind
* sources such as the track-based estimation wind source, may be excluded by adding them to this set.
*/
private final Set<WindSource> windSourcesToExclude;
private final ConcurrentHashMap<WindSource, TrackedRaceImpl> windSourcesToExclude;
/**
* Keeps the oldest timestamp that is fed into this tracked race, either from a boat fix, a mark fix, a race
@@ -269,7 +269,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
*/
private transient SmartFutureCache<Competitor, com.sap.sse.common.Util.Triple<TimePoint, TimePoint, List<Maneuver>>, EmptyUpdateInterval> maneuverCache;
private transient Map<TimePoint, Future<Wind>> directionFromStartToNextMarkCache;
private transient ConcurrentHashMap<TimePoint, Future<Wind>> directionFromStartToNextMarkCache;
protected final MarkPassingCalculator markPassingCalculator;
@@ -354,7 +354,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
*/
private final NamedReentrantReadWriteLock loadingFromGPSFixStoreLock;
private final Map<Iterable<MarkPassing>, NamedReentrantReadWriteLock> locksForMarkPassings;
private final ConcurrentHashMap<IdentityWrapper<Iterable<MarkPassing>>, NamedReentrantReadWriteLock> locksForMarkPassings;
/**
* Caches wind requests for a few seconds to accelerate access in live mode
@@ -412,7 +412,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
rankingMetric = rankingMetricConstructor.apply(this);
raceStates = new WeakHashMap<>();
shortTimeWindCache = new ShortTimeWindCache(this, millisecondsOverWhichToAverageWind / 2);
locksForMarkPassings = new IdentityHashMap<>();
locksForMarkPassings = new ConcurrentHashMap<IdentityWrapper<Iterable<MarkPassing>>, NamedReentrantReadWriteLock>();
attachedRaceLogs = new ConcurrentHashMap<>();
attachedRegattaLogs = new ConcurrentHashMap<>();
attachedRaceExecutionOrderProviders = new ConcurrentHashMap<>();
@@ -424,8 +424,8 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
+ race.getName(), /* fair */ false);
this.cacheInvalidationTimerLock = new Object();
this.updateCount = 0;
this.windSourcesToExclude = new HashSet<WindSource>();
this.directionFromStartToNextMarkCache = new HashMap<TimePoint, Future<Wind>>();
this.windSourcesToExclude = new ConcurrentHashMap<>();
this.directionFromStartToNextMarkCache = new ConcurrentHashMap<TimePoint, Future<Wind>>();
this.millisecondsOverWhichToAverageSpeed = millisecondsOverWhichToAverageSpeed;
this.delayToLiveInMillis = delayToLiveInMillis;
this.startToNextMarkCacheInvalidationListeners = new ConcurrentHashMap<Mark, TrackedRaceImpl.StartToNextMarkCacheInvalidationListener>();
@@ -624,7 +624,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
gpsFixStore = EmptyGPSFixStore.INSTANCE;
competitorRankings = createCompetitorRankingsCache();
competitorRankingsLocks = createCompetitorRankingsLockMap();
directionFromStartToNextMarkCache = new HashMap<TimePoint, Future<Wind>>();
directionFromStartToNextMarkCache = new ConcurrentHashMap<>();
crossTrackErrorCache = new CrossTrackErrorCache(this);
crossTrackErrorCache.invalidate();
maneuverCache = createManeuverCache();
@@ -1146,11 +1146,11 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
int indexOfWaypoint = getRace().getCourse().getIndexOfWaypoint(startOfLeg);
if (indexOfWaypoint == -1) {
throw new IllegalArgumentException("Waypoint " + startOfLeg + " not found in " + getRace().getCourse());
} else if (indexOfWaypoint == Util.size(getRace().getCourse().getWaypoints()) - 1) {
} else if (indexOfWaypoint == getRace().getCourse().getNumberOfWaypoints() - 1) {
throw new IllegalArgumentException("Waypoint " + startOfLeg + " isn't start of any leg in "
+ getRace().getCourse());
}
return trackedLegs.get(race.getCourse().getLegs().get(indexOfWaypoint));
return trackedLegs.get(race.getCourse().getLeg(indexOfWaypoint));
} finally {
getRace().getCourse().unlockAfterRead();
}
@@ -1679,24 +1679,20 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
@Override
public Set<WindSource> getWindSourcesToExclude() {
synchronized (windSourcesToExclude) {
return Collections.unmodifiableSet(windSourcesToExclude);
}
return Collections.unmodifiableSet(windSourcesToExclude.keySet());
}
@Override
public void setWindSourcesToExclude(Iterable<? extends WindSource> windSourcesToExclude) {
Set<WindSource> old = new HashSet<>(this.windSourcesToExclude);
synchronized (this.windSourcesToExclude) {
LockUtil.lockForRead(getSerializationLock());
try {
this.windSourcesToExclude.clear();
for (WindSource windSourceToExclude : windSourcesToExclude) {
this.windSourcesToExclude.add(windSourceToExclude);
}
} finally {
LockUtil.unlockAfterRead(getSerializationLock());
Set<WindSource> old = new HashSet<>(getWindSourcesToExclude());
LockUtil.lockForRead(getSerializationLock());
try {
this.windSourcesToExclude.clear();
for (WindSource windSourceToExclude : windSourcesToExclude) {
this.windSourcesToExclude.put(windSourceToExclude, this);
}
} finally {
LockUtil.unlockAfterRead(getSerializationLock());
}
if (!old.equals(this.windSourcesToExclude)) {
clearAllCachesExceptManeuvers();
@@ -1742,30 +1738,33 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
public Wind getDirectionFromStartToNextMark(final TimePoint at) {
Future<Wind> future;
FutureTask<Wind> newFuture = null;
synchronized (directionFromStartToNextMarkCache) {
future = directionFromStartToNextMarkCache.get(at);
if (future == null) {
newFuture = new FutureTask<Wind>(new Callable<Wind>() {
@Override
public Wind call() {
Wind result;
Leg firstLeg = getRace().getCourse().getFirstLeg();
if (firstLeg != null) {
Position firstLegEnd = getApproximatePosition(firstLeg.getTo(), at);
Position firstLegStart = getApproximatePosition(firstLeg.getFrom(), at);
if (firstLegStart != null && firstLegEnd != null) {
result = new WindImpl(firstLegStart, at, new KnotSpeedWithBearingImpl(0.0,
firstLegEnd.getBearingGreatCircle(firstLegStart)));
future = directionFromStartToNextMarkCache.get(at);
if (future == null) {
synchronized (directionFromStartToNextMarkCache) {
future = directionFromStartToNextMarkCache.get(at);
if (future == null) {
newFuture = new FutureTask<Wind>(new Callable<Wind>() {
@Override
public Wind call() {
Wind result;
Leg firstLeg = getRace().getCourse().getFirstLeg();
if (firstLeg != null) {
Position firstLegEnd = getApproximatePosition(firstLeg.getTo(), at);
Position firstLegStart = getApproximatePosition(firstLeg.getFrom(), at);
if (firstLegStart != null && firstLegEnd != null) {
result = new WindImpl(firstLegStart, at, new KnotSpeedWithBearingImpl(0.0,
firstLegEnd.getBearingGreatCircle(firstLegStart)));
} else {
result = null;
}
} else {
result = null;
}
} else {
result = null;
return result;
}
return result;
}
});
directionFromStartToNextMarkCache.put(at, newFuture);
});
directionFromStartToNextMarkCache.put(at, newFuture);
}
}
}
if (newFuture != null) {
@@ -2062,15 +2061,19 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
}
protected NamedReentrantReadWriteLock getMarkPassingsLock(Iterable<MarkPassing> markPassings) {
synchronized (locksForMarkPassings) {
NamedReentrantReadWriteLock lock = locksForMarkPassings.get(markPassings);
if (lock == null) {
lock = new NamedReentrantReadWriteLock("mark passings lock for tracked race " + getRace().getName(), /* fair */
false);
locksForMarkPassings.put(markPassings, lock);
final IdentityWrapper<Iterable<MarkPassing>> markPassingsIdentity = new IdentityWrapper<>(markPassings);
NamedReentrantReadWriteLock lock = locksForMarkPassings.get(markPassingsIdentity);
if (lock == null) {
synchronized (locksForMarkPassings) {
lock = locksForMarkPassings.get(markPassingsIdentity);
if (lock == null) {
lock = new NamedReentrantReadWriteLock(
"mark passings lock for tracked race " + getRace().getName(), /* fair */false);
locksForMarkPassings.put(markPassingsIdentity, lock);
}
}
return lock;
}
return lock;
}
private void updateStartToNextMarkCacheInvalidationCacheListenersAfterWaypointRemoved(int zeroBasedIndex,
@@ -95,7 +95,7 @@ public class GwtTestCaseColumnToggling extends GWTTestCase {
+ TracTracConnectionConstants.PORT_TUNNEL_STORED : "tcp://" + TracTracConnectionConstants.HOST_NAME
+ ":" + TracTracConnectionConstants.PORT_STORED,
COURSE_DESIGN_UPDATE_URI,
false, false, /* simulateWithStartTimeNow */ false, /* ignoreTracTracMarkPassings */ false, TRACTRAC_USERNAME, TRACTRAC_PASSWORD, new AsyncCallback<Void>() {
false, false, /* offsetToStartTimeOfSimulatedRace */ null, /* ignoreTracTracMarkPassings */ false, TRACTRAC_USERNAME, TRACTRAC_PASSWORD, new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
@@ -40,18 +40,18 @@ public class AddWindFixDialog extends DataEntryDialog<WindDTO> {
if(windDTO.trueWindSpeedInKnots == null) {
errorMessage = stringMessages.pleaseEnterAValue();
} else if(windDTO.trueWindSpeedInKnots != null && (windDTO.trueWindSpeedInKnots < 0.0 || windDTO.trueWindSpeedInKnots > 100.0)) {
errorMessage = stringMessages.valueMustBeBetweenMinMax(stringMessages.speedInKnots(), "0", "100");
errorMessage = stringMessages.valueMustBeBetweenMinMax(stringMessages.speedInKnots(), 0, 100);
} else if(windDTO.trueWindFromDeg == null){
errorMessage = stringMessages.pleaseEnterAValue();
} else if(windDTO.trueWindFromDeg != null && (windDTO.trueWindFromDeg < 0.0 || windDTO.trueWindFromDeg > 360.0)){
errorMessage = stringMessages.valueMustBeBetweenMinMax(stringMessages.fromDeg(), "0", "360");
errorMessage = stringMessages.valueMustBeBetweenMinMax(stringMessages.fromDeg(), 0, 360);
} else if(windDTO.measureTimepoint == null) {
errorMessage = stringMessages.pleaseEnterAValue();
} else if(windDTO.position != null) {
if(windDTO.position.getLatDeg() < -90.0 || windDTO.position.getLatDeg() > 90.0){
errorMessage = stringMessages.valueMustBeBetweenMinMax(stringMessages.latitude(), "-90", "90");
errorMessage = stringMessages.valueMustBeBetweenMinMax(stringMessages.latitude(), -90, 90);
} else if(windDTO.position.getLngDeg() < -180.0 || windDTO.position.getLngDeg() > 180.0){
errorMessage = stringMessages.valueMustBeBetweenMinMax(stringMessages.longitude(), "-180", "180");
errorMessage = stringMessages.valueMustBeBetweenMinMax(stringMessages.longitude(), -180, 180);
}
}
@@ -225,7 +225,7 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane
declinationCheckbox.setValue(true);
trackableRacesPanel.add(declinationCheckbox);
final CheckBox simulateWithStartTimeNow = new CheckBox(stringMessages.simulateWithStartTimeNow());
final CheckBox simulateWithStartTimeNow = new CheckBox(stringMessages.simulateAsLiveRace());
simulateWithStartTimeNow.setWordWrap(false);
simulateWithStartTimeNow.setValue(false);
trackableRacesPanel.add(simulateWithStartTimeNow);
@@ -24,6 +24,7 @@ import com.google.gwt.user.client.ui.CaptionPanel;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter;
import com.google.gwt.user.client.ui.FlowPanel;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.HTMLTable.ColumnFormatter;
import com.google.gwt.user.client.ui.HorizontalPanel;
@@ -44,6 +45,7 @@ import com.sap.sailing.gwt.ui.client.shared.controls.SelectionCheckboxColumn;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sailing.gwt.ui.shared.TracTracConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.TracTracRaceRecordDTO;
import com.sap.sse.common.Duration;
import com.sap.sse.common.util.NaturalComparator;
import com.sap.sse.gwt.client.ErrorReporter;
import com.sap.sse.gwt.client.async.MarkedAsyncCallback;
@@ -83,7 +85,8 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel {
private LabeledAbstractFilterablePanel<TracTracRaceRecordDTO> racesFilterablePanel;
private CellTable<TracTracRaceRecordDTO> racesTable;
private static final String ZERO_AS_STRING = "0";
public TracTracEventManagementPanel(final SailingServiceAsync sailingService, ErrorReporter errorReporter,
RegattaRefresher regattaRefresher, StringMessages stringMessages) {
super(sailingService, regattaRefresher, errorReporter, new RaceSelectionModel(), true, stringMessages);
@@ -229,17 +232,12 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel {
protected HorizontalPanel createRacesPanel() {
HorizontalPanel racesPanel = new HorizontalPanel();
CaptionPanel trackableRacesPanel = createTrackableRacesPanel();
racesPanel.add(trackableRacesPanel);
racesPanel.setCellWidth(trackableRacesPanel, "50%");
CaptionPanel trackedRacesPanel = createTrackedRacesPanel();
racesPanel.add(trackedRacesPanel);
racesPanel.setCellWidth(trackedRacesPanel, "50%");
return racesPanel;
}
@@ -276,10 +274,32 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel {
correctWindCheckBox.setWordWrap(false);
correctWindCheckBox.setValue(Boolean.TRUE);
final CheckBox simulateWithStartTimeNowCheckBox = new CheckBox(stringMessages.simulateWithStartTimeNow());
final TextBox offsetToStartTimeOfSimulatedRaceTextBox = new TextBox();
offsetToStartTimeOfSimulatedRaceTextBox.setWidth("40px");
offsetToStartTimeOfSimulatedRaceTextBox.setEnabled(false);
offsetToStartTimeOfSimulatedRaceTextBox.setValue(ZERO_AS_STRING);
final CheckBox simulateWithStartTimeNowCheckBox = new CheckBox(stringMessages.simulateAsLiveRace());
simulateWithStartTimeNowCheckBox.ensureDebugId("SimulateWithStartTimeNowCheckBox");
simulateWithStartTimeNowCheckBox.setWordWrap(false);
simulateWithStartTimeNowCheckBox.setValue(Boolean.FALSE);
simulateWithStartTimeNowCheckBox.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
offsetToStartTimeOfSimulatedRaceTextBox.setEnabled(simulateWithStartTimeNowCheckBox.getValue());
offsetToStartTimeOfSimulatedRaceTextBox.setFocus(simulateWithStartTimeNowCheckBox.getValue());
}
});
final FlowPanel simulateAsLiveRacePanel = new FlowPanel();
simulateAsLiveRacePanel.add(simulateWithStartTimeNowCheckBox);
final Label offsetToStartLabel = new Label(stringMessages.simulateWithOffset());
final HorizontalPanel simulateWithOffsetPanel = new HorizontalPanel();
simulateWithOffsetPanel.add(offsetToStartLabel);
simulateWithOffsetPanel.add(offsetToStartTimeOfSimulatedRaceTextBox);
final CheckBox ignoreTracTracMarkPassingsCheckbox = new CheckBox(stringMessages.useInternalAlgorithm());
ignoreTracTracMarkPassingsCheckbox.setWordWrap(false);
ignoreTracTracMarkPassingsCheckbox.setValue(Boolean.FALSE);
@@ -287,8 +307,9 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel {
layoutTable.setWidget(1, 0, trackSettingsLabel);
layoutTable.setWidget(1, 1, trackWindCheckBox);
layoutTable.setWidget(2, 1, correctWindCheckBox);
layoutTable.setWidget(3, 1, simulateWithStartTimeNowCheckBox);
layoutTable.setWidget(4, 1, ignoreTracTracMarkPassingsCheckbox);
layoutTable.setWidget(3, 1, simulateAsLiveRacePanel);
layoutTable.setWidget(4, 1, simulateWithOffsetPanel);
layoutTable.setWidget(5, 1, ignoreTracTracMarkPassingsCheckbox);
// Filter
Label racesFilterLabel = new Label(stringMessages.filterRacesByName() + ":");
@@ -310,8 +331,8 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel {
}
};
racesFilterablePanel.getTextBox().ensureDebugId("TrackableRacesFilterTextBox");
layoutTable.setWidget(5, 0, racesFilterLabel);
layoutTable.setWidget(5, 1, racesFilterablePanel);
layoutTable.setWidget(6, 0, racesFilterLabel);
layoutTable.setWidget(6, 1, racesFilterablePanel);
// Races
TextColumn<TracTracRaceRecordDTO> regattaNameColumn = new TextColumn<TracTracRaceRecordDTO>() {
@@ -357,7 +378,6 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel {
}
};
raceVisibilityColumn.setSortable(true);
SelectionCheckboxColumn<TracTracRaceRecordDTO> selectionCheckboxColumn = new SelectionCheckboxColumn<TracTracRaceRecordDTO>(tableResources.cellTableStyle().cellTableCheckboxSelected(),
tableResources.cellTableStyle().cellTableCheckboxDeselected(), tableResources.cellTableStyle().cellTableCheckboxColumnCell()) {
@Override
@@ -381,26 +401,24 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel {
raceNameColumn, boatClassColumn, raceStartTrackingColumn, raceStatusColumn));
racesTable.setSelectionModel(selectionCheckboxColumn.getSelectionModel(), selectionCheckboxColumn.getSelectionManager());
racesTable.setWidth("100%");
raceList.addDataDisplay(racesTable);
layoutTable.setWidget(6, 0, racesTable);
cellFormatter.setColSpan(6, 0, 2);
Button startTrackingButton = new Button(stringMessages.startTracking());
startTrackingButton.ensureDebugId("StartTrackingButton");
startTrackingButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
Duration offsetToStartTimeOfSimulatedRace = null;
if(simulateWithStartTimeNowCheckBox.getValue().booleanValue()) {
offsetToStartTimeOfSimulatedRace = getMillisecondsDurationFromMinutesAsString(offsetToStartTimeOfSimulatedRaceTextBox.getValue());
}
trackSelectedRaces(trackWindCheckBox.getValue(), correctWindCheckBox.getValue(),
simulateWithStartTimeNowCheckBox.getValue(), ignoreTracTracMarkPassingsCheckbox.getValue());
offsetToStartTimeOfSimulatedRace, ignoreTracTracMarkPassingsCheckbox.getValue());
}
});
layoutTable.setWidget(7, 1, startTrackingButton);
trackableRacesPanel.setContentWidget(layoutTable);
return trackableRacesPanel;
}
@@ -462,6 +480,17 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel {
return boatClassNames.substring(0, boatClassNames.length() - 2);
}
private Duration getMillisecondsDurationFromMinutesAsString(String minutesAsString) {
Duration result = null;
if (minutesAsString != null) {
Double minutesAsDouble = Double.parseDouble(minutesAsString);
if (minutesAsDouble != null) {
result = Duration.ONE_MINUTE.times(minutesAsDouble.longValue());
}
}
return result;
}
private void fillConfigurations() {
this.sailingService.getPreviousTracTracConfigurations(new MarkedAsyncCallback<List<TracTracConfigurationDTO>>(
@@ -551,7 +580,7 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel {
}));
}
private void trackSelectedRaces(boolean trackWind, boolean correctWind, final boolean simulateWithStartTimeNow, boolean ignoreTracTracMarkPassings) {
private void trackSelectedRaces(boolean trackWind, boolean correctWind, final Duration offsetToStartTimeOfSimulatedRace, boolean ignoreTracTracMarkPassings) {
String liveURI = liveURITextBox.getValue();
String storedURI = storedURITextBox.getValue();
String courseDesignUpdateURI = tracTracUpdateURITextBox.getValue();
@@ -577,7 +606,7 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel {
}
if (checkBoatClassOK(selectedRegatta, selectedRaces)) {
sailingService.trackWithTracTrac(regattaIdentifier, selectedRaces, liveURI, storedURI,
courseDesignUpdateURI, trackWind, correctWind, simulateWithStartTimeNow, ignoreTracTracMarkPassings, tractracUsername,
courseDesignUpdateURI, trackWind, correctWind, offsetToStartTimeOfSimulatedRace, ignoreTracTracMarkPassings, tractracUsername,
tractracPassword, new MarkedAsyncCallback<Void>(new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
@@ -86,6 +86,7 @@ import com.sap.sailing.gwt.ui.shared.TrackFileImportDeviceIdentifierDTO;
import com.sap.sailing.gwt.ui.shared.VenueDTO;
import com.sap.sailing.gwt.ui.shared.WindDTO;
import com.sap.sailing.gwt.ui.shared.WindInfoForRaceDTO;
import com.sap.sse.common.Duration;
import com.sap.sse.common.NoCorrespondingServiceRegisteredException;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
@@ -116,7 +117,7 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
void trackWithTracTrac(RegattaIdentifier regattaToAddTo, Iterable<TracTracRaceRecordDTO> rrs, String liveURI,
String storedURI, String courseDesignUpdateURI, boolean trackWind, boolean correctWindByDeclination,
boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, String tracTracUsername, String tracTracPassword) throws Exception;
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, String tracTracUsername, String tracTracPassword) throws Exception;
void trackWithSwissTiming(RegattaIdentifier regattaToAddTo, Iterable<SwissTimingRaceRecordDTO> rrs, String hostname, int port,
boolean trackWind, boolean correctWindByDeclination, boolean useInternalMarkPassingAlgorithm) throws Exception;
@@ -78,6 +78,7 @@ import com.sap.sailing.gwt.ui.shared.TrackFileImportDeviceIdentifierDTO;
import com.sap.sailing.gwt.ui.shared.VenueDTO;
import com.sap.sailing.gwt.ui.shared.WindDTO;
import com.sap.sailing.gwt.ui.shared.WindInfoForRaceDTO;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
@@ -114,10 +115,10 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan
* @param liveURI
* may be <code>null</code> or the empty string in which case the server will use the
* {@link TracTracRaceRecordDTO#liveURI} from the <code>rr</code> race record.
* @param simulateWithStartTimeNow
* if <code>true</code>, the connector will adjust the time stamps of all events received such that the
* @param offsetToStartTimeOfSimulatedRace
* if not <code>null</code>, the connector will adjust the time stamps of all events received such that the
* first mark passing for the first waypoint will be set to "now." It will delay the forwarding of all
* events received such that they seem to be sent in "real-time." So, more or less the time points
* events received such that they seem to be sent in "real-time" plus the <code>offsetToStartTimeOfSimulatedRace</code>. So, more or less the time points
* attached to the events sent to the receivers will again approximate the wall time.
* @param useInternalMarkPassingAlgorithm
* whether or not to ignore the TracTrac-provided mark passings; if <code>true</code>, a separate mark
@@ -128,7 +129,7 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan
*/
void trackWithTracTrac(RegattaIdentifier regattaToAddTo, Iterable<TracTracRaceRecordDTO> rrs, String liveURI,
String storedURI, String courseDesignUpdateURI, boolean trackWind, boolean correctWindByDeclination,
boolean simulateWithStartTimeNow, boolean useInternalMarkPassingAlgorithm, String tracTracUsername,
Duration offsetToStartTimeOfSimulatedRace, boolean useInternalMarkPassingAlgorithm, String tracTracUsername,
String tracTracPassword, AsyncCallback<Void> callback);
void trackWithSwissTiming(RegattaIdentifier regattaToAddTo, Iterable<SwissTimingRaceRecordDTO> rrs,
@@ -361,7 +361,8 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c
String successfullyUpdatedScores();
String errorUpdatingScoresForLeaderboard(String leaderboardName, String message);
String maneuverTypesToShowWhenCompetitorIsClicked();
String simulateWithStartTimeNow();
String simulateAsLiveRace();
String simulateWithOffset();
String boatClassDoesNotMatchSelectedRegatta(String boatClass);
String regattaExistForSelectedBoatClass();
String reload();
@@ -436,7 +437,8 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c
String overallDetailsToShow();
String hhmmssUnit();
String actionAddWindData();
String valueMustBeBetweenMinMax(String name, String minValue, String maxValue);
String valueMustBeBetweenMinMax(String name, int minValue, int maxValue);
String valueMustBeGreaterThan(String name, String minValue);
String optional();
String pleaseEnterAValue();
String latitude();
@@ -1444,4 +1446,6 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c
String requiresValidRegatta();
String couldNotObtainRace(String regattaLikeName, String raceColumnName, String fleetName, String technicalErrorMessage);
String errorTryingToCreateEmbeddedMap(String message);
String transparentBufferLineOnHover();
String bufferLineStrokeWeight();
}
@@ -371,7 +371,8 @@ apply=Apply
successfullyUpdatedScores=Successfully updated scores
errorUpdatingScoresForLeaderboard=Error updating scores for leaderboard {0}: {1}
maneuverTypesToShowWhenCompetitorIsClicked=Maneuver types to show when competitor is clicked
simulateWithStartTimeNow=Simulate with "now" as start time
simulateAsLiveRace=Simulate as live race
simulateWithOffset=Offset before start in minutes:
boatClassDoesNotMatchSelectedRegatta=The selected races contain boat classes which are not the same as the boat class ''{0}'' of the selected regatta. No races will be loaded.
regattaExistForSelectedBoatClass=There is at least one regatta for the selected boat classes. Really create default regatta(s) for those race(s)?
reload=Reload
@@ -456,6 +457,7 @@ totalAverageSpeedOverGroundTooltip=Average speed over ground across the regatta.
hhmmssUnit=hh:mm:ss
actionAddWindData=Add wind data
valueMustBeBetweenMinMax=The value of ''{0}'' must be between {1} and {2}.
valueMustBeGreaterThan=The value of ''{0}'' must be greater than {1}.
optional=optional
pleaseEnterAValue=Please enter a value.
latitude=Latitude
@@ -1410,4 +1412,6 @@ selectALeaderboardGroup=Select a leaderboard group...
pleaseSelect=Please select
requiresValidRegatta=This page requires a valid regatta, race column and fleet name to identify the race to show.
couldNotObtainRace=Could not obtain a race with name {1} for fleet {2} for a regatta with name {0}: {3}
errorTryingToCreateEmbeddedMap=Error trying to create the embedded map: {0}
errorTryingToCreateEmbeddedMap=Error trying to create the embedded map: {0}
transparentBufferLineOnHover=Transparent buffer lines on hover of helplines
bufferLineStrokeWeight=Buffer line stroke weight
@@ -372,7 +372,8 @@ apply=Übernehmen
successfullyUpdatedScores=Ergebnisse erfolgreich aktualisiert
errorUpdatingScoresForLeaderboard=Fehler beim Aktualisieren der Ergebnisse für {0}: {1}
maneuverTypesToShowWhenCompetitorIsClicked=Anzuzeigende Manövertypen für angeklickte Teilnehmer
simulateWithStartTimeNow=Simulieren mit "jetzt" als Startzeit
simulateAsLiveRace=Simuliere als Live Rennen
simulateWithOffset=Zeit bis zum Start in Minuten:
boatClassDoesNotMatchSelectedRegatta=Die ausgewählten Rennen enthalten eine Bootsklasse, die nicht der Bootsklasse der ausgewählten Regatta ''{0}'' entspricht. Es werden keine Rennen geladen.
regattaExistForSelectedBoatClass=Es gibt mindestens eine Regatta zu den ausgewählten Bootsklassen. Dennoch Default-Regatta für diese(s) Rennen anlegen?
reload=Neu laden
@@ -456,7 +457,8 @@ totalAverageSpeedOverGround=\u2205 FüG
totalAverageSpeedOverGroundTooltip=Durchschnittliche Fahrt über Grund über die gesamte Regatta.
hhmmssUnit=hh:mm:ss
actionAddWindData=Winddaten hinzufügen
valueMustBeBetweenMinMax=Der Wert von ''{0}'' muß zwischen {1} und {2} sein.
valueMustBeBetweenMinMax=Der Wert von ''{0}'' muss zwischen {1} und {2} sein.
valueMustBeGreaterThan=Der Wert von ''{0}'' muss größer als {1} sein.
optional=optional
pleaseEnterAValue=Bitte geben Sie einen Wert ein.
latitude=Breite
@@ -1401,4 +1403,6 @@ selectALeaderboardGroup=Bitte eine Ranglisten-Gruppe auswählen...
pleaseSelect=Bitte auswählen
requiresValidRegatta=Diese Seite benötigt eine gültige Regatta, Rennspalte und einen gültigen Gruppennamen um die anzuzeigende Wettfahrt zu identifizieren.
couldNotObtainRace=Wettfahrt namens {1} für Gruppe {2} in einer Regatta names {0} konnte nicht gefunden werden: {3}
errorTryingToCreateEmbeddedMap=Fehler beim Erstellen der eingebetteten Karte: {0}
errorTryingToCreateEmbeddedMap=Fehler beim Erstellen der eingebetteten Karte: {0}
transparentBufferLineOnHover=Transparente Pufferlinien bei Maus über Hilfslinien
bufferLineStrokeWeight=Linienbreite der Pufferlinien
@@ -364,7 +364,8 @@ apply=Применить
successfullyUpdatedScores=Очки обновлены успешно
errorUpdatingScoresForLeaderboard=Ошибка обновления очков для таблицы лидеров {0}: {1}
maneuverTypesToShowWhenCompetitorIsClicked=Типы маневров для отображения при нажатии на участника
simulateWithStartTimeNow=Моделирования с "сейчас" в качестве времени начала
simulateAsLiveRace=Simulate as live race
simulateWithOffset=Offset before start in minutes:
boatClassDoesNotMatchSelectedRegatta=Выбранные гонки содержат классы судов не соответствующие классу судов ''{0}'' выбранной регаты.
regattaExistForSelectedBoatClass=Есть только одна регата для выбранных классов судов.
reload=Перезагрузить
@@ -440,6 +441,7 @@ totalAverageSpeedOverGroundTooltip=Средняя скорость относи
hhmmssUnit=чч:мм:сс
actionAddWindData=Добавить данные о ветре
valueMustBeBetweenMinMax=Значение ''{0}''должно быть в пределах от {1} до {2}.
valueMustBeGreaterThan=The value of ''{0}'' must be greater than {1}.
optional=Дополнительно
pleaseEnterAValue=Введите, пожалуйста, значение.
latitude=Широта
@@ -1340,4 +1342,6 @@ selectALeaderboardGroup=Select a leaderboard group...
pleaseSelect=Please select
requiresValidRegatta=This page requires a valid regatta, race column and fleet name to identify the race to show.
couldNotObtainRace=Could not obtain a race with name {1} for fleet {2} for a regatta with name {0}: {3}
errorTryingToCreateEmbeddedMap=Error trying to create the embedded map: {0}
errorTryingToCreateEmbeddedMap=Error trying to create the embedded map: {0}
transparentBufferLineOnHover=Transparent buffer lines on hover of helplines
bufferLineStrokeWeight=Buffer line stroke weight
@@ -538,6 +538,7 @@ connectors=Connectors
dampeningInterval=Dampening Interval
trackBasedEstimationWindSourceTypeTooltip=Calculated data from the competitor tracks
valueMustBeBetweenMinMax=The value of ''{0}'' must be between {1} and {2}.
valueMustBeGreaterThan=The value of ''{0}'' must be greater than {1}.
orMultipleEmails=or multiple E-Mails separated by comma
searchEvents=Search for events
explainNoConnectionsFromReplicas=No connections from a replica found. You can connect replicas by using their AdminConsole.
@@ -1072,7 +1073,8 @@ trackWind=Track Wind
rawFixes=All fixes (including outliers)
showCompetitorFullNameColumn=Show Competitor Full Name
disableRaceFilter=Disable filter
simulateWithStartTimeNow=Simulate with "now" as start time
simulateAsLiveRace=Simulate as live race
simulateWithOffset=Offset before start in minutes:
polarSheetOutlierDetectionRadiusTooltip=The radius used in the distance based outlierndetection algorithm.
noWindFixesAvailable=No Wind-Fixes available.
correctScoreFor=Correct score for competitor {0} in race {1}
@@ -1355,4 +1357,6 @@ selectALeaderboardGroup=Select a leaderboard group...
pleaseSelect=Please select
requiresValidRegatta=This page requires a valid regatta, race column and fleet name to identify the race to show.
couldNotObtainRace=Could not obtain a race with name {1} for fleet {2} for a regatta with name {0}: {3}
errorTryingToCreateEmbeddedMap=Error trying to create the embedded map: {0}
errorTryingToCreateEmbeddedMap=Error trying to create the embedded map: {0}
transparentBufferLineOnHover=Transparent buffer lines on hover of helplines
bufferLineStrokeWeight=Buffer line stroke weight
@@ -0,0 +1,57 @@
package com.sap.sailing.gwt.ui.client.shared.racemap;
import com.google.gwt.event.shared.HandlerRegistration;
import com.google.gwt.maps.client.events.click.ClickMapHandler;
import com.google.gwt.maps.client.events.mouseout.MouseOutMapEvent;
import com.google.gwt.maps.client.events.mouseout.MouseOutMapHandler;
import com.google.gwt.maps.client.events.mouseover.MouseOverMapEvent;
import com.google.gwt.maps.client.events.mouseover.MouseOverMapHandler;
import com.google.gwt.maps.client.overlays.Polyline;
import com.google.gwt.maps.client.overlays.PolylineOptions;
public class Hoverline {
private static final double TRANSPARENT = 0;
private static final double VISIBLE = 0.2d;
private final Polyline hoverline;
private final PolylineOptions options;
public Hoverline(final Polyline polyline, PolylineOptions polylineOptions, final RaceMap map) {
this.options = PolylineOptions.newInstance();
this.options.setClickable(polylineOptions.getClickable());
this.options.setGeodesic(polylineOptions.getGeodesic());
this.options.setMap(polyline.getMap());
this.options.setPath(polyline.getPath());
this.options.setStrokeColor(polylineOptions.getStrokeColor());
this.options.setZindex(polylineOptions.getZindex());
this.hoverline = Polyline.newInstance(this.options);
this.hoverline.setVisible(false);
polyline.addMouseOverHandler(new MouseOverMapHandler() {
@Override
public void onEvent(MouseOverMapEvent event) {
options.setStrokeOpacity(map.getSettings().getTransparentHoverlines() ? TRANSPARENT : VISIBLE);
options.setStrokeWeight(map.getSettings().getHoverlineStrokeWeight());
options.setVisible(true);
hoverline.setOptions(options);
}
});
hoverline.addMouseOutMoveHandler(new MouseOutMapHandler() {
@Override
public void onEvent(MouseOutMapEvent event) {
hoverline.setVisible(false);
}
});
}
public HandlerRegistration addClickHandler(ClickMapHandler handler) {
return this.hoverline.addClickHandler(handler);
}
public HandlerRegistration addMouseOutMoveHandler(MouseOutMapHandler handler) {
return this.hoverline.addMouseOutMoveHandler(handler);
}
public HandlerRegistration addMouseOverHandler(MouseOverMapHandler handler) {
return this.hoverline.addMouseOverHandler(handler);
}
}
@@ -1158,7 +1158,6 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
Polyline tail = fixesAndTails.getTail(competitorDTO);
if (tail == null) {
tail = fixesAndTails.createTailAndUpdateIndices(competitorDTO, tailsFromTime, tailsToTime, this);
tail.setMap(map);
} else {
fixesAndTails.updateTail(tail, competitorDTO, tailsFromTime, tailsToTime,
(int) (timeForPositionTransitionMillis==-1?-1:timeForPositionTransitionMillis/2));
@@ -1305,17 +1304,18 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
pointsAsArray.insertAt(0, advantageLinePos1);
pointsAsArray.insertAt(1, advantageLinePos2);
advantageLine.setPath(pointsAsArray);
advantageLine.setMap(map);
Hoverline advantageHoverline = new Hoverline(advantageLine, options, this);
advantageLineMouseOverHandler = new AdvantageLineMouseOverMapHandler(
bearingOfCombinedWindInDeg, new Date(windFix.measureTimepoint));
advantageLine.addMouseOverHandler(advantageLineMouseOverHandler);
advantageLine.addMouseOutMoveHandler(new MouseOutMapHandler() {
advantageHoverline.addMouseOutMoveHandler(new MouseOutMapHandler() {
@Override
public void onEvent(MouseOutMapEvent event) {
map.setTitle("");
}
});
advantageLine.setMap(map);
} else {
advantageLine.getPath().removeAt(1);
advantageLine.getPath().removeAt(0);
@@ -1561,20 +1561,20 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
pointsAsArray = MVCArray.newInstance();
lineToShowOrRemoveOrUpdate = Polyline.newInstance(options);
lineToShowOrRemoveOrUpdate.setPath(pointsAsArray);
lineToShowOrRemoveOrUpdate.setMap(map);
Hoverline lineToShowOrRemoveOrUpdateHoverline = new Hoverline(lineToShowOrRemoveOrUpdate, options, this);
lineToShowOrRemoveOrUpdate.addMouseOverHandler(new MouseOverMapHandler() {
@Override
public void onEvent(MouseOverMapEvent event) {
map.setTitle(lineInfoProvider.getLineInfo());
}
});
lineToShowOrRemoveOrUpdate.addMouseOutMoveHandler(new MouseOutMapHandler() {
lineToShowOrRemoveOrUpdateHoverline.addMouseOutMoveHandler(new MouseOutMapHandler() {
@Override
public void onEvent(MouseOutMapEvent event) {
map.setTitle("");
}
});
lineToShowOrRemoveOrUpdate.setMap(map);
} else {
pointsAsArray = lineToShowOrRemoveOrUpdate.getPath();
pointsAsArray.removeAt(1);
@@ -2328,6 +2328,12 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
updateCoordinateSystemFromSettings();
requiredRedraw = true;
}
if (newSettings.getTransparentHoverlines() != settings.getTransparentHoverlines()) {
settings.setTransparentHoverlines(newSettings.getTransparentHoverlines());
}
if (newSettings.getHoverlineStrokeWeight() != settings.getHoverlineStrokeWeight()) {
settings.setHoverlineStrokeWeight(newSettings.getHoverlineStrokeWeight());
}
if (requiredRedraw) {
redraw();
}
@@ -2493,23 +2499,25 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
public Polyline createTail(final CompetitorDTO competitor, List<LatLng> points) {
PolylineOptions options = createTailStyle(competitor, displayHighlighted(competitor));
Polyline result = Polyline.newInstance(options);
MVCArray<LatLng> pointsAsArray = MVCArray.newInstance(points.toArray(new LatLng[0]));
result.setPath(pointsAsArray);
result.addClickHandler(new ClickMapHandler() {
result.setMap(map);
Hoverline resultHoverline = new Hoverline(result, options, this);
final ClickMapHandler clickHandler = new ClickMapHandler() {
@Override
public void onEvent(ClickMapEvent event) {
showCompetitorInfoWindow(competitor, event.getMouseEvent().getLatLng());
}
});
};
result.addClickHandler(clickHandler);
resultHoverline.addClickHandler(clickHandler);
result.addMouseOverHandler(new MouseOverMapHandler() {
@Override
public void onEvent(MouseOverMapEvent event) {
map.setTitle(competitor.getSailID() + ", " + competitor.getName());
}
});
result.addMouseOutMoveHandler(new MouseOutMapHandler() {
resultHoverline.addMouseOutMoveHandler(new MouseOutMapHandler() {
@Override
public void onEvent(MouseOutMapEvent event) {
map.setTitle("");
@@ -19,6 +19,10 @@ public class RaceMapSettings extends AbstractSettings {
private RaceMapZoomSettings zoomSettings;
private RaceMapHelpLinesSettings helpLinesSettings;
private boolean transparentHoverlines = true;
private int hoverlineStrokeWeight = 10;
private long tailLengthInMilliseconds = 100000l;
@@ -55,6 +59,8 @@ public class RaceMapSettings extends AbstractSettings {
public RaceMapSettings(RaceMapSettings settings) {
this.buoyZoneRadiusInMeters = settings.buoyZoneRadiusInMeters;
this.helpLinesSettings = new RaceMapHelpLinesSettings(settings.getHelpLinesSettings().getVisibleHelpLineTypes());
this.transparentHoverlines = settings.transparentHoverlines;
this.hoverlineStrokeWeight = settings.hoverlineStrokeWeight;
this.maneuverTypesToShow = settings.maneuverTypesToShow;
this.showDouglasPeuckerPoints = settings.showDouglasPeuckerPoints;
this.showOnlySelectedCompetitors = settings.showOnlySelectedCompetitors;
@@ -153,6 +159,22 @@ public class RaceMapSettings extends AbstractSettings {
public void setHelpLinesSettings(RaceMapHelpLinesSettings helpLinesSettings) {
this.helpLinesSettings = helpLinesSettings;
}
public boolean getTransparentHoverlines() {
return this.transparentHoverlines;
}
public void setTransparentHoverlines(boolean transparentHoverlines) {
this.transparentHoverlines = transparentHoverlines;
}
public int getHoverlineStrokeWeight() {
return this.hoverlineStrokeWeight;
}
public void setHoverlineStrokeWeight(int hoverlineStrokeWeight) {
this.hoverlineStrokeWeight = hoverlineStrokeWeight;
}
public boolean isShowSelectedCompetitorsInfo() {
return showSelectedCompetitorsInfo;
@@ -24,11 +24,14 @@ import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapHelpLinesSettings.HelpLineTypes;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapZoomSettings.ZoomTypes;
import com.sap.sse.common.Util;
import com.sap.sse.gwt.client.controls.IntegerBox;
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
import com.sap.sse.gwt.client.dialog.DataEntryDialog.Validator;
import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent;
public class RaceMapSettingsDialogComponent implements SettingsDialogComponent<RaceMapSettings> {
private static final int MAX_BUOY_ZONE_RADIUS_IN_METERS = 100;
private static final int MAX_STROKE_WEIGHT = 33;
//Initializing the lists to prevent a null pointer exception in the first validation call
private List<Util.Pair<CheckBox, ManeuverType>> checkboxAndManeuverType = new ArrayList<Util.Pair<CheckBox, ManeuverType>>();
private List<Util.Pair<CheckBox, ZoomTypes>> checkboxAndZoomType = new ArrayList<Util.Pair<CheckBox,ZoomTypes>>();
@@ -43,6 +46,8 @@ public class RaceMapSettingsDialogComponent implements SettingsDialogComponent<R
private CheckBox showSelectedCompetitorsInfoCheckBox;
private LongBox tailLengthBox;
private DoubleBox buoyZoneRadiusBox;
private CheckBox transparentHoverlines;
private IntegerBox hoverlineStrokeWeight;
private boolean showViewSimulation;
private final StringMessages stringMessages;
@@ -211,6 +216,17 @@ public class RaceMapSettingsDialogComponent implements SettingsDialogComponent<R
vp.add(createHelpLineCheckBox(dialog, HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE));
vp.add(createHelpLineCheckBox(dialog, HelpLineTypes.COURSEGEOMETRY));
transparentHoverlines = dialog.createCheckbox(stringMessages.transparentBufferLineOnHover());
transparentHoverlines.setValue(initialSettings.getTransparentHoverlines());
vp.add(transparentHoverlines);
HorizontalPanel hoverlineStrokeWeightPanel = new HorizontalPanel();
Label hoverlineStrokeWeightLabel = new Label(stringMessages.bufferLineStrokeWeight() + ":");
hoverlineStrokeWeightPanel.add(hoverlineStrokeWeightLabel);
hoverlineStrokeWeight = dialog.createIntegerBox(initialSettings.getHoverlineStrokeWeight(), 3);
hoverlineStrokeWeightPanel.add(hoverlineStrokeWeight);
vp.add(hoverlineStrokeWeightPanel);
return vp;
}
@@ -267,6 +283,8 @@ public class RaceMapSettingsDialogComponent implements SettingsDialogComponent<R
result.setBuoyZoneRadiusInMeters(value);
}
}
result.setTransparentHoverlines(transparentHoverlines.getValue());
result.setHoverlineStrokeWeight(hoverlineStrokeWeight.getValue());
return result;
}
@@ -304,8 +322,10 @@ public class RaceMapSettingsDialogComponent implements SettingsDialogComponent<R
if (valueToValidate.getHelpLinesSettings().isVisible(HelpLineTypes.BOATTAILS) && valueToValidate.getTailLengthInMilliseconds() <= 0) {
errorMessage = stringMessages.tailLengthMustBePositive();
} else if (valueToValidate.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE)
&& (valueToValidate.getBuoyZoneRadiusInMeters() < 0.0 || valueToValidate.getBuoyZoneRadiusInMeters() > 100.0)) {
errorMessage = stringMessages.valueMustBeBetweenMinMax(stringMessages.buoyZone(), "0", "100");
&& (valueToValidate.getBuoyZoneRadiusInMeters() < 0 || valueToValidate.getBuoyZoneRadiusInMeters() > MAX_BUOY_ZONE_RADIUS_IN_METERS)) {
errorMessage = stringMessages.valueMustBeBetweenMinMax(stringMessages.buoyZone(), 0, 100);
} else if (valueToValidate.getHoverlineStrokeWeight() < 0 || valueToValidate.getHoverlineStrokeWeight() > MAX_STROKE_WEIGHT) {
errorMessage = stringMessages.valueMustBeBetweenMinMax(stringMessages.bufferLineStrokeWeight(), 0, MAX_STROKE_WEIGHT);
}
return errorMessage;
}
@@ -1023,7 +1023,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
@Override
public void trackWithTracTrac(RegattaIdentifier regattaToAddTo, Iterable<TracTracRaceRecordDTO> rrs, String liveURI, String storedURI,
String courseDesignUpdateURI, boolean trackWind, final boolean correctWindByDeclination,
final boolean simulateWithStartTimeNow, final boolean useInternalMarkPassingAlgorithm, String tracTracUsername, String tracTracPassword)
final Duration offsetToStartTimeOfSimulatedRace, final boolean useInternalMarkPassingAlgorithm, String tracTracUsername, String tracTracPassword)
throws Exception {
logger.info("tracWithTracTrac for regatta " + regattaToAddTo + " for race records " + rrs + " with liveURI " + liveURI
+ " and storedURI " + storedURI);
@@ -1054,7 +1054,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
new URI(courseDesignUpdateURI), new MillisecondsTimePoint(record.getTrackingStartTime().asMillis()),
new MillisecondsTimePoint(record.getTrackingEndTime().asMillis()),
getRaceLogStore(), getRegattaLogStore(),
RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, simulateWithStartTimeNow,
RaceTracker.TIMEOUT_FOR_RECEIVING_RACE_DEFINITION_IN_MILLISECONDS, offsetToStartTimeOfSimulatedRace,
useInternalMarkPassingAlgorithm, tracTracUsername, tracTracPassword, record.getRaceStatus(), record.getRaceVisibility());
if (trackWind) {
new Thread("Wind tracking starter for race " + record.getEventName() + "/" + record.getName()) {
@@ -4,6 +4,16 @@
<div id="mainContent">
<h4 class="articleHeadline">What's New - SAP Sailing Analytics</h4>
<div class="innerContent">
<h5 class="articleSubheadline">December 2015</h5>
<ul class="bulletList">
<li>New line hover behavior in race map. When the mouse is moved onto a line, for example the start line, a transparent,
wider line is activated behind it that acts as a buffer for the thin line, where the text displayed by the hover is still shown.
This makes it easier to hover over the lines. These wider lines can be made semi transparent instead of transparent
by unticking the corresponding checkbox in the settings of the race map. Furthermore the stroke weight of the wider lines can be
configured in the same settings dialog.
</li>
</ul>
<h5 class="articleSubheadline">November 2015</h5>
<ul class="bulletList">
@@ -70,7 +70,7 @@ public class TrackRaceBoatCompetitorMetadataReplicationTest extends AbstractServ
trackingParams = com.sap.sailing.domain.tractracadapter.DomainFactory.INSTANCE
.createTrackingConnectivityParameters(paramURL, liveURI, storedURI, courseDesignUpdateURI,
startOfTracking, endOfTracking, /* delayToLiveInMillis */
0l, /* simulateWithStartTimeNow */false, /*ignoreTracTracMarkPassings*/ false, EmptyRaceLogStore.INSTANCE,
0l, /* offsetToStartTimeOfSimulatedRace */null, /*ignoreTracTracMarkPassings*/ false, EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, tracTracUsername, tracTracPassword, "", "");
}
@@ -88,7 +88,7 @@ public class TrackRaceReplicationTest extends AbstractServerReplicationTest {
trackingParams = com.sap.sailing.domain.tractracadapter.DomainFactory.INSTANCE
.createTrackingConnectivityParameters(paramURL, liveURI, storedURI, courseDesignUpdateURI,
startOfTracking, endOfTracking, /* delayToLiveInMillis */
0l, /* simulateWithStartTimeNow */false, /*ignoreTracTracMarkPassings*/ false, EmptyRaceLogStore.INSTANCE,
0l, /* offsetToStartTimeOfSimulatedRace */null, /*ignoreTracTracMarkPassings*/ false, EmptyRaceLogStore.INSTANCE,
EmptyRegattaLogStore.INSTANCE, tracTracUsername, tracTracPassword, "", "");
}
@@ -7,6 +7,9 @@ import com.google.gwt.text.client.IntegerParser;
import com.google.gwt.text.shared.Renderer;
import com.google.gwt.user.client.ui.ValueBox;
/**
* Renders integers in their box without any separators, non-localized, plain.
*/
public class IntegerBox extends ValueBox<Integer> {
private static final Renderer<Integer> RENDERER = new Renderer<Integer>() {
public String render(Integer object) {
@@ -3,6 +3,8 @@ package com.sap.sse;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* A class providing static information about the running server.
@@ -10,14 +12,16 @@ import java.io.FileReader;
*
*/
public class ServerInfo {
private static final Logger logger = Logger.getLogger(ServerInfo.class.getName());
public static String getBuildVersion() {
String version = "Unknown or Development (" + ServerStartupConstants.SERVER_NAME + ")";
File versionfile = new File(ServerStartupConstants.JETTY_HOME + File.separator + "version.txt");
if (versionfile.exists()) {
try {
version = new BufferedReader(new FileReader(versionfile)).readLine();
try (BufferedReader bufferedReader = new BufferedReader(new FileReader(versionfile))) {
version = bufferedReader.readLine();
} catch (Exception ex) {
/* ignore */
logger.log(Level.WARNING, "Error trying to obtain version info", ex);
}
}
return version;
@@ -0,0 +1,34 @@
package com.sap.sse.util;
import java.io.Serializable;
/**
* Implements {@link Object#equals(Object)} and {@link Object#hashCode()} based on the wrapped object's
* identity, no matter what the object itself defines as {@code equals} and {@code hashCode}.
*
* @author Axel Uhl (D043530)
*
*/
public class IdentityWrapper<T> implements Serializable {
private static final long serialVersionUID = 3514488568026067341L;
private final T t;
public IdentityWrapper(T t) {
this.t = t;
}
@Override
public int hashCode() {
return System.identityHashCode(t);
}
@Override
public boolean equals(Object obj) {
return (obj instanceof IdentityWrapper<?>) ? ((IdentityWrapper<?>) obj).t == this.t : obj == t;
}
@Override
public String toString() {
return "IdentityWrapper for "+t;
}
}
@@ -31,7 +31,7 @@
<Arg name="threadpool">
<New id="threadpool" class="org.eclipse.jetty.util.thread.QueuedThreadPool">
<Arg name="minThreads" type="int">10</Arg>
<Arg name="maxThreads" type="int">3000</Arg>
<Arg name="maxThreads" type="int">64</Arg>
<Arg name="idleTimeout" type="int">25000</Arg>
<Set name="detailedDump">false</Set>
</New>