mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-08 05:11:04 +00:00
Merge branch 'master' into improved-replication
Conflicts: java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties
This commit is contained in:
commit
93c9ea3a4d
185 files changed
+2348
-1234
No files matched your search
BIN
Binary file not shown.
@@ -16,6 +16,7 @@ Like businesses, sailors need the latest information to make strategic decisions
|
||||
* [[TracTrac|wiki/tractrac-lifecycle]]
|
||||
* [[Analytics on a stick|wiki/analytics-on-a-stick]]
|
||||
* [[Monitoring Apache and RabbitMQ|wiki/monitoring-apache-and-rabbitmq]]
|
||||
* [[JMX Support|wiki/jmx]]
|
||||
* Information for Developers
|
||||
* [[Cook Book|wiki/cook-book]]
|
||||
* [[OnBoarding Information|wiki/onboarding]]
|
||||
@@ -43,11 +44,12 @@ Like businesses, sailors need the latest information to make strategic decisions
|
||||
* [[Racecommittee App|wiki/racecommittee-app]]
|
||||
* [[Web Services API|wiki/sailing-webservices]]
|
||||
* [[Theses (Bachelor, Master, ...)|wiki/theses]]
|
||||
* [[Google Analytics (Web Page Tracking)|wiki/ganalytics]]
|
||||
* Planning and Event Information
|
||||
* [[Project Planning (bigger development)|wiki/planning]]
|
||||
* [[General Event Planning|wiki/general-event-planning]]
|
||||
* [[Information about Extreme Sailing Series|wiki/extreme-sailing-series]]
|
||||
* [[Kieler Woche 2014 event page|wiki/kielerwoche2014]]
|
||||
* [[Travemünder Woche 2014 event page|wiki/tw2014]]
|
||||
|
||||
### Internal services (not related to wiki but useful)
|
||||
|
||||
|
||||
+24
@@ -1,6 +1,7 @@
|
||||
package com.sap.sailing.domain.common;
|
||||
|
||||
import com.sap.sailing.domain.common.impl.CentralAngleDistance;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.common.impl.RadianBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.RadianPosition;
|
||||
@@ -65,6 +66,17 @@ public class AbstractPosition implements Position {
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getQuickApproximateNauticalMileDistance(Position p) {
|
||||
double latDeg = getLatDeg();
|
||||
double pLatDeg = p.getLatDeg();
|
||||
final double latDiffDeg = Math.abs(latDeg - pLatDeg);
|
||||
double cosineOfAverageLatitude = Math.cos((latDeg+pLatDeg)/2./180.*Math.PI);
|
||||
final double normalizedLngDiffDeg = cosineOfAverageLatitude * Math.abs(getLngDeg() - p.getLngDeg());
|
||||
// One degree of latitude or one degree of longitude at the equator each correspond to 60 nautical miles.
|
||||
return Math.sqrt(latDiffDeg*latDiffDeg + normalizedLngDiffDeg*normalizedLngDiffDeg) * 60.;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bearing getBearingGreatCircle(Position p) {
|
||||
@@ -161,6 +173,18 @@ public class AbstractPosition implements Position {
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getLocalCoordinates(Position localOrigin, Bearing localEquatorBearing) {
|
||||
return this.getTargetCoordinates(localOrigin, localEquatorBearing, new DegreePosition(0.0, 0.0), new DegreeBearingImpl(90.0));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getTargetCoordinates(Position localOrigin, Bearing localEquatorBearing, Position targetOrigin, Bearing targetEquatorBearing) {
|
||||
Bearing localBearing = localEquatorBearing.getDifferenceTo(localOrigin.getBearingGreatCircle(this));
|
||||
Distance localDistance = this.getDistance(localOrigin);
|
||||
return targetOrigin.translateGreatCircle(targetEquatorBearing.add(localBearing), localDistance);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "(" + getLatDeg() + "," + getLngDeg() + ")";
|
||||
|
||||
@@ -71,4 +71,38 @@ public interface Position extends Serializable {
|
||||
* is positive if <code>left<code> is actually the position farther to the left, as seen from this position.
|
||||
*/
|
||||
Distance getDistanceToLine(Position left, Position right);
|
||||
|
||||
/**
|
||||
* Transform a position into its representation in a local geo-coordinate frame where <code>localOrigin</code> is
|
||||
* locally represented as <code>(latitude, longitude) = (0,0)</code> and <code>localEquatorBearing</code> is locally
|
||||
* represented as <code>East = 90 degrees</code>.
|
||||
* <p>
|
||||
*
|
||||
* This can be used to move a GPS-track to the origin <code>(0,0)</code> and aligning wind direction with North, or
|
||||
* bearing of start line with East.
|
||||
*/
|
||||
Position getLocalCoordinates(Position localOrigin, Bearing localEquatorBearing);
|
||||
|
||||
/**
|
||||
* Transform a position from local geo-coordinate frame <code>(localOrigin, localEquatorBearing)</code>
|
||||
* into the corresponding position in target geo-coordinate frame <code>(targetOrigin, targetEquatorBearing)</code>.
|
||||
* The transformation is equivalent to a rotation of the earth, which moves <code>localOrigin</code>
|
||||
* to <code>targetOrigin</code> while aligning <code>localEquatorBearing</code> to <code>targetEquatorBearing</code>.<p>
|
||||
*
|
||||
* This can be used to move a GPS-track from one location to another, in order to compare GPS-tracks from different areas
|
||||
* by superposing and aligning them based on wind direction or bearing of start line.
|
||||
*/
|
||||
Position getTargetCoordinates(Position localOrigin, Bearing localEquatorBearing, Position targetOrigin, Bearing targetEquatorBearing);
|
||||
|
||||
/**
|
||||
* Computes an approximate distance between this position and <code>p</code> by
|
||||
* using the average latitude's cosine to scale the longitudes and then calculate
|
||||
* the distance non-spherically in an imaginary Euklidian plane. The error is below
|
||||
* 1% even for several hundred nautical miles of randomly chosen distances, and even
|
||||
* lower (~.1%) for distances below ten nautical miles.<p>
|
||||
*
|
||||
* The method executes efficiently and requires only one call to the cosine method.
|
||||
*/
|
||||
double getQuickApproximateNauticalMileDistance(Position p);
|
||||
|
||||
}
|
||||
+10
-2
@@ -4,6 +4,14 @@ import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.confidence.Weigher;
|
||||
|
||||
/**
|
||||
* Uses {@link Position#getQuickApproximateNauticalMileDistance(Position)} to come
|
||||
* to a quick and reasonably fair weight for distances where the weight asymptotically
|
||||
* decreases hyperbolically with distance.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
*/
|
||||
public class HyperbolicDistanceWeigher implements Weigher<Position> {
|
||||
private static final long serialVersionUID = 5521299690322077224L;
|
||||
|
||||
@@ -37,8 +45,8 @@ public class HyperbolicDistanceWeigher implements Weigher<Position> {
|
||||
*/
|
||||
@Override
|
||||
public double getConfidence(Position fix, Position request) {
|
||||
double x = fix.getDistance(request).getMeters();
|
||||
double c = halfConfidence.getMeters();
|
||||
double x = fix.getQuickApproximateNauticalMileDistance(request);
|
||||
double c = halfConfidence.getNauticalMiles();
|
||||
double y = c;
|
||||
return c/(x+y);
|
||||
}
|
||||
|
||||
+6
-6
@@ -1083,11 +1083,11 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
private Regatta loadRegatta(DBObject dbRegatta, TrackedRegattaRegistry trackedRegattaRegistry) {
|
||||
Regatta result = null;
|
||||
if (dbRegatta != null) {
|
||||
String baseName = (String) dbRegatta.get(FieldNames.REGATTA_BASE_NAME.name());
|
||||
String name = (String) dbRegatta.get(FieldNames.REGATTA_NAME.name());
|
||||
String boatClassName = (String) dbRegatta.get(FieldNames.BOAT_CLASS_NAME.name());
|
||||
Serializable id = (Serializable) dbRegatta.get(FieldNames.REGATTA_ID.name());
|
||||
if (id == null) {
|
||||
id = baseName + "("+boatClassName+")";
|
||||
id = name;
|
||||
}
|
||||
BoatClass boatClass = null;
|
||||
if (boatClassName != null) {
|
||||
@@ -1099,14 +1099,12 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
RaceLogStore raceLogStore = MongoRaceLogStoreFactory.INSTANCE.getMongoRaceLogStore(
|
||||
new MongoObjectFactoryImpl(database, serviceFinderFactory),
|
||||
this);
|
||||
|
||||
Serializable courseAreaId = (Serializable) dbRegatta.get(FieldNames.COURSE_AREA_ID.name());
|
||||
CourseArea courseArea = null;
|
||||
if (courseAreaId != null) {
|
||||
UUID courseAreaUuid = UUID.fromString(courseAreaId.toString());
|
||||
courseArea = baseDomainFactory.getExistingCourseAreaById(courseAreaUuid);
|
||||
}
|
||||
|
||||
RegattaConfiguration configuration = null;
|
||||
if (dbRegatta.containsField(FieldNames.REGATTA_REGATTA_CONFIGURATION.name())) {
|
||||
try {
|
||||
@@ -1116,8 +1114,10 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
logger.log(Level.WARNING, "Error loading racing procedure configration for regatta.", e);
|
||||
}
|
||||
}
|
||||
|
||||
result = new RegattaImpl(raceLogStore, baseName, boatClass, series, /* persistent */ true, loadScoringScheme(dbRegatta), id, courseArea);
|
||||
Boolean useStartTimeInference = (Boolean) dbRegatta.get(FieldNames.REGATTA_USE_START_TIME_INFERENCE.name());
|
||||
result = new RegattaImpl(raceLogStore, name, boatClass, series, /* persistent */true,
|
||||
loadScoringScheme(dbRegatta), id, courseArea, useStartTimeInference == null ? true
|
||||
: useStartTimeInference);
|
||||
result.setRegattaConfiguration(configuration);
|
||||
}
|
||||
return result;
|
||||
|
||||
+1
@@ -26,6 +26,7 @@ public enum FieldNames {
|
||||
REGATTA_BASE_NAME,
|
||||
REGATTA_SERIES,
|
||||
REGATTA_REGATTA_CONFIGURATION,
|
||||
REGATTA_USE_START_TIME_INFERENCE,
|
||||
SERIES_NAME,
|
||||
SERIES_IS_MEDAL,
|
||||
SERIES_STARTS_WITH_ZERO_SCORE,
|
||||
|
||||
+1
-2
@@ -585,7 +585,6 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
DBObject dbRegatta = new BasicDBObject();
|
||||
DBObject query = new BasicDBObject(FieldNames.REGATTA_NAME.name(), regatta.getName());
|
||||
dbRegatta.put(FieldNames.REGATTA_NAME.name(), regatta.getName());
|
||||
dbRegatta.put(FieldNames.REGATTA_BASE_NAME.name(), regatta.getBaseName());
|
||||
dbRegatta.put(FieldNames.REGATTA_ID.name(), regatta.getId());
|
||||
dbRegatta.put(FieldNames.SCORING_SCHEME_TYPE.name(), regatta.getScoringScheme().getType().name());
|
||||
if (regatta.getBoatClass() != null) {
|
||||
@@ -605,7 +604,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
|
||||
DBObject configurationObject = (DBObject) JSON.parse(json.toString());
|
||||
dbRegatta.put(FieldNames.REGATTA_REGATTA_CONFIGURATION.name(), configurationObject);
|
||||
}
|
||||
|
||||
dbRegatta.put(FieldNames.REGATTA_USE_START_TIME_INFERENCE.name(), regatta.useStartTimeInference());
|
||||
regattasCollection.update(query, dbRegatta, /* upsrt */ true, /* multi */ false, WriteConcern.SAFE);
|
||||
}
|
||||
|
||||
|
||||
+3
-2
@@ -24,6 +24,7 @@ import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.base.impl.FleetImpl;
|
||||
import com.sap.sailing.domain.base.impl.RegattaImpl;
|
||||
import com.sap.sailing.domain.base.impl.SeriesImpl;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
@@ -71,8 +72,8 @@ public class CreateAndTrackWithRaceLogTest {
|
||||
author = service.getServerAuthor();
|
||||
Series series = new SeriesImpl("series", false, Collections.singletonList(fleet),
|
||||
Collections.singletonList(columnName), service);
|
||||
regatta = service.createRegatta("regatta", "Laser", UUID.randomUUID(), Collections.<Series>singletonList(series),
|
||||
false, new HighPoint(), UUID.randomUUID());
|
||||
regatta = service.createRegatta(RegattaImpl.getDefaultName("regatta", "Laser"), "Laser", UUID.randomUUID(), Collections.<Series>singletonList(series),
|
||||
false, new HighPoint(), UUID.randomUUID(), /* useStartTimeInference */ true);
|
||||
leaderboard = service.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "RegattaLeaderboard", new int[] {});
|
||||
adapter = RaceLogTrackingAdapterFactoryImpl.INSTANCE.getAdapter(DomainFactory.INSTANCE);
|
||||
}
|
||||
|
||||
+3
-3
@@ -32,8 +32,8 @@ import com.sap.sailing.domain.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sailing.domain.tracking.impl.TrackedRegattaImpl;
|
||||
|
||||
public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
private final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("49er");
|
||||
@@ -68,7 +68,7 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
store.storeFix(device, createFix(i+1000, 10, 20, 30, 40));
|
||||
}
|
||||
|
||||
TrackedRegatta regatta = new TrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE, "regatta", boatClass, null, null, "a", null));
|
||||
TrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, null, null, "a", null));
|
||||
DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race, Collections.<Sideline>emptyList(),
|
||||
EmptyWindStore.INSTANCE, store, 0, 0, 0);
|
||||
|
||||
@@ -110,7 +110,7 @@ public class TrackedRaceLoadsFixesTest extends AbstractGPSFixStoreTest {
|
||||
store.storeFix(device, createFix(i, 10, 20, 30, 40));
|
||||
}
|
||||
|
||||
TrackedRegatta regatta = new TrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE, "regatta", boatClass, null, null, "a", null));
|
||||
TrackedRegatta regatta = new DynamicTrackedRegattaImpl(new RegattaImpl(EmptyRaceLogStore.INSTANCE, RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, null, null, "a", null));
|
||||
DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(regatta, race, Collections.<Sideline>emptyList(),
|
||||
EmptyWindStore.INSTANCE, store, 0, 0, 0);
|
||||
|
||||
|
||||
+4
-2
@@ -7,6 +7,7 @@ import com.sap.sailing.domain.base.DomainFactory;
|
||||
import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.impl.RegattaImpl;
|
||||
import com.sap.sailing.domain.common.racelog.tracking.RaceNotCreatedException;
|
||||
import com.sap.sailing.domain.leaderboard.Leaderboard;
|
||||
import com.sap.sailing.domain.racelog.RaceLog;
|
||||
@@ -55,8 +56,9 @@ public class RaceLogConnectivityParams implements RaceTrackingConnectivityParame
|
||||
WindStore windStore, GPSFixStore gpsFixStore) {
|
||||
if (regatta == null) {
|
||||
BoatClass boatClass = new RaceInformationFinder(getRaceLog()).analyze().getBoatClass();
|
||||
regatta = service.getOrCreateDefaultRegatta("RaceLog-tracking default Regatta", boatClass.getName(),
|
||||
UUID.randomUUID());
|
||||
regatta = service.getOrCreateDefaultRegatta(
|
||||
RegattaImpl.getDefaultName("RaceLog-tracking default Regatta", boatClass.getName()),
|
||||
boatClass.getName(), UUID.randomUUID());
|
||||
}
|
||||
if (regatta == null) {
|
||||
throw new RaceNotCreatedException("No regatta for race-log tracked race");
|
||||
|
||||
+4
-5
@@ -1,6 +1,5 @@
|
||||
package com.sap.sailing.domain.racelogtracking.impl;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
@@ -22,12 +21,12 @@ public class RaceLogRacesHandle implements RaceHandle {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RaceDefinition> getRaces() {
|
||||
return getRaces(-1);
|
||||
public RaceDefinition getRace() {
|
||||
return getRace(-1);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RaceDefinition> getRaces(long timeoutInMilliseconds) {
|
||||
public RaceDefinition getRace(long timeoutInMilliseconds) {
|
||||
long start = System.currentTimeMillis();
|
||||
Set<RaceDefinition> raceDefs = tracker.getRaces();
|
||||
|
||||
@@ -54,7 +53,7 @@ public class RaceLogRacesHandle implements RaceHandle {
|
||||
}
|
||||
}
|
||||
}
|
||||
return result == null ? null : Collections.singleton(result);
|
||||
return result == null ? null : result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+3
-3
@@ -178,9 +178,9 @@ public abstract class EventBaseImpl implements EventBase {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSponsorImageURL(URL SponsorImageURL) {
|
||||
if (!sponsorImageURLs.contains(SponsorImageURL)) {
|
||||
sponsorImageURLs.add(SponsorImageURL);
|
||||
public void addSponsorImageURL(URL sponsorImageURL) {
|
||||
if (!sponsorImageURLs.contains(sponsorImageURL)) {
|
||||
sponsorImageURLs.add(sponsorImageURL);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@ public interface SwissTimingFactory {
|
||||
* When the connector is used with SailMaster instances hidden behind a "bridge" / firewall, no explicit requests
|
||||
* are possible, and the connector has to rely solely on the events it receives. It may, though, load recorded
|
||||
* race-specific messages through a {@link RaceSpecificMessageLoader} object. If a non-<code>null</code>
|
||||
* {@link RaceSpecificMessageLoader} is provided, the connector will fetch the {@link #getRaces() list of races}
|
||||
* {@link RaceSpecificMessageLoader} is provided, the connector will fetch the {@link #getRace() race}
|
||||
* from that loader. Additionally, the connector will use the loader upon each
|
||||
* {@link SailMasterConnector#trackRace(String)} to load all messages recorded by the loader for the race requested
|
||||
* so far.
|
||||
|
||||
+4
-2
@@ -99,8 +99,10 @@ public class DomainFactoryImpl implements DomainFactory {
|
||||
if (result == null) {
|
||||
BoatClass regattaBoatClass = boatClass != null ? boatClass : getRaceTypeFromRaceID(raceID).getBoatClass();
|
||||
Calendar calendar = Calendar.getInstance();
|
||||
result = new RegattaImpl(raceLogStore, "ST Regatta " + calendar.get(Calendar.YEAR)+" for race "+raceID, regattaBoatClass, trackedRegattaRegistry,
|
||||
getBaseDomainFactory().createScoringScheme(ScoringSchemeType.LOW_POINT), raceID, null);
|
||||
result = new RegattaImpl(raceLogStore, RegattaImpl.getDefaultName(
|
||||
"ST Regatta " + calendar.get(Calendar.YEAR) + " for race " + raceID, regattaBoatClass.getName()),
|
||||
regattaBoatClass, trackedRegattaRegistry, getBaseDomainFactory().createScoringScheme(
|
||||
ScoringSchemeType.LOW_POINT), raceID, null);
|
||||
logger.info("Created regatta "+result.getName()+" ("+result.hashCode()+")");
|
||||
raceIDToRegattaCache.put(raceID, result);
|
||||
}
|
||||
|
||||
+1
-1
@@ -64,7 +64,7 @@ import com.sap.sse.common.Util;
|
||||
* When the connector is used with SailMaster instances hidden behind a "bridge" / firewall, no explicit requests are
|
||||
* possible, and the connector has to rely solely on the events it receives. It may, though, load recorded race-specific
|
||||
* messages through a {@link RaceSpecificMessageLoader} object. If a non-<code>null</code> {@link RaceSpecificMessageLoader}
|
||||
* is provided to the constructor, the connector will fetch the {@link #getRaces() list of races} from that loader.
|
||||
* is provided to the constructor, the connector will fetch the {@link #getRace() race} from that loader.
|
||||
* Additionally, the connector will use the loader upon each {@link #trackRace(String)} to load all messages recorded
|
||||
* by the loader for the race requested so far.
|
||||
* <p>
|
||||
|
||||
+6
-6
@@ -170,7 +170,7 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl implemen
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RaceDefinition> getRaces() {
|
||||
public RaceDefinition getRace() {
|
||||
synchronized (this) {
|
||||
while (race == null) {
|
||||
try {
|
||||
@@ -180,11 +180,11 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl implemen
|
||||
}
|
||||
}
|
||||
}
|
||||
return Collections.singleton(race);
|
||||
return race;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RaceDefinition> getRaces(long timeoutInMilliseconds) {
|
||||
public RaceDefinition getRace(long timeoutInMilliseconds) {
|
||||
long start = System.currentTimeMillis();
|
||||
synchronized (this) {
|
||||
RaceDefinition preResult = race;
|
||||
@@ -200,11 +200,11 @@ public class SwissTimingRaceTrackerImpl extends AbstractRaceTrackerImpl implemen
|
||||
interrupted = true;
|
||||
}
|
||||
}
|
||||
final Set<RaceDefinition> result;
|
||||
final RaceDefinition result;
|
||||
if (preResult == null) {
|
||||
result = Collections.emptySet();
|
||||
result = null;
|
||||
} else {
|
||||
result = Collections.singleton(preResult);
|
||||
result = preResult;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -1,32 +1,31 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
|
||||
<booleanAttribute key="com.sap.jvm.profiling.ui.widgets.allowDebugging" value="false"/>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="2"/>
|
||||
</listAttribute>
|
||||
<mapAttribute key="org.eclipse.debug.core.preferred_launchers">
|
||||
<mapEntry key="[debug]" value="org.eclipse.jdt.junit.launchconfig"/>
|
||||
<mapEntry key="[run]" value="org.eclipse.jdt.junit.launchconfig"/>
|
||||
</mapAttribute>
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=com.sap.sailing.domain.test/src<com.sap.sailing.domain.test"/>
|
||||
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
|
||||
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
|
||||
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/sapjvm7"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.domain.test"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -DspatialWind=true"/>
|
||||
<stringAttribute key="profilingTraceType-ALLOCATION_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_INC_LINE_NRS%CTX_KEY%true%CTX_ENTRY%KEY_MAX_SIZE%CTX_KEY%65536%CTX_ENTRY%INCREASE_COUNT%CTX_KEY%8192%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ID%CTX_KEY%false%CTX_ENTRY%KEY_MIN_SIZE%CTX_KEY%32%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%CLASS_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ADAPTIVE%CTX_KEY%false%CTX_ENTRY%KEY_JRUBY_SUPPORT%CTX_KEY%false%CTX_ENTRY%"/>
|
||||
<stringAttribute key="profilingTraceType-IO_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%"/>
|
||||
<stringAttribute key="profilingTraceType-METHOD_PARAMETER_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%"/>
|
||||
<stringAttribute key="profilingTraceType-NETWORK_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_RESOLVE_ALL_HOSTS%CTX_KEY%true%CTX_ENTRY%"/>
|
||||
<stringAttribute key="profilingTraceType-PERFORMANCE_HOTSPOT_TRACE" value="KEY_IGNORE_SLEEPING_THREADS%CTX_KEY%true%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%"/>
|
||||
<stringAttribute key="profilingTraceType-SYNCHRONIZATION_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%"/>
|
||||
</launchConfiguration>
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<launchConfiguration type="org.eclipse.jdt.junit.launchconfig">
|
||||
<booleanAttribute key="com.sap.jvm.profiling.ui.widgets.allowDebugging" value="false"/>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
|
||||
<listEntry value="/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test"/>
|
||||
</listAttribute>
|
||||
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
|
||||
<listEntry value="2"/>
|
||||
</listAttribute>
|
||||
<mapAttribute key="org.eclipse.debug.core.preferred_launchers">
|
||||
<mapEntry key="[debug]" value="org.eclipse.jdt.junit.launchconfig"/>
|
||||
<mapEntry key="[run]" value="org.eclipse.jdt.junit.launchconfig"/>
|
||||
</mapAttribute>
|
||||
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.jdt.junit.CONTAINER" value="=com.sap.sailing.domain.test/src<com.sap.sailing.domain.test"/>
|
||||
<booleanAttribute key="org.eclipse.jdt.junit.KEEPRUNNING_ATTR" value="false"/>
|
||||
<stringAttribute key="org.eclipse.jdt.junit.TESTNAME" value=""/>
|
||||
<stringAttribute key="org.eclipse.jdt.junit.TEST_KIND" value="org.eclipse.jdt.junit.loader.junit4"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value=""/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.domain.test"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -DspatialWind=true"/>
|
||||
<stringAttribute key="profilingTraceType-ALLOCATION_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_INC_LINE_NRS%CTX_KEY%true%CTX_ENTRY%KEY_MAX_SIZE%CTX_KEY%65536%CTX_ENTRY%INCREASE_COUNT%CTX_KEY%8192%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ID%CTX_KEY%false%CTX_ENTRY%KEY_MIN_SIZE%CTX_KEY%32%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%CLASS_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ADAPTIVE%CTX_KEY%false%CTX_ENTRY%KEY_JRUBY_SUPPORT%CTX_KEY%false%CTX_ENTRY%"/>
|
||||
<stringAttribute key="profilingTraceType-IO_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%"/>
|
||||
<stringAttribute key="profilingTraceType-METHOD_PARAMETER_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%"/>
|
||||
<stringAttribute key="profilingTraceType-NETWORK_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_RESOLVE_ALL_HOSTS%CTX_KEY%true%CTX_ENTRY%"/>
|
||||
<stringAttribute key="profilingTraceType-PERFORMANCE_HOTSPOT_TRACE" value="KEY_IGNORE_SLEEPING_THREADS%CTX_KEY%true%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%"/>
|
||||
<stringAttribute key="profilingTraceType-SYNCHRONIZATION_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%"/>
|
||||
</launchConfiguration>
|
||||
+122
@@ -0,0 +1,122 @@
|
||||
package com.sap.sailing.domain.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Matchers.anyBoolean;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.impl.CompetitorImpl;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.LegType;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
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.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.TrackedLegImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.TrackedLegOfCompetitorImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.WindImpl;
|
||||
|
||||
public class BeatAngleTest {
|
||||
private Competitor competitor;
|
||||
private GPSFixTrack<Competitor, GPSFixMoving> competitorTrack;
|
||||
private TrackedRace trackedRace;
|
||||
private TrackedLeg trackedLeg;
|
||||
private TrackedLegOfCompetitor trackedLegOfCompetitor;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Before
|
||||
public void setUp() {
|
||||
competitor = mock(CompetitorImpl.class);
|
||||
trackedRace = mock(DynamicTrackedRaceImpl.class);
|
||||
trackedLeg = mock(TrackedLegImpl.class);
|
||||
trackedLegOfCompetitor = mock(TrackedLegOfCompetitorImpl.class);
|
||||
competitorTrack = mock(DynamicGPSFixMovingTrackImpl.class);
|
||||
when(competitorTrack.getEstimatedPosition((TimePoint) anyObject(), /* extrapolate */ anyBoolean())).thenReturn(new DegreePosition(0, 0));
|
||||
when(trackedLegOfCompetitor.getCompetitor()).thenReturn(competitor);
|
||||
when(trackedLegOfCompetitor.getTrackedLeg()).thenReturn(trackedLeg);
|
||||
when(trackedRace.getTrack(competitor)).thenReturn(competitorTrack);
|
||||
when(trackedLeg.getTrackedRace()).thenReturn(trackedRace);
|
||||
}
|
||||
|
||||
private void setUp(final Bearing windToBearing, final Bearing boatToBearing, final Bearing legBearing, LegType legType)
|
||||
throws NoWindException {
|
||||
when(trackedLeg.getLegBearing((TimePoint) anyObject())).thenReturn(legBearing);
|
||||
when(trackedLeg.getLegType((TimePoint) anyObject())).thenReturn(legType);
|
||||
when(trackedRace.getWind((Position) anyObject(), (TimePoint) anyObject())).thenReturn(
|
||||
new WindImpl(new DegreePosition(0, 0), MillisecondsTimePoint.now(), new KnotSpeedWithBearingImpl(12,
|
||||
windToBearing)));
|
||||
when(trackedLegOfCompetitor.getSpeedOverGround((TimePoint) anyObject())).thenReturn(new KnotSpeedWithBearingImpl(10, boatToBearing));
|
||||
when(trackedLegOfCompetitor.getBeatAngle((TimePoint) anyObject())).thenCallRealMethod();
|
||||
when(trackedLegOfCompetitor.getBeatAngle((TimePoint) anyObject(), (WindLegTypeAndLegBearingCache) anyObject())).thenCallRealMethod();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBeatAngleUpwindPort() throws NoWindException {
|
||||
final Bearing windToBearing = new DegreeBearingImpl(123);
|
||||
final Bearing boatToBearing = new DegreeBearingImpl(348);
|
||||
final Bearing legBearing = new DegreeBearingImpl(303);
|
||||
setUp(windToBearing, boatToBearing, legBearing, LegType.UPWIND);
|
||||
assertEquals(-45., trackedLegOfCompetitor.getBeatAngle(MillisecondsTimePoint.now()).getDegrees(), 0.00001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBeatAngleUpwindStarboard() throws NoWindException {
|
||||
final Bearing windToBearing = new DegreeBearingImpl(123);
|
||||
final Bearing boatToBearing = new DegreeBearingImpl(258);
|
||||
final Bearing legBearing = new DegreeBearingImpl(303);
|
||||
setUp(windToBearing, boatToBearing, legBearing, LegType.UPWIND);
|
||||
assertEquals(45., trackedLegOfCompetitor.getBeatAngle(MillisecondsTimePoint.now()).getDegrees(), 0.00001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBeatAngleDownwindPort() throws NoWindException {
|
||||
final Bearing windToBearing = new DegreeBearingImpl(123);
|
||||
final Bearing boatToBearing = new DegreeBearingImpl(120);
|
||||
final Bearing legBearing = new DegreeBearingImpl(123);
|
||||
setUp(windToBearing, boatToBearing, legBearing, LegType.DOWNWIND);
|
||||
assertEquals(-177., trackedLegOfCompetitor.getBeatAngle(MillisecondsTimePoint.now()).getDegrees(), 0.00001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBeatAngleDownwindStarboard() throws NoWindException {
|
||||
final Bearing windToBearing = new DegreeBearingImpl(123);
|
||||
final Bearing boatToBearing = new DegreeBearingImpl(126);
|
||||
final Bearing legBearing = new DegreeBearingImpl(123);
|
||||
setUp(windToBearing, boatToBearing, legBearing, LegType.DOWNWIND);
|
||||
assertEquals(177., trackedLegOfCompetitor.getBeatAngle(MillisecondsTimePoint.now()).getDegrees(), 0.00001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBeatAngleReachingPort() throws NoWindException {
|
||||
final Bearing windToBearing = new DegreeBearingImpl(123);
|
||||
final Bearing boatToBearing = new DegreeBearingImpl(33);
|
||||
final Bearing legBearing = new DegreeBearingImpl(33);
|
||||
setUp(windToBearing, boatToBearing, legBearing, LegType.REACHING);
|
||||
assertEquals(-90., trackedLegOfCompetitor.getBeatAngle(MillisecondsTimePoint.now()).getDegrees(), 0.00001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBeatAngleReachingStarboard() throws NoWindException {
|
||||
final Bearing windToBearing = new DegreeBearingImpl(303);
|
||||
final Bearing boatToBearing = new DegreeBearingImpl(33);
|
||||
final Bearing legBearing = new DegreeBearingImpl(33);
|
||||
setUp(windToBearing, boatToBearing, legBearing, LegType.REACHING);
|
||||
assertEquals(90., trackedLegOfCompetitor.getBeatAngle(MillisecondsTimePoint.now()).getDegrees(), 0.00001);
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
package com.sap.sailing.domain.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Iterator;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.base.Event;
|
||||
import com.sap.sailing.domain.base.impl.EventImpl;
|
||||
|
||||
public class CalculateImageSizeFromUrlTest {
|
||||
|
||||
@Test
|
||||
public void calculateImageSizeFromUrl() throws IOException {
|
||||
int width = 350;
|
||||
int height = 150;
|
||||
Dimension size = calculate("http://placehold.it/" + width + "x" + height);
|
||||
assertTrue(size.getWidth() == width);
|
||||
assertTrue(size.getHeight() == height);
|
||||
}
|
||||
|
||||
public Dimension calculate(String urlS) throws IOException {
|
||||
ImageInputStream in = null;
|
||||
try {
|
||||
URL url = new URL(urlS);
|
||||
URLConnection conn = url.openConnection();
|
||||
in = ImageIO.createImageInputStream(conn.getInputStream());
|
||||
final Iterator<ImageReader> readers = ImageIO.getImageReaders(in);
|
||||
if (readers.hasNext()) {
|
||||
ImageReader reader = readers.next();
|
||||
try {
|
||||
reader.setInput(in);
|
||||
return new Dimension(reader.getWidth(0), reader.getHeight(0));
|
||||
} finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testEventImageSize() throws MalformedURLException, InterruptedException, ExecutionException {
|
||||
Event e = new EventImpl("Event Name", /* startDate */ null, /* endDate */ null, "Kiel", /* isPublic */ true, UUID.randomUUID());
|
||||
int width = Math.max(10, (int) (200. * Math.random()));
|
||||
int height = Math.max(10, (int) (100. * Math.random()));
|
||||
URL imageURL = new URL("http://placehold.it/" + width + "x" + height);
|
||||
e.addImageURL(imageURL);
|
||||
Dimension expectedSize = new Dimension(width, height);
|
||||
assertEquals(expectedSize, e.getImageSize(imageURL));
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -349,7 +349,7 @@ public class ConfidenceTest {
|
||||
Position p = new DegreePosition(12, 13);
|
||||
assertEquals(1., weigher.getConfidence(p, p), 0.00000001);
|
||||
Position p2 = p.translateGreatCircle(new DegreeBearingImpl(123), new MeterDistance(1000));
|
||||
assertEquals(.5, weigher.getConfidence(p, p2), 0.00000001);
|
||||
assertEquals(.5, weigher.getConfidence(p, p2), 0.001); // somewhat more tolerant; using quick distance in weigher
|
||||
Position p3 = p.translateGreatCircle(new DegreeBearingImpl(123), new NauticalMileDistance(1000));
|
||||
final double threshold = 0.001;
|
||||
assertTrue("Expected confidence in 1000nm distance to be less than "+threshold+" but was "+weigher.getConfidence(p, p3),
|
||||
|
||||
@@ -34,8 +34,8 @@ import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sailing.domain.tracking.impl.TrackedRegattaImpl;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
import difflib.PatchFailedException;
|
||||
@@ -249,7 +249,7 @@ public class CourseTest {
|
||||
Course course = new CourseImpl("Test Course", waypoints);
|
||||
assertWaypointIndexes(course);
|
||||
final Set<CompetitorImpl> hasso = Collections.singleton(AbstractLeaderboardTest.createCompetitor("Hasso"));
|
||||
DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(/* trackedRegatta */new TrackedRegattaImpl(
|
||||
DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(/* trackedRegatta */new DynamicTrackedRegattaImpl(
|
||||
new RegattaImpl("test", null, new HashSet<Series>(), false, null, "test", null)),
|
||||
new RaceDefinitionImpl("Test Race", course, new BoatClassImpl("49er", /* upwind start */true), hasso),
|
||||
Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE,/* delayToLiveInMillis */3000,
|
||||
@@ -279,7 +279,7 @@ public class CourseTest {
|
||||
waypoints.add(wp2);
|
||||
Course course = new CourseImpl("Test Course", waypoints);
|
||||
final Set<CompetitorImpl> hasso = Collections.singleton(AbstractLeaderboardTest.createCompetitor("Hasso"));
|
||||
DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(/* trackedRegatta */ new TrackedRegattaImpl(
|
||||
DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(/* trackedRegatta */ new DynamicTrackedRegattaImpl(
|
||||
new RegattaImpl("test", null, new HashSet<Series>(), false, null, "test", null)),
|
||||
new RaceDefinitionImpl("Test Race", course, new BoatClassImpl("49er", /* upwind start */ true),
|
||||
hasso), Collections.<Sideline> emptyList(),
|
||||
|
||||
+1
-1
@@ -84,7 +84,7 @@ public class LeaderboardCourseChangeTest {
|
||||
String raceColumnName = "TestRace";
|
||||
RaceColumn raceColumn = series.addRaceColumn(raceColumnName, trackedRegattaRegistry);
|
||||
ScoringScheme scoringScheme = new LowPoint();
|
||||
Regatta mockedRegatta = new RegattaImpl("TestRegatta", boatClass, seriesSet, false, scoringScheme,
|
||||
Regatta mockedRegatta = new RegattaImpl(RegattaImpl.getDefaultName("TestRegatta", boatClass.getName()), boatClass, seriesSet, false, scoringScheme,
|
||||
UUID.randomUUID(), mock(CourseArea.class));
|
||||
ControlPoint start = new MarkImpl("Start");
|
||||
ControlPoint m1 = new MarkImpl("M1");
|
||||
|
||||
+3
-2
@@ -1158,7 +1158,7 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
|
||||
series.add(medalSeries);
|
||||
}
|
||||
|
||||
Regatta regatta = new RegattaImpl(regattaBaseName, boatClass, series, /* persistent */ false, scoringScheme, "123", null);
|
||||
Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName(regattaBaseName, boatClass.getName()), boatClass, series, /* persistent */ false, scoringScheme, "123", null);
|
||||
return regatta;
|
||||
}
|
||||
|
||||
@@ -1222,7 +1222,8 @@ public class LeaderboardScoringAndRankingTest extends AbstractLeaderboardTest {
|
||||
Series finalSeries = new SeriesImpl("After", /* isMedal */false, afterFleets, finalRaceColumnNames, /* trackedRegattaRegistry */ null);
|
||||
series.add(finalSeries);
|
||||
}
|
||||
Regatta regatta = new RegattaImpl("Test Regatta",DomainFactory.INSTANCE.getOrCreateBoatClass("Extreme40", /* typicallyStartsUpwind */ false),
|
||||
final BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("Extreme40", /* typicallyStartsUpwind */ false);
|
||||
Regatta regatta = new RegattaImpl(RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass,
|
||||
series, /* persistent */false, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.HIGH_POINT_FIRST_GETS_TEN), "123", /* course area */null);
|
||||
List<Competitor> competitors = createCompetitors(12);
|
||||
final int firstYellowCompetitorIndex = 3;
|
||||
|
||||
Executable
+118
@@ -0,0 +1,118 @@
|
||||
package com.sap.sailing.domain.test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Matchers.anyObject;
|
||||
import static org.mockito.Matchers.same;
|
||||
import static org.mockito.Mockito.doCallRealMethod;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.impl.MarkImpl;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
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.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.GPSFixMovingImpl;
|
||||
|
||||
public class MarkPositionTimeFilterTest {
|
||||
private DynamicTrackedRace trackedRace;
|
||||
private MarkImpl m;
|
||||
private DynamicGPSFixTrackImpl<Mark> track;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
trackedRace = mock(DynamicTrackedRaceImpl.class);
|
||||
m = new MarkImpl("Test Mark");
|
||||
track = new DynamicGPSFixTrackImpl<Mark>(m, /* millisecondsOverWhichToAverage */ 5000);
|
||||
when(trackedRace.getOrCreateTrack(m)).thenReturn(track);
|
||||
doCallRealMethod().when(trackedRace).recordFix(same(m), (GPSFixMoving) anyObject());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generalSetupTest() {
|
||||
assertSame(track, trackedRace.getOrCreateTrack(m));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddFixForMark() {
|
||||
trackedRace.recordFix(m, new GPSFixMovingImpl(new DegreePosition(12, 13), MillisecondsTimePoint.now(),
|
||||
new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(123))));
|
||||
track.lockForRead();
|
||||
try {
|
||||
assertEquals(1, track.getRawFixes().size());
|
||||
} finally {
|
||||
track.unlockAfterRead();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddFixForMarkWithinTrackingTimeRange() {
|
||||
TimePoint start = MillisecondsTimePoint.now();
|
||||
TimePoint fix = start.plus(10000);
|
||||
TimePoint end = fix.plus(10000);
|
||||
when(trackedRace.getStartOfTracking()).thenReturn(start);
|
||||
when(trackedRace.getEndOfTracking()).thenReturn(end);
|
||||
trackedRace.recordFix(m, new GPSFixMovingImpl(new DegreePosition(12, 13), fix,
|
||||
new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(123))));
|
||||
track.lockForRead();
|
||||
try {
|
||||
assertEquals(1, track.getRawFixes().size());
|
||||
} finally {
|
||||
track.unlockAfterRead();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddFixForMarkOutsideTrackingTimeRange() {
|
||||
TimePoint start = MillisecondsTimePoint.now();
|
||||
TimePoint end = start.plus(10000);
|
||||
TimePoint fix = end.plus(10000);
|
||||
when(trackedRace.getStartOfTracking()).thenReturn(start);
|
||||
when(trackedRace.getEndOfTracking()).thenReturn(end);
|
||||
trackedRace.recordFix(m, new GPSFixMovingImpl(new DegreePosition(12, 13), fix,
|
||||
new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(123))));
|
||||
track.lockForRead();
|
||||
try {
|
||||
assertTrue(track.getRawFixes().isEmpty());
|
||||
} finally {
|
||||
track.unlockAfterRead();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAddFixForMarkFirstOutsideTrackingTimeRangeThenWithin() {
|
||||
TimePoint start = MillisecondsTimePoint.now();
|
||||
TimePoint end = start.plus(10000);
|
||||
TimePoint fix = end.plus(10000);
|
||||
when(trackedRace.getStartOfTracking()).thenReturn(start);
|
||||
when(trackedRace.getEndOfTracking()).thenReturn(end);
|
||||
trackedRace.recordFix(m, new GPSFixMovingImpl(new DegreePosition(12, 13), fix,
|
||||
new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(123))));
|
||||
track.lockForRead();
|
||||
try {
|
||||
assertTrue(track.getRawFixes().isEmpty());
|
||||
} finally {
|
||||
track.unlockAfterRead();
|
||||
}
|
||||
when(trackedRace.getEndOfTracking()).thenReturn(fix.plus(10000));
|
||||
trackedRace.recordFix(m, new GPSFixMovingImpl(new DegreePosition(12, 13), fix,
|
||||
new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(123))));
|
||||
track.lockForRead();
|
||||
try {
|
||||
assertEquals(1, track.getRawFixes().size());
|
||||
} finally {
|
||||
track.unlockAfterRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,4 +240,56 @@ public class PositionTest {
|
||||
assertEquals(p6.getDistanceToLine(p1, p4).getMeters(), p6.getDistance(p1).scale(-1).getMeters(), delta);
|
||||
assertEquals(right.getDistanceToLine(p1, p6).getMeters(), right.getDistance(p1).getMeters(), delta);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuickApproximateDistance1() {
|
||||
Position p1 = new DegreePosition(0, 0);
|
||||
Position p2 = new DegreePosition(1, 0);
|
||||
assertEquals(60., p1.getQuickApproximateNauticalMileDistance(p2), 0.001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuickApproximateDistance2() {
|
||||
Position p1 = new DegreePosition(0, 0);
|
||||
Position p2 = new DegreePosition(0, 1);
|
||||
assertEquals(60., p1.getQuickApproximateNauticalMileDistance(p2), 0.001);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuickApproximateDistanceDiagonalNearEquator() {
|
||||
Position p1 = new DegreePosition(0, 0);
|
||||
Position p2 = new DegreePosition(0.1, 0.1);
|
||||
assertEquals(p1.getDistance(p2).getNauticalMiles(), p1.getQuickApproximateNauticalMileDistance(p2), 0.01 * p1.getDistance(p2).getNauticalMiles());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuickApproximateDistanceDiagonalFarFromEquator() {
|
||||
Position p1 = new DegreePosition(49, 8);
|
||||
Position p2 = new DegreePosition(50, 9);
|
||||
assertEquals(p1.getDistance(p2).getNauticalMiles(), p1.getQuickApproximateNauticalMileDistance(p2), 0.01 * p1.getDistance(p2).getNauticalMiles());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testQuickApproximateLongDistanceDiagonalFarFromEquator() {
|
||||
Position p1 = new DegreePosition(20, -4);
|
||||
Position p2 = new DegreePosition(50, 9);
|
||||
assertEquals(p1.getDistance(p2).getNauticalMiles(), p1.getQuickApproximateNauticalMileDistance(p2), 0.01 * p1.getDistance(p2).getNauticalMiles());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void performanceComparisonQuickAndFullDistance() {
|
||||
Position p1 = new DegreePosition(20, -4);
|
||||
Position p2 = new DegreePosition(50, 9);
|
||||
long startFull = System.currentTimeMillis();
|
||||
int numberOfExecutions = 1000000;
|
||||
for (int i=0; i<numberOfExecutions; i++) {
|
||||
p1.getDistance(p2);
|
||||
}
|
||||
long endFull = System.currentTimeMillis();
|
||||
for (int i=0; i<numberOfExecutions; i++) {
|
||||
p1.getQuickApproximateNauticalMileDistance(p2);
|
||||
}
|
||||
long endQuick = System.currentTimeMillis();
|
||||
System.out.println("Full: "+(endFull-startFull)+"ms; Quick: "+(endQuick-endFull)+"ms");
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -50,10 +50,10 @@ import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.WindPositionMode;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sailing.domain.tracking.impl.GPSFixImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.GPSFixMovingImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.TrackedRegattaImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.WindImpl;
|
||||
|
||||
public class ReachingLegTest extends TrackBasedTest {
|
||||
@@ -87,9 +87,9 @@ public class ReachingLegTest extends TrackBasedTest {
|
||||
protected DynamicTrackedRace createTrackedRace(String regattaName, String raceName, String boatClassName,
|
||||
Iterable<Competitor> competitors, TimePoint timePointForFixes) {
|
||||
BoatClassImpl boatClass = new BoatClassImpl(boatClassName, /* typicallyStartsUpwind */ true);
|
||||
Regatta regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, regattaName, boatClass, /* trackedRegattaRegistry */ null,
|
||||
Regatta regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, RegattaImpl.getDefaultName(regattaName, boatClass.getName()), boatClass, /* trackedRegattaRegistry */ null,
|
||||
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), "123", null);
|
||||
TrackedRegatta trackedRegatta = new TrackedRegattaImpl(regatta);
|
||||
TrackedRegatta trackedRegatta = new DynamicTrackedRegattaImpl(regatta);
|
||||
List<Waypoint> waypoints = new ArrayList<Waypoint>();
|
||||
// create a two-lap upwind/downwind course:
|
||||
MarkImpl left = new MarkImpl("Left lee gate buoy");
|
||||
|
||||
+3
-3
@@ -41,10 +41,10 @@ import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sailing.domain.tracking.impl.GPSFixImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.TrackedRegattaImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.WindImpl;
|
||||
|
||||
public abstract class TrackBasedTest {
|
||||
@@ -96,9 +96,9 @@ public abstract class TrackBasedTest {
|
||||
public static DynamicTrackedRace createTestTrackedRace(String regattaName, String raceName, String boatClassName,
|
||||
Iterable<Competitor> competitors, TimePoint timePointForFixes) {
|
||||
BoatClassImpl boatClass = new BoatClassImpl(boatClassName, /* typicallyStartsUpwind */ true);
|
||||
Regatta regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, regattaName, boatClass, /* trackedRegattaRegistry */ null,
|
||||
Regatta regatta = new RegattaImpl(EmptyRaceLogStore.INSTANCE, RegattaImpl.getDefaultName(regattaName, boatClass.getName()), boatClass, /* trackedRegattaRegistry */ null,
|
||||
DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), "123", null);
|
||||
TrackedRegatta trackedRegatta = new TrackedRegattaImpl(regatta);
|
||||
TrackedRegatta trackedRegatta = new DynamicTrackedRegattaImpl(regatta);
|
||||
List<Waypoint> waypoints = new ArrayList<Waypoint>();
|
||||
// create a two-lap upwind/downwind course:
|
||||
MarkImpl left = new MarkImpl("Left lee gate buoy");
|
||||
|
||||
+1
-2
@@ -65,8 +65,7 @@ public class UnicodeCharactersInCompetitorNamesTest {
|
||||
/* simulateWithStartTimeNow */ false, EmptyRaceLogStore.INSTANCE, EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE, "tracTest", "tracTest", "",
|
||||
new DummyTrackedRegattaRegistry());
|
||||
|
||||
Iterable<Competitor> competitors = fourtyninerYellow_2.getRacesHandle().getRaces().iterator().next()
|
||||
.getCompetitors();
|
||||
Iterable<Competitor> competitors = fourtyninerYellow_2.getRacesHandle().getRace().getCompetitors();
|
||||
for (Competitor competitor : competitors) {
|
||||
System.out.println(competitor.getName());
|
||||
}
|
||||
|
||||
+2
-2
@@ -26,9 +26,9 @@ import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.TrackedRegattaImpl;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public class UpdateMarkPassingTest {
|
||||
@@ -43,7 +43,7 @@ public class UpdateMarkPassingTest {
|
||||
when(race.getBoatClass()).thenReturn(new BoatClassImpl("49er", /* typicallyStartsUpwind */ true));
|
||||
when(race.getCompetitors()).thenReturn(Collections.singleton(competitor));
|
||||
DynamicTrackedRaceImpl trackedRace = new DynamicTrackedRaceImpl(
|
||||
/* trackedRegatta */new TrackedRegattaImpl(new RegattaImpl("test", null, new HashSet<Series>(), false, null,
|
||||
/* trackedRegatta */new DynamicTrackedRegattaImpl(new RegattaImpl("test", null, new HashSet<Series>(), false, null,
|
||||
"test", null)), race, Collections.<Sideline> emptyList(), EmptyWindStore.INSTANCE, EmptyGPSFixStore.INSTANCE,
|
||||
/* delayToLiveInMillis */1000, /* millisecondsOverWhichToAverageWind */30000,
|
||||
/* millisecondsOverWhichToAverageSpeed */30000);
|
||||
|
||||
@@ -277,7 +277,7 @@ public class WindTest {
|
||||
new BoatClassImpl("505", /* typicallyStartsUpwind */true), null));
|
||||
final BoatClass boatClass = domainFactory.getOrCreateBoatClass("ESS40");
|
||||
DynamicTrackedRace trackedRace = new DynamicTrackedRaceImpl(new DynamicTrackedRegattaImpl(
|
||||
new RegattaImpl(EmptyRaceLogStore.INSTANCE, "Test Regatta", boatClass,
|
||||
new RegattaImpl(EmptyRaceLogStore.INSTANCE, RegattaImpl.getDefaultName("Test Regatta", boatClass.getName()), boatClass,
|
||||
/* trackedRegattaRegistry */ null, domainFactory.createScoringScheme(ScoringSchemeType.LOW_POINT), "123", null)),
|
||||
new RaceDefinitionImpl("Test Race",
|
||||
new CourseImpl("Test Course", Arrays.asList(new Waypoint[] { w1, w2, w3 })),
|
||||
|
||||
+5
-4
@@ -38,10 +38,10 @@ import com.sap.sailing.domain.racelog.tracking.EmptyGPSFixStore;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||
import com.sap.sailing.domain.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
import com.sap.sailing.domain.tracking.impl.GPSFixImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.GPSFixMovingImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.TrackedRegattaImpl;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public class AbstractMockedRaceMarkPassingTest {
|
||||
@@ -63,11 +63,12 @@ public class AbstractMockedRaceMarkPassingTest {
|
||||
Waypoint w1 = new WaypointImpl(cp, PassingInstruction.Line);
|
||||
Waypoint w2 = new WaypointImpl(m, PassingInstruction.Port);
|
||||
Waypoint w3 = new WaypointImpl(cp, PassingInstruction.Line);
|
||||
Regatta r = new RegattaImpl("regatta", new BoatClassImpl("boat", true), Arrays.asList(new SeriesImpl("Series", true, Arrays.asList(new FleetImpl("fleet")),
|
||||
final BoatClassImpl boatClass = new BoatClassImpl("boat", true);
|
||||
Regatta r = new RegattaImpl(RegattaImpl.getDefaultName("regatta", boatClass.getName()), boatClass, Arrays.asList(new SeriesImpl("Series", true, Arrays.asList(new FleetImpl("fleet")),
|
||||
new ArrayList<String>(), null)), true, new HighPoint(), "ID", new CourseAreaImpl("area", new UUID(5, 5)));
|
||||
Course course = new CourseImpl("course", Arrays.asList(w1, w2, w3));
|
||||
RaceDefinition race = new RaceDefinitionImpl("Performance Race", course, new BoatClassImpl("boat", true), Arrays.asList(bob));
|
||||
trackedRace = new DynamicTrackedRaceImpl(new TrackedRegattaImpl(r), race, new ArrayList<Sideline>(), new EmptyWindStore(), EmptyGPSFixStore.INSTANCE, 0, 10000, 10000);
|
||||
RaceDefinition race = new RaceDefinitionImpl("Performance Race", course, boatClass, Arrays.asList(bob));
|
||||
trackedRace = new DynamicTrackedRaceImpl(new DynamicTrackedRegattaImpl(r), race, new ArrayList<Sideline>(), new EmptyWindStore(), EmptyGPSFixStore.INSTANCE, 0, 10000, 10000);
|
||||
trackedRace.setStartTimeReceived(new MillisecondsTimePoint(System.currentTimeMillis() - 120000));
|
||||
List<MillisecondsTimePoint> tps = Arrays.asList(new MillisecondsTimePoint(System.currentTimeMillis()), new MillisecondsTimePoint(System.currentTimeMillis() - 30000),
|
||||
new MillisecondsTimePoint(System.currentTimeMillis() + 30000));
|
||||
|
||||
+24
-11
@@ -9,7 +9,6 @@ import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.CourseArea;
|
||||
import com.sap.sailing.domain.base.CourseBase;
|
||||
import com.sap.sailing.domain.base.Event;
|
||||
import com.sap.sailing.domain.base.Leg;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.RaceColumnListener;
|
||||
@@ -33,6 +32,7 @@ import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.WindSourceType;
|
||||
import com.sap.sailing.domain.common.racelog.Flags;
|
||||
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||
import com.sap.sailing.domain.racelog.RaceLog;
|
||||
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
|
||||
@@ -46,6 +46,7 @@ import com.sap.sailing.domain.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.LineDetails;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.RaceAbortedListener;
|
||||
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
||||
import com.sap.sailing.domain.tracking.RaceListener;
|
||||
import com.sap.sailing.domain.tracking.StartTimeChangedListener;
|
||||
@@ -54,8 +55,8 @@ import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackedRaceStatus;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
import com.sap.sailing.domain.tracking.WindPositionMode;
|
||||
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
|
||||
import com.sap.sailing.domain.tracking.WindPositionMode;
|
||||
import com.sap.sailing.domain.tracking.WindStore;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
@@ -369,12 +370,6 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBaseName() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<? extends Series> getSeries() {
|
||||
// TODO Auto-generated method stub
|
||||
@@ -453,9 +448,9 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Event getEvent() {
|
||||
public boolean useStartTimeInference() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -463,11 +458,17 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUseStartTimeInference(boolean useStartTimeInference) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<TrackedRace> getTrackedRaces() {
|
||||
public Iterable<DynamicTrackedRace> getTrackedRaces() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
@@ -993,4 +994,16 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRaceAbortedListener(RaceAbortedListener listener) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAbortedByRaceCommittee(Flags flag) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+7
@@ -34,6 +34,7 @@ import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.LineDetails;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.RaceAbortedListener;
|
||||
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
||||
import com.sap.sailing.domain.tracking.StartTimeChangedListener;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
@@ -646,4 +647,10 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRaceAbortedListener(RaceAbortedListener listener) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+8
@@ -270,4 +270,12 @@ public interface DomainFactory {
|
||||
|
||||
List<Sideline> createSidelines(String raceMetadataString, Iterable<? extends TracTracControlPoint> allEventControlPoints);
|
||||
|
||||
/**
|
||||
* When a tracked race has been created for tracking with the TracTrac adapter, change listeners have to be subscribed to
|
||||
* the {@link TrackedRace} which will notify certain changes to the race's state to TracTrac. This includes the course layout,
|
||||
* the start time and whether a race was aborted.
|
||||
*/
|
||||
void addTracTracUpdateHandlers(URI tracTracUpdateURI, UUID tracTracEventUuid, String tracTracUsername,
|
||||
String tracTracPassword, RaceDefinition raceDefinition, DynamicTrackedRace trackedRace);
|
||||
|
||||
}
|
||||
+20
-11
@@ -372,7 +372,7 @@ public class DomainFactoryImpl implements DomainFactory {
|
||||
// This is particularly bad if a persistent regatta was loaded but a default regatta was accidentally created.
|
||||
// Then, there is no way but restart the server to get rid of this stale cache entry here.
|
||||
if (result == null) {
|
||||
result = new RegattaImpl(raceLogStore, defaultRegattaNameAndBoatClass.getA(), boatClass, trackedRegattaRegistry,
|
||||
result = new RegattaImpl(raceLogStore, RegattaImpl.getDefaultName(defaultRegattaNameAndBoatClass.getA(), boatClass.getName()), boatClass, trackedRegattaRegistry,
|
||||
// use the low-point system as the default scoring scheme
|
||||
getBaseDomainFactory().createScoringScheme(ScoringSchemeType.LOW_POINT), race.getId(), null);
|
||||
regattaCache.put(key, result);
|
||||
@@ -519,16 +519,8 @@ public class DomainFactoryImpl implements DomainFactory {
|
||||
trackedRace = createTrackedRace(trackedRegatta, raceDefinition, sidelines, windStore, EmptyGPSFixStore.INSTANCE,
|
||||
delayToLiveInMillis, millisecondsOverWhichToAverageWind, raceDefinitionSetToUpdate);
|
||||
logger.info("Added race " + raceDefinition + " to regatta " + trackedRegatta.getRegatta());
|
||||
|
||||
TracTracCourseDesignUpdateHandler courseDesignHandler = new TracTracCourseDesignUpdateHandler(
|
||||
tracTracUpdateURI, tracTracUsername, tracTracPassword, tracTracEventUuid,
|
||||
raceDefinition.getId());
|
||||
trackedRace.addCourseDesignChangedListener(courseDesignHandler);
|
||||
|
||||
TracTracStartTimeUpdateHandler startTimeHandler = new TracTracStartTimeUpdateHandler(
|
||||
tracTracUpdateURI, tracTracUsername, tracTracPassword, tracTracEventUuid,
|
||||
raceDefinition.getId());
|
||||
trackedRace.addStartTimeChangedListener(startTimeHandler);
|
||||
addTracTracUpdateHandlers(tracTracUpdateURI, tracTracEventUuid, tracTracUsername, tracTracPassword,
|
||||
raceDefinition, trackedRace);
|
||||
raceCache.put(raceId, raceDefinition);
|
||||
raceCache.notifyAll();
|
||||
} else {
|
||||
@@ -546,6 +538,23 @@ public class DomainFactoryImpl implements DomainFactory {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addTracTracUpdateHandlers(URI tracTracUpdateURI, UUID tracTracEventUuid, String tracTracUsername,
|
||||
String tracTracPassword, RaceDefinition raceDefinition, DynamicTrackedRace trackedRace) {
|
||||
TracTracCourseDesignUpdateHandler courseDesignHandler = new TracTracCourseDesignUpdateHandler(
|
||||
tracTracUpdateURI, tracTracUsername, tracTracPassword, tracTracEventUuid,
|
||||
raceDefinition.getId());
|
||||
trackedRace.addCourseDesignChangedListener(courseDesignHandler);
|
||||
TracTracStartTimeUpdateHandler startTimeHandler = new TracTracStartTimeUpdateHandler(
|
||||
tracTracUpdateURI, tracTracUsername, tracTracPassword, tracTracEventUuid,
|
||||
raceDefinition.getId());
|
||||
trackedRace.addStartTimeChangedListener(startTimeHandler);
|
||||
TracTracRaceAbortedHandler raceAbortedHandler = new TracTracRaceAbortedHandler(
|
||||
tracTracUpdateURI, tracTracUsername, tracTracPassword, tracTracEventUuid,
|
||||
raceDefinition.getId());
|
||||
trackedRace.addRaceAbortedListener(raceAbortedHandler);
|
||||
}
|
||||
|
||||
private DynamicTrackedRace createTrackedRace(TrackedRegatta trackedRegatta, RaceDefinition race, Iterable<Sideline> sidelines, WindStore windStore, GPSFixStore gpsFixStore,
|
||||
long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, DynamicRaceDefinitionSet raceDefinitionSetToUpdate) {
|
||||
return trackedRegatta.createTrackedRace(race, sidelines,
|
||||
|
||||
-3
@@ -43,9 +43,6 @@ public class MarkPositionReceiver extends AbstractReceiverWithQueue<IControl, IP
|
||||
private final IRace tractracRace;
|
||||
final IControlPointPositionListener listener;
|
||||
|
||||
/**
|
||||
* @param tractracRace TODO
|
||||
*/
|
||||
public MarkPositionReceiver(final DynamicTrackedRegatta trackedRegatta, IEvent tractracEvent,
|
||||
IRace tractracRace, Simulator simulator, final DomainFactory domainFactory, IEventSubscriber eventSubscriber, IRaceSubscriber raceSubscriber) {
|
||||
super(domainFactory, tractracEvent, trackedRegatta, simulator, eventSubscriber, raceSubscriber);
|
||||
|
||||
+1
-7
@@ -220,13 +220,7 @@ public class RaceCourseReceiver extends AbstractReceiverWithQueue<IControlRoute,
|
||||
windStore, EmptyGPSFixStore.INSTANCE, delayToLiveInMillis, millisecondsOverWhichToAverageWind,
|
||||
/* time over which to average speed: */ race.getBoatClass().getApproximateManeuverDurationInMilliseconds(),
|
||||
raceDefinitionSetToUpdate);
|
||||
TracTracCourseDesignUpdateHandler courseDesignHandler = new TracTracCourseDesignUpdateHandler(tracTracUpdateURI,
|
||||
tracTracUsername, tracTracPassword,
|
||||
getTracTracEvent().getId(), race.getId());
|
||||
trackedRace.addCourseDesignChangedListener(courseDesignHandler);
|
||||
TracTracStartTimeUpdateHandler startTimeHandler = new TracTracStartTimeUpdateHandler(tracTracUpdateURI,
|
||||
tracTracUsername, tracTracPassword, getTracTracEvent().getId(), race.getId());
|
||||
trackedRace.addStartTimeChangedListener(startTimeHandler);
|
||||
getDomainFactory().addTracTracUpdateHandlers(tracTracUpdateURI, getTracTracEvent().getId(), tracTracUsername, tracTracPassword, race, trackedRace);
|
||||
if (!Activator.getInstance().isUseTracTracMarkPassings()) {
|
||||
new MarkPassingCalculator(trackedRace, true);
|
||||
}
|
||||
|
||||
+7
-11
@@ -1,12 +1,9 @@
|
||||
package com.sap.sailing.domain.tractracadapter.impl;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.tracking.DynamicTrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.RaceTracker;
|
||||
import com.sap.sailing.domain.tracking.RaceHandle;
|
||||
import com.sap.sailing.domain.tracking.RaceTracker;
|
||||
import com.sap.sailing.domain.tractracadapter.DomainFactory;
|
||||
import com.tractrac.model.lib.api.event.IRace;
|
||||
|
||||
@@ -29,9 +26,8 @@ public class RaceHandleImpl implements RaceHandle {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RaceDefinition> getRaces() {
|
||||
Set<RaceDefinition> result = Collections.singleton(domainFactory.getAndWaitForRaceDefinition(tractracRace.getId()));
|
||||
return result;
|
||||
public RaceDefinition getRace() {
|
||||
return domainFactory.getAndWaitForRaceDefinition(tractracRace.getId());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -45,13 +41,13 @@ public class RaceHandleImpl implements RaceHandle {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<RaceDefinition> getRaces(long timeoutInMilliseconds) {
|
||||
final Set<RaceDefinition> result;
|
||||
public RaceDefinition getRace(long timeoutInMilliseconds) {
|
||||
final RaceDefinition result;
|
||||
final RaceDefinition raceDefinition = domainFactory.getAndWaitForRaceDefinition(tractracRace.getId(), timeoutInMilliseconds);
|
||||
if (raceDefinition != null) { // may have time-outed
|
||||
result = Collections.singleton(raceDefinition);
|
||||
result = raceDefinition;
|
||||
} else {
|
||||
result = Collections.emptySet();
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
package com.sap.sailing.domain.tractracadapter.impl;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.json.simple.parser.ParseException;
|
||||
|
||||
import com.sap.sailing.domain.common.racelog.Flags;
|
||||
import com.sap.sailing.domain.tracking.RaceAbortedListener;
|
||||
|
||||
public class TracTracRaceAbortedHandler extends UpdateHandler implements RaceAbortedListener {
|
||||
|
||||
private final static String ACTION = "update_race_status";
|
||||
|
||||
private final static Logger logger = Logger.getLogger(TracTracRaceAbortedHandler.class.getName());
|
||||
|
||||
public TracTracRaceAbortedHandler(URI updateURI, String tracTracUsername, String tracTracPassword, Serializable tracTracEventId, Serializable raceId) {
|
||||
super(updateURI, ACTION, tracTracUsername, tracTracPassword, tracTracEventId, raceId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void raceAborted(Flags flag) throws MalformedURLException, IOException {
|
||||
if (!isActive()) {
|
||||
logger.info("Not sending race abort notification to TracTrac because no URL has been provided.");
|
||||
return;
|
||||
}
|
||||
final String raceStatus;
|
||||
if (flag == Flags.AP) {
|
||||
raceStatus = "POSTPONED";
|
||||
} else {
|
||||
raceStatus = "ABORTED";
|
||||
}
|
||||
Map<String, String> additionalArgs = new HashMap<>();
|
||||
additionalArgs.put("race_status", raceStatus);
|
||||
URL raceAbortedURL = buildUpdateURL(additionalArgs);
|
||||
logger.info("Using " + raceAbortedURL.toString() + " for the race aborted notification!");
|
||||
HttpURLConnection connection = (HttpURLConnection) raceAbortedURL.openConnection();
|
||||
try {
|
||||
setConnectionProperties(connection);
|
||||
try {
|
||||
checkAndLogUpdateResponse(connection);
|
||||
} catch (ParseException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} finally {
|
||||
if (connection != null) {
|
||||
connection.disconnect();
|
||||
} else {
|
||||
logger.severe("Connection to TracTrac race aborted URL " + raceAbortedURL.toString() + " could not be established");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -14,6 +14,7 @@ import java.net.URI;
|
||||
import java.net.URL;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -62,7 +63,7 @@ public class UpdateHandler {
|
||||
}
|
||||
}
|
||||
|
||||
protected URL buildUpdateURL(HashMap<String, String> additionalParameters) throws MalformedURLException, UnsupportedEncodingException {
|
||||
protected URL buildUpdateURL(Map<String, String> additionalParameters) throws MalformedURLException, UnsupportedEncodingException {
|
||||
String serverUpdateURI = this.updateURI.toString();
|
||||
// make sure that the update URI always ends with a slash
|
||||
if (!serverUpdateURI.endsWith("/")) {
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.sap.sailing.domain.base;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.net.URL;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
|
||||
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
|
||||
|
||||
@@ -46,5 +49,16 @@ public interface Event extends EventBase {
|
||||
* leaderboard groups in <code>leaderboardGroups</code>.
|
||||
*/
|
||||
void setLeaderboardGroups(Iterable<LeaderboardGroup> leaderboardGroups);
|
||||
|
||||
/**
|
||||
* For the images references by the image URLs in {@link #getImageURLs()}, {@link #getSponsorImageURLs()} and {@link #getLogoImageURL()}
|
||||
* determines the image dimensions. The retrieval of the image size is started when the URL is added to this event, and the result is
|
||||
* cached. Should the image change its size afterwards or change its availability status, this event won't notice unless the image is
|
||||
* removed from this event and added again.<p>
|
||||
*
|
||||
* Note that exceptions may result should the image be unavailable. If the image URL leads to a document that is not an image,
|
||||
* <code>null</code> will result.
|
||||
*/
|
||||
Dimension getImageSize(URL imageURL) throws InterruptedException, ExecutionException;
|
||||
|
||||
}
|
||||
@@ -100,12 +100,6 @@ public interface Regatta extends Named, WithID {
|
||||
|
||||
RegattaIdentifier getRegattaIdentifier();
|
||||
|
||||
/**
|
||||
* A regatta name may be composed, e.g., from an overall regatta name and the boat class name. A factory or constructor
|
||||
* may require the base name to which the boat class name will be appended. This method emits the base name.
|
||||
*/
|
||||
String getBaseName();
|
||||
|
||||
/**
|
||||
* Regattas may be constructed as implicit default regattas in which case they won't need to be stored
|
||||
* durably and don't contain valuable information worth being preserved; or they are constructed explicitly
|
||||
@@ -128,7 +122,30 @@ public interface Regatta extends Named, WithID {
|
||||
RegattaAndRaceIdentifier getRaceIdentifier(RaceDefinition race);
|
||||
|
||||
/**
|
||||
* @return the associated event. Can be null.
|
||||
* When there is no race committee app in place and no operator is managing the race start times for this regatta,
|
||||
* start times can optionally be inferred from the start mark passings by the {@link TrackedRace}s in this regatta.
|
||||
* The default for this is <code>true</code>, particularly because the race committee app has not been used for all
|
||||
* races, and start times need to be inferred for those.
|
||||
* <p>
|
||||
*
|
||||
* This option should only be set to <code>false</code> if the race log is maintained thorougly for this regatta,
|
||||
* either by a well-working race committee app or by an on-shore operator using the administration console to enter
|
||||
* start times for all races of this regatta consistently.
|
||||
* <p>
|
||||
*
|
||||
* Note that the effects of setting this option to <code>false</code> are severe when no start times are maintained.
|
||||
* The races for which then no start time is entered will not be scored at all.
|
||||
* <p>
|
||||
*
|
||||
* A discussion of this subject can also be found at <a
|
||||
* href="http://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=1994"
|
||||
* >http://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=1994</a>.
|
||||
*/
|
||||
Event getEvent();
|
||||
boolean useStartTimeInference();
|
||||
|
||||
/**
|
||||
* Updates the flag deciding whether to use start time inference. See {@link #useStartTimeInference()}.
|
||||
*/
|
||||
void setUseStartTimeInference(boolean useStartTimeInference);
|
||||
|
||||
}
|
||||
@@ -1,12 +1,26 @@
|
||||
package com.sap.sailing.domain.base.impl;
|
||||
|
||||
import java.awt.Dimension;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Future;
|
||||
|
||||
import javax.imageio.ImageIO;
|
||||
import javax.imageio.ImageReader;
|
||||
import javax.imageio.stream.ImageInputStream;
|
||||
|
||||
import com.sap.sailing.domain.base.Event;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
@@ -22,6 +36,9 @@ public class EventImpl extends EventBaseImpl implements Event {
|
||||
|
||||
private ConcurrentLinkedQueue<LeaderboardGroup> leaderboardGroups;
|
||||
|
||||
private transient ConcurrentHashMap<URL, Future<Dimension>> imageSizeFetchers;
|
||||
private transient ExecutorService executor;
|
||||
|
||||
public EventImpl(String name, TimePoint startDate, TimePoint endDate, String venueName, boolean isPublic, UUID id) {
|
||||
this(name, startDate, endDate, new VenueImpl(venueName), isPublic, id);
|
||||
}
|
||||
@@ -33,6 +50,8 @@ public class EventImpl extends EventBaseImpl implements Event {
|
||||
super(name, startDate, endDate, venue, isPublic, id);
|
||||
this.regattas = new HashSet<Regatta>();
|
||||
this.leaderboardGroups = new ConcurrentLinkedQueue<>();
|
||||
this.imageSizeFetchers = new ConcurrentHashMap<>();
|
||||
this.executor = Executors.newCachedThreadPool();
|
||||
}
|
||||
|
||||
private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {
|
||||
@@ -40,6 +59,8 @@ public class EventImpl extends EventBaseImpl implements Event {
|
||||
if (leaderboardGroups == null) {
|
||||
leaderboardGroups = new ConcurrentLinkedQueue<>();
|
||||
}
|
||||
this.imageSizeFetchers = new ConcurrentHashMap<>();
|
||||
this.executor = Executors.newCachedThreadPool();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,4 +102,103 @@ public class EventImpl extends EventBaseImpl implements Event {
|
||||
public boolean removeLeaderboardGroup(LeaderboardGroup leaderboardGroup) {
|
||||
return leaderboardGroups.remove(leaderboardGroup);
|
||||
}
|
||||
|
||||
private Future<Dimension> getOrCreateImageSizeCalculator(final URL imageURL) {
|
||||
Future<Dimension> imageSizeFetcher = imageSizeFetchers.get(imageURL);
|
||||
if (imageSizeFetcher == null) {
|
||||
imageSizeFetcher = executor.submit(new Callable<Dimension>() {
|
||||
@Override
|
||||
public Dimension call() throws IOException {
|
||||
Dimension result = null;
|
||||
ImageInputStream in = null;
|
||||
try {
|
||||
URLConnection conn = imageURL.openConnection();
|
||||
in = ImageIO.createImageInputStream(conn.getInputStream());
|
||||
final Iterator<ImageReader> readers = ImageIO.getImageReaders(in);
|
||||
if (readers.hasNext()) {
|
||||
ImageReader reader = readers.next();
|
||||
try {
|
||||
reader.setInput(in);
|
||||
result = new Dimension(reader.getWidth(0), reader.getHeight(0));
|
||||
} finally {
|
||||
reader.dispose();
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
if (in != null) {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
});
|
||||
imageSizeFetchers.put(imageURL, imageSizeFetcher);
|
||||
}
|
||||
return imageSizeFetcher;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Dimension getImageSize(URL imageURL) throws InterruptedException, ExecutionException {
|
||||
Future<Dimension> imageSizeCalculator = getOrCreateImageSizeCalculator(imageURL);
|
||||
return imageSizeCalculator.get();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addImageURL(URL imageURL) {
|
||||
super.addImageURL(imageURL);
|
||||
refreshImageSizeFetcher(imageURL);
|
||||
}
|
||||
|
||||
private void refreshImageSizeFetcher(URL imageURL) {
|
||||
if (imageURL != null) {
|
||||
removeImageSizeFetcher(imageURL);
|
||||
getOrCreateImageSizeCalculator(imageURL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeImageURL(URL imageURL) {
|
||||
super.removeImageURL(imageURL);
|
||||
removeImageSizeFetcher(imageURL);
|
||||
}
|
||||
|
||||
private void removeImageSizeFetcher(URL imageURL) {
|
||||
if (imageURL != null) {
|
||||
imageSizeFetchers.remove(imageURL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setImageURLs(Iterable<URL> imageURLs) {
|
||||
super.setImageURLs(imageURLs);
|
||||
for (URL imageURL : imageURLs) {
|
||||
refreshImageSizeFetcher(imageURL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addSponsorImageURL(URL sponsorImageURL) {
|
||||
super.addSponsorImageURL(sponsorImageURL);
|
||||
refreshImageSizeFetcher(sponsorImageURL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSponsorImageURL(URL sponsorImageURL) {
|
||||
super.removeSponsorImageURL(sponsorImageURL);
|
||||
removeImageSizeFetcher(sponsorImageURL);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setSponsorImageURLs(Iterable<URL> sponsorImageURLs) {
|
||||
super.setSponsorImageURLs(sponsorImageURLs);
|
||||
for (URL imageURL : sponsorImageURLs) {
|
||||
refreshImageSizeFetcher(imageURL);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setLogoImageURL(URL logoImageURL) {
|
||||
super.setLogoImageURL(logoImageURL);
|
||||
refreshImageSizeFetcher(logoImageURL);
|
||||
}
|
||||
}
|
||||
@@ -13,7 +13,6 @@ import java.util.logging.Logger;
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.CourseArea;
|
||||
import com.sap.sailing.domain.base.Event;
|
||||
import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
import com.sap.sailing.domain.base.RaceColumnInSeries;
|
||||
@@ -84,11 +83,16 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
|
||||
*/
|
||||
private final boolean persistent;
|
||||
|
||||
/**
|
||||
* Defaults to <code>true</code>. See {@link Regatta#useStartTimeInference()}.
|
||||
*/
|
||||
private boolean useStartTimeInference;
|
||||
|
||||
/**
|
||||
* Constructs a regatta with an empty {@link RaceLogStore}.
|
||||
*/
|
||||
public RegattaImpl(String baseName, BoatClass boatClass, Iterable<? extends Series> series, boolean persistent, ScoringScheme scoringScheme, Serializable id, CourseArea courseArea) {
|
||||
this(EmptyRaceLogStore.INSTANCE, baseName, boatClass, series, persistent, scoringScheme, id, courseArea);
|
||||
public RegattaImpl(String name, BoatClass boatClass, Iterable<? extends Series> series, boolean persistent, ScoringScheme scoringScheme, Serializable id, CourseArea courseArea) {
|
||||
this(EmptyRaceLogStore.INSTANCE, name, boatClass, series, persistent, scoringScheme, id, courseArea, /* useStartTimeInference */ true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -101,11 +105,11 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
|
||||
* this column's series {@link Regatta}, respectively. If <code>null</code>, the re-association won't be
|
||||
* carried out.
|
||||
*/
|
||||
public RegattaImpl(RaceLogStore raceLogStore, String baseName, BoatClass boatClass, TrackedRegattaRegistry trackedRegattaRegistry, ScoringScheme scoringScheme, Serializable id, CourseArea courseArea) {
|
||||
this(raceLogStore, baseName, boatClass, Collections.singletonList(new SeriesImpl(LeaderboardNameConstants.DEFAULT_SERIES_NAME,
|
||||
public RegattaImpl(RaceLogStore raceLogStore, String name, BoatClass boatClass, TrackedRegattaRegistry trackedRegattaRegistry, ScoringScheme scoringScheme, Serializable id, CourseArea courseArea) {
|
||||
this(raceLogStore, name, boatClass, Collections.singletonList(new SeriesImpl(LeaderboardNameConstants.DEFAULT_SERIES_NAME,
|
||||
/* isMedal */false, Collections
|
||||
.singletonList(new FleetImpl(LeaderboardNameConstants.DEFAULT_FLEET_NAME)), /* race column names */new ArrayList<String>(),
|
||||
trackedRegattaRegistry)), /* persistent */false, scoringScheme, id, courseArea);
|
||||
trackedRegattaRegistry)), /* persistent */false, scoringScheme, id, courseArea, /* useStartTimeInference */ true);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -113,8 +117,9 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
|
||||
* all {@link Series} in this iterable will have their {@link Series#setRegatta(Regatta) regatta set} to
|
||||
* this new regatta.
|
||||
*/
|
||||
public <S extends Series> RegattaImpl(RaceLogStore raceLogStore, String baseName, BoatClass boatClass, Iterable<S> series, boolean persistent, ScoringScheme scoringScheme, Serializable id, CourseArea courseArea) {
|
||||
super(getDefaultName(baseName, boatClass==null?null:boatClass.getName()));
|
||||
public <S extends Series> RegattaImpl(RaceLogStore raceLogStore, String name, BoatClass boatClass, Iterable<S> series, boolean persistent, ScoringScheme scoringScheme, Serializable id, CourseArea courseArea, boolean useStartTimeInference) {
|
||||
super(name);
|
||||
this.useStartTimeInference = useStartTimeInference;
|
||||
this.id = id;
|
||||
this.raceLogStore = raceLogStore;
|
||||
races = new HashSet<RaceDefinition>();
|
||||
@@ -148,24 +153,13 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
|
||||
new RaceLogOnRegattaIdentifier(this, raceColumn.getName())));
|
||||
}
|
||||
|
||||
public static String getDefaultName(String baseName, String boatClassName) {
|
||||
return baseName+(boatClassName==null?"":" ("+boatClassName+")");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Serializable getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBaseName() {
|
||||
String result;
|
||||
if (boatClass == null) {
|
||||
result = getName();
|
||||
} else {
|
||||
result = getName().substring(0, getName().length()-boatClass.getName().length()-3); // remove tralining boat class name and " (" and ")"
|
||||
}
|
||||
return result;
|
||||
public static String getDefaultName(String baseName, String boatClassName) {
|
||||
return baseName+(boatClassName==null?"":" ("+boatClassName+")");
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -417,6 +411,11 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
|
||||
this.defaultCourseArea = newCourseArea;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUseStartTimeInference(boolean useStartTimeInference) {
|
||||
this.useStartTimeInference = useStartTimeInference;
|
||||
}
|
||||
|
||||
@Override
|
||||
public RegattaConfiguration getRegattaConfiguration() {
|
||||
return configuration;
|
||||
@@ -468,12 +467,6 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
|
||||
registerRaceLogsOnRaceColumns(seriesToAdd);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Event getEvent() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeSeries(Series series) {
|
||||
Series existingSeries = getSeriesByName(series.getName());
|
||||
@@ -499,4 +492,9 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean useStartTimeInference() {
|
||||
return useStartTimeInference;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -280,10 +280,6 @@ public interface Leaderboard extends LeaderboardBase {
|
||||
|
||||
/**
|
||||
* Returns the total rank of the given competitor.
|
||||
* @param competitor
|
||||
* @param timePoint
|
||||
* @return
|
||||
* @throws NoWindException
|
||||
*/
|
||||
int getTotalRankOfCompetitor(Competitor competitor, TimePoint timePoint) throws NoWindException;
|
||||
|
||||
|
||||
+5
@@ -105,6 +105,11 @@ public class LeaderboardCacheManager {
|
||||
removeFromCache(leaderboard);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startOfRaceChanged(TimePoint oldStartOfRace, TimePoint newStartOfRace) {
|
||||
removeFromCache(leaderboard);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) {
|
||||
removeFromCache(leaderboard);
|
||||
|
||||
+10
-3
@@ -347,6 +347,11 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
invalidateCacheAndRemoveThisListenerFromTrackedRace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startOfRaceChanged(TimePoint oldStartOfRace, TimePoint newStartOfRace) {
|
||||
invalidateCacheAndRemoveThisListenerFromTrackedRace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void delayToLiveChanged(long delayToLiveInMillis) {
|
||||
invalidateCacheAndRemoveThisListenerFromTrackedRace();
|
||||
@@ -1287,7 +1292,9 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
} else {
|
||||
result.discardThresholds = null;
|
||||
}
|
||||
// competitor, leading to square effort. We therefore need to compute the leg ranks for those race where leg
|
||||
// Computing the competitor leg ranks is expensive, especially in live mode, in case new events keep
|
||||
// invalidating the ranks cache in TrackedLegImpl. Then problem then is that the sorting based on wind data is repeated for
|
||||
// each competitor, leading to square effort. We therefore need to compute the leg ranks for those race where leg
|
||||
// details are requested only once and pass them into getLeaderboardEntryDTO
|
||||
final Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache = new HashMap<Leg, LinkedHashMap<Competitor, Integer>>();
|
||||
for (final RaceColumn raceColumn : this.getRaceColumns()) {
|
||||
@@ -1300,7 +1307,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
trackedRace.getRace().getCourse().lockForRead();
|
||||
try {
|
||||
for (TrackedLeg trackedLeg : trackedRace.getTrackedLegs()) {
|
||||
legRanksCache.put(trackedLeg.getLeg(), trackedLeg.getRanks(timePoint));
|
||||
legRanksCache.put(trackedLeg.getLeg(), trackedLeg.getRanks(timePoint, cache));
|
||||
}
|
||||
} finally {
|
||||
trackedRace.getRace().getCourse().unlockAfterRead();
|
||||
@@ -1679,7 +1686,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
if (legRanks != null) {
|
||||
result.rank = legRanks.get(trackedLeg.getCompetitor());
|
||||
} else {
|
||||
result.rank = trackedLeg.getRank(timePoint);
|
||||
result.rank = trackedLeg.getRank(timePoint, cache);
|
||||
}
|
||||
result.started = trackedLeg.hasStartedLeg(timePoint);
|
||||
Speed velocityMadeGood;
|
||||
|
||||
+3
@@ -7,6 +7,7 @@ import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.racelog.Flags;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public interface DynamicTrackedRace extends TrackedRace {
|
||||
@@ -96,6 +97,8 @@ public interface DynamicTrackedRace extends TrackedRace {
|
||||
void onCourseDesignChangedByRaceCommittee(CourseBase courseDesign);
|
||||
|
||||
void onStartTimeChangedByRaceCommittee(TimePoint newStartTime);
|
||||
|
||||
void onAbortedByRaceCommittee(Flags flag);
|
||||
|
||||
void invalidateStartTime();
|
||||
|
||||
|
||||
+3
@@ -7,6 +7,9 @@ import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
|
||||
public interface DynamicTrackedRegatta extends TrackedRegatta {
|
||||
|
||||
DynamicTrackedRace getTrackedRace(RaceDefinition race);
|
||||
|
||||
@Override
|
||||
Iterable<DynamicTrackedRace> getTrackedRaces();
|
||||
|
||||
/**
|
||||
* Non-blocking call that returns <code>null</code> if no tracking information currently exists
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
package com.sap.sailing.domain.tracking;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
|
||||
import com.sap.sailing.domain.common.racelog.Flags;
|
||||
|
||||
public interface RaceAbortedListener {
|
||||
void raceAborted(Flags flag) throws MalformedURLException, IOException;
|
||||
}
|
||||
+9
@@ -32,6 +32,15 @@ public interface RaceChangeListener extends CourseListener {
|
||||
void windAveragingChanged(long oldMillisecondsOverWhichToAverage, long newMillisecondsOverWhichToAverage);
|
||||
|
||||
void raceTimesChanged(TimePoint startOfTracking, TimePoint endOfTracking, TimePoint startTimeReceived);
|
||||
|
||||
/**
|
||||
* Fired when the {@link TrackedRace#getStartOfRace() start of race} time point has changed for the tracked race
|
||||
* observed by this listener. There can be several reasons for this to happen, among them the setting of the start
|
||||
* time by the race committee app, or receiving a new start time from the tracking provider, or receiving a new
|
||||
* start mark passing with start time inference enabled, or a switch of the flag determining whether start mark
|
||||
* passing-based start time inference is active or not.
|
||||
*/
|
||||
void startOfRaceChanged(TimePoint oldStartOfRace, TimePoint newStartOfRace);
|
||||
|
||||
void delayToLiveChanged(long delayToLiveInMillis);
|
||||
|
||||
|
||||
@@ -1,14 +1,12 @@
|
||||
package com.sap.sailing.domain.tracking;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import com.sap.sailing.domain.base.DomainFactory;
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
|
||||
/**
|
||||
* Gives clients a way to refer to a specific set of races even though the {@link RaceDefinition} objects may not have
|
||||
* been created yet, but without exposing the tracking provider's internals. The {@link #getRaces()} call will block
|
||||
* been created yet, but without exposing the tracking provider's internals. The {@link #getRace()} call will block
|
||||
* until the {@link RaceDefinition}s have been created.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
@@ -19,10 +17,10 @@ public interface RaceHandle {
|
||||
Regatta getRegatta();
|
||||
|
||||
/**
|
||||
* Fetch the race definitions. If the race definitions represented by this handle haven't been created yet, the call
|
||||
* Fetch the race definitions. If the race definition represented by this handle hasn't been created yet, the call
|
||||
* blocks until such a definition is provided by another call, usually by the {@link RaceCourseReceiver}.
|
||||
*/
|
||||
Set<RaceDefinition> getRaces();
|
||||
RaceDefinition getRace();
|
||||
|
||||
/**
|
||||
* Fetch the race definition. If the race definition represented by this handle hasn't been created yet, the call
|
||||
@@ -32,7 +30,7 @@ public interface RaceHandle {
|
||||
* <code>timeoutInMilliseconds</code> in case no modifications happen on the {@link DomainFactory}'s
|
||||
* set of races during that time.
|
||||
*/
|
||||
Set<RaceDefinition> getRaces(long timeoutInMilliseconds);
|
||||
RaceDefinition getRace(long timeoutInMilliseconds);
|
||||
|
||||
DynamicTrackedRegatta getTrackedRegatta();
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
|
||||
* tracker architectures may not be able to deliver all data for the {@link RaceDefinition} when created or started.
|
||||
* Therefore, {@link #getRaces()} may return <code>null</code> if the race information hasn't been received by the
|
||||
* tracker yet. Through the {@link RaceHandle} returned by {@link #getRacesHandle()} it is also possible to perform a
|
||||
* {@link RaceHandle#getRaces() blocking get} for the races tracked by this tracker.
|
||||
* {@link RaceHandle#getRace() blocking get} for the race tracked by this tracker.
|
||||
* <p>
|
||||
*
|
||||
* The data received by the tracker is usually fed into {@link TrackedRace} objects that {@link TrackedRace#getRace()
|
||||
|
||||
+1
-1
@@ -9,5 +9,5 @@ import com.sap.sailing.domain.common.TimePoint;
|
||||
public interface StartTimeChangedListener {
|
||||
|
||||
void startTimeChanged(TimePoint newTimePoint) throws MalformedURLException, IOException;
|
||||
|
||||
|
||||
}
|
||||
@@ -43,6 +43,13 @@ public interface TrackedLeg extends Serializable {
|
||||
*/
|
||||
LinkedHashMap<Competitor, Integer> getRanks(TimePoint timePoint);
|
||||
|
||||
/**
|
||||
* Same as {@link #getRanks(TimePoint)}, only that a cache for wind and leg type
|
||||
* and bearing data can be passed to avoid redundant calculations during a single
|
||||
* round trip.
|
||||
*/
|
||||
LinkedHashMap<Competitor, Integer> getRanks(TimePoint timePoint, WindLegTypeAndLegBearingCache cache);
|
||||
|
||||
Bearing getLegBearing(TimePoint at);
|
||||
|
||||
/**
|
||||
@@ -77,15 +84,22 @@ public interface TrackedLeg extends Serializable {
|
||||
* returned. The distance returned from this method is always positive. See also {@link #getWindwardDistance}.
|
||||
* @param at
|
||||
* the wind estimation is performed for this point in time
|
||||
* @param windPositionMode TODO
|
||||
*/
|
||||
Distance getAbsoluteWindwardDistance(Position pos1, Position pos2, TimePoint at, WindPositionMode windPositionMode) throws NoWindException;
|
||||
|
||||
|
||||
/**
|
||||
* Same as {@link #getAbsoluteWindwardDistance(Position, Position, TimePoint, WindPositionMode)}, only that a cache
|
||||
* for leg types, wind data and leg bearings can be passed to save the effort of redundant calculations of these
|
||||
* values.
|
||||
*/
|
||||
Distance getAbsoluteWindwardDistance(Position pos1, Position pos2, TimePoint at, WindPositionMode windPositionMode,
|
||||
WindLegTypeAndLegBearingCache cache) throws NoWindException;
|
||||
|
||||
/**
|
||||
* Same as {@link #getAbsoluteWindwardDistance(Position, Position, TimePoint, WindPositionMode)}, but this method considers the leg's
|
||||
* direction and will return a negative distance if <code>pos1</code> is already "ahead" of <code>pos2</code> in the
|
||||
* leg's direction, or a positive distance otherwise.
|
||||
* @param windPositionMode TODO
|
||||
*/
|
||||
Distance getWindwardDistance(Position pos1, Position pos2, TimePoint at, WindPositionMode windPositionMode) throws NoWindException;
|
||||
|
||||
}
|
||||
+26
-2
@@ -43,13 +43,22 @@ public interface TrackedLegOfCompetitor extends Serializable {
|
||||
* Estimates how much the competitor still has to go to the end waypoint of this leg, projected onto the wind
|
||||
* direction. If the competitor already finished this leg, a zero, non-<code>null</code> distance will result.
|
||||
* If the competitor hasn't started the leg yet, the full leg distance is returned.
|
||||
* @param windPositionMode TODO
|
||||
*
|
||||
* @throws NoWindException
|
||||
* thrown in case no wind direction is known
|
||||
*/
|
||||
Distance getWindwardDistanceToGo(TimePoint timePoint, WindPositionMode windPositionMode) throws NoWindException;
|
||||
|
||||
/**
|
||||
* Same as {@link #getWindwardDistanceToGo(TimePoint, WindPositionMode)}, only that a cache for wind and leg
|
||||
* type / bearing can be passed.
|
||||
*
|
||||
* @throws NoWindException
|
||||
* thrown in case no wind direction is known
|
||||
*/
|
||||
Distance getWindwardDistanceToGo(TimePoint timePoint, WindPositionMode legMiddle,
|
||||
WindLegTypeAndLegBearingCache cache) throws NoWindException;
|
||||
|
||||
/**
|
||||
* Computes an approximation for the average velocity made good (windward / leeward speed) of this leg's competitor at
|
||||
* <code>timePoint</code>. If the competitor hasn't started the leg yet, <code>null</code> is returned. If the competitor
|
||||
@@ -119,6 +128,12 @@ public interface TrackedLegOfCompetitor extends Serializable {
|
||||
*/
|
||||
int getRank(TimePoint timePoint);
|
||||
|
||||
/**
|
||||
* Same as {@link #getRank(TimePoint)} with the additional option to provide a cache
|
||||
* that can help avoid redundant calculations of wind and leg data.
|
||||
*/
|
||||
int getRank(TimePoint timePoint, WindLegTypeAndLegBearingCache cache);
|
||||
|
||||
/**
|
||||
* Computes the gap in seconds to the leader / winner of this leg. Returns <code>null</code> in case this leg's
|
||||
* competitor hasn't started the leg yet.
|
||||
@@ -207,6 +222,13 @@ public interface TrackedLegOfCompetitor extends Serializable {
|
||||
*/
|
||||
Distance getWindwardDistanceToOverallLeader(TimePoint timePoint, WindPositionMode windPositionMode) throws NoWindException;
|
||||
|
||||
/**
|
||||
* Same as {@link #getWindwardDistanceToOverallLeader(TimePoint, WindPositionMode)}, only that a cache for leg type
|
||||
* calculation is passed.
|
||||
*/
|
||||
Distance getWindwardDistanceToOverallLeader(TimePoint timePoint, WindPositionMode windPositionMode,
|
||||
WindLegTypeAndLegBearingCache cache) throws NoWindException;
|
||||
|
||||
/**
|
||||
* Computes the average absolute cross track error for this leg. The cross track error for each fix is taken to be a
|
||||
* positive number, thereby ignoring whether the competitor was left or right of the course middle line. If you
|
||||
@@ -236,7 +258,9 @@ public interface TrackedLegOfCompetitor extends Serializable {
|
||||
TrackedLeg getTrackedLeg();
|
||||
|
||||
/**
|
||||
* Computes the angle between the competitors direction and the wind.
|
||||
* Computes the angle between the competitors direction and the wind's "from" direction. The angle's direction is chosen such that
|
||||
* it can be added to the boat's course over ground to arrive at the wind's {@link Wind#getFrom() "from"} direction. Example: wind
|
||||
* from the north (0deg), boat's course over ground 90deg (moving east), then the bearing returned is -90deg.
|
||||
*/
|
||||
Bearing getBeatAngle(TimePoint at) throws NoWindException;
|
||||
|
||||
|
||||
@@ -615,6 +615,8 @@ public interface TrackedRace extends Serializable {
|
||||
|
||||
void addStartTimeChangedListener(StartTimeChangedListener listener);
|
||||
|
||||
void addRaceAbortedListener(RaceAbortedListener listener);
|
||||
|
||||
/**
|
||||
* Tells how far the given <code>competitor</code> was from the start line at the time point of the given seconds before the start.
|
||||
* <p>
|
||||
|
||||
@@ -29,7 +29,7 @@ import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
|
||||
public interface TrackedRegatta extends Serializable {
|
||||
Regatta getRegatta();
|
||||
|
||||
Iterable<TrackedRace> getTrackedRaces();
|
||||
Iterable<? extends TrackedRace> getTrackedRaces();
|
||||
|
||||
Iterable<TrackedRace> getTrackedRaces(BoatClass boatClass);
|
||||
|
||||
@@ -72,5 +72,5 @@ public interface TrackedRegatta extends Serializable {
|
||||
int getNetPoints(Competitor competitor, TimePoint timePoint) throws NoWindException;
|
||||
|
||||
void removeTrackedRace(RaceDefinition raceDefinition);
|
||||
|
||||
|
||||
}
|
||||
+4
@@ -16,6 +16,10 @@ import com.sap.sailing.domain.tracking.Wind;
|
||||
|
||||
public abstract class AbstractRaceChangeListener implements RaceChangeListener {
|
||||
|
||||
@Override
|
||||
public void startOfRaceChanged(TimePoint oldStartOfRace, TimePoint newStartOfRace) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void statusChanged(TrackedRaceStatus newStatus) {
|
||||
}
|
||||
|
||||
+70
-9
@@ -34,6 +34,7 @@ import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.WindSourceType;
|
||||
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.common.impl.WindSourceImpl;
|
||||
import com.sap.sailing.domain.common.racelog.Flags;
|
||||
import com.sap.sailing.domain.racelog.RaceLog;
|
||||
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
|
||||
import com.sap.sailing.domain.tracking.CourseDesignChangedListener;
|
||||
@@ -45,6 +46,7 @@ import com.sap.sailing.domain.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.GPSTrackListener;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.RaceAbortedListener;
|
||||
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
||||
import com.sap.sailing.domain.tracking.StartTimeChangedListener;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
@@ -73,6 +75,7 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
|
||||
private transient Set<CourseDesignChangedListener> courseDesignChangedListeners;
|
||||
private transient Set<StartTimeChangedListener> startTimeChangedListeners;
|
||||
private transient Set<RaceAbortedListener> raceAbortedListeners;
|
||||
|
||||
public DynamicTrackedRaceImpl(TrackedRegatta trackedRegatta, RaceDefinition race, Iterable<Sideline> sidelines,
|
||||
WindStore windStore, GPSFixStore gpsFixStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed,
|
||||
@@ -80,8 +83,9 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
super(trackedRegatta, race, sidelines, windStore, gpsFixStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed,
|
||||
delayForCacheInvalidationOfWindEstimation);
|
||||
this.logListener = new DynamicTrackedRaceLogListener(this);
|
||||
this.courseDesignChangedListeners = new HashSet<CourseDesignChangedListener>();
|
||||
this.startTimeChangedListeners = new HashSet<StartTimeChangedListener>();
|
||||
this.courseDesignChangedListeners = new HashSet<>();
|
||||
this.startTimeChangedListeners = new HashSet<>();
|
||||
this.raceAbortedListeners = new HashSet<>();
|
||||
this.raceIsKnownToStartUpwind = race.getBoatClass().typicallyStartsUpwind();
|
||||
if (!raceIsKnownToStartUpwind) {
|
||||
Set<WindSource> windSourcesToExclude = new HashSet<WindSource>();
|
||||
@@ -107,8 +111,9 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
ois.defaultReadObject();
|
||||
listeners = new HashSet<RaceChangeListener>();
|
||||
logListener = new DynamicTrackedRaceLogListener(this);
|
||||
courseDesignChangedListeners = new HashSet<CourseDesignChangedListener>();
|
||||
startTimeChangedListeners = new HashSet<StartTimeChangedListener>();
|
||||
courseDesignChangedListeners = new HashSet<>();
|
||||
startTimeChangedListeners = new HashSet<>();
|
||||
raceAbortedListeners = new HashSet<>();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -147,7 +152,10 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
|
||||
@Override
|
||||
public void recordFix(Mark mark, GPSFix fix) {
|
||||
getOrCreateTrack(mark).addGPSFix(fix);
|
||||
if ((getStartOfTracking() == null || getStartOfTracking().compareTo(fix.getTimePoint()) <= 0) &&
|
||||
(getEndOfTracking() == null || getEndOfTracking().compareTo(fix.getTimePoint()) >= 0)) {
|
||||
getOrCreateTrack(mark).addGPSFix(fix);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -251,6 +259,16 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
getListeners().add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void invalidateStartTime() {
|
||||
TimePoint oldStartOfRace = getStartOfRace();
|
||||
super.invalidateStartTime();
|
||||
TimePoint newStartOfRace = getStartOfRace();
|
||||
if (!Util.equalsWithNull(oldStartOfRace, newStartOfRace)) {
|
||||
notifyListenersStartOfRaceChanged(oldStartOfRace, newStartOfRace);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addListener(RaceChangeListener listener, final boolean notifyAboutWindFixesAlreadyLoaded,
|
||||
@@ -362,6 +380,21 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyListenersStartOfRaceChanged(TimePoint oldStartOfRace, TimePoint newStartOfRace) {
|
||||
RaceChangeListener[] listeners;
|
||||
synchronized (getListeners()) {
|
||||
listeners = getListeners().toArray(new RaceChangeListener[getListeners().size()]);
|
||||
}
|
||||
for (RaceChangeListener listener : listeners) {
|
||||
try {
|
||||
listener.startOfRaceChanged(oldStartOfRace, newStartOfRace);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "RaceChangeListener " + listener + " threw exception " + e.getMessage());
|
||||
logger.log(Level.SEVERE, "notifyListenersStartOfRaceChanged(TimePoint, TimePoint)", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyListenersWaypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) {
|
||||
RaceChangeListener[] listeners;
|
||||
synchronized (getListeners()) {
|
||||
@@ -548,6 +581,7 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
public void updateMarkPassings(Competitor competitor, Iterable<MarkPassing> markPassings) {
|
||||
Map<Waypoint, MarkPassing> oldMarkPassings = new HashMap<Waypoint, MarkPassing>();
|
||||
MarkPassing oldStartMarkPassing = null;
|
||||
TimePoint oldStartOfRace = getStartOfRace(); // getStartOfRace() may respond with a new result already after updating the mark passings
|
||||
boolean requiresStartTimeUpdate = true;
|
||||
final NavigableSet<MarkPassing> markPassingsForCompetitor = getMarkPassings(competitor);
|
||||
lockForRead(markPassingsForCompetitor);
|
||||
@@ -622,11 +656,17 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
triggerManeuverCacheRecalculation(competitor);
|
||||
// update the race times like start, end and the leg times
|
||||
if (requiresStartTimeUpdate) {
|
||||
TimePoint interimsStartOfRace = getStartOfRace();
|
||||
invalidateStartTime();
|
||||
TimePoint newStartOfRace = getStartOfRace();
|
||||
if (Util.equalsWithNull(interimsStartOfRace, newStartOfRace) && !Util.equalsWithNull(oldStartOfRace, newStartOfRace)) {
|
||||
// invalidateStartTime() will not have thrown a startOfRaceChanged event notification because it already saw the new
|
||||
// start of race time; we have to throw the notification here:
|
||||
notifyListenersStartOfRaceChanged(oldStartOfRace, newStartOfRace);
|
||||
}
|
||||
}
|
||||
invalidateMarkPassingTimes();
|
||||
invalidateEndTime();
|
||||
|
||||
// notify *after* all mark passings have been re-established; should avoid flicker
|
||||
notifyListeners(competitor, oldMarkPassings, markPassings);
|
||||
}
|
||||
@@ -676,10 +716,14 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
|
||||
@Override
|
||||
public void setStartTimeReceived(TimePoint startTimeReceived) {
|
||||
if ((startTimeReceived == null) != (getStartTimeReceived() == null)
|
||||
|| (startTimeReceived != null && !startTimeReceived.equals(getStartTimeReceived()))) {
|
||||
if (!Util.equalsWithNull(startTimeReceived, getStartTimeReceived())) {
|
||||
TimePoint oldStartOfRace = getStartOfRace();
|
||||
super.setStartTimeReceived(startTimeReceived);
|
||||
notifyListenersRaceTimesChanged(getStartOfTracking(), getEndOfTracking(), getStartTimeReceived());
|
||||
TimePoint newStartOfRace = getStartOfRace();
|
||||
if (!Util.equalsWithNull(oldStartOfRace, newStartOfRace)) {
|
||||
notifyListenersStartOfRaceChanged(oldStartOfRace, newStartOfRace);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -851,7 +895,18 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
startTimeChangedListener.startTimeChanged(newStartTime);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
logger.log(Level.INFO, "Exception trying to notify race status change listeners about start time change", e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAbortedByRaceCommittee(Flags flag) {
|
||||
try {
|
||||
for (RaceAbortedListener raceAbortedListener : raceAbortedListeners) {
|
||||
raceAbortedListener.raceAborted(flag);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
logger.log(Level.INFO, "Exception trying to notify race status change listeners about start time change", e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -859,6 +914,12 @@ DynamicTrackedRace, GPSTrackListener<Competitor, GPSFixMoving> {
|
||||
public void addStartTimeChangedListener(StartTimeChangedListener listener) {
|
||||
this.startTimeChangedListeners.add(listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRaceAbortedListener(RaceAbortedListener listener) {
|
||||
this.raceAbortedListeners.add(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The Bearing of a line starting at <code>w</code> that needs to be crossed to pass a mark.
|
||||
*/
|
||||
|
||||
+10
-2
@@ -10,9 +10,11 @@ import com.sap.sailing.domain.common.impl.WindSourceImpl;
|
||||
import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
|
||||
import com.sap.sailing.domain.racelog.RaceLog;
|
||||
import com.sap.sailing.domain.racelog.RaceLogCourseDesignChangedEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogFlagEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogPassChangeEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogStartTimeEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogWindFixEvent;
|
||||
import com.sap.sailing.domain.racelog.analyzing.impl.AbortingFlagFinder;
|
||||
import com.sap.sailing.domain.racelog.analyzing.impl.LastPublishedCourseDesignFinder;
|
||||
import com.sap.sailing.domain.racelog.analyzing.impl.StartTimeFinder;
|
||||
import com.sap.sailing.domain.racelog.analyzing.impl.WindFixesFinder;
|
||||
@@ -34,6 +36,7 @@ public class DynamicTrackedRaceLogListener extends BaseRaceLogEventVisitor {
|
||||
|
||||
private LastPublishedCourseDesignFinder courseDesignFinder;
|
||||
private StartTimeFinder startTimeFinder;
|
||||
private AbortingFlagFinder abortingFlagFinder;
|
||||
|
||||
public DynamicTrackedRaceLogListener(DynamicTrackedRace trackedRace) {
|
||||
this.trackedRace = trackedRace;
|
||||
@@ -49,6 +52,7 @@ public class DynamicTrackedRaceLogListener extends BaseRaceLogEventVisitor {
|
||||
trackedRace.invalidateEndTime();
|
||||
courseDesignFinder = new LastPublishedCourseDesignFinder(raceLog);
|
||||
startTimeFinder = new StartTimeFinder(raceLog);
|
||||
abortingFlagFinder = new AbortingFlagFinder(raceLog);
|
||||
initializeWindTrack(raceLog);
|
||||
analyze();
|
||||
}
|
||||
@@ -129,9 +133,14 @@ public class DynamicTrackedRaceLogListener extends BaseRaceLogEventVisitor {
|
||||
|
||||
@Override
|
||||
public void visit(RaceLogPassChangeEvent event) {
|
||||
trackedRace.invalidateStartTime();
|
||||
trackedRace.invalidateStartTime(); // this will notify RaceStateListeners in case the start time changes by the event
|
||||
/* reset start time */
|
||||
trackedRace.onStartTimeChangedByRaceCommittee(null);
|
||||
RaceLogFlagEvent abortingFlag = abortingFlagFinder.analyze();
|
||||
if (abortingFlag != null) {
|
||||
// previous pass was aborted; notify TracTrac
|
||||
trackedRace.onAbortedByRaceCommittee(abortingFlag.getUpperFlag());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -149,5 +158,4 @@ public class DynamicTrackedRaceLogListener extends BaseRaceLogEventVisitor {
|
||||
// add the wind fix to the race committee WindTrack
|
||||
trackedRace.recordWind(event.getWindFix(), raceCommitteeWindSource);
|
||||
}
|
||||
|
||||
}
|
||||
+6
@@ -21,6 +21,12 @@ public class DynamicTrackedRegattaImpl extends TrackedRegattaImpl implements Dyn
|
||||
return (DynamicTrackedRace) super.getTrackedRace(race);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked") // the tracked races of a dynamic tracked regatta are always DynamicTrackedRace objects; see also getTrackedRace(RaceDefinition)
|
||||
@Override
|
||||
public Iterable<DynamicTrackedRace> getTrackedRaces() {
|
||||
return (Iterable<DynamicTrackedRace>) super.getTrackedRaces();
|
||||
}
|
||||
|
||||
@Override
|
||||
public DynamicTrackedRace getExistingTrackedRace(RaceDefinition race) {
|
||||
return (DynamicTrackedRace) super.getExistingTrackedRace(race);
|
||||
|
||||
+3
-1
@@ -11,6 +11,7 @@ import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
@@ -44,11 +45,12 @@ public class RaceRankComparator implements Comparator<Competitor> {
|
||||
this.timePoint = timePoint;
|
||||
this.markPassingWithTimePoint = new DummyMarkPassingWithTimePointOnly(timePoint);
|
||||
this.windwardDistanceToGoInLegCache = new HashMap<Competitor, Distance>();
|
||||
LeaderboardDTOCalculationReuseCache cache = new LeaderboardDTOCalculationReuseCache(timePoint);
|
||||
for (Competitor competitor : trackedRace.getRace().getCompetitors()) {
|
||||
final TrackedLegOfCompetitor trackedLegOfCompetitor = trackedRace.getTrackedLeg(competitor, timePoint);
|
||||
if (trackedLegOfCompetitor != null) {
|
||||
windwardDistanceToGoInLegCache.put(competitor, trackedLegOfCompetitor.getWindwardDistanceToGo(timePoint,
|
||||
WindPositionMode.LEG_MIDDLE));
|
||||
WindPositionMode.LEG_MIDDLE, cache));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+25
-65
@@ -1,20 +1,15 @@
|
||||
package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.Set;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
import com.sap.sailing.util.impl.LockUtil;
|
||||
import com.sap.sailing.util.impl.NamedReentrantReadWriteLock;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
|
||||
/**
|
||||
@@ -27,15 +22,18 @@ import com.sap.sse.common.Util.Pair;
|
||||
*/
|
||||
public class ShortTimeWindCache {
|
||||
private static final Logger logger = Logger.getLogger(ShortTimeWindCache.class.getName());
|
||||
private final NamedReentrantReadWriteLock lock;
|
||||
private final ConcurrentHashMap<Pair<Position, TimePoint>, Wind> cache;
|
||||
|
||||
/**
|
||||
* The keys of {@link #cache} in the order in which to invalidate them, keyed by the time they were entered into the cache.
|
||||
*/
|
||||
private final LinkedList<Pair<Long, Pair<Position, TimePoint>>> order;
|
||||
private final ConcurrentLinkedDeque<Pair<Long, Pair<Position, TimePoint>>> order;
|
||||
|
||||
/**
|
||||
* Creation and removal / cancellation of the timer is synchronized using {@link #order}.
|
||||
*/
|
||||
private Timer timer;
|
||||
|
||||
private final long preserveHowManyMilliseconds;
|
||||
private final TrackedRaceImpl trackedRace;
|
||||
|
||||
@@ -46,34 +44,15 @@ public class ShortTimeWindCache {
|
||||
@Override
|
||||
public void run() {
|
||||
long oldestToKeep = System.currentTimeMillis() - preserveHowManyMilliseconds;
|
||||
Set<Pair<Position, TimePoint>> remove = new HashSet<>();
|
||||
LockUtil.lockForRead(lock);
|
||||
try {
|
||||
if (!order.isEmpty()) {
|
||||
Iterator<Pair<Long, Pair<Position, TimePoint>>> i = order.iterator();
|
||||
while (i.hasNext()) {
|
||||
Pair<Long, Pair<Position, TimePoint>> next = i.next();
|
||||
if (next.getA() < oldestToKeep) {
|
||||
remove.add(next.getB());
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Pair<Long, Pair<Position, TimePoint>> next;
|
||||
while ((next = order.pollFirst()) != null && next.getA() < oldestToKeep) {
|
||||
cache.remove(next.getB());
|
||||
}
|
||||
synchronized (order) {
|
||||
if (order.isEmpty()) {
|
||||
cancel();
|
||||
timer = null;
|
||||
}
|
||||
} finally {
|
||||
LockUtil.unlockAfterRead(lock);
|
||||
}
|
||||
LockUtil.lockForWrite(lock);
|
||||
try {
|
||||
for (Pair<Position, TimePoint> keyToRemove : remove) {
|
||||
cache.remove(keyToRemove);
|
||||
order.removeFirst();
|
||||
}
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -82,52 +61,33 @@ public class ShortTimeWindCache {
|
||||
this.trackedRace = trackedRace;
|
||||
this.preserveHowManyMilliseconds = preserveHowManyMilliseconds;
|
||||
cache = new ConcurrentHashMap<>();
|
||||
order = new LinkedList<>();
|
||||
lock = new NamedReentrantReadWriteLock(getClass().getSimpleName()+" for "+trackedRace.getRace(), /* fair */ true);
|
||||
order = new ConcurrentLinkedDeque<>();
|
||||
}
|
||||
|
||||
public void add(Position position, TimePoint timePoint, Wind wind) {
|
||||
LockUtil.lockForWrite(lock);
|
||||
try {
|
||||
final Pair<Position, TimePoint> key = new Pair<Position, TimePoint>(position, timePoint);
|
||||
cache.put(key, wind);
|
||||
boolean orderEmpty = order.isEmpty();
|
||||
final Pair<Position, TimePoint> key = new Pair<Position, TimePoint>(position, timePoint);
|
||||
cache.put(key, wind);
|
||||
boolean orderEmpty = order.isEmpty();
|
||||
synchronized (order) {
|
||||
order.add(new Pair<Long, Pair<Position, TimePoint>>(System.currentTimeMillis(), key));
|
||||
if (orderEmpty) {
|
||||
ensureTimerIsRunning();
|
||||
}
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(lock);
|
||||
}
|
||||
}
|
||||
|
||||
public Wind getWind(Position position, TimePoint timePoint) {
|
||||
Wind wind;
|
||||
final Pair<Position, TimePoint> key = new Pair<>(position, timePoint);
|
||||
LockUtil.lockForRead(lock);
|
||||
try {
|
||||
wind = cache.get(key);
|
||||
} finally {
|
||||
LockUtil.unlockAfterRead(lock);
|
||||
}
|
||||
wind = cache.get(key);
|
||||
if (wind == null) {
|
||||
LockUtil.lockForWrite(lock);
|
||||
try {
|
||||
wind = cache.get(key); // try again, now holding the write lock
|
||||
if (wind == null) {
|
||||
misses++;
|
||||
if (misses % 100000l == 0 && logger.isLoggable(Level.FINE)) {
|
||||
logger.fine("hits: "+hits+", misses: "+misses);
|
||||
}
|
||||
wind = trackedRace.getWindUncached(position, timePoint);
|
||||
if (wind != null) {
|
||||
add(position, timePoint, wind);
|
||||
}
|
||||
} else {
|
||||
hits++;
|
||||
}
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(lock);
|
||||
misses++;
|
||||
if (misses % 100000l == 0 && logger.isLoggable(Level.FINE)) {
|
||||
logger.fine("hits: " + hits + ", misses: " + misses);
|
||||
}
|
||||
wind = trackedRace.getWindUncached(position, timePoint);
|
||||
if (wind != null) {
|
||||
add(position, timePoint, wind);
|
||||
}
|
||||
} else {
|
||||
hits++;
|
||||
|
||||
+5
@@ -671,6 +671,11 @@ public class TrackBasedEstimationWindTrackImpl extends VirtualWindTrackImpl impl
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startOfRaceChanged(TimePoint oldStartOfRace, TimePoint newStartOfRace) {
|
||||
// no action required; we're calculating based on each competitor's individual start time, not the start of race
|
||||
}
|
||||
|
||||
@Override
|
||||
public void speedAveragingChanged(long oldMillisecondsOverWhichToAverage, long newMillisecondsOverWhichToAverage) {
|
||||
|
||||
+21
-2
@@ -95,6 +95,10 @@ public class TrackedLegImpl implements TrackedLeg, RaceChangeListener {
|
||||
* consider the order of the boats not currently in this leg, too.
|
||||
*/
|
||||
protected List<TrackedLegOfCompetitor> getCompetitorTracksOrderedByRank(TimePoint timePoint) {
|
||||
return getCompetitorTracksOrderedByRank(timePoint, new NoCachingWindLegTypeAndLegBearingCache());
|
||||
}
|
||||
|
||||
List<TrackedLegOfCompetitor> getCompetitorTracksOrderedByRank(TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
|
||||
List<TrackedLegOfCompetitor> rankedCompetitorList;
|
||||
synchronized (competitorTracksOrderedByRank) {
|
||||
rankedCompetitorList = competitorTracksOrderedByRank.get(timePoint);
|
||||
@@ -111,7 +115,7 @@ public class TrackedLegImpl implements TrackedLeg, RaceChangeListener {
|
||||
// synchronized, usually by read-write locks, so there is no major difference in synchronization issues
|
||||
// an the asynchronous nature of how the data is being received
|
||||
// TODO See bug 469; competitors already disqualified may need to be ranked worst
|
||||
Collections.sort(rankedCompetitorList, new WindwardToGoComparator(this, timePoint));
|
||||
Collections.sort(rankedCompetitorList, new WindwardToGoComparator(this, timePoint, cache));
|
||||
rankedCompetitorList = Collections.unmodifiableList(rankedCompetitorList);
|
||||
synchronized (competitorTracksOrderedByRank) {
|
||||
competitorTracksOrderedByRank.put(timePoint, rankedCompetitorList);
|
||||
@@ -127,7 +131,12 @@ public class TrackedLegImpl implements TrackedLeg, RaceChangeListener {
|
||||
|
||||
@Override
|
||||
public LinkedHashMap<Competitor, Integer> getRanks(TimePoint timePoint) {
|
||||
List<TrackedLegOfCompetitor> orderedTrackedLegsOfCompetitors = getCompetitorTracksOrderedByRank(timePoint);
|
||||
return getRanks(timePoint, new NoCachingWindLegTypeAndLegBearingCache());
|
||||
}
|
||||
|
||||
@Override
|
||||
public LinkedHashMap<Competitor, Integer> getRanks(TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
|
||||
List<TrackedLegOfCompetitor> orderedTrackedLegsOfCompetitors = getCompetitorTracksOrderedByRank(timePoint, cache);
|
||||
LinkedHashMap<Competitor, Integer> result = new LinkedHashMap<Competitor, Integer>();
|
||||
int i=1;
|
||||
for (TrackedLegOfCompetitor tloc : orderedTrackedLegsOfCompetitors) {
|
||||
@@ -298,6 +307,10 @@ public class TrackedLegImpl implements TrackedLeg, RaceChangeListener {
|
||||
public void delayToLiveChanged(long delayToLiveInMillis) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startOfRaceChanged(TimePoint oldStartOfRace, TimePoint newStartOfRace) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void waypointsMayHaveChanges() {
|
||||
clearCaches();
|
||||
@@ -338,6 +351,12 @@ public class TrackedLegImpl implements TrackedLeg, RaceChangeListener {
|
||||
|
||||
@Override
|
||||
public Distance getAbsoluteWindwardDistance(Position pos1, Position pos2, TimePoint at, WindPositionMode windPositionMode) throws NoWindException {
|
||||
return getAbsoluteWindwardDistance(pos1, pos2, at, windPositionMode, new NoCachingWindLegTypeAndLegBearingCache());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAbsoluteWindwardDistance(Position pos1, Position pos2, TimePoint at,
|
||||
WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) throws NoWindException {
|
||||
final Distance preResult = getWindwardDistance(pos1, pos2, at, windPositionMode);
|
||||
final Distance result;
|
||||
if (preResult.getMeters() >= 0) {
|
||||
|
||||
+35
-17
@@ -172,14 +172,16 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
return track.getMaximumSpeedOverGround(legStart.getTimePoint(), to);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Distance getWindwardDistanceToGo(TimePoint timePoint, WindPositionMode windPositionMode) throws NoWindException {
|
||||
public Distance getWindwardDistanceToGo(TimePoint timePoint, WindPositionMode windPositionMode,
|
||||
WindLegTypeAndLegBearingCache cache) throws NoWindException {
|
||||
if (hasFinishedLeg(timePoint)) {
|
||||
return Distance.NULL;
|
||||
} else {
|
||||
Distance result = null;
|
||||
for (Mark mark : getLeg().getTo().getMarks()) {
|
||||
Distance d = getWindwardDistanceTo(mark, timePoint, windPositionMode);
|
||||
Distance d = getWindwardDistanceTo(mark, timePoint, windPositionMode, cache);
|
||||
if (result == null || d != null && d.compareTo(result) < 0) {
|
||||
result = d;
|
||||
}
|
||||
@@ -188,13 +190,23 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getWindwardDistanceToGo(TimePoint timePoint, WindPositionMode windPositionMode) throws NoWindException {
|
||||
return getWindwardDistanceToGo(timePoint, windPositionMode, new NoCachingWindLegTypeAndLegBearingCache());
|
||||
}
|
||||
|
||||
/**
|
||||
* If the current {@link #getLeg() leg} is +/- {@link #UPWIND_DOWNWIND_TOLERANCE_IN_DEG} degrees collinear with the
|
||||
* wind's bearing, the competitor's position is projected onto the line crossing <code>mark</code> in the wind's
|
||||
* bearing, and the distance from the projection to the <code>mark</code> is returned. Otherwise, it is assumed that
|
||||
* the leg is neither an upwind nor a downwind leg, and hence the true distance to <code>mark</code> is returned.
|
||||
* the leg is neither an upwind nor a downwind leg, and hence the true distance to <code>mark</code> is returned. A
|
||||
* cache for wind and leg type / bearing can be passed to avoid their redundant calculation during a single
|
||||
* round-trip.
|
||||
*
|
||||
* @throws NoWindException
|
||||
*/
|
||||
private Distance getWindwardDistanceTo(Mark mark, TimePoint at, WindPositionMode windPositionMode) throws NoWindException {
|
||||
private Distance getWindwardDistanceTo(Mark mark, TimePoint at, WindPositionMode windPositionMode,
|
||||
WindLegTypeAndLegBearingCache cache) throws NoWindException {
|
||||
Position estimatedPosition = getTrackedRace().getTrack(getCompetitor()).getEstimatedPosition(at, false);
|
||||
if (!hasStartedLeg(at) || estimatedPosition == null) {
|
||||
// covers the case with no fixes for this leg yet, also if the mark passing has already been received
|
||||
@@ -208,7 +220,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
if (estimatedMarkPosition == null) {
|
||||
return null;
|
||||
}
|
||||
return getTrackedLeg().getAbsoluteWindwardDistance(estimatedPosition, estimatedMarkPosition, at, windPositionMode);
|
||||
return getTrackedLeg().getAbsoluteWindwardDistance(estimatedPosition, estimatedMarkPosition, at, windPositionMode, cache);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -264,9 +276,14 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
*/
|
||||
@Override
|
||||
public int getRank(TimePoint timePoint) {
|
||||
return getRank(timePoint, new NoCachingWindLegTypeAndLegBearingCache());
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRank(TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
|
||||
int result = 0;
|
||||
if (hasStartedLeg(timePoint)) {
|
||||
List<TrackedLegOfCompetitor> competitorTracksByRank = getTrackedLeg().getCompetitorTracksOrderedByRank(timePoint);
|
||||
List<TrackedLegOfCompetitor> competitorTracksByRank = getTrackedLeg().getCompetitorTracksOrderedByRank(timePoint, cache);
|
||||
result = competitorTracksByRank.indexOf(this)+1;
|
||||
}
|
||||
return result;
|
||||
@@ -363,6 +380,11 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
|
||||
@Override
|
||||
public Distance getWindwardDistanceToOverallLeader(TimePoint timePoint, WindPositionMode windPositionMode) throws NoWindException {
|
||||
return getWindwardDistanceToOverallLeader(timePoint, windPositionMode, new NoCachingWindLegTypeAndLegBearingCache());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getWindwardDistanceToOverallLeader(TimePoint timePoint, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) throws NoWindException {
|
||||
// FIXME bug 607 it seems the following fetches the leader of this leg, not the overall leader; validate!!! Use getTrackedRace().getRanks() instead
|
||||
Competitor leader = getTrackedRace().getOverallLeader(timePoint);
|
||||
TrackedLegOfCompetitor leaderLeg = getTrackedRace().getCurrentLeg(leader, timePoint);
|
||||
@@ -384,14 +406,14 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
// add distance to next mark
|
||||
Position nextMarkPosition = getTrackedRace().getApproximatePosition(leg.getTo(), timePoint);
|
||||
Distance distanceToNextMark = getTrackedRace().getTrackedLeg(leg)
|
||||
.getAbsoluteWindwardDistance(currentPosition, nextMarkPosition, timePoint, windPositionMode);
|
||||
.getAbsoluteWindwardDistance(currentPosition, nextMarkPosition, timePoint, windPositionMode, cache);
|
||||
result = new MeterDistance(result.getMeters() + distanceToNextMark.getMeters());
|
||||
currentPosition = nextMarkPosition;
|
||||
} else {
|
||||
// we're now in the same leg with leader; compute windward distance to leader
|
||||
result = new MeterDistance(result.getMeters()
|
||||
+ getTrackedRace().getTrackedLeg(leg)
|
||||
.getAbsoluteWindwardDistance(currentPosition, leaderPosition, timePoint, windPositionMode)
|
||||
.getAbsoluteWindwardDistance(currentPosition, leaderPosition, timePoint, windPositionMode, cache)
|
||||
.getMeters());
|
||||
break;
|
||||
}
|
||||
@@ -820,18 +842,14 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
public Bearing getBeatAngle(TimePoint at, WindLegTypeAndLegBearingCache cache) throws NoWindException {
|
||||
Bearing beatAngle = null;
|
||||
Bearing projectToBearing;
|
||||
if (cache.getLegType(getTrackedLeg(), at) != LegType.REACHING) {
|
||||
Wind wind = cache.getWind(getTrackedRace(), getCompetitor(), at);
|
||||
if (wind == null) {
|
||||
throw new NoWindException("Need at least wind direction to determine windward speed");
|
||||
}
|
||||
projectToBearing = wind.getBearing();
|
||||
} else {
|
||||
projectToBearing = cache.getLegBearing(getTrackedLeg(), at);
|
||||
Wind wind = cache.getWind(getTrackedRace(), getCompetitor(), at);
|
||||
if (wind == null) {
|
||||
throw new NoWindException("Need at least wind direction to determine windward speed");
|
||||
}
|
||||
projectToBearing = wind.getFrom();
|
||||
SpeedWithBearing speed = getSpeedOverGround(at);
|
||||
if (speed != null) {
|
||||
beatAngle = projectToBearing.getDifferenceTo(speed.getBearing());
|
||||
beatAngle = speed.getBearing().getDifferenceTo(projectToBearing);
|
||||
}
|
||||
return beatAngle;
|
||||
}
|
||||
|
||||
+16
-15
@@ -49,6 +49,7 @@ import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.BearingChangeAnalyzer;
|
||||
import com.sap.sailing.domain.common.CourseChange;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.Duration;
|
||||
import com.sap.sailing.domain.common.LegType;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
@@ -590,9 +591,12 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
// startTimeReceived; if so, return an adjusted, later start time.
|
||||
// If no official start time was received, try to estimate the start time using the mark passings for
|
||||
// the start line.
|
||||
final Waypoint firstWaypoint = getRace().getCourse().getFirstWaypoint();
|
||||
if (firstWaypoint != null) {
|
||||
final Waypoint firstWaypoint;
|
||||
if (getTrackedRegatta().getRegatta().useStartTimeInference() && (firstWaypoint = getRace().getCourse().getFirstWaypoint()) != null) {
|
||||
if (startTimeReceived != null) {
|
||||
// plausibility check for start time received, based on start mark passings; if no boat started within
|
||||
// a grace period of MAX_TIME_BETWEEN_START_AND_FIRST_MARK_PASSING_IN_MILLISECONDS after the start time
|
||||
// received then the startTimeReceived is believed to be wrong
|
||||
TimePoint timeOfFirstMarkPassing = getFirstPassingTime(firstWaypoint);
|
||||
if (timeOfFirstMarkPassing != null) {
|
||||
long startTimeReceived2timeOfFirstMarkPassingFirstMark = timeOfFirstMarkPassing.asMillis()
|
||||
@@ -667,11 +671,14 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
return timeOfFirstMarkPassing;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the largest group of competitors that started within a one-minute time period and returns the time
|
||||
* point of the earliest start mark passing within that group.
|
||||
*/
|
||||
private TimePoint calculateStartOfRaceFromMarkPassings(NavigableSet<MarkPassing> markPassings,
|
||||
Iterable<Competitor> competitors) {
|
||||
TimePoint startOfRace = null;
|
||||
// Find the first mark passing within the largest cluster crossing the line within one minute.
|
||||
final long ONE_MINUTE_IN_MILLIS = 60 * 1000;
|
||||
lockForRead(markPassings);
|
||||
try {
|
||||
if (markPassings != null) {
|
||||
@@ -692,8 +699,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
startOfLargestGroupSoFar = currentMarkPassing;
|
||||
largestStartGroupWithinOneMinuteSize = 1;
|
||||
} else {
|
||||
if (currentMarkPassing.getTimePoint().asMillis()
|
||||
- candidateForStartOfLargestGroupSoFar.getTimePoint().asMillis() <= ONE_MINUTE_IN_MILLIS) {
|
||||
if (candidateForStartOfLargestGroupSoFar.getTimePoint().until(currentMarkPassing.getTimePoint()).compareTo(Duration.ONE_MINUTE) <= 0) {
|
||||
// currentMarkPassing is within one minute of candidateForStartOfLargestGroupSoFar; extend
|
||||
// candidate group...
|
||||
candiateGroupSize++;
|
||||
@@ -704,14 +710,10 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
}
|
||||
} else {
|
||||
// currentMarkPassing is more than a minute after candidateForStartOfLargestGroupSoFar;
|
||||
// advance
|
||||
// candidateForStartOfLargestGroupSoFar and reduce group size counter, until
|
||||
// candidateForStartOfLargestGroupSoFar
|
||||
// is again within the one-minute interval; may catch up all the way to currentMarkPassing
|
||||
// if that was
|
||||
// more than a minute after its predecessor
|
||||
while (currentMarkPassing.getTimePoint().asMillis()
|
||||
- candidateForStartOfLargestGroupSoFar.getTimePoint().asMillis() > ONE_MINUTE_IN_MILLIS) {
|
||||
// advance candidateForStartOfLargestGroupSoFar and reduce group size counter, until
|
||||
// candidateForStartOfLargestGroupSoFar is again within the one-minute interval; may catch
|
||||
// up all the way to currentMarkPassing if that was more than a minute after its predecessor
|
||||
while (candidateForStartOfLargestGroupSoFar.getTimePoint().until(currentMarkPassing.getTimePoint()).compareTo(Duration.ONE_MINUTE) > 0) {
|
||||
candidateForStartOfLargestGroupSoFar = markPassings
|
||||
.higher(candidateForStartOfLargestGroupSoFar);
|
||||
candiateGroupSize--;
|
||||
@@ -1041,8 +1043,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
try {
|
||||
if (rankedCompetitors == null) {
|
||||
rankedCompetitors = competitorRankings.get(timePoint); // try again; maybe a writer released the
|
||||
// write
|
||||
// lock after updating the cache
|
||||
// write lock after updating the cache
|
||||
if (rankedCompetitors == null) {
|
||||
RaceRankComparator comparator = new RaceRankComparator(this, timePoint);
|
||||
rankedCompetitors = new ArrayList<Competitor>();
|
||||
|
||||
+2
-2
@@ -36,7 +36,7 @@ public class TrackedRegattaImpl implements TrackedRegatta {
|
||||
private final Map<RaceDefinition, TrackedRace> trackedRaces;
|
||||
private final Map<BoatClass, Collection<TrackedRace>> trackedRacesByBoatClass;
|
||||
private transient Set<RaceListener> raceListeners;
|
||||
|
||||
|
||||
public TrackedRegattaImpl(Regatta regatta) {
|
||||
super();
|
||||
this.regatta = regatta;
|
||||
@@ -118,7 +118,7 @@ public class TrackedRegattaImpl implements TrackedRegatta {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<TrackedRace> getTrackedRaces() {
|
||||
public Iterable<? extends TrackedRace> getTrackedRaces() {
|
||||
return trackedRaces.values();
|
||||
}
|
||||
|
||||
|
||||
+5
-2
@@ -13,6 +13,7 @@ import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
|
||||
import com.sap.sailing.domain.tracking.WindPositionMode;
|
||||
|
||||
/**
|
||||
@@ -26,11 +27,13 @@ public class WindwardToGoComparator implements Comparator<TrackedLegOfCompetitor
|
||||
private final TrackedLeg trackedLeg;
|
||||
private final TimePoint timePoint;
|
||||
private final Map<TrackedLegOfCompetitor, Distance> wwdtgCache;
|
||||
private final WindLegTypeAndLegBearingCache windAndLegTypeCache;
|
||||
|
||||
public WindwardToGoComparator(TrackedLeg trackedLeg, TimePoint timePoint) {
|
||||
public WindwardToGoComparator(TrackedLeg trackedLeg, TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
|
||||
this.trackedLeg = trackedLeg;
|
||||
this.timePoint = timePoint;
|
||||
wwdtgCache = new HashMap<TrackedLegOfCompetitor, Distance>();
|
||||
windAndLegTypeCache = cache;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -80,7 +83,7 @@ public class WindwardToGoComparator implements Comparator<TrackedLegOfCompetitor
|
||||
private Distance getWindwardDistanceToGo(TrackedLegOfCompetitor o1) throws NoWindException {
|
||||
Distance result = wwdtgCache.get(o1);
|
||||
if (result == null) {
|
||||
result = o1.getWindwardDistanceToGo(timePoint, WindPositionMode.LEG_MIDDLE);
|
||||
result = o1.getWindwardDistanceToGo(timePoint, WindPositionMode.LEG_MIDDLE, windAndLegTypeCache);
|
||||
wwdtgCache.put(o1, result);
|
||||
}
|
||||
return result;
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
<import plugin="org.apache.felix.gogo.command"/>
|
||||
<import plugin="org.apache.felix.gogo.runtime"/>
|
||||
<import plugin="org.apache.felix.gogo.shell"/>
|
||||
<import plugin="org.eclipse.jetty.jmx"/>
|
||||
</requires>
|
||||
|
||||
<plugin
|
||||
|
||||
+1
-1
@@ -76,7 +76,7 @@ public class GwtTestCaseColumnToggling extends GWTTestCase {
|
||||
public void onSuccess(Util.Pair<String, List<TracTracRaceRecordDTO>> result) {
|
||||
System.out.println("Listed races.");
|
||||
for (TracTracRaceRecordDTO rr : result.getB()){
|
||||
if (rr.name.equals(TRACKED_RACE)){
|
||||
if (rr.getName().equals(TRACKED_RACE)){
|
||||
rrDao = rr;
|
||||
}
|
||||
}
|
||||
|
||||
+24
-11
@@ -8,7 +8,6 @@ import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.CourseArea;
|
||||
import com.sap.sailing.domain.base.CourseBase;
|
||||
import com.sap.sailing.domain.base.Event;
|
||||
import com.sap.sailing.domain.base.Leg;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.RaceColumnListener;
|
||||
@@ -32,6 +31,7 @@ import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.WindSourceType;
|
||||
import com.sap.sailing.domain.common.racelog.Flags;
|
||||
import com.sap.sailing.domain.leaderboard.ScoringScheme;
|
||||
import com.sap.sailing.domain.racelog.RaceLog;
|
||||
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
|
||||
@@ -45,6 +45,7 @@ import com.sap.sailing.domain.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.LineDetails;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.RaceAbortedListener;
|
||||
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
||||
import com.sap.sailing.domain.tracking.RaceListener;
|
||||
import com.sap.sailing.domain.tracking.StartTimeChangedListener;
|
||||
@@ -53,8 +54,8 @@ import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.TrackedRaceStatus;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
import com.sap.sailing.domain.tracking.WindPositionMode;
|
||||
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
|
||||
import com.sap.sailing.domain.tracking.WindPositionMode;
|
||||
import com.sap.sailing.domain.tracking.WindStore;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
@@ -369,12 +370,6 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getBaseName() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<? extends Series> getSeries() {
|
||||
// TODO Auto-generated method stub
|
||||
@@ -454,9 +449,9 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Event getEvent() {
|
||||
public boolean useStartTimeInference() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -464,11 +459,17 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setUseStartTimeInference(boolean useStartTimeInference) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<TrackedRace> getTrackedRaces() {
|
||||
public Iterable<DynamicTrackedRace> getTrackedRaces() {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
@@ -994,4 +995,16 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addRaceAbortedListener(RaceAbortedListener listener) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAbortedByRaceCommittee(Flags flag) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+3
-2
@@ -10,6 +10,7 @@ import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.Series;
|
||||
import com.sap.sailing.domain.base.impl.FleetImpl;
|
||||
import com.sap.sailing.domain.base.impl.RegattaImpl;
|
||||
import com.sap.sailing.domain.base.impl.SeriesImpl;
|
||||
import com.sap.sailing.domain.common.dto.PositionDTO;
|
||||
import com.sap.sailing.domain.leaderboard.RegattaLeaderboard;
|
||||
@@ -34,8 +35,8 @@ public class PingMarkViaRaceLogTest {
|
||||
service.getMongoObjectFactory().getDatabase().dropDatabase();
|
||||
Series series = new SeriesImpl("series", false, Collections.singletonList(fleet),
|
||||
Collections.singletonList(columnName), service);
|
||||
Regatta regatta = service.createRegatta("regatta", "Laser", UUID.randomUUID(), Collections.<Series>singletonList(series),
|
||||
false, new HighPoint(), UUID.randomUUID());
|
||||
Regatta regatta = service.createRegatta(RegattaImpl.getDefaultName("regatta", "Laser"), "Laser", UUID.randomUUID(), Collections.<Series>singletonList(series),
|
||||
false, new HighPoint(), UUID.randomUUID(), /* useStartTimeInference */ true);
|
||||
RegattaLeaderboard leaderboard = service.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "RegattaLeaderboard", new int[] {});
|
||||
|
||||
MarkDTO mark = new MarkDTO("mark", "mark");
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
eclipse.preferences.version=1
|
||||
entryPointModules=
|
||||
filesCopiedToWebInfLib=gwt-servlet.jar
|
||||
gwtCompileSettings=PGd3dC1jb21waWxlLXNldHRpbmdzPjxsb2ctbGV2ZWw+SU5GTzwvbG9nLWxldmVsPjxvdXRwdXQtc3R5bGU+T0JGVVNDQVRFRDwvb3V0cHV0LXN0eWxlPjxleHRyYS1hcmdzPjwhW0NEQVRBWy13YXIgLiAtbG9jYWxXb3JrZXJzIDMgLXN0cmljdCAtbG9nTGV2ZWwgVFJBQ0UgLWNvbXBpbGVSZXBvcnQgLVhzb3ljRGV0YWlsZWRdXT48L2V4dHJhLWFyZ3M+PHZtLWFyZ3M+PCFbQ0RBVEFbLVhteDIwMjRtIC1EZ3d0LnVzZWFyY2hpdmVzPWZhbHNlXV0+PC92bS1hcmdzPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmd3dC51aS5BZG1pbkNvbnNvbGU8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuUmFjZUJvYXJkPC9lbnRyeS1wb2ludC1tb2R1bGU+PGVudHJ5LXBvaW50LW1vZHVsZT5jb20uc2FwLnNhaWxpbmcuZ3d0LnVpLkxlYWRlcmJvYXJkPC9lbnRyeS1wb2ludC1tb2R1bGU+PC9nd3QtY29tcGlsZS1zZXR0aW5ncz4\=
|
||||
eclipse.preferences.version=1
|
||||
entryPointModules=
|
||||
filesCopiedToWebInfLib=gwt-servlet.jar
|
||||
gwtCompileSettings=PGd3dC1jb21waWxlLXNldHRpbmdzPjxsb2ctbGV2ZWw+SU5GTzwvbG9nLWxldmVsPjxvdXRwdXQtc3R5bGU+T0JGVVNDQVRFRDwvb3V0cHV0LXN0eWxlPjxleHRyYS1hcmdzPjwhW0NEQVRBWy13YXIgLiAtbG9jYWxXb3JrZXJzIDMgLXN0cmljdCAtbG9nTGV2ZWwgVFJBQ0UgLWNvbXBpbGVSZXBvcnQgLVhzb3ljRGV0YWlsZWRdXT48L2V4dHJhLWFyZ3M+PHZtLWFyZ3M+PCFbQ0RBVEFbLVhteDIwMjRtIC1EZ3d0LnVzZWFyY2hpdmVzPWZhbHNlXV0+PC92bS1hcmdzPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmd3dC51aS5BZG1pbkNvbnNvbGU8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuUmFjZUJvYXJkPC9lbnRyeS1wb2ludC1tb2R1bGU+PGVudHJ5LXBvaW50LW1vZHVsZT5jb20uc2FwLnNhaWxpbmcuZ3d0LnVpLkxlYWRlcmJvYXJkPC9lbnRyeS1wb2ludC1tb2R1bGU+PC9nd3QtY29tcGlsZS1zZXR0aW5ncz4\=
|
||||
@@ -29,6 +29,10 @@
|
||||
margin: 9px 12px 0 0;
|
||||
}
|
||||
|
||||
.viewerToolbar-settingsButton:last-child {
|
||||
margin: 0 0px 0 1px;
|
||||
}
|
||||
|
||||
.viewerToolbar-settingsButton {
|
||||
background: url(images/settings_white.png), url(images/btn-cta-orangeleft.png);
|
||||
background-position: center, center 0;
|
||||
|
||||
+85
@@ -3,8 +3,14 @@ package com.sap.sailing.gwt.ui.adminconsole;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.ui.ListBox;
|
||||
import com.sap.sailing.gwt.ui.client.AbstractRegattaPanel;
|
||||
import com.sap.sailing.gwt.ui.client.ErrorReporter;
|
||||
@@ -12,7 +18,9 @@ import com.sap.sailing.gwt.ui.client.RaceSelectionProvider;
|
||||
import com.sap.sailing.gwt.ui.client.RegattaRefresher;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceRecordDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public abstract class AbstractEventManagementPanel extends AbstractRegattaPanel {
|
||||
protected final TrackedRacesListComposite trackedRacesListComposite;
|
||||
@@ -64,6 +72,9 @@ public abstract class AbstractEventManagementPanel extends AbstractRegattaPanel
|
||||
return availableRegattasListBox;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return <code>null</code> in case of the "Default Regatta" choice
|
||||
*/
|
||||
public RegattaDTO getSelectedRegatta() {
|
||||
RegattaDTO result = null;
|
||||
int selIndex = this.availableRegattasListBox.getSelectedIndex();
|
||||
@@ -80,4 +91,78 @@ public abstract class AbstractEventManagementPanel extends AbstractRegattaPanel
|
||||
return result;
|
||||
}
|
||||
|
||||
protected boolean checkBoatClassOK(RegattaDTO selectedRegatta, Iterable<? extends RaceRecordDTO> selectedRaces) {
|
||||
final boolean result;
|
||||
if (selectedRegatta != null) {
|
||||
List<RaceRecordDTO> racesWithNotMatchingBoatClasses = new ArrayList<>();
|
||||
// show an error and don't load anything if the boat class of any race doesn't match the boat class of the selected regatta;
|
||||
// show the user the names of the offending races
|
||||
for (RaceRecordDTO race : selectedRaces) {
|
||||
if (!checkBoatClassMatch(race.getBoatClassNames(), selectedRegatta)) {
|
||||
racesWithNotMatchingBoatClasses.add(race);
|
||||
}
|
||||
}
|
||||
if (!racesWithNotMatchingBoatClasses.isEmpty()) {
|
||||
StringBuilder builder = new StringBuilder(100 + racesWithNotMatchingBoatClasses.size() * 30);
|
||||
builder.append(stringMessages
|
||||
.boatClassDoesNotMatchSelectedRegatta(selectedRegatta.boatClass == null ? ""
|
||||
: selectedRegatta.boatClass.getName()));
|
||||
builder.append("\n\n");
|
||||
builder.append(stringMessages.races());
|
||||
builder.append("\n");
|
||||
for (RaceRecordDTO record: racesWithNotMatchingBoatClasses) {
|
||||
builder.append(record.getName());
|
||||
builder.append(" (");
|
||||
builder.append(record.getBoatClassNames());
|
||||
builder.append(")");
|
||||
builder.append("\n");
|
||||
}
|
||||
Window.alert(builder.toString());
|
||||
result = false;
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
} else {
|
||||
// "Default Regatta" selected; check if for races for which no previous regatta assignment is known there is
|
||||
// another regatta with the same boat class into which the user may want to load
|
||||
Map<RaceRecordDTO, Set<RegattaDTO>> existingRegattasWithMatchingBoatClassPerRace = new HashMap<>();
|
||||
for (RaceRecordDTO race : selectedRaces) {
|
||||
if (!race.hasRememberedRegatta()) {
|
||||
Set<RegattaDTO> existingRegattasWithMatchingBoatClass = new LinkedHashSet<>();
|
||||
for (RegattaDTO regatta : getAvailableRegattas()) {
|
||||
if (checkBoatClassMatch(race.getBoatClassNames(), regatta)) {
|
||||
existingRegattasWithMatchingBoatClass.add(regatta);
|
||||
}
|
||||
}
|
||||
if (!existingRegattasWithMatchingBoatClass.isEmpty()) {
|
||||
existingRegattasWithMatchingBoatClassPerRace.put(race, existingRegattasWithMatchingBoatClass);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!existingRegattasWithMatchingBoatClassPerRace.isEmpty()) {
|
||||
StringBuilder builder = new StringBuilder(100 + existingRegattasWithMatchingBoatClassPerRace.size() * 30);
|
||||
builder.append(stringMessages.regattaExistForSelectedBoatClass());
|
||||
builder.append("\n\n");
|
||||
builder.append(stringMessages.races());
|
||||
builder.append("\n");
|
||||
for (Entry<RaceRecordDTO, Set<RegattaDTO>> e : existingRegattasWithMatchingBoatClassPerRace.entrySet()) {
|
||||
builder.append(e.getKey().getName());
|
||||
builder.append(" (");
|
||||
builder.append(e.getKey().getBoatClassNames());
|
||||
builder.append("): ");
|
||||
builder.append(e.getValue());
|
||||
builder.append("\n");
|
||||
}
|
||||
result = Window.confirm(builder.toString());
|
||||
} else {
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private boolean checkBoatClassMatch(Iterable<String> boatClassNames, RegattaDTO selectedRegatta) {
|
||||
return Util.contains(boatClassNames, selectedRegatta.boatClass.getName());
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -115,7 +115,7 @@ public class AdminConsoleEntryPoint extends AbstractEntryPoint implements Regatt
|
||||
addToTabPanel(tabPanel, user, regattaManagementPanel, stringMessages.regattas(), AdminConsoleFeatures.MANAGE_REGATTAS);
|
||||
regattasDisplayers.add(regattaManagementPanel);
|
||||
|
||||
TracTracEventManagementPanel tractracEventManagementPanel = new TracTracEventManagementPanel(sailingService,
|
||||
AbstractEventManagementPanel tractracEventManagementPanel = new TracTracEventManagementPanel(sailingService,
|
||||
this, this, stringMessages);
|
||||
tractracEventManagementPanel.ensureDebugId("TracTracEventManagement");
|
||||
addToTabPanel(tabPanel, user, tractracEventManagementPanel, stringMessages.tracTracEvents(), AdminConsoleFeatures.MANAGE_TRACKED_RACES);
|
||||
|
||||
+10
-9
@@ -1,18 +1,18 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
|
||||
import com.google.gwt.i18n.client.DateTimeFormat;
|
||||
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
|
||||
import com.google.gwt.user.datepicker.client.DateBox;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
|
||||
|
||||
public class EventCreateDialog extends EventDialog {
|
||||
|
||||
public EventCreateDialog(Collection<EventDTO> existingEvents, StringMessages stringConstants, DialogCallback<EventDTO> callback) {
|
||||
super(new EventParameterValidator(stringConstants, existingEvents), stringConstants, callback);
|
||||
|
||||
super(new EventParameterValidator(stringConstants, existingEvents), stringConstants,
|
||||
/* leaderboardGroups */ Collections.<LeaderboardGroupDTO>emptyList(), callback);
|
||||
nameEntryField = createTextBox(null);
|
||||
nameEntryField.setVisibleLength(50);
|
||||
descriptionEntryField = createTextArea(null);
|
||||
@@ -21,10 +21,11 @@ public class EventCreateDialog extends EventDialog {
|
||||
descriptionEntryField.getElement().getStyle().setProperty("resize", "none");
|
||||
venueEntryField = createTextBox(null);
|
||||
venueEntryField.setVisibleLength(35);
|
||||
startDateBox = createDateBox(12);
|
||||
startDateBox.setFormat(new DateBox.DefaultFormat(DateTimeFormat.getFormat(PredefinedFormat.DATE_SHORT)));
|
||||
endDateBox = createDateBox(12);
|
||||
endDateBox.setFormat(new DateBox.DefaultFormat(DateTimeFormat.getFormat(PredefinedFormat.DATE_SHORT)));
|
||||
final Date now = new Date();
|
||||
startDateBox = createDateTimeBox(now);
|
||||
startDateBox.setFormat("dd/mm/yyyy hh:ii");
|
||||
endDateBox = createDateTimeBox(now);
|
||||
endDateBox.setFormat("dd/mm/yyyy hh:ii");
|
||||
officialWebsiteURLEntryField = createTextBox(null);
|
||||
officialWebsiteURLEntryField.setVisibleLength(50);
|
||||
logoImageURLEntryField = createTextBox(null);
|
||||
|
||||
+21
-6
@@ -18,22 +18,23 @@ import com.google.gwt.user.client.ui.TextArea;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwt.user.datepicker.client.DateBox;
|
||||
import com.sap.sailing.gwt.ui.client.DataEntryDialogWithBootstrap;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.client.shared.controls.listedit.StringListInlineEditorComposite;
|
||||
import com.sap.sailing.gwt.ui.shared.BetterDateTimeBox;
|
||||
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.VenueDTO;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
|
||||
public abstract class EventDialog extends DataEntryDialog<EventDTO> {
|
||||
public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO> {
|
||||
private final AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
|
||||
protected StringMessages stringMessages;
|
||||
protected TextBox nameEntryField;
|
||||
protected TextArea descriptionEntryField;
|
||||
protected TextBox venueEntryField;
|
||||
protected DateBox startDateBox;
|
||||
protected DateBox endDateBox;
|
||||
protected BetterDateTimeBox startDateBox;
|
||||
protected BetterDateTimeBox endDateBox;
|
||||
protected CheckBox isPublicCheckBox;
|
||||
protected UUID id;
|
||||
protected TextBox officialWebsiteURLEntryField;
|
||||
@@ -42,6 +43,12 @@ public abstract class EventDialog extends DataEntryDialog<EventDTO> {
|
||||
protected StringListInlineEditorComposite imageURLList;
|
||||
protected StringListInlineEditorComposite videoURLList;
|
||||
protected StringListInlineEditorComposite sponsorImageURLList;
|
||||
|
||||
/**
|
||||
* Allows subclasses to "park" a set of associated leaderboard groups for re-attachment to the new {@link EventDTO} produced
|
||||
* by {@link #getResult()}.
|
||||
*/
|
||||
private final Iterable<LeaderboardGroupDTO> leaderboardGroups;
|
||||
|
||||
protected static class EventParameterValidator implements Validator<EventDTO> {
|
||||
|
||||
@@ -105,10 +112,15 @@ public abstract class EventDialog extends DataEntryDialog<EventDTO> {
|
||||
|
||||
}
|
||||
|
||||
public EventDialog(EventParameterValidator validator, StringMessages stringMessages,
|
||||
/**
|
||||
* @param leaderboardGroups even though not editable in this dialog, this parameter gives an editing subclass a chance to "park" the leaderboard group
|
||||
* assignments for re-association with the new {@link EventDTO} created by the {@link #getResult} method.
|
||||
*/
|
||||
public EventDialog(EventParameterValidator validator, StringMessages stringMessages, Iterable<LeaderboardGroupDTO> leaderboardGroups,
|
||||
DialogCallback<EventDTO> callback) {
|
||||
super(stringMessages.event(), null, stringMessages.ok(), stringMessages.cancel(), validator,
|
||||
callback);
|
||||
this.leaderboardGroups = leaderboardGroups;
|
||||
this.stringMessages = stringMessages;
|
||||
final ValueChangeHandler<List<String>> valueChangeHandler = new ValueChangeHandler<List<String>>() {
|
||||
@Override
|
||||
@@ -140,6 +152,9 @@ public abstract class EventDialog extends DataEntryDialog<EventDTO> {
|
||||
@Override
|
||||
protected EventDTO getResult() {
|
||||
EventDTO result = new EventDTO();
|
||||
for (LeaderboardGroupDTO lg : leaderboardGroups) {
|
||||
result.addLeaderboardGroup(lg);
|
||||
}
|
||||
result.setName(nameEntryField.getText());
|
||||
result.setDescription(descriptionEntryField.getText());
|
||||
result.setOfficialWebsiteURL(officialWebsiteURLEntryField.getText().trim().isEmpty() ? null : officialWebsiteURLEntryField.getText().trim());
|
||||
|
||||
+6
-11
@@ -4,19 +4,14 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gwt.i18n.client.DateTimeFormat;
|
||||
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
|
||||
import com.google.gwt.user.datepicker.client.DateBox;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
public class EventEditDialog extends EventDialog {
|
||||
|
||||
public EventEditDialog(EventDTO event, Collection<EventDTO> otherExistingEvents, StringMessages stringConstants, DialogCallback<EventDTO> callback) {
|
||||
super(new EventParameterValidator(stringConstants, otherExistingEvents), stringConstants, callback);
|
||||
|
||||
public EventEditDialog(EventDTO event, Collection<EventDTO> otherExistingEvents, StringMessages stringMessages, DialogCallback<EventDTO> callback) {
|
||||
super(new EventParameterValidator(stringMessages, otherExistingEvents), stringMessages, event.getLeaderboardGroups(), callback);
|
||||
nameEntryField = createTextBox(event.getName());
|
||||
nameEntryField.setVisibleLength(50);
|
||||
descriptionEntryField = createTextArea(event.getDescription());
|
||||
@@ -25,10 +20,10 @@ public class EventEditDialog extends EventDialog {
|
||||
descriptionEntryField.getElement().getStyle().setProperty("resize", "none");
|
||||
venueEntryField = createTextBox(event.venue.getName());
|
||||
venueEntryField.setVisibleLength(35);
|
||||
startDateBox = createDateBox(event.startDate, 12);
|
||||
startDateBox.setFormat(new DateBox.DefaultFormat(DateTimeFormat.getFormat(PredefinedFormat.DATE_SHORT)));
|
||||
endDateBox = createDateBox(event.endDate, 12);
|
||||
endDateBox.setFormat(new DateBox.DefaultFormat(DateTimeFormat.getFormat(PredefinedFormat.DATE_SHORT)));
|
||||
startDateBox = createDateTimeBox(event.startDate);
|
||||
startDateBox.setFormat("dd/mm/yyyy hh:ii");
|
||||
endDateBox = createDateTimeBox(event.endDate);
|
||||
endDateBox.setFormat("dd/mm/yyyy hh:ii");
|
||||
isPublicCheckBox = createCheckbox("");
|
||||
isPublicCheckBox.setValue(event.isPublic);
|
||||
id = event.id;
|
||||
|
||||
+2
-2
@@ -348,9 +348,9 @@ TrackedRaceChangedListener, LeaderboardsDisplayer {
|
||||
unlinkRaceColumnFromTrackedRace(object.getA().getRaceColumnName(), object.getB());
|
||||
} else if (LeaderboardRaceConfigImagesBarCell.ACTION_REFRESH_RACELOG.equals(value)) {
|
||||
refreshRaceLog(object.getA(), object.getB(), true);
|
||||
} else if(LeaderboardRaceConfigImagesBarCell.ACTION_SET_STARTTIME.equals(value)) {
|
||||
} else if (LeaderboardRaceConfigImagesBarCell.ACTION_SET_STARTTIME.equals(value)) {
|
||||
setStartTime(object.getA(), object.getB());
|
||||
} else if(LeaderboardRaceConfigImagesBarCell.ACTION_SHOW_RACELOG.equals(value)) {
|
||||
} else if (LeaderboardRaceConfigImagesBarCell.ACTION_SHOW_RACELOG.equals(value)) {
|
||||
showRaceLog(object.getA(), object.getB());
|
||||
}
|
||||
}
|
||||
|
||||
+12
-6
@@ -50,6 +50,7 @@ public class RegattaDetailsComposite extends Composite {
|
||||
private final Label boatClassName;
|
||||
private final Label scoringSystem;
|
||||
private final Label defaultCourseArea;
|
||||
private final Label useStartTimeInference;
|
||||
private final Label configuration;
|
||||
|
||||
private final SelectionModel<SeriesDTO> seriesSelectionModel;
|
||||
@@ -68,7 +69,7 @@ public class RegattaDetailsComposite extends Composite {
|
||||
mainPanel = new CaptionPanel(stringMessages.regatta());
|
||||
VerticalPanel vPanel = new VerticalPanel();
|
||||
mainPanel.add(vPanel);
|
||||
Grid grid = new Grid(5, 2);
|
||||
Grid grid = new Grid(6, 2);
|
||||
vPanel.add(grid);
|
||||
|
||||
regattaName = new Label();
|
||||
@@ -86,15 +87,20 @@ public class RegattaDetailsComposite extends Composite {
|
||||
grid.setWidget(2 , 0, new Label(stringMessages.courseArea() + ":"));
|
||||
grid.setWidget(2 , 1, defaultCourseArea);
|
||||
|
||||
useStartTimeInference = new Label();
|
||||
useStartTimeInference.ensureDebugId("UseStartTimeInferenceLabel");
|
||||
grid.setWidget(3 , 0, new Label(stringMessages.useStartTimeInference() + ":"));
|
||||
grid.setWidget(3 , 1, useStartTimeInference);
|
||||
|
||||
configuration = new Label();
|
||||
configuration.ensureDebugId("RacingProcedureLabel");
|
||||
grid.setWidget(3, 0, new Label(stringMessages.racingProcedureConfiguration() + ":"));
|
||||
grid.setWidget(3, 1, configuration);
|
||||
grid.setWidget(4, 0, new Label(stringMessages.racingProcedureConfiguration() + ":"));
|
||||
grid.setWidget(4, 1, configuration);
|
||||
|
||||
scoringSystem = new Label();
|
||||
scoringSystem.ensureDebugId("ScoringSystemLabel");
|
||||
grid.setWidget(4 , 0, new Label(stringMessages.scoringSystem() + ":"));
|
||||
grid.setWidget(4 , 1, scoringSystem);
|
||||
grid.setWidget(5 , 0, new Label(stringMessages.scoringSystem() + ":"));
|
||||
grid.setWidget(5 , 1, scoringSystem);
|
||||
|
||||
seriesTable = createRegattaSeriesTable();
|
||||
seriesTable.ensureDebugId("SeriesCellTable");
|
||||
@@ -108,7 +114,6 @@ public class RegattaDetailsComposite extends Composite {
|
||||
seriesListDataProvider = new ListDataProvider<SeriesDTO>();
|
||||
seriesListDataProvider.addDataDisplay(seriesTable);
|
||||
vPanel.add(seriesTable);
|
||||
|
||||
initWidget(mainPanel);
|
||||
}
|
||||
|
||||
@@ -372,6 +377,7 @@ public class RegattaDetailsComposite extends Composite {
|
||||
regattaName.setText(regatta.getName());
|
||||
boatClassName.setText(regatta.boatClass != null ? regatta.boatClass.getName() : "");
|
||||
defaultCourseArea.setText(regatta.defaultCourseAreaUuid == null ? "" : regatta.defaultCourseAreaName);
|
||||
useStartTimeInference.setText(regatta.useStartTimeInference ? stringMessages.yes() : stringMessages.no());
|
||||
if (regatta.configuration != null) {
|
||||
configuration.setText(stringMessages.configured());
|
||||
} else {
|
||||
|
||||
+1
-1
@@ -233,7 +233,7 @@ public class RegattaListComposite extends Composite implements RegattasDisplayer
|
||||
final RegattaIdentifier regattaName = new RegattaName(editedRegatta.getName());
|
||||
|
||||
sailingService.updateRegatta(regattaName, editedRegatta.defaultCourseAreaUuid,
|
||||
editedRegatta.configuration, new MarkedAsyncCallback<Void>(
|
||||
editedRegatta.configuration, editedRegatta.useStartTimeInference, new MarkedAsyncCallback<Void>(
|
||||
new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
|
||||
+1
-1
@@ -172,7 +172,7 @@ public class RegattaStructureManagementPanel extends SimplePanel implements Rega
|
||||
}
|
||||
sailingService.createRegatta(newRegatta.getName(), newRegatta.boatClass==null?null:newRegatta.boatClass.getName(),
|
||||
new RegattaCreationParametersDTO(seriesStructure), true,
|
||||
newRegatta.scoringScheme, newRegatta.defaultCourseAreaUuid, new AsyncCallback<RegattaDTO>() {
|
||||
newRegatta.scoringScheme, newRegatta.defaultCourseAreaUuid, newRegatta.useStartTimeInference, new AsyncCallback<RegattaDTO>() {
|
||||
@Override
|
||||
public void onFailure(Throwable t) {
|
||||
errorReporter.reportError("Error trying to create new regatta " + newRegatta.getName() + ": " + t.getMessage());
|
||||
|
||||
-1
@@ -98,7 +98,6 @@ public class RegattaWithSeriesAndFleetsCreateDialog extends RegattaWithSeriesAnd
|
||||
List<EventDTO> existingEvents, StringMessages stringConstants, DialogCallback<RegattaDTO> callback) {
|
||||
super(new RegattaDTO(), existingEvents, stringConstants.addRegatta(), stringConstants.ok(), stringConstants,
|
||||
new RegattaParameterValidator(stringConstants, existingRegattas), callback);
|
||||
|
||||
this.seriesEditor = new SeriesWithFleetsListEditor(Collections.<SeriesDTO>emptyList(), stringMessages, resources.removeIcon(), /*enableFleetRemoval*/true);
|
||||
}
|
||||
|
||||
|
||||
+11
-11
@@ -5,6 +5,7 @@ import java.util.UUID;
|
||||
|
||||
import com.google.gwt.event.dom.client.ChangeEvent;
|
||||
import com.google.gwt.event.dom.client.ChangeHandler;
|
||||
import com.google.gwt.user.client.ui.CheckBox;
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.ListBox;
|
||||
@@ -30,6 +31,7 @@ public abstract class RegattaWithSeriesAndFleetsDialog extends DataEntryDialog<R
|
||||
protected ListBox scoringSchemeListBox;
|
||||
protected ListBox courseAreaListBox;
|
||||
protected ListBox sailingEventsListBox;
|
||||
protected CheckBox useStartTimeInferenceCheckBox;
|
||||
|
||||
protected List<EventDTO> existingEvents;
|
||||
|
||||
@@ -39,24 +41,19 @@ public abstract class RegattaWithSeriesAndFleetsDialog extends DataEntryDialog<R
|
||||
String okButton, StringMessages stringMessages, Validator<RegattaDTO> validator,
|
||||
DialogCallback<RegattaDTO> callback) {
|
||||
super(title, null, okButton, stringMessages.cancel(), validator, callback);
|
||||
|
||||
this.stringMessages = stringMessages;
|
||||
|
||||
this.regatta = regatta;
|
||||
this.existingEvents = existingEvents;
|
||||
|
||||
nameEntryField = createTextBox(null);
|
||||
nameEntryField.ensureDebugId("NameTextBox");
|
||||
nameEntryField.setVisibleLength(40);
|
||||
nameEntryField.setText(regatta.getName());
|
||||
|
||||
boatClassEntryField = createTextBox(null);
|
||||
boatClassEntryField.ensureDebugId("BoatClassTextBox");
|
||||
boatClassEntryField.setVisibleLength(20);
|
||||
if (regatta.boatClass != null) {
|
||||
boatClassEntryField.setText(regatta.boatClass.getName());
|
||||
}
|
||||
|
||||
scoringSchemeListBox = createListBox(false);
|
||||
scoringSchemeListBox.ensureDebugId("ScoringSchemeListBox");
|
||||
for (ScoringSchemeType scoringSchemeType : ScoringSchemeType.values()) {
|
||||
@@ -66,14 +63,14 @@ public abstract class RegattaWithSeriesAndFleetsDialog extends DataEntryDialog<R
|
||||
scoringSchemeListBox.setSelectedIndex(scoringSchemeListBox.getItemCount() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
sailingEventsListBox = createListBox(false);
|
||||
sailingEventsListBox.ensureDebugId("EventListBox");
|
||||
|
||||
useStartTimeInferenceCheckBox = createCheckbox(stringMessages.useStartTimeInference());
|
||||
useStartTimeInferenceCheckBox.ensureDebugId("UseStartTimeInferenceCheckBox");
|
||||
useStartTimeInferenceCheckBox.setValue(regatta.useStartTimeInference);
|
||||
courseAreaListBox = createListBox(false);
|
||||
courseAreaListBox.ensureDebugId("CourseAreaListBox");
|
||||
courseAreaListBox.setEnabled(false);
|
||||
|
||||
setupEventAndCourseAreaListBoxes(stringMessages);
|
||||
}
|
||||
|
||||
@@ -84,7 +81,7 @@ public abstract class RegattaWithSeriesAndFleetsDialog extends DataEntryDialog<R
|
||||
if (additionalWidget != null) {
|
||||
panel.add(additionalWidget);
|
||||
}
|
||||
Grid formGrid = new Grid(5, 2);
|
||||
Grid formGrid = new Grid(6, 2);
|
||||
panel.add(formGrid);
|
||||
formGrid.setWidget(0, 0, new Label(stringMessages.name() + ":"));
|
||||
formGrid.setWidget(0, 1, nameEntryField);
|
||||
@@ -96,6 +93,8 @@ public abstract class RegattaWithSeriesAndFleetsDialog extends DataEntryDialog<R
|
||||
formGrid.setWidget(3, 1, sailingEventsListBox);
|
||||
formGrid.setWidget(4, 0, new Label(stringMessages.courseArea() + ":"));
|
||||
formGrid.setWidget(4, 1, courseAreaListBox);
|
||||
formGrid.setWidget(5, 0, new Label(stringMessages.useStartTimeInference() + ":"));
|
||||
formGrid.setWidget(5, 1, useStartTimeInferenceCheckBox);
|
||||
setupAdditionalWidgetsOnPanel(panel);
|
||||
return panel;
|
||||
}
|
||||
@@ -105,6 +104,7 @@ public abstract class RegattaWithSeriesAndFleetsDialog extends DataEntryDialog<R
|
||||
regatta.setName(nameEntryField.getText());
|
||||
regatta.boatClass = new BoatClassDTO(boatClassEntryField.getText(), 0.0);
|
||||
regatta.scoringScheme = getSelectedScoringSchemeType();
|
||||
regatta.useStartTimeInference = useStartTimeInferenceCheckBox.getValue();
|
||||
setCourseAreaInRegatta(regatta);
|
||||
return regatta;
|
||||
}
|
||||
@@ -139,8 +139,8 @@ public abstract class RegattaWithSeriesAndFleetsDialog extends DataEntryDialog<R
|
||||
return false;
|
||||
}
|
||||
|
||||
private void setupEventAndCourseAreaListBoxes(StringMessages stringConstants) {
|
||||
sailingEventsListBox.addItem(stringConstants.selectSailingEvent());
|
||||
private void setupEventAndCourseAreaListBoxes(StringMessages stringMessages) {
|
||||
sailingEventsListBox.addItem(stringMessages.selectSailingEvent());
|
||||
for (EventDTO event : existingEvents) {
|
||||
sailingEventsListBox.addItem(event.getName());
|
||||
if (isCourseAreaInEvent(event, regatta.defaultCourseAreaUuid)) {
|
||||
|
||||
+1
@@ -35,6 +35,7 @@ public class RegattaWithSeriesAndFleetsEditDialog extends RegattaWithSeriesAndFl
|
||||
List<EventDTO> existingEvents, final StringMessages stringMessages, DialogCallback<RegattaDTO> callback) {
|
||||
super(regatta, existingEvents, stringMessages.editRegatta(), stringMessages.ok(), stringMessages,
|
||||
null, callback);
|
||||
ensureDebugId("RegattaWithSeriesAndFleetsEditDialog");
|
||||
currentRegattaConfiguration = regatta.configuration;
|
||||
|
||||
nameEntryField.setEnabled(false);
|
||||
|
||||
+3
-23
@@ -2,14 +2,9 @@ package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.github.gwtbootstrap.datetimepicker.client.ui.base.HasViewMode.ViewMode;
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.event.logical.shared.AttachEvent;
|
||||
import com.google.gwt.event.logical.shared.AttachEvent.Handler;
|
||||
import com.google.gwt.event.logical.shared.ValueChangeEvent;
|
||||
import com.google.gwt.event.logical.shared.ValueChangeHandler;
|
||||
import com.google.gwt.i18n.client.DateTimeFormat;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.CaptionPanel;
|
||||
@@ -25,6 +20,7 @@ import com.google.gwt.user.client.ui.TextBox;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
|
||||
import com.sap.sailing.gwt.ui.client.DataEntryDialogWithBootstrap;
|
||||
import com.sap.sailing.gwt.ui.client.ErrorReporter;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
@@ -32,7 +28,7 @@ import com.sap.sailing.gwt.ui.shared.BetterDateTimeBox;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceLogSetStartTimeAndProcedureDTO;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
|
||||
public class SetStartTimeDialog extends DataEntryDialog<RaceLogSetStartTimeAndProcedureDTO> {
|
||||
public class SetStartTimeDialog extends DataEntryDialogWithBootstrap<RaceLogSetStartTimeAndProcedureDTO> {
|
||||
|
||||
private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class);
|
||||
|
||||
@@ -103,23 +99,7 @@ public class SetStartTimeDialog extends DataEntryDialog<RaceLogSetStartTimeAndPr
|
||||
|
||||
private Widget createInputPanel() {
|
||||
Grid content = new Grid(5, 2);
|
||||
timeBox = new BetterDateTimeBox();
|
||||
timeBox.addValueChangeHandler(new ValueChangeHandler<Date>() {
|
||||
@Override
|
||||
public void onValueChange(ValueChangeEvent<Date> event) {
|
||||
validate();
|
||||
}
|
||||
});
|
||||
timeBox.addAttachHandler(new Handler() {
|
||||
@Override
|
||||
public void onAttachOrDetach(AttachEvent event) {
|
||||
if (event.isAttached()) {
|
||||
addAutoHidePartner(timeBox.getPicker());
|
||||
}
|
||||
}
|
||||
});
|
||||
timeBox.setAutoClose(true);
|
||||
timeBox.setStartView(ViewMode.HOUR);
|
||||
timeBox = createDateTimeBox(new Date());
|
||||
timeBox.setFormat("dd/mm/yyyy hh:ii");
|
||||
content.setWidget(0, 0, createLabel(stringMessages.startTime()));
|
||||
content.setWidget(0, 1, timeBox);
|
||||
|
||||
+1
-1
@@ -4,7 +4,7 @@ import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
public interface SuggestedCourseAreaNames {
|
||||
static final List<String> suggestedCourseAreaNames = Arrays.asList("Alpha", "Bravo", "Charly", "Delta", "Echo",
|
||||
static final List<String> suggestedCourseAreaNames = Arrays.asList("Alpha", "Bravo", "Charlie", "Delta", "Echo",
|
||||
"Foxtrott", "Golf", "Hotel", "India", "Juliett", "Stadium", "Offshore");
|
||||
|
||||
}
|
||||
+5
-55
@@ -17,7 +17,6 @@ import com.google.gwt.user.cellview.client.Column;
|
||||
import com.google.gwt.user.cellview.client.ColumnSortEvent.Handler;
|
||||
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
|
||||
import com.google.gwt.user.cellview.client.TextColumn;
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.CaptionPanel;
|
||||
@@ -131,7 +130,7 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane
|
||||
TextColumn<SwissTimingRaceRecordDTO> raceNameColumn = new TextColumn<SwissTimingRaceRecordDTO>() {
|
||||
@Override
|
||||
public String getValue(SwissTimingRaceRecordDTO object) {
|
||||
return object.raceName;
|
||||
return object.getName();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -272,7 +271,7 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane
|
||||
List<String> strings = new ArrayList<>();
|
||||
strings.add(t.regattaName);
|
||||
strings.add(t.seriesName);
|
||||
strings.add(t.raceName);
|
||||
strings.add(t.getName());
|
||||
strings.add(t.raceStatus);
|
||||
strings.add(t.boatClass);
|
||||
strings.add(t.gender);
|
||||
@@ -319,7 +318,7 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane
|
||||
result.setComparator(nameColumn, new Comparator<SwissTimingRaceRecordDTO>() {
|
||||
@Override
|
||||
public int compare(SwissTimingRaceRecordDTO o1, SwissTimingRaceRecordDTO o2) {
|
||||
return new NaturalComparator().compare(o1.raceName, o2.raceName);
|
||||
return new NaturalComparator().compare(o1.getName(), o2.getName());
|
||||
}
|
||||
});
|
||||
result.setComparator(trackingStartColumn, new Comparator<SwissTimingRaceRecordDTO>() {
|
||||
@@ -446,34 +445,8 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane
|
||||
}
|
||||
|
||||
// Check if the assigned regatta makes sense
|
||||
List<SwissTimingRaceRecordDTO> racesWithNotMatchingBoatClasses = new ArrayList<SwissTimingRaceRecordDTO>();
|
||||
for (SwissTimingRaceRecordDTO race : selectedRaces) {
|
||||
if (!checkBoatClassMatch(race, selectedRegatta)) {
|
||||
racesWithNotMatchingBoatClasses.add(race);
|
||||
}
|
||||
}
|
||||
if (racesWithNotMatchingBoatClasses.size() > 0) {
|
||||
StringBuilder builder = new StringBuilder(100 + racesWithNotMatchingBoatClasses.size() * 30);
|
||||
builder.append("WARNING\n");
|
||||
if (selectedRegatta != null) {
|
||||
builder.append(stringMessages.boatClassDoesNotMatchSelectedRegatta(
|
||||
selectedRegatta.boatClass==null?"":selectedRegatta.boatClass.getName()));
|
||||
} else {
|
||||
builder.append(stringMessages.regattaExistForSelectedBoatClass());
|
||||
}
|
||||
builder.append("\n\n");
|
||||
builder.append(stringMessages.races());
|
||||
builder.append("\n");
|
||||
for (SwissTimingRaceRecordDTO record: racesWithNotMatchingBoatClasses) {
|
||||
builder.append(record.raceName);
|
||||
builder.append("\n");
|
||||
}
|
||||
|
||||
Window.alert(builder.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
sailingService.trackWithSwissTiming(
|
||||
if (checkBoatClassOK(selectedRegatta, selectedRaces)) {
|
||||
sailingService.trackWithSwissTiming(
|
||||
/* regattaToAddTo */ regattaIdentifier,
|
||||
selectedRaces, hostname, port, trackWind, correctWindByDeclination,
|
||||
new AsyncCallback<Void>() {
|
||||
@@ -488,30 +461,7 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane
|
||||
regattaRefresher.fillRegattas();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private boolean checkBoatClassMatch(SwissTimingRaceRecordDTO swissTimingRecord, RegattaDTO selectedRegatta) {
|
||||
String boatClassName = swissTimingRecord.boatClass;
|
||||
if (boatClassName != null) {
|
||||
if (selectedRegatta == null) {
|
||||
if (swissTimingRecord.hasRememberedRegatta) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// in case no regatta has been selected we check if there would be a matching regatta
|
||||
for (RegattaDTO regatta : getAvailableRegattas()) {
|
||||
if ((boatClassName == null && regatta.boatClass == null) ||
|
||||
(regatta.boatClass != null && boatClassName.equals(regatta.boatClass.getName()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!boatClassName.equals(selectedRegatta.boatClass.getName())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void updatePanelFromSelectedStoredConfiguration() {
|
||||
|
||||
+8
-58
@@ -18,7 +18,6 @@ import com.google.gwt.user.cellview.client.Column;
|
||||
import com.google.gwt.user.cellview.client.ColumnSortEvent.Handler;
|
||||
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
|
||||
import com.google.gwt.user.cellview.client.TextColumn;
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.CaptionPanel;
|
||||
@@ -119,7 +118,7 @@ public class SwissTimingReplayConnectorPanel extends AbstractEventManagementPane
|
||||
TextColumn<SwissTimingReplayRaceDTO> raceNameColumn = new TextColumn<SwissTimingReplayRaceDTO>() {
|
||||
@Override
|
||||
public String getValue(SwissTimingReplayRaceDTO object) {
|
||||
return object.name;
|
||||
return object.getName();
|
||||
}
|
||||
};
|
||||
TextColumn<SwissTimingReplayRaceDTO> raceStartTrackingColumn = new TextColumn<SwissTimingReplayRaceDTO>() {
|
||||
@@ -205,7 +204,7 @@ public class SwissTimingReplayConnectorPanel extends AbstractEventManagementPane
|
||||
@Override
|
||||
public List<String> getSearchableStrings(SwissTimingReplayRaceDTO t) {
|
||||
List<String> strings = new ArrayList<String>();
|
||||
strings.addAll(Arrays.asList(t.boat_class, t.flight_number, t.name, t.race_id, t.rsc));
|
||||
strings.addAll(Arrays.asList(t.boat_class, t.flight_number, t.getName(), t.race_id, t.rsc));
|
||||
return strings;
|
||||
}
|
||||
};
|
||||
@@ -258,7 +257,7 @@ public class SwissTimingReplayConnectorPanel extends AbstractEventManagementPane
|
||||
result.setComparator(nameColumn, new Comparator<SwissTimingReplayRaceDTO>() {
|
||||
@Override
|
||||
public int compare(SwissTimingReplayRaceDTO o1, SwissTimingReplayRaceDTO o2) {
|
||||
return new NaturalComparator().compare(o1.name, o2.name);
|
||||
return new NaturalComparator().compare(o1.getName(), o2.getName());
|
||||
}
|
||||
});
|
||||
result.setComparator(boatClassColumn, new Comparator<SwissTimingReplayRaceDTO>() {
|
||||
@@ -338,30 +337,6 @@ public class SwissTimingReplayConnectorPanel extends AbstractEventManagementPane
|
||||
});
|
||||
}
|
||||
|
||||
private boolean checkBoatClassMatch(SwissTimingReplayRaceDTO swissTimingRaceRecord, RegattaDTO selectedRegatta) {
|
||||
String boatClassName = swissTimingRaceRecord.boat_class;
|
||||
if (boatClassName != null) {
|
||||
if (selectedRegatta == null) {
|
||||
if (swissTimingRaceRecord.hasRememberedRegatta) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// in case no regatta has been selected we check if there would be a matching regatta
|
||||
for (RegattaDTO regatta : getAvailableRegattas()) {
|
||||
if ((boatClassName == null && regatta.boatClass == null) ||
|
||||
(regatta.boatClass != null && boatClassName.equals(regatta.boatClass.getName()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!boatClassName.equals(selectedRegatta.boatClass.getName())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void trackSelectedRaces(boolean trackWind, boolean correctWindByDeclination, final boolean simulateWithStartTimeNow) {
|
||||
RegattaDTO selectedRegatta = getSelectedRegatta();
|
||||
RegattaIdentifier regattaIdentifier = null;
|
||||
@@ -370,40 +345,14 @@ public class SwissTimingReplayConnectorPanel extends AbstractEventManagementPane
|
||||
}
|
||||
|
||||
// Check if the assigned regatta makes sense
|
||||
List<SwissTimingReplayRaceDTO> racesWithNotMatchingBoatClasses = new ArrayList<SwissTimingReplayRaceDTO>();
|
||||
final List<SwissTimingReplayRaceDTO> selectedRaces = new ArrayList<>();
|
||||
for (SwissTimingReplayRaceDTO replayRace : raceList.getList()) {
|
||||
if (raceTable.getSelectionModel().isSelected(replayRace)) {
|
||||
if (!checkBoatClassMatch(replayRace, selectedRegatta)) {
|
||||
racesWithNotMatchingBoatClasses.add(replayRace);
|
||||
}
|
||||
selectedRaces.add(replayRace);
|
||||
}
|
||||
}
|
||||
|
||||
if (racesWithNotMatchingBoatClasses.size() > 0) {
|
||||
String warningText = "";
|
||||
if (selectedRegatta != null) {
|
||||
warningText += stringMessages.boatClassDoesNotMatchSelectedRegatta(
|
||||
selectedRegatta.boatClass==null?"":selectedRegatta.boatClass.getName());
|
||||
} else {
|
||||
warningText += stringMessages.regattaExistForSelectedBoatClass();
|
||||
}
|
||||
warningText += "\n\n";
|
||||
warningText += stringMessages.races() + "\n";
|
||||
for (SwissTimingReplayRaceDTO record : racesWithNotMatchingBoatClasses) {
|
||||
warningText += record.name + "\n";
|
||||
}
|
||||
|
||||
Window.alert(warningText);
|
||||
return;
|
||||
}
|
||||
|
||||
final List<SwissTimingReplayRaceDTO> selectedRaces = new ArrayList<SwissTimingReplayRaceDTO>();
|
||||
for (final SwissTimingReplayRaceDTO race : this.raceList.getList()) {
|
||||
if (raceTable.getSelectionModel().isSelected(race)) {
|
||||
selectedRaces.add(race);
|
||||
}
|
||||
}
|
||||
sailingService.replaySwissTimingRace(regattaIdentifier, selectedRaces, trackWind, correctWindByDeclination,
|
||||
if (checkBoatClassOK(selectedRegatta, selectedRaces)) {
|
||||
sailingService.replaySwissTimingRace(regattaIdentifier, selectedRaces, trackWind, correctWindByDeclination,
|
||||
simulateWithStartTimeNow, new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
@@ -416,6 +365,7 @@ public class SwissTimingReplayConnectorPanel extends AbstractEventManagementPane
|
||||
regattaRefresher.fillRegattas();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void updateJsonUrlFromSelectedPreviousConfiguration() {
|
||||
|
||||
+29
-76
@@ -3,8 +3,10 @@ package com.sap.sailing.gwt.ui.adminconsole;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.event.dom.client.ChangeEvent;
|
||||
@@ -15,7 +17,6 @@ import com.google.gwt.user.cellview.client.CellTable;
|
||||
import com.google.gwt.user.cellview.client.Column;
|
||||
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
|
||||
import com.google.gwt.user.cellview.client.TextColumn;
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.CaptionPanel;
|
||||
@@ -44,7 +45,6 @@ import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
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.Util;
|
||||
import com.sap.sse.gwt.client.async.MarkedAsyncCallback;
|
||||
import com.sap.sse.gwt.client.panels.LabeledAbstractFilterablePanel;
|
||||
|
||||
@@ -294,7 +294,7 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel {
|
||||
@Override
|
||||
public List<String> getSearchableStrings(TracTracRaceRecordDTO t) {
|
||||
List<String> strings = new ArrayList<String>();
|
||||
strings.add(t.name);
|
||||
strings.add(t.getName());
|
||||
strings.add(t.regattaName);
|
||||
if (t.boatClassNames != null) {
|
||||
for (String boatClassName : t.boatClassNames) {
|
||||
@@ -326,7 +326,7 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel {
|
||||
TextColumn<TracTracRaceRecordDTO> raceNameColumn = new TextColumn<TracTracRaceRecordDTO>() {
|
||||
@Override
|
||||
public String getValue(TracTracRaceRecordDTO object) {
|
||||
return object.name;
|
||||
return object.getName();
|
||||
}
|
||||
};
|
||||
raceNameColumn.setSortable(true);
|
||||
@@ -399,7 +399,7 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel {
|
||||
result.setComparator(nameColumn, new Comparator<TracTracRaceRecordDTO>() {
|
||||
@Override
|
||||
public int compare(TracTracRaceRecordDTO o1, TracTracRaceRecordDTO o2) {
|
||||
return new NaturalComparator().compare(o1.name, o2.name);
|
||||
return new NaturalComparator().compare(o1.getName(), o2.getName());
|
||||
}
|
||||
});
|
||||
result.setComparator(boatClassColumn, new Comparator<TracTracRaceRecordDTO>() {
|
||||
@@ -520,95 +520,48 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel {
|
||||
}));
|
||||
}
|
||||
|
||||
private boolean checkBoatClassMatch(TracTracRaceRecordDTO tracTracRecord, RegattaDTO selectedRegatta) {
|
||||
Iterable<String> boatClassNames = tracTracRecord.boatClassNames;
|
||||
if (boatClassNames != null && Util.size(boatClassNames) > 0) {
|
||||
String tracTracBoatClassName = boatClassNames.iterator().next();
|
||||
if (selectedRegatta == null) {
|
||||
if (tracTracRecord.hasRememberedRegatta) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// in case no regatta has been selected we check if there would be a matching regatta
|
||||
for (RegattaDTO regatta : getAvailableRegattas()) {
|
||||
if ((tracTracBoatClassName == null && regatta.boatClass == null) ||
|
||||
(regatta.boatClass != null && tracTracBoatClassName.equalsIgnoreCase(regatta.boatClass.getName()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!tracTracBoatClassName.equals(selectedRegatta.boatClass.getName())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private void trackSelectedRaces(boolean trackWind, boolean correctWind, final boolean simulateWithStartTimeNow) {
|
||||
String liveURI = liveURITextBox.getValue();
|
||||
String storedURI = storedURITextBox.getValue();
|
||||
String courseDesignUpdateURI = tracTracUpdateURITextBox.getValue();
|
||||
String tractracUsername = tractracUsernameTextBox.getValue();
|
||||
String tractracPassword = tractracPasswordTextBox.getValue();
|
||||
RegattaDTO selectedRegatta = getSelectedRegatta();
|
||||
RegattaDTO selectedRegatta = getSelectedRegatta(); // null meaning "Default Regatta" selection
|
||||
RegattaIdentifier regattaIdentifier = null;
|
||||
if (selectedRegatta != null) {
|
||||
regattaIdentifier = new RegattaName(selectedRegatta.getName());
|
||||
}
|
||||
// Check if the assigned regatta makes sense
|
||||
// Check if the assigned regatta makes sense; the following cases need to be distinguished:
|
||||
// - non-default regatta explicitly selected: check that boat class matches; disallow loading when there is a mismatch
|
||||
// - "Default Regatta" selected: if race was assigned to a regatta before, use that without further checks;
|
||||
// otherwise, warn user if a "persistent" regatta with the same boat class already exists
|
||||
// because it may be an accidental omission to select that regatta for loading
|
||||
List<TracTracRaceRecordDTO> allRaces = raceList.getList();
|
||||
List<TracTracRaceRecordDTO> racesWithNotMatchingBoatClasses = new ArrayList<TracTracRaceRecordDTO>();
|
||||
SelectionModel<? super TracTracRaceRecordDTO> selectionModel = racesTable.getSelectionModel();
|
||||
|
||||
final Set<TracTracRaceRecordDTO> selectedRaces = new LinkedHashSet<>();
|
||||
for (TracTracRaceRecordDTO race : allRaces) {
|
||||
if (selectionModel.isSelected(race)) {
|
||||
if (!checkBoatClassMatch(race, selectedRegatta))
|
||||
racesWithNotMatchingBoatClasses.add(race);
|
||||
}
|
||||
}
|
||||
if (racesWithNotMatchingBoatClasses.size() > 0) {
|
||||
StringBuilder builder = new StringBuilder(100 + racesWithNotMatchingBoatClasses.size() * 30);
|
||||
if (selectedRegatta != null) {
|
||||
builder.append(stringMessages.boatClassDoesNotMatchSelectedRegatta(
|
||||
selectedRegatta.boatClass==null?"":selectedRegatta.boatClass.getName()));
|
||||
} else {
|
||||
builder.append(stringMessages.regattaExistForSelectedBoatClass());
|
||||
}
|
||||
builder.append("\n\n");
|
||||
builder.append(stringMessages.races());
|
||||
builder.append("\n");
|
||||
for (TracTracRaceRecordDTO record: racesWithNotMatchingBoatClasses) {
|
||||
builder.append(record.name);
|
||||
builder.append("\n");
|
||||
}
|
||||
|
||||
Window.alert(builder.toString());
|
||||
return;
|
||||
}
|
||||
|
||||
final List<TracTracRaceRecordDTO> selectedRaces = new ArrayList<TracTracRaceRecordDTO>();
|
||||
for (final TracTracRaceRecordDTO race : raceList.getList()) {
|
||||
if (selectionModel.isSelected(race)) {
|
||||
selectedRaces.add(race);
|
||||
}
|
||||
}
|
||||
sailingService.trackWithTracTrac(regattaIdentifier, selectedRaces, liveURI, storedURI, courseDesignUpdateURI, trackWind, correctWind,
|
||||
simulateWithStartTimeNow, tractracUsername, tractracPassword, new MarkedAsyncCallback<Void>(
|
||||
new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
reportError("Error trying to register races " + selectedRaces + " for tracking: "
|
||||
+ caught.getMessage() + ". Check live/stored URI syntax.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
TracTracEventManagementPanel.this.regattaRefresher.fillRegattas();
|
||||
}
|
||||
}));
|
||||
}
|
||||
if (checkBoatClassOK(selectedRegatta, selectedRaces)) {
|
||||
sailingService.trackWithTracTrac(regattaIdentifier, selectedRaces, liveURI, storedURI,
|
||||
courseDesignUpdateURI, trackWind, correctWind, simulateWithStartTimeNow, tractracUsername,
|
||||
tractracPassword, new MarkedAsyncCallback<Void>(new AsyncCallback<Void>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
reportError("Error trying to register races " + selectedRaces + " for tracking: "
|
||||
+ caught.getMessage() + ". Check live/stored URI syntax.");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(Void result) {
|
||||
TracTracEventManagementPanel.this.regattaRefresher.fillRegattas();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
private void updatePanelFromSelectedStoredConfiguration() {
|
||||
int index = connectionsHistoryListBox.getSelectedIndex();
|
||||
|
||||
|
||||
+5
-3
@@ -96,19 +96,21 @@ public class TrackedRacesManagementPanel extends AbstractRaceManagementPanel {
|
||||
} else {
|
||||
raceDataGrid.setText(2, 1, "");
|
||||
}
|
||||
if(selectedRaceDTO.trackedRace.startOfTracking != null) {
|
||||
if (selectedRaceDTO.trackedRace != null && selectedRaceDTO.trackedRace.startOfTracking != null) {
|
||||
raceDataGrid.setText(3, 1, dateFormatter.render(selectedRaceDTO.trackedRace.startOfTracking) + " "
|
||||
+ timeFormatter.render(selectedRaceDTO.trackedRace.startOfTracking));
|
||||
} else {
|
||||
raceDataGrid.setText(3, 1, "");
|
||||
}
|
||||
if(selectedRaceDTO.trackedRace.endOfTracking != null) {
|
||||
if (selectedRaceDTO.trackedRace != null && selectedRaceDTO.trackedRace.endOfTracking != null) {
|
||||
raceDataGrid.setText(4, 1, dateFormatter.render(selectedRaceDTO.trackedRace.endOfTracking) + " "
|
||||
+ timeFormatter.render(selectedRaceDTO.trackedRace.endOfTracking));
|
||||
} else {
|
||||
raceDataGrid.setText(4, 1, "");
|
||||
}
|
||||
raceDataGrid.setText(5, 1, "" + selectedRaceDTO.trackedRace.delayToLiveInMs);
|
||||
if (selectedRaceDTO.trackedRace != null) {
|
||||
raceDataGrid.setText(5, 1, "" + selectedRaceDTO.trackedRace.delayToLiveInMs);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package com.sap.sailing.gwt.ui.client;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.github.gwtbootstrap.datetimepicker.client.ui.base.HasViewMode.ViewMode;
|
||||
import com.google.gwt.event.logical.shared.AttachEvent;
|
||||
import com.google.gwt.event.logical.shared.ValueChangeEvent;
|
||||
import com.google.gwt.event.logical.shared.ValueChangeHandler;
|
||||
import com.google.gwt.event.logical.shared.AttachEvent.Handler;
|
||||
import com.sap.sailing.gwt.ui.shared.BetterDateTimeBox;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
import com.sap.sse.gwt.client.dialog.DialogUtils;
|
||||
|
||||
public abstract class DataEntryDialogWithBootstrap<T> extends DataEntryDialog<T> {
|
||||
|
||||
public DataEntryDialogWithBootstrap(String title, String message, String okButtonName, String cancelButtonName,
|
||||
com.sap.sse.gwt.client.dialog.DataEntryDialog.Validator<T> validator, boolean animationEnabled,
|
||||
com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback<T> callback) {
|
||||
super(title, message, okButtonName, cancelButtonName, validator, animationEnabled, callback);
|
||||
}
|
||||
|
||||
protected DataEntryDialogWithBootstrap(String title, String message, String okButtonName, String cancelButtonName,
|
||||
com.sap.sse.gwt.client.dialog.DataEntryDialog.Validator<T> validator,
|
||||
com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback<T> callback) {
|
||||
super(title, message, okButtonName, cancelButtonName, validator, callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Call something like <code>setFormat("dd/mm/yyyy hh:ii")</code> on the result to set the date / time entry format.
|
||||
*/
|
||||
public BetterDateTimeBox createDateTimeBox(Date initialValue) {
|
||||
final BetterDateTimeBox result = new BetterDateTimeBox();
|
||||
result.setValue(initialValue);
|
||||
result.addValueChangeHandler(new ValueChangeHandler<Date>() {
|
||||
@Override
|
||||
public void onValueChange(ValueChangeEvent<Date> event) {
|
||||
validate();
|
||||
}
|
||||
});
|
||||
result.addAttachHandler(new Handler() {
|
||||
@Override
|
||||
public void onAttachOrDetach(AttachEvent event) {
|
||||
if (event.isAttached()) {
|
||||
addAutoHidePartner(result.getPicker());
|
||||
}
|
||||
}
|
||||
});
|
||||
result.setAutoClose(true);
|
||||
result.setStartView(ViewMode.HOUR);
|
||||
DialogUtils.linkEnterToButton(getOkButton(), result.getBox());
|
||||
DialogUtils.linkEscapeToButton(getCancelButton(), result.getBox());
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-2
@@ -182,7 +182,7 @@ public interface SailingService extends RemoteService {
|
||||
|
||||
RegattaDTO createRegatta(String regattaName, String boatClassName,
|
||||
RegattaCreationParametersDTO seriesNamesWithFleetNamesAndFleetOrderingAndMedal, boolean persistent,
|
||||
ScoringSchemeType scoringSchemeType, UUID defaultCourseAreaId);
|
||||
ScoringSchemeType scoringSchemeType, UUID defaultCourseAreaId, boolean useStartTimeInference);
|
||||
|
||||
void removeRegatta(RegattaIdentifier regattaIdentifier);
|
||||
|
||||
@@ -190,7 +190,7 @@ public interface SailingService extends RemoteService {
|
||||
|
||||
void removeRegattas(Collection<RegattaIdentifier> regattas);
|
||||
|
||||
void updateRegatta(RegattaIdentifier regattaIdentifier, UUID defaultCourseAreaUuid, RegattaConfigurationDTO regattaConfiguration);
|
||||
void updateRegatta(RegattaIdentifier regattaIdentifier, UUID defaultCourseAreaUuid, RegattaConfigurationDTO regattaConfiguration, boolean useStartTimeInference);
|
||||
|
||||
List<RaceColumnInSeriesDTO> addRaceColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames);
|
||||
|
||||
|
||||
+3
-2
@@ -383,7 +383,7 @@ public interface SailingServiceAsync {
|
||||
|
||||
void createRegatta(String regattaName, String boatClassName,
|
||||
RegattaCreationParametersDTO seriesNamesWithFleetNamesAndFleetOrderingAndMedal, boolean persistent,
|
||||
ScoringSchemeType scoringSchemeType, UUID defaultCourseAreaId, AsyncCallback<RegattaDTO> callback);
|
||||
ScoringSchemeType scoringSchemeType, UUID defaultCourseAreaId, boolean useStartTimeInference, AsyncCallback<RegattaDTO> callback);
|
||||
|
||||
void addRaceColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName, List<String> columnNames,
|
||||
AsyncCallback<List<RaceColumnInSeriesDTO>> callback);
|
||||
@@ -457,7 +457,8 @@ public interface SailingServiceAsync {
|
||||
PolarSheetGenerationSettings settings, String name, AsyncCallback<PolarSheetGenerationResponse> asyncCallback);
|
||||
|
||||
|
||||
void updateRegatta(RegattaIdentifier regattaIdentifier, UUID defaultCourseAreaUuid, RegattaConfigurationDTO regattaConfiguration, AsyncCallback<Void> callback);
|
||||
void updateRegatta(RegattaIdentifier regattaIdentifier, UUID defaultCourseAreaUuid,
|
||||
RegattaConfigurationDTO regattaConfiguration, boolean useStartTimeInference, AsyncCallback<Void> callback);
|
||||
|
||||
/**
|
||||
* @param detailType
|
||||
|
||||
+1
@@ -1056,4 +1056,5 @@ public interface StringMessages extends Messages {
|
||||
String lastScoreCorrectionsTime();
|
||||
String lastScoreCorrectionsComment();
|
||||
String setTimeToNow();
|
||||
String useStartTimeInference();
|
||||
}
|
||||
+4
-3
@@ -363,8 +363,8 @@ 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
|
||||
boatClassDoesNotMatchSelectedRegatta=The selected races contain boat classes which are not the same as the boat class ''{0}'' of the selected regatta.
|
||||
regattaExistForSelectedBoatClass=There is at least one regatta for the selected boat classes.
|
||||
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
|
||||
addRegatta=Add Regatta...
|
||||
exchangeName=Exchange Name
|
||||
@@ -1039,4 +1039,5 @@ setStartTimeReceived=Set start time received
|
||||
setStartTimeReceivedDescription=This sets the startTimeReceived of the selected TrackedRace that isn''t persistent. This means that the new value will be forgotten after the server has been restarted.
|
||||
lastScoreCorrectionsTime=Last score correction time
|
||||
lastScoreCorrectionsComment=Last score correction comment
|
||||
setTimeToNow=Set time to ''now''
|
||||
setTimeToNow=Set time to ''now''
|
||||
useStartTimeInference=Use start time inference from start mark passings
|
||||
Loaded 100 of 185 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user