mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-24 04:53:54 +00:00
an untested attempt to re-structure the PlayerModes
Change-Id: I7685f7018899551941f2d6a7b3297687b653fc9d
This commit is contained in:
1 parent
82d9b3cf63
commit
dae3acd2e9
6 files changed
+386
-329
No files matched your search
+120
-10
@@ -1,27 +1,49 @@
|
||||
package com.sap.sailing.gwt.ui.raceboard;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gwt.core.shared.GWT;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.sap.sailing.domain.common.RaceIdentifier;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.dto.CompetitorDTO;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
|
||||
import com.sap.sailing.gwt.ui.actions.GetLeaderboardByNameAction;
|
||||
import com.sap.sailing.gwt.ui.client.LeaderboardUpdateListener;
|
||||
import com.sap.sailing.gwt.ui.client.RaceTimePanel;
|
||||
import com.sap.sailing.gwt.ui.client.RaceTimesInfoProviderListener;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceCompetitorSet.CompetitorsForRaceDefinedListener;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
|
||||
import com.sap.sse.gwt.client.player.Timer;
|
||||
|
||||
/**
|
||||
* Abstract base class for implementing a {@link PlayerMode}. The {@link #applyTo(RaceBoardPanel)} method
|
||||
* registers this object as a leaderboard update listener and as a race times info provider listener. The
|
||||
* default implementations of the corresponding callback methods will simply unregister this object as a
|
||||
* listener. If subclasses want to stick with this behavior they can call the {@code super} implementation
|
||||
* in their overrides.
|
||||
* Abstract base class for implementing a {@link RaceBoardMode}. The {@link #applyTo(RaceBoardPanel)} method registers
|
||||
* this object as a leaderboard update listener and as a race times info provider listener, also as a
|
||||
* {@link CompetitorsForRaceDefinedListener} in order to learn when the set of competitors for the race displayed by the
|
||||
* player is known. The default implementations of the corresponding callback methods will simply unregister this object
|
||||
* as a listener for the respective callback, enters the knowledge gained in the object's fields and calls the
|
||||
* {@link #trigger} method each time (abstract in this class). Subclasses can and should do the following:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Redefine specific callback methods in case more processing is required or canceling the listener is desired only
|
||||
* when certain preconditions are met</li>
|
||||
* <li>Implement the {@link #trigger} method to check if all information required has been received by now and then use
|
||||
* it to configure the {@link RaceBoard} and its components accordingly</li>
|
||||
* </ul>
|
||||
*
|
||||
* Note that the {@link #updatedLeaderboard(LeaderboardDTO)} method will typically be called several times in more or
|
||||
* less unpredictable order, asynchronously. Calling {@link Timer#setTime(long)} will trigger
|
||||
* {@link #updatedLeaderboard(LeaderboardDTO)} eventually, but in between, other callbacks to the same method
|
||||
* may occur, triggered by other, automatic adjustments or initialization of the {@link Timer}. To solve this
|
||||
* problem, subclasses may decide to use a separate {@link GetLeaderboardByNameAction} action and execute
|
||||
* it using the {@link #getLeaderboardPanel()}.{@link LeaderboardPanel#getExecutor() getExecutor()}. In the
|
||||
* dedicated callback the subclass can then be sure that the {@link LeaderboardDTO} received matches the
|
||||
* time previously set on the {@link Timer}.
|
||||
*
|
||||
* @author Axel Uhl (d043530)
|
||||
*
|
||||
@@ -33,6 +55,13 @@ public abstract class AbstractRaceBoardMode implements RaceBoardMode, RaceTimesI
|
||||
private LeaderboardPanel leaderboardPanel;
|
||||
private RaceBoardPanel raceBoardPanel;
|
||||
|
||||
private Map<RegattaAndRaceIdentifier, RaceTimesInfoDTO> raceTimesInfo;
|
||||
private RaceTimesInfoDTO raceTimesInfoForRace;
|
||||
private LeaderboardDTO leaderboard;
|
||||
private LeaderboardDTO leaderboardForSpecificTimePoint;
|
||||
private Iterable<CompetitorDTO> competitorsInRace;
|
||||
private RaceColumnDTO raceColumn;
|
||||
|
||||
@Override
|
||||
public void applyTo(RaceBoardPanel raceBoardPanel) {
|
||||
this.raceBoardPanel = raceBoardPanel;
|
||||
@@ -45,6 +74,12 @@ public abstract class AbstractRaceBoardMode implements RaceBoardMode, RaceTimesI
|
||||
raceBoardPanel.getMap().addCompetitorsForRaceDefinedListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called whenever new information of any sort has become available. Subclasses can use this
|
||||
* to decide whether everything they need has been received in order to carry out their actions.
|
||||
*/
|
||||
protected abstract void trigger();
|
||||
|
||||
protected Timer getTimer() {
|
||||
return timer;
|
||||
}
|
||||
@@ -66,13 +101,21 @@ public abstract class AbstractRaceBoardMode implements RaceBoardMode, RaceTimesI
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the {@link RaceTimePanel} has reacted to this update. We assume that now the timing for the race has been
|
||||
* received, and it should be clear by now whether we're talking about a live or a replay race. In case of a replay race
|
||||
* the timer is set to
|
||||
* Called after the {@link RaceTimePanel} has reacted to this update. We assume that now the timing for the race has
|
||||
* been received, and it should be clear by now whether we're talking about a live or a replay race. The
|
||||
* {@link #raceTimesInfo} field is set to the {@code raceTimesInfo} object received as parameter, the
|
||||
* {@link #raceTimesInfoForRace} field is set to the race times info specific to the race identified by
|
||||
* {@link #getRaceIdentifier()}.
|
||||
*/
|
||||
@Override
|
||||
public void raceTimesInfosReceived(Map<RegattaAndRaceIdentifier, RaceTimesInfoDTO> raceTimesInfo,
|
||||
long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) {
|
||||
this.raceTimesInfo = raceTimesInfo;
|
||||
this.raceTimesInfoForRace = raceTimesInfo.get(getRaceIdentifier());
|
||||
trigger();
|
||||
}
|
||||
|
||||
protected void stopReceivingRaceTimesInfos() {
|
||||
raceTimePanel.removeRaceTimesInfoProviderListener(this);
|
||||
}
|
||||
|
||||
@@ -81,16 +124,83 @@ public abstract class AbstractRaceBoardMode implements RaceBoardMode, RaceTimesI
|
||||
*/
|
||||
@Override
|
||||
public void updatedLeaderboard(LeaderboardDTO leaderboard) {
|
||||
this.leaderboard = leaderboard;
|
||||
trigger();
|
||||
}
|
||||
|
||||
protected void stopReceivingLeaderboard() {
|
||||
leaderboardPanel.removeLeaderboardUpdateListener(this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches the leaderboard named {@code leaderboardName} for {@code timePoint} with details for race column
|
||||
* {@code raceColumnName} and stores it in {@link #leaderboardForSpecificTimePoint}, then invokes {@link #trigger()}.
|
||||
* If a {@link #getLeaderboard()} has been received already, it is passed on in the call in order to reduce the
|
||||
* bandwidth required, based on a differential approach.
|
||||
*/
|
||||
protected void loadLeaderboardForSpecificTimePoint(String leaderboardName,
|
||||
String raceColumnName, Date timePoint) {
|
||||
final AsyncCallback<LeaderboardDTO> callback = new AsyncCallback<LeaderboardDTO>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
GWT.log("Error trying to load leaderboard", caught);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(LeaderboardDTO result) {
|
||||
leaderboardForSpecificTimePoint = result;
|
||||
trigger();
|
||||
}
|
||||
};
|
||||
final ArrayList<String> raceColumnNameAsList = new ArrayList<>();
|
||||
if (getRaceColumn() != null) {
|
||||
raceColumnNameAsList.add(getRaceColumn().getName());
|
||||
}
|
||||
final GetLeaderboardByNameAction getLeaderboardByNameAction = new GetLeaderboardByNameAction(
|
||||
getLeaderboardPanel().getSailingService(), getLeaderboardPanel().getLeaderboard().name,
|
||||
getTimer().getTime(), raceColumnNameAsList, /* addOverallDetails */ false,
|
||||
getLeaderboard(), /* fillTotalPointsUncorrected */ false,
|
||||
/* timerToAdjustOffsetIn */ getTimer(), /* errorReporter */ null, StringMessages.INSTANCE);
|
||||
getLeaderboardPanel().getExecutor().execute(getLeaderboardByNameAction, LeaderboardPanel.LOAD_LEADERBOARD_DATA_CATEGORY, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void currentRaceSelected(RaceIdentifier raceIdentifier, RaceColumnDTO raceColumn) {
|
||||
// nothing to do
|
||||
this.raceColumn = raceColumn;
|
||||
trigger();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void competitorsForRaceDefined(Iterable<CompetitorDTO> competitorsInRace) {
|
||||
// nothing to do here
|
||||
this.competitorsInRace = competitorsInRace;
|
||||
trigger();
|
||||
}
|
||||
|
||||
protected void stopReceivingCompetitorsInRace() {
|
||||
raceBoardPanel.getMap().removeCompetitorsForRaceDefinedListener(this);
|
||||
}
|
||||
|
||||
protected Map<RegattaAndRaceIdentifier, RaceTimesInfoDTO> getRaceTimesInfo() {
|
||||
return raceTimesInfo;
|
||||
}
|
||||
|
||||
protected RaceTimesInfoDTO getRaceTimesInfoForRace() {
|
||||
return raceTimesInfoForRace;
|
||||
}
|
||||
|
||||
protected LeaderboardDTO getLeaderboard() {
|
||||
return leaderboard;
|
||||
}
|
||||
|
||||
protected LeaderboardDTO getLeaderboardForSpecificTimePoint() {
|
||||
return leaderboardForSpecificTimePoint;
|
||||
}
|
||||
|
||||
protected Iterable<CompetitorDTO> getCompetitorsInRace() {
|
||||
return competitorsInRace;
|
||||
}
|
||||
|
||||
protected RaceColumnDTO getRaceColumn() {
|
||||
return raceColumn;
|
||||
}
|
||||
}
|
||||
+20
-23
@@ -1,16 +1,10 @@
|
||||
package com.sap.sailing.gwt.ui.raceboard;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.DetailType;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.gwt.ui.client.RaceTimePanel;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettings;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
|
||||
import com.sap.sse.gwt.client.player.Timer.PlayModes;
|
||||
import com.sap.sse.gwt.client.player.Timer.PlayStates;
|
||||
|
||||
@@ -22,30 +16,33 @@ import com.sap.sse.gwt.client.player.Timer.PlayStates;
|
||||
*
|
||||
*/
|
||||
public class FullAnalysisMode extends AbstractRaceBoardMode {
|
||||
|
||||
private boolean leaderboardSettingsAdjusted;
|
||||
private boolean timerAdjusted;
|
||||
|
||||
/**
|
||||
* Called after the {@link RaceTimePanel} has reacted to this update. We assume that now the timing for the race has been
|
||||
* received, and it should be clear by now whether we're talking about a live or a replay race. In case of a replay race
|
||||
* the timer is set to
|
||||
* Listening for race times info continues until information about the end of the race has been received. The leaderboard
|
||||
* settings are adjusted as soon as a valid leaderboard has been received.
|
||||
*/
|
||||
@Override
|
||||
public void raceTimesInfosReceived(Map<RegattaAndRaceIdentifier, RaceTimesInfoDTO> raceTimesInfo,
|
||||
long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) {
|
||||
if (getTimer().getPlayMode() != PlayModes.Live && !raceTimesInfo.isEmpty() && raceTimesInfo.containsKey(getRaceIdentifier())) {
|
||||
final RaceTimesInfoDTO times = raceTimesInfo.get(getRaceIdentifier());
|
||||
if (times.endOfRace != null) {
|
||||
getTimer().setTime(times.endOfRace.getTime());
|
||||
// we've done our adjustments; remove listener and let go
|
||||
super.raceTimesInfosReceived(raceTimesInfo, clientTimeWhenRequestWasSent, serverTimeDuringRequest, clientTimeWhenResponseWasReceived);
|
||||
protected void trigger() {
|
||||
if (!leaderboardSettingsAdjusted && getLeaderboard() != null) {
|
||||
leaderboardSettingsAdjusted = true;
|
||||
// it's important to first unregister the listener before updateSettings is called because
|
||||
// updateSettings will trigger another leaderboard load, leading to an endless recursion otherwise
|
||||
stopReceivingLeaderboard();
|
||||
adjustLeaderboardSettings();
|
||||
}
|
||||
if (!timerAdjusted && getRaceTimesInfoForRace() != null && getRaceTimesInfoForRace().endOfRace != null) {
|
||||
timerAdjusted = true;
|
||||
stopReceivingRaceTimesInfos();
|
||||
if (getTimer().getPlayMode() == PlayModes.Live) {
|
||||
getTimer().setPlayMode(PlayModes.Replay);
|
||||
}
|
||||
getTimer().setTime(getRaceTimesInfoForRace().endOfRace.getTime());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatedLeaderboard(LeaderboardDTO leaderboard) {
|
||||
// it's important to first unregister the listener before updateSettings is called because
|
||||
// updateSettings will trigger another leaderboard load, leading to an endless recursion otherwise
|
||||
super.updatedLeaderboard(leaderboard);
|
||||
private void adjustLeaderboardSettings() {
|
||||
final LeaderboardSettings existingSettings = getLeaderboardPanel().getSettings();
|
||||
final List<DetailType> raceDetailsToShow = new ArrayList<>(existingSettings.getRaceDetailsToShow());
|
||||
raceDetailsToShow.add(DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS);
|
||||
|
||||
+22
-29
@@ -1,16 +1,10 @@
|
||||
package com.sap.sailing.gwt.ui.raceboard;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.DetailType;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.gwt.ui.client.RaceTimePanel;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettings;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.gwt.client.player.Timer.PlayModes;
|
||||
@@ -26,30 +20,11 @@ import com.sap.sse.gwt.client.player.Timer.PlayStates;
|
||||
public class PlayerMode extends AbstractRaceBoardMode {
|
||||
private final Duration DURATION_BEFORE_START_TO_SET_TIMER_TO_FOR_REPLAY_RACES = Duration.ONE_SECOND.times(10);
|
||||
|
||||
/**
|
||||
* Called after the {@link RaceTimePanel} has reacted to this update. We assume that now the timing for the race has been
|
||||
* received, and it should be clear by now whether we're talking about a live or a replay race. In case of a replay race
|
||||
* the timer is set to
|
||||
*/
|
||||
@Override
|
||||
public void raceTimesInfosReceived(Map<RegattaAndRaceIdentifier, RaceTimesInfoDTO> raceTimesInfo,
|
||||
long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) {
|
||||
if (getTimer().getPlayMode() != PlayModes.Live && !raceTimesInfo.isEmpty() && raceTimesInfo.containsKey(getRaceIdentifier())) {
|
||||
final RaceTimesInfoDTO times = raceTimesInfo.get(getRaceIdentifier());
|
||||
if (times.startOfRace != null) {
|
||||
getTimer().setTime(new MillisecondsTimePoint(times.startOfRace).minus(DURATION_BEFORE_START_TO_SET_TIMER_TO_FOR_REPLAY_RACES).asMillis());
|
||||
getTimer().play();
|
||||
// we've done our adjustments; remove listener and let go
|
||||
super.raceTimesInfosReceived(raceTimesInfo, clientTimeWhenRequestWasSent, serverTimeDuringRequest, clientTimeWhenResponseWasReceived);
|
||||
}
|
||||
}
|
||||
}
|
||||
private boolean adjustedLeaderboardSettings;
|
||||
|
||||
@Override
|
||||
public void updatedLeaderboard(LeaderboardDTO leaderboard) {
|
||||
// it's important to first unregister the listener before updateSettings is called because
|
||||
// updateSettings will trigger another leaderboard load, leading to an endless recursion otherwise
|
||||
super.updatedLeaderboard(leaderboard);
|
||||
private boolean timerAdjusted;
|
||||
|
||||
private void adjustLeaderboardSettings() {
|
||||
final LeaderboardSettings existingSettings = getLeaderboardPanel().getSettings();
|
||||
final List<DetailType> raceDetailsToShow = new ArrayList<>(existingSettings.getRaceDetailsToShow());
|
||||
raceDetailsToShow.add(DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS);
|
||||
@@ -69,4 +44,22 @@ public class PlayerMode extends AbstractRaceBoardMode {
|
||||
existingSettings.isShowCompetitorFullNameColumn());
|
||||
getLeaderboardPanel().updateSettings(newSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void trigger() {
|
||||
if (!adjustedLeaderboardSettings && getLeaderboard() != null) {
|
||||
adjustedLeaderboardSettings = true;
|
||||
// it's important to first unregister the listener before updateSettings is called because
|
||||
// updateSettings will trigger another leaderboard load, leading to an endless recursion otherwise
|
||||
stopReceivingLeaderboard();
|
||||
adjustLeaderboardSettings();
|
||||
}
|
||||
if (!timerAdjusted && getTimer().getPlayMode() != PlayModes.Live && getRaceTimesInfoForRace() != null && getRaceTimesInfoForRace().startOfRace != null) {
|
||||
timerAdjusted = true;
|
||||
getTimer().setTime(new MillisecondsTimePoint(getRaceTimesInfoForRace().startOfRace).minus(DURATION_BEFORE_START_TO_SET_TIMER_TO_FOR_REPLAY_RACES).asMillis());
|
||||
getTimer().play();
|
||||
// we've done our adjustments; remove listener and let go
|
||||
stopReceivingRaceTimesInfos();
|
||||
}
|
||||
}
|
||||
}
|
||||
+4
-14
@@ -20,7 +20,6 @@ public abstract class RaceBoardModeWithPerRaceCompetitors extends AbstractRaceBo
|
||||
@Override
|
||||
public void competitorsForRaceDefined(Iterable<CompetitorDTO> competitorsInRace) {
|
||||
this.competitorsInRace = competitorsInRace;
|
||||
updateCompetitorSelection();
|
||||
}
|
||||
|
||||
protected Iterable<CompetitorDTO> getCompetitorsInRace() {
|
||||
@@ -31,28 +30,19 @@ public abstract class RaceBoardModeWithPerRaceCompetitors extends AbstractRaceBo
|
||||
return leaderboard;
|
||||
}
|
||||
|
||||
protected void setLeaderboard(LeaderboardDTO leaderboard) {
|
||||
this.leaderboard = leaderboard;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatedLeaderboard(LeaderboardDTO leaderboard) {
|
||||
super.updatedLeaderboard(leaderboard);
|
||||
setLeaderboard(leaderboard);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void currentRaceSelected(RaceIdentifier raceIdentifier, RaceColumnDTO raceColumn) {
|
||||
this.raceColumn = raceColumn;
|
||||
trigger();
|
||||
}
|
||||
|
||||
protected RaceColumnDTO getRaceColumn() {
|
||||
return raceColumn;
|
||||
}
|
||||
|
||||
protected void updateCompetitorSelection(final int howManyTopCompetitorsInRaceToSelect) {
|
||||
if (getLeaderboard() != null) {
|
||||
final List<CompetitorDTO> competitorsFromBestToWorstInColumn = getLeaderboard().getCompetitorsFromBestToWorst(getRaceColumn());
|
||||
protected void updateCompetitorSelection(final int howManyTopCompetitorsInRaceToSelect, LeaderboardDTO leaderboard) {
|
||||
if (leaderboard != null) {
|
||||
final List<CompetitorDTO> competitorsFromBestToWorstInColumn = leaderboard.getCompetitorsFromBestToWorst(getRaceColumn());
|
||||
final Set<CompetitorDTO> competitorsToSelect = new HashSet<>();
|
||||
int numberOfSelectedCompetitors = 0;
|
||||
for (int i=0; numberOfSelectedCompetitors<howManyTopCompetitorsInRaceToSelect && i<competitorsFromBestToWorstInColumn.size(); i++) {
|
||||
|
||||
+132
-168
@@ -2,33 +2,22 @@ package com.sap.sailing.gwt.ui.raceboard;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.google.gwt.core.client.Scheduler;
|
||||
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
|
||||
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
|
||||
import com.google.gwt.core.shared.GWT;
|
||||
import com.google.gwt.event.shared.HandlerRegistration;
|
||||
import com.google.gwt.maps.client.events.idle.IdleMapEvent;
|
||||
import com.google.gwt.maps.client.events.idle.IdleMapHandler;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.sap.sailing.domain.common.DetailType;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.gwt.ui.actions.GetLeaderboardByNameAction;
|
||||
import com.sap.sailing.gwt.ui.client.RaceTimePanel;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.ChartSettings;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.MultiCompetitorRaceChart;
|
||||
import com.sap.sailing.gwt.ui.client.shared.charts.MultiCompetitorRaceChartSettings;
|
||||
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapSettings;
|
||||
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapZoomSettings;
|
||||
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapZoomSettings.ZoomTypes;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettings;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.gwt.client.player.Timer.PlayModes;
|
||||
@@ -47,10 +36,14 @@ public class StartAnalysisMode extends RaceBoardModeWithPerRaceCompetitors {
|
||||
private static final Duration DURATION_AFTER_START_TO_SET_TIMER_TO_FOR_START_ANALYSIS = Duration.ONE_SECOND.times(10);
|
||||
private static final Duration DURATION_BEFORE_START_TO_INCLUDE_IN_CHART_TIME_RANGE = Duration.ONE_SECOND.times(30);
|
||||
private static final Duration DURATION_AFTER_START_TO_INCLUDE_IN_CHART_TIME_RANGE = Duration.ONE_SECOND.times(30);
|
||||
|
||||
private MultiCompetitorRaceChart competitorChart;
|
||||
private boolean raceTimesInfoReceived;
|
||||
private boolean leaderboardUpdateReveiced;
|
||||
private RaceTimesInfoDTO raceTimesInfo;
|
||||
|
||||
private boolean leaderboardSettingsAdjusted;
|
||||
|
||||
private boolean competitorSelectionUpdated;
|
||||
private boolean timerAdjusted;
|
||||
private boolean mapSettingsAndTimeZoomAdjusted;
|
||||
|
||||
@Override
|
||||
public void applyTo(RaceBoardPanel raceBoardPanel) {
|
||||
@@ -58,168 +51,139 @@ public class StartAnalysisMode extends RaceBoardModeWithPerRaceCompetitors {
|
||||
this.competitorChart = raceBoardPanel.getCompetitorChart();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after the {@link RaceTimePanel} has reacted to this update. We assume that now the timing for the race has been
|
||||
* received, and it should be clear by now whether we're talking about a live or a replay race. In case of a replay race
|
||||
* the timer is set to
|
||||
*/
|
||||
@Override
|
||||
public void raceTimesInfosReceived(Map<RegattaAndRaceIdentifier, RaceTimesInfoDTO> raceTimesInfo,
|
||||
long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) {
|
||||
if (!raceTimesInfo.isEmpty() && raceTimesInfo.containsKey(getRaceIdentifier())) {
|
||||
final RaceTimesInfoDTO times = raceTimesInfo.get(getRaceIdentifier());
|
||||
if (times.startOfRace != null) {
|
||||
// make sure we're no longer in live/playing mode/state; the start analysis is supposed to be a "frozen" display at first
|
||||
if (getTimer().getPlayMode() == PlayModes.Live) {
|
||||
getTimer().setPlayMode(PlayModes.Replay);
|
||||
}
|
||||
// the following call will always trigger a leaderboard load and therefore a callback
|
||||
// to updatedLeaderboard; therefore, updatedLeaderboard can decide when it's time to
|
||||
// trigger the chart display and the map settings application
|
||||
getTimer().setTime(new MillisecondsTimePoint(times.startOfRace).plus(DURATION_AFTER_START_TO_SET_TIMER_TO_FOR_START_ANALYSIS).asMillis());
|
||||
// we've done our adjustments; remove listener and let go
|
||||
super.raceTimesInfosReceived(raceTimesInfo, clientTimeWhenRequestWasSent, serverTimeDuringRequest, clientTimeWhenResponseWasReceived);
|
||||
this.raceTimesInfo = times;
|
||||
raceTimesInfoReceived = true;
|
||||
protected void updateCompetitorSelection() {
|
||||
updateCompetitorSelection(/* howManyTopCompetitorsInRaceToSelect */ 3, getLeaderboard());
|
||||
}
|
||||
|
||||
private void adjustMapSettings() {
|
||||
final RaceMapSettings existingMapSettings = getRaceBoardPanel().getMap().getSettings();
|
||||
final RaceMapSettings newMapSettings = new RaceMapSettings(
|
||||
new RaceMapZoomSettings(Collections.singleton(ZoomTypes.BOATS), /* zoomToSelected */ false),
|
||||
existingMapSettings.getHelpLinesSettings(),
|
||||
existingMapSettings.getTransparentHoverlines(),
|
||||
existingMapSettings.getHoverlineStrokeWeight(),
|
||||
existingMapSettings.getTailLengthInMilliseconds(),
|
||||
/* existingMapSettings.isWindUp() */ true,
|
||||
existingMapSettings.getBuoyZoneRadiusInMeters(),
|
||||
existingMapSettings.isShowOnlySelectedCompetitors(),
|
||||
existingMapSettings.isShowSelectedCompetitorsInfo(),
|
||||
existingMapSettings.isShowWindStreamletColors(),
|
||||
existingMapSettings.isShowWindStreamletOverlay(),
|
||||
existingMapSettings.isShowSimulationOverlay(),
|
||||
existingMapSettings.isShowMapControls(),
|
||||
existingMapSettings.getManeuverTypesToShow(),
|
||||
existingMapSettings.isShowDouglasPeuckerPoints());
|
||||
// try to update the settings once; the problem is the "wind up" display; it changes pan/zoom
|
||||
// which is a major source of instability for the map. Going twice in the map idle event handler
|
||||
// to ensure the settings are really applied.
|
||||
final Object[] registration = new Object[1];
|
||||
registration[0] = getRaceBoardPanel().getMap().getMap().addIdleHandler(new IdleMapHandler() {
|
||||
@Override
|
||||
public void onEvent(IdleMapEvent event) {
|
||||
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
|
||||
@Override
|
||||
public void execute() {
|
||||
getRaceBoardPanel().getMap().updateSettings(newMapSettings);
|
||||
}
|
||||
});
|
||||
((HandlerRegistration) registration[0]).removeHandler();
|
||||
}
|
||||
});
|
||||
getRaceBoardPanel().getMap().updateSettings(newMapSettings);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void trigger() {
|
||||
if (!leaderboardSettingsAdjusted && getLeaderboard() != null) {
|
||||
leaderboardSettingsAdjusted = true;
|
||||
stopReceivingLeaderboard();
|
||||
adjustLeaderboardSettings();
|
||||
}
|
||||
if (!timerAdjusted && getRaceTimesInfoForRace() != null && getRaceTimesInfoForRace().startOfRace != null) {
|
||||
timerAdjusted = true;
|
||||
// we've done our adjustments; remove listener and let go
|
||||
stopReceivingRaceTimesInfos();
|
||||
// make sure we're no longer in live/playing mode/state; the start analysis is supposed to be a "frozen" display at first
|
||||
if (getTimer().getPlayMode() == PlayModes.Live) {
|
||||
getTimer().setPlayMode(PlayModes.Replay);
|
||||
}
|
||||
// the following call will always trigger a leaderboard load and therefore a callback
|
||||
// to updatedLeaderboard; therefore, updatedLeaderboard can decide when it's time to
|
||||
// trigger the chart display and the map settings application
|
||||
getTimer().setTime(new MillisecondsTimePoint(getRaceTimesInfoForRace().startOfRace).plus(DURATION_AFTER_START_TO_SET_TIMER_TO_FOR_START_ANALYSIS).asMillis());
|
||||
// no need to do anything if we don't have the timing info yet; after obtaining the timing
|
||||
// info we can start waiting for the simulation overlay and then adjust the map settings and,
|
||||
// if available, show the competitor chart
|
||||
if (!mapSettingsAndTimeZoomAdjusted) {
|
||||
mapSettingsAndTimeZoomAdjusted = true;
|
||||
Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
|
||||
@Override
|
||||
public boolean execute() {
|
||||
// keep trying in 500ms steps until the simulation overlay is found; this way, changing the competitor chart
|
||||
// settings is largely more successful
|
||||
final boolean simulationOverlayFound = getRaceBoardPanel().getMap().getSimulationOverlay() != null;
|
||||
if (simulationOverlayFound) {
|
||||
if (isCompetitorChartEnabled()) {
|
||||
final MultiCompetitorRaceChartSettings newCompetitorChartSettings = new MultiCompetitorRaceChartSettings(
|
||||
new ChartSettings(/* stepSizeInMillis */ 1000), DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS,
|
||||
/* no second series */ null);
|
||||
competitorChart.updateSettings(newCompetitorChartSettings);
|
||||
getRaceTimePanel().getTimeRangeProvider().setTimeZoom(
|
||||
new MillisecondsTimePoint(getRaceTimesInfoForRace().startOfRace).minus(DURATION_BEFORE_START_TO_INCLUDE_IN_CHART_TIME_RANGE).asDate(),
|
||||
new MillisecondsTimePoint(getRaceTimesInfoForRace().startOfRace).plus(DURATION_AFTER_START_TO_INCLUDE_IN_CHART_TIME_RANGE).asDate());
|
||||
}
|
||||
adjustMapSettings();
|
||||
}
|
||||
return !simulationOverlayFound;
|
||||
}
|
||||
}, /* delay in milliseconds */ 500);
|
||||
}
|
||||
}
|
||||
if (getLeaderboardForSpecificTimePoint() == null &&
|
||||
getRaceColumn() != null && getLeaderboard() != null
|
||||
&& timerAdjusted) {
|
||||
loadLeaderboardForSpecificTimePoint(getLeaderboard().name, getRaceColumn().getName(), getTimer().getTime());
|
||||
}
|
||||
if (!competitorSelectionUpdated && getLeaderboardForSpecificTimePoint() != null && getCompetitorsInRace() != null) {
|
||||
competitorSelectionUpdated = true;
|
||||
stopReceivingCompetitorsInRace();
|
||||
updateCompetitorSelection();
|
||||
if (isCompetitorChartEnabled()) {
|
||||
getRaceBoardPanel().setCompetitorChartVisible(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void updateCompetitorSelection() {
|
||||
updateCompetitorSelection(/* howManyTopCompetitorsInRaceToSelect */ 3);
|
||||
}
|
||||
|
||||
private void showCompetitorChartIfAllDataReceived() {
|
||||
if (getRaceColumn() != null && leaderboardUpdateReveiced && raceTimesInfoReceived && getLeaderboard() != null
|
||||
&& getLeaderboard().getCompetitorsFromBestToWorst(getRaceColumn()) != null
|
||||
&& raceTimesInfo != null && raceTimesInfo.startOfRace != null) {
|
||||
ArrayList<String> raceColumnName = new ArrayList<>();
|
||||
raceColumnName.add(getRaceColumn().getName());
|
||||
// The problem with the leaderboard received in updatedLeaderboard(...) is that we don't know which request
|
||||
// has caused it to be delivered; there is an early request for "now" and maybe even another request for start of race;
|
||||
// therefore, we need to fire our own request for the time point desired and make sure we get the right
|
||||
// LeaderboardDTO to determine the competitor ordering for the current race.
|
||||
final GetLeaderboardByNameAction getLeaderboardByNameAction = new GetLeaderboardByNameAction(
|
||||
getLeaderboardPanel().getSailingService(), getLeaderboardPanel().getLeaderboard().name,
|
||||
getTimer().getTime(), raceColumnName, /* addOverallDetails */ false,
|
||||
getLeaderboard(), /* fillTotalPointsUncorrected */ false,
|
||||
/* timerToAdjustOffsetIn */ getTimer(), /* errorReporter */ null, StringMessages.INSTANCE);
|
||||
getLeaderboardPanel().getExecutor().execute(getLeaderboardByNameAction, LeaderboardPanel.LOAD_LEADERBOARD_DATA_CATEGORY,
|
||||
new AsyncCallback<LeaderboardDTO>() {
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
GWT.log("Error trying to load leaderboard", caught);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(LeaderboardDTO result) {
|
||||
setLeaderboard(result);
|
||||
getLeaderboardPanel().updateLeaderboard(result);
|
||||
updateCompetitorSelection();
|
||||
if (isCompetitorChartEnabled()) {
|
||||
getRaceBoardPanel().setCompetitorChartVisible(true);
|
||||
}
|
||||
}
|
||||
});
|
||||
Scheduler.get().scheduleFixedDelay(new RepeatingCommand() {
|
||||
@Override
|
||||
public boolean execute() {
|
||||
// keep trying in 500ms steps until the simulation overlay is found; this way, changing the competitor chart
|
||||
// settings is largely more successful because
|
||||
boolean result = getRaceBoardPanel().getMap().getSimulationOverlay() == null;
|
||||
if (!result) {
|
||||
if (isCompetitorChartEnabled()) {
|
||||
final MultiCompetitorRaceChartSettings newCompetitorChartSettings = new MultiCompetitorRaceChartSettings(
|
||||
new ChartSettings(/* stepSizeInMillis */ 1000), DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS,
|
||||
/* no second series */ null);
|
||||
competitorChart.updateSettings(newCompetitorChartSettings);
|
||||
getRaceTimePanel().getTimeRangeProvider().setTimeZoom(
|
||||
new MillisecondsTimePoint(raceTimesInfo.startOfRace).minus(DURATION_BEFORE_START_TO_INCLUDE_IN_CHART_TIME_RANGE).asDate(),
|
||||
new MillisecondsTimePoint(raceTimesInfo.startOfRace).plus(DURATION_AFTER_START_TO_INCLUDE_IN_CHART_TIME_RANGE).asDate());
|
||||
}
|
||||
final RaceMapSettings existingMapSettings = getRaceBoardPanel().getMap().getSettings();
|
||||
final RaceMapSettings newMapSettings = new RaceMapSettings(
|
||||
new RaceMapZoomSettings(Collections.singleton(ZoomTypes.BOATS), /* zoomToSelected */ false),
|
||||
existingMapSettings.getHelpLinesSettings(),
|
||||
existingMapSettings.getTransparentHoverlines(),
|
||||
existingMapSettings.getHoverlineStrokeWeight(),
|
||||
existingMapSettings.getTailLengthInMilliseconds(),
|
||||
/* existingMapSettings.isWindUp() */ true,
|
||||
existingMapSettings.getBuoyZoneRadiusInMeters(),
|
||||
existingMapSettings.isShowOnlySelectedCompetitors(),
|
||||
existingMapSettings.isShowSelectedCompetitorsInfo(),
|
||||
existingMapSettings.isShowWindStreamletColors(),
|
||||
existingMapSettings.isShowWindStreamletOverlay(),
|
||||
existingMapSettings.isShowSimulationOverlay(),
|
||||
existingMapSettings.isShowMapControls(),
|
||||
existingMapSettings.getManeuverTypesToShow(),
|
||||
existingMapSettings.isShowDouglasPeuckerPoints());
|
||||
// try to update the settings once; the problem is the "wind up" display; it changes pan/zoom
|
||||
// which is a major source of instability for the map. Going twice in the map idle event handler
|
||||
// to ensure the settings are really applied.
|
||||
final Object[] registration = new Object[1];
|
||||
registration[0] = getRaceBoardPanel().getMap().getMap().addIdleHandler(new IdleMapHandler() {
|
||||
@Override
|
||||
public void onEvent(IdleMapEvent event) {
|
||||
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
|
||||
@Override
|
||||
public void execute() {
|
||||
getRaceBoardPanel().getMap().updateSettings(newMapSettings);
|
||||
}
|
||||
});
|
||||
((HandlerRegistration) registration[0]).removeHandler();
|
||||
}
|
||||
});
|
||||
getRaceBoardPanel().getMap().updateSettings(newMapSettings);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}, /* delay in milliseconds */ 500);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatedLeaderboard(LeaderboardDTO leaderboard) {
|
||||
// no need to do anything if we don't have the timing info yet; after obtaining the timing
|
||||
// info there will always be at least one more leaderboard requested, triggering a callback
|
||||
// to this method
|
||||
if (raceTimesInfoReceived) {
|
||||
// it's important to first unregister the listener before updateSettings is called because
|
||||
// updateSettings will trigger another leaderboard load, leading to an endless recursion otherwise
|
||||
super.updatedLeaderboard(leaderboard);
|
||||
final LeaderboardSettings existingSettings = getLeaderboardPanel().getSettings();
|
||||
final List<DetailType> raceDetailsToShow = new ArrayList<>(existingSettings.getRaceDetailsToShow());
|
||||
raceDetailsToShow.add(DetailType.RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START);
|
||||
raceDetailsToShow.add(DetailType.RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START);
|
||||
raceDetailsToShow.add(DetailType.DISTANCE_TO_START_AT_RACE_START);
|
||||
raceDetailsToShow.add(DetailType.DISTANCE_TO_START_LINE);
|
||||
raceDetailsToShow.add(DetailType.DISTANCE_TO_STARBOARD_END_OF_STARTLINE_WHEN_PASSING_START_IN_METERS);
|
||||
raceDetailsToShow.add(DetailType.SPEED_OVER_GROUND_AT_RACE_START);
|
||||
raceDetailsToShow.add(DetailType.SPEED_OVER_GROUND_WHEN_PASSING_START);
|
||||
raceDetailsToShow.add(DetailType.START_TACK);
|
||||
raceDetailsToShow.remove(DetailType.DISPLAY_LEGS);
|
||||
final LeaderboardSettings newSettings = new LeaderboardSettings(existingSettings.getManeuverDetailsToShow(),
|
||||
existingSettings.getLegDetailsToShow(),
|
||||
raceDetailsToShow, existingSettings.getOverallDetailsToShow(), existingSettings.getNamesOfRaceColumnsToShow(),
|
||||
existingSettings.getNamesOfRacesToShow(),
|
||||
existingSettings.getNumberOfLastRacesToShow(), /* auto-expand pre-selected race */ true,
|
||||
existingSettings.getDelayBetweenAutoAdvancesInMilliseconds(),
|
||||
existingSettings.getNameOfRaceToSort(), existingSettings.isSortAscending(),
|
||||
existingSettings.isUpdateUponPlayStateChange(),
|
||||
existingSettings.getActiveRaceColumnSelectionStrategy(),
|
||||
existingSettings.isShowAddedScores(),
|
||||
existingSettings.isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor(),
|
||||
existingSettings.isShowCompetitorSailIdColumn(),
|
||||
existingSettings.isShowCompetitorFullNameColumn());
|
||||
getLeaderboardPanel().updateSettings(newSettings);
|
||||
leaderboardUpdateReveiced = true;
|
||||
showCompetitorChartIfAllDataReceived();
|
||||
}
|
||||
private void adjustLeaderboardSettings() {
|
||||
final LeaderboardSettings existingSettings = getLeaderboardPanel().getSettings();
|
||||
final List<DetailType> raceDetailsToShow = new ArrayList<>(existingSettings.getRaceDetailsToShow());
|
||||
raceDetailsToShow.add(DetailType.RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START);
|
||||
raceDetailsToShow.add(DetailType.RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START);
|
||||
raceDetailsToShow.add(DetailType.DISTANCE_TO_START_AT_RACE_START);
|
||||
raceDetailsToShow.add(DetailType.DISTANCE_TO_START_LINE);
|
||||
raceDetailsToShow.add(DetailType.DISTANCE_TO_STARBOARD_END_OF_STARTLINE_WHEN_PASSING_START_IN_METERS);
|
||||
raceDetailsToShow.add(DetailType.SPEED_OVER_GROUND_AT_RACE_START);
|
||||
raceDetailsToShow.add(DetailType.SPEED_OVER_GROUND_WHEN_PASSING_START);
|
||||
raceDetailsToShow.add(DetailType.START_TACK);
|
||||
raceDetailsToShow.remove(DetailType.DISPLAY_LEGS);
|
||||
final LeaderboardSettings newSettings = new LeaderboardSettings(existingSettings.getManeuverDetailsToShow(),
|
||||
existingSettings.getLegDetailsToShow(),
|
||||
raceDetailsToShow, existingSettings.getOverallDetailsToShow(), existingSettings.getNamesOfRaceColumnsToShow(),
|
||||
existingSettings.getNamesOfRacesToShow(),
|
||||
existingSettings.getNumberOfLastRacesToShow(), /* auto-expand pre-selected race */ true,
|
||||
existingSettings.getDelayBetweenAutoAdvancesInMilliseconds(),
|
||||
existingSettings.getNameOfRaceToSort(), existingSettings.isSortAscending(),
|
||||
existingSettings.isUpdateUponPlayStateChange(),
|
||||
existingSettings.getActiveRaceColumnSelectionStrategy(),
|
||||
existingSettings.isShowAddedScores(),
|
||||
existingSettings.isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor(),
|
||||
existingSettings.isShowCompetitorSailIdColumn(),
|
||||
existingSettings.isShowCompetitorFullNameColumn());
|
||||
getLeaderboardPanel().updateSettings(newSettings);
|
||||
}
|
||||
|
||||
private boolean isCompetitorChartEnabled() {
|
||||
return competitorChart != null;
|
||||
}
|
||||
|
||||
}
|
||||
+88
-85
@@ -3,16 +3,12 @@ package com.sap.sailing.gwt.ui.raceboard;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.DetailType;
|
||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||
import com.sap.sailing.domain.common.dto.CompetitorDTO;
|
||||
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
|
||||
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapSettings;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettings;
|
||||
import com.sap.sailing.gwt.ui.shared.MarkPassingTimesDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
@@ -30,93 +26,58 @@ import com.sap.sse.gwt.client.player.Timer.PlayModes;
|
||||
public class WinningLanesMode extends RaceBoardModeWithPerRaceCompetitors {
|
||||
private Duration tailLength;
|
||||
|
||||
/**
|
||||
* When set to {@code true}, when {@link #updatedLeaderboard(LeaderboardDTO)} is called the next time it will
|
||||
* stop the leaderboard update notifications and will adjust the tail length to {@link #tailLength}. Must only
|
||||
* be set to {@code true} if {@link #tailLength} has been set to a valid duration before.
|
||||
*/
|
||||
private boolean stopReceivingLeaderboardUpdatesAndAdjustTailLength;
|
||||
|
||||
@Override
|
||||
public void raceTimesInfosReceived(Map<RegattaAndRaceIdentifier, RaceTimesInfoDTO> raceTimesInfo,
|
||||
long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) {
|
||||
final Date startOfRace;
|
||||
final RaceTimesInfoDTO raceTimesInfoForRace;
|
||||
if (!raceTimesInfo.isEmpty() && raceTimesInfo.containsKey(getRaceIdentifier()) &&
|
||||
(startOfRace=(raceTimesInfoForRace=raceTimesInfo.get(getRaceIdentifier())).startOfRace) != null) {
|
||||
if (getTimer().getPlayMode() == PlayModes.Live) {
|
||||
// adjust tail length such that for the leading boat the tail is shown since the start time point
|
||||
tailLength = new MillisecondsTimePoint(startOfRace).until(getTimer().getLiveTimePoint());
|
||||
stopReceivingLeaderboardUpdatesAndAdjustTailLength = true;
|
||||
} else {
|
||||
final List<MarkPassingTimesDTO> markPassingTimes = raceTimesInfoForRace.getMarkPassingTimes();
|
||||
final Date firstPassingOfLastWaypointPassed = markPassingTimes == null || markPassingTimes.isEmpty() ? null :
|
||||
markPassingTimes.get(markPassingTimes.size()-1).firstPassingDate;
|
||||
final Date endOfRace = raceTimesInfoForRace.endOfRace;
|
||||
final Date end = firstPassingOfLastWaypointPassed != null ? firstPassingOfLastWaypointPassed :
|
||||
endOfRace != null ? endOfRace : raceTimesInfoForRace.endOfTracking;
|
||||
if (end != null) {
|
||||
tailLength = new MillisecondsTimePoint(startOfRace).until(new MillisecondsTimePoint(end));
|
||||
stopReceivingLeaderboardUpdatesAndAdjustTailLength = true;
|
||||
getTimer().setTime(end.getTime());
|
||||
}
|
||||
// we've done our adjustments; remove listener and let go
|
||||
super.raceTimesInfosReceived(raceTimesInfo, clientTimeWhenRequestWasSent, serverTimeDuringRequest, clientTimeWhenResponseWasReceived);
|
||||
}
|
||||
}
|
||||
}
|
||||
private boolean adjustedLeaderboardSettings;
|
||||
|
||||
private boolean adjustedCompetitorSelection;
|
||||
|
||||
@Override
|
||||
protected void updateCompetitorSelection() {
|
||||
final int howManyCompetitorsToSelect = getHowManyCompetitorsToSelect(getLeaderboardPanel().getCompetitors(getRaceIdentifier()));
|
||||
updateCompetitorSelection(howManyCompetitorsToSelect);
|
||||
updateCompetitorSelection(howManyCompetitorsToSelect, getLeaderboard());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updatedLeaderboard(LeaderboardDTO leaderboard) {
|
||||
if (stopReceivingLeaderboardUpdatesAndAdjustTailLength) {
|
||||
// it's important to first unregister the listener before updateSettings is called because
|
||||
// updateSettings will trigger another leaderboard load, leading to an endless recursion otherwise
|
||||
super.updatedLeaderboard(leaderboard);
|
||||
final LeaderboardSettings existingSettings = getLeaderboardPanel().getSettings();
|
||||
final List<DetailType> raceDetailsToShow = new ArrayList<>(existingSettings.getRaceDetailsToShow());
|
||||
raceDetailsToShow.add(DetailType.RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS);
|
||||
raceDetailsToShow.add(DetailType.RACE_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS);
|
||||
raceDetailsToShow.add(DetailType.RACE_DISTANCE_TRAVELED);
|
||||
raceDetailsToShow.add(DetailType.RACE_TIME_TRAVELED);
|
||||
raceDetailsToShow.remove(DetailType.DISPLAY_LEGS);
|
||||
final LeaderboardSettings newSettings = new LeaderboardSettings(existingSettings.getManeuverDetailsToShow(),
|
||||
existingSettings.getLegDetailsToShow(),
|
||||
raceDetailsToShow, existingSettings.getOverallDetailsToShow(), existingSettings.getNamesOfRaceColumnsToShow(),
|
||||
existingSettings.getNamesOfRacesToShow(),
|
||||
existingSettings.getNumberOfLastRacesToShow(), /* auto-expand pre-selected race */ true,
|
||||
existingSettings.getDelayBetweenAutoAdvancesInMilliseconds(),
|
||||
existingSettings.getNameOfRaceToSort(), existingSettings.isSortAscending(),
|
||||
existingSettings.isUpdateUponPlayStateChange(),
|
||||
existingSettings.getActiveRaceColumnSelectionStrategy(),
|
||||
existingSettings.isShowAddedScores(),
|
||||
existingSettings.isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor(),
|
||||
existingSettings.isShowCompetitorSailIdColumn(),
|
||||
existingSettings.isShowCompetitorFullNameColumn());
|
||||
getLeaderboardPanel().updateSettings(newSettings);
|
||||
final RaceMapSettings existingMapSettings = getRaceBoardPanel().getMap().getSettings();
|
||||
final RaceMapSettings newMapSettings = new RaceMapSettings(existingMapSettings.getZoomSettings(),
|
||||
existingMapSettings.getHelpLinesSettings(),
|
||||
existingMapSettings.getTransparentHoverlines(),
|
||||
existingMapSettings.getHoverlineStrokeWeight(),
|
||||
tailLength.asMillis(),
|
||||
/* existingMapSettings.isWindUp() */ true,
|
||||
existingMapSettings.getBuoyZoneRadiusInMeters(),
|
||||
/* existingMapSettings.isShowOnlySelectedCompetitors() */ true, // show the top n competitors and their tails quickly
|
||||
existingMapSettings.isShowSelectedCompetitorsInfo(),
|
||||
existingMapSettings.isShowWindStreamletColors(),
|
||||
existingMapSettings.isShowWindStreamletOverlay(),
|
||||
existingMapSettings.isShowSimulationOverlay(),
|
||||
existingMapSettings.isShowMapControls(),
|
||||
existingMapSettings.getManeuverTypesToShow(),
|
||||
existingMapSettings.isShowDouglasPeuckerPoints());
|
||||
getRaceBoardPanel().getMap().updateSettings(newMapSettings);
|
||||
}
|
||||
private void adjustLeaderboardSettings() {
|
||||
final LeaderboardSettings existingSettings = getLeaderboardPanel().getSettings();
|
||||
final List<DetailType> raceDetailsToShow = new ArrayList<>(existingSettings.getRaceDetailsToShow());
|
||||
raceDetailsToShow.add(DetailType.RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS);
|
||||
raceDetailsToShow.add(DetailType.RACE_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS);
|
||||
raceDetailsToShow.add(DetailType.RACE_DISTANCE_TRAVELED);
|
||||
raceDetailsToShow.add(DetailType.RACE_TIME_TRAVELED);
|
||||
raceDetailsToShow.remove(DetailType.DISPLAY_LEGS);
|
||||
final LeaderboardSettings newSettings = new LeaderboardSettings(existingSettings.getManeuverDetailsToShow(),
|
||||
existingSettings.getLegDetailsToShow(),
|
||||
raceDetailsToShow, existingSettings.getOverallDetailsToShow(), existingSettings.getNamesOfRaceColumnsToShow(),
|
||||
existingSettings.getNamesOfRacesToShow(),
|
||||
existingSettings.getNumberOfLastRacesToShow(), /* auto-expand pre-selected race */ true,
|
||||
existingSettings.getDelayBetweenAutoAdvancesInMilliseconds(),
|
||||
existingSettings.getNameOfRaceToSort(), existingSettings.isSortAscending(),
|
||||
existingSettings.isUpdateUponPlayStateChange(),
|
||||
existingSettings.getActiveRaceColumnSelectionStrategy(),
|
||||
existingSettings.isShowAddedScores(),
|
||||
existingSettings.isShowOverallColumnWithNumberOfRacesCompletedPerCompetitor(),
|
||||
existingSettings.isShowCompetitorSailIdColumn(),
|
||||
existingSettings.isShowCompetitorFullNameColumn());
|
||||
getLeaderboardPanel().updateSettings(newSettings);
|
||||
}
|
||||
|
||||
private void adjustMapSettings() {
|
||||
final RaceMapSettings existingMapSettings = getRaceBoardPanel().getMap().getSettings();
|
||||
final RaceMapSettings newMapSettings = new RaceMapSettings(existingMapSettings.getZoomSettings(),
|
||||
existingMapSettings.getHelpLinesSettings(),
|
||||
existingMapSettings.getTransparentHoverlines(),
|
||||
existingMapSettings.getHoverlineStrokeWeight(),
|
||||
tailLength.asMillis(),
|
||||
/* existingMapSettings.isWindUp() */ true,
|
||||
existingMapSettings.getBuoyZoneRadiusInMeters(),
|
||||
/* existingMapSettings.isShowOnlySelectedCompetitors() */ true, // show the top n competitors and their tails quickly
|
||||
existingMapSettings.isShowSelectedCompetitorsInfo(),
|
||||
existingMapSettings.isShowWindStreamletColors(),
|
||||
existingMapSettings.isShowWindStreamletOverlay(),
|
||||
existingMapSettings.isShowSimulationOverlay(),
|
||||
existingMapSettings.isShowMapControls(),
|
||||
existingMapSettings.getManeuverTypesToShow(),
|
||||
existingMapSettings.isShowDouglasPeuckerPoints());
|
||||
getRaceBoardPanel().getMap().updateSettings(newMapSettings);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -128,4 +89,46 @@ public class WinningLanesMode extends RaceBoardModeWithPerRaceCompetitors {
|
||||
final int numberOfCompetitors = Util.size(competitors);
|
||||
return numberOfCompetitors==0 ? 0 : numberOfCompetitors<=4 ? 1 : numberOfCompetitors <=9 ? 2 : 3;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void trigger() {
|
||||
final Date startOfRace;
|
||||
if (getRaceTimesInfoForRace() != null && (startOfRace=getRaceTimesInfoForRace().startOfRace) != null) {
|
||||
if (getTimer().getPlayMode() == PlayModes.Live) {
|
||||
// adjust tail length such that for the leading boat the tail is shown since the start time point
|
||||
tailLength = new MillisecondsTimePoint(startOfRace).until(getTimer().getLiveTimePoint());
|
||||
} else {
|
||||
final List<MarkPassingTimesDTO> markPassingTimes = getRaceTimesInfoForRace().getMarkPassingTimes();
|
||||
final Date firstPassingOfLastWaypointPassed = markPassingTimes == null || markPassingTimes.isEmpty() ? null :
|
||||
markPassingTimes.get(markPassingTimes.size()-1).firstPassingDate;
|
||||
final Date endOfRace = getRaceTimesInfoForRace().endOfRace;
|
||||
final Date end = firstPassingOfLastWaypointPassed != null ? firstPassingOfLastWaypointPassed :
|
||||
endOfRace != null ? endOfRace : getRaceTimesInfoForRace().endOfTracking;
|
||||
if (end != null) {
|
||||
tailLength = new MillisecondsTimePoint(startOfRace).until(new MillisecondsTimePoint(end));
|
||||
getTimer().setTime(end.getTime());
|
||||
}
|
||||
}
|
||||
// we've done our adjustments; remove listener and let go
|
||||
stopReceivingRaceTimesInfos();
|
||||
}
|
||||
if (!adjustedLeaderboardSettings && getLeaderboard() != null) {
|
||||
adjustedLeaderboardSettings = true;
|
||||
// it's important to first unregister the listener before updateSettings is called because
|
||||
// updateSettings will trigger another leaderboard load, leading to an endless recursion otherwise
|
||||
stopReceivingLeaderboard();
|
||||
adjustLeaderboardSettings();
|
||||
}
|
||||
if (adjustedLeaderboardSettings && tailLength != null) {
|
||||
adjustMapSettings();
|
||||
}
|
||||
if (getLeaderboardForSpecificTimePoint() == null && tailLength != null && getLeaderboard() != null && getRaceColumn() != null) {
|
||||
loadLeaderboardForSpecificTimePoint(getLeaderboard().name, getRaceColumn().getName(), getTimer().getTime());
|
||||
}
|
||||
if (!adjustedCompetitorSelection && getLeaderboardForSpecificTimePoint() != null) {
|
||||
adjustedCompetitorSelection = true;
|
||||
updateCompetitorSelection();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user