Merge branch 'master' into updatable-chart_2

Conflicts:
	java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java
This commit is contained in:
Lennart Hensler committed 2012-02-28 09:48:00 +01:00
commit 1e8d5dfabc
44 files changed
+424 -211

No files matched your search

@@ -28,10 +28,19 @@ public interface DomainObjectFactory {
RaceIdentifier loadRaceIdentifier(DBObject dbObject);
/**
* @return The group with the name <code>name</code>, or <code>null</code> if no such group exists.
*/
LeaderboardGroup loadLeaderboardGroup(String name);
/**
* @return All groups in the database.
*/
Iterable<LeaderboardGroup> getAllLeaderboardGroups();
/**
* @return All leaderboards in the database, which aren't contained by a leaderboard group
*/
Iterable<Leaderboard> getLeaderboardsNotInGroup();
}
@@ -43,10 +43,20 @@ public interface MongoObjectFactory {
void storeRaceIdentifier(RaceIdentifier raceIdentifier, DBObject dbObject);
/**
* Stores the group, if it doesn't exist or updates it.<br />
* Leaderboards in the group, which aren't stored in the database, will be stored.
*/
void storeLeaderboardGroup(LeaderboardGroup leaderboardGroup);
/**
* Removes the group with the name <code>groupName</code> from the database.
*/
void removeLeaderboardGroup(String groupName);
/**
* Renames the group with the name <code>oldName</code>.
*/
void renameLeaderboardGroup(String oldName, String newName);
}
@@ -1,6 +1,8 @@
package com.sap.sailing.domain.tracking.impl;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Team;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.tracking.MarkPassing;
@@ -25,7 +27,27 @@ public class DummyMarkPassingWithTimePointOnly implements MarkPassing {
@Override
public Competitor getCompetitor() {
throw new UnsupportedOperationException("getCompetitor() not supported");
return new Competitor() {
@Override
public String getName() {
return "Dummy";
}
@Override
public Object getId() {
return "Dummy";
}
@Override
public Team getTeam() {
return null;
}
@Override
public Boat getBoat() {
return null;
}
};
}
}
@@ -0,0 +1,29 @@
package com.sap.sailing.domain.tracking.impl;
import java.util.Comparator;
import com.sap.sailing.domain.tracking.MarkPassing;
/**
* Compares two {@link MarkPassing} objects, using their {@link MarkPassing#getTimePoint() time point} as the primary
* ordering criterion in ascending order. If two mark passings have equal time points, the competitor IDs' string
* representations are compared as the secondary ordering criterion to ensure that two competitors passing a mark at the
* same time are still identified as not being equal.
*
* @author Axel Uhl (D043530)
*
*/
public class MarkPassingByTimeComparator implements Comparator<MarkPassing> {
public static MarkPassingByTimeComparator INSTANCE = new MarkPassingByTimeComparator();
@Override
public int compare(MarkPassing o1, MarkPassing o2) {
int result = o1 == null ? o2 == null ? 0 : -1 : o2 == null ? 1 : o1.getTimePoint().compareTo(o2.getTimePoint());
if (result == 0 && o1 != null) {
assert o2 != null;
result = o1.getCompetitor().getId().toString().compareTo(o2.getCompetitor().getId().toString());
}
return result;
}
}
@@ -166,12 +166,12 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
markPassingsForCompetitor = new HashMap<Competitor, NavigableSet<MarkPassing>>();
tracks = new HashMap<Competitor, GPSFixTrack<Competitor, GPSFixMoving>>();
for (Competitor competitor : race.getCompetitors()) {
markPassingsForCompetitor.put(competitor, new ConcurrentSkipListSet<MarkPassing>(TimedComparator.INSTANCE));
markPassingsForCompetitor.put(competitor, new ConcurrentSkipListSet<MarkPassing>(MarkPassingByTimeComparator.INSTANCE));
tracks.put(competitor, new DynamicGPSFixMovingTrackImpl<Competitor>(competitor, millisecondsOverWhichToAverageSpeed));
}
markPassingsForWaypoint = new HashMap<Waypoint, NavigableSet<MarkPassing>>();
for (Waypoint waypoint : race.getCourse().getWaypoints()) {
markPassingsForWaypoint.put(waypoint, new ConcurrentSkipListSet<MarkPassing>(TimedComparator.INSTANCE));
markPassingsForWaypoint.put(waypoint, new ConcurrentSkipListSet<MarkPassing>(MarkPassingByTimeComparator.INSTANCE));
}
windTracks = new HashMap<WindSource, WindTrack>();
for (WindSource windSource : WindSource.values()) {
@@ -305,9 +305,27 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
NavigableSet<MarkPassing> roundings = markPassingsForCompetitor.get(competitor);
MarkPassing lastBeforeOrAt = roundings.floor(new DummyMarkPassingWithTimePointOnly(at));
TrackedLegOfCompetitor result = null;
TrackedLeg trackedLeg;
// already finished the race?
if (lastBeforeOrAt != null && getRace().getCourse().getLastWaypoint() != lastBeforeOrAt.getWaypoint()) {
TrackedLeg trackedLeg = getTrackedLegStartingAt(lastBeforeOrAt.getWaypoint());
if (lastBeforeOrAt != null) {
// and not at or after last mark passing
if (getRace().getCourse().getLastWaypoint() != lastBeforeOrAt.getWaypoint()) {
trackedLeg = getTrackedLegStartingAt(lastBeforeOrAt.getWaypoint());
} else {
// exactly *at* last mark passing?
if (at.equals(roundings.last().getTimePoint())) {
// exactly at finish line; return last leg
trackedLeg = getTrackedLegFinishingAt(lastBeforeOrAt.getWaypoint());
} else {
// no, then we're after the last mark passing
trackedLeg = null;
}
}
} else {
// before beginning of race
trackedLeg = null;
}
if (trackedLeg != null) {
result = trackedLeg.getTrackedLeg(competitor);
}
return result;
@@ -548,7 +566,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
@Override
public synchronized void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) {
updateStartToNextMarkCacheInvalidationCacheListenersAfterWaypointAdded(zeroBasedIndex, waypointThatGotAdded);
markPassingsForWaypoint.put(waypointThatGotAdded, new ConcurrentSkipListSet<MarkPassing>(TimedComparator.INSTANCE));
markPassingsForWaypoint.put(waypointThatGotAdded, new ConcurrentSkipListSet<MarkPassing>(MarkPassingByTimeComparator.INSTANCE));
for (Buoy buoy : waypointThatGotAdded.getBuoys()) {
getOrCreateTrack(buoy);
}
@@ -63,7 +63,9 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
@Override
public void add(Wind wind) {
getInternalRawFixes().add(wind);
synchronized (this) {
getInternalRawFixes().add(wind);
}
notifyListenersAboutReceive(wind);
}
@@ -6,7 +6,6 @@ import java.util.List;
import junit.framework.Assert;
import org.json.simple.parser.ParseException;
import org.junit.Ignore;
import org.junit.Test;
import com.sap.sailing.domain.common.Placemark;
@@ -16,9 +15,7 @@ import com.sap.sailing.domain.common.impl.PlacemarkImpl;
import com.sap.sailing.domain.common.impl.SerializablePositionImpl;
import com.sap.sailing.geocoding.ReverseGeocoder;
@Ignore // currently, there seems to be something wrong with the Geocoder. Lennart to be on it. See bug 313
public class ReverseGeocoderTest {
private ReverseGeocoder geocoder = ReverseGeocoder.INSTANCE;
private static final Placemark KIEL = new PlacemarkImpl("Kiel", "DE", new SerializablePositionImpl(54.32132926107913, 10.1348876953125), 232758);
private static final Position KIEL_POSITION = new DegreePosition(54.3231063453431, 10.12265682220459);
@@ -6,6 +6,7 @@ import com.sap.sailing.gwt.ui.client.ErrorReporter;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettingsFactory;
import com.sap.sailing.gwt.ui.leaderboard.SortableColumn;
import com.sap.sailing.gwt.ui.shared.LeaderboardDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardRowDTO;
@@ -16,7 +17,10 @@ public class LeaderboardPanelMock extends LeaderboardPanel {
public LeaderboardPanelMock(SailingServiceAsync sailingService,
String leaderboardName, ErrorReporter errorReporter,
StringMessages stringConstants) {
super(sailingService, null, new CompetitorSelectionModel(/* hasMultiSelection */ true), leaderboardName, null, errorReporter, stringConstants, null);
super(sailingService, LeaderboardSettingsFactory.getInstance()
.createNewDefaultSettings(
/* autoExpandFirstRace */false), new CompetitorSelectionModel(/* hasMultiSelection */true),
leaderboardName, null, errorReporter, stringConstants, null);
// TODO Auto-generated constructor stub
}
Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

@@ -702,8 +702,15 @@ public class LeaderboardGroupConfigPanel extends AbstractEventPanel {
private void moveToGroup() {
LeaderboardGroupDTO selectedGroup = groupsSelectionModel.getSelectedObject();
Set<LeaderboardDTO> selectedLeaderboards = leaderboardsSelectionModel.getSelectedSet();
if (selectedGroup != null && selectedLeaderboards != null && selectedLeaderboards.size() > 0) {
ArrayList<LeaderboardDTO> selectedLeaderboards = new ArrayList<LeaderboardDTO>(leaderboardsSelectionModel.getSelectedSet());
if (selectedGroup != null && selectedLeaderboards != null && !selectedLeaderboards.isEmpty()) {
Collections.sort(selectedLeaderboards, new Comparator<LeaderboardDTO>() {
@Override
public int compare(LeaderboardDTO l1, LeaderboardDTO l2) {
List<LeaderboardDTO> leaderboards = leaderboardsProvider.getList();
return ((Integer) leaderboards.indexOf(l1)).compareTo(leaderboards.indexOf(l2));
}
});
for (LeaderboardDTO leaderboard : selectedLeaderboards) {
if (!selectedGroup.leaderboards.contains(leaderboard)) {
selectedGroup.leaderboards.add(leaderboard);
@@ -230,10 +230,11 @@ public class RaceMap extends SimplePanel implements TimeListener, CompetitorSele
map.addMapZoomEndHandler(new MapZoomEndHandler() {
@Override
public void onZoomEnd(MapZoomEndEvent event) {
map.checkResizeAndCenter();
mapZoomedOrPannedSinceLastRaceSelectionChange = true;
Set<CompetitorDTO> competitorDTOsOfUnusedMarkers = new HashSet<CompetitorDTO>(boatMarkers.keySet());
for (CompetitorDTO competitorDTO : getCompetitorsToShow()) {
boolean usedExistingMarker = updateMarkerForCompetitor(competitorDTO);
boolean usedExistingMarker = updateBoatMarkerForCompetitor(competitorDTO);
if (usedExistingMarker) {
competitorDTOsOfUnusedMarkers.remove(competitorDTO);
}
@@ -352,15 +353,15 @@ public class RaceMap extends SimplePanel implements TimeListener, CompetitorSele
/**
* From {@link #fixes} as well as the selection of {@link #getCompetitorsToShow competitors to show}, computes the
* from/to times for which to request GPS raceMapData.fixes from the server. No update is performed here to {@link #fixes}. The
* from/to times for which to request GPS fixes from the server. No update is performed here to {@link #fixes}. The
* result guarantees that, when used in
* {@link SailingServiceAsync#getBoatPositions(String, String, Map, Map, boolean, AsyncCallback)}, for each
* competitor from {@link #competitorsToShow} there are all raceMapData.fixes known by the server for that competitor starting
* competitor from {@link #competitorsToShow} there are all fixes known by the server for that competitor starting
* at <code>upTo-{@link #tailLengthInMilliSeconds}</code> and ending at <code>upTo</code> (exclusive).
*
* @return a triple whose {@link Triple#getA() first} component contains the "from", and whose {@link Triple#getB()
* second} component contains the "to" times for the competitors whose trails / positions to show; the
* {@link Triple#getC() third} component tells whether the existing raceMapData.fixes can remain and be augmented by
* {@link Triple#getC() third} component tells whether the existing fixes can remain and be augmented by
* those requested or need to be replaced
*/
protected Triple<Map<CompetitorDTO, Date>, Map<CompetitorDTO, Date>, Map<CompetitorDTO, Boolean>> computeFromAndTo(
@@ -381,6 +382,7 @@ public class RaceMap extends SimplePanel implements TimeListener, CompetitorSele
&& !tailstart.before(timepointOfFirstKnownFix) && timepointOfLastKnownFix != null
&& !tailstart.after(timepointOfLastKnownFix)) {
// the beginning of what we need is contained in the interval we already have; skip what we already have
// FIXME requests the lastKnownFix again because "from" is *inclusive*; could lead to bug 319
fromDate = timepointOfLastKnownFix;
overlap = true;
} else {
@@ -408,10 +410,10 @@ public class RaceMap extends SimplePanel implements TimeListener, CompetitorSele
/**
* Adds the raceMapData.fixes received in <code>result</code> to {@link #fixes} and ensures they are still contiguous for each
* competitor. If <code>overlapsWithKnownraceMapData.fixes</code> indicates that the raceMapData.fixes received in <code>result</code>
* overlap with those already known, the raceMapData.fixes are merged into the list of already known raceMapData.fixes for the competitor.
* Otherwise, the raceMapData.fixes received in <code>result</code> replace those known so far for the respective competitor.
* Adds the fixes received in <code>result</code> to {@link #fixes} and ensures they are still contiguous for each
* competitor. If <code>overlapsWithKnownFixes</code> indicates that the fixes received in <code>result</code>
* overlap with those already known, the fixes are merged into the list of already known fixes for the competitor.
* Otherwise, the fixes received in <code>result</code> replace those known so far for the respective competitor.
*/
protected void updateFixes(Map<CompetitorDTO, List<GPSFixDTO>> result,
Map<CompetitorDTO, Boolean> overlapsWithKnownFixes) {
@@ -424,8 +426,8 @@ public class RaceMap extends SimplePanel implements TimeListener, CompetitorSele
}
if (!overlapsWithKnownFixes.get(e.getKey())) {
fixesForCompetitor.clear();
// to re-establish the invariants for raceMapData.tails, raceMapData.firstShownFix and raceMapData.lastShownFix, we now need to remove
// all points from the competitor's polyline and clear the entries in raceMapData.firstShownFix and raceMapData.lastShownFix
// to re-establish the invariants for tails, firstShownFix and lastShownFix, we now need to remove
// all points from the competitor's polyline and clear the entries in firstShownFix and lastShownFix
if (map != null && tails.containsKey(e.getKey())) {
map.removeOverlay(tails.remove(e.getKey()));
}
@@ -481,7 +483,7 @@ public class RaceMap extends SimplePanel implements TimeListener, CompetitorSele
updateTail(tail, competitorDTO, from, to);
competitorDTOsOfUnusedTails.remove(competitorDTO);
}
boolean usedExistingMarker = updateMarkerForCompetitor(competitorDTO);
boolean usedExistingMarker = updateBoatMarkerForCompetitor(competitorDTO);
if (usedExistingMarker) {
competitorDTOsOfUnusedMarkers.remove(competitorDTO);
}
@@ -499,88 +501,14 @@ public class RaceMap extends SimplePanel implements TimeListener, CompetitorSele
private void zoomMapToNewBounds(LatLngBounds newBounds) {
if (newBounds != null) {
map.setZoomLevel(map.getBoundsZoomLevel(newBounds));
boolean oldMapZoomedOrPannedSinceLastRaceSelectionChange = mapZoomedOrPannedSinceLastRaceSelectionChange;
map.setCenter(newBounds.getCenter());
map.checkResizeAndCenter();
map.setZoomLevel(map.getBoundsZoomLevel(newBounds));
mapZoomedOrPannedSinceLastRaceSelectionChange = oldMapZoomedOrPannedSinceLastRaceSelectionChange;
}
}
/**
* Calculates the new map bounds for the given data considering the current {@link ZoomTypes} of the map.
* @return The new map bounds
*/
// private LatLngBounds getNewMapBounds(Iterable<CompetitorDTO> competitorsToZoom, Iterable<MarkDTO> marksToZoom) {
// LatLngBounds newBounds = null;
// if (getSettings().getZoomSetting() != ZoomTypes.MANUAL) {
// //Extend bounds for the boats
// if (getSettings().getZoomSetting() == ZoomTypes.ZOOM_TO_BOATS
// || getSettings().getZoomSetting() == ZoomTypes.ZOOM_TO_BOATS_AND_BUOYS) {
// LatLngBounds boatBounds = getBoundsForBoats(competitorsToZoom);
// if (boatBounds != null) {
// newBounds = boatBounds;
// }
// }
// //Extend bounds for the marks
// if (getSettings().getZoomSetting() == ZoomTypes.ZOOM_TO_BUOYS
// || getSettings().getZoomSetting() == ZoomTypes.ZOOM_TO_BOATS_AND_BUOYS) {
// LatLngBounds markBounds = getBoundsForMarks(marksToZoom);
// if (markBounds != null) {
// if (newBounds == null) {
// newBounds = markBounds;
// } else {
// newBounds.extend(markBounds.getNorthEast());
// newBounds.extend(markBounds.getSouthWest());
// }
// }
// }
// }
// return newBounds;
// }
// private LatLngBounds getBoundsForBoats(Iterable<CompetitorDTO> competitorsToZoom) {
// LatLngBounds newBounds = null;
// for (CompetitorDTO competitorDTO : competitorsToZoom) {
// LatLngBounds bounds = null;
// if (getSettings().isIncludeTailsToAutoZoom()) {
// Polyline tail = tails.get(competitorDTO);
// bounds = tail != null ? tail.getBounds() : null;
// } else {
// List<GPSFixDTO> competitorFixes = fixes.get(competitorDTO);
// GPSFixDTO competitorFix = competitorFixes != null ? competitorFixes.get(competitorFixes.size() - 1) : null;
// PositionDTO competitorPosition = competitorFix != null ? competitorFix.position : null;
// LatLng competitorLatLng = competitorPosition != null ? LatLng.newInstance(competitorPosition.latDeg, competitorPosition.lngDeg) : null;
// bounds = competitorLatLng != null ? LatLngBounds.newInstance(competitorLatLng, competitorLatLng) : null;
// }
// if (bounds != null) {
// if (newBounds == null) {
// newBounds = bounds;
// } else {
// newBounds.extend(bounds.getNorthEast());
// newBounds.extend(bounds.getSouthWest());
// }
// }
// }
// return newBounds;
// }
// private LatLngBounds getBoundsForMarks(Iterable<MarkDTO> marksToZoom) {
// LatLngBounds newBounds = null;
// if (marksToZoom != null) {
// for (MarkDTO markDTO : marksToZoom) {
// LatLng markLatLng = LatLng.newInstance(markDTO.position.latDeg, markDTO.position.lngDeg);
// LatLngBounds bounds = LatLngBounds.newInstance(markLatLng, markLatLng);
// if (newBounds == null) {
// newBounds = bounds;
// } else {
// newBounds.extend(bounds.getNorthEast());
// newBounds.extend(bounds.getSouthWest());
// }
// }
// }
// return newBounds;
// }
private boolean updateMarkerForCompetitor(CompetitorDTO competitorDTO) {
private boolean updateBoatMarkerForCompetitor(CompetitorDTO competitorDTO) {
boolean usedExistingMarker = false;
if (lastShownFix.containsKey(competitorDTO) && lastShownFix.get(competitorDTO) != -1) {
GPSFixDTO lastPos = getBoatFix(competitorDTO);
@@ -1179,7 +1107,7 @@ public class RaceMap extends SimplePanel implements TimeListener, CompetitorSele
}
for (CompetitorDTO competitor : competitors) {
List<GPSFixDTO> competitorFixes = forMap.fixes.get(competitor);
GPSFixDTO competitorFix = competitorFixes != null ? competitorFixes.get(competitorFixes.size() - 1) : null;
GPSFixDTO competitorFix = competitorFixes != null ? competitorFixes.get(forMap.lastShownFix.get(competitor)) : null;
PositionDTO competitorPosition = competitorFix != null ? competitorFix.position : null;
LatLng competitorLatLng = competitorPosition != null ? LatLng.newInstance(competitorPosition.latDeg,
competitorPosition.lngDeg) : null;
@@ -1235,8 +1163,7 @@ public class RaceMap extends SimplePanel implements TimeListener, CompetitorSele
if (newBounds == null) {
newBounds = bounds;
} else {
newBounds.extend(bounds.getNorthEast());
newBounds.extend(bounds.getSouthWest());
newBounds.extend(markLatLng);
}
}
}
@@ -15,7 +15,7 @@ public class RaceMapSettings {
private RaceMapZoomSettings zoomSettings;
private long tailLengthInMilliseconds = 30000l;
private long tailLengthInMilliseconds = 300000l;
public RaceMapSettings() {
maneuverTypesToShow = new HashSet<ManeuverType>();
@@ -39,6 +39,7 @@ public class RaceMapZoomSettings {
typesToConsiderOnZoom = new ArrayList<ZoomTypes>();
typesToConsiderOnZoom.add(ZoomTypes.BOATS);
typesToConsiderOnZoom.add(ZoomTypes.TAILS);
typesToConsiderOnZoom.add(ZoomTypes.BUOYS);
zoomToSelectedCompetitors = false;
}
@@ -92,6 +92,7 @@ public class WindChart implements Component<WindChartSettings>, RaceSelectionCha
chart.setToolTip(new ToolTip().setEnabled(true).setFormatter(new ToolTipFormatter() {
@Override
public String format(ToolTipData toolTipData) {
// TODO consider using toolTipData.getPoint().getName() instead...
return "<b>" + toolTipData.getSeriesName() + (toolTipData.getPointName() != null ? " "+toolTipData.getPointName() : "")
+ "</b><br/>" +
dateFormat.format(new Date(toolTipData.getXAsLong())) + ": " +
@@ -115,6 +116,7 @@ public class WindChart implements Component<WindChartSettings>, RaceSelectionCha
onRaceSelectionChange(raceSelectionProvider.getSelectedRaces());
}
showVisibleSeries();
timer.addTimeListener(this);
}
@Override
@@ -154,7 +156,11 @@ public class WindChart implements Component<WindChartSettings>, RaceSelectionCha
return newSeries;
}
public void updateStripChartSeries(WindInfoForRaceDTO result) {
/**
* Updates the wind charts with the wind data from <code>result</code>. If <code>append</code> is <code>true</code>, previously
* existing points in the chart are left unchanged. Otherwise, the existing wind series are replaced.
*/
public void updateStripChartSeries(WindInfoForRaceDTO result, boolean append) {
final NumberFormat numberFormat = NumberFormat.getFormat("0");
for (Map.Entry<WindSource, WindTrackInfoDTO> e : result.windTrackInfoByWindSource.entrySet()) {
WindSource windSource = e.getKey();
@@ -175,7 +181,16 @@ public class WindChart implements Component<WindChartSettings>, RaceSelectionCha
}
points[i++] = newPoint;
}
series.setPoints(points);
Point[] newPoints;
if (append) {
Point[] oldPoints = series.getPoints();
newPoints = new Point[oldPoints.length + points.length];
System.arraycopy(oldPoints, 0, newPoints, 0, oldPoints.length);
System.arraycopy(points, 0, newPoints, oldPoints.length, points.length);
} else {
newPoints = points;
}
series.setPoints(newPoints);
}
}
@@ -191,7 +206,7 @@ public class WindChart implements Component<WindChartSettings>, RaceSelectionCha
/**
* Sets the visibilities of the wind source series based on the new settings. Note that this does not
* re-load any wind data. This has to happen by calling {@link #updateStripChartSeries(WindInfoForRaceDTO)}.
* re-load any wind data. This has to happen by calling {@link #updateStripChartSeries(WindInfoForRaceDTO, boolean)}.
*/
@Override
public void updateSettings(WindChartSettings newSettings) {
@@ -204,7 +219,12 @@ public class WindChart implements Component<WindChartSettings>, RaceSelectionCha
}
}
private void loadData(final RaceIdentifier raceIdentifier, final Date from, final Date to) {
/**
* @param append
* if <code>true</code>, the results retrieved from the server will be appended to the wind chart instead
* of overwriting the existing series.
*/
private void loadData(final RaceIdentifier raceIdentifier, final Date from, final Date to, final boolean append) {
sailingService.getWindInfo(raceIdentifier,
// TODO Time interval should be determined by a selection in the chart but be at most 60s. See bug #121. Consider incremental updates for new data only.
from, to, // use race start and time of newest event as default time period
@@ -213,9 +233,11 @@ public class WindChart implements Component<WindChartSettings>, RaceSelectionCha
@Override
public void onSuccess(WindInfoForRaceDTO result) {
if (result != null) {
updateStripChartSeries(result);
updateStripChartSeries(result, append);
} else {
clearChart(); // no wind known for untracked race
if (!append) {
clearChart(); // no wind known for untracked race
}
}
}
@@ -238,7 +260,7 @@ public class WindChart implements Component<WindChartSettings>, RaceSelectionCha
selectedRaceIdentifier = selectedRaces.iterator().next();
timeOfEarliestRequestInMillis = null;
timeOfLatestRequestInMillis = null;
loadData(selectedRaceIdentifier, /* from */ null, /* to */ null);
loadData(selectedRaceIdentifier, /* from */ null, /* to */ null, /* append */ false);
} else {
clearChart();
}
@@ -254,15 +276,15 @@ public class WindChart implements Component<WindChartSettings>, RaceSelectionCha
if (timer.getPlayMode() == PlayModes.Live) {
// is date before first cache entry or is cache empty?
if (timeOfEarliestRequestInMillis == null || timeOfEarliestRequestInMillis > date.getTime()) {
loadData(selectedRaceIdentifier, null, date);
loadData(selectedRaceIdentifier, null, date, /* append */ true);
} else if (timeOfLatestRequestInMillis < date.getTime()) {
loadData(selectedRaceIdentifier, new Date(timeOfLatestRequestInMillis), /* to */ null);
loadData(selectedRaceIdentifier, new Date(timeOfLatestRequestInMillis), /* to */ null, /* append */ true);
}
// otherwise the cache spans across date and so we don't need to load anything
} else {
// assuming play mode is replay / non-live
if (timeOfLatestRequestInMillis == null) {
loadData(selectedRaceIdentifier, /* from */ null, /* to */ null);
loadData(selectedRaceIdentifier, /* from */ null, /* to */ null, /* append */ false); // replace old series
}
}
}
@@ -253,7 +253,7 @@ public class WindPanel extends FormPanel implements EventDisplayer, WindShower,
grid.setWidget(3, 0, null);
VerticalPanel windDisplay = new VerticalPanel();
grid.setWidget(3, 0, windDisplay);
windChart.updateStripChartSeries(result);
windChart.updateStripChartSeries(result, /* append */ false);
for (Map.Entry<WindSource, WindTrackInfoDTO> e : result.windTrackInfoByWindSource.entrySet()) {
Label windSourceLabel = new Label(stringMessages.windSource()+": "+e.getKey()+
", "+stringMessages.dampeningInterval()+" "+e.getValue().dampeningIntervalInMilliseconds+"ms");
@@ -94,15 +94,9 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
if (raceTimesInfo.startOfTracking != null && raceTimesInfo.timePointOfNewestEvent != null) {
// we set here the min and max of the time slider, the start and end of the race as well as the known
// leg markers
long livePlayDelayInMillis = timer.getLivePlayDelayInMillis();
long liveTimePointInMillis = System.currentTimeMillis() - livePlayDelayInMillis;
if (liveTimePointInMillis < raceTimesInfo.timePointOfNewestEvent.getTime()
&& liveTimePointInMillis > raceTimesInfo.startOfTracking.getTime()) {
// don't worry; this will only fire an event if something actually changed
timer.setPlayMode(PlayModes.Live);
} else {
timer.setPlayMode(PlayModes.Replay);
}
boolean liveModeToBeMadePossible = isLiveModeToBeMadePossible();
setLiveGenerallyPossible(liveModeToBeMadePossible);
setJumpToLiveEnablement(liveModeToBeMadePossible && timer.getPlayMode() != PlayModes.Live);
boolean timerAlreadyInitialized = getMin() != null && getMax() != null && sliderBar.getCurrentValue() != null;
initMinMax(raceTimesInfo);
if (!timerAlreadyInitialized) {
@@ -116,6 +110,18 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
}
}
@Override
protected boolean isLiveModeToBeMadePossible() {
long livePlayDelayInMillis = timer.getLivePlayDelayInMillis();
long eventTimeoutTolerance = 30 * 1000; // 30s
long liveTimePointInMillis = System.currentTimeMillis() - livePlayDelayInMillis;
return lastRaceTimesInfo != null &&
lastRaceTimesInfo.timePointOfNewestEvent != null &&
liveTimePointInMillis < lastRaceTimesInfo.timePointOfNewestEvent.getTime() + eventTimeoutTolerance &&
lastRaceTimesInfo.startOfTracking != null &&
liveTimePointInMillis > lastRaceTimesInfo.startOfTracking.getTime();
}
@Override
public void onRaceSelectionChange(List<RaceIdentifier> selectedRaces) {
if (selectedRaces != null && !selectedRaces.isEmpty()) {
@@ -133,20 +139,34 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
private void initMinMax(RaceTimesInfoDTO newRaceTimesInfo) {
Date min = null;
Date max = null;
if (newRaceTimesInfo.startOfTracking != null) {
min = newRaceTimesInfo.startOfTracking;
} else if (newRaceTimesInfo.startOfRace != null) {
min = new Date(newRaceTimesInfo.startOfRace.getTime() - 5 * 60 * 1000);
}
switch (timer.getPlayMode()) {
case Live:
if (newRaceTimesInfo.startOfRace != null) {
long extensionTime = calculateRaceExtensionTime(newRaceTimesInfo.startOfRace, newRaceTimesInfo.timePointOfNewestEvent);
min = new Date(newRaceTimesInfo.startOfRace.getTime() - extensionTime);
} else if (newRaceTimesInfo.startOfTracking != null) {
min = newRaceTimesInfo.startOfTracking;
}
if (newRaceTimesInfo.timePointOfNewestEvent != null) {
max = newRaceTimesInfo.timePointOfNewestEvent;
}
break;
case Replay:
//TODO Merge with Franks branch for better end of race calculation
Date tempEndOfRace = newRaceTimesInfo.getLastLegTimes() != null ? newRaceTimesInfo.getLastLegTimes().firstPassingDate : newRaceTimesInfo.endOfRace;
long extensionTime = calculateRaceExtensionTime(newRaceTimesInfo.startOfRace, tempEndOfRace);
if (newRaceTimesInfo.startOfRace != null) {
min = new Date(newRaceTimesInfo.startOfRace.getTime() - extensionTime);
} else if (newRaceTimesInfo.startOfTracking != null) {
min = newRaceTimesInfo.startOfTracking;
}
if (newRaceTimesInfo.endOfRace != null) {
max = newRaceTimesInfo.endOfRace;
max = new Date(tempEndOfRace.getTime() + extensionTime);
} else if (newRaceTimesInfo.timePointOfNewestEvent != null) {
max = newRaceTimesInfo.timePointOfNewestEvent;
}
@@ -158,6 +178,20 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
}
}
private long calculateRaceExtensionTime(Date startTime, Date endTime) {
if (startTime == null || endTime == null) {
return 5 * 60 * 1000; //5 minutes
}
long minExtensionTime = 60 * 1000; // 1 minute
long maxExtensionTime = 10 * 60 * 1000; // 10 minutes
double extensionTimeFactor = 0.1; // 10 percent of the overall race length
long extensionTime = (long) ((endTime.getTime() - startTime.getTime()) * extensionTimeFactor);
return extensionTime < minExtensionTime ? minExtensionTime : extensionTime > maxExtensionTime ? maxExtensionTime : extensionTime;
}
/**
* When in {@link PlayModes#Replay} mode, tries to put the {@link #timer} to the time point when the last leg was
* finished first. If this time point is not (yet?) known, tries to put the {@link #timer} to the
@@ -14,17 +14,15 @@ public class RaceTimePanelSettingsDialogComponent extends TimePanelSettingsDialo
@Override
public Widget getAdditionalWidget(DataEntryDialog<RaceTimePanelSettings> dialog) {
Widget widget = super.getAdditionalWidget(dialog);
HorizontalPanel labelAndTDelayForLiveBoxPanel = new HorizontalPanel();
labelAndTDelayForLiveBoxPanel.setSpacing(5);
labelAndTDelayForLiveBoxPanel.add(new Label("Delay for live mode:"));
labelAndTDelayForLiveBoxPanel.add(new Label(getStringMessages().delayForLiveMode()));
RaceTimesInfoDTO raceTimesInfo = initialSettings.getRaceTimesInfo();
if(raceTimesInfo != null && raceTimesInfo.startOfTracking != null) {
if (raceTimesInfo != null && raceTimesInfo.startOfTracking != null) {
long delayforLiveModeInMs = System.currentTimeMillis() - raceTimesInfo.getStartOfTracking().getTime();
labelAndTDelayForLiveBoxPanel.add(new Label(delayforLiveModeInMs / 1000 + " s"));
}
mainContentPanel.add(labelAndTDelayForLiveBoxPanel);
return widget;
}
@@ -159,8 +159,6 @@ public interface SailingService extends RemoteService {
void renameLeaderboardGroup(String oldName, String newName);
void removeLeaderboardGroup(String groupName);
LeaderboardGroupDTO createLeaderboardGroup(String groupName, String description, List<String> leaderboardNames);
LeaderboardGroupDTO createLeaderboardGroup(String groupName, String description);
@@ -14,6 +14,7 @@ import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.impl.Util.Pair;
import com.sap.sailing.domain.leaderboard.RaceInLeaderboard;
import com.sap.sailing.domain.leaderboard.LeaderboardGroup;
import com.sap.sailing.gwt.ui.shared.CompetitorDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
@@ -234,18 +235,44 @@ public interface SailingServiceAsync {
void getManeuvers(RaceIdentifier raceIdentifier, Map<CompetitorDTO, Date> from, Map<CompetitorDTO, Date> to,
AsyncCallback<Map<CompetitorDTO, List<ManeuverDTO>>> callback);
/**
* Creates a {@link LeaderboardGroupDTO} for each {@link LeaderboardGroup} known by the server, which contains the
* name, the description and a list with {@link LeaderboardDTO LeaderboardDTOs} contained by the group.
*/
void getLeaderboardGroups(AsyncCallback<List<LeaderboardGroupDTO>> callback);
/**
* Creates a {@link LeaderboardGroupDTO} for the {@link LeaderboardGroup} with the name <code>groupName</code>, which contains the
* name, the description and a list with {@link LeaderboardDTO LeaderboardDTOs} contained by the group.<br />
* If no group with the name <code>groupName</code> is known, an {@link IllegalArgumentException} is thrown.
*/
void getLeaderboardGroupByName(String groupName, AsyncCallback<LeaderboardGroupDTO> callback);
/**
* Renames the group with the name <code>oldName</code> to the <code>newName</code>.<br />
* If there's no group with the name <code>oldName</code> or there's already a group with the name
* <code>newName</code> a {@link IllegalArgumentException} is thrown.
*/
void renameLeaderboardGroup(String oldName, String newName, AsyncCallback<Void> callback);
/**
* Removes the leaderboard group with the name <code>groupName</code> from the service and the persistant store.
*/
void removeLeaderboardGroup(String groupName, AsyncCallback<Void> callback);
void createLeaderboardGroup(String groupName, String description, List<String> leaderboardNames, AsyncCallback<LeaderboardGroupDTO> callback);
/**
* Creates a new group with the name <code>groupname</code>, the description <code>description</code> and an empty list of leaderboards.<br/>
*/
void createLeaderboardGroup(String groupName, String description, AsyncCallback<LeaderboardGroupDTO> callback);
/**
* Updates the data of the group with the name <code>oldName</code>.
*
* @param oldName The old name of the group
* @param newName The new name of the group
* @param description The new description of the group
* @param leaderboards The new leaderboards of the group
*/
void updateLeaderboardGroup(String oldName, String newName, String description, List<LeaderboardDTO> leaderboards, AsyncCallback<Void> callback);
/**
@@ -261,4 +261,5 @@ public interface StringMessages extends Messages {
String directionChange();
String speedChange();
String degreesShort();
String delayForLiveMode();
}
@@ -255,4 +255,5 @@ competitorCharts=Competitor charts
selectAtLeastOneCompetitor=Select at least one competitor
directionChange=Direction change
speedChange=Speed change
degreesShort=deg
degreesShort=deg
delayForLiveMode=Delay for live mode:
@@ -256,4 +256,5 @@ competitorCharts=Competitor charts
selectAtLeastOneCompetitor=Mindestens einen Segler auswählen
directionChange=Richtungsänderung
speedChange=Geschwindigkeitsänderung
degreesShort=deg
degreesShort=deg
delayForLiveMode=Verzögerung im Live-Modus:
@@ -118,6 +118,10 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
@Override
public void onValueChange(ValueChangeEvent<Double> newValue) {
if(sliderBar.getCurrentValue() != null) {
if (TimePanel.this.timer.getPlayMode() == PlayModes.Live) {
// put timer into replay mode when user explicitly adjusts time; avoids having to press pause first
TimePanel.this.timer.setPlayMode(PlayModes.Replay);
}
TimePanel.this.timer.setTime(sliderBar.getCurrentValue().longValue());
}
}
@@ -156,10 +160,11 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
playPauseImage.getElement().getStyle().setPadding(3, Style.Unit.PX);
playControlPanel.add(playPauseImage);
backToLivePlayButton = new Button("Live");
backToLivePlayButton = new Button(stringMessages.playModeLive());
backToLivePlayButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
TimePanel.this.timer.setPlayMode(PlayModes.Live);
TimePanel.this.timer.play();
}
});
@@ -327,20 +332,22 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
@Override
public void playStateChanged(PlayStates playState, PlayModes playMode) {
boolean liveModeToBeMadePossible = isLiveModeToBeMadePossible();
setLiveGenerallyPossible(liveModeToBeMadePossible);
setJumpToLiveEnablement(liveModeToBeMadePossible && playMode != PlayModes.Live);
switch (playState) {
case Playing:
playPauseImage.setResource(pauseButtonImg);
if (playMode == PlayModes.Live)
if (playMode == PlayModes.Live) {
playModeImage.setResource(playModeLiveActiveImg);
else
} else {
playModeImage.setResource(playModeReplayActiveImg);
backToLivePlayButton.setEnabled(false);
}
break;
case Paused:
case Stopped:
playPauseImage.setResource(playButtonImg);
playModeImage.setResource(playModeInactiveImg);
backToLivePlayButton.setEnabled(true);
break;
}
@@ -350,7 +357,6 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
timeDelayLabel.setText(stringMessages.timeDelay() + ": " + timer.getCurrentDelayInMillis() / 1000 + " s");
timeDelayLabel.setVisible(true);
sliderBar.setEnabled(true);
backToLivePlayButton.setVisible(true);
playSpeedBox.setEnabled(false);
slowDownButton.setEnabled(false);
speedUpButton.setEnabled(false);
@@ -360,13 +366,30 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
timeDelayLabel.setText("");
playModeLabel.setText(stringMessages.playModeReplay());
sliderBar.setEnabled(true);
backToLivePlayButton.setVisible(false);
playSpeedBox.setEnabled(true);
slowDownButton.setEnabled(true);
speedUpButton.setEnabled(true);
break;
}
}
protected boolean isLiveModeToBeMadePossible() {
return false;
}
/**
* Enables the {@link #backToLivePlayButton} if and only if <code>enabled</code> is <code>true</code>.
*/
protected void setJumpToLiveEnablement(boolean enabled) {
backToLivePlayButton.setEnabled(enabled);
}
/**
* Iff <code>possible</code>, makes the {@link #backToLivePlayButton} button visible.
*/
protected void setLiveGenerallyPossible(boolean possible) {
backToLivePlayButton.setVisible(possible);
}
@SuppressWarnings("unchecked")
public T getSettings() {
@@ -396,12 +419,11 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
boolean delayChanged = newSettings.getDelayToLivePlayInSeconds() != getSettings().getDelayToLivePlayInSeconds();
if (delayChanged) {
timer.setDelay(1000l * newSettings.getDelayToLivePlayInSeconds());
if(timer.getPlayMode() == PlayModes.Live) {
timeDelayLabel.setText(String.valueOf(newSettings.getDelayToLivePlayInSeconds()) + " s");
}
}
timer.setRefreshInterval(getSettings().getRefreshInterval());
timer.setRefreshInterval(newSettings.getRefreshInterval());
}
@Override
@@ -416,7 +438,6 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
if (child instanceof RequiresResize) {
((RequiresResize) child).onResize();
}
sliderBar.onResize();
}
@@ -21,6 +21,10 @@ public class TimePanelSettingsDialogComponent<T extends TimePanelSettings> imple
this.stringMessages = stringMessages;
initialSettings = settings;
}
protected StringMessages getStringMessages() {
return stringMessages;
}
@Override
public Widget getAdditionalWidget(DataEntryDialog<T> dialog) {
@@ -9,7 +9,11 @@ import com.google.gwt.core.client.Scheduler.RepeatingCommand;
/**
* Manages a timer and can auto-advance it at a given acceleration/deceleration rate, with a given delay compared to
* real time. It can be {@link #pause() paused} and {@link #resume() resumed}.
* real time. It can be {@link #pause() paused} and {@link #resume() resumed}. It can be in one of two play modes:
* {@link PlayModes#Live} and {@link PlayModes#Replay}. In live play mode the timer is always in state {@link PlayStates#Playing}
* and adjusts the time to now-{@link #livePlayDelayInMillis}. As soon as it's paused or stopped it enters
* {@link PlayModes#Replay}. By entering {@link PlayModes#Live} with {@link #setPlayMode(PlayModes)}, the timer starts
* playing right away.
*
* @author Axel Uhl (d043530)
*
@@ -43,7 +47,8 @@ public class Timer {
private PlayStates playState;
/**
* The current play mode of the timer
* The current play mode of the timer: live or replay. In live mode the timer will adjust to now-delay each time it
* refreshes. In replay mode it will advance time by the refresh interval times the play speed factor.
*/
private PlayModes playMode;
@@ -60,6 +65,7 @@ public class Timer {
/**
* Factor by which the timer runs faster than real time. 1.0 means real-time, 2.0 means twice as fast as real time, and so on.
* Applies only if the timer is in replay mode.
*/
private double playSpeedFactor;
@@ -143,36 +149,46 @@ public class Timer {
return time;
}
/**
* When setting the play mode to live, the timer will automatically be put into play state playing.
*/
public void setPlayMode(PlayModes newPlayMode) {
if (this.playMode != newPlayMode) {
this.playMode = newPlayMode;
for (PlayStateListener playStateListener : playStateListeners) {
playStateListener.playStateChanged(playState, playMode);
}
if (newPlayMode == PlayModes.Live) {
play();
}
}
}
/**
* Pauses this timer after the next time advance. {@link #playing} is set to <code>false</code> if not already
* paused, and registered {@link PlayStateListener}s will be notified.
* paused, and registered {@link PlayStateListener}s will be notified. If the play mode was live, it'll be set to
* replay.
*/
public void pause() {
if (playState == PlayStates.Playing) {
playState = PlayStates.Paused; // this will cause the repeating command to stop executing
for (PlayStateListener playStateListener : playStateListeners) {
playStateListener.playStateChanged(playState, playMode);
}
setPlayMode(PlayModes.Replay);
}
}
/**
* Puts the timer into the stopped state. If it was in live mode, puts it into replay mode.
*/
public void stop() {
if (playState == PlayStates.Playing) {
playState = PlayStates.Stopped;
for (PlayStateListener playStateListener : playStateListeners) {
playStateListener.playStateChanged(playState, playMode);
}
setPlayMode(PlayModes.Replay);
}
}
@@ -195,10 +211,11 @@ public class Timer {
public boolean execute() {
if (time != null && playState == PlayStates.Playing) {
long newTime = time.getTime();
if (playMode == PlayModes.Replay)
if (playMode == PlayModes.Replay) {
newTime += (long) playSpeedFactor * refreshInterval;
else
newTime += refreshInterval;
} else {
newTime = System.currentTimeMillis() - getLivePlayDelayInMillis();
}
setTime(newTime);
}
if (refreshIntervalChanged) {
@@ -124,6 +124,7 @@ implements CompetitorSelectionChangeListener, RaceSelectionChangeListener, TimeL
if(!selectedRaces.isEmpty()) {
loadData();
}
timer.addTimeListener(this);
}
protected void selectRace(final RaceIdentifier selectedRace) {
@@ -20,6 +20,7 @@ import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.UserAgentChecker.UserAgentTypes;
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettingsFactory;
import com.sap.sailing.gwt.ui.shared.CompetitorDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardEntryDTO;
@@ -251,8 +252,9 @@ public class EditableLeaderboardPanel extends LeaderboardPanel {
public EditableLeaderboardPanel(SailingServiceAsync sailingService, String leaderboardName, String leaderboardGroupName,
ErrorReporter errorReporter, StringMessages stringConstants, UserAgentTypes userAgentType) {
super(sailingService, null, new CompetitorSelectionModel(/* hasMultiSelection */ true), leaderboardName, leaderboardGroupName,
errorReporter, stringConstants, userAgentType);
super(sailingService, LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(
/* autoExpandFirstRace */false), new CompetitorSelectionModel(/* hasMultiSelection */true),
leaderboardName, leaderboardGroupName, errorReporter, stringConstants, userAgentType);
}
/**
@@ -32,31 +32,26 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint {
@Override
public void onModuleLoad() {
super.onModuleLoad();
eventName = Window.Location.getParameter("eventName");
raceName = Window.Location.getParameter("raceName");
String leaderboardNameParamValue = Window.Location.getParameter("leaderboardName");
String leaderboardGroupNameParamValue = Window.Location.getParameter("leaderboardGroupName");
if(leaderboardNameParamValue == null || leaderboardNameParamValue.isEmpty()) {
leaderboardName = DefaultLeaderboardName.DEFAULT_LEADERBOARD_NAME;
} else {
leaderboardName = leaderboardNameParamValue;
}
if(leaderboardGroupNameParamValue != null && !leaderboardGroupNameParamValue.isEmpty())
if(leaderboardGroupNameParamValue != null && !leaderboardGroupNameParamValue.isEmpty()) {
leaderboardGroupName = leaderboardGroupNameParamValue;
}
if (eventName == null || eventName.isEmpty() || raceName == null || raceName.isEmpty()) {
createErrorPage("This page requires a valid event name and race name.");
return;
}
final ParallelExecutionCallback<List<String>> getLeaderboardNamesCallback = new ParallelExecutionCallback<List<String>>();
final ParallelExecutionCallback<List<EventDTO>> listEventsCallback = new ParallelExecutionCallback<List<EventDTO>>();
final ParallelExecutionCallback<LeaderboardGroupDTO> getLeaderboardGroupByNameCallback = new ParallelExecutionCallback<LeaderboardGroupDTO>();
final ParallelExecutionCallback<UserDTO> getUserCallback = new ParallelExecutionCallback<UserDTO>();
if (leaderboardGroupName != null) {
new ParallelExecutionHolder(getLeaderboardNamesCallback, getLeaderboardGroupByNameCallback, listEventsCallback, getUserCallback) {
@Override
@@ -81,21 +76,19 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint {
}
};
}
sailingService.listEvents(false, listEventsCallback);
sailingService.getLeaderboardNames(getLeaderboardNamesCallback);
if(leaderboardGroupName != null)
if(leaderboardGroupName != null) {
sailingService.getLeaderboardGroupByName(leaderboardGroupNameParamValue, getLeaderboardGroupByNameCallback);
}
userManagementService.getUser(getUserCallback);
}
private void checkUrlParameters(List<String> leaderboardNames, LeaderboardGroupDTO leaderboardGroup, List<EventDTO> events, UserDTO user)
{
private void checkUrlParameters(List<String> leaderboardNames, LeaderboardGroupDTO leaderboardGroup, List<EventDTO> events, UserDTO user) {
if (!leaderboardNames.contains(leaderboardName)) {
createErrorPage(stringMessages.noSuchLeaderboard());
return;
}
if (leaderboardGroupName != null && leaderboardGroup != null) {
boolean foundLeaderboard = false;
for(LeaderboardDTO leaderBoard: leaderboardGroup.leaderboards) {
@@ -109,13 +102,11 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint {
return;
}
}
selectedRace = findRace(eventName, raceName, events);
if(selectedRace == null) {
createErrorPage("Could not obtain a race with name " + raceName + " for an event with name " + eventName);
return;
}
createRaceBoardPanel(selectedRace, events, user);
}
@@ -75,13 +75,11 @@ public class RaceBoardPanel extends FormPanel implements EventDisplayer, RaceSel
private final RaceTimePanel timePanel;
private final Timer timer;
private final RaceSelectionProvider raceSelectionProvider;
private final UserDTO user;
public RaceBoardPanel(SailingServiceAsync sailingService, UserDTO theUser, RaceSelectionProvider theRaceSelectionProvider, String leaderboardName,
String leaderboardGroupName, ErrorReporter errorReporter, final StringMessages stringMessages, UserAgentTypes userAgentType) {
this.sailingService = sailingService;
this.raceSelectionProvider = theRaceSelectionProvider;
this.user = theUser;
this.scrollOffset = 0;
raceSelectionProvider.addRaceSelectionChangeListener(this);
racesByIdentifier = new HashMap<RaceIdentifier, RaceDTO>();
@@ -113,14 +111,7 @@ public class RaceBoardPanel extends FormPanel implements EventDisplayer, RaceSel
boolean showLeaderboard = true;
boolean showMap = true;
boolean showWindCharts = true;
boolean showCompetitorCharts = true;
if (user == null) {
// anonymous user
showWindCharts = false;
}
// create the default leaderboard and select the right race
if(showLeaderboard) {
LeaderboardSettings leaderBoardSettings = LeaderboardSettingsFactory.getInstance().createNewSettingsForPlayMode(timer.getPlayMode());
@@ -142,16 +133,15 @@ public class RaceBoardPanel extends FormPanel implements EventDisplayer, RaceSel
raceMap.onRaceSelectionChange(Collections.singletonList(selectedRaceIdentifier));
collapsableViewers.add(raceMapViewer);
}
if(showWindCharts) {
WindChartSettings windChartSettings = new WindChartSettings(WindSource.values());
WindChart windChart = new WindChart(sailingService, raceSelectionProvider, timer, windChartSettings, stringMessages, errorReporter);
CollapsableComponentViewer<WindChartSettings> windChartViewer = new CollapsableComponentViewer<WindChartSettings>(
windChart, "auto", "400px", stringMessages);
windChart.onRaceSelectionChange(raceSelectionProvider.getSelectedRaces());
collapsableViewers.add(windChartViewer);
}
if(showCompetitorCharts) {
WindChartSettings windChartSettings = new WindChartSettings(WindSource.values());
WindChart windChart = new WindChart(sailingService, raceSelectionProvider, timer, windChartSettings,
stringMessages, errorReporter);
CollapsableComponentViewer<WindChartSettings> windChartViewer = new CollapsableComponentViewer<WindChartSettings>(
windChart, "auto", "400px", stringMessages);
windChart.onRaceSelectionChange(raceSelectionProvider.getSelectedRaces());
collapsableViewers.add(windChartViewer);
if (showCompetitorCharts) {
// DON'T DELETE -> this is temporary for testing of different chart types
// ChartPanel competitorCharts = new ChartPanel(sailingService, competitorSelectionModel, raceSelectionProvider,
// timer, DetailType.WINDWARD_DISTANCE_TO_OVERALL_LEADER, stringMessages, errorReporter);
@@ -781,7 +781,7 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
while (iterator.hasNext()) {
MarkPassing currentMarkPassing = iterator.next();
long diff = currentMarkPassing.getTimePoint().asMillis() - currentTimeToCheck;
if(diff > maxTimeFrameInMs) {
if (diff > maxTimeFrameInMs) {
// reset the check
currentCompetitorsInTime = 0;
currentTimeToCheck = currentMarkPassing.getTimePoint().asMillis();
@@ -1576,14 +1576,9 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
getService().removeLeaderboardGroup(groupName);
}
@Override
public LeaderboardGroupDTO createLeaderboardGroup(String groupName, String description, List<String> leaderboardNames) {
return convertToLeaderboardGroupDTO(getService().addLeaderboardGroup(groupName, description, leaderboardNames));
}
@Override
public LeaderboardGroupDTO createLeaderboardGroup(String groupName, String description) {
return createLeaderboardGroup(groupName, description, new ArrayList<String>());
return convertToLeaderboardGroupDTO(getService().addLeaderboardGroup(groupName, description, new ArrayList<String>()));
}
@Override
@@ -43,6 +43,7 @@ import com.sap.sailing.gwt.ui.client.EventRefresher;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettingsFactory;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardDTO;
@@ -479,7 +480,8 @@ public class OverviewEventPanel extends AbstractEventPanel {
}
currentLeaderboard = boardToDisplay;
CompetitorSelectionModel competitorSelectionModel = new CompetitorSelectionModel(true);
displayedLeaderboardPanel = new LeaderboardPanel(sailingService, null, competitorSelectionModel,
displayedLeaderboardPanel = new LeaderboardPanel(sailingService, LeaderboardSettingsFactory.getInstance()
.createNewDefaultSettings(/* autoExpandFirstRace */false), competitorSelectionModel,
currentLeaderboard.name, null, errorReporter, stringConstants, null);
leaderboardPanel.add(displayedLeaderboardPanel);
}
@@ -20,10 +20,9 @@ public class SpectatorEntryPoint extends AbstractEntryPoint implements EventRefr
@Override
public void onModuleLoad() {
super.onModuleLoad();
String groupParamValue = Window.Location.getParameter("leaderboardGroupName");
final String groupName;
if(groupParamValue == null || groupParamValue.isEmpty()) {
if (groupParamValue == null || groupParamValue.isEmpty()) {
groupName = null;
} else {
groupName = groupParamValue;
@@ -36,7 +35,6 @@ public class SpectatorEntryPoint extends AbstractEntryPoint implements EventRefr
public void onSuccess(LeaderboardGroupDTO group) {}
});
}
String root = Window.Location.getParameter("root");
//Check if the root contains an allowed value
if (root != null) {
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

@@ -227,16 +227,51 @@ public interface RacingEventService extends TrackedEventRegistry {
TrackedRace getExistingTrackedRace(RaceIdentifier raceIdentifier);
/**
* Obtains an unmodifiable map of the leaderboard groups configured in this service keyed by their names.
*/
Map<String, LeaderboardGroup> getLeaderboardGroups();
/**
* @param groupName The name of the requested leaderboard group
* @return The leaderboard group with the name <code>groupName</code>, or <code>null</code> if theres no such group
*/
LeaderboardGroup getLeaderboardGroupByName(String groupName);
/**
* Creates a new group with the name <code>groupName</code>, the description <code>desciption</code> and the
* leaderboards with the names in <code>leaderboardNames</code> and saves it in the database.
*
* @param groupName
* The name of the new group
* @param description
* The description of the new group
* @param leaderboardNames
* The names of the leaderboards, which should be contained by the new group.<br />
* If there isn't a leaderboard with one of these names an {@link IllegalArgumentException} is thrown.
* @return The new leaderboard group
*/
LeaderboardGroup addLeaderboardGroup(String groupName, String description, List<String> leaderboardNames);
/**
* Removes the group with the name <code>groupName</code> from the service and the database.
* @param groupName The name of the group which shall be removed.
*/
void removeLeaderboardGroup(String groupName);
/**
* Renames the group with the name <code>oldName</code> to the <code>newName</code>.<br />
* If there's no group with the name <code>oldName</code> or there's already a group with the name
* <code>newName</code> a {@link IllegalArgumentException} is thrown.
*
* @param oldName The old name of the group
* @param newName The new name of the group
*/
void renameLeaderboardGroup(String oldName, String newName);
/**
* Updates the group data in the persistant store.
*/
void updateStoredLeaderboardGroup(LeaderboardGroup leaderboardGroup);
void addExpeditionListener(ExpeditionListener listener, boolean validMessagesOnly) throws SocketException;
@@ -7,6 +7,8 @@ import java.net.InetAddress;
import java.net.SocketException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.LinkedBlockingDeque;
import java.util.concurrent.TimeUnit;
import java.util.logging.Logger;
public abstract class UDPReceiver<MessageType extends UDPMessage, ListenerType extends UDPMessageListener<MessageType>> implements Runnable {
@@ -17,6 +19,8 @@ public abstract class UDPReceiver<MessageType extends UDPMessage, ListenerType e
private final int listeningOnPort;
private final DatagramSocket udpSocket;
private final Map<ListenerType, ToListenerDispatcher> listenerThreads;
/**
* For each listener remembers if the listener is only interested in valid messages.
@@ -28,6 +32,45 @@ public abstract class UDPReceiver<MessageType extends UDPMessage, ListenerType e
*/
private static final int MAX_PACKET_SIZE = 65536;
private class ToListenerDispatcher extends Thread {
private final LinkedBlockingDeque<MessageType> queue;
private final ListenerType listener;
private boolean stopped;
public ToListenerDispatcher(ListenerType listener) {
super("UDPReceiver ToListenerDispatcher for listener "+listener);
queue = new LinkedBlockingDeque<MessageType>(/* capacity */ 10000);
this.listener = listener;
stopped = false;
}
/**
* Offers the message to the queue used by this dispatcher. If the queue is full, the message is dropped without notice.
*/
public void dispatch(MessageType message) {
queue.offer(message);
}
public void halt() {
stopped = true;
}
public void run() {
MessageType message;
try {
message = queue.poll(/* timeout */ 10000, TimeUnit.MILLISECONDS);
while (!stopped) {
if (message != null) {
listener.received(message);
}
message = queue.poll(/* timeout */ 10000, TimeUnit.MILLISECONDS);
}
} catch (InterruptedException e) {
// when interrupted, this means we'll terminate
}
}
}
/**
* You need call {@link #run} to actually start receiving events. To do this asynchronously,
* start this object in a new thread.
@@ -36,6 +79,7 @@ public abstract class UDPReceiver<MessageType extends UDPMessage, ListenerType e
udpSocket = new DatagramSocket(listeningOnPort);
listeners = new HashMap<ListenerType, Boolean>();
this.listeningOnPort = listeningOnPort;
listenerThreads = new HashMap<ListenerType, ToListenerDispatcher>();
}
public void stop() throws SocketException, IOException {
@@ -78,7 +122,8 @@ public abstract class UDPReceiver<MessageType extends UDPMessage, ListenerType e
for (ListenerType listener : listeners.keySet()) {
if (!listeners.get(listener) || msg.isValid()) {
try {
listener.received(msg);
ToListenerDispatcher dispatcher = listenerThreads.get(listener);
dispatcher.dispatch(msg);
} catch (Throwable t) {
logger.info("Exception while dispatching UDP packet received to "+listener+": "+t.getMessage());
}
@@ -97,11 +142,15 @@ public abstract class UDPReceiver<MessageType extends UDPMessage, ListenerType e
}
public synchronized void addListener(ListenerType listener, boolean validMessagesOnly) {
ToListenerDispatcher listenerThread = new ToListenerDispatcher(listener);
listenerThread.start();
listenerThreads.put(listener, listenerThread);
listeners.put(listener, validMessagesOnly);
}
public synchronized void removeListener(ListenerType listener) {
listeners.remove(listener);
listenerThreads.remove(listener).halt();
}
public int getPort() {
+1 -1
View File
@@ -5,7 +5,7 @@ active_branch=$(git symbolic-ref -q HEAD)
active_branch=`basename $active_branch`
echo Active branch is: $active_branch
cd $exec_dir
server_instance=`basename \`pwd\` | sed -e 's/[12]$//'`
server_instance=`basename \`pwd\``
echo Active server is: $server_instance
if [ $active_branch == $server_instance ]
then
+1 -1
View File
@@ -1 +1 @@
java -Dfile.encoding=cp1252 -Djetty.home=configuration/jetty -Djava.util.logging.config.file=configuration/logging.properties -Xmx8192m -jar plugins/org.eclipse.equinox.launcher_1.2.0.v20110502.jar -console -configuration ./ -clean
"$JAVA_HOME/bin/java" -Dfile.encoding=cp1252 -Djetty.home=configuration/jetty -Djava.util.logging.config.file=configuration/logging.properties -Xmx8192m -jar plugins/org.eclipse.equinox.launcher_1.2.0.v20110502.jar -console -configuration ./ -clean