mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-08 05:11:04 +00:00
Merge branch 'bug2026' into spatial-wind
Conflicts: java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java java/com.sap.sailing.gwt.ui/.settings/com.google.gwt.eclipse.core.prefs java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java
This commit is contained in:
commit
212ca8f153
110 files changed
+1549
-417
No files matched your search
@@ -15,6 +15,7 @@ Like businesses, sailors need the latest information to make strategic decisions
|
||||
* [[EC2 Backup Strategy|wiki/amazon-ec2-backup-strategy]]
|
||||
* [[TracTrac|wiki/tractrac-lifecycle]]
|
||||
* [[Analytics on a stick|wiki/analytics-on-a-stick]]
|
||||
* [[Monitoring Apache and RabbitMQ|wiki/monitoring-apache-and-rabbitmq]]
|
||||
* Information for Developers
|
||||
* [[Cook Book|wiki/cook-book]]
|
||||
* [[OnBoarding Information|wiki/onboarding]]
|
||||
|
||||
+5
@@ -34,4 +34,9 @@ public class FlagPole {
|
||||
this.isDisplayed == otherPole.isDisplayed;
|
||||
}
|
||||
|
||||
public boolean describesSameButForDisplayed(FlagPole otherPole) {
|
||||
return this.upperFlag == otherPole.upperFlag &&
|
||||
this.lowerFlag == otherPole.lowerFlag;
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -21,7 +21,7 @@ public class AbstractTestWithIgtimiConnection {
|
||||
public void setUp() throws ClientProtocolException, IOException, org.json.simple.parser.ParseException {
|
||||
final IgtimiConnectionFactory connectionFactory = Activator.getInstance().getConnectionFactory();
|
||||
Account account = connectionFactory.registerAccountForWhichClientIsAuthorized("9fded995cf21c8ed91ddaec13b220e8d5e44c65808d22ec2b1b7c32261121f26");
|
||||
connection = connectionFactory.connect(account, /* correctByDeclination */ true);
|
||||
connection = connectionFactory.connect(account);
|
||||
}
|
||||
|
||||
}
|
||||
+1
-1
@@ -159,7 +159,7 @@ public class WebSocketTest {
|
||||
// the following is an access token for an account allowing axel.uhl@gmx.de to access
|
||||
// the data from baur@stg-academy.org, particularly containing the Berlin test data
|
||||
Account account = igtimiConnectionFactory.registerAccountForWhichClientIsAuthorized("9fded995cf21c8ed91ddaec13b220e8d5e44c65808d22ec2b1b7c32261121f26");
|
||||
IgtimiConnection conn = igtimiConnectionFactory.connect(account, /* correctByDeclination */ true);
|
||||
IgtimiConnection conn = igtimiConnectionFactory.connect(account);
|
||||
LiveDataConnection liveDataConnection = conn.getOrCreateLiveConnection(Collections.singleton("GA-EN-AAEJ"));
|
||||
LiveDataConnection redundantSecondSharedConnection = conn.getOrCreateLiveConnection(Collections.singleton("GA-EN-AAEJ"));
|
||||
assertTrue(liveDataConnection instanceof LiveDataConnectionWrapper);
|
||||
|
||||
+2
-3
@@ -8,7 +8,6 @@ import org.apache.http.client.ClientProtocolException;
|
||||
import org.json.simple.parser.ParseException;
|
||||
import org.xml.sax.SAXException;
|
||||
|
||||
import com.sap.sailing.declination.DeclinationService;
|
||||
import com.sap.sailing.domain.igtimiadapter.oauth.AuthorizationCallback;
|
||||
|
||||
/**
|
||||
@@ -21,7 +20,7 @@ import com.sap.sailing.domain.igtimiadapter.oauth.AuthorizationCallback;
|
||||
public interface IgtimiConnectionFactory {
|
||||
Iterable<Account> getAllAccounts();
|
||||
|
||||
IgtimiConnection connect(Account account, boolean correctByDeclination);
|
||||
IgtimiConnection connect(Account account);
|
||||
|
||||
/**
|
||||
* Removes the account and the credentials associated with it permanently. The account is identified by the e-mail
|
||||
@@ -39,7 +38,7 @@ public interface IgtimiConnectionFactory {
|
||||
/**
|
||||
* Using the "account" service, retrieves the {@link Account} information for this access token and stores the token
|
||||
* that authorizes this factory's client to access the account together with the {@link Account} data. This information is
|
||||
* used when a caller wants to {@link #connect(Account, DeclinationService)} to a specific account on behalf of the {@link Client} to
|
||||
* used when a caller wants to {@link #connect(Account)} to a specific account on behalf of the {@link Client} to
|
||||
* which this factory belongs.
|
||||
* @return
|
||||
*/
|
||||
|
||||
+13
-3
@@ -55,13 +55,15 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
|
||||
private static final Logger logger = Logger.getLogger(IgtimiConnectionFactoryImpl.class.getName());
|
||||
|
||||
private final Map<Account, String> accessTokensByAccount;
|
||||
private Map<String, Account> accountsByEmail;
|
||||
private final Map<String, Account> accountsByEmail;
|
||||
private final Map<Account, IgtimiConnection> connectionsByAccount;
|
||||
private final Client client;
|
||||
private final MongoObjectFactory mongoObjectFactory;
|
||||
|
||||
public IgtimiConnectionFactoryImpl(Client client, DomainObjectFactory domainObjectFactory, MongoObjectFactory mongoObjectFactory) {
|
||||
this.accessTokensByAccount = new HashMap<>();
|
||||
this.accountsByEmail = new HashMap<>();
|
||||
connectionsByAccount = new HashMap<>();
|
||||
this.client = client;
|
||||
this.mongoObjectFactory = mongoObjectFactory;
|
||||
for (String accessToken : domainObjectFactory.getAccessTokens()) {
|
||||
@@ -130,8 +132,16 @@ public class IgtimiConnectionFactoryImpl implements IgtimiConnectionFactory {
|
||||
}
|
||||
|
||||
@Override
|
||||
public IgtimiConnection connect(Account account, boolean correctByDeclination) {
|
||||
return new IgtimiConnectionImpl(this, account);
|
||||
public IgtimiConnection connect(Account account) {
|
||||
IgtimiConnection connection;
|
||||
synchronized (connectionsByAccount) {
|
||||
connection = connectionsByAccount.get(account);
|
||||
if (connection == null) {
|
||||
connection = new IgtimiConnectionImpl(this, account);
|
||||
connectionsByAccount.put(account, connection);
|
||||
}
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
|
||||
private String getAccessTokenForAccount(Account account) {
|
||||
|
||||
+1
-1
@@ -44,7 +44,7 @@ public class IgtimiWindTracker extends AbstractWindTracker implements WindTracke
|
||||
for (Account account : accounts) {
|
||||
try {
|
||||
if (!stopping) {
|
||||
IgtimiConnection connection = connectionFactory.connect(account, correctByDeclination);
|
||||
IgtimiConnection connection = connectionFactory.connect(account);
|
||||
Iterable<String> devicesWeShouldListenTo = connection.getWindDevices();
|
||||
if (!stopping) {
|
||||
LiveDataConnection liveConnection = connection.getOrCreateLiveConnection(devicesWeShouldListenTo);
|
||||
|
||||
+8
-2
@@ -378,8 +378,14 @@ public class IgtimiWindReceiver implements BulkFixReceiver {
|
||||
if (declinationService == null) {
|
||||
trueHeading = hdgm;
|
||||
} else {
|
||||
Declination declination = declinationService.getDeclination(timePoint, position, /* timeoutForOnlineFetchInMilliseconds 5s */ 5000);
|
||||
trueHeading = hdgm.add(declination.getBearingCorrectedTo(timePoint));
|
||||
try {
|
||||
Declination declination = declinationService.getDeclination(timePoint, position, /* timeoutForOnlineFetchInMilliseconds 5s */ 5000);
|
||||
trueHeading = hdgm.add(declination.getBearingCorrectedTo(timePoint));
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Correction of declination was requested but unsuccessful. Can't correct heading "+
|
||||
hdgm+"@"+timePoint+" by declination. Forwarding exception.");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
trueHeading = null;
|
||||
|
||||
+5
-1
@@ -154,7 +154,11 @@ public class WebSocketConnectionManager extends WebSocketAdapter implements Live
|
||||
|
||||
private void notifyListeners(List<Fix> fixes) {
|
||||
for (BulkFixReceiver listener : listeners.keySet()) {
|
||||
listener.received(fixes);
|
||||
try {
|
||||
listener.received(fixes);
|
||||
} catch (Exception e) {
|
||||
logger.log(Level.SEVERE, "Error notifying listener "+listener+" of Igtimi fixes "+fixes, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-11
@@ -1151,10 +1151,10 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
Boolean hasSplitFleetContiguousScoring = (Boolean) dbSeries.get(FieldNames.SERIES_HAS_SPLIT_FLEET_CONTIGUOUS_SCORING.name());
|
||||
Boolean firstColumnIsNonDiscardableCarryForward = (Boolean) dbSeries.get(FieldNames.SERIES_STARTS_WITH_NON_DISCARDABLE_CARRY_FORWARD.name());
|
||||
final BasicDBList dbFleets = (BasicDBList) dbSeries.get(FieldNames.SERIES_FLEETS.name());
|
||||
Map<String, Fleet> fleetsByName = loadFleets(dbFleets);
|
||||
List<Fleet> fleets = loadFleets(dbFleets);
|
||||
BasicDBList dbRaceColumns = (BasicDBList) dbSeries.get(FieldNames.SERIES_RACE_COLUMNS.name());
|
||||
Iterable<String> raceColumnNames = loadRaceColumnNames(dbRaceColumns, fleetsByName);
|
||||
Series series = new SeriesImpl(name, isMedal, fleetsByName.values(), raceColumnNames, trackedRegattaRegistry);
|
||||
Iterable<String> raceColumnNames = loadRaceColumnNames(dbRaceColumns);
|
||||
Series series = new SeriesImpl(name, isMedal, fleets, raceColumnNames, trackedRegattaRegistry);
|
||||
if (dbSeries.get(FieldNames.SERIES_DISCARDING_THRESHOLDS.name()) != null) {
|
||||
ThresholdBasedResultDiscardingRule resultDiscardingRule = loadResultDiscardingRule(dbSeries, FieldNames.SERIES_DISCARDING_THRESHOLDS);
|
||||
series.setResultDiscardingRule(resultDiscardingRule);
|
||||
@@ -1172,11 +1172,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
return series;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fleetsByName used to ensure the {@link RaceColumn#getFleets()} points to the same {@link Fleet} objects also
|
||||
* used in the {@link Series#getFleets()} collection.
|
||||
*/
|
||||
private Iterable<String> loadRaceColumnNames(BasicDBList dbRaceColumns, Map<String, Fleet> fleetsByName) {
|
||||
private Iterable<String> loadRaceColumnNames(BasicDBList dbRaceColumns) {
|
||||
List<String> result = new ArrayList<String>();
|
||||
for (Object o : dbRaceColumns) {
|
||||
DBObject dbRaceColumn = (DBObject) o;
|
||||
@@ -1201,12 +1197,12 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, Fleet> loadFleets(BasicDBList dbFleets) {
|
||||
Map<String, Fleet> result = new HashMap<String, Fleet>();
|
||||
private List<Fleet> loadFleets(BasicDBList dbFleets) {
|
||||
List<Fleet> result = new ArrayList<Fleet>();
|
||||
for (Object o : dbFleets) {
|
||||
DBObject dbFleet = (DBObject) o;
|
||||
Fleet fleet = loadFleet(dbFleet);
|
||||
result.put(fleet.getName(), fleet);
|
||||
result.add(fleet);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+9
-1
@@ -1,17 +1,25 @@
|
||||
package com.sap.sailing.domain.racelog.analyzing.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.racelog.Flags;
|
||||
import com.sap.sailing.domain.racelog.RaceLog;
|
||||
import com.sap.sailing.domain.racelog.RaceLogFlagEvent;
|
||||
|
||||
public class IndividualRecallDisplayedFinder extends IndividualRecallFinder {
|
||||
private final TimePoint at;
|
||||
|
||||
public IndividualRecallDisplayedFinder(RaceLog raceLog) {
|
||||
this(raceLog, null);
|
||||
}
|
||||
|
||||
public IndividualRecallDisplayedFinder(RaceLog raceLog, TimePoint at) {
|
||||
super(raceLog);
|
||||
this.at = at;
|
||||
}
|
||||
|
||||
protected boolean isRelevant(RaceLogFlagEvent flagEvent) {
|
||||
return flagEvent.getUpperFlag().equals(Flags.XRAY) && flagEvent.isDisplayed();
|
||||
boolean disregardBecauseAfterAt = at != null && flagEvent.getTimePoint().after(at);
|
||||
return ! disregardBecauseAfterAt && flagEvent.getUpperFlag().equals(Flags.XRAY) && flagEvent.isDisplayed();
|
||||
}
|
||||
|
||||
}
|
||||
+10
-1
@@ -1,17 +1,26 @@
|
||||
package com.sap.sailing.domain.racelog.analyzing.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.racelog.Flags;
|
||||
import com.sap.sailing.domain.racelog.RaceLog;
|
||||
import com.sap.sailing.domain.racelog.RaceLogFlagEvent;
|
||||
|
||||
public class IndividualRecallRemovedFinder extends IndividualRecallFinder {
|
||||
|
||||
private final TimePoint at;
|
||||
|
||||
public IndividualRecallRemovedFinder(RaceLog raceLog) {
|
||||
this(raceLog, null);
|
||||
}
|
||||
|
||||
public IndividualRecallRemovedFinder(RaceLog raceLog, TimePoint at) {
|
||||
super(raceLog);
|
||||
this.at = at;
|
||||
}
|
||||
|
||||
protected boolean isRelevant(RaceLogFlagEvent flagEvent) {
|
||||
return flagEvent.getUpperFlag().equals(Flags.XRAY) && !flagEvent.isDisplayed();
|
||||
boolean disregardBecauseAfterAt = at != null && flagEvent.getTimePoint().after(at);
|
||||
return ! disregardBecauseAfterAt && flagEvent.getUpperFlag().equals(Flags.XRAY) && !flagEvent.isDisplayed();
|
||||
}
|
||||
|
||||
}
|
||||
+6
-2
@@ -9,11 +9,15 @@ public class IsIndividualRecallDisplayedAnalyzer extends RaceLogAnalyzer<Boolean
|
||||
private final IndividualRecallFinder removedFinder;
|
||||
|
||||
public IsIndividualRecallDisplayedAnalyzer(RaceLog raceLog) {
|
||||
this(raceLog, new IndividualRecallDisplayedFinder(raceLog), new IndividualRecallRemovedFinder(raceLog));
|
||||
this(raceLog, null);
|
||||
}
|
||||
|
||||
public IsIndividualRecallDisplayedAnalyzer(RaceLog raceLog, TimePoint at) {
|
||||
this(raceLog, new IndividualRecallDisplayedFinder(raceLog, at), new IndividualRecallRemovedFinder(raceLog, at), at);
|
||||
}
|
||||
|
||||
public IsIndividualRecallDisplayedAnalyzer(RaceLog raceLog, IndividualRecallDisplayedFinder displayedFinder,
|
||||
IndividualRecallRemovedFinder removedFinder) {
|
||||
IndividualRecallRemovedFinder removedFinder, TimePoint at) {
|
||||
super(raceLog);
|
||||
this.displayedFinder = displayedFinder;
|
||||
this.removedFinder = removedFinder;
|
||||
|
||||
+93
-21
@@ -8,25 +8,29 @@ import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.racelog.FlagPole;
|
||||
|
||||
/**
|
||||
* Allows you to query the current and state of displayed and remove flags.
|
||||
* Allows you to query the current state of displayed and removed flags.
|
||||
*/
|
||||
public class FlagPoleState {
|
||||
|
||||
private final List<FlagPole> currentState;
|
||||
private final List<FlagPole> nextState;
|
||||
private final TimePoint currentStateValidFrom;
|
||||
private final TimePoint nextStateValidFrom;
|
||||
private final boolean hasNextState;
|
||||
|
||||
public FlagPoleState(List<FlagPole> currentState) {
|
||||
this(currentState, null, null, false);
|
||||
public FlagPoleState(List<FlagPole> currentState, TimePoint currentStateValidFrom) {
|
||||
this(currentState, currentStateValidFrom, null, null, false);
|
||||
}
|
||||
|
||||
public FlagPoleState(List<FlagPole> currentState, List<FlagPole> nextState, TimePoint nextStateValidFrom) {
|
||||
this(currentState, nextState, nextStateValidFrom, true);
|
||||
public FlagPoleState(List<FlagPole> currentState, TimePoint currentStateValidFrom,
|
||||
List<FlagPole> nextState, TimePoint nextStateValidFrom) {
|
||||
this(currentState, currentStateValidFrom, nextState, nextStateValidFrom, true);
|
||||
}
|
||||
|
||||
private FlagPoleState(List<FlagPole> currentState, List<FlagPole> nextState, TimePoint nextStateValidFrom, boolean hasNextState) {
|
||||
private FlagPoleState(List<FlagPole> currentState, TimePoint currentStateValidFrom,
|
||||
List<FlagPole> nextState, TimePoint nextStateValidFrom, boolean hasNextState) {
|
||||
this.currentState = currentState;
|
||||
this.currentStateValidFrom = currentStateValidFrom;
|
||||
this.nextState = nextState;
|
||||
this.nextStateValidFrom = nextStateValidFrom;
|
||||
this.hasNextState = hasNextState;
|
||||
@@ -42,30 +46,51 @@ public class FlagPoleState {
|
||||
public TimePoint getNextStateValidFrom() {
|
||||
return nextStateValidFrom;
|
||||
}
|
||||
|
||||
public TimePoint getCurrentStateValidFrom() {
|
||||
return currentStateValidFrom;
|
||||
}
|
||||
|
||||
public boolean hasNextState() {
|
||||
return hasNextState;
|
||||
}
|
||||
|
||||
public static List<FlagPole> computeChanges(List<FlagPole> before, List<FlagPole> after) {
|
||||
before = before == null ? Collections.<FlagPole>emptyList() : before;
|
||||
after = after == null ? Collections.<FlagPole>emptyList() : after;
|
||||
|
||||
List<FlagPole> beforeWithoutMatchInAfter = new ArrayList<FlagPole>(before);
|
||||
List<FlagPole> changes = new ArrayList<FlagPole>();
|
||||
for (FlagPole poleAfter : after) {
|
||||
boolean hasSamePoleBefore = false;
|
||||
for (FlagPole poleBefore : before) {
|
||||
if (poleAfter.describesSameButForDisplayed(poleBefore)) {
|
||||
beforeWithoutMatchInAfter.remove(poleBefore);
|
||||
}
|
||||
if (poleAfter.describesSame(poleBefore)) {
|
||||
hasSamePoleBefore = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasSamePoleBefore) {
|
||||
changes.add(poleAfter);
|
||||
}
|
||||
}
|
||||
|
||||
for (FlagPole poleBefore : beforeWithoutMatchInAfter) {
|
||||
if (poleBefore.isDisplayed()) {
|
||||
changes.add(new FlagPole(poleBefore.getUpperFlag(), poleBefore.getLowerFlag(), false));
|
||||
}
|
||||
}
|
||||
return changes;
|
||||
}
|
||||
|
||||
public List<FlagPole> computeUpcomingChanges() {
|
||||
if (!hasNextState) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
List<FlagPole> changes = new ArrayList<FlagPole>();
|
||||
for (FlagPole nextPole : nextState) {
|
||||
boolean hasSamePoleInCurrentState = false;
|
||||
for (FlagPole currentPole : currentState) {
|
||||
if (nextPole.describesSame(currentPole)) {
|
||||
hasSamePoleInCurrentState = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hasSamePoleInCurrentState) {
|
||||
changes.add(nextPole);
|
||||
}
|
||||
}
|
||||
return changes;
|
||||
return computeChanges(currentState, nextState);
|
||||
}
|
||||
|
||||
public static boolean describesSameState(FlagPoleState left, FlagPoleState right) {
|
||||
@@ -99,5 +124,52 @@ public class FlagPoleState {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
public FlagPoleState getPreviousState(ReadonlyRacingProcedure racingProcedure, TimePoint startTime) {
|
||||
if (currentStateValidFrom == null) {
|
||||
return this;
|
||||
}
|
||||
return racingProcedure.getActiveFlags(startTime, currentStateValidFrom.minus(1));
|
||||
}
|
||||
|
||||
public static FlagPole getMostInterestingFlagPole(List<FlagPole> poles) {
|
||||
return getMostInterestingFlagPole(poles, poles);
|
||||
}
|
||||
|
||||
public static FlagPole getMostInterestingFlagPole(List<FlagPole> previousPoles, List<FlagPole> currentPoles) {
|
||||
List<FlagPole> changes = computeChanges(previousPoles, currentPoles);
|
||||
FlagPole poleRemoved = null;
|
||||
for (FlagPole changedPole : changes) {
|
||||
if (changedPole.isDisplayed()) {
|
||||
return changedPole;
|
||||
} else {
|
||||
poleRemoved = changedPole;
|
||||
}
|
||||
}
|
||||
if (poleRemoved != null) {
|
||||
return poleRemoved;
|
||||
}
|
||||
|
||||
for (FlagPole currentPole : currentPoles) {
|
||||
if (currentPole.isDisplayed()) {
|
||||
return currentPole;
|
||||
}
|
||||
}
|
||||
return currentPoles.size() == 0 ? null : currentPoles.get(0);
|
||||
}
|
||||
|
||||
public boolean hasPoleChanged(FlagPole newPole) {
|
||||
boolean found = false;
|
||||
for (FlagPole current : currentState) {
|
||||
if (current.describesSameButForDisplayed(newPole)) {
|
||||
found = true;
|
||||
if (current.isDisplayed() != newPole.isDisplayed()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (! found && newPole.isDisplayed()) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
+10
@@ -68,6 +68,16 @@ public interface ReadonlyRacingProcedure extends RaceStateEventProcessor, RaceSt
|
||||
*/
|
||||
boolean isIndividualRecallDisplayed();
|
||||
|
||||
/**
|
||||
* Returns <code>true</code> if there is an individual recall signaled in the {@link RaceLog} at the TimePoint {@code at}.
|
||||
*/
|
||||
boolean isIndividualRecallDisplayed(TimePoint at);
|
||||
|
||||
/**
|
||||
* Gets the time the individual recall flag was displayed (or <code>null</code>).
|
||||
*/
|
||||
TimePoint getIndividualRecallDisplayedTime();
|
||||
|
||||
/**
|
||||
* Gets the time the individual recall flag was removed (or <code>null</code>).
|
||||
*/
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ public class BasicRacingProcedureImpl extends BaseRacingProcedure {
|
||||
|
||||
@Override
|
||||
public FlagPoleState getActiveFlags(TimePoint startTime, TimePoint now) {
|
||||
return new FlagPoleState(Collections.<FlagPole>emptyList());
|
||||
return new FlagPoleState(Collections.<FlagPole>emptyList(), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+13
-7
@@ -91,37 +91,43 @@ public class ESSRacingProcedureImpl extends BaseRacingProcedure implements ESSRa
|
||||
public FlagPoleState getActiveFlags(TimePoint startTime, TimePoint now) {
|
||||
if (now.before(startTime.minus(startPhaseAPDownInterval))) {
|
||||
return new FlagPoleState(
|
||||
Arrays.asList(new FlagPole(Flags.AP, true), new FlagPole(Flags.ESSTHREE, false)),
|
||||
Arrays.asList(new FlagPole(Flags.AP, true), new FlagPole(Flags.ESSTHREE, false)), null,
|
||||
Arrays.asList(new FlagPole(Flags.AP, false), new FlagPole(Flags.ESSTHREE, false)),
|
||||
startTime.minus(startPhaseAPDownInterval));
|
||||
} else if (now.before(startTime.minus(startPhaseESSThreeUpInterval))) {
|
||||
return new FlagPoleState(
|
||||
Arrays.asList(new FlagPole(Flags.AP, false), new FlagPole(Flags.ESSTHREE, false)),
|
||||
Arrays.asList(new FlagPole(Flags.AP, false), new FlagPole(Flags.ESSTHREE, false)),
|
||||
startTime.minus(startPhaseAPDownInterval),
|
||||
Arrays.asList(new FlagPole(Flags.ESSTHREE, true), new FlagPole(Flags.ESSTWO, false)),
|
||||
startTime.minus(startPhaseESSThreeUpInterval));
|
||||
} else if (now.before(startTime.minus(startPhaseESSTwoUpInterval))) {
|
||||
return new FlagPoleState(
|
||||
Arrays.asList(new FlagPole(Flags.ESSTHREE, true), new FlagPole(Flags.ESSTWO, false)),
|
||||
Arrays.asList(new FlagPole(Flags.ESSTHREE, true), new FlagPole(Flags.ESSTWO, false)),
|
||||
startTime.minus(startPhaseESSThreeUpInterval),
|
||||
Arrays.asList(new FlagPole(Flags.ESSTWO, true), new FlagPole(Flags.ESSONE, false)),
|
||||
startTime.minus(startPhaseESSTwoUpInterval));
|
||||
} else if (now.before(startTime.minus(startPhaseESSOneUpInterval))) {
|
||||
return new FlagPoleState(
|
||||
Arrays.asList(new FlagPole(Flags.ESSTWO, true), new FlagPole(Flags.ESSONE, false)),
|
||||
Arrays.asList(new FlagPole(Flags.ESSTWO, true), new FlagPole(Flags.ESSONE, false)),
|
||||
startTime.minus(startPhaseESSTwoUpInterval),
|
||||
Arrays.asList(new FlagPole(Flags.ESSONE, true)),
|
||||
startTime.minus(startPhaseESSOneUpInterval));
|
||||
} if (now.before(startTime)) {
|
||||
return new FlagPoleState(
|
||||
Arrays.asList(new FlagPole(Flags.ESSONE, true)),
|
||||
Arrays.asList(new FlagPole(Flags.ESSONE, true)),
|
||||
startTime.minus(startPhaseESSOneUpInterval),
|
||||
Arrays.asList(new FlagPole(Flags.ESSONE, false)),
|
||||
startTime);
|
||||
} else {
|
||||
if (isIndividualRecallDisplayed()) {
|
||||
if (isIndividualRecallDisplayed(now)) {
|
||||
return new FlagPoleState(
|
||||
Arrays.asList(new FlagPole(Flags.XRAY, true)),
|
||||
getIndividualRecallDisplayedTime(),
|
||||
Arrays.asList(new FlagPole(Flags.XRAY, false)),
|
||||
getIndividualRecallRemovalTime());
|
||||
} else {
|
||||
return new FlagPoleState(Collections.<FlagPole>emptyList());
|
||||
TimePoint recallRemoved = getIndividualRecallRemovalTime();
|
||||
return new FlagPoleState(Collections.<FlagPole>emptyList(), recallRemoved == null ? startTime : recallRemoved);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+10
-7
@@ -146,24 +146,27 @@ public class GateStartRacingProcedureImpl extends BaseRacingProcedure implements
|
||||
TimePoint gateShutdownTime = getGateShutdownTimePoint(startTime);
|
||||
if (now.before(startTime.minus(startPhaseClassOverGolfUpIntervall))) {
|
||||
return new FlagPoleState(Arrays.asList(new FlagPole(classFlag, Flags.GOLF, false), new FlagPole(Flags.PAPA,
|
||||
false)), Arrays.asList(new FlagPole(classFlag, Flags.GOLF, true), new FlagPole(Flags.PAPA, false)),
|
||||
false)), null, Arrays.asList(new FlagPole(classFlag, Flags.GOLF, true), new FlagPole(Flags.PAPA, false)),
|
||||
startTime.minus(startPhaseClassOverGolfUpIntervall));
|
||||
} else if (now.before(startTime.minus(startPhasePapaUpInterval))) {
|
||||
return new FlagPoleState(Arrays.asList(new FlagPole(classFlag, Flags.GOLF, true), new FlagPole(Flags.PAPA,
|
||||
false)), Arrays.asList(new FlagPole(classFlag, Flags.GOLF, true), new FlagPole(Flags.PAPA, true)),
|
||||
false)), startTime.minus(startPhaseClassOverGolfUpIntervall),
|
||||
Arrays.asList(new FlagPole(classFlag, Flags.GOLF, true), new FlagPole(Flags.PAPA, true)),
|
||||
startTime.minus(startPhasePapaUpInterval));
|
||||
} else if (now.before(startTime.minus(startPhasePapaDownInterval))) {
|
||||
return new FlagPoleState(Arrays.asList(new FlagPole(classFlag, Flags.GOLF, true), new FlagPole(Flags.PAPA,
|
||||
true)), Arrays.asList(new FlagPole(classFlag, Flags.GOLF, true), new FlagPole(Flags.PAPA, false)),
|
||||
true)), startTime.minus(startPhasePapaUpInterval),
|
||||
Arrays.asList(new FlagPole(classFlag, Flags.GOLF, true), new FlagPole(Flags.PAPA, false)),
|
||||
startTime.minus(startPhasePapaDownInterval));
|
||||
} else if (now.before(startTime)) {
|
||||
return new FlagPoleState(Arrays.asList(new FlagPole(classFlag, Flags.GOLF, true), new FlagPole(Flags.PAPA,
|
||||
false)), Arrays.asList(new FlagPole(classFlag, false), new FlagPole(Flags.GOLF, true)), startTime);
|
||||
false)), startTime.minus(startPhasePapaDownInterval),
|
||||
Arrays.asList(new FlagPole(classFlag, false), new FlagPole(Flags.GOLF, true)), startTime);
|
||||
} else if (now.before(gateShutdownTime)) {
|
||||
return new FlagPoleState(Arrays.asList(new FlagPole(Flags.GOLF, true)), Arrays.asList(new FlagPole(
|
||||
Flags.GOLF, false)), gateShutdownTime);
|
||||
return new FlagPoleState(Arrays.asList(new FlagPole(Flags.GOLF, true)), startTime,
|
||||
Arrays.asList(new FlagPole(Flags.GOLF, false)), gateShutdownTime);
|
||||
} else {
|
||||
return new FlagPoleState(Arrays.asList(new FlagPole(Flags.GOLF, false)));
|
||||
return new FlagPoleState(Arrays.asList(new FlagPole(Flags.GOLF, false)), gateShutdownTime);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+31
-10
@@ -13,6 +13,7 @@ import com.sap.sailing.domain.racelog.RaceLogEventAuthor;
|
||||
import com.sap.sailing.domain.racelog.RaceLogEventFactory;
|
||||
import com.sap.sailing.domain.racelog.RaceLogEventVisitor;
|
||||
import com.sap.sailing.domain.racelog.analyzing.impl.IndividualRecallDisplayedFinder;
|
||||
import com.sap.sailing.domain.racelog.analyzing.impl.IndividualRecallRemovedFinder;
|
||||
import com.sap.sailing.domain.racelog.analyzing.impl.IsIndividualRecallDisplayedAnalyzer;
|
||||
import com.sap.sailing.domain.racelog.impl.RaceLogChangedVisitor;
|
||||
import com.sap.sailing.domain.racelog.state.RaceStateEvent;
|
||||
@@ -46,6 +47,7 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
|
||||
private final RacingProcedureChangedListeners<? extends RacingProcedureChangedListener> changedListeners;
|
||||
private final IsIndividualRecallDisplayedAnalyzer isRecallDisplayedAnalyzer;
|
||||
private final IndividualRecallDisplayedFinder recallDisplayedFinder;
|
||||
private final IndividualRecallRemovedFinder recallRemovedFinder;
|
||||
private final RaceLogEventVisitor raceLogListener;
|
||||
|
||||
private boolean cachedIsIndividualRecallDisplayed;
|
||||
@@ -67,6 +69,7 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
|
||||
this.changedListeners = createChangedListenerContainer();
|
||||
this.isRecallDisplayedAnalyzer = new IsIndividualRecallDisplayedAnalyzer(raceLog);
|
||||
this.recallDisplayedFinder = new IndividualRecallDisplayedFinder(raceLog);
|
||||
this.recallRemovedFinder = new IndividualRecallRemovedFinder(raceLog);
|
||||
|
||||
this.raceLogListener = new RaceLogChangedVisitor(this);
|
||||
this.raceLog.addListener(raceLogListener);
|
||||
@@ -126,17 +129,34 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
|
||||
public boolean isIndividualRecallDisplayed() {
|
||||
return cachedIsIndividualRecallDisplayed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isIndividualRecallDisplayed(TimePoint at) {
|
||||
if (hasIndividualRecall()) {
|
||||
return new IsIndividualRecallDisplayedAnalyzer(getRaceLog(), at).analyze();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getIndividualRecallDisplayedTime() {
|
||||
if (hasIndividualRecall()) {
|
||||
return recallDisplayedFinder.analyze();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TimePoint getIndividualRecallRemovalTime() {
|
||||
TimePoint displayed = getIndividualRecallDisplayedTime();
|
||||
if (hasIndividualRecall()) {
|
||||
if (isIndividualRecallDisplayed()) {
|
||||
TimePoint displayTime = recallDisplayedFinder.analyze();
|
||||
if (displayTime != null) {
|
||||
return displayTime.plus(individualRecallRemovalTimeout);
|
||||
}
|
||||
TimePoint removedEvent = recallRemovedFinder.analyze();
|
||||
if (removedEvent != null && (displayed == null || removedEvent.after(displayed))) {
|
||||
return removedEvent;
|
||||
}
|
||||
|
||||
}
|
||||
if (displayed != null) {
|
||||
return displayed.plus(individualRecallRemovalTimeout);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -177,10 +197,6 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
|
||||
|
||||
protected void update() {
|
||||
boolean isRecallDisplayed = isRecallDisplayedAnalyzer.analyze();
|
||||
// always call listeners for changed flag, as this does not only affect recal, but also
|
||||
// changes start procedure, for which some text elements need to be updated
|
||||
// ({@link BaseRaceInfoFragmen#renderFlagChangesCountdown}
|
||||
changedListeners.onActiveFlagsChanged(this);
|
||||
|
||||
if (cachedIsIndividualRecallDisplayed != isRecallDisplayed) {
|
||||
cachedIsIndividualRecallDisplayed = isRecallDisplayed;
|
||||
@@ -192,6 +208,11 @@ public abstract class BaseRacingProcedure extends BaseRaceStateChangedListener i
|
||||
unscheduleStateEvent(RaceStateEvents.INDIVIDUAL_RECALL_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
// always call listeners for changed flag, as this does not only affect recal, but also
|
||||
// changes start procedure, for which some text elements need to be updated
|
||||
// ({@link BaseRaceInfoFragmen#renderFlagChangesCountdown}
|
||||
changedListeners.onActiveFlagsChanged(this);
|
||||
}
|
||||
|
||||
private void rescheduleIndividualRecallTimeout(TimePoint displayTime) {
|
||||
|
||||
+12
-6
@@ -126,32 +126,38 @@ public class RRS26RacingProcedureImpl extends BaseRacingProcedure implements RRS
|
||||
getConfiguration().getClassFlag() : Flags.CLASS;
|
||||
if (now.before(startTime.minus(startPhaseClassUpInterval))) {
|
||||
return new FlagPoleState(
|
||||
Arrays.asList(new FlagPole(classFlag, false), new FlagPole(cachedStartmodeFlag, false)),
|
||||
Arrays.asList(new FlagPole(classFlag, false), new FlagPole(cachedStartmodeFlag, false)),
|
||||
null,
|
||||
Arrays.asList(new FlagPole(classFlag, true), new FlagPole(cachedStartmodeFlag, false)),
|
||||
startTime.minus(startPhaseClassUpInterval));
|
||||
} else if (now.before(startTime.minus(startPhaseStartModeUpInterval))) {
|
||||
return new FlagPoleState(
|
||||
Arrays.asList(new FlagPole(classFlag, true), new FlagPole(cachedStartmodeFlag, false)),
|
||||
Arrays.asList(new FlagPole(classFlag, true), new FlagPole(cachedStartmodeFlag, false)),
|
||||
startTime.minus(startPhaseClassUpInterval),
|
||||
Arrays.asList(new FlagPole(classFlag, true), new FlagPole(cachedStartmodeFlag, true)),
|
||||
startTime.minus(startPhaseStartModeUpInterval));
|
||||
} else if (now.before(startTime.minus(startPhaseStartModeDownInterval))) {
|
||||
return new FlagPoleState(
|
||||
Arrays.asList(new FlagPole(classFlag, true), new FlagPole(cachedStartmodeFlag, true)),
|
||||
Arrays.asList(new FlagPole(classFlag, true), new FlagPole(cachedStartmodeFlag, true)),
|
||||
startTime.minus(startPhaseStartModeUpInterval),
|
||||
Arrays.asList(new FlagPole(classFlag, true), new FlagPole(cachedStartmodeFlag, false)),
|
||||
startTime.minus(startPhaseStartModeDownInterval));
|
||||
} else if (now.before(startTime)) {
|
||||
return new FlagPoleState(
|
||||
Arrays.asList(new FlagPole(classFlag, true), new FlagPole(cachedStartmodeFlag, false)),
|
||||
Arrays.asList(new FlagPole(classFlag, true), new FlagPole(cachedStartmodeFlag, false)),
|
||||
startTime.minus(startPhaseStartModeDownInterval),
|
||||
Arrays.asList(new FlagPole(classFlag, false), new FlagPole(cachedStartmodeFlag, false)),
|
||||
startTime);
|
||||
} else {
|
||||
if (isIndividualRecallDisplayed()) {
|
||||
if (isIndividualRecallDisplayed(now)) {
|
||||
return new FlagPoleState(
|
||||
Arrays.asList(new FlagPole(Flags.XRAY, true)),
|
||||
getIndividualRecallDisplayedTime(),
|
||||
Arrays.asList(new FlagPole(Flags.XRAY, false)),
|
||||
getIndividualRecallRemovalTime());
|
||||
} else {
|
||||
return new FlagPoleState(Collections.<FlagPole>emptyList());
|
||||
TimePoint recallRemoved = getIndividualRecallRemovalTime();
|
||||
return new FlagPoleState(Collections.<FlagPole>emptyList(), recallRemoved == null ? startTime : recallRemoved);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -117,7 +117,7 @@ public class LeaderboardCourseChangeTest {
|
||||
|
||||
DomainFactory baseDomainFactory = new DomainFactoryImpl();
|
||||
LeaderboardDTO leaderboardDTO = leaderboard.getLeaderboardDTO(timePoint, raceColumnNames, false,
|
||||
trackedRegattaRegistry, baseDomainFactory);
|
||||
trackedRegattaRegistry, baseDomainFactory, /* fillNetPointsUncorrected */ false);
|
||||
|
||||
assertEquals(6, leaderboardDTO.rows.values().iterator().next().fieldsByRaceColumnName.values().iterator()
|
||||
.next().legDetails.size());
|
||||
@@ -126,7 +126,7 @@ public class LeaderboardCourseChangeTest {
|
||||
course.removeWaypoint(3);
|
||||
|
||||
leaderboardDTO = leaderboard.getLeaderboardDTO(timePoint, raceColumnNames, false, trackedRegattaRegistry,
|
||||
baseDomainFactory);
|
||||
baseDomainFactory, /* fillNetPointsUncorrected */ false);
|
||||
assertEquals(4, leaderboardDTO.rows.values().iterator().next().fieldsByRaceColumnName.values().iterator()
|
||||
.next().legDetails.size());
|
||||
|
||||
|
||||
+6
-6
@@ -147,17 +147,17 @@ public class LeaderboardOfflineTest extends AbstractLeaderboardTest {
|
||||
InterruptedException {
|
||||
}
|
||||
};
|
||||
LeaderboardDTO leaderboardDTO = leaderboard.getLeaderboardDTO(now, emptySet, /* addOverallDetails */ true, trackedRegattaRegistry, DomainFactory.INSTANCE);
|
||||
LeaderboardDTO leaderboardDTO = leaderboard.getLeaderboardDTO(now, emptySet, /* addOverallDetails */ true, trackedRegattaRegistry, DomainFactory.INSTANCE, /* fillNetPointsUncorrected */ false);
|
||||
assertNotNull(leaderboardDTO);
|
||||
assertSame(leaderboardDTO, leaderboard.getLeaderboardDTO(now, emptySet, /* addOverallDetails */ true, trackedRegattaRegistry, DomainFactory.INSTANCE)); // assert it's cached
|
||||
assertSame(leaderboardDTO, leaderboard.getLeaderboardDTO(now, emptySet, /* addOverallDetails */ true, trackedRegattaRegistry, DomainFactory.INSTANCE, /* fillNetPointsUncorrected */ false)); // assert it's cached
|
||||
leaderboard.getRaceColumnByName(columnName).releaseTrackedRace(defaultFleet); // this should clear the cache
|
||||
LeaderboardDTO leaderboardDTO2 = leaderboard.getLeaderboardDTO(now, emptySet, /* addOverallDetails */ true, trackedRegattaRegistry, DomainFactory.INSTANCE);
|
||||
LeaderboardDTO leaderboardDTO2 = leaderboard.getLeaderboardDTO(now, emptySet, /* addOverallDetails */ true, trackedRegattaRegistry, DomainFactory.INSTANCE, /* fillNetPointsUncorrected */ false);
|
||||
assertNotSame(leaderboardDTO, leaderboardDTO2);
|
||||
assertSame(leaderboardDTO2, leaderboard.getLeaderboardDTO(now, emptySet, /* addOverallDetails */ true, trackedRegattaRegistry, DomainFactory.INSTANCE)); // and cached again
|
||||
assertSame(leaderboardDTO2, leaderboard.getLeaderboardDTO(now, emptySet, /* addOverallDetails */ true, trackedRegattaRegistry, DomainFactory.INSTANCE, /* fillNetPointsUncorrected */ false)); // and cached again
|
||||
leaderboard.getRaceColumnByName(columnName).setTrackedRace(defaultFleet, testRaces.iterator().next()); // clear cache again; requires listener(s) to still be attached
|
||||
LeaderboardDTO leaderboardDTO3 = leaderboard.getLeaderboardDTO(now, emptySet, /* addOverallDetails */ true, trackedRegattaRegistry, DomainFactory.INSTANCE);
|
||||
LeaderboardDTO leaderboardDTO3 = leaderboard.getLeaderboardDTO(now, emptySet, /* addOverallDetails */ true, trackedRegattaRegistry, DomainFactory.INSTANCE, /* fillNetPointsUncorrected */ false);
|
||||
assertNotSame(leaderboardDTO2, leaderboardDTO3);
|
||||
assertSame(leaderboardDTO3, leaderboard.getLeaderboardDTO(now, emptySet, /* addOverallDetails */ true, trackedRegattaRegistry, DomainFactory.INSTANCE)); // and cached again
|
||||
assertSame(leaderboardDTO3, leaderboard.getLeaderboardDTO(now, emptySet, /* addOverallDetails */ true, trackedRegattaRegistry, DomainFactory.INSTANCE, /* fillNetPointsUncorrected */ false)); // and cached again
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
+22
@@ -28,6 +28,7 @@ import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.RegattaIdentifier;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
@@ -54,6 +55,7 @@ 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.WindStore;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
@@ -539,6 +541,12 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tack getTack(SpeedWithBearing speedWithBearing, Wind wind, TimePoint timePoint) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Wind getDirectionFromStartToNextMark(TimePoint at) {
|
||||
@@ -679,6 +687,13 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint,
|
||||
boolean waitForLatestAnalyses, WindLegTypeAndLegBearingCache cache) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindTrack getOrCreateWindTrack(WindSource windSource) {
|
||||
// TODO Auto-generated method stub
|
||||
@@ -958,6 +973,13 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint timePoint,
|
||||
boolean waitForLatestAnalyses, WindLegTypeAndLegBearingCache cache) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint from, TimePoint to,
|
||||
boolean upwindOnly, boolean waitForLatestAnalysis) throws NoWindException {
|
||||
|
||||
+22
@@ -20,6 +20,7 @@ import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
@@ -42,6 +43,7 @@ import com.sap.sailing.domain.tracking.TrackedRaceStatus;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
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.WindStore;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
@@ -316,6 +318,12 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tack getTack(SpeedWithBearing speedWithBearing, Wind wind, TimePoint timePoint) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackedRegatta getTrackedRegatta() {
|
||||
@@ -416,6 +424,13 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint,
|
||||
boolean waitForLatestAnalyses, WindLegTypeAndLegBearingCache cache) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindStore getWindStore() {
|
||||
// TODO Auto-generated method stub
|
||||
@@ -605,6 +620,13 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint timePoint,
|
||||
boolean waitForLatestAnalyses, WindLegTypeAndLegBearingCache cache) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint from, TimePoint to,
|
||||
boolean upwindOnly, boolean waitForLatestAnalysis) throws NoWindException {
|
||||
|
||||
@@ -76,7 +76,8 @@ public class SeriesImpl extends RenamableImpl implements Series, RaceColumnListe
|
||||
for (Fleet fleet : fleets) {
|
||||
this.fleetsByName.put(fleet.getName(), fleet);
|
||||
}
|
||||
fleetsInAscendingOrder = new ArrayList<Fleet>(fleetsByName.values());
|
||||
fleetsInAscendingOrder = new ArrayList<Fleet>();
|
||||
Util.addAll(fleets, fleetsInAscendingOrder);
|
||||
Collections.sort(fleetsInAscendingOrder);
|
||||
List<RaceColumnInSeries> myRaceColumns = new ArrayList<RaceColumnInSeries>();
|
||||
this.raceColumns = myRaceColumns;
|
||||
|
||||
@@ -73,7 +73,7 @@ public interface Leaderboard extends LeaderboardBase {
|
||||
LeaderboardDTO computeDTO(final TimePoint timePoint,
|
||||
final Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails, boolean addOverallDetails,
|
||||
final boolean waitForLatestAnalyses, TrackedRegattaRegistry trackedRegattaRegistry,
|
||||
DomainFactory baseDomainFactory) throws NoWindException;
|
||||
DomainFactory baseDomainFactory, boolean fillNetPointsUncorrected) throws NoWindException;
|
||||
|
||||
/**
|
||||
* Obtains the unique set of {@link Competitor} objects from all {@link TrackedRace}s currently linked to this
|
||||
@@ -480,10 +480,11 @@ public interface Leaderboard extends LeaderboardBase {
|
||||
* used to determine which of the races are still being tracked and which ones are not
|
||||
* @param baseDomainFactory
|
||||
* required as factory and cache for various DTO types
|
||||
* @param fillNetPointsUncorrected TODO
|
||||
*/
|
||||
LeaderboardDTO getLeaderboardDTO(TimePoint timePoint,
|
||||
Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails,
|
||||
boolean addOverallDetails, TrackedRegattaRegistry trackedRegattaRegistry, DomainFactory baseDomainFactory) throws NoWindException,
|
||||
boolean addOverallDetails, TrackedRegattaRegistry trackedRegattaRegistry, DomainFactory baseDomainFactory, boolean fillNetPointsUncorrected) throws NoWindException,
|
||||
InterruptedException, ExecutionException;
|
||||
|
||||
NumberOfCompetitorsInLeaderboardFetcher getNumberOfCompetitorsInLeaderboardFetcher();
|
||||
|
||||
+2
-3
@@ -88,7 +88,7 @@ public class LeaderboardDTOCache implements LeaderboardCache {
|
||||
private static final long serialVersionUID = 7287916997229815039L;
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Map.Entry<Util.Triple<TimePoint, Collection<String>, Boolean>, FutureTask<LeaderboardDTO>> e) {
|
||||
return size() > 10; // remember 10 LeaderboardDTOs per leaderborad
|
||||
return size() > 10; // remember 10 LeaderboardDTOs per leaderboard
|
||||
}
|
||||
};
|
||||
leaderboardCacheLock = new NamedReentrantReadWriteLock("leaderboardCacheLock for "+leaderboard.getName(), /* fair */ false);
|
||||
@@ -119,7 +119,6 @@ public class LeaderboardDTOCache implements LeaderboardCache {
|
||||
* The {@link #waitForLatestAnalyses} field is passed on to
|
||||
* {@link SailingServiceImpl#computeLeaderboardByName(Leaderboard, TimePoint, Collection, boolean)} if a new cache
|
||||
* entry needs to be computed. Caching distinguished between
|
||||
*
|
||||
* @param addOverallDetails
|
||||
* tells whether the columns containing the overall details shall be added
|
||||
*/
|
||||
@@ -167,7 +166,7 @@ public class LeaderboardDTOCache implements LeaderboardCache {
|
||||
try {
|
||||
LeaderboardDTO result = leaderboard.computeDTO(adjustedTimePoint,
|
||||
namesOfRaceColumnsForWhichToLoadLegDetails, addOverallDetails,
|
||||
waitForLatestAnalyses, trackedRegattaRegistry, baseDomainFactory);
|
||||
waitForLatestAnalyses, trackedRegattaRegistry, baseDomainFactory, /* fillNetPointsUncorrected */ false);
|
||||
return result;
|
||||
} finally {
|
||||
LockUtil.unpropagateLockSetFrom(callerThread);
|
||||
|
||||
+107
@@ -0,0 +1,107 @@
|
||||
package com.sap.sailing.domain.leaderboard.caching;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Leg;
|
||||
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.TimePoint;
|
||||
import com.sap.sailing.domain.leaderboard.impl.AbstractSimpleLeaderboardImpl;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Triple;
|
||||
|
||||
/**
|
||||
* A cache structure that is used for a single call to
|
||||
* {@link AbstractSimpleLeaderboardImpl#computeDTO(com.sap.sailing.domain.common.TimePoint, java.util.Collection, boolean, boolean, com.sap.sailing.domain.tracking.TrackedRegattaRegistry, com.sap.sailing.domain.base.DomainFactory)}.
|
||||
* It is to be passed on to various query methods that may required common data that is expensive to compute and
|
||||
* depends on equal parameters, such as an equal time point. The underlying assumption is that during one leaderboard (re-)calculation cycle the
|
||||
* dynamic changes in the wind field and the mark positions can safely be ignored so that wind data for competitors and legs and the legs'
|
||||
* bearings only need to be calculated once.
|
||||
* <p>
|
||||
*
|
||||
* This cache is equipped with the references necessary to compute the information if need be. The cache is thread safe. Note that it wouldn't be
|
||||
* a good idea to use {@link ThreadLocal}s for this because there is a lot of concurrency involved, and the {@link ThreadLocal} would have to be set
|
||||
* on each thread involved to be helpful which seems too complicated.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public class LeaderboardDTOCalculationReuseCache implements WindLegTypeAndLegBearingCache {
|
||||
/**
|
||||
* The reference time point for all queries to which values will be cached here. Queries that don't match this time point are
|
||||
* neither cached nor looked up in the cache.
|
||||
*/
|
||||
private final TimePoint timePoint;
|
||||
|
||||
/**
|
||||
* the leg types at "timePoint"
|
||||
*/
|
||||
final ConcurrentHashMap<Leg, LegType> legTypeCache;
|
||||
|
||||
|
||||
/**
|
||||
* the wind at competitor's position at timePoint
|
||||
*/
|
||||
final ConcurrentHashMap<Triple<TrackedRace, Competitor, TimePoint>, Wind> windCache;
|
||||
|
||||
/**
|
||||
* the leg's bearing at timePoint
|
||||
*/
|
||||
final ConcurrentHashMap<Leg, Bearing> legBearingCache;
|
||||
|
||||
public LeaderboardDTOCalculationReuseCache(TimePoint timePoint) {
|
||||
legTypeCache = new ConcurrentHashMap<>();
|
||||
windCache = new ConcurrentHashMap<>();
|
||||
legBearingCache = new ConcurrentHashMap<>();
|
||||
this.timePoint = timePoint;
|
||||
}
|
||||
|
||||
public LegType getLegType(TrackedLeg trackedLeg, TimePoint timePoint) throws NoWindException {
|
||||
LegType result;
|
||||
if (Util.equalsWithNull(this.timePoint, timePoint)) {
|
||||
result = legTypeCache.get(trackedLeg.getLeg());
|
||||
if (result == null) {
|
||||
result = trackedLeg.getLegType(timePoint);
|
||||
legTypeCache.put(trackedLeg.getLeg(), result);
|
||||
}
|
||||
} else {
|
||||
result = trackedLeg.getLegType(timePoint); // different time point; don't cache
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Bearing getLegBearing(TrackedLeg trackedLeg, TimePoint timePoint) {
|
||||
Bearing result;
|
||||
if (Util.equalsWithNull(this.timePoint, timePoint)) {
|
||||
result = legBearingCache.get(trackedLeg.getLeg());
|
||||
if (result == null) {
|
||||
result = trackedLeg.getLegBearing(timePoint);
|
||||
legBearingCache.put(trackedLeg.getLeg(), result);
|
||||
}
|
||||
} else {
|
||||
result = trackedLeg.getLegBearing(timePoint); // different time point; don't cache
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines the wind at the <code>competitor</code>'s {@link GPSFixTrack#getEstimatedPosition(TimePoint, boolean) estimated position} at
|
||||
* <code>timePoint</code>. The result is cached for subsequent calls with equal parameters.
|
||||
*/
|
||||
public Wind getWind(TrackedRace trackedRace, Competitor competitor, TimePoint timePoint) {
|
||||
Triple<TrackedRace, Competitor, TimePoint> cacheKey = new Triple<>(trackedRace, competitor, timePoint);
|
||||
Wind result = windCache.get(cacheKey);
|
||||
if (result == null) {
|
||||
result = trackedRace.getWind(trackedRace.getTrack(competitor).getEstimatedPosition(timePoint, false), timePoint);
|
||||
windCache.put(cacheKey, result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+4
-3
@@ -188,7 +188,8 @@ public class LiveLeaderboardUpdater implements Runnable {
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.info(""+LiveLeaderboardUpdater.class.getSimpleName()+" cache hits/misses: "+cacheHitCount+"/"+cacheMissCount);
|
||||
logger.info(""+LiveLeaderboardUpdater.class.getSimpleName()+" for "+getLeaderboard().getName()+
|
||||
" cache hits/misses: "+cacheHitCount+"/"+cacheMissCount);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -217,7 +218,7 @@ public class LiveLeaderboardUpdater implements Runnable {
|
||||
* <code>result</code> has the details.
|
||||
*
|
||||
* @param addOverallDetails tells whether overall details are requested for the resulting leaderboard; see also
|
||||
* {@link Leaderboard#computeDTO(TimePoint, Collection, boolean, boolean, TrackedRegattaRegistry, DomainFactory)}.
|
||||
* {@link Leaderboard#computeDTO(TimePoint, Collection, boolean, boolean, TrackedRegattaRegistry, DomainFactory, boolean)}.
|
||||
*/
|
||||
private synchronized void updateRequestTimes(Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails, boolean addOverallDetails) {
|
||||
lastRequest = getLeaderboard().getNowMinusDelay();
|
||||
@@ -275,7 +276,7 @@ public class LiveLeaderboardUpdater implements Runnable {
|
||||
final boolean addOverallDetails = getOverallDetails(timePoint);
|
||||
LeaderboardDTO newCacheValue = leaderboard.computeDTO(timePoint,
|
||||
namesOfRaceColumnsForWhichToLoadLegDetails, addOverallDetails,
|
||||
/* waitForLatestAnalyses */false, trackedRegattaRegistry, baseDomainFactory);
|
||||
/* waitForLatestAnalyses */false, trackedRegattaRegistry, baseDomainFactory, /* fillNetPointsUncorrected */ false);
|
||||
updateCacheContents(namesOfRaceColumnsForWhichToLoadLegDetails, addOverallDetails, newCacheValue);
|
||||
} catch (NoWindException e) {
|
||||
logger.info("Unable to update cached leaderboard results for leaderboard " + leaderboard.getName() + ": "
|
||||
|
||||
+65
-36
@@ -66,6 +66,7 @@ import com.sap.sailing.domain.leaderboard.ScoreCorrection.Result;
|
||||
import com.sap.sailing.domain.leaderboard.SettableScoreCorrection;
|
||||
import com.sap.sailing.domain.leaderboard.ThresholdBasedResultDiscardingRule;
|
||||
import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCache;
|
||||
import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache;
|
||||
import com.sap.sailing.domain.leaderboard.caching.LiveLeaderboardUpdater;
|
||||
import com.sap.sailing.domain.racelog.RaceLogEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogIdentifier;
|
||||
@@ -83,6 +84,7 @@ import com.sap.sailing.domain.tracking.TrackedRaceStatus;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegattaRegistry;
|
||||
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.util.impl.LockUtil;
|
||||
import com.sap.sailing.util.impl.NamedReentrantReadWriteLock;
|
||||
import com.sap.sailing.util.impl.RaceColumnListeners;
|
||||
@@ -164,19 +166,20 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
class EntryImpl implements Entry {
|
||||
private final Callable<Integer> trackedRankProvider;
|
||||
private final Double netPoints;
|
||||
private final Double netPointsUncorrected;
|
||||
private final Callable<Double> netPointsUncorrectedProvider;
|
||||
private final boolean isNetPointsCorrected;
|
||||
private final Double totalPoints;
|
||||
private final MaxPointsReason maxPointsReason;
|
||||
private final boolean discarded;
|
||||
private final Fleet fleet;
|
||||
|
||||
private EntryImpl(Callable<Integer> trackedRankProvider, Double netPoints, Double netPointsUncorrected, boolean isNetPointsCorrected,
|
||||
private EntryImpl(Callable<Integer> trackedRankProvider, Double netPoints,
|
||||
Callable<Double> netPointsUncorrectedProvider, boolean isNetPointsCorrected,
|
||||
Double totalPoints, MaxPointsReason maxPointsReason, boolean discarded, Fleet fleet) {
|
||||
super();
|
||||
this.trackedRankProvider = trackedRankProvider;
|
||||
this.netPoints = netPoints;
|
||||
this.netPointsUncorrected = netPointsUncorrected;
|
||||
this.netPointsUncorrectedProvider = netPointsUncorrectedProvider;
|
||||
this.isNetPointsCorrected = isNetPointsCorrected;
|
||||
this.totalPoints = totalPoints;
|
||||
this.maxPointsReason = maxPointsReason;
|
||||
@@ -217,7 +220,11 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
}
|
||||
@Override
|
||||
public Double getNetPointsUncorrected() throws NoWindException {
|
||||
return netPointsUncorrected;
|
||||
try {
|
||||
return netPointsUncorrectedProvider.call();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -690,7 +697,8 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
timePoint, new NumberOfCompetitorsFetcherImpl(), getScoringScheme());
|
||||
boolean discarded = isDiscarded(competitor, race, timePoint);
|
||||
final Double correctedScore = correctedResults.getCorrectedScore();
|
||||
return new EntryImpl(trackedRankProvider, correctedScore, correctedResults.getUncorrectedScore(),
|
||||
return new EntryImpl(trackedRankProvider, correctedScore,
|
||||
new Callable<Double>() { @Override public Double call() { return correctedResults.getUncorrectedScore(); } },
|
||||
correctedResults.isCorrected(),
|
||||
discarded ? DOUBLE_0
|
||||
: correctedScore == null ? null : Double.valueOf(correctedScore * race.getFactor()), correctedResults.getMaxPointsReason(), discarded, race.getFleetOfCompetitor(competitor));
|
||||
@@ -759,7 +767,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
return getTrackedRank(competitor, raceColumn, timePoint);
|
||||
}
|
||||
};
|
||||
Result correctedResults = getScoreCorrection().getCorrectedScore(trackedRankProvider, competitor, raceColumn,
|
||||
final Result correctedResults = getScoreCorrection().getCorrectedScore(trackedRankProvider, competitor, raceColumn,
|
||||
timePoint, new NumberOfCompetitorsFetcherImpl(), getScoringScheme());
|
||||
Set<RaceColumn> discardedRacesForCompetitor = discardedRaces.get(competitor);
|
||||
if (discardedRacesForCompetitor == null) {
|
||||
@@ -769,7 +777,8 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
boolean discarded = discardedRacesForCompetitor.contains(raceColumn);
|
||||
final Double correctedScore = correctedResults.getCorrectedScore();
|
||||
Entry entry = new EntryImpl(trackedRankProvider, correctedScore,
|
||||
correctedResults.getUncorrectedScore(), correctedResults.isCorrected(),
|
||||
new Callable<Double>() { @Override public Double call() { return correctedResults.getUncorrectedScore(); } },
|
||||
correctedResults.isCorrected(),
|
||||
discarded ? DOUBLE_0 : (correctedScore==null?null:
|
||||
Double.valueOf((correctedScore * raceColumn.getFactor()))), correctedResults.getMaxPointsReason(),
|
||||
discarded, raceColumn.getFleetOfCompetitor(competitor));
|
||||
@@ -1159,9 +1168,11 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
@Override
|
||||
public LeaderboardDTO computeDTO(final TimePoint timePoint,
|
||||
final Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails, boolean addOverallDetails,
|
||||
final boolean waitForLatestAnalyses, TrackedRegattaRegistry trackedRegattaRegistry, final DomainFactory baseDomainFactory)
|
||||
final boolean waitForLatestAnalyses, TrackedRegattaRegistry trackedRegattaRegistry, final DomainFactory baseDomainFactory,
|
||||
final boolean fillNetPointsUncorrected)
|
||||
throws NoWindException {
|
||||
long startOfRequestHandling = System.currentTimeMillis();
|
||||
final LeaderboardDTOCalculationReuseCache cache = new LeaderboardDTOCalculationReuseCache(timePoint);
|
||||
final LeaderboardDTO result = new LeaderboardDTO(this.getScoreCorrection().getTimePointOfLastCorrectionsValidity() == null ? null
|
||||
: this.getScoreCorrection().getTimePointOfLastCorrectionsValidity().asDate(),
|
||||
this.getScoreCorrection() == null ? null : this.getScoreCorrection().getComment(), this
|
||||
@@ -1273,7 +1284,8 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
return getLeaderboardEntryDTO(entry, raceColumn, competitor, timePoint,
|
||||
namesOfRaceColumnsForWhichToLoadLegDetails != null
|
||||
&& namesOfRaceColumnsForWhichToLoadLegDetails.contains(raceColumn
|
||||
.getName()), waitForLatestAnalyses, legRanksCache, baseDomainFactory);
|
||||
.getName()), waitForLatestAnalyses, legRanksCache, baseDomainFactory,
|
||||
fillNetPointsUncorrected, cache);
|
||||
} catch (NoWindException e) {
|
||||
logger.info("Exception trying to compute leaderboard entry for competitor "
|
||||
+ competitor.getName() + " in race column " + raceColumn.getName() + ": "
|
||||
@@ -1345,15 +1357,24 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
* if <code>false</code>, this method is allowed to read the maneuver analysis results from a cache that
|
||||
* may not reflect all data already received; otherwise, the method will always block for the latest
|
||||
* cache updates to have happened before returning.
|
||||
* @param fillNetPointsUncorrected
|
||||
* tells if {@link LeaderboardEntryDTO#netPointsUncorrected} shall be filled; filling it is rather
|
||||
* expensive, especially when compared to simply retrieving a score correction, and particularly if in a
|
||||
* larger fleet a number of competitors haven't properly finished the race. This should only be used for
|
||||
* leaderboard editing where a user needs to see what the uncorrected score was that would be used when
|
||||
* the correction was removed.
|
||||
*/
|
||||
private LeaderboardEntryDTO getLeaderboardEntryDTO(Entry entry, RaceColumn raceColumn, Competitor competitor,
|
||||
TimePoint timePoint, boolean addLegDetails, boolean waitForLatestAnalyses,
|
||||
Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache, DomainFactory baseDomainFactory) throws NoWindException {
|
||||
Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache, DomainFactory baseDomainFactory,
|
||||
boolean fillNetPointsUncorrected, LeaderboardDTOCalculationReuseCache cache) throws NoWindException {
|
||||
LeaderboardEntryDTO entryDTO = new LeaderboardEntryDTO();
|
||||
TrackedRace trackedRace = raceColumn.getTrackedRace(competitor);
|
||||
entryDTO.race = trackedRace == null ? null : trackedRace.getRaceIdentifier();
|
||||
entryDTO.netPoints = entry.getNetPoints();
|
||||
entryDTO.netPointsUncorrected = entry.getNetPointsUncorrected();
|
||||
if (fillNetPointsUncorrected) {
|
||||
entryDTO.netPointsUncorrected = entry.getNetPointsUncorrected();
|
||||
}
|
||||
entryDTO.netPointsCorrected = entry.isNetPointsCorrected();
|
||||
entryDTO.totalPoints = entry.getTotalPoints();
|
||||
entryDTO.reasonForMaxPoints = entry.getMaxPointsReason();
|
||||
@@ -1373,7 +1394,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
}
|
||||
if (addLegDetails && trackedRace != null) {
|
||||
try {
|
||||
RaceDetails raceDetails = getRaceDetails(trackedRace, competitor, timePoint, waitForLatestAnalyses, legRanksCache);
|
||||
RaceDetails raceDetails = getRaceDetails(trackedRace, competitor, timePoint, waitForLatestAnalyses, legRanksCache, cache);
|
||||
entryDTO.legDetails = raceDetails.getLegDetails();
|
||||
entryDTO.windwardDistanceToOverallLeaderInMeters = raceDetails.getWindwardDistanceToOverallLeader() == null ? null
|
||||
: raceDetails.getWindwardDistanceToOverallLeader().getMeters();
|
||||
@@ -1492,19 +1513,20 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
* cache updates to have happened before returning.
|
||||
*/
|
||||
private RaceDetails getRaceDetails(TrackedRace trackedRace, Competitor competitor, TimePoint timePoint,
|
||||
boolean waitForLatestAnalyses, Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache) throws NoWindException, InterruptedException, ExecutionException {
|
||||
RaceDetails raceDetails;
|
||||
boolean waitForLatestAnalyses, Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache,
|
||||
LeaderboardDTOCalculationReuseCache cache) throws NoWindException, InterruptedException, ExecutionException {
|
||||
final RaceDetails raceDetails;
|
||||
if (trackedRace.getEndOfTracking() != null && trackedRace.getEndOfTracking().compareTo(timePoint) < 0) {
|
||||
raceDetails = getRaceDetailsForEndOfTrackingFromCacheOrCalculateAndCache(trackedRace, competitor, legRanksCache);
|
||||
raceDetails = getRaceDetailsForEndOfTrackingFromCacheOrCalculateAndCache(trackedRace, competitor, legRanksCache, cache);
|
||||
} else {
|
||||
raceDetails = calculateRaceDetails(trackedRace, competitor, timePoint, waitForLatestAnalyses, legRanksCache);
|
||||
raceDetails = calculateRaceDetails(trackedRace, competitor, timePoint, waitForLatestAnalyses, legRanksCache, cache);
|
||||
}
|
||||
return raceDetails;
|
||||
}
|
||||
|
||||
private RaceDetails getRaceDetailsForEndOfTrackingFromCacheOrCalculateAndCache(final TrackedRace trackedRace,
|
||||
final Competitor competitor, final Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache)
|
||||
throws NoWindException, InterruptedException, ExecutionException {
|
||||
final Competitor competitor, final Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache,
|
||||
final LeaderboardDTOCalculationReuseCache cache) throws NoWindException, InterruptedException, ExecutionException {
|
||||
final com.sap.sse.common.Util.Pair<TrackedRace, Competitor> key = new com.sap.sse.common.Util.Pair<TrackedRace, Competitor>(trackedRace, competitor);
|
||||
RunnableFuture<RaceDetails> raceDetails;
|
||||
synchronized (raceDetailsAtEndOfTrackingCache) {
|
||||
@@ -1521,7 +1543,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
// TODO see bug 1358: for now, use waitForLatest==false until we've switched to optimistic locking for the course read lock
|
||||
/* TODO old comment when it was still true: "because this is done only once after end of tracking" */
|
||||
/* waitForLatestAnalyses (maneuver and cross track error) */ false,
|
||||
legRanksCache);
|
||||
legRanksCache, cache);
|
||||
}
|
||||
});
|
||||
raceDetailsExecutor.execute(raceDetails);
|
||||
@@ -1535,9 +1557,9 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
}
|
||||
|
||||
private RaceDetails calculateRaceDetails(TrackedRace trackedRace, Competitor competitor, TimePoint timePoint,
|
||||
boolean waitForLatestAnalyses, Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache) throws NoWindException {
|
||||
List<LegEntryDTO> legDetails;
|
||||
legDetails = new ArrayList<LegEntryDTO>();
|
||||
boolean waitForLatestAnalyses, Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache,
|
||||
LeaderboardDTOCalculationReuseCache cache) throws NoWindException {
|
||||
final List<LegEntryDTO> legDetails = new ArrayList<LegEntryDTO>();
|
||||
final Course course = trackedRace.getRace().getCourse();
|
||||
course.lockForRead(); // hold back any course re-configurations while looping over the legs
|
||||
try {
|
||||
@@ -1548,7 +1570,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
// immediately. That's why we've acquired a read lock for the course above.
|
||||
TrackedLegOfCompetitor trackedLeg = trackedRace.getTrackedLeg(competitor, leg);
|
||||
if (trackedLeg != null && trackedLeg.hasStartedLeg(timePoint)) {
|
||||
legEntry = createLegEntry(trackedLeg, timePoint, waitForLatestAnalyses, legRanksCache);
|
||||
legEntry = createLegEntry(trackedLeg, timePoint, waitForLatestAnalyses, legRanksCache, cache);
|
||||
} else {
|
||||
legEntry = null;
|
||||
}
|
||||
@@ -1557,9 +1579,9 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
final Distance windwardDistanceToOverallLeader = trackedRace == null ? null : trackedRace
|
||||
.getWindwardDistanceToOverallLeader(competitor, timePoint, WindPositionMode.LEG_MIDDLE);
|
||||
final Distance averageAbsoluteCrossTrackError = trackedRace == null ? null : trackedRace.getAverageAbsoluteCrossTrackError(
|
||||
competitor, timePoint, waitForLatestAnalyses);
|
||||
competitor, timePoint, waitForLatestAnalyses, cache);
|
||||
final Distance averageSignedCrossTrackError = trackedRace == null ? null : trackedRace.getAverageSignedCrossTrackError(
|
||||
competitor, timePoint, waitForLatestAnalyses);
|
||||
competitor, timePoint, waitForLatestAnalyses, cache);
|
||||
return new RaceDetails(legDetails, windwardDistanceToOverallLeader, averageAbsoluteCrossTrackError, averageSignedCrossTrackError);
|
||||
} finally {
|
||||
course.unlockAfterRead();
|
||||
@@ -1567,7 +1589,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
}
|
||||
|
||||
private LegEntryDTO createLegEntry(TrackedLegOfCompetitor trackedLeg, TimePoint timePoint,
|
||||
boolean waitForLatestAnalyses, Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache) throws NoWindException {
|
||||
boolean waitForLatestAnalyses, Map<Leg, LinkedHashMap<Competitor, Integer>> legRanksCache, LeaderboardDTOCalculationReuseCache cache) throws NoWindException {
|
||||
LegEntryDTO result;
|
||||
if (trackedLeg == null || trackedLeg.getTime(timePoint) == null) {
|
||||
result = null;
|
||||
@@ -1599,7 +1621,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
// FIXME problem: asking just after the beginning of the leg yields very different values from asking for the end of the previous leg.
|
||||
// This is because for the previous leg it's decided based on the mark passings; for the next (current) leg it's decided based on
|
||||
// windward distance and VMG
|
||||
Double gapAtEndOfPreviousLegInSeconds = getGapAtEndOfPreviousLegInSeconds(trackedLeg);
|
||||
Double gapAtEndOfPreviousLegInSeconds = getGapAtEndOfPreviousLegInSeconds(trackedLeg, cache);
|
||||
if (gapAtEndOfPreviousLegInSeconds != null) {
|
||||
result.gapChangeSinceLegStartInSeconds = result.gapToLeaderInSeconds - gapAtEndOfPreviousLegInSeconds;
|
||||
}
|
||||
@@ -1684,7 +1706,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
return result;
|
||||
}
|
||||
|
||||
private Double getGapAtEndOfPreviousLegInSeconds(TrackedLegOfCompetitor trackedLeg) throws NoWindException {
|
||||
private Double getGapAtEndOfPreviousLegInSeconds(TrackedLegOfCompetitor trackedLeg, WindLegTypeAndLegBearingCache cache) throws NoWindException {
|
||||
final Double result;
|
||||
final Course course = trackedLeg.getTrackedLeg().getTrackedRace().getRace().getCourse();
|
||||
course.lockForRead();
|
||||
@@ -1715,7 +1737,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
} else {
|
||||
TrackedLeg previousTrackedLeg = trackedLeg.getTrackedLeg().getTrackedRace().getTrackedLeg(course.getLegs().get(indexOfStartWaypoint-1));
|
||||
TrackedLegOfCompetitor previousTrackedLegOfCompetitor = previousTrackedLeg.getTrackedLeg(trackedLeg.getCompetitor());
|
||||
result = previousTrackedLegOfCompetitor.getGapToLeaderInSeconds(previousTrackedLegOfCompetitor.getFinishTime(), WindPositionMode.LEG_MIDDLE);
|
||||
result = previousTrackedLegOfCompetitor.getGapToLeaderInSeconds(previousTrackedLegOfCompetitor.getFinishTime(), WindPositionMode.LEG_MIDDLE, cache);
|
||||
}
|
||||
return result;
|
||||
} finally {
|
||||
@@ -1766,7 +1788,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
@Override
|
||||
public LeaderboardDTO getLeaderboardDTO(TimePoint timePoint,
|
||||
Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails, boolean addOverallDetails,
|
||||
TrackedRegattaRegistry trackedRegattaRegistry, DomainFactory baseDomainFactory) throws NoWindException,
|
||||
TrackedRegattaRegistry trackedRegattaRegistry, DomainFactory baseDomainFactory, boolean fillNetPointsUncorrected) throws NoWindException,
|
||||
InterruptedException, ExecutionException {
|
||||
LeaderboardDTO result = null;
|
||||
if (timePoint == null) {
|
||||
@@ -1776,7 +1798,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
// when the tracked race associations or score corrections or tracked race contents changes:
|
||||
final TimePoint nowMinusDelay = this.getNowMinusDelay();
|
||||
final TimePoint timePointOfLatestModification = this.getTimePointOfLatestModification();
|
||||
if (timePointOfLatestModification != null && !nowMinusDelay.before(timePointOfLatestModification)) {
|
||||
if (fillNetPointsUncorrected || (timePointOfLatestModification != null && !nowMinusDelay.before(timePointOfLatestModification))) {
|
||||
// if there hasn't been any modification to the leaderboard since nowMinusDelay, use non-live mode
|
||||
// and pull the result from the regular leaderboard cache:
|
||||
timePoint = timePointOfLatestModification;
|
||||
@@ -1787,11 +1809,18 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
}
|
||||
}
|
||||
if (timePoint != null) {
|
||||
// in replay we'd like up-to-date results; they are still cached
|
||||
// which is OK because the cache is invalidated whenever any of the tracked races attached to the
|
||||
// leaderboard changes.
|
||||
result = getLeaderboardDTOCache().getLeaderboardByName(timePoint, namesOfRaceColumnsForWhichToLoadLegDetails,
|
||||
addOverallDetails, baseDomainFactory, trackedRegattaRegistry);
|
||||
if (fillNetPointsUncorrected) {
|
||||
// explicitly filling the uncorrected net points requires uncached recalculation
|
||||
result = computeDTO(timePoint, namesOfRaceColumnsForWhichToLoadLegDetails, addOverallDetails, /* waitForLatestAnalyses */ true,
|
||||
trackedRegattaRegistry, baseDomainFactory, fillNetPointsUncorrected);
|
||||
} else {
|
||||
// in replay we'd like up-to-date results; they are still cached
|
||||
// which is OK because the cache is invalidated whenever any of the tracked races attached to the
|
||||
// leaderboard changes.
|
||||
result = getLeaderboardDTOCache().getLeaderboardByName(timePoint,
|
||||
namesOfRaceColumnsForWhichToLoadLegDetails, addOverallDetails, baseDomainFactory,
|
||||
trackedRegattaRegistry);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
+17
-17
@@ -337,9 +337,10 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
|
||||
* <p>
|
||||
*/
|
||||
@Override
|
||||
public Result getCorrectedScore(Callable<Integer> trackedRankProvider, final Competitor competitor,
|
||||
final RaceColumn raceColumn, final TimePoint timePoint, final NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher,
|
||||
ScoringScheme scoringScheme) {
|
||||
public Result getCorrectedScore(final Callable<Integer> trackedRankProvider, final Competitor competitor,
|
||||
final RaceColumn raceColumn, final TimePoint timePoint,
|
||||
final NumberOfCompetitorsInLeaderboardFetcher numberOfCompetitorsInLeaderboardFetcher,
|
||||
final ScoringScheme scoringScheme) {
|
||||
Double result;
|
||||
final MaxPointsReason maxPointsReason = getMaxPointsReason(competitor, raceColumn, timePoint);
|
||||
if (maxPointsReason == MaxPointsReason.NONE) {
|
||||
@@ -356,19 +357,6 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
|
||||
result = correctedNonMaxedScore;
|
||||
}
|
||||
}
|
||||
// also compute uncorrected score
|
||||
Double resultUncorrected = 0.0;
|
||||
try {
|
||||
resultUncorrected = scoringScheme.getScoreForRank(raceColumn, competitor, trackedRankProvider.call(), new Callable<Integer>() {
|
||||
@Override
|
||||
public Integer call() {
|
||||
return getNumberOfCompetitorsInRace(raceColumn, competitor, numberOfCompetitorsInLeaderboardFetcher);
|
||||
}
|
||||
}, numberOfCompetitorsInLeaderboardFetcher);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
final Double uncorrectedScore = resultUncorrected;
|
||||
final Double correctedScore = result;
|
||||
return new Result() {
|
||||
@Override
|
||||
@@ -393,7 +381,19 @@ public class ScoreCorrectionImpl implements SettableScoreCorrection {
|
||||
|
||||
@Override
|
||||
public Double getUncorrectedScore() {
|
||||
return uncorrectedScore;
|
||||
Double resultUncorrected = 0.0;
|
||||
try {
|
||||
resultUncorrected = scoringScheme.getScoreForRank(raceColumn, competitor,
|
||||
trackedRankProvider.call(), new Callable<Integer>() {
|
||||
@Override
|
||||
public Integer call() {
|
||||
return getNumberOfCompetitorsInRace(raceColumn, competitor, numberOfCompetitorsInLeaderboardFetcher);
|
||||
}
|
||||
}, numberOfCompetitorsInLeaderboardFetcher);
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
return resultUncorrected;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
package com.sap.sailing.domain.tracking;
|
||||
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
|
||||
/**
|
||||
* When comprehensive calculations are prone to query a leg's bearing several times for the same leg for
|
||||
* the same time point, a cache of this type can be used to eliminate repeated evaluation of common sub-expressions that
|
||||
* are expensive to calculate.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public interface LegBearingCache {
|
||||
Bearing getLegBearing(TrackedLeg trackedLeg, TimePoint timePoint);
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
package com.sap.sailing.domain.tracking;
|
||||
|
||||
import com.sap.sailing.domain.common.LegType;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
|
||||
/**
|
||||
* When comprehensive calculations are prone to query the leg type several times for the same leg for the same time
|
||||
* point, a cache of this type can be used to eliminate repeated evaluation of common sub-expressions that are expensive
|
||||
* to calculate.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public interface LegTypeCache {
|
||||
LegType getLegType(TrackedLeg trackedLeg, TimePoint timePoint) throws NoWindException;
|
||||
}
|
||||
+24
@@ -126,6 +126,11 @@ public interface TrackedLegOfCompetitor extends Serializable {
|
||||
*/
|
||||
Double getGapToLeaderInSeconds(TimePoint timePoint, WindPositionMode windPositionMode) throws NoWindException;
|
||||
|
||||
/**
|
||||
* Same as {@link #getGapToLeaderInSeconds(TimePoint)}, only that a cache for wind and leg type data is used.
|
||||
*/
|
||||
Double getGapToLeaderInSeconds(TimePoint timePoint, WindLegTypeAndLegBearingCache cache) throws NoWindException;
|
||||
|
||||
/**
|
||||
* If a caller already went through the effort of computing the leg's leader at <code>timePoint</code>, it
|
||||
* can share this knowledge to speed up computation as compared to {@link #getGapToLeaderInSeconds(TimePoint, WindPositionMode)}.
|
||||
@@ -133,6 +138,13 @@ public interface TrackedLegOfCompetitor extends Serializable {
|
||||
*/
|
||||
Double getGapToLeaderInSeconds(TimePoint timePoint, Competitor leaderInLegAtTimePoint, WindPositionMode windPositionMode) throws NoWindException;
|
||||
|
||||
/**
|
||||
* Same as {@link #getGapToLeaderInSeconds(TimePoint, Competitor)}, only that an additional cache is used
|
||||
* to avoid redundant evaluations of leg types and wind field information across various calculations that
|
||||
* all can use the same basic information.
|
||||
*/
|
||||
Double getGapToLeaderInSeconds(TimePoint timePoint, Competitor leaderInLegAtTimePoint, WindLegTypeAndLegBearingCache cache) throws NoWindException;
|
||||
|
||||
boolean hasStartedLeg(TimePoint timePoint);
|
||||
|
||||
boolean hasFinishedLeg(TimePoint timePoint);
|
||||
@@ -213,4 +225,16 @@ public interface TrackedLegOfCompetitor extends Serializable {
|
||||
* Computes the angle between the competitors direction and the wind.
|
||||
*/
|
||||
Bearing getBeatAngle(TimePoint at) throws NoWindException;
|
||||
|
||||
/**
|
||||
* Same as {@link #getBeatAngle}, only that additionally a cache is provided that can allow the method to use
|
||||
* cached wind and leg type values.
|
||||
*/
|
||||
Bearing getBeatAngle(TimePoint at, WindLegTypeAndLegBearingCache cache) throws NoWindException;
|
||||
|
||||
/**
|
||||
* Like {@link #getAverageVelocityMadeGood(TimePoint)}, only with an additional cache argument that allows the method to
|
||||
* use already computed values for wind and leg type, potentially also updating the cache as it goes.
|
||||
*/
|
||||
Speed getAverageVelocityMadeGood(TimePoint timePoint, WindLegTypeAndLegBearingCache cache) throws NoWindException;
|
||||
}
|
||||
@@ -20,6 +20,7 @@ import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.TimingConstants;
|
||||
@@ -381,6 +382,17 @@ public interface TrackedRace extends Serializable {
|
||||
*/
|
||||
Tack getTack(Competitor competitor, TimePoint timePoint) throws NoWindException;
|
||||
|
||||
/**
|
||||
* Determines whether the <code>competitor</code> is sailing on port or starboard tack at the <code>timePoint</code>
|
||||
* requested.
|
||||
* <p>
|
||||
* This method outperforms {@link #getTack(Competitor, TimePoint)}, based on being passed an already calculated wind
|
||||
* for the given time and competitor position as well as the competitors speed and course over ground.
|
||||
* <p>
|
||||
* This method will acquire the read lock for the competitor's track.
|
||||
*/
|
||||
Tack getTack(SpeedWithBearing speedWithBearing, Wind wind, TimePoint timePoint);
|
||||
|
||||
TrackedRegatta getTrackedRegatta();
|
||||
|
||||
/**
|
||||
@@ -506,12 +518,26 @@ public interface TrackedRace extends Serializable {
|
||||
Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis)
|
||||
throws NoWindException;
|
||||
|
||||
/**
|
||||
* Same as {@link #getAverageAbsoluteCrossTrackError(Competitor, TimePoint, boolean)}, only that a cache for leg type,
|
||||
* wind on leg and leg bearing is provided.
|
||||
*/
|
||||
Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalyses,
|
||||
WindLegTypeAndLegBearingCache cache) throws NoWindException;
|
||||
|
||||
Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint from, TimePoint to, boolean upwindOnly,
|
||||
boolean waitForLatestAnalyses) throws NoWindException;
|
||||
|
||||
Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis)
|
||||
throws NoWindException;
|
||||
|
||||
/**
|
||||
* Same as {@link #getAverageSignedCrossTrackError(Competitor, TimePoint, boolean)}, only that a cache for leg type,
|
||||
* wind direction and leg bearing is provided.
|
||||
*/
|
||||
Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalyses,
|
||||
WindLegTypeAndLegBearingCache cache) throws NoWindException;
|
||||
|
||||
Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint from, TimePoint to, boolean upwindOnly,
|
||||
boolean waitForLatestAnalysis) throws NoWindException;
|
||||
|
||||
@@ -674,4 +700,5 @@ public interface TrackedRace extends Serializable {
|
||||
SpeedWithConfidence<TimePoint> getAverageWindSpeedWithConfidence(long resolutionInMillis);
|
||||
|
||||
GPSFixStore getGPSFixStore();
|
||||
|
||||
}
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
package com.sap.sailing.domain.tracking;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
|
||||
/**
|
||||
* When comprehensive calculations are prone to query wind several times for the same competitor in the same race for
|
||||
* the same time point, a cache of this type can be used to eliminate repeated evaluation of common sub-expressions that
|
||||
* are expensive to calculate.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public interface WindCache {
|
||||
/**
|
||||
* Estimates the <code>competitor</code>'s position at <code>timePoint</code> and determines the wind at that position and
|
||||
* at that time.
|
||||
*/
|
||||
Wind getWind(TrackedRace trackedRace, Competitor competitor, TimePoint timePoint);
|
||||
}
|
||||
Executable
+4
@@ -0,0 +1,4 @@
|
||||
package com.sap.sailing.domain.tracking;
|
||||
|
||||
public interface WindLegTypeAndLegBearingCache extends WindCache, LegTypeCache, LegBearingCache {
|
||||
}
|
||||
+11
-7
@@ -41,13 +41,17 @@ public class DynamicTrackedRaceLogListener extends BaseRaceLogEventVisitor {
|
||||
}
|
||||
|
||||
public void addTo(RaceLog raceLog) {
|
||||
raceLog.addListener(this);
|
||||
trackedRace.invalidateStartTime();
|
||||
trackedRace.invalidateEndTime();
|
||||
courseDesignFinder = new LastPublishedCourseDesignFinder(raceLog);
|
||||
startTimeFinder = new StartTimeFinder(raceLog);
|
||||
initializeWindTrack(raceLog);
|
||||
analyze();
|
||||
if (raceLog == null) {
|
||||
logger.severe("Trying to add "+this+" as listener to a null race log for tracked race "+trackedRace.getRace());
|
||||
} else {
|
||||
raceLog.addListener(this);
|
||||
trackedRace.invalidateStartTime();
|
||||
trackedRace.invalidateEndTime();
|
||||
courseDesignFinder = new LastPublishedCourseDesignFinder(raceLog);
|
||||
startTimeFinder = new StartTimeFinder(raceLog);
|
||||
initializeWindTrack(raceLog);
|
||||
analyze();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+9
-3
@@ -10,6 +10,7 @@ import java.util.NavigableSet;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.tracking.GPSFix;
|
||||
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.util.impl.ArrayListNavigableSet;
|
||||
@@ -262,9 +263,14 @@ public class MaxSpeedCache<ItemType, FixType extends GPSFix> implements GPSTrack
|
||||
if (fix.getTimePoint().after(to)) {
|
||||
break;
|
||||
}
|
||||
Speed averagedSpeedAtFixTime = track.getEstimatedSpeed(fix.getTimePoint());
|
||||
if (averagedSpeedAtFixTime != null && averagedSpeedAtFixTime.compareTo(max) > 0) {
|
||||
max = averagedSpeedAtFixTime;
|
||||
Speed speedAtFixTime = null;
|
||||
if (fix instanceof GPSFixMoving) {
|
||||
speedAtFixTime = ((GPSFixMoving)fix).getSpeed();
|
||||
} else {
|
||||
speedAtFixTime = track.getEstimatedSpeed(fix.getTimePoint());
|
||||
}
|
||||
if (speedAtFixTime != null && speedAtFixTime.compareTo(max) > 0) {
|
||||
max = speedAtFixTime;
|
||||
maxSpeedFix = fix;
|
||||
}
|
||||
}
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
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.TimePoint;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
|
||||
|
||||
/**
|
||||
* This trivial "cache" implementation doesn't cache and may be used as a default for those cases where only few
|
||||
* calculations are to be done and creating the caching structure would cost spend cycles than simply doing the
|
||||
* calculation.
|
||||
*
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
public class NoCachingWindLegTypeAndLegBearingCache implements WindLegTypeAndLegBearingCache {
|
||||
@Override
|
||||
public Wind getWind(TrackedRace trackedRace, Competitor competitor, TimePoint timePoint) {
|
||||
return trackedRace.getWind(trackedRace.getTrack(competitor).getEstimatedPosition(timePoint, false), timePoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LegType getLegType(TrackedLeg trackedLeg, TimePoint timePoint) throws NoWindException {
|
||||
return trackedLeg.getLegType(timePoint);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bearing getLegBearing(TrackedLeg trackedLeg, TimePoint timePoint) {
|
||||
return trackedLeg.getLegBearing(timePoint);
|
||||
}
|
||||
}
|
||||
+8
-3
@@ -31,6 +31,7 @@ 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.sse.common.Util;
|
||||
|
||||
public class TrackedLegImpl implements TrackedLeg, RaceChangeListener {
|
||||
@@ -344,21 +345,25 @@ public class TrackedLegImpl implements TrackedLeg, RaceChangeListener {
|
||||
|
||||
@Override
|
||||
public Distance getWindwardDistance(Position pos1, Position pos2, TimePoint at, WindPositionMode windPositionMode) throws NoWindException {
|
||||
LegType legType = getLegType(at);
|
||||
return getWindwardDistance(pos1, pos2, at, new NoCachingWindLegTypeAndLegBearingCache());
|
||||
}
|
||||
|
||||
Distance getWindwardDistance(Position pos1, Position pos2, TimePoint at, WindLegTypeAndLegBearingCache cache) throws NoWindException {
|
||||
LegType legType = cache.getLegType(this, at);
|
||||
if (legType != LegType.REACHING) { // upwind or downwind
|
||||
final Position effectivePosition = getEffectiveWindPosition(
|
||||
pos1.translateGreatCircle(pos1.getBearingGreatCircle(pos2), pos1.getDistance(pos2).scale(0.5)), at,
|
||||
windPositionMode);
|
||||
Wind wind = getTrackedRace().getWind(effectivePosition, at);
|
||||
if (wind == null) {
|
||||
return pos2.alongTrackDistance(pos1, getLegBearing(at));
|
||||
return pos2.alongTrackDistance(pos1, cache.getLegBearing(this, at));
|
||||
} else {
|
||||
Position projectionToLineThroughPos2 = pos1.projectToLineThrough(pos2, wind.getBearing());
|
||||
return pos2.alongTrackDistance(projectionToLineThroughPos2, legType == LegType.UPWIND ? wind.getFrom() : wind.getBearing());
|
||||
}
|
||||
} else {
|
||||
// reaching leg, return distance projected onto leg's bearing
|
||||
return pos2.alongTrackDistance(pos1, getLegBearing(at));
|
||||
return pos2.alongTrackDistance(pos1, cache.getLegBearing(this, at));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+41
-20
@@ -12,6 +12,7 @@ import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
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.NoWindException;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
@@ -29,6 +30,7 @@ import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
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.util.impl.ArrayListNavigableSet;
|
||||
import com.sap.sse.common.Util;
|
||||
|
||||
@@ -216,18 +218,23 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
*
|
||||
* @throws NoWindException in case the wind direction is not known
|
||||
*/
|
||||
private SpeedWithBearing getWindwardSpeed(SpeedWithBearing speed, TimePoint at, WindPositionMode windPositionMode) throws NoWindException {
|
||||
private SpeedWithBearing getWindwardSpeed(SpeedWithBearing speed, TimePoint at, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) throws NoWindException {
|
||||
SpeedWithBearing result = null;
|
||||
if (speed != null) {
|
||||
Bearing projectToBearing;
|
||||
if (getTrackedLeg().isUpOrDownwindLeg(at)) {
|
||||
Wind wind = getWind(getTrackedRace().getTrack(getCompetitor()).getEstimatedPosition(at, false), at, windPositionMode);
|
||||
if (cache.getLegType(getTrackedLeg(), at) != LegType.REACHING) {
|
||||
final Wind wind;
|
||||
if (windPositionMode == WindPositionMode.EXACT) {
|
||||
wind = cache.getWind(getTrackedRace(), getCompetitor(), at);
|
||||
} else {
|
||||
wind = getWind(getTrackedRace().getTrack(getCompetitor()).getEstimatedPosition(at, false), at, windPositionMode);
|
||||
}
|
||||
if (wind == null) {
|
||||
throw new NoWindException("Need at least wind direction to determine windward speed");
|
||||
}
|
||||
projectToBearing = wind.getBearing();
|
||||
} else {
|
||||
projectToBearing = getTrackedLeg().getLegBearing(at);
|
||||
projectToBearing = cache.getLegBearing(getTrackedLeg(), at);
|
||||
}
|
||||
double cos = Math.cos(speed.getBearing().getRadians() - projectToBearing.getRadians());
|
||||
if (cos < 0) {
|
||||
@@ -249,10 +256,6 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
* is passed as position to {@link TrackedRace#getWind(Position, TimePoint)} which yields a general average
|
||||
* of the various wind sources available for the race, independent of any position.
|
||||
*/
|
||||
Wind getWind(Position p, TimePoint at, WindPositionMode windPositionMode) {
|
||||
return getTrackedRace().getWind(getTrackedLeg().getEffectiveWindPosition(p, at, windPositionMode), at);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRank(TimePoint timePoint) {
|
||||
int result = 0;
|
||||
@@ -265,6 +268,11 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
|
||||
@Override
|
||||
public Speed getAverageVelocityMadeGood(TimePoint timePoint) throws NoWindException {
|
||||
return getAverageVelocityMadeGood(timePoint, new NoCachingWindLegTypeAndLegBearingCache());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Speed getAverageVelocityMadeGood(TimePoint timePoint, WindLegTypeAndLegBearingCache cache) throws NoWindException {
|
||||
Speed result = null;
|
||||
MarkPassing start = getMarkPassingForLegStart();
|
||||
if (start != null && start.getTimePoint().compareTo(timePoint) <= 0) {
|
||||
@@ -429,35 +437,46 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
@Override
|
||||
public Double getGapToLeaderInSeconds(TimePoint timePoint, final Competitor leaderInLegAtTimePoint, WindPositionMode windPositionMode)
|
||||
throws NoWindException {
|
||||
return getGapToLeaderInSeconds(timePoint, leaderInLegAtTimePoint, new NoCachingWindLegTypeAndLegBearingCache());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getGapToLeaderInSeconds(TimePoint timePoint, final Competitor leaderInLegAtTimePoint, WindLegTypeAndLegBearingCache cache)
|
||||
throws NoWindException {
|
||||
return getGapToLeaderInSeconds(timePoint, new LeaderGetter() {
|
||||
@Override
|
||||
public Competitor getLeader() {
|
||||
return leaderInLegAtTimePoint;
|
||||
}
|
||||
}, windPositionMode);
|
||||
}, windPositionMode, cache);
|
||||
}
|
||||
|
||||
private static interface LeaderGetter {
|
||||
Competitor getLeader();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Double getGapToLeaderInSeconds(final TimePoint timePoint, WindPositionMode windPositionMode) throws NoWindException {
|
||||
return getGapToLeaderInSeconds(timePoint, new NoCachingWindLegTypeAndLegBearingCache());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getGapToLeaderInSeconds(final TimePoint timePoint, WindLegTypeAndLegBearingCache cache) throws NoWindException {
|
||||
return getGapToLeaderInSeconds(timePoint, new LeaderGetter() {
|
||||
@Override
|
||||
public Competitor getLeader() {
|
||||
return getTrackedLeg().getLeader(hasFinishedLeg(timePoint) ? getFinishTime() : timePoint);
|
||||
}
|
||||
}, windPositionMode);
|
||||
}, windPositionMode, new NoCachingWindLegTypeAndLegBearingCache());
|
||||
}
|
||||
|
||||
private Double getGapToLeaderInSeconds(TimePoint timePoint, LeaderGetter leaderGetter, WindPositionMode windPositionMode) throws NoWindException {
|
||||
private Double getGapToLeaderInSeconds(TimePoint timePoint, LeaderGetter leaderGetter, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) throws NoWindException {
|
||||
// If the leader already completed this leg, compute the estimated arrival time at the
|
||||
// end of this leg; if this leg's competitor also already finished the leg, return the
|
||||
// difference between this competitor's leg completion time point and the leader's completion
|
||||
// time point; else, calculate the windward distance to the leader and divide by
|
||||
// the windward speed
|
||||
Speed windwardSpeed = getWindwardSpeed(getTrackedRace().getTrack(getCompetitor()).getEstimatedSpeed(timePoint), timePoint, windPositionMode);
|
||||
Speed windwardSpeed = getWindwardSpeed(getTrackedRace().getTrack(getCompetitor()).getEstimatedSpeed(timePoint), timePoint, windPositionMode, cache);
|
||||
Double result = null;
|
||||
// Has our competitor started the leg already? If not, we won't be able to compute a gap
|
||||
if (hasStartedLeg(timePoint)) {
|
||||
@@ -563,7 +582,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
timePoint = at;
|
||||
}
|
||||
SpeedWithBearing speedOverGround = getSpeedOverGround(timePoint);
|
||||
return speedOverGround == null ? null : getWindwardSpeed(speedOverGround, timePoint, windPositionMode);
|
||||
return speedOverGround == null ? null : getWindwardSpeed(speedOverGround, timePoint, windPositionMode, new NoCachingWindLegTypeAndLegBearingCache());
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
@@ -775,24 +794,26 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
|
||||
@Override
|
||||
public Bearing getBeatAngle(TimePoint at) throws NoWindException {
|
||||
return getBeatAngle(at, new NoCachingWindLegTypeAndLegBearingCache());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bearing getBeatAngle(TimePoint at, WindLegTypeAndLegBearingCache cache) throws NoWindException {
|
||||
Bearing beatAngle = null;
|
||||
|
||||
Bearing projectToBearing;
|
||||
if (getTrackedLeg().isUpOrDownwindLeg(at)) {
|
||||
Wind wind = getWind(getTrackedRace().getTrack(getCompetitor()).getEstimatedPosition(at, false), at, WindPositionMode.EXACT);
|
||||
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 = getTrackedLeg().getLegBearing(at);
|
||||
projectToBearing = cache.getLegBearing(getTrackedLeg(), at);
|
||||
}
|
||||
|
||||
SpeedWithBearing speed = getSpeedOverGround(at);
|
||||
if (speed != null) {
|
||||
beatAngle = projectToBearing.getDifferenceTo(speed.getBearing());
|
||||
}
|
||||
|
||||
return beatAngle;
|
||||
}
|
||||
}
|
||||
+110
-38
@@ -17,6 +17,7 @@ import java.util.LinkedHashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.NavigableSet;
|
||||
import java.util.Set;
|
||||
import java.util.Timer;
|
||||
@@ -102,6 +103,7 @@ import com.sap.sailing.domain.tracking.TrackedRaceWithWindEssentials;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
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.WindStore;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
@@ -182,7 +184,12 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
|
||||
private long updateCount;
|
||||
|
||||
private transient Map<TimePoint, List<Competitor>> competitorRankings;
|
||||
/**
|
||||
* Limit for the cache size in {@link #competitorRankings} and respectively in {@link #competitorRankingsLocks}.
|
||||
*/
|
||||
private static final int MAX_COMPETITOR_RANKINGS_CACHE_SIZE = 10;
|
||||
|
||||
private transient LinkedHashMap<TimePoint, List<Competitor>> competitorRankings;
|
||||
|
||||
/**
|
||||
* The locks managed here correspond with the {@link #competitorRankings} structure. When
|
||||
@@ -190,7 +197,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
* time point. Readers use the read lock. Checking / entering a lock into this map uses <code>synchronized</code> on
|
||||
* the map itself.
|
||||
*/
|
||||
private transient Map<TimePoint, NamedReentrantReadWriteLock> competitorRankingsLocks;
|
||||
private transient LinkedHashMap<TimePoint, NamedReentrantReadWriteLock> competitorRankingsLocks;
|
||||
|
||||
/**
|
||||
* legs appear in the order in which they appear in the race's course
|
||||
@@ -378,8 +385,8 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
WindSource trackBasedWindSource = new WindSourceImpl(WindSourceType.TRACK_BASED_ESTIMATION);
|
||||
windTracks.put(trackBasedWindSource,
|
||||
getOrCreateWindTrack(trackBasedWindSource, delayForWindEstimationCacheInvalidation));
|
||||
competitorRankings = new HashMap<TimePoint, List<Competitor>>();
|
||||
competitorRankingsLocks = new HashMap<TimePoint, NamedReentrantReadWriteLock>();
|
||||
competitorRankings = createCompetitorRankingsCache();
|
||||
competitorRankingsLocks = createCompetitorRankingsLockMap();
|
||||
// now wait until wind loading has at least started; then we know that the serialization lock is safely held by the loader
|
||||
try {
|
||||
waitUntilLoadingFromWindStoreComplete();
|
||||
@@ -388,6 +395,26 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
}
|
||||
}
|
||||
|
||||
private LinkedHashMap<TimePoint, NamedReentrantReadWriteLock> createCompetitorRankingsLockMap() {
|
||||
return new LinkedHashMap<TimePoint, NamedReentrantReadWriteLock>() {
|
||||
private static final long serialVersionUID = 6298801656693955386L;
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Entry<TimePoint, NamedReentrantReadWriteLock> eldest) {
|
||||
return size() > MAX_COMPETITOR_RANKINGS_CACHE_SIZE;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
private LinkedHashMap<TimePoint, List<Competitor>> createCompetitorRankingsCache() {
|
||||
return new LinkedHashMap<TimePoint, List<Competitor>>() {
|
||||
private static final long serialVersionUID = -6044369612727021861L;
|
||||
@Override
|
||||
protected boolean removeEldestEntry(Entry<TimePoint, List<Competitor>> eldest) {
|
||||
return size() > MAX_COMPETITOR_RANKINGS_CACHE_SIZE;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Assuming that the tracks were loaded from the persistent store, this method updates
|
||||
* the time stamps that frame the data held by this tracked race. See {@link #timePointOfLastEvent}, {@link #timePointOfNewestEvent}
|
||||
@@ -430,8 +457,8 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
markPassingsTimes = new ArrayList<com.sap.sse.common.Util.Pair<Waypoint, com.sap.sse.common.Util.Pair<TimePoint, TimePoint>>>();
|
||||
cacheInvalidationTimerLock = new Object();
|
||||
windStore = EmptyWindStore.INSTANCE;
|
||||
competitorRankings = new HashMap<TimePoint, List<Competitor>>();
|
||||
competitorRankingsLocks = new HashMap<TimePoint, NamedReentrantReadWriteLock>();
|
||||
competitorRankings = createCompetitorRankingsCache();
|
||||
competitorRankingsLocks = createCompetitorRankingsLockMap();
|
||||
directionFromStartToNextMarkCache = new HashMap<TimePoint, Future<Wind>>();
|
||||
crossTrackErrorCache = new CrossTrackErrorCache(this);
|
||||
maneuverCache = createManeuverCache();
|
||||
@@ -999,49 +1026,45 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
}
|
||||
}
|
||||
List<Competitor> rankedCompetitors;
|
||||
final boolean lockForWrite;
|
||||
synchronized (competitorRankings) {
|
||||
rankedCompetitors = competitorRankings.get(timePoint);
|
||||
if (rankedCompetitors == null) {
|
||||
lockForWrite = true;
|
||||
} else {
|
||||
lockForWrite = false;
|
||||
}
|
||||
}
|
||||
if (lockForWrite) {
|
||||
if (rankedCompetitors == null) {
|
||||
LockUtil.lockForWrite(readWriteLock);
|
||||
} else {
|
||||
LockUtil.lockForRead(readWriteLock);
|
||||
}
|
||||
try {
|
||||
if (rankedCompetitors == null) {
|
||||
rankedCompetitors = competitorRankings.get(timePoint); // try again; maybe a writer released the write
|
||||
// lock after updating the cache
|
||||
try {
|
||||
if (rankedCompetitors == null) {
|
||||
RaceRankComparator comparator = new RaceRankComparator(this, timePoint);
|
||||
rankedCompetitors = new ArrayList<Competitor>();
|
||||
for (Competitor c : getRace().getCompetitors()) {
|
||||
rankedCompetitors.add(c);
|
||||
}
|
||||
Collections.sort(rankedCompetitors, comparator);
|
||||
synchronized (competitorRankings) {
|
||||
competitorRankings.put(timePoint, rankedCompetitors);
|
||||
rankedCompetitors = competitorRankings.get(timePoint); // try again; maybe a writer released the
|
||||
// write
|
||||
// lock after updating the cache
|
||||
if (rankedCompetitors == null) {
|
||||
RaceRankComparator comparator = new RaceRankComparator(this, timePoint);
|
||||
rankedCompetitors = new ArrayList<Competitor>();
|
||||
for (Competitor c : getRace().getCompetitors()) {
|
||||
rankedCompetitors.add(c);
|
||||
}
|
||||
Collections.sort(rankedCompetitors, comparator);
|
||||
synchronized (competitorRankings) {
|
||||
competitorRankings.put(timePoint, rankedCompetitors);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return rankedCompetitors;
|
||||
} finally {
|
||||
if (lockForWrite) {
|
||||
} finally {
|
||||
LockUtil.unlockAfterWrite(readWriteLock);
|
||||
} else {
|
||||
LockUtil.unlockAfterRead(readWriteLock);
|
||||
}
|
||||
}
|
||||
return rankedCompetitors;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis)
|
||||
throws NoWindException {
|
||||
return getAverageAbsoluteCrossTrackError(competitor, timePoint, waitForLatestAnalysis, new NoCachingWindLegTypeAndLegBearingCache());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis,
|
||||
WindLegTypeAndLegBearingCache cache)
|
||||
throws NoWindException {
|
||||
NavigableSet<MarkPassing> markPassings = getMarkPassings(competitor);
|
||||
TimePoint from = null;
|
||||
lockForRead(markPassings);
|
||||
@@ -1064,6 +1087,12 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
@Override
|
||||
public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis)
|
||||
throws NoWindException {
|
||||
return getAverageSignedCrossTrackError(competitor, timePoint, waitForLatestAnalysis, new NoCachingWindLegTypeAndLegBearingCache());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint timePoint,
|
||||
boolean waitForLatestAnalyses, WindLegTypeAndLegBearingCache cache) throws NoWindException {
|
||||
NavigableSet<MarkPassing> markPassings = getMarkPassings(competitor);
|
||||
TimePoint from = null;
|
||||
lockForRead(markPassings);
|
||||
@@ -1076,7 +1105,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
}
|
||||
Distance result;
|
||||
if (from != null) {
|
||||
result = getAverageSignedCrossTrackError(competitor, from, timePoint, /* upwindOnly */true, waitForLatestAnalysis);
|
||||
result = getAverageSignedCrossTrackError(competitor, from, timePoint, /* upwindOnly */true, waitForLatestAnalyses);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
@@ -1895,6 +1924,42 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This is probably best explained by example. If the wind bearing is from port to starboard, the situation looks
|
||||
* like this:
|
||||
*
|
||||
* <pre>
|
||||
* ^
|
||||
* Wind | Boat
|
||||
* -----------> |
|
||||
* |
|
||||
*
|
||||
* </pre>
|
||||
*
|
||||
* In this case, the boat gets the wind from port, so the result has to be {@link Tack#PORT}. The angle between the
|
||||
* boat's heading (which we can only approximate by the boat's course over ground) and the wind bearing in this case
|
||||
* is 90 degrees. <code>wind.{@link Bearing#getDifferenceTo(Bearing) getDifferenceTo}(boat)</code> in this case will
|
||||
* return a bearing representing -90 degrees.
|
||||
* <p>
|
||||
*
|
||||
* If the wind is blowing the other way, the angle returned by {@link Bearing#getDifferenceTo(Bearing)} will
|
||||
* correspond to +90 degrees. In other words, a negative angle means starboard tack, a positive angle represents
|
||||
* port tack.
|
||||
* <p>
|
||||
*
|
||||
* For the unlikely case of 0 degrees difference, {@link Tack#STARBOARD} will result.
|
||||
*
|
||||
* @return <code>null</code> in case the boat's bearing cannot be determined for <code>timePoint</code>
|
||||
*/
|
||||
@Override
|
||||
public Tack getTack(SpeedWithBearing estimatedSpeed, Wind wind, TimePoint timePoint) {
|
||||
Tack result = null;
|
||||
if (estimatedSpeed != null) {
|
||||
result = getTack(wind, estimatedSpeed.getBearing());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Based on the wind direction at <code>timePoint</code> and at position <code>where</code>, compares the
|
||||
* <code>boatBearing</code> to the wind's bearing at that time and place and determined the tack.
|
||||
@@ -1905,15 +1970,22 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
*/
|
||||
private Tack getTack(Position where, TimePoint timePoint, Bearing boatBearing) throws NoWindException {
|
||||
final Wind wind = getWind(where, timePoint);
|
||||
Tack result;
|
||||
if (wind == null) {
|
||||
throw new NoWindException("Can't determine wind direction in position " + where + " at " + timePoint
|
||||
+ ", therefore cannot determine tack");
|
||||
}
|
||||
return getTack(wind, boatBearing);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Based on the wind, compares the <code>boatBearing</code> to the wind's bearing at
|
||||
* that time and place and determined the tack.
|
||||
*/
|
||||
private Tack getTack(Wind wind, Bearing boatBearing) {
|
||||
Bearing windBearing = wind.getBearing();
|
||||
Bearing difference = windBearing.getDifferenceTo(boatBearing);
|
||||
result = difference.getDegrees() <= 0 ? Tack.PORT : Tack.STARBOARD;
|
||||
return result;
|
||||
return difference.getDegrees() <= 0 ? Tack.PORT : Tack.STARBOARD;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+2
-1
@@ -178,7 +178,8 @@ public class GwtTestCaseColumnToggling extends GWTTestCase {
|
||||
private void getLeaderboardByNameAndDeleteColumn(){
|
||||
ArrayList<String> al = new ArrayList<String>();
|
||||
al.add(COLUMN1_NAME);
|
||||
service.getLeaderboardByName(LEADERBOARD_NAME, new Date(), al, /* addOverallDetails */ false, /* previousLeaderboardId */ null, new AsyncCallback<IncrementalOrFullLeaderboardDTO>() {
|
||||
service.getLeaderboardByName(LEADERBOARD_NAME, new Date(), al, /* addOverallDetails */ false, /* previousLeaderboardId */ null,
|
||||
/* fillUncorrectedNetPoints */ false, new AsyncCallback<IncrementalOrFullLeaderboardDTO>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
fail("Failed to get leaderboard.");
|
||||
|
||||
+22
@@ -27,6 +27,7 @@ import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.RegattaIdentifier;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
@@ -53,6 +54,7 @@ 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.WindStore;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
@@ -540,6 +542,12 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tack getTack(SpeedWithBearing speedWithBearing, Wind wind, TimePoint timePoint) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Wind getDirectionFromStartToNextMark(TimePoint at) {
|
||||
@@ -680,6 +688,13 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint,
|
||||
boolean waitForLatestAnalyses, WindLegTypeAndLegBearingCache cache) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WindTrack getOrCreateWindTrack(WindSource windSource) {
|
||||
// TODO Auto-generated method stub
|
||||
@@ -959,6 +974,13 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint timePoint,
|
||||
boolean waitForLatestAnalyses, WindLegTypeAndLegBearingCache cache) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint from, TimePoint to,
|
||||
boolean upwindOnly, boolean waitForLatestAnalysis) throws NoWindException {
|
||||
|
||||
+4
-2
@@ -98,7 +98,8 @@ public class TestColumnSwapping {
|
||||
sailingService.moveLeaderboardColumnUp(TEST_LEADERBOARD_NAME, races[2]);
|
||||
sailingService.updateIsMedalRace(TEST_LEADERBOARD_NAME, races[0], true);
|
||||
sailingService.updateIsMedalRace(TEST_LEADERBOARD_NAME, races[2], false);
|
||||
lb = sailingService.getLeaderboardByName(TEST_LEADERBOARD_NAME, new Date(), /* races to load */ null, /* addOverallDetails */ true, /* previous leaderboard ID */ null).getLeaderboardDTO(/* previousVersion */ null);
|
||||
lb = sailingService.getLeaderboardByName(TEST_LEADERBOARD_NAME, new Date(), /* races to load */ null, /* addOverallDetails */ true, /* previous leaderboard ID */ null,
|
||||
/* fillNetPointsUncorrected */ false).getLeaderboardDTO(/* previousVersion */ null);
|
||||
} catch (Exception e) {
|
||||
// e.printStackTrace();
|
||||
fail(e.getLocalizedMessage());
|
||||
@@ -139,7 +140,8 @@ public class TestColumnSwapping {
|
||||
public void testColumnSwappingFabian() {
|
||||
service.moveLeaderboardColumnUp(LEADERBOARDNAME, "Race3");
|
||||
try {
|
||||
leaderboardDTO = service.getLeaderboardByName(LEADERBOARDNAME, leaderboardCreationDate, leglist, /* addOverallDetails */ true, /* previous leaderboard ID */ null).getLeaderboardDTO(/* previousVersion */ null);
|
||||
leaderboardDTO = service.getLeaderboardByName(LEADERBOARDNAME, leaderboardCreationDate, leglist, /* addOverallDetails */
|
||||
true, /* previous leaderboard ID */null, /* fillNetPointsUncorrected */false).getLeaderboardDTO(/* previousVersion */null);
|
||||
} catch (Exception e) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
eclipse.preferences.version=1
|
||||
entryPointModules=
|
||||
filesCopiedToWebInfLib=gwt-servlet.jar
|
||||
gwtCompileSettings=PGd3dC1jb21waWxlLXNldHRpbmdzPjxsb2ctbGV2ZWw+SU5GTzwvbG9nLWxldmVsPjxvdXRwdXQtc3R5bGU+T0JGVVNDQVRFRDwvb3V0cHV0LXN0eWxlPjxleHRyYS1hcmdzPjwhW0NEQVRBWy13YXIgLiAtbG9jYWxXb3JrZXJzIDMgLXN0cmljdCAtbG9nTGV2ZWwgVFJBQ0UgLWNvbXBpbGVSZXBvcnQgLVhzb3ljRGV0YWlsZWRdXT48L2V4dHJhLWFyZ3M+PHZtLWFyZ3M+PCFbQ0RBVEFbLVhteDIwMjRtIC1EZ3d0LnVzZWFyY2hpdmVzPWZhbHNlXV0+PC92bS1hcmdzPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmd3dC51aS5SYWNlQm9hcmQ8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuQWRtaW5Db25zb2xlPC9lbnRyeS1wb2ludC1tb2R1bGU+PC9nd3QtY29tcGlsZS1zZXR0aW5ncz4\=
|
||||
gwtCompileSettings=PGd3dC1jb21waWxlLXNldHRpbmdzPjxsb2ctbGV2ZWw+SU5GTzwvbG9nLWxldmVsPjxvdXRwdXQtc3R5bGU+T0JGVVNDQVRFRDwvb3V0cHV0LXN0eWxlPjxleHRyYS1hcmdzPjwhW0NEQVRBWy13YXIgLiAtbG9jYWxXb3JrZXJzIDMgLXN0cmljdCAtbG9nTGV2ZWwgVFJBQ0UgLWNvbXBpbGVSZXBvcnQgLVhzb3ljRGV0YWlsZWRdXT48L2V4dHJhLWFyZ3M+PHZtLWFyZ3M+PCFbQ0RBVEFbLVhteDIwMjRtIC1EZ3d0LnVzZWFyY2hpdmVzPWZhbHNlXV0+PC92bS1hcmdzPjxlbnRyeS1wb2ludC1tb2R1bGU+Y29tLnNhcC5zYWlsaW5nLmd3dC51aS5BZG1pbkNvbnNvbGU8L2VudHJ5LXBvaW50LW1vZHVsZT48ZW50cnktcG9pbnQtbW9kdWxlPmNvbS5zYXAuc2FpbGluZy5nd3QudWkuUmFjZUJvYXJkPC9lbnRyeS1wb2ludC1tb2R1bGU+PGVudHJ5LXBvaW50LW1vZHVsZT5jb20uc2FwLnNhaWxpbmcuZ3d0LnVpLkxlYWRlcmJvYXJkPC9lbnRyeS1wb2ludC1tb2R1bGU+PC9nd3QtY29tcGlsZS1zZXR0aW5ncz4\=
|
||||
@@ -350,6 +350,13 @@ body ::-webkit-scrollbar-track {
|
||||
margin: 0 14px 0 0;
|
||||
}
|
||||
|
||||
.timePanel-timeslider-play {
|
||||
float:left;
|
||||
padding: 10px;
|
||||
height: 24px;
|
||||
margin: 5px 14px 5px 0;
|
||||
}
|
||||
|
||||
.timePanel-controls-playSpeed {
|
||||
vertical-align: middle;
|
||||
line-height: 25px;
|
||||
|
||||
+6
-2
@@ -37,10 +37,12 @@ public class GetLeaderboardByNameAction implements AsyncAction<LeaderboardDTO> {
|
||||
private final StringMessages stringMessages;
|
||||
private final ErrorReporter errorReporter;
|
||||
private final Timer timerToAdjustOffetIn;
|
||||
private final boolean fillNetPointsUncorrected;
|
||||
|
||||
public GetLeaderboardByNameAction(SailingServiceAsync sailingService, String leaderboardName, Date date,
|
||||
final Collection<String> namesOfRacesForWhichToLoadLegDetails, boolean addOverallDetails,
|
||||
LeaderboardDTO previousLeaderboard, Timer timerToAdjustOffsetIn, ErrorReporter errorReporter, StringMessages stringMessages) {
|
||||
LeaderboardDTO previousLeaderboard, boolean fillNetPointsUncorrected, Timer timerToAdjustOffsetIn,
|
||||
ErrorReporter errorReporter, StringMessages stringMessages) {
|
||||
this.sailingService = sailingService;
|
||||
this.errorReporter = errorReporter;
|
||||
this.stringMessages = stringMessages;
|
||||
@@ -50,13 +52,15 @@ public class GetLeaderboardByNameAction implements AsyncAction<LeaderboardDTO> {
|
||||
this.addOverallDetails = addOverallDetails;
|
||||
this.previousLeaderboard = previousLeaderboard;
|
||||
this.timerToAdjustOffetIn = timerToAdjustOffsetIn;
|
||||
this.fillNetPointsUncorrected = fillNetPointsUncorrected;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(final AsyncCallback<LeaderboardDTO> callback) {
|
||||
final long clientTimeWhenRequestWasSent = System.currentTimeMillis();
|
||||
sailingService.getLeaderboardByName(leaderboardName, date, namesOfRacesForWhichToLoadLegDetails, addOverallDetails,
|
||||
previousLeaderboard==null?null:previousLeaderboard.getId(), new AsyncCallback<IncrementalOrFullLeaderboardDTO>() {
|
||||
previousLeaderboard==null?null:previousLeaderboard.getId(), fillNetPointsUncorrected,
|
||||
new AsyncCallback<IncrementalOrFullLeaderboardDTO>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
callback.onFailure(caught);
|
||||
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
package com.sap.sailing.gwt.ui.actions;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
|
||||
import com.sap.sse.gwt.client.async.AsyncAction;
|
||||
|
||||
public class GetRaceTimesInfoAction implements AsyncAction<List<RaceTimesInfoDTO>> {
|
||||
private final SailingServiceAsync sailingService;
|
||||
private final Collection<RegattaAndRaceIdentifier> raceIdentifiers;
|
||||
|
||||
public GetRaceTimesInfoAction(SailingServiceAsync sailingService, Collection<RegattaAndRaceIdentifier> raceIdentifiers) {
|
||||
this.sailingService = sailingService;
|
||||
this.raceIdentifiers = raceIdentifiers;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void execute(AsyncCallback<List<RaceTimesInfoDTO>> callback) {
|
||||
sailingService.getRaceTimesInfos(raceIdentifiers, callback);
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -103,7 +103,7 @@ public class AddDeviceMappingDialog extends DataEntryDialog<DeviceMappingDTO> {
|
||||
|
||||
final SingleSelectionModel<MarkDTO> markSelectionModel = new SingleSelectionModel<MarkDTO>();
|
||||
competitorTable = new CompetitorTableWrapper<>(sailingService, stringMessages, errorReporter,
|
||||
new SingleSelectionModel<CompetitorDTO>());
|
||||
new SingleSelectionModel<CompetitorDTO>(), true);
|
||||
markTable = new MarkTableWrapper<SingleSelectionModel<MarkDTO>>(
|
||||
markSelectionModel, sailingService, stringMessages, errorReporter);
|
||||
|
||||
@@ -200,8 +200,8 @@ public class AddDeviceMappingDialog extends DataEntryDialog<DeviceMappingDTO> {
|
||||
tablesPanel.add(marksPanel);
|
||||
tablesPanel.add(competitorsPanel);
|
||||
|
||||
marksPanel.setContentWidget(markTable.getTable());
|
||||
competitorsPanel.setContentWidget(competitorTable.getTable());
|
||||
marksPanel.setContentWidget(markTable.asWidget());
|
||||
competitorsPanel.setContentWidget(competitorTable.asWidget());
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
+1
-1
@@ -51,7 +51,7 @@ public class CompetitorPanel extends SimplePanel {
|
||||
this.errorReporter = errorReporter;
|
||||
this.leaderboardName = leaderboardName;
|
||||
this.competitorTable = new CompetitorTableWrapper<>(sailingService, stringMessages, errorReporter,
|
||||
new MultiSelectionModel<CompetitorDTO>());
|
||||
new MultiSelectionModel<CompetitorDTO>(), false);
|
||||
this.competitorSelectionModel = competitorTable.getSelectionModel();
|
||||
VerticalPanel mainPanel = new VerticalPanel();
|
||||
this.setWidget(mainPanel);
|
||||
|
||||
+3
-4
@@ -30,8 +30,8 @@ public class CompetitorTableWrapper<S extends SelectionModel<CompetitorDTO>> ext
|
||||
private final LabeledAbstractFilterablePanel<CompetitorDTO> filterField;
|
||||
|
||||
public CompetitorTableWrapper(SailingServiceAsync sailingService, StringMessages stringMessages,ErrorReporter errorReporter,
|
||||
S selectionModel) {
|
||||
super(sailingService, stringMessages, errorReporter, selectionModel);
|
||||
S selectionModel, boolean enablePager) {
|
||||
super(sailingService, stringMessages, errorReporter, selectionModel, enablePager);
|
||||
|
||||
ListHandler<CompetitorDTO> competitorColumnListHandler = new ListHandler<CompetitorDTO>(dataProvider.getList());
|
||||
|
||||
@@ -130,14 +130,13 @@ public class CompetitorTableWrapper<S extends SelectionModel<CompetitorDTO>> ext
|
||||
}
|
||||
};
|
||||
|
||||
mainPanel.add(filterField);
|
||||
mainPanel.insert(filterField, 0);
|
||||
table.addColumnSortHandler(competitorColumnListHandler);
|
||||
table.addColumn(sailIdColumn, stringMessages.sailNumber());
|
||||
table.addColumn(competitorNameColumn, stringMessages.name());
|
||||
table.addColumn(boatClassColumn, stringMessages.boatClass());
|
||||
table.addColumn(displayColorColumn, stringMessages.color());
|
||||
table.addColumn(competitorIdColumn, stringMessages.id());
|
||||
mainPanel.add(table);
|
||||
}
|
||||
|
||||
public Iterable<CompetitorDTO> getAllCompetitors() {
|
||||
|
||||
+1
-3
@@ -12,9 +12,7 @@ import com.sap.sailing.gwt.ui.shared.MarkDTO;
|
||||
public class ControlPointTableWrapper<S extends SelectionModel<ControlPointDTO>> extends TableWrapper<ControlPointDTO, S> {
|
||||
public ControlPointTableWrapper(S selectionModel, SailingServiceAsync sailingService, final StringMessages stringMessages,
|
||||
ErrorReporter errorReporter) {
|
||||
super(sailingService, stringMessages, errorReporter, selectionModel);
|
||||
|
||||
mainPanel.add(table);
|
||||
super(sailingService, stringMessages, errorReporter, selectionModel, true);
|
||||
|
||||
TextColumn<ControlPointDTO> nameColumn = new TextColumn<ControlPointDTO>() {
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ public class DeviceIdentifierTableWrapper extends TableWrapper<DeviceIdentifierD
|
||||
|
||||
public DeviceIdentifierTableWrapper(SailingServiceAsync sailingService, StringMessages stringMessages,
|
||||
ErrorReporter errorReporter) {
|
||||
super(sailingService, stringMessages, errorReporter, new MultiSelectionModel<DeviceIdentifierDTO>());
|
||||
super(sailingService, stringMessages, errorReporter, new MultiSelectionModel<DeviceIdentifierDTO>(), false);
|
||||
|
||||
TextColumn<DeviceIdentifierDTO> typeColumn = new TextColumn<DeviceIdentifierDTO>() {
|
||||
@Override
|
||||
|
||||
+2
-9
@@ -10,8 +10,6 @@ import com.google.gwt.event.logical.shared.ValueChangeHandler;
|
||||
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
|
||||
import com.google.gwt.user.cellview.client.TextColumn;
|
||||
import com.google.gwt.user.client.ui.CheckBox;
|
||||
import com.google.gwt.user.client.ui.Panel;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwt.view.client.SingleSelectionModel;
|
||||
import com.sap.sailing.domain.common.dto.CompetitorDTO;
|
||||
@@ -25,8 +23,6 @@ public class DeviceMappingTableWrapper extends TableWrapper<DeviceMappingDTO, Si
|
||||
static interface FilterChangedHandler {
|
||||
void onFilterChanged(List<DeviceMappingDTO> filteredList);
|
||||
}
|
||||
|
||||
private final Panel mainPanel;
|
||||
private List<DeviceMappingDTO> allMappings = new ArrayList<>();
|
||||
private final CheckBox showPingMappingsCb;
|
||||
|
||||
@@ -34,9 +30,8 @@ public class DeviceMappingTableWrapper extends TableWrapper<DeviceMappingDTO, Si
|
||||
|
||||
public DeviceMappingTableWrapper(SailingServiceAsync sailingService, final StringMessages stringMessages,
|
||||
ErrorReporter errorReporter) {
|
||||
super(sailingService, stringMessages, errorReporter, new SingleSelectionModel<DeviceMappingDTO>());
|
||||
super(sailingService, stringMessages, errorReporter, new SingleSelectionModel<DeviceMappingDTO>(), true);
|
||||
|
||||
mainPanel = new VerticalPanel();
|
||||
showPingMappingsCb = new CheckBox(stringMessages.showPingMarkMappings());
|
||||
showPingMappingsCb.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
|
||||
@Override
|
||||
@@ -44,8 +39,7 @@ public class DeviceMappingTableWrapper extends TableWrapper<DeviceMappingDTO, Si
|
||||
filterPingMappings();
|
||||
}
|
||||
});
|
||||
mainPanel.add(showPingMappingsCb);
|
||||
mainPanel.add(table);
|
||||
mainPanel.insert(showPingMappingsCb, 0);
|
||||
|
||||
table.setWidth("1000px", true);
|
||||
table.addStyleName("wrap-cols");
|
||||
@@ -145,7 +139,6 @@ public class DeviceMappingTableWrapper extends TableWrapper<DeviceMappingDTO, Si
|
||||
table.addColumn(toCol, stringMessages.to());
|
||||
|
||||
table.addColumnSortHandler(listHandler);
|
||||
mainPanel.add(table);
|
||||
}
|
||||
|
||||
private void notifyFilterHandlers() {
|
||||
|
||||
+3
-3
@@ -61,7 +61,7 @@ public class ImportFixesAndAddMappingsDialog extends DataEntryDialog<Collection<
|
||||
});
|
||||
|
||||
competitorTable = new CompetitorTableWrapper<>(sailingService, stringMessages, errorReporter,
|
||||
new SingleSelectionModel<CompetitorDTO>());
|
||||
new SingleSelectionModel<CompetitorDTO>(), true);
|
||||
markTable = new MarkTableWrapper<SingleSelectionModel<MarkDTO>>(
|
||||
new SingleSelectionModel<MarkDTO>(), sailingService, stringMessages, errorReporter);
|
||||
|
||||
@@ -181,8 +181,8 @@ public class ImportFixesAndAddMappingsDialog extends DataEntryDialog<Collection<
|
||||
tablesPanel.add(marksPanel);
|
||||
tablesPanel.add(competitorsPanel);
|
||||
|
||||
marksPanel.setContentWidget(markTable.getTable());
|
||||
competitorsPanel.setContentWidget(competitorTable.getTable());
|
||||
marksPanel.setContentWidget(markTable.asWidget());
|
||||
competitorsPanel.setContentWidget(competitorTable.asWidget());
|
||||
|
||||
return panel;
|
||||
}
|
||||
|
||||
+1
-3
@@ -21,9 +21,7 @@ import com.sap.sailing.gwt.ui.shared.MarkDTO;
|
||||
public class MarkTableWrapper<S extends SelectionModel<MarkDTO>> extends TableWrapper<MarkDTO, S> {
|
||||
public MarkTableWrapper(S selectionModel, SailingServiceAsync sailingService, StringMessages stringMessages,
|
||||
ErrorReporter errorReporter) {
|
||||
super(sailingService, stringMessages, errorReporter, selectionModel);
|
||||
|
||||
mainPanel.add(table);
|
||||
super(sailingService, stringMessages, errorReporter, selectionModel, true);
|
||||
|
||||
TextColumn<MarkDTO> markNameColumn = new TextColumn<MarkDTO>() {
|
||||
@Override
|
||||
|
||||
+2
-2
@@ -82,9 +82,9 @@ public class RaceLogTrackingCompetitorRegistrationsDialog extends RaceLogTrackin
|
||||
CaptionPanel allCompetitorsPanel = new CaptionPanel(stringMessages.competitorPool());
|
||||
CaptionPanel registeredCompetitorsPanel = new CaptionPanel(stringMessages.registeredCompetitors());
|
||||
allCompetitorsTable = new CompetitorTableWrapper<>(sailingService, stringMessages, errorReporter,
|
||||
new MultiSelectionModel<CompetitorDTO>());
|
||||
new MultiSelectionModel<CompetitorDTO>(), true);
|
||||
registeredCompetitorsTable = new CompetitorTableWrapper<>(sailingService, stringMessages, errorReporter,
|
||||
new MultiSelectionModel<CompetitorDTO>());
|
||||
new MultiSelectionModel<CompetitorDTO>(), true);
|
||||
allCompetitorsPanel.add(allCompetitorsTable);
|
||||
registeredCompetitorsPanel.add(registeredCompetitorsTable);
|
||||
VerticalPanel movePanel = new VerticalPanel();
|
||||
|
||||
+1
@@ -61,6 +61,7 @@ public class RaceLogTrackingDeviceMappingsDialog extends RaceLogTrackingDialog {
|
||||
super(sailingService, stringMessages, errorReporter, leaderboardName, raceColumnName, fleetName, false);
|
||||
|
||||
deviceMappingTable = new DeviceMappingTableWrapper(sailingService, stringMessages, errorReporter);
|
||||
|
||||
deviceMappingTable.addFilterChangedHandler(new FilterChangedHandler() {
|
||||
@Override
|
||||
public void onFilterChanged(List<DeviceMappingDTO> filteredList) {
|
||||
|
||||
+1
-1
@@ -25,7 +25,7 @@ extends TableWrapper<RaceColumnDTOAndFleetDTOWithNameBasedEquality, S> {
|
||||
|
||||
public RaceTableWrapper(SailingServiceAsync sailingService, StringMessages stringMessages,
|
||||
ErrorReporter errorReporter, S selectionModel) {
|
||||
super(sailingService, stringMessages, errorReporter, selectionModel);
|
||||
super(sailingService, stringMessages, errorReporter, selectionModel, false);
|
||||
|
||||
Column<RaceColumnDTOAndFleetDTOWithNameBasedEquality, SafeHtml> raceNameColumn =
|
||||
new Column<RaceColumnDTOAndFleetDTOWithNameBasedEquality, SafeHtml>(new AnchorCell()) {
|
||||
|
||||
+1
-1
@@ -47,7 +47,7 @@ public class SelectRacesDialog extends DataEntryDialog<Set<RaceColumnDTOAndFleet
|
||||
|
||||
@Override
|
||||
protected Widget getAdditionalWidget() {
|
||||
return racesTable.getTable();
|
||||
return racesTable.asWidget();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+63
@@ -0,0 +1,63 @@
|
||||
package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.google.gwt.event.logical.shared.ValueChangeEvent;
|
||||
import com.google.gwt.event.logical.shared.ValueChangeHandler;
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.shared.BetterDateTimeBox;
|
||||
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
|
||||
|
||||
public class SetStartTimeReceivedDialog extends DataEntryDialog<Date> {
|
||||
|
||||
private final StringMessages stringMessages;
|
||||
|
||||
private BetterDateTimeBox timeBox;
|
||||
|
||||
public SetStartTimeReceivedDialog(StringMessages stringMessages, DataEntryDialog.DialogCallback<Date> callback) {
|
||||
super(stringMessages.setStartTimeReceived(), stringMessages.setStartTimeReceivedDescription(), stringMessages.ok(), stringMessages.cancel(), new ReceivedStartTimeDialog(stringMessages), callback);
|
||||
this.stringMessages = stringMessages;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Widget getAdditionalWidget() {
|
||||
Grid content = new Grid(1, 2);
|
||||
|
||||
Label timeBoxLabel = new Label(stringMessages.startTime() + ":");
|
||||
content.setWidget(0, 0, timeBoxLabel);
|
||||
timeBox = new BetterDateTimeBox();
|
||||
timeBox.addValueChangeHandler(new ValueChangeHandler<Date>() {
|
||||
@Override
|
||||
public void onValueChange(ValueChangeEvent<Date> event) {
|
||||
validate();
|
||||
}
|
||||
});
|
||||
content.setWidget(0, 1, timeBox);
|
||||
|
||||
return content;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Date getResult() {
|
||||
return timeBox.getValue();
|
||||
}
|
||||
|
||||
private static class ReceivedStartTimeDialog implements Validator<Date> {
|
||||
|
||||
private StringMessages stringMessages;
|
||||
|
||||
public ReceivedStartTimeDialog(StringMessages stringMessages) {
|
||||
this.stringMessages = stringMessages;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getErrorMessage(Date valueToValidate) {
|
||||
return valueToValidate == null ? stringMessages.pleaseEnterAValue() : null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
+10
-3
@@ -4,8 +4,8 @@ import java.util.Collection;
|
||||
|
||||
import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.user.cellview.client.CellTable;
|
||||
import com.google.gwt.user.cellview.client.SimplePager;
|
||||
import com.google.gwt.user.client.ui.IsWidget;
|
||||
import com.google.gwt.user.client.ui.Panel;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwt.view.client.ListDataProvider;
|
||||
@@ -21,7 +21,7 @@ public abstract class TableWrapper<T, S extends SelectionModel<T>> implements Is
|
||||
protected final CellTable<T> table;
|
||||
protected final S selectionModel;
|
||||
protected final ListDataProvider<T> dataProvider;
|
||||
protected Panel mainPanel;
|
||||
protected VerticalPanel mainPanel;
|
||||
protected final SailingServiceAsync sailingService;
|
||||
protected final ErrorReporter errorReporter;
|
||||
|
||||
@@ -33,7 +33,7 @@ public abstract class TableWrapper<T, S extends SelectionModel<T>> implements Is
|
||||
}
|
||||
|
||||
public TableWrapper(SailingServiceAsync sailingService, StringMessages stringMessages, ErrorReporter errorReporter,
|
||||
S selectionModel) {
|
||||
S selectionModel, boolean enablePager) {
|
||||
this.sailingService = sailingService;
|
||||
this.errorReporter = errorReporter;
|
||||
this.selectionModel = selectionModel;
|
||||
@@ -43,6 +43,13 @@ public abstract class TableWrapper<T, S extends SelectionModel<T>> implements Is
|
||||
dataProvider.addDataDisplay(table);
|
||||
table.setSelectionModel(selectionModel);
|
||||
mainPanel.add(table);
|
||||
|
||||
if (enablePager) {
|
||||
table.setPageSize(8);
|
||||
SimplePager pager = new SimplePager();
|
||||
pager.setDisplay(table);
|
||||
mainPanel.add(pager);
|
||||
}
|
||||
}
|
||||
|
||||
public CellTable<T> getTable() {
|
||||
|
||||
+1
-1
@@ -533,7 +533,7 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (!tracTracBoatClassName.equalsIgnoreCase(selectedRegatta.boatClass.getName())) {
|
||||
if (!tracTracBoatClassName.equals(selectedRegatta.boatClass.getName())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -13,7 +13,7 @@ SingleSelectionModel<TrackFileImportDeviceIdentifierDTO>> {
|
||||
|
||||
public TrackFileImportDeviceIdentifierTableWrapper(SailingServiceAsync sailingService, StringMessages stringMessages,
|
||||
ErrorReporter errorReporter) {
|
||||
super(sailingService, stringMessages, errorReporter, new SingleSelectionModel<TrackFileImportDeviceIdentifierDTO>());
|
||||
super(sailingService, stringMessages, errorReporter, new SingleSelectionModel<TrackFileImportDeviceIdentifierDTO>(), true);
|
||||
|
||||
TextColumn<TrackFileImportDeviceIdentifierDTO> uuidColumn = new TextColumn<TrackFileImportDeviceIdentifierDTO>() {
|
||||
@Override
|
||||
|
||||
+38
@@ -2,15 +2,22 @@ package com.sap.sailing.gwt.ui.adminconsole;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.i18n.client.DateTimeFormat;
|
||||
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
|
||||
import com.google.gwt.i18n.client.TimeZone;
|
||||
import com.google.gwt.text.client.DateTimeFormatRenderer;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.HorizontalPanel;
|
||||
import com.sap.sailing.domain.common.dto.RaceDTO;
|
||||
import com.sap.sailing.gwt.ui.client.ErrorReporter;
|
||||
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.sse.gwt.client.dialog.DataEntryDialog.DialogCallback;
|
||||
|
||||
public class TrackedRacesManagementPanel extends AbstractRaceManagementPanel {
|
||||
private final DateTimeFormatRenderer dateFormatter = new DateTimeFormatRenderer(
|
||||
@@ -25,6 +32,37 @@ public class TrackedRacesManagementPanel extends AbstractRaceManagementPanel {
|
||||
public TrackedRacesManagementPanel(final SailingServiceAsync sailingService, ErrorReporter errorReporter,
|
||||
RegattaRefresher regattaRefresher, StringMessages stringConstants) {
|
||||
super(sailingService, errorReporter, regattaRefresher, stringConstants);
|
||||
|
||||
HorizontalPanel controlsPanel = new HorizontalPanel();
|
||||
Button setStartTimeButton = new Button(stringMessages.setStartTimeReceived(), new ClickHandler() {
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
new SetStartTimeReceivedDialog(stringMessages, new DialogCallback<Date>() {
|
||||
@Override
|
||||
public void ok(Date newStartTimeReceived) {
|
||||
sailingService.setStartTimeReceivedForRace(selectedRaceDTO.getRaceIdentifier(), newStartTimeReceived, new AsyncCallback<RaceDTO>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
TrackedRacesManagementPanel.this.errorReporter.reportError("Error setting the received start time: " + caught.getMessage());
|
||||
}
|
||||
@Override
|
||||
public void onSuccess(RaceDTO result) {
|
||||
if (result != null) {
|
||||
selectedRaceDTO = result;
|
||||
refreshSelectedRaceData();
|
||||
|
||||
TrackedRacesManagementPanel.this.regattaRefresher.fillRegattas();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
@Override
|
||||
public void cancel() { }
|
||||
}).show();
|
||||
}
|
||||
});
|
||||
controlsPanel.add(setStartTimeButton);
|
||||
selectedRaceContentPanel.add(controlsPanel);
|
||||
|
||||
raceDataGrid = new Grid(6,2);
|
||||
this.selectedRaceContentPanel.add(raceDataGrid);
|
||||
|
||||
+1
-3
@@ -13,9 +13,7 @@ import com.sap.sailing.gwt.ui.shared.WaypointDTO;
|
||||
public class WaypointTableWrapper<S extends SelectionModel<WaypointDTO>> extends TableWrapper<WaypointDTO, S> {
|
||||
public WaypointTableWrapper(S selectionModel, SailingServiceAsync sailingService, final StringMessages stringMessages,
|
||||
ErrorReporter errorReporter) {
|
||||
super(sailingService, stringMessages, errorReporter, selectionModel);
|
||||
|
||||
mainPanel.add(table);
|
||||
super(sailingService, stringMessages, errorReporter, selectionModel, true);
|
||||
|
||||
TextColumn<WaypointDTO> nameColumn = new TextColumn<WaypointDTO>() {
|
||||
@Override
|
||||
|
||||
+7
-3
@@ -17,26 +17,29 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.dto.FleetDTO;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
|
||||
import com.sap.sailing.gwt.ui.actions.GetRaceTimesInfoAction;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
|
||||
import com.sap.sse.gwt.client.async.AsyncActionsExecutor;
|
||||
|
||||
public class RaceTimesInfoProvider {
|
||||
|
||||
private final SailingServiceAsync sailingService;
|
||||
private final AsyncActionsExecutor asyncActionsExecutor;
|
||||
private final ErrorReporter errorReporter;
|
||||
|
||||
private final Set<RegattaAndRaceIdentifier> raceIdentifiers;
|
||||
private long requestIntervalInMillis;
|
||||
|
||||
private final HashMap<RegattaAndRaceIdentifier, RaceTimesInfoDTO> raceTimesInfos;
|
||||
|
||||
private final Set<RaceTimesInfoProviderListener> listeners;
|
||||
|
||||
/**
|
||||
* The <code>raceIdentifiers</code> has to be non-<code>null</code>, but can be empty.
|
||||
*/
|
||||
public RaceTimesInfoProvider(SailingServiceAsync sailingService, ErrorReporter errorReporter,
|
||||
public RaceTimesInfoProvider(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter,
|
||||
Collection<RegattaAndRaceIdentifier> raceIdentifiers, long requestIntervalInMillis) {
|
||||
this.sailingService = sailingService;
|
||||
this.asyncActionsExecutor = asyncActionsExecutor;
|
||||
this.errorReporter = errorReporter;
|
||||
this.raceIdentifiers = new HashSet<RegattaAndRaceIdentifier>(raceIdentifiers);
|
||||
this.requestIntervalInMillis = requestIntervalInMillis;
|
||||
@@ -97,7 +100,8 @@ public class RaceTimesInfoProvider {
|
||||
private void readTimesInfos() {
|
||||
if (!raceIdentifiers.isEmpty()) {
|
||||
final long clientTimeWhenRequestWasSent = System.currentTimeMillis();
|
||||
sailingService.getRaceTimesInfos(raceIdentifiers, new AsyncCallback<List<RaceTimesInfoDTO>>() {
|
||||
GetRaceTimesInfoAction getRaceTimesInfoAction = new GetRaceTimesInfoAction(sailingService, raceIdentifiers);
|
||||
asyncActionsExecutor.execute(getRaceTimesInfoAction, new AsyncCallback<List<RaceTimesInfoDTO>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError("Error trying to obtain the race time infos: " + caught.getMessage(),
|
||||
|
||||
+8
-2
@@ -19,6 +19,7 @@ import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.PassingInstruction;
|
||||
import com.sap.sailing.domain.common.PolarSheetGenerationResponse;
|
||||
import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
|
||||
import com.sap.sailing.domain.common.RaceIdentifier;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.RegattaIdentifier;
|
||||
import com.sap.sailing.domain.common.ScoringSchemeType;
|
||||
@@ -149,7 +150,7 @@ public interface SailingService extends RemoteService {
|
||||
|
||||
IncrementalOrFullLeaderboardDTO getLeaderboardByName(String leaderboardName, Date date,
|
||||
Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails, boolean addOverallDetails,
|
||||
String previousLeaderboardId) throws Exception;
|
||||
String previousLeaderboardId, boolean fillNetPointsUncorrected) throws Exception;
|
||||
|
||||
List<StrippedLeaderboardDTO> getLeaderboards();
|
||||
|
||||
@@ -511,5 +512,10 @@ public interface SailingService extends RemoteService {
|
||||
* by {@link #getSearchServerNames()} which corresponds to a name of a
|
||||
* {@link RemoteSailingServerReference}, to search a remote server.
|
||||
*/
|
||||
Iterable<LeaderboardSearchResultDTO> search(String serverNameOrNullForMain, KeywordQuery query) throws Exception;
|
||||
Iterable<LeaderboardSearchResultDTO> search(String serverNameOrNullForMain, KeywordQuery query) throws Exception;
|
||||
|
||||
/**
|
||||
* @return The RaceDTO of the modified race or <code>null</code>, if the given newStartTimeReceived was null.
|
||||
*/
|
||||
RaceDTO setStartTimeReceivedForRace(RaceIdentifier raceIdentifier, Date newStartTimeReceived);
|
||||
}
|
||||
+4
-1
@@ -188,7 +188,8 @@ public interface SailingServiceAsync {
|
||||
|
||||
void getLeaderboardByName(String leaderboardName, Date date,
|
||||
Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails,
|
||||
boolean addOverallDetails, String previousLeaderboardId, AsyncCallback<IncrementalOrFullLeaderboardDTO> callback);
|
||||
boolean addOverallDetails, String previousLeaderboardId, boolean fillNetPointsUncorrected,
|
||||
AsyncCallback<IncrementalOrFullLeaderboardDTO> callback);
|
||||
|
||||
void getLeaderboardNames(AsyncCallback<List<String>> callback);
|
||||
|
||||
@@ -633,4 +634,6 @@ public interface SailingServiceAsync {
|
||||
*/
|
||||
void search(String serverNameOrNullForMain, KeywordQuery query,
|
||||
AsyncCallback<Iterable<LeaderboardSearchResultDTO>> callback);
|
||||
|
||||
void setStartTimeReceivedForRace(RaceIdentifier raceIdentifier, Date newStartTimeReceived, AsyncCallback<RaceDTO> callback);
|
||||
}
|
||||
+6
@@ -1049,4 +1049,10 @@ public interface StringMessages extends Messages {
|
||||
String showNumberOfRacesScored();
|
||||
String racesScored();
|
||||
String racesScoredTooltip();
|
||||
String showUncorrectedNetPoints();
|
||||
String setStartTimeReceived();
|
||||
String setStartTimeReceivedDescription();
|
||||
String lastScoreCorrectionsTime();
|
||||
String lastScoreCorrectionsComment();
|
||||
String setTimeToNow();
|
||||
}
|
||||
+6
@@ -1035,3 +1035,9 @@ errorFetchingWindStreamletData=Error fetching wind streamlet data: {0}
|
||||
showWindStreamletOverlay=Show wind streamlets overlay
|
||||
racesScored=Races scored
|
||||
racesScoredTooltip=Number of races the competitor has completed or gotten a score for in this leaderboard.
|
||||
showUncorrectedNetPoints=Show uncorrected net points
|
||||
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''
|
||||
+8
-1
@@ -1028,4 +1028,11 @@ showWindStreamletOverlay=Wind als Streamlets überlagern
|
||||
regattaNameMustNotContainSlashes=Regattaname darf keine Schrägstriche enthalten
|
||||
showNumberOfRacesScored=Anzahl bepunkteter Rennen
|
||||
racesScored=Bepunktete Rennen
|
||||
racesScoredTooltip=Anzahl von Rennen, die der Segler vollendet oder für die er Punkte bekommen hat
|
||||
racesScoredTooltip=Anzahl von Rennen, die der Segler vollendet oder für die er Punkte bekommen hat
|
||||
showUncorrectedNetPoints=Unkorrigierte Netto-Punkte anzeigen
|
||||
setStartTimeReceived=Setze die erhaltene Startzeit
|
||||
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.
|
||||
setStartTimeReceivedDescription=Dies setzt die startTimeReceived des selektierten TrackedRaces, welches nicht persistent ist. Das beudeutet, dass der neue Wert vergessen wird, sobald der Server neu gestartet wurde.
|
||||
lastScoreCorrectionsTime=Letzter Zeitpunkt der Punktzahl-Korrektur
|
||||
lastScoreCorrectionsComment=Letzter Kommentar der Punktzahl-Korrektur
|
||||
setTimeToNow=Setze den Zeitpunkt auf ''Jetzt''
|
||||
+2
-1
@@ -987,4 +987,5 @@ showWindStreamletOverlay=Show wind streamlets overlay
|
||||
showNumberOfRacesScored=Показать количество завершенных гонок
|
||||
racesScored=Завершенные гонки
|
||||
racesScoredTooltip=Количество гонок завершенное участником в данной таблице лидеров
|
||||
regattaNameMustNotContainSlashes=Regatta name must not contain slashes
|
||||
regattaNameMustNotContainSlashes=Regatta name must not contain slashes
|
||||
showUncorrectedNetPoints=Show uncorrected net points
|
||||
+27
-5
@@ -71,6 +71,12 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
|
||||
private final Button slowDownButton;
|
||||
private final Button speedUpButton;
|
||||
|
||||
private final FlowPanel controlsPanel;
|
||||
private final SimplePanel timePanelSlider;
|
||||
private final FlowPanel timePanelSliderFlowWrapper;
|
||||
private final FlowPanel playControlPanel;
|
||||
private final FlowPanel timePanelInnerWrapper;
|
||||
|
||||
/**
|
||||
* the minimum time the slider extends it's time when the end of the slider is reached
|
||||
*/
|
||||
@@ -99,14 +105,16 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
|
||||
timer.addTimeListener(this);
|
||||
timer.addPlayStateListener(this);
|
||||
timeRangeProvider.addTimeRangeChangeListener(this);
|
||||
FlowPanel timePanelInnerWrapper = new FlowPanel();
|
||||
timePanelInnerWrapper = new FlowPanel();
|
||||
timePanelInnerWrapper.setStyleName("timePanelInnerWrapper");
|
||||
timePanelInnerWrapper.setSize("100%", "100%");
|
||||
|
||||
SimplePanel timePanelSlider = new SimplePanel();
|
||||
timePanelSlider = new SimplePanel();
|
||||
timePanelSliderFlowWrapper = new FlowPanel();
|
||||
timePanelSlider.setStyleName("timePanelSlider");
|
||||
timePanelSlider.getElement().getStyle().setPaddingLeft(66, Unit.PX);
|
||||
timePanelSlider.getElement().getStyle().setPaddingRight(66, Unit.PX);
|
||||
timePanelSliderFlowWrapper.add(timePanelSlider);
|
||||
|
||||
playSpeedImg = resources.timesliderPlaySpeedIcon();
|
||||
playPauseButton = new Button("");
|
||||
@@ -141,16 +149,16 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
|
||||
}
|
||||
});
|
||||
|
||||
timePanelInnerWrapper.add(timePanelSlider);
|
||||
timePanelInnerWrapper.add(timePanelSliderFlowWrapper);
|
||||
timePanelSlider.add(timeSlider);
|
||||
|
||||
FlowPanel controlsPanel = new FlowPanel();
|
||||
controlsPanel = new FlowPanel();
|
||||
|
||||
controlsPanel.setStyleName("timePanel-controls");
|
||||
timePanelInnerWrapper.add(controlsPanel);
|
||||
|
||||
// play button control
|
||||
FlowPanel playControlPanel = new FlowPanel();
|
||||
playControlPanel = new FlowPanel();
|
||||
playControlPanel.setStyleName("timePanel-controls-play");
|
||||
controlsPanel.add(playControlPanel);
|
||||
|
||||
@@ -293,6 +301,20 @@ public class TimePanel<T extends TimePanelSettings> extends SimplePanel implemen
|
||||
controlsPanel.add(timeToStartControlPanel);
|
||||
}
|
||||
|
||||
public void hideControlsPanelAndMovePlayButtonUp() {
|
||||
controlsPanel.remove(playControlPanel);
|
||||
timePanelInnerWrapper.remove(controlsPanel);
|
||||
timePanelSliderFlowWrapper.insert(playControlPanel, 0);
|
||||
playControlPanel.setStyleName("timePanel-timeslider-play");
|
||||
}
|
||||
|
||||
public void showControlsPanelAndMovePlayButtonDown() {
|
||||
timePanelInnerWrapper.add(controlsPanel);
|
||||
timePanelSliderFlowWrapper.remove(playControlPanel);
|
||||
controlsPanel.insert(playControlPanel, 0);
|
||||
playControlPanel.setStyleName("timePanel-controls-play");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void timeChanged(Date newTime, Date oldTime) {
|
||||
if (timeRangeProvider.isZoomed()) {
|
||||
|
||||
+13
-4
@@ -465,7 +465,8 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
|
||||
getSailingService().getLeaderboardByName(getLeaderboardName(),
|
||||
timer.getPlayMode() == PlayModes.Live ? null : getLeaderboardDisplayDate(),
|
||||
/* namesOfRacesForWhichToLoadLegDetails */getNamesOfExpandedRaces(),
|
||||
shallAddOverallDetails(), previousLeaderboard.getId(), new MarkedAsyncCallback<IncrementalOrFullLeaderboardDTO>(
|
||||
shallAddOverallDetails(), previousLeaderboard.getId(), /* fillNetPointsUncorrected */ false,
|
||||
new MarkedAsyncCallback<IncrementalOrFullLeaderboardDTO>(
|
||||
new AsyncCallback<IncrementalOrFullLeaderboardDTO>() {
|
||||
@Override
|
||||
public void onSuccess(IncrementalOrFullLeaderboardDTO result) {
|
||||
@@ -518,7 +519,7 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
|
||||
} else {
|
||||
trackedRacesIdentifiers = Collections.emptyList();
|
||||
}
|
||||
raceTimesInfoProvider = new RaceTimesInfoProvider(getSailingService(), errorReporter,
|
||||
raceTimesInfoProvider = new RaceTimesInfoProvider(getSailingService(), asyncActionsExecutor, errorReporter,
|
||||
trackedRacesIdentifiers, timer.getRefreshInterval());
|
||||
}
|
||||
return raceTimesInfoProvider;
|
||||
@@ -1896,8 +1897,8 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
|
||||
if (needsDataLoading()) {
|
||||
GetLeaderboardByNameAction getLeaderboardByNameAction = new GetLeaderboardByNameAction(sailingService,
|
||||
getLeaderboardName(), useNullAsTimePoint() ? null : date,
|
||||
/* namesOfRacesForWhichToLoadLegDetails */getNamesOfExpandedRaces(),
|
||||
shallAddOverallDetails(), /* previousLeaderboard */ getLeaderboard(), timer, errorReporter, stringMessages);
|
||||
/* namesOfRacesForWhichToLoadLegDetails */getNamesOfExpandedRaces(), shallAddOverallDetails(), /* previousLeaderboard */
|
||||
getLeaderboard(), isFillNetPointsUncorrected(), timer, errorReporter, stringMessages);
|
||||
this.asyncActionsExecutor.execute(getLeaderboardByNameAction, LOAD_LEADERBOARD_DATA_CATEGORY,
|
||||
new AsyncCallback<LeaderboardDTO>() {
|
||||
@Override
|
||||
@@ -1919,6 +1920,10 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean isFillNetPointsUncorrected() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* In {@link PlayModes#Live live mode}, when {@link #loadCompleteLeaderboard(Date) loading the leaderboard contents}, <code>null</code>
|
||||
* is used as time point. The condition for this is encapsulated in this method so others can find out. For example, when a time change
|
||||
@@ -2716,4 +2721,8 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS
|
||||
timer.removeTimeListener(this);
|
||||
}
|
||||
}
|
||||
|
||||
protected Timer getTimer() {
|
||||
return timer;
|
||||
}
|
||||
}
|
||||
+26
-5
@@ -17,6 +17,8 @@ import com.google.gwt.cell.client.TextCell;
|
||||
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.ValueChangeEvent;
|
||||
import com.google.gwt.event.logical.shared.ValueChangeHandler;
|
||||
import com.google.gwt.resources.client.ImageResource;
|
||||
import com.google.gwt.safehtml.shared.SafeHtml;
|
||||
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
|
||||
@@ -34,6 +36,7 @@ import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.AbstractImagePrototype;
|
||||
import com.google.gwt.user.client.ui.Anchor;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.CheckBox;
|
||||
import com.google.gwt.user.client.ui.Grid;
|
||||
import com.google.gwt.user.client.ui.ImageResourceRenderer;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
@@ -83,6 +86,8 @@ public class EditableLeaderboardPanel extends LeaderboardPanel {
|
||||
|
||||
final private CellTable<CompetitorDTO> suppressedCompetitorsTable;
|
||||
final private ListDataProvider<CompetitorDTO> suppressedCompetitorsShown;
|
||||
|
||||
private CheckBox showUncorrectedNetPointsCheckbox;
|
||||
|
||||
private class SettingsClickHandler implements ClickHandler {
|
||||
private final StringMessages stringMessages;
|
||||
@@ -630,13 +635,13 @@ public class EditableLeaderboardPanel extends LeaderboardPanel {
|
||||
|
||||
|
||||
|
||||
Grid scoreCorrectionInfoGrid = new Grid(3,3);
|
||||
Grid scoreCorrectionInfoGrid = new Grid(3,4);
|
||||
scoreCorrectionInfoGrid.setCellPadding(3);
|
||||
scoreCorrectionInfoGrid.setWidget(0, 0, new Label("Last score correction time:"));
|
||||
scoreCorrectionInfoGrid.setWidget(0, 0, new Label(stringMessages.lastScoreCorrectionsTime() + ":"));
|
||||
lastScoreCorrectionTimeBox = new DateBox();
|
||||
scoreCorrectionInfoGrid.setWidget(0, 1, lastScoreCorrectionTimeBox);
|
||||
|
||||
scoreCorrectionInfoGrid.setWidget(1, 0, new Label("Last score correction comment:"));
|
||||
scoreCorrectionInfoGrid.setWidget(1, 0, new Label(stringMessages.lastScoreCorrectionsComment() + ":"));
|
||||
lastScoreCorrectionCommentBox = new TextBox();
|
||||
scoreCorrectionInfoGrid.setWidget(1, 1, lastScoreCorrectionCommentBox);
|
||||
|
||||
@@ -664,9 +669,17 @@ public class EditableLeaderboardPanel extends LeaderboardPanel {
|
||||
}
|
||||
});
|
||||
|
||||
final Button setScoreCorrectionDefaultTimeBtn = new Button("Set time to 'now'");
|
||||
final Button setScoreCorrectionDefaultTimeBtn = new Button(stringMessages.setTimeToNow()); // FIXME i18n
|
||||
setScoreCorrectionDefaultTimeBtn.addStyleName("inlineButton");
|
||||
scoreCorrectionInfoGrid.setWidget(0, 2, setScoreCorrectionDefaultTimeBtn);
|
||||
scoreCorrectionInfoGrid.setWidget(0, 2, setScoreCorrectionDefaultTimeBtn);
|
||||
showUncorrectedNetPointsCheckbox = new CheckBox(stringMessages.showUncorrectedNetPoints());
|
||||
showUncorrectedNetPointsCheckbox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
|
||||
@Override
|
||||
public void onValueChange(ValueChangeEvent<Boolean> event) {
|
||||
timeChanged(getTimer().getTime(), getTimer().getTime());
|
||||
}
|
||||
});
|
||||
scoreCorrectionInfoGrid.setWidget(0, 3, showUncorrectedNetPointsCheckbox);
|
||||
|
||||
setScoreCorrectionDefaultTimeBtn.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
@@ -883,6 +896,14 @@ public class EditableLeaderboardPanel extends LeaderboardPanel {
|
||||
maxPointsDropDownCellProvider, netPointsEditCellProvider));
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* Computing the uncorrected scores can be expensive for large leaderboards; disable by default but allow user to enable
|
||||
*/
|
||||
@Override
|
||||
protected boolean isFillNetPointsUncorrected() {
|
||||
return showUncorrectedNetPointsCheckbox==null?false:showUncorrectedNetPointsCheckbox.getValue();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateLeaderboard(LeaderboardDTO leaderboard) {
|
||||
|
||||
+31
-11
@@ -29,6 +29,7 @@ import com.sap.sailing.gwt.ui.client.MediaServiceAsync;
|
||||
import com.sap.sailing.gwt.ui.client.ParallelExecutionCallback;
|
||||
import com.sap.sailing.gwt.ui.client.ParallelExecutionHolder;
|
||||
import com.sap.sailing.gwt.ui.client.RaceSelectionModel;
|
||||
import com.sap.sailing.gwt.ui.client.RaceTimePanel;
|
||||
import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider;
|
||||
import com.sap.sailing.gwt.ui.client.RemoteServiceMappingConstants;
|
||||
import com.sap.sailing.gwt.ui.client.SailingService;
|
||||
@@ -40,6 +41,7 @@ import com.sap.sailing.gwt.ui.shared.RegattaDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.UserDTO;
|
||||
import com.sap.sse.gwt.client.EntryPointHelper;
|
||||
import com.sap.sse.gwt.client.async.AsyncActionsExecutor;
|
||||
import com.sap.sse.gwt.client.player.Timer;
|
||||
import com.sap.sse.gwt.client.player.Timer.PlayModes;
|
||||
import com.sap.sse.gwt.client.useragent.UserAgentChecker;
|
||||
@@ -65,7 +67,7 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint {
|
||||
private final MediaServiceAsync mediaService = GWT.create(MediaService.class);
|
||||
private final UserManagementServiceAsync userManagementService = GWT.create(UserManagementService.class);
|
||||
|
||||
private boolean toolbarHidden;
|
||||
private boolean toolbarAndLogoAndTitleBarHidden;
|
||||
|
||||
@Override
|
||||
protected void doOnModuleLoad() {
|
||||
@@ -172,8 +174,9 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint {
|
||||
List<RegattaAndRaceIdentifier> singletonList = Collections.singletonList(selectedRace.getRaceIdentifier());
|
||||
raceSelectionModel.setSelection(singletonList);
|
||||
Timer timer = new Timer(PlayModes.Replay, 1000l);
|
||||
RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, this, singletonList, 5000l /* requestInterval*/);
|
||||
RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, mediaService, user, timer, raceSelectionModel, leaderboardName,
|
||||
AsyncActionsExecutor asyncActionsExecutor = new AsyncActionsExecutor();
|
||||
RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, asyncActionsExecutor, this, singletonList, 5000l /* requestInterval*/);
|
||||
RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, mediaService, asyncActionsExecutor, user, timer, raceSelectionModel, leaderboardName,
|
||||
leaderboardGroupName, raceboardViewConfig, RaceBoardEntryPoint.this, stringMessages, userAgent, raceTimesInfoProvider, showMapControls);
|
||||
raceBoardPanel.fillRegattas(regattas);
|
||||
createRaceBoardInOneScreenMode(raceBoardPanel, raceboardViewConfig);
|
||||
@@ -238,15 +241,26 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint {
|
||||
lbl.setStyleName("browserOptimizedMessage");
|
||||
toolbarPanel.add(lbl);
|
||||
}
|
||||
FlowPanel logoAndTitlePanel = createLogoAndTitlePanel(raceBoardPanel);
|
||||
final FlowPanel logoAndTitlePanel = createLogoAndTitlePanel(raceBoardPanel);
|
||||
FlowPanel timePanel = createTimePanel(raceBoardPanel);
|
||||
p.addNorth(logoAndTitlePanel, 68);
|
||||
p.addNorth(toolbarPanel, 40);
|
||||
toolbarHidden = false;
|
||||
toolbarAndLogoAndTitleBarHidden = false;
|
||||
if (!raceboardViewConfiguration.isShowNavigationPanel()) {
|
||||
p.setWidgetHidden(toolbarPanel, true);
|
||||
toolbarHidden = true;
|
||||
globalNavigationPanel.setVisible(false);
|
||||
toolbarAndLogoAndTitleBarHidden = true;
|
||||
}
|
||||
|
||||
p.addSouth(timePanel, 90);
|
||||
p.add(raceBoardPanel);
|
||||
p.addStyleName("dockLayoutPanel");
|
||||
|
||||
addModeratorShortkeyFunctionality(p, toolbarPanel, raceBoardPanel, timePanel);
|
||||
}
|
||||
|
||||
private void addModeratorShortkeyFunctionality(final DockLayoutPanel p, final Panel toolbarPanel,
|
||||
final RaceBoardPanel raceBoardPanel, final Panel timeWrapperPanel) {
|
||||
Event.addNativePreviewHandler(new NativePreviewHandler() {
|
||||
@Override
|
||||
public void onPreviewNativeEvent(NativePreviewEvent event) {
|
||||
@@ -257,16 +271,22 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint {
|
||||
Scheduler.get().scheduleDeferred(new Command() {
|
||||
@Override
|
||||
public void execute() {
|
||||
p.setWidgetHidden(toolbarPanel, !toolbarHidden);
|
||||
toolbarHidden = !toolbarHidden;
|
||||
p.setWidgetHidden(toolbarPanel, !toolbarAndLogoAndTitleBarHidden);
|
||||
globalNavigationPanel.setVisible(toolbarAndLogoAndTitleBarHidden);
|
||||
toolbarAndLogoAndTitleBarHidden = !toolbarAndLogoAndTitleBarHidden;
|
||||
RaceTimePanel timePanel = raceBoardPanel.getTimePanel();
|
||||
if (toolbarAndLogoAndTitleBarHidden) {
|
||||
timePanel.hideControlsPanelAndMovePlayButtonUp();
|
||||
p.setWidgetSize(timeWrapperPanel, 53);
|
||||
} else {
|
||||
timePanel.showControlsPanelAndMovePlayButtonDown();
|
||||
p.setWidgetSize(timeWrapperPanel, 90);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
});
|
||||
p.addSouth(timePanel, 90);
|
||||
p.add(raceBoardPanel);
|
||||
p.addStyleName("dockLayoutPanel");
|
||||
}
|
||||
}
|
||||
+3
-3
@@ -131,7 +131,7 @@ public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, Ra
|
||||
|
||||
private final static String LOCAL_STORAGE_COMPETITORS_FILTER_SETS_KEY = "sailingAnalytics.raceBoard.competitorsFilterSets";
|
||||
|
||||
public RaceBoardPanel(SailingServiceAsync sailingService, MediaServiceAsync mediaService, UserDTO theUser, Timer timer,
|
||||
public RaceBoardPanel(SailingServiceAsync sailingService, MediaServiceAsync mediaService, AsyncActionsExecutor asyncActionsExecutor, UserDTO theUser, Timer timer,
|
||||
RaceSelectionProvider theRaceSelectionProvider, String leaderboardName, String leaderboardGroupName,
|
||||
RaceBoardViewConfiguration raceboardViewConfiguration, ErrorReporter errorReporter, final StringMessages stringMessages,
|
||||
UserAgentDetails userAgent, RaceTimesInfoProvider raceTimesInfoProvider, boolean showMapControls) {
|
||||
@@ -150,7 +150,7 @@ public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, Ra
|
||||
racesByIdentifier = new HashMap<RaceIdentifier, RaceDTO>();
|
||||
selectedRaceIdentifier = raceSelectionProvider.getSelectedRaces().iterator().next();
|
||||
this.setRaceBoardName(selectedRaceIdentifier.getRaceName());
|
||||
asyncActionsExecutor = new AsyncActionsExecutor();
|
||||
this.asyncActionsExecutor = asyncActionsExecutor;
|
||||
FlowPanel mainPanel = new FlowPanel();
|
||||
mainPanel.setSize("100%", "100%");
|
||||
setWidget(mainPanel);
|
||||
@@ -538,7 +538,7 @@ public class RaceBoardPanel extends SimplePanel implements RegattasDisplayer, Ra
|
||||
return toolbarPanel;
|
||||
}
|
||||
|
||||
public Panel getTimePanel() {
|
||||
public RaceTimePanel getTimePanel() {
|
||||
return timePanel;
|
||||
}
|
||||
|
||||
|
||||
+10
-8
@@ -38,17 +38,19 @@ public class FlagImageResolver {
|
||||
flagImageMap.put(Flags.ZULU, resources.flagZulu());
|
||||
}
|
||||
|
||||
public ImageResource resolveFlagToImage(Flags flag) {
|
||||
return flagImageMap.get(flag);
|
||||
public ImageResource resolveFlagToImage(Flags flag, boolean isDisplayed, boolean displayStateChanged) {
|
||||
return isDisplayed || displayStateChanged ? flagImageMap.get(flag) : null;
|
||||
}
|
||||
|
||||
public ImageResource resolveFlagDirectionToImage(boolean isDisplayed) {
|
||||
ImageResource result;
|
||||
public ImageResource resolveFlagDirectionToImage(boolean isDisplayed, boolean displayStateChanged) {
|
||||
ImageResource result = null;
|
||||
|
||||
if (isDisplayed) {
|
||||
result = resources.arrowUp();
|
||||
} else {
|
||||
result = resources.arrowDown();
|
||||
if (displayStateChanged) {
|
||||
if (isDisplayed) {
|
||||
result = resources.arrowUp();
|
||||
} else {
|
||||
result = resources.arrowDown();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
+15
-4
@@ -31,7 +31,7 @@ public class RaceDetailPanel extends SimplePanel {
|
||||
d = -d;
|
||||
}
|
||||
NumberFormat decimalFormat = NumberFormat.getFormat("##.###");
|
||||
return "" + d + '\u00B0' + m + '\u2032' + decimalFormat.format(s) + '\u2033'; // add º, ', " symbols
|
||||
return "" + d + '\u00B0' + m + '\u2032' + decimalFormat.format(s) + '\u2033'; // add �, ', " symbols
|
||||
}
|
||||
|
||||
private static final int MAX_PROCEDURE_FIELDS = 2;
|
||||
@@ -50,7 +50,9 @@ public class RaceDetailPanel extends SimplePanel {
|
||||
private final Label finishTimeLabel = new Label();
|
||||
private final Label finishDurationLabel = new Label();
|
||||
private final Label protestTimeLabel = new Label();
|
||||
private final Label protestDateLabel = new Label();
|
||||
private final Label updateTimeLabel = new Label();
|
||||
private final Label updateDateLabel = new Label();
|
||||
private final Label vesselPositionLabel = new Label();
|
||||
private final Label windLabel = new Label();
|
||||
private final Label startProcedureLabel = new Label();
|
||||
@@ -94,9 +96,9 @@ public class RaceDetailPanel extends SimplePanel {
|
||||
basicsGrid.setWidget(2, 1, protestTimeLabel);
|
||||
basicsGrid.setWidget(0, 2, startDateLabel);
|
||||
basicsGrid.setWidget(1, 2, finishDurationLabel);
|
||||
basicsGrid.setWidget(2, 2, new Label(""));
|
||||
basicsGrid.setWidget(2, 2, protestDateLabel);
|
||||
|
||||
Grid managementGrid = new Grid(3, 2);
|
||||
Grid managementGrid = new Grid(3, 3);
|
||||
managementGrid.setCellSpacing(cellSpacing);
|
||||
managementGrid.setWidget(0, 0, new Label("Last update at"));
|
||||
managementGrid.setWidget(1, 0, new Label("Vessel position"));
|
||||
@@ -104,6 +106,7 @@ public class RaceDetailPanel extends SimplePanel {
|
||||
managementGrid.setWidget(0, 1, updateTimeLabel);
|
||||
managementGrid.setWidget(1, 1, vesselPositionLabel);
|
||||
managementGrid.setWidget(2, 1, windLabel);
|
||||
managementGrid.setWidget(0, 2, updateDateLabel);
|
||||
|
||||
procedureGrid = new FlexTable();
|
||||
procedureGrid.setCellSpacing(cellSpacing);
|
||||
@@ -138,7 +141,7 @@ public class RaceDetailPanel extends SimplePanel {
|
||||
return;
|
||||
}
|
||||
String statusText = flagInterpreter.getMeaningOfRaceStateAndFlags(data.raceInfo.lastStatus,
|
||||
data.raceInfo.lastUpperFlag, data.raceInfo.lastLowerFlag, data.raceInfo.isLastFlagDisplayed);
|
||||
data.raceInfo.lastUpperFlag, data.raceInfo.lastLowerFlag, data.raceInfo.lastFlagsAreDisplayed);
|
||||
String headerLabelText = data.regattaDisplayName + " " + data.raceInfo.fleetName + " " + data.raceInfo.raceName
|
||||
+ " (" + statusText + ")";
|
||||
headerLabel.setText(stringMessages.showingDetailsOfRace(headerLabelText));
|
||||
@@ -166,10 +169,18 @@ public class RaceDetailPanel extends SimplePanel {
|
||||
.format(data.raceInfo.protestFinishTime);
|
||||
protestTimeLabel.setText(protestTimeText);
|
||||
|
||||
String protestDateText = data.raceInfo.protestFinishTime == null ? "" : dateFormatter
|
||||
.format(data.raceInfo.protestFinishTime);
|
||||
protestDateLabel.setText(protestDateText);
|
||||
|
||||
String updateTimeText = data.raceInfo.lastUpdateTime == null ? "-" : timeFormatter
|
||||
.format(data.raceInfo.lastUpdateTime);
|
||||
updateTimeLabel.setText(updateTimeText);
|
||||
|
||||
String updateDateText = data.raceInfo.lastUpdateTime == null ? "" : dateFormatter
|
||||
.format(data.raceInfo.lastUpdateTime);
|
||||
updateDateLabel.setText(updateDateText);
|
||||
|
||||
String vesselPositionText = stringMessages.unknown();
|
||||
String windText = stringMessages.unknown();
|
||||
if (data.raceInfo.lastWind != null) {
|
||||
|
||||
+40
-17
@@ -16,6 +16,7 @@ import com.google.gwt.core.client.GWT;
|
||||
import com.google.gwt.event.dom.client.ClickEvent;
|
||||
import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.i18n.client.DateTimeFormat;
|
||||
import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat;
|
||||
import com.google.gwt.json.client.JSONObject;
|
||||
import com.google.gwt.json.client.JSONParser;
|
||||
import com.google.gwt.json.client.JSONValue;
|
||||
@@ -27,6 +28,7 @@ import com.google.gwt.user.cellview.client.CellTable;
|
||||
import com.google.gwt.user.cellview.client.Column;
|
||||
import com.google.gwt.user.cellview.client.ColumnSortEvent;
|
||||
import com.google.gwt.user.cellview.client.ColumnSortEvent.ListHandler;
|
||||
import com.google.gwt.user.cellview.client.ColumnSortList.ColumnSortInfo;
|
||||
import com.google.gwt.user.cellview.client.TextColumn;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.Anchor;
|
||||
@@ -38,6 +40,7 @@ import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.SimplePanel;
|
||||
import com.google.gwt.user.client.ui.VerticalPanel;
|
||||
import com.google.gwt.user.client.ui.Widget;
|
||||
import com.google.gwt.user.datepicker.client.CalendarUtil;
|
||||
import com.google.gwt.view.client.ListDataProvider;
|
||||
import com.sap.sailing.domain.common.LeaderboardNameConstants;
|
||||
import com.sap.sailing.domain.common.PassingInstruction;
|
||||
@@ -83,9 +86,11 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
|
||||
private ListDataProvider<RegattaOverviewEntryDTO> regattaOverviewDataProvider;
|
||||
private final VerticalPanel mainPanel;
|
||||
private final DateTimeFormat timeFormatter = DateTimeFormat.getFormat("HH:mm:ss");
|
||||
private final DateTimeFormat dateFormatter = DateTimeFormat.getFormat(PredefinedFormat.DATE_SHORT);
|
||||
|
||||
private final SailingServiceAsync sailingService;
|
||||
private final StringMessages stringMessages;
|
||||
private final ErrorReporter errorReporter;
|
||||
private final UUID eventId;
|
||||
|
||||
private EventDTO eventDTO;
|
||||
@@ -126,6 +131,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
|
||||
final StringMessages stringMessages, final UUID eventId, RegattaRaceStatesSettings settings, Timer timerToSynchronize) {
|
||||
this.sailingService = sailingService;
|
||||
this.stringMessages = stringMessages;
|
||||
this.errorReporter = errorReporter;
|
||||
this.eventId = eventId;
|
||||
this.allEntries = new ArrayList<RegattaOverviewEntryDTO>();
|
||||
this.timerToSynchronize = timerToSynchronize;
|
||||
@@ -190,7 +196,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
|
||||
new AsyncCallback<List<RegattaOverviewEntryDTO>>() {
|
||||
@Override
|
||||
public void onFailure(Throwable cause) {
|
||||
|
||||
errorReporter.reportError(cause.getMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -207,6 +213,18 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
private String formatTime(Date time) {
|
||||
String result = "-";
|
||||
if (time != null) {
|
||||
result = "";
|
||||
if (! CalendarUtil.isSameDate(time, new Date())) {
|
||||
result += dateFormatter.format(time) + " ";
|
||||
}
|
||||
result += timeFormatter.format(time);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private CellTable<RegattaOverviewEntryDTO> createRegattaTable() {
|
||||
CellTable<RegattaOverviewEntryDTO> table = new CellTable<RegattaOverviewEntryDTO>(/* pageSize */10000, tableRes);
|
||||
@@ -341,11 +359,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
|
||||
TextColumn<RegattaOverviewEntryDTO> raceStartTimeColumn = new TextColumn<RegattaOverviewEntryDTO>() {
|
||||
@Override
|
||||
public String getValue(RegattaOverviewEntryDTO entryDTO) {
|
||||
String result = "-";
|
||||
if (entryDTO.raceInfo.startTime != null) {
|
||||
result = timeFormatter.format(entryDTO.raceInfo.startTime);
|
||||
}
|
||||
return result;
|
||||
return formatTime(entryDTO.raceInfo.startTime);
|
||||
}
|
||||
};
|
||||
raceStartTimeColumn.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
|
||||
@@ -375,7 +389,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
|
||||
@Override
|
||||
public SafeHtml getValue(final RegattaOverviewEntryDTO entryDTO) {
|
||||
String status = flagInterpreter.getMeaningOfRaceStateAndFlags(entryDTO.raceInfo.lastStatus, entryDTO.raceInfo.lastUpperFlag,
|
||||
entryDTO.raceInfo.lastLowerFlag, entryDTO.raceInfo.isLastFlagDisplayed);
|
||||
entryDTO.raceInfo.lastLowerFlag, entryDTO.raceInfo.lastFlagsAreDisplayed);
|
||||
SafeHtmlBuilder sb = new SafeHtmlBuilder();
|
||||
sb.appendHtmlConstant("<a class=\"pointeredLink\">");
|
||||
sb.appendEscaped(status);
|
||||
@@ -444,12 +458,13 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
|
||||
tooltip += " " + FlagsMeaningExplanator.getFlagsMeaning(stringMessages,
|
||||
entryDTO.raceInfo.lastUpperFlag,
|
||||
entryDTO.raceInfo.lastLowerFlag,
|
||||
entryDTO.raceInfo.isLastFlagDisplayed);
|
||||
entryDTO.raceInfo.lastFlagsAreDisplayed);
|
||||
}
|
||||
return SailingFlagsBuilder.render(
|
||||
entryDTO.raceInfo.lastUpperFlag,
|
||||
entryDTO.raceInfo.lastLowerFlag,
|
||||
entryDTO.raceInfo.isLastFlagDisplayed,
|
||||
entryDTO.raceInfo.lastFlagsAreDisplayed,
|
||||
entryDTO.raceInfo.lastFlagsDisplayedStateChanged,
|
||||
tooltip);
|
||||
}
|
||||
|
||||
@@ -459,18 +474,25 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
|
||||
TextColumn<RegattaOverviewEntryDTO> lastUpdateColumn = new TextColumn<RegattaOverviewEntryDTO>() {
|
||||
@Override
|
||||
public String getValue(RegattaOverviewEntryDTO entryDTO) {
|
||||
String lastUpdateTime = "-";
|
||||
if (entryDTO.raceInfo.lastUpdateTime != null) {
|
||||
lastUpdateTime = timeFormatter.format(entryDTO.raceInfo.lastUpdateTime);
|
||||
}
|
||||
return lastUpdateTime;
|
||||
return formatTime(entryDTO.raceInfo.lastUpdateTime);
|
||||
}
|
||||
};
|
||||
lastUpdateColumn.setSortable(true);
|
||||
regattaOverviewListHandler.setComparator(lastUpdateColumn, new Comparator<RegattaOverviewEntryDTO>() {
|
||||
@Override
|
||||
public int compare(RegattaOverviewEntryDTO left, RegattaOverviewEntryDTO right) {
|
||||
return left.raceInfo.lastUpdateTime.compareTo(right.raceInfo.lastUpdateTime);
|
||||
boolean nullGreater = false;
|
||||
Date leftLastUpdateTime = left.raceInfo.lastUpdateTime;
|
||||
Date rightLastUpdateTime = right.raceInfo.lastUpdateTime;
|
||||
|
||||
if (leftLastUpdateTime == rightLastUpdateTime) {
|
||||
return 0;
|
||||
} else if (leftLastUpdateTime == null) {
|
||||
return (nullGreater ? 1 : -1);
|
||||
} else if (rightLastUpdateTime == null) {
|
||||
return (nullGreater ? -1 : 1);
|
||||
}
|
||||
return leftLastUpdateTime.compareTo(rightLastUpdateTime);
|
||||
}
|
||||
});
|
||||
lastUpdateColumn.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_CENTER);
|
||||
@@ -486,7 +508,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
|
||||
} else if (entryDTO.raceInfo.lastLowerFlag != null && entryDTO.raceInfo.lastLowerFlag.equals(Flags.HOTEL)) {
|
||||
additionalInformation.append(stringMessages.furtherSignalsAshore());
|
||||
isInfoBefore = true;
|
||||
} else if (entryDTO.raceInfo.lastUpperFlag != null && entryDTO.raceInfo.lastUpperFlag.equals(Flags.XRAY) && entryDTO.raceInfo.isLastFlagDisplayed) {
|
||||
} else if (entryDTO.raceInfo.lastUpperFlag != null && entryDTO.raceInfo.lastUpperFlag.equals(Flags.XRAY) && entryDTO.raceInfo.lastFlagsAreDisplayed) {
|
||||
additionalInformation.append(stringMessages.earlyStarters());
|
||||
isInfoBefore = true;
|
||||
}
|
||||
@@ -530,7 +552,8 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
|
||||
table.addColumn(raceAdditionalInformationColumn, stringMessages.additionalInformation());
|
||||
|
||||
table.addColumnSortHandler(regattaOverviewListHandler);
|
||||
table.getColumnSortList().push(courseAreaColumn);
|
||||
ColumnSortInfo lastUpdateColumnSortInfo = new ColumnSortInfo(lastUpdateColumn, /*ascending*/ false);
|
||||
table.getColumnSortList().push(lastUpdateColumnSortInfo);
|
||||
|
||||
return table;
|
||||
}
|
||||
|
||||
+5
-5
@@ -40,15 +40,15 @@ public class SailingFlagsBuilder {
|
||||
return SafeStylesUtils.fromTrustedString("float:left;padding-right:10px;");
|
||||
}
|
||||
|
||||
public static SafeHtml render(Flags upperFlag, Flags lowerFlag, boolean isDisplayed, String tooltip) {
|
||||
public static SafeHtml render(Flags upperFlag, Flags lowerFlag, boolean isDisplayed, boolean displayStateChanged, String tooltip) {
|
||||
FlagImageResolver flagImageResolver = new FlagImageResolver();
|
||||
ImageResource upperFlagImage = flagImageResolver.resolveFlagToImage(upperFlag);
|
||||
ImageResource lowerFlagImage = flagImageResolver.resolveFlagToImage(lowerFlag);
|
||||
ImageResource directionImage = flagImageResolver.resolveFlagDirectionToImage(isDisplayed);
|
||||
ImageResource upperFlagImage = flagImageResolver.resolveFlagToImage(upperFlag, isDisplayed, displayStateChanged);
|
||||
ImageResource lowerFlagImage = flagImageResolver.resolveFlagToImage(lowerFlag, isDisplayed, displayStateChanged);
|
||||
ImageResource directionImage = flagImageResolver.resolveFlagDirectionToImage(isDisplayed, displayStateChanged);
|
||||
|
||||
SafeHtml upperFlagHtml = (upperFlagImage != null) ? makeImagePrototype(upperFlagImage).getSafeHtml() : SafeHtmlUtils.EMPTY_SAFE_HTML;
|
||||
SafeHtml lowerFlagHtml = (lowerFlagImage != null) ? makeImagePrototype(lowerFlagImage).getSafeHtml() : SafeHtmlUtils.EMPTY_SAFE_HTML;
|
||||
SafeHtml directionHtml = (upperFlagImage != null) ? makeImagePrototype(directionImage).getSafeHtml() : SafeHtmlUtils.EMPTY_SAFE_HTML;
|
||||
SafeHtml directionHtml = (directionImage != null) ? makeImagePrototype(directionImage).getSafeHtml() : SafeHtmlUtils.EMPTY_SAFE_HTML;
|
||||
|
||||
SafeHtmlBuilder builder = new SafeHtmlBuilder();
|
||||
SafeStyles imgStyle = getImageStyle();
|
||||
|
||||
+88
-36
@@ -67,6 +67,7 @@ import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.LeaderboardGroupBase;
|
||||
import com.sap.sailing.domain.base.LeaderboardSearchResult;
|
||||
import com.sap.sailing.domain.base.LeaderboardSearchResultBase;
|
||||
import com.sap.sailing.domain.base.Leg;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.Nationality;
|
||||
import com.sap.sailing.domain.base.RaceColumn;
|
||||
@@ -117,6 +118,7 @@ import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
|
||||
import com.sap.sailing.domain.common.PolarSheetsData;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.RaceFetcher;
|
||||
import com.sap.sailing.domain.common.RaceIdentifier;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.RegattaFetcher;
|
||||
import com.sap.sailing.domain.common.RegattaIdentifier;
|
||||
@@ -651,7 +653,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
@Override
|
||||
public IncrementalOrFullLeaderboardDTO getLeaderboardByName(final String leaderboardName, final Date date,
|
||||
final Collection<String> namesOfRaceColumnsForWhichToLoadLegDetails, boolean addOverallDetails,
|
||||
String previousLeaderboardId) throws NoWindException, InterruptedException, ExecutionException,
|
||||
String previousLeaderboardId, boolean fillNetPointsUncorrected) throws NoWindException, InterruptedException, ExecutionException,
|
||||
IllegalArgumentException {
|
||||
try {
|
||||
long startOfRequestHandling = System.currentTimeMillis();
|
||||
@@ -665,7 +667,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
timePoint = new MillisecondsTimePoint(date);
|
||||
}
|
||||
LeaderboardDTO leaderboardDTO = leaderboard.getLeaderboardDTO(timePoint,
|
||||
namesOfRaceColumnsForWhichToLoadLegDetails, addOverallDetails, getService(), baseDomainFactory);
|
||||
namesOfRaceColumnsForWhichToLoadLegDetails, addOverallDetails, getService(), baseDomainFactory, fillNetPointsUncorrected);
|
||||
LeaderboardDTO previousLeaderboardDTO = null;
|
||||
synchronized (leaderboardByNameResultsCacheById) {
|
||||
leaderboardByNameResultsCacheById.put(leaderboardDTO.getId(), leaderboardDTO);
|
||||
@@ -831,11 +833,16 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
if (startTime != null) {
|
||||
FlagPoleState activeFlagState = state.getRacingProcedure().getActiveFlags(startTime, MillisecondsTimePoint.now());
|
||||
List<FlagPole> activeFlags = activeFlagState.getCurrentState();
|
||||
FlagPoleState previousFlagState = activeFlagState.getPreviousState(state.getRacingProcedure(), startTime);
|
||||
List<FlagPole> previousFlags = previousFlagState.getCurrentState();
|
||||
FlagPole mostInterestingFlagPole = FlagPoleState.getMostInterestingFlagPole(previousFlags, activeFlags);
|
||||
|
||||
// TODO: adapt the LastFlagFinder#getMostRecent method!
|
||||
if (!activeFlags.isEmpty()) {
|
||||
raceInfoDTO.lastUpperFlag = activeFlags.get(0).getUpperFlag();
|
||||
raceInfoDTO.lastLowerFlag = activeFlags.get(0).getLowerFlag();
|
||||
raceInfoDTO.isLastFlagDisplayed = activeFlags.get(0).isDisplayed();
|
||||
if (mostInterestingFlagPole != null) {
|
||||
raceInfoDTO.lastUpperFlag = mostInterestingFlagPole.getUpperFlag();
|
||||
raceInfoDTO.lastLowerFlag = mostInterestingFlagPole.getLowerFlag();
|
||||
raceInfoDTO.lastFlagsAreDisplayed = mostInterestingFlagPole.isDisplayed();
|
||||
raceInfoDTO.lastFlagsDisplayedStateChanged = previousFlagState.hasPoleChanged(mostInterestingFlagPole);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -849,7 +856,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
if (raceInfoDTO.lastStatus.equals(RaceLogRaceStatus.UNSCHEDULED)) {
|
||||
raceInfoDTO.lastUpperFlag = abortingFlagEvent.getUpperFlag();
|
||||
raceInfoDTO.lastLowerFlag = abortingFlagEvent.getLowerFlag();
|
||||
raceInfoDTO.isLastFlagDisplayed = abortingFlagEvent.isDisplayed();
|
||||
raceInfoDTO.lastFlagsAreDisplayed = abortingFlagEvent.isDisplayed();
|
||||
raceInfoDTO.lastFlagsDisplayedStateChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -866,7 +874,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
raceInfoDTO.protestFinishTime = protestStartTime.plus(protestDuration).asDate();
|
||||
raceInfoDTO.lastUpperFlag = Flags.BRAVO;
|
||||
raceInfoDTO.lastLowerFlag = Flags.NONE;
|
||||
raceInfoDTO.isLastFlagDisplayed = true;
|
||||
raceInfoDTO.lastFlagsAreDisplayed = true;
|
||||
raceInfoDTO.lastFlagsDisplayedStateChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -885,7 +894,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
raceInfoDTO.isTracked = raceColumn.getTrackedRace(fleet) != null ? true : false;
|
||||
|
||||
return raceInfoDTO;
|
||||
}
|
||||
}
|
||||
|
||||
private void fillStartProcedureSpecifics(RaceInfoDTO raceInfoDTO, ReadonlyRaceState state) {
|
||||
RaceInfoExtensionDTO info = null;
|
||||
@@ -1403,12 +1412,18 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
public CompactRaceMapDataDTO getRaceMapData(RegattaAndRaceIdentifier raceIdentifier, Date date,
|
||||
Map<String, Date> fromPerCompetitorIdAsString, Map<String, Date> toPerCompetitorIdAsString,
|
||||
boolean extrapolate) throws NoWindException {
|
||||
return new CompactRaceMapDataDTO(getBoatPositions(raceIdentifier, fromPerCompetitorIdAsString,
|
||||
toPerCompetitorIdAsString, extrapolate), getCoursePositions(raceIdentifier, date), getCourseSidelines(raceIdentifier, date), getQuickRanks(
|
||||
raceIdentifier, date));
|
||||
final Map<CompetitorDTO, List<GPSFixDTO>> boatPositions = getBoatPositions(raceIdentifier,
|
||||
fromPerCompetitorIdAsString, toPerCompetitorIdAsString, extrapolate);
|
||||
final CoursePositionsDTO coursePositions = getCoursePositions(raceIdentifier, date);
|
||||
final List<SidelineDTO> courseSidelines = getCourseSidelines(raceIdentifier, date);
|
||||
final List<QuickRankDTO> quickRanks = getQuickRanks(raceIdentifier, date);
|
||||
return new CompactRaceMapDataDTO(boatPositions, coursePositions, courseSidelines, quickRanks);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link LegType}s are cached within the method with a resolution of one minute. The cache key is a pair of
|
||||
* {@link TrackedLegOfCompetitor} and {@link TimePoint}.
|
||||
*
|
||||
* @param from
|
||||
* for the list of competitors provided as keys of this map, requests the GPS fixes starting with the
|
||||
* date provided as value
|
||||
@@ -1425,7 +1440,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
*/
|
||||
private Map<CompetitorDTO, List<GPSFixDTO>> getBoatPositions(RegattaAndRaceIdentifier raceIdentifier,
|
||||
Map<String, Date> fromPerCompetitorIdAsString, Map<String, Date> toPerCompetitorIdAsString,
|
||||
boolean extrapolate) throws NoWindException {
|
||||
boolean extrapolate)
|
||||
throws NoWindException {
|
||||
Map<Pair<Leg, TimePoint>, LegType> legTypeCache = new HashMap<>();
|
||||
Map<CompetitorDTO, List<GPSFixDTO>> result = new HashMap<CompetitorDTO, List<GPSFixDTO>>();
|
||||
TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier);
|
||||
if (trackedRace != null) {
|
||||
@@ -1475,19 +1492,28 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
GPSFixMoving fix = fixIter.next();
|
||||
while (fix != null && (fix.getTimePoint().compareTo(toTimePointExcluding) < 0 ||
|
||||
(fix.getTimePoint().equals(toTimePointExcluding) && toTimePointExcluding.equals(fromTimePoint)))) {
|
||||
Tack tack;
|
||||
try {
|
||||
tack = trackedRace.getTack(competitor, fix.getTimePoint());
|
||||
} catch (NoWindException nwe) {
|
||||
tack = null;
|
||||
}
|
||||
Wind wind = trackedRace.getWind(fix.getPosition(), toTimePointExcluding);
|
||||
final SpeedWithBearing estimatedSpeed = track.getEstimatedSpeed(fix.getTimePoint());
|
||||
Tack tack = wind == null? null : trackedRace.getTack(estimatedSpeed, wind, fix.getTimePoint());
|
||||
TrackedLegOfCompetitor trackedLegOfCompetitor = trackedRace.getTrackedLeg(competitor,
|
||||
fix.getTimePoint());
|
||||
LegType legType = trackedLegOfCompetitor == null ? null : trackedRace.getTrackedLeg(
|
||||
trackedLegOfCompetitor.getLeg()).getLegType(fix.getTimePoint());
|
||||
Wind wind = trackedRace.getWind(fix.getPosition(),toTimePointExcluding);
|
||||
LegType legType;
|
||||
if (trackedLegOfCompetitor != null && trackedLegOfCompetitor.getLeg() != null) {
|
||||
TimePoint quantifiedTimePoint = quantifyTimePointWithResolution(fix.getTimePoint(), /* resolutionInMilliseconds */
|
||||
60000);
|
||||
Pair<Leg, TimePoint> cacheKey = new Pair<Leg, TimePoint>(
|
||||
trackedLegOfCompetitor.getLeg(), quantifiedTimePoint);
|
||||
legType = legTypeCache.get(cacheKey);
|
||||
if (legType == null) {
|
||||
legType = trackedRace.getTrackedLeg(trackedLegOfCompetitor.getLeg()).getLegType(
|
||||
fix.getTimePoint());
|
||||
legTypeCache.put(cacheKey, legType);
|
||||
}
|
||||
} else {
|
||||
legType = null;
|
||||
}
|
||||
WindDTO windDTO = wind == null ? null : createWindDTOFromAlreadyAveraged(wind, toTimePointExcluding);
|
||||
GPSFixDTO fixDTO = createGPSFixDTO(fix, track.getEstimatedSpeed(fix.getTimePoint()), windDTO, tack, legType, /* extrapolate */
|
||||
GPSFixDTO fixDTO = createGPSFixDTO(fix, estimatedSpeed, windDTO, tack, legType, /* extrapolate */
|
||||
false);
|
||||
fixesForCompetitor.add(fixDTO);
|
||||
if (fixIter.hasNext()) {
|
||||
@@ -1496,22 +1522,31 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
// check if fix was at date and if extrapolation is requested
|
||||
if (!fix.getTimePoint().equals(toTimePointExcluding) && extrapolate) {
|
||||
Position position = track.getEstimatedPosition(toTimePointExcluding, extrapolate);
|
||||
Tack tack2;
|
||||
try {
|
||||
tack2 = trackedRace.getTack(competitor, toTimePointExcluding);
|
||||
} catch (NoWindException nwe) {
|
||||
tack2 = null;
|
||||
}
|
||||
LegType legType2 = trackedLegOfCompetitor == null ? null : trackedRace
|
||||
.getTrackedLeg(trackedLegOfCompetitor.getLeg()).getLegType(
|
||||
fix.getTimePoint());
|
||||
SpeedWithBearing speedWithBearing = track.getEstimatedSpeed(toTimePointExcluding);
|
||||
Wind wind2 = trackedRace.getWind(position, toTimePointExcluding);
|
||||
SpeedWithBearing estimatedSpeed2 = track.getEstimatedSpeed(toTimePointExcluding);
|
||||
Tack tack2 = wind2 == null ? null : trackedRace.getTack(estimatedSpeed2, wind2, toTimePointExcluding);
|
||||
LegType legType2;
|
||||
if (trackedLegOfCompetitor != null && trackedLegOfCompetitor.getLeg() != null) {
|
||||
TimePoint quantifiedTimePoint = quantifyTimePointWithResolution(
|
||||
fix.getTimePoint(), /* resolutionInMilliseconds */
|
||||
60000);
|
||||
Pair<Leg, TimePoint> cacheKey = new Pair<Leg, TimePoint>(
|
||||
trackedLegOfCompetitor.getLeg(), quantifiedTimePoint);
|
||||
legType2 = legTypeCache.get(cacheKey);
|
||||
if (legType2 == null) {
|
||||
legType2 = trackedRace.getTrackedLeg(trackedLegOfCompetitor.getLeg())
|
||||
.getLegType(
|
||||
fix.getTimePoint());
|
||||
legTypeCache.put(cacheKey, legType2);
|
||||
}
|
||||
} else {
|
||||
legType2 = null;
|
||||
}
|
||||
WindDTO windDTO2 = wind2 == null ? null : createWindDTOFromAlreadyAveraged(wind2, toTimePointExcluding);
|
||||
GPSFixDTO extrapolated = new GPSFixDTO(
|
||||
toPerCompetitorIdAsString.get(competitorDTO.getIdAsString()),
|
||||
position==null?null:new PositionDTO(position.getLatDeg(), position.getLngDeg()),
|
||||
speedWithBearing==null?null:createSpeedWithBearingDTO(speedWithBearing), windDTO2,
|
||||
estimatedSpeed2==null?null:createSpeedWithBearingDTO(estimatedSpeed2), windDTO2,
|
||||
tack2, legType2, /* extrapolated */ true);
|
||||
fixesForCompetitor.add(extrapolated);
|
||||
}
|
||||
@@ -1525,6 +1560,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
return result;
|
||||
}
|
||||
|
||||
private TimePoint quantifyTimePointWithResolution(TimePoint timePoint, long resolutionInMilliseconds) {
|
||||
return new MillisecondsTimePoint(timePoint.asMillis() / resolutionInMilliseconds * resolutionInMilliseconds);
|
||||
}
|
||||
|
||||
private SpeedWithBearingDTO createSpeedWithBearingDTO(SpeedWithBearing speedWithBearing) {
|
||||
return new SpeedWithBearingDTO(speedWithBearing.getKnots(), speedWithBearing
|
||||
.getBearing().getDegrees());
|
||||
@@ -2521,7 +2560,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
Map<CompetitorDTO, FutureTask<CompetitorRaceDataDTO>> resultFutures = new HashMap<CompetitorDTO, FutureTask<CompetitorRaceDataDTO>>();
|
||||
for (final CompetitorDTO competitorDTO : competitors) {
|
||||
FutureTask<CompetitorRaceDataDTO> future = new FutureTask<CompetitorRaceDataDTO>(new Callable<CompetitorRaceDataDTO>() {
|
||||
@Override
|
||||
@Override
|
||||
public CompetitorRaceDataDTO call() throws NoWindException {
|
||||
Competitor competitor = getCompetitorByIdAsString(trackedRace.getRace().getCompetitors(),
|
||||
competitorDTO.getIdAsString());
|
||||
@@ -4177,7 +4216,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
}
|
||||
Map<RegattaAndRaceIdentifier, Integer> numberOfWindFixesImportedPerRace = new HashMap<RegattaAndRaceIdentifier, Integer>();
|
||||
for (Account account : igtimiConnectionFactory.getAllAccounts()) {
|
||||
IgtimiConnection conn = igtimiConnectionFactory.connect(account, correctByDeclination);
|
||||
IgtimiConnection conn = igtimiConnectionFactory.connect(account);
|
||||
Map<TrackedRace, Integer> resultsForAccounts = conn.importWindIntoRace(trackedRaces, correctByDeclination);
|
||||
for (Entry<TrackedRace, Integer> resultForAccount : resultsForAccounts.entrySet()) {
|
||||
RegattaAndRaceIdentifier key = resultForAccount.getKey().getRaceIdentifier();
|
||||
@@ -4658,4 +4697,17 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
leaderboardSearchResult.getBoatClassName(), convertToEventDTO(leaderboardSearchResult.getEvent()),
|
||||
leaderboardGroups);
|
||||
}
|
||||
|
||||
@Override
|
||||
public RaceDTO setStartTimeReceivedForRace(RaceIdentifier raceIdentifier, Date newStartTimeReceived) {
|
||||
if (newStartTimeReceived != null) {
|
||||
RegattaNameAndRaceName regattaAndRaceIdentifier = new RegattaNameAndRaceName(
|
||||
raceIdentifier.getRegattaName(), raceIdentifier.getRaceName());
|
||||
DynamicTrackedRace trackedRace = getService().getTrackedRace(regattaAndRaceIdentifier);
|
||||
trackedRace.setStartTimeReceived(new MillisecondsTimePoint(newStartTimeReceived));
|
||||
|
||||
return baseDomainFactory.createRaceDTO(getService(), false, regattaAndRaceIdentifier, trackedRace);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -49,7 +49,8 @@ public class RaceInfoDTO implements IsSerializable {
|
||||
public Date lastUpdateTime;
|
||||
public Flags lastUpperFlag;
|
||||
public Flags lastLowerFlag;
|
||||
public boolean isLastFlagDisplayed;
|
||||
public boolean lastFlagsAreDisplayed;
|
||||
public boolean lastFlagsDisplayedStateChanged;
|
||||
public boolean isRaceAbortedInPassBefore;
|
||||
public Date abortingTimeInPassBefore;
|
||||
public boolean isTracked;
|
||||
|
||||
+5
-5
@@ -23,6 +23,8 @@ import com.sap.sse.gwt.client.EntryPointHelper;
|
||||
import com.sap.sse.gwt.shared.GwtHttpRequestUtils;
|
||||
|
||||
public class TVEntryPoint extends AbstractEntryPoint {
|
||||
|
||||
private static final String PARAM_LEADERBOARD_NAME = "name";
|
||||
private static final String PARAM_LEADERBOARD_GROUP_NAME = "leaderboardGroupName";
|
||||
private static final String PARAM_EMBEDDED = "embedded";
|
||||
private static final String PARAM_SHOW_RACE_DETAILS = "showRaceDetails";
|
||||
@@ -42,10 +44,8 @@ public class TVEntryPoint extends AbstractEntryPoint {
|
||||
EntryPointHelper.registerASyncService((ServiceDefTarget) sailingService, RemoteServiceMappingConstants.sailingServiceRemotePath);
|
||||
EntryPointHelper.registerASyncService((ServiceDefTarget) mediaService, RemoteServiceMappingConstants.mediaServiceRemotePath);
|
||||
|
||||
final boolean showRaceDetails = Window.Location.getParameter(PARAM_SHOW_RACE_DETAILS) != null
|
||||
&& Window.Location.getParameter(PARAM_SHOW_RACE_DETAILS).equalsIgnoreCase("true");
|
||||
final boolean embedded = Window.Location.getParameter(PARAM_EMBEDDED) != null
|
||||
&& Window.Location.getParameter(PARAM_EMBEDDED).equalsIgnoreCase("true");
|
||||
final boolean showRaceDetails = GwtHttpRequestUtils.getBooleanParameter(PARAM_SHOW_RACE_DETAILS, true);
|
||||
final boolean embedded = GwtHttpRequestUtils.getBooleanParameter(PARAM_EMBEDDED, false);
|
||||
final long delayToLiveMillis = Window.Location.getParameter(PARAM_DELAY_TO_LIVE_MILLIS) != null ? Long
|
||||
.valueOf(Window.Location.getParameter(PARAM_DELAY_TO_LIVE_MILLIS)) : 5000l; // default 5s
|
||||
final boolean showNavigationPanel = GwtHttpRequestUtils.getBooleanParameter(
|
||||
@@ -67,7 +67,7 @@ public class TVEntryPoint extends AbstractEntryPoint {
|
||||
sailingService.getLeaderboardNames(new AsyncCallback<List<String>>() {
|
||||
@Override
|
||||
public void onSuccess(List<String> leaderboardNames) {
|
||||
leaderboardName = Window.Location.getParameter("name");
|
||||
leaderboardName = Window.Location.getParameter(PARAM_LEADERBOARD_NAME);
|
||||
leaderboardGroupName = Window.Location.getParameter(PARAM_LEADERBOARD_GROUP_NAME);
|
||||
if (leaderboardNames.contains(leaderboardName)) {
|
||||
createUI(showRaceDetails, embedded, showNavigationPanel, delayToLiveMillis);
|
||||
|
||||
+5
-3
@@ -45,6 +45,7 @@ public class TVViewController implements RaceTimesInfoProviderListener {
|
||||
private final UserAgentDetails userAgent;
|
||||
private final LogoAndTitlePanel logoAndTitlePanel;
|
||||
private final DockLayoutPanel dockPanel;
|
||||
private final AsyncActionsExecutor asyncActionsExecutor;
|
||||
|
||||
/**
|
||||
* We use two timers: one for the leaderboard, one for the race board. This way, we can stop one while the other continues.
|
||||
@@ -93,6 +94,7 @@ public class TVViewController implements RaceTimesInfoProviderListener {
|
||||
this.dockPanel = dockPanel;
|
||||
this.showRaceDetails = showRaceDetails;
|
||||
|
||||
asyncActionsExecutor = new AsyncActionsExecutor();
|
||||
showWindChart = false;
|
||||
leaderboard = null;
|
||||
leaderboardSettings = LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(null, null, null, /* autoExpandFirstRace */ false);
|
||||
@@ -106,13 +108,13 @@ public class TVViewController implements RaceTimesInfoProviderListener {
|
||||
raceboardTimer.setRefreshInterval(REFRESH_INTERVAL_IN_MILLIS_RACEBOARD);
|
||||
raceboardTimer.play();
|
||||
|
||||
raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, errorReporter, new ArrayList<RegattaAndRaceIdentifier>(), 3000l);
|
||||
raceTimesInfoProvider = new RaceTimesInfoProvider(sailingService, asyncActionsExecutor, errorReporter, new ArrayList<RegattaAndRaceIdentifier>(), 3000l);
|
||||
raceTimesInfoProvider.addRaceTimesInfoProviderListener(this);
|
||||
}
|
||||
|
||||
private LeaderboardPanel createLeaderboardPanel(String leaderboardGroupName, String leaderboardName, boolean showRaceDetails) {
|
||||
CompetitorSelectionModel selectionModel = new CompetitorSelectionModel(/* hasMultiSelection */ true);
|
||||
LeaderboardPanel leaderboardPanel = new LeaderboardPanel(sailingService, new AsyncActionsExecutor(), leaderboardSettings,
|
||||
LeaderboardPanel leaderboardPanel = new LeaderboardPanel(sailingService, asyncActionsExecutor, leaderboardSettings,
|
||||
/* preSelectedRace */null, selectionModel, leaderboardTimer, leaderboardGroupName, leaderboardName, errorReporter, stringMessages,
|
||||
userAgent, showRaceDetails, /* raceTimesInfoProvider */ null, /* autoExpandLastRaceColumn */ false, /* adjustTimerDelay */ true) {
|
||||
@Override
|
||||
@@ -146,7 +148,7 @@ public class TVViewController implements RaceTimesInfoProviderListener {
|
||||
RaceSelectionModel raceSelectionModel = new RaceSelectionModel();
|
||||
List<RegattaAndRaceIdentifier> singletonList = Collections.singletonList(raceToShow);
|
||||
raceSelectionModel.setSelection(singletonList);
|
||||
RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, mediaService, null, raceboardTimer, raceSelectionModel, leaderboardName,
|
||||
RaceBoardPanel raceBoardPanel = new RaceBoardPanel(sailingService, mediaService, asyncActionsExecutor,null, raceboardTimer, raceSelectionModel, leaderboardName,
|
||||
null, raceboardViewConfig, errorReporter, stringMessages, userAgent, raceTimesInfoProvider, /* showMapControls */ false);
|
||||
return raceBoardPanel;
|
||||
}
|
||||
|
||||
+30
@@ -468,6 +468,36 @@ public class TestStoringAndLoadingEventsAndRegattas extends AbstractMongoDBTest
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLoadStoreRegattaWithFleetsEnsuringFleetOrdering() {
|
||||
final String regattaBaseName = "Kieler Woche";
|
||||
BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass("29erXX", /* typicallyStartsUpwind */ true);
|
||||
Regatta regatta = createRegatta(regattaBaseName, boatClass,
|
||||
/* persistent */ false, DomainFactory.INSTANCE.createScoringScheme(ScoringSchemeType.LOW_POINT), null);
|
||||
MongoObjectFactory mof = PersistenceFactory.INSTANCE.getMongoObjectFactory(getMongoService());
|
||||
mof.storeRegatta(regatta);
|
||||
|
||||
DomainObjectFactory dof = PersistenceFactory.INSTANCE.getDomainObjectFactory(getMongoService(), DomainFactory.INSTANCE);
|
||||
Regatta loadedRegatta = dof.loadRegatta(regatta.getName(), /* trackedRegattaRegistry */ null);
|
||||
assertEquals(regattaBaseName, loadedRegatta.getBaseName());
|
||||
|
||||
Iterator<? extends Series> seriesIter = loadedRegatta.getSeries().iterator();
|
||||
Series loadedQualifyingSeries = seriesIter.next();
|
||||
|
||||
Iterator<? extends Fleet> qualiFleetIt = loadedQualifyingSeries.getFleets().iterator();
|
||||
Fleet qualiFleet1 = qualiFleetIt.next();
|
||||
assertEquals(qualiFleet1.getName(), "Yellow");
|
||||
Fleet qualiFleet2 = qualiFleetIt.next();
|
||||
assertEquals(qualiFleet2.getName(), "Blue");
|
||||
|
||||
Series loadedFinalSeries = seriesIter.next();
|
||||
Iterator<? extends Fleet> finalFleetIt = loadedFinalSeries.getFleets().iterator();
|
||||
Fleet finalFleet1 = finalFleetIt.next();
|
||||
assertEquals(finalFleet1.getName(), "Gold");
|
||||
Fleet finalFleet2 = finalFleetIt.next();
|
||||
assertEquals(finalFleet2.getName(), "Silver");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStorageOfRaceIdentifiersOnRaceColumnInSeries() {
|
||||
final int numberOfQualifyingRaces = 5;
|
||||
|
||||
+6
-3
@@ -7,7 +7,7 @@ import org.json.simple.JSONObject;
|
||||
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.DomainFactory;
|
||||
import com.sap.sailing.domain.base.SharedDomainFactory;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.racelog.RaceLogEvent;
|
||||
import com.sap.sailing.domain.racelog.RaceLogEventAuthor;
|
||||
@@ -16,15 +16,18 @@ import com.sap.sailing.server.gateway.deserialization.JsonDeserializer;
|
||||
import com.sap.sailing.server.gateway.serialization.racelog.impl.RaceLogDenoteForTrackingEventSerializer;
|
||||
|
||||
public class RaceLogDenoteForTrackingEventDeserializer extends BaseRaceLogEventDeserializer {
|
||||
public RaceLogDenoteForTrackingEventDeserializer(JsonDeserializer<Competitor> competitorDeserializer) {
|
||||
private final SharedDomainFactory domainFactory;
|
||||
public RaceLogDenoteForTrackingEventDeserializer(JsonDeserializer<Competitor> competitorDeserializer,
|
||||
SharedDomainFactory factory) {
|
||||
super(competitorDeserializer);
|
||||
this.domainFactory = factory;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected RaceLogEvent deserialize(JSONObject object, Serializable id, TimePoint createdAt, RaceLogEventAuthor author, TimePoint timePoint, int passId, List<Competitor> competitors)
|
||||
throws JsonDeserializationException {
|
||||
String raceName = (String) object.get(RaceLogDenoteForTrackingEventSerializer.FIELD_RACE_NAME);
|
||||
BoatClass boatClass = DomainFactory.INSTANCE.getOrCreateBoatClass(
|
||||
BoatClass boatClass = domainFactory.getOrCreateBoatClass(
|
||||
(String) object.get(RaceLogDenoteForTrackingEventSerializer.FIELD_BOAT_CLASS));
|
||||
Serializable raceId = (Serializable) object.get(RaceLogDenoteForTrackingEventSerializer.FIELD_RACE_ID);
|
||||
|
||||
|
||||
+1
-1
@@ -75,7 +75,7 @@ public class RaceLogEventDeserializer implements JsonDeserializer<RaceLogEvent>
|
||||
new PositionJsonDeserializer())),
|
||||
new RaceLogDeviceCompetitorMappingEventDeserializer(competitorDeserializer, deviceDeserializer),
|
||||
new RaceLogDeviceMarkMappingEventDeserializer(competitorDeserializer, new MarkDeserializer(domainFactory), deviceDeserializer),
|
||||
new RaceLogDenoteForTrackingEventDeserializer(competitorDeserializer),
|
||||
new RaceLogDenoteForTrackingEventDeserializer(competitorDeserializer, domainFactory),
|
||||
new RaceLogStartTrackingEventDeserializer(competitorDeserializer),
|
||||
new RaceLogRevokeEventDeserializer(competitorDeserializer),
|
||||
new RaceLogRegisterCompetitorEventDeserializer(competitorDeserializer),
|
||||
|
||||
+6
-2
@@ -137,7 +137,7 @@ public class WindStatusServlet extends SailingServerHttpServlet implements Igtim
|
||||
IgtimiConnectionFactory igtimiConnectionFactory = igtimiServiceTracker.getService();
|
||||
for (Account account : igtimiConnectionFactory.getAllAccounts()) {
|
||||
if (account.getUser() != null) {
|
||||
IgtimiConnection igtimiConnection = igtimiConnectionFactory.connect(account, /* correctByDeclination */ true);
|
||||
IgtimiConnection igtimiConnection = igtimiConnectionFactory.connect(account);
|
||||
try {
|
||||
liveDataConnection = igtimiConnection.getOrCreateLiveConnection(igtimiConnection.getWindDevices());
|
||||
igtimiWindReceiver = new IgtimiWindReceiver(igtimiConnection.getWindDevices(), DeclinationService.INSTANCE);
|
||||
@@ -189,10 +189,14 @@ public class WindStatusServlet extends SailingServerHttpServlet implements Igtim
|
||||
|
||||
public String toString() {
|
||||
if (message.getTrueWind() != null) {
|
||||
return messageReceivedAt.toString() + ": [" + boatID + "] Knots: " + message.getTrueWind().getKnots() + " Bearing: " + message.getTrueWindBearing().getDegrees();
|
||||
return messageReceivedAt.toString() + ": [" + boatID + "] Knots: " + message.getTrueWind().getKnots() + " From: " + getFromAsDegrees();
|
||||
}
|
||||
return messageReceivedAt.toString() + ": [" + boatID + "] " + message.getOriginalMessage();
|
||||
}
|
||||
|
||||
private double getFromAsDegrees() {
|
||||
return message.getTrueWindBearing().reverse().getDegrees();
|
||||
}
|
||||
}
|
||||
|
||||
private class IgtimiMessageInfo {
|
||||
|
||||
+2
-1
@@ -104,7 +104,8 @@ public class LeaderboardsResource extends AbstractSailingServerResource {
|
||||
private JSONObject getLeaderboardJson(Leaderboard leaderboard, Util.Triple<TimePoint, ResultStates, Integer> timePointAndResultStateAndMaxCompetitorsCount)
|
||||
throws NoWindException, InterruptedException, ExecutionException {
|
||||
LeaderboardDTO leaderboardDTO = leaderboard.getLeaderboardDTO(timePointAndResultStateAndMaxCompetitorsCount.getA(),
|
||||
Collections.<String> emptyList(), /* addOverallDetails */ false, getService(), getService().getBaseDomainFactory());
|
||||
Collections.<String> emptyList(), /* addOverallDetails */ false, getService(), getService().getBaseDomainFactory(),
|
||||
/* fillNetPointsUncorrected */ false);
|
||||
|
||||
TimePoint resultTimePoint = timePointAndResultStateAndMaxCompetitorsCount.getA();
|
||||
ResultStates resultState = timePointAndResultStateAndMaxCompetitorsCount.getB();
|
||||
|
||||
+1
@@ -567,6 +567,7 @@ public class RegattasResource extends AbstractSailingServerResource {
|
||||
JSONObject jsonRaceResults = new JSONObject();
|
||||
jsonRaceResults.put("name", trackedRace.getRace().getName());
|
||||
jsonRaceResults.put("regatta", regatta.getName());
|
||||
jsonRaceResults.put("startOfRace-ms", trackedRace.getStartOfRace() == null ? null : trackedRace.getStartOfRace().asMillis());
|
||||
|
||||
JSONArray jsonLegs = new JSONArray();
|
||||
for (TrackedLeg leg : trackedRace.getTrackedLegs()) {
|
||||
|
||||
+5
-3
@@ -152,10 +152,12 @@ public class TrackRaceReplicationTest extends AbstractServerReplicationTest {
|
||||
public void testRaceTimeReplication() throws InterruptedException, Exception {
|
||||
startTracking();
|
||||
// now wait at least until the start of tracking time has been received on the master copy
|
||||
boolean receivedStartOfTracking = master.getTrackedRace(raceIdentifier).getStartOfTracking() != null;
|
||||
while (!receivedStartOfTracking) { // relying on the Timeout rule for this test
|
||||
boolean receivedStartAndEndOfTracking = master.getTrackedRace(raceIdentifier).getStartOfTracking() != null &&
|
||||
master.getTrackedRace(raceIdentifier).getEndOfTracking() != null;
|
||||
while (!receivedStartAndEndOfTracking) { // relying on the Timeout rule for this test
|
||||
Thread.sleep(10);
|
||||
receivedStartOfTracking = master.getTrackedRace(raceIdentifier).getStartOfTracking() != null;
|
||||
receivedStartAndEndOfTracking = master.getTrackedRace(raceIdentifier).getStartOfTracking() != null &&
|
||||
master.getTrackedRace(raceIdentifier).getEndOfTracking() != null;
|
||||
}
|
||||
Thread.sleep(2000); // kept failing several times for a 1000ms timeout
|
||||
TrackedRace replicaTrackedRace = replica.getTrackedRace(raceIdentifier);
|
||||
|
||||
+1
@@ -164,6 +164,7 @@ public class TrackedRaceContentsReplicationTest extends AbstractServerReplicatio
|
||||
new KnotSpeedWithBearingImpl(13, new DegreeBearingImpl(234)));
|
||||
WindSource webWindSource = new WindSourceImpl(WindSourceType.WEB);
|
||||
trackedRace.recordWind(wind, webWindSource);
|
||||
Thread.sleep(500); // wind addition and wind removal are asynchronous operations that could otherwise pass each other
|
||||
trackedRace.removeWind(wind, webWindSource);
|
||||
final WindTrack windTrack = trackedRace.getOrCreateWindTrack(webWindSource);
|
||||
windTrack.lockForRead();
|
||||
|
||||
+53
-2
@@ -3,9 +3,15 @@ package com.sap.sailing.server.replication.impl;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.ObjectInputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@@ -55,6 +61,22 @@ public class Replicator implements Runnable {
|
||||
|
||||
private boolean stopped = false;
|
||||
|
||||
/**
|
||||
* When many updates are triggered in a short period of time by a single thread, ensure that the single thread
|
||||
* providing the updates is not outperformed by all the re-calculations happening here. Leave at least one
|
||||
* core to other things, but by using at least three threads ensure that no simplistic deadlocks may occur.
|
||||
*/
|
||||
private static final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors()-1, 3);
|
||||
|
||||
/**
|
||||
* Used for the parallel execution of operations that don't
|
||||
* {@link RacingEventServiceOperation#requiresSynchronousExecution()}.
|
||||
*/
|
||||
private final static Executor executor = new ThreadPoolExecutor(/* corePoolSize */ THREAD_POOL_SIZE,
|
||||
/* maximumPoolSize */ THREAD_POOL_SIZE,
|
||||
/* keepAliveTime */ 60, TimeUnit.SECONDS,
|
||||
/* workQueue */ new LinkedBlockingQueue<Runnable>());
|
||||
|
||||
/**
|
||||
* @param master
|
||||
* descriptor of the master server from which this replicator receives messages
|
||||
@@ -83,12 +105,31 @@ public class Replicator implements Runnable {
|
||||
@Override
|
||||
public void run() {
|
||||
ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader();
|
||||
long messageCount = 0;
|
||||
Field _queue;
|
||||
try {
|
||||
_queue = QueueingConsumer.class.getDeclaredField("_queue");
|
||||
_queue.setAccessible(true);
|
||||
} catch (Exception e) {
|
||||
_queue = null;
|
||||
}
|
||||
while (true) {
|
||||
if (isBeingStopped()) {
|
||||
break;
|
||||
}
|
||||
try {
|
||||
Delivery delivery = consumer.nextDelivery();
|
||||
messageCount++;
|
||||
if (_queue != null) {
|
||||
if (messageCount % 10000l == 0) {
|
||||
try {
|
||||
logger.info("Inbound replication queue size: "+((BlockingQueue<?>) _queue.get(consumer)).size());
|
||||
} catch (Exception e) {
|
||||
// it didn't work; but it's a log message only...
|
||||
logger.info("Received another 10000 replication messages");
|
||||
}
|
||||
}
|
||||
}
|
||||
byte[] bytesFromMessage = delivery.getBody();
|
||||
checksPerformed = 0;
|
||||
// Set this object's class's class loader as context for de-serialization so that all exported classes
|
||||
@@ -172,8 +213,18 @@ public class Replicator implements Runnable {
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void apply(RacingEventServiceOperation<?> operation) {
|
||||
racingEventServiceTracker.getRacingEventService().apply(operation);
|
||||
private synchronized void apply(final RacingEventServiceOperation<?> operation) {
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
racingEventServiceTracker.getRacingEventService().apply(operation);
|
||||
}
|
||||
};
|
||||
if (operation.requiresSynchronousExecution()) {
|
||||
runnable.run();
|
||||
} else {
|
||||
executor.execute(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
private synchronized void queue(RacingEventServiceOperation<?> operation) {
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
<stringAttribute key="bootstrap" value=""/>
|
||||
<stringAttribute key="checked" value="[NONE]"/>
|
||||
<booleanAttribute key="clearConfig" value="false"/>
|
||||
<booleanAttribute key="com.sap.jvm.profiling.ui.widgets.allowDebugging" value="false"/>
|
||||
<stringAttribute key="configLocation" value="${workspace_loc}/target"/>
|
||||
<booleanAttribute key="default" value="true"/>
|
||||
<booleanAttribute key="default_auto_start" value="true"/>
|
||||
@@ -15,18 +16,18 @@
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
|
||||
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
|
||||
</listAttribute>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
|
||||
<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.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog -console -clean"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Declipse.ignoreApp=true -Dosgi.noShutdown=true -Dexpedition.udp.port=2010 -Xmx4096m -Djetty.home=${project_loc:com.sap.sailing.server}/../target/configuration/jetty -Djava.util.logging.config.file=${project_loc:com.sap.sailing.server}/../target/configuration/logging_debug.properties -Dkiwo.results=${project_loc:com.sap.sailing.kiworesultimport.test}/resources -Dpersistentcompetitors.clear=false -XX:+UseMembar -Digtimi.client.id=7fcdd217e0aa16090edb4ad55b09ec43b2021090e209541fc9b7003c2a2b70c6 -Digtimi.client.secret=aa569cf4909bdc7b0e04b11873f3c4ea20687421e010fcc25b771cca9e6f3f9a -Digtimi.client.redirecturi=http://127.0.0.1:8888/igtimi/oauth/v1/authorizationcallback -DspatialWind=true"/>
|
||||
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc}"/>
|
||||
<stringAttribute key="pde.version" value="3.3"/>
|
||||
<stringAttribute key="profilingTraceType-ALLOCATION_TRACE" value="KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%INCREASE_COUNT%CTX_KEY%8192%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ADAPTIVE%CTX_KEY%false%CTX_ENTRY%KEY_MIN_SIZE%CTX_KEY%32%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%CLASS_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_MAX_SIZE%CTX_KEY%65536%CTX_ENTRY%KEY_INC_LINE_NRS%CTX_KEY%true%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%"/>
|
||||
<stringAttribute key="profilingTraceType-IO_TRACE" value="KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%"/>
|
||||
<stringAttribute key="profilingTraceType-METHOD_PARAMETER_TRACE" value="KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%"/>
|
||||
<stringAttribute key="profilingTraceType-NETWORK_TRACE" value="KEY_SESSION_FILTER%CTX_KEY%*%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%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%"/>
|
||||
<stringAttribute key="profilingTraceType-PERFORMANCE_HOTSPOT_TRACE" value="KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_IGNORE_SLEEPING_THREADS%CTX_KEY%true%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%"/>
|
||||
<stringAttribute key="profilingTraceType-SYNCHRONIZATION_TRACE" value="KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%"/>
|
||||
<stringAttribute key="profilingTraceType-ALLOCATION_TRACE" value="KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%INCREASE_COUNT%CTX_KEY%8192%CTX_ENTRY%KEY_MIN_SIZE%CTX_KEY%32%CTX_ENTRY%KEY_MAX_SIZE%CTX_KEY%65536%CTX_ENTRY%KEY_INC_LINE_NRS%CTX_KEY%true%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%CLASS_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ADAPTIVE%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%false%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%true%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%"/>
|
||||
<booleanAttribute key="show_selected_only" value="false"/>
|
||||
<stringAttribute key="target_bundles" value="com.rabbitmq.client@default:default,com.sun.jersey@default:default,com.sun.mail.javax.mail@default:default,javax.servlet*3.0.0.v201112011016@default:default,javax.validation@default:default,javax.ws.rs@default:default,jcl.over.slf4j@default:default,org.apache.activemq.activeio-core@default:default,org.apache.activemq.activemq-core@default:default,org.apache.activemq.kahadb@default:default,org.apache.commons.codec@default:default,org.apache.commons.fileupload@default:default,org.apache.commons.io@default:default,org.apache.commons.lang@default:default,org.apache.commons.logging@default:default,org.apache.felix.gogo.command@default:default,org.apache.felix.gogo.runtime@default:default,org.apache.felix.gogo.shell@default:default,org.apache.geronimo.specs.geronimo-annotation_1.0_spec@default:default,org.apache.geronimo.specs.geronimo-j2ee-management_1.1_spec@default:default,org.apache.geronimo.specs.geronimo-jms_1.1_spec@default:default,org.apache.httpcomponents.httpclient@default:default,org.apache.httpcomponents.httpcore@default:default,org.eclipse.equinox.common@2:true,org.eclipse.equinox.console@default:default,org.eclipse.jetty.continuation@default:default,org.eclipse.jetty.deploy@default:default,org.eclipse.jetty.http@default:default,org.eclipse.jetty.io@default:default,org.eclipse.jetty.jmx@default:default,org.eclipse.jetty.osgi.boot@3:default,org.eclipse.jetty.security@default:default,org.eclipse.jetty.server@default:default,org.eclipse.jetty.servlet@default:default,org.eclipse.jetty.util@default:default,org.eclipse.jetty.webapp@default:default,org.eclipse.jetty.websocket.api@default:default,org.eclipse.jetty.websocket.client@default:default,org.eclipse.jetty.websocket.common@default:default,org.eclipse.jetty.websocket.server@default:default,org.eclipse.jetty.websocket.servlet@default:default,org.eclipse.jetty.xml@default:default,org.eclipse.osgi.services@default:default,org.eclipse.osgi@-1:true,org.jdom@default:default,org.objectweb.asm@default:default,rome@default:default,routeconverter@default:default,slf4j.api@default:default,slf4j.jdk14@default:false"/>
|
||||
<booleanAttribute key="tracing" value="false"/>
|
||||
|
||||
Regular → Executable
+22
@@ -19,6 +19,7 @@ import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
@@ -40,6 +41,7 @@ import com.sap.sailing.domain.tracking.TrackedRaceWithWindEssentials;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
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.WindStore;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
import com.sap.sailing.domain.tracking.impl.EmptyWindStore;
|
||||
@@ -278,6 +280,12 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Tack getTack(SpeedWithBearing speedWithBearing, Wind wind, TimePoint timePoint) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackedRegatta getTrackedRegatta() {
|
||||
@@ -391,6 +399,13 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint,
|
||||
boolean waitForLatestAnalyses, WindLegTypeAndLegBearingCache cache) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint timePoint,
|
||||
boolean waitForLatestAnalysis) throws NoWindException {
|
||||
@@ -398,6 +413,13 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint timePoint,
|
||||
boolean waitForLatestAnalyses, WindLegTypeAndLegBearingCache cache) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint from, TimePoint to,
|
||||
boolean upwindOnly, boolean waitForLatestAnalysis) throws NoWindException {
|
||||
|
||||
+9
@@ -15,6 +15,15 @@ public abstract class AbstractWindOperation extends AbstractRaceOperation<Void>
|
||||
this.wind = wind;
|
||||
}
|
||||
|
||||
/**
|
||||
* Operations of this type can be run in parallel to other operations; subsequent operations do not have to wait
|
||||
* for this operation's completion.
|
||||
*/
|
||||
@Override
|
||||
public boolean requiresSynchronousExecution() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected WindSource getWindSource() {
|
||||
return windSource;
|
||||
}
|
||||
|
||||
Loaded 100 of 110 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user