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
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
|
||||
}
|
||||
|
||||
|
||||
|
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) {
|
||||
|
||||
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.6 KiB |
|
Before Width: | Height: | Size: 2.5 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.0 KiB |
|
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() {
|
||||
|
||||
@@ -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 @@
|
||||
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
|
||||