From 61d1c2da2e0c6e006bd719eef1a0a0e564f611bd Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 14 Dec 2015 13:34:00 +0100 Subject: [PATCH] compileable but untested refactored version of RaceMap.timeChanged, working towards a solution for bug3378 Change-Id: Idc67121b9c1931e66453def87da8243250b9c9d2 --- .../AbstractGetMapRelatedDataAction.java | 47 +++++ .../ui/actions/GetBoatPositionsAction.java | 43 ++++ .../gwt/ui/actions/GetRaceMapDataAction.java | 30 +-- .../sailing/gwt/ui/client/SailingService.java | 6 + .../gwt/ui/client/SailingServiceAsync.java | 6 + .../gwt/ui/client/shared/racemap/RaceMap.java | 185 +++++++++--------- .../gwt/ui/server/SailingServiceImpl.java | 13 +- .../ui/shared/CompactBoatPositionsDTO.java | 31 +++ .../gwt/ui/shared/CompactRaceMapDataDTO.java | 22 +-- 9 files changed, 251 insertions(+), 132 deletions(-) create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/AbstractGetMapRelatedDataAction.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetBoatPositionsAction.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactBoatPositionsDTO.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/AbstractGetMapRelatedDataAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/AbstractGetMapRelatedDataAction.java new file mode 100644 index 00000000000..c2e4aeab9f8 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/AbstractGetMapRelatedDataAction.java @@ -0,0 +1,47 @@ +package com.sap.sailing.gwt.ui.actions; + +import java.util.Date; +import java.util.Map; + +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sse.gwt.client.async.AsyncAction; + +public abstract class AbstractGetMapRelatedDataAction implements AsyncAction { + private final SailingServiceAsync sailingService; + private final RegattaAndRaceIdentifier raceIdentifier; + private final Map from; + private final Map to; + private final boolean extrapolate; + + public AbstractGetMapRelatedDataAction(SailingServiceAsync sailingService, + RegattaAndRaceIdentifier raceIdentifier, Map from, + Map to, boolean extrapolate) { + this.sailingService = sailingService; + this.raceIdentifier = raceIdentifier; + this.from = from; + this.to = to; + this.extrapolate = extrapolate; + } + + protected SailingServiceAsync getSailingService() { + return sailingService; + } + + protected RegattaAndRaceIdentifier getRaceIdentifier() { + return raceIdentifier; + } + + protected Map getFrom() { + return from; + } + + protected Map getTo() { + return to; + } + + protected boolean isExtrapolate() { + return extrapolate; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetBoatPositionsAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetBoatPositionsAction.java new file mode 100644 index 00000000000..74bc77702d4 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetBoatPositionsAction.java @@ -0,0 +1,43 @@ +package com.sap.sailing.gwt.ui.actions; + +import java.util.Date; +import java.util.HashMap; +import java.util.Map; + +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.shared.CompactBoatPositionsDTO; + +public class GetBoatPositionsAction extends AbstractGetMapRelatedDataAction { + public GetBoatPositionsAction(SailingServiceAsync sailingService, + RegattaAndRaceIdentifier raceIdentifier, Map from, + Map to, boolean extrapolate) { + super(sailingService, raceIdentifier, from, to, extrapolate); + } + + @Override + public void execute(final AsyncCallback callback) { + Map fromByCompetitorIdAsString = new HashMap(); + for (Map.Entry fromEntry : getFrom().entrySet()) { + fromByCompetitorIdAsString.put(fromEntry.getKey().getIdAsString(), fromEntry.getValue()); + } + Map toByCompetitorIdAsString = new HashMap(); + for (Map.Entry toEntry : getTo().entrySet()) { + toByCompetitorIdAsString.put(toEntry.getKey().getIdAsString(), toEntry.getValue()); + } + getSailingService().getBoatPositions(getRaceIdentifier(), fromByCompetitorIdAsString, toByCompetitorIdAsString, + isExtrapolate(), new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + callback.onFailure(caught); + } + + @Override + public void onSuccess(CompactBoatPositionsDTO result) { + callback.onSuccess(result); + } + }); + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetRaceMapDataAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetRaceMapDataAction.java index fd6895658cf..244247e5216 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetRaceMapDataAction.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetRaceMapDataAction.java @@ -11,43 +11,33 @@ import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; import com.sap.sailing.gwt.ui.shared.CompactRaceMapDataDTO; import com.sap.sailing.gwt.ui.shared.RaceMapDataDTO; -import com.sap.sse.gwt.client.async.AsyncAction; -public class GetRaceMapDataAction implements AsyncAction { - private final SailingServiceAsync sailingService; - private final Iterable allCompetitors; - private final RegattaAndRaceIdentifier raceIdentifier; - private final Map from; - private final Map to; - private final boolean extrapolate; +public class GetRaceMapDataAction extends AbstractGetMapRelatedDataAction { + private final Map competitorsByIdAsString; private final Date date; private final LegIdentifier simulationLegIdentifier; - public GetRaceMapDataAction(SailingServiceAsync sailingService, Iterable allCompetitors, + public GetRaceMapDataAction(SailingServiceAsync sailingService, Map competitorsByIdAsString, RegattaAndRaceIdentifier raceIdentifier, Date date, Map from, Map to, boolean extrapolate, LegIdentifier simulationLegIdentifier) { - this.allCompetitors = allCompetitors; - this.sailingService = sailingService; - this.raceIdentifier = raceIdentifier; + super(sailingService, raceIdentifier, from, to, extrapolate); + this.competitorsByIdAsString = competitorsByIdAsString; this.date = date; - this.from = from; - this.to = to; - this.extrapolate = extrapolate; this.simulationLegIdentifier = simulationLegIdentifier; } @Override public void execute(final AsyncCallback callback) { Map fromByCompetitorIdAsString = new HashMap(); - for (Map.Entry fromEntry : from.entrySet()) { + for (Map.Entry fromEntry : getFrom().entrySet()) { fromByCompetitorIdAsString.put(fromEntry.getKey().getIdAsString(), fromEntry.getValue()); } Map toByCompetitorIdAsString = new HashMap(); - for (Map.Entry toEntry : to.entrySet()) { + for (Map.Entry toEntry : getTo().entrySet()) { toByCompetitorIdAsString.put(toEntry.getKey().getIdAsString(), toEntry.getValue()); } - sailingService.getRaceMapData(raceIdentifier, date, fromByCompetitorIdAsString, toByCompetitorIdAsString, - extrapolate, simulationLegIdentifier, new AsyncCallback() { + getSailingService().getRaceMapData(getRaceIdentifier(), date, fromByCompetitorIdAsString, toByCompetitorIdAsString, + isExtrapolate(), simulationLegIdentifier, new AsyncCallback() { @Override public void onFailure(Throwable caught) { callback.onFailure(caught); @@ -55,7 +45,7 @@ public class GetRaceMapDataAction implements AsyncAction { @Override public void onSuccess(CompactRaceMapDataDTO result) { - callback.onSuccess(result.getRaceMapDataDTO(allCompetitors)); + callback.onSuccess(result.getRaceMapDataDTO(competitorsByIdAsString)); } }); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java index 594417853c8..f2b20fa0ef9 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java @@ -45,6 +45,7 @@ import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.gwt.ui.adminconsole.RaceLogSetTrackingTimesDTO; import com.sap.sailing.gwt.ui.shared.BulkScoreCorrectionDTO; +import com.sap.sailing.gwt.ui.shared.CompactBoatPositionsDTO; import com.sap.sailing.gwt.ui.shared.CompactRaceMapDataDTO; import com.sap.sailing.gwt.ui.shared.CompetitorsRaceDataDTO; import com.sap.sailing.gwt.ui.shared.ControlPointDTO; @@ -158,6 +159,10 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS CompactRaceMapDataDTO getRaceMapData(RegattaAndRaceIdentifier raceIdentifier, Date date, Map fromPerCompetitorIdAsString, Map toPerCompetitorIdAsString, boolean extrapolate, LegIdentifier simulationLegIdentifier) throws NoWindException; + CompactBoatPositionsDTO getBoatPositions(RegattaAndRaceIdentifier raceIdentifier, + Map fromPerCompetitorIdAsString, Map toPerCompetitorIdAsString, + boolean extrapolate) throws NoWindException; + RaceTimesInfoDTO getRaceTimesInfo(RegattaAndRaceIdentifier raceIdentifier); List getRaceTimesInfos(Collection raceIdentifiers); @@ -612,4 +617,5 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS */ Collection getCompetitorRegistrationsOnRaceLog(String leaderboardName, String raceColumnName, String fleetName); + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java index ebcfba02b56..0282609e482 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java @@ -37,6 +37,7 @@ import com.sap.sailing.domain.common.racelog.RacingProcedureType; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.gwt.ui.adminconsole.RaceLogSetTrackingTimesDTO; import com.sap.sailing.gwt.ui.shared.BulkScoreCorrectionDTO; +import com.sap.sailing.gwt.ui.shared.CompactBoatPositionsDTO; import com.sap.sailing.gwt.ui.shared.CompactRaceMapDataDTO; import com.sap.sailing.gwt.ui.shared.CompetitorsRaceDataDTO; import com.sap.sailing.gwt.ui.shared.ControlPointDTO; @@ -368,6 +369,10 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan Map fromPerCompetitorIdAsString, Map toPerCompetitorIdAsString, boolean extrapolate, LegIdentifier simulationLegIdentifier, AsyncCallback callback); + void getBoatPositions(RegattaAndRaceIdentifier raceIdentifier, Map fromPerCompetitorIdAsString, + Map toPerCompetitorIdAsString, boolean extrapolate, + AsyncCallback callback); + void getReplicaInfo(AsyncCallback callback); void startReplicatingFromMaster(String messagingHost, String masterName, String exchangeName, int servletPort, @@ -753,4 +758,5 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan void copyCourseToOtherRaceLogs(Triple fromTriple, Set> toTriples, AsyncCallback callback); + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 9c82136a5e7..53241dd1b08 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -83,6 +83,7 @@ import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing; import com.sap.sailing.domain.common.scalablevalue.impl.ScalablePosition; +import com.sap.sailing.gwt.ui.actions.GetBoatPositionsAction; import com.sap.sailing.gwt.ui.actions.GetPolarAction; import com.sap.sailing.gwt.ui.actions.GetRaceMapDataAction; import com.sap.sailing.gwt.ui.actions.GetWindInfoAction; @@ -100,6 +101,7 @@ import com.sap.sailing.gwt.ui.client.shared.filter.QuickRankProvider; import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapHelpLinesSettings.HelpLineTypes; import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapZoomSettings.ZoomTypes; import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil; +import com.sap.sailing.gwt.ui.shared.CompactBoatPositionsDTO; import com.sap.sailing.gwt.ui.shared.ControlPointDTO; import com.sap.sailing.gwt.ui.shared.CoursePositionsDTO; import com.sap.sailing.gwt.ui.shared.GPSFixDTO; @@ -776,29 +778,8 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe final long transitionTimeInMillis = calculateTimeForPositionTransitionInMillis(newTime, oldTime); final com.sap.sse.common.Util.Triple, Map, Map> fromAndToAndOverlap = fixesAndTails.computeFromAndTo(newTime, competitorsToShow, settings.getEffectiveTailLengthInMilliseconds()); - int requestID = ++boatPositionRequestIDCounter; - // For those competitors for which the tails don't overlap (and therefore will be replaced by the new tail coming from the server) - // we expect some potential delay in computing the full tail. Therefore, in those cases we fire two requests: one fetching only the - // boat positions at newTime with zero tail length; and another one fetching everything else. - // TODO bug3378: two calls that both request simulation and mark data wastes bandwidth and CPU; one of them should only use getBoatPositions to request missing pieces of tails; showBoatsOnMap must not remove canvases / tails if one of the two calls delivers only partial results - final GetRaceMapDataAction getRaceMapDataForAllOverlappingAndTipsOfNonOverlapping = getRaceMapDataForAllOverlappingAndTipsOfNonOverlapping(fromAndToAndOverlap, race, newTime); - final boolean updateMarksAndLinesExceptAdvantageLineInCallback; - if (getRaceMapDataForAllOverlappingAndTipsOfNonOverlapping != null) { - asyncActionsExecutor.execute(getRaceMapDataForAllOverlappingAndTipsOfNonOverlapping, GET_RACE_MAP_DATA_CATEGORY, - getRaceMapDataCallback(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), competitorsToShow, requestID, - /* updateMarksAndLinesExceptAdvantageLineInCallback */ true)); - updateMarksAndLinesExceptAdvantageLineInCallback = false; // don't do this again in the second call that mainly fetches - requestID = ++boatPositionRequestIDCounter; - } else { - updateMarksAndLinesExceptAdvantageLineInCallback = true; - } - // next, do the full thing; being the later call, if request throttling kicks in, the later call - // supersedes the earlier call which may get dropped then - GetRaceMapDataAction getRaceMapDataAction = new GetRaceMapDataAction(sailingService, competitorSelection.getAllCompetitors(), race, - useNullAsTimePoint() ? null : newTime, fromAndToAndOverlap.getA(), fromAndToAndOverlap.getB(), /* extrapolate */ true, (settings.isShowSimulationOverlay() ? simulationOverlay.getLegIdentifier() : null)); - asyncActionsExecutor.execute(getRaceMapDataAction, GET_RACE_MAP_DATA_CATEGORY, - getRaceMapDataCallback(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), competitorsToShow, requestID, - updateMarksAndLinesExceptAdvantageLineInCallback)); + // Request map data update, possibly in two calls; see method details + callGetRaceMapDataForAllOverlappingAndTipsOfNonOverlappingAndGetBoatPositionsForAllOthers(fromAndToAndOverlap, race, newTime, transitionTimeInMillis, competitorsToShow); // draw the wind into the map, get the combined wind List windSourceTypeNames = new ArrayList(); @@ -851,63 +832,79 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe } /** - * We assume that overlapping segments usually don't require a lot of loading time as the most typical case will be to update a longer - * tail with a few new fixes that were received since the last time tick. Non-overlapping position requests typically occur for the - * first request when no fix at all is known for the competitor yet, or when the user has radically moved the time slider to some - * other time such that given the current tail length setting the new tail segment does not overlap with the old one, requiring a full - * load of the entire tail data for that competitor.

+ * Requests updates for map data and, when received, updates the map structures accordingly. + *

* - * For the non-overlapping requests, this method creates a separate request which only loads boat positions, quick ranks, sidelines and - * mark positions for the zero-length interval at newTime, assuming that this will work fairly fast and in particular in - * O(1) time regardless of tail length, compared to fetching the entire tail for all competitors. + * The update can happen in one or two round trips. We assume that overlapping segments usually don't require a lot + * of loading time as the most typical case will be to update a longer tail with a few new fixes that were received + * since the last time tick. These supposedly fast requests are handled by a {@link GetRaceMapDataAction} which also + * requests updates for mark positions, sidelines and quick ranks. The same request also loads boat positions for + * the zero-length interval at newTime for the non-overlapping tails assuming that this will work + * fairly fast and in particular in O(1) time regardless of tail length, compared to fetching the entire tail for + * all competitors. This will at least provide quick feedback about those competitors' positions even if loading + * their entire tail may take a little longer. The {@link GetRaceMapDataAction} therefore is intended to be a rather + * quick call. + *

* - * TODO bug 3378: This shall produce two calls: one complete GetRaceMapDataAction and another fetching of missing "long tails" which only needs to be carried out if the first call does not deliver everything requested by fromAndToAndOverlap - * The second call of GetRaceMapDataAction is this: - * - * GetRaceMapDataAction getRaceMapDataAction = new GetRaceMapDataAction(sailingService, competitorSelection.getAllCompetitors(), race, - * useNullAsTimePoint() ? null : newTime, fromAndToAndOverlap.getA(), fromAndToAndOverlap.getB(), - * true, // extrapolate - * (settings.isShowSimulationOverlay() ? simulationOverlay.getLegIdentifier() : null)); - * asyncActionsExecutor.execute(getRaceMapDataAction, GET_RACE_MAP_DATA_CATEGORY, - * getRaceMapDataCallback(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), competitorsToShow, requestID, - * updateMarksAndLinesExceptAdvantageLineInCallback)); - * + * Non-overlapping position requests typically occur for the first request when no fix at all is known for the + * competitor yet, or when the user has radically moved the time slider to some other time such that given the + * current tail length setting the new tail segment does not overlap with the old one, requiring a full load of the + * entire tail data for that competitor. For these non-overlapping requests, this method creates a + * {@link GetBoatPositionsAction} request loading the entire tail required, but not quick ranks, sidelines and mark + * positions. Updating the results of this call is done in {@link #updateBoatPositions(Date, long, Map, Iterable, Map)}. + *

*/ - private GetRaceMapDataAction getRaceMapDataForAllOverlappingAndTipsOfNonOverlapping( - Triple, Map, Map> fromAndToAndOverlap, - RegattaAndRaceIdentifier race, Date newTime) { - Map fromTimes = new HashMap<>(); - Map toTimes = new HashMap<>(); + private void callGetRaceMapDataForAllOverlappingAndTipsOfNonOverlappingAndGetBoatPositionsForAllOthers( + final Triple, Map, Map> fromAndToAndOverlap, + RegattaAndRaceIdentifier race, final Date newTime, final long transitionTimeInMillis, final Iterable competitorsToShow) { + final Map fromTimesForQuickCall = new HashMap<>(); + final Map toTimesForQuickCall = new HashMap<>(); + final Map fromTimesForNonOverlappingTailsCall = new HashMap<>(); + final Map toTimesForNonOverlappingTailsCall = new HashMap<>(); for (Map.Entry e : fromAndToAndOverlap.getC().entrySet()) { - if (!e.getValue()) { - // no overlap; add competitor to request - fromTimes.put(e.getKey(), newTime); - toTimes.put(e.getKey(), newTime); + if (e.getValue()) { + // overlap: expect a quick response; add original request interval for the competitor + fromTimesForQuickCall.put(e.getKey(), fromAndToAndOverlap.getA().get(e.getKey())); + toTimesForQuickCall.put(e.getKey(), fromAndToAndOverlap.getB().get(e.getKey())); + } else { + // no overlap; add competitor to request with a zero-length interval asking only position at newTime, not the entire tail + fromTimesForQuickCall.put(e.getKey(), newTime); + toTimesForQuickCall.put(e.getKey(), newTime); + fromTimesForNonOverlappingTailsCall.put(e.getKey(), fromAndToAndOverlap.getA().get(e.getKey())); + toTimesForNonOverlappingTailsCall.put(e.getKey(), fromAndToAndOverlap.getB().get(e.getKey())); } } - final GetRaceMapDataAction result; - if (!fromTimes.isEmpty()) { - result = new GetRaceMapDataAction(sailingService, competitorSelection.getAllCompetitors(), - race, useNullAsTimePoint() ? null : newTime, fromTimes, toTimes, /* extrapolate */true, (settings.isShowSimulationOverlay() ? simulationOverlay.getLegIdentifier() : null)); - } else { - result = null; + final Map competitorsByIdAsString = new HashMap(); + for (CompetitorDTO competitor : competitorSelection.getAllCompetitors()) { + competitorsByIdAsString.put(competitor.getIdAsString(), competitor); } - return result; + + asyncActionsExecutor.execute(new GetRaceMapDataAction(sailingService, competitorsByIdAsString, + race, useNullAsTimePoint() ? null : newTime, fromTimesForQuickCall, toTimesForQuickCall, /* extrapolate */true, + (settings.isShowSimulationOverlay() ? simulationOverlay.getLegIdentifier() : null)), GET_RACE_MAP_DATA_CATEGORY, + getRaceMapDataCallback(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), competitorsToShow, ++boatPositionRequestIDCounter)); + // next, if necessary, do the full thing; the two calls have different action classes, so throttling should not drop one for the other + asyncActionsExecutor.execute(new GetBoatPositionsAction(sailingService, race, fromAndToAndOverlap.getA(), fromAndToAndOverlap.getB(), + /* extrapolate */ true), GET_RACE_MAP_DATA_CATEGORY, + new MarkedAsyncCallback<>(new AsyncCallback() { + @Override + public void onFailure(Throwable t) { + errorReporter.reportError("Error obtaining racemap data: " + t.getMessage(), true /*silentMode */); + } + + @Override + public void onSuccess(CompactBoatPositionsDTO result) { + updateBoatPositions(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), competitorsToShow, result.getBoatPositionsForCompetitors(competitorsByIdAsString)); + } + })); } - /** - * @param updateMarksAndLinesExceptAdvantageLineInCallback - * when true, mark positions, mark-related lines such as start line and course middle lines, - * three hull length circles and side lines are drawn; otherwise, only the boats, their info flags and - * the advantage line will be updated. - */ private AsyncCallback getRaceMapDataCallback( final Date newTime, final long transitionTimeInMillis, final Map hasTailOverlapForCompetitor, - final Iterable competitorsToShow, final int requestID, - final boolean updateMarksAndLinesExceptAdvantageLineInCallback) { - return new AsyncCallback() { + final Iterable competitorsToShow, final int requestID) { + return new MarkedAsyncCallback<>(new AsyncCallback() { @Override public void onFailure(Throwable caught) { errorReporter.reportError("Error obtaining racemap data: " + caught.getMessage(), true /*silentMode */); @@ -916,28 +913,19 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe @Override public void onSuccess(RaceMapDataDTO raceMapDataDTO) { if (map != null && raceMapDataDTO != null) { - quickRanks = raceMapDataDTO.quickRanks; - competitorsInOrderOfWindwardDistanceTraveledWithOneBasedLegNumber = - raceMapDataDTO.competitorsInOrderOfWindwardDistanceTraveledWithOneBasedLegNumber; - if (showViewSimulation && settings.isShowSimulationOverlay()) { - lastLegNumber = raceMapDataDTO.coursePositions.currentLegNumber; - simulationOverlay.updateLeg(Math.max(lastLegNumber,1), /* clearCanvas */ false, raceMapDataDTO.simulationResultVersion); - } // process response only if not received out of order if (startedProcessingRequestID < requestID) { startedProcessingRequestID = requestID; + quickRanks = raceMapDataDTO.quickRanks; + competitorsInOrderOfWindwardDistanceTraveledWithOneBasedLegNumber = + raceMapDataDTO.competitorsInOrderOfWindwardDistanceTraveledWithOneBasedLegNumber; + if (showViewSimulation && settings.isShowSimulationOverlay()) { + lastLegNumber = raceMapDataDTO.coursePositions.currentLegNumber; + simulationOverlay.updateLeg(Math.max(lastLegNumber, 1), /* clearCanvas */ false, raceMapDataDTO.simulationResultVersion); + } // Do boat specific actions Map> boatData = raceMapDataDTO.boatPositions; - fixesAndTails.updateFixes(boatData, hasTailOverlapForCompetitor, RaceMap.this, transitionTimeInMillis); - Iterable competitorsForWhichNewFixesWereReveiced = - fixesAndTails.updateFixes(boatData, hasTailOverlapForCompetitor, RaceMap.this, transitionTimeInMillis); - if (!Util.isEmpty(competitorsForWhichNewFixesWereReveiced)) { - showBoatsOnMap(newTime, transitionTimeInMillis, competitorsForWhichNewFixesWereReveiced); - showCompetitorInfoOnMap(newTime, transitionTimeInMillis, competitorSelection.getSelectedFilteredCompetitors()); - // even though the wind data is retrieved by a separate call, re-draw the advantage line because it needs to - // adjust to new boat positions - showAdvantageLine(competitorsToShow, newTime, transitionTimeInMillis); - } + updateBoatPositions(newTime, transitionTimeInMillis, hasTailOverlapForCompetitor, competitorsToShow, boatData); if (douglasMarkers != null) { removeAllMarkDouglasPeuckerpoints(); } @@ -948,13 +936,11 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { updateCoordinateSystemFromSettings(); } - if (updateMarksAndLinesExceptAdvantageLineInCallback) { - // Do mark specific actions - showCourseMarksOnMap(raceMapDataDTO.coursePositions); - showCourseSidelinesOnMap(raceMapDataDTO.courseSidelines); - showStartAndFinishAndCourseMiddleLines(raceMapDataDTO.coursePositions); - showStartLineToFirstMarkTriangle(raceMapDataDTO.coursePositions); - } + // Do mark specific actions + showCourseMarksOnMap(raceMapDataDTO.coursePositions); + showCourseSidelinesOnMap(raceMapDataDTO.courseSidelines); + showStartAndFinishAndCourseMiddleLines(raceMapDataDTO.coursePositions); + showStartLineToFirstMarkTriangle(raceMapDataDTO.coursePositions); // Rezoom the map LatLngBounds zoomToBounds = null; @@ -982,7 +968,22 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe lastTimeChangeBeforeInitialization = newTime; } } - }; + }); + } + + private void updateBoatPositions(final Date newTime, final long transitionTimeInMillis, + final Map hasTailOverlapForCompetitor, + final Iterable competitorsToShow, Map> boatData) { + fixesAndTails.updateFixes(boatData, hasTailOverlapForCompetitor, RaceMap.this, transitionTimeInMillis); + Iterable competitorsForWhichNewFixesWereReveiced = + fixesAndTails.updateFixes(boatData, hasTailOverlapForCompetitor, RaceMap.this, transitionTimeInMillis); + if (!Util.isEmpty(competitorsForWhichNewFixesWereReveiced)) { + showBoatsOnMap(newTime, transitionTimeInMillis, competitorsForWhichNewFixesWereReveiced); + showCompetitorInfoOnMap(newTime, transitionTimeInMillis, competitorSelection.getSelectedFilteredCompetitors()); + // even though the wind data is retrieved by a separate call, re-draw the advantage line because it needs to + // adjust to new boat positions + showAdvantageLine(competitorsToShow, newTime, transitionTimeInMillis); + } } private void showCourseSidelinesOnMap(List sidelinesDTOs) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 11cd2e1b1c5..eaad130a029 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -291,6 +291,7 @@ import com.sap.sailing.domain.tractracadapter.TracTracConnectionConstants; import com.sap.sailing.gwt.ui.adminconsole.RaceLogSetTrackingTimesDTO; import com.sap.sailing.gwt.ui.client.SailingService; import com.sap.sailing.gwt.ui.shared.BulkScoreCorrectionDTO; +import com.sap.sailing.gwt.ui.shared.CompactBoatPositionsDTO; import com.sap.sailing.gwt.ui.shared.CompactRaceMapDataDTO; import com.sap.sailing.gwt.ui.shared.CompetitorRaceDataDTO; import com.sap.sailing.gwt.ui.shared.CompetitorsRaceDataDTO; @@ -1640,7 +1641,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S public CompactRaceMapDataDTO getRaceMapData(RegattaAndRaceIdentifier raceIdentifier, Date date, Map fromPerCompetitorIdAsString, Map toPerCompetitorIdAsString, boolean extrapolate, LegIdentifier simulationLegIdentifier) throws NoWindException { - final Map> boatPositions = getBoatPositions(raceIdentifier, + final Map> boatPositions = getBoatPositionsInternal(raceIdentifier, fromPerCompetitorIdAsString, toPerCompetitorIdAsString, extrapolate); final CoursePositionsDTO coursePositions = getCoursePositions(raceIdentifier, date); final List courseSidelines = getCourseSidelines(raceIdentifier, date); @@ -1670,6 +1671,14 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S return competitorBoats; } + + @Override + public CompactBoatPositionsDTO getBoatPositions(RegattaAndRaceIdentifier raceIdentifier, + Map fromPerCompetitorIdAsString, Map toPerCompetitorIdAsString, + boolean extrapolate) throws NoWindException { + return new CompactBoatPositionsDTO(getBoatPositionsInternal(raceIdentifier, fromPerCompetitorIdAsString, toPerCompetitorIdAsString, extrapolate)); + } + /** * {@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}. @@ -1688,7 +1697,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S * @return a map where for each competitor participating in the race the list of GPS fixes in increasing * chronological order is provided. The last one is the last position at or before date. */ - private Map> getBoatPositions(RegattaAndRaceIdentifier raceIdentifier, + private Map> getBoatPositionsInternal(RegattaAndRaceIdentifier raceIdentifier, Map fromPerCompetitorIdAsString, Map toPerCompetitorIdAsString, boolean extrapolate) throws NoWindException { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactBoatPositionsDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactBoatPositionsDTO.java new file mode 100644 index 00000000000..6aaab8b0af5 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactBoatPositionsDTO.java @@ -0,0 +1,31 @@ +package com.sap.sailing.gwt.ui.shared; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; + +import com.google.gwt.user.client.rpc.IsSerializable; +import com.sap.sailing.domain.common.dto.CompetitorDTO; + +public class CompactBoatPositionsDTO implements IsSerializable { + private Map> boatPositions; + + CompactBoatPositionsDTO() {} // for GWT serialization only + + public CompactBoatPositionsDTO(Map> boatPositions) { + super(); + this.boatPositions = new HashMap<>(); + for (final Entry> e : boatPositions.entrySet()) { + this.boatPositions.put(e.getKey().getIdAsString(), e.getValue()); + } + } + + public Map> getBoatPositionsForCompetitors(Map competitorsByIdsAsStrings) { + final Map> result = new HashMap<>(); + for (final Entry> e : boatPositions.entrySet()) { + result.put(competitorsByIdsAsStrings.get(e.getKey()), e.getValue()); + } + return result; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactRaceMapDataDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactRaceMapDataDTO.java index d62034481db..c2635fa64f4 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactRaceMapDataDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactRaceMapDataDTO.java @@ -23,7 +23,7 @@ import com.sap.sse.common.Util; * */ public class CompactRaceMapDataDTO implements IsSerializable { - private Map> boatPositionsByCompetitorIdAsString; + private CompactBoatPositionsDTO boatPositionsByCompetitorIdAsString; private CoursePositionsDTO coursePositions; private List courseSidelines; @@ -35,10 +35,7 @@ public class CompactRaceMapDataDTO implements IsSerializable { public CompactRaceMapDataDTO(Map> boatPositions, CoursePositionsDTO coursePositions, List courseSidelines, QuickRanksDTO quickRanks, long simulationResultVersion) { - this.boatPositionsByCompetitorIdAsString = new HashMap>(); - for (Map.Entry> e : boatPositions.entrySet()) { - this.boatPositionsByCompetitorIdAsString.put(e.getKey().getIdAsString(), e.getValue()); - } + this.boatPositionsByCompetitorIdAsString = new CompactBoatPositionsDTO(boatPositions); this.quickRanks = new ArrayList(quickRanks == null ? 0 : Util.size(quickRanks.getQuickRanks())); this.competitorsInOrderOfWindwardDistanceTraveledFarthestFirstIdAsStringAndOneBasedLegNumber = new LinkedHashMap<>(); Map competitorIdAsStringToOneBasedLegNumber = new HashMap<>(); @@ -59,11 +56,7 @@ public class CompactRaceMapDataDTO implements IsSerializable { this.simulationResultVersion = simulationResultVersion; } - public RaceMapDataDTO getRaceMapDataDTO(Iterable competitors) { - Map competitorsByIdAsString = new HashMap(); - for (CompetitorDTO competitor : competitors) { - competitorsByIdAsString.put(competitor.getIdAsString(), competitor); - } + public RaceMapDataDTO getRaceMapDataDTO(Map competitorsByIdAsString) { RaceMapDataDTO result = new RaceMapDataDTO(); result.quickRanks = new LinkedHashMap(this.quickRanks.size()); for (CompactQuickRankDTO compactQuickRank : this.quickRanks) { @@ -74,14 +67,7 @@ public class CompactRaceMapDataDTO implements IsSerializable { } result.courseSidelines = courseSidelines; result.coursePositions = coursePositions; - result.boatPositions = new HashMap>(); - for (Map.Entry> e : boatPositionsByCompetitorIdAsString.entrySet()) { - final CompetitorDTO competitor = competitorsByIdAsString.get(e.getKey()); - if (competitor != null) { - // maybe null in case the competitor was added, e.g., by unsuppressing, while this call was underway - result.boatPositions.put(competitor, e.getValue()); - } - } + result.boatPositions = boatPositionsByCompetitorIdAsString.getBoatPositionsForCompetitors(competitorsByIdAsString); result.competitorsInOrderOfWindwardDistanceTraveledWithOneBasedLegNumber = new LinkedHashMap<>(); for (Entry i : competitorsInOrderOfWindwardDistanceTraveledFarthestFirstIdAsStringAndOneBasedLegNumber.entrySet()) { CompetitorDTO c = competitorsByIdAsString.get(i.getKey());