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);
|
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);
|
LeaderboardGroup loadLeaderboardGroup(String name);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return All groups in the database.
|
||||||
|
*/
|
||||||
Iterable<LeaderboardGroup> getAllLeaderboardGroups();
|
Iterable<LeaderboardGroup> getAllLeaderboardGroups();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return All leaderboards in the database, which aren't contained by a leaderboard group
|
||||||
|
*/
|
||||||
Iterable<Leaderboard> getLeaderboardsNotInGroup();
|
Iterable<Leaderboard> getLeaderboardsNotInGroup();
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -43,10 +43,20 @@ public interface MongoObjectFactory {
|
|||||||
|
|
||||||
void storeRaceIdentifier(RaceIdentifier raceIdentifier, DBObject dbObject);
|
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);
|
void storeLeaderboardGroup(LeaderboardGroup leaderboardGroup);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the group with the name <code>groupName</code> from the database.
|
||||||
|
*/
|
||||||
void removeLeaderboardGroup(String groupName);
|
void removeLeaderboardGroup(String groupName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Renames the group with the name <code>oldName</code>.
|
||||||
|
*/
|
||||||
void renameLeaderboardGroup(String oldName, String newName);
|
void renameLeaderboardGroup(String oldName, String newName);
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
package com.sap.sailing.domain.tracking.impl;
|
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.Competitor;
|
||||||
|
import com.sap.sailing.domain.base.Team;
|
||||||
import com.sap.sailing.domain.base.Waypoint;
|
import com.sap.sailing.domain.base.Waypoint;
|
||||||
import com.sap.sailing.domain.common.TimePoint;
|
import com.sap.sailing.domain.common.TimePoint;
|
||||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||||
@@ -25,7 +27,27 @@ public class DummyMarkPassingWithTimePointOnly implements MarkPassing {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Competitor getCompetitor() {
|
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>>();
|
markPassingsForCompetitor = new HashMap<Competitor, NavigableSet<MarkPassing>>();
|
||||||
tracks = new HashMap<Competitor, GPSFixTrack<Competitor, GPSFixMoving>>();
|
tracks = new HashMap<Competitor, GPSFixTrack<Competitor, GPSFixMoving>>();
|
||||||
for (Competitor competitor : race.getCompetitors()) {
|
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));
|
tracks.put(competitor, new DynamicGPSFixMovingTrackImpl<Competitor>(competitor, millisecondsOverWhichToAverageSpeed));
|
||||||
}
|
}
|
||||||
markPassingsForWaypoint = new HashMap<Waypoint, NavigableSet<MarkPassing>>();
|
markPassingsForWaypoint = new HashMap<Waypoint, NavigableSet<MarkPassing>>();
|
||||||
for (Waypoint waypoint : race.getCourse().getWaypoints()) {
|
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>();
|
windTracks = new HashMap<WindSource, WindTrack>();
|
||||||
for (WindSource windSource : WindSource.values()) {
|
for (WindSource windSource : WindSource.values()) {
|
||||||
@@ -305,9 +305,27 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
|||||||
NavigableSet<MarkPassing> roundings = markPassingsForCompetitor.get(competitor);
|
NavigableSet<MarkPassing> roundings = markPassingsForCompetitor.get(competitor);
|
||||||
MarkPassing lastBeforeOrAt = roundings.floor(new DummyMarkPassingWithTimePointOnly(at));
|
MarkPassing lastBeforeOrAt = roundings.floor(new DummyMarkPassingWithTimePointOnly(at));
|
||||||
TrackedLegOfCompetitor result = null;
|
TrackedLegOfCompetitor result = null;
|
||||||
|
TrackedLeg trackedLeg;
|
||||||
// already finished the race?
|
// already finished the race?
|
||||||
if (lastBeforeOrAt != null && getRace().getCourse().getLastWaypoint() != lastBeforeOrAt.getWaypoint()) {
|
if (lastBeforeOrAt != null) {
|
||||||
TrackedLeg trackedLeg = getTrackedLegStartingAt(lastBeforeOrAt.getWaypoint());
|
// 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);
|
result = trackedLeg.getTrackedLeg(competitor);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@@ -548,7 +566,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
|||||||
@Override
|
@Override
|
||||||
public synchronized void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) {
|
public synchronized void waypointAdded(int zeroBasedIndex, Waypoint waypointThatGotAdded) {
|
||||||
updateStartToNextMarkCacheInvalidationCacheListenersAfterWaypointAdded(zeroBasedIndex, 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()) {
|
for (Buoy buoy : waypointThatGotAdded.getBuoys()) {
|
||||||
getOrCreateTrack(buoy);
|
getOrCreateTrack(buoy);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -63,7 +63,9 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void add(Wind wind) {
|
public void add(Wind wind) {
|
||||||
getInternalRawFixes().add(wind);
|
synchronized (this) {
|
||||||
|
getInternalRawFixes().add(wind);
|
||||||
|
}
|
||||||
notifyListenersAboutReceive(wind);
|
notifyListenersAboutReceive(wind);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ import java.util.List;
|
|||||||
import junit.framework.Assert;
|
import junit.framework.Assert;
|
||||||
|
|
||||||
import org.json.simple.parser.ParseException;
|
import org.json.simple.parser.ParseException;
|
||||||
import org.junit.Ignore;
|
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import com.sap.sailing.domain.common.Placemark;
|
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.domain.common.impl.SerializablePositionImpl;
|
||||||
import com.sap.sailing.geocoding.ReverseGeocoder;
|
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 {
|
public class ReverseGeocoderTest {
|
||||||
|
|
||||||
private ReverseGeocoder geocoder = ReverseGeocoder.INSTANCE;
|
private ReverseGeocoder geocoder = ReverseGeocoder.INSTANCE;
|
||||||
private static final Placemark KIEL = new PlacemarkImpl("Kiel", "DE", new SerializablePositionImpl(54.32132926107913, 10.1348876953125), 232758);
|
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);
|
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.SailingServiceAsync;
|
||||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
|
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.leaderboard.SortableColumn;
|
||||||
import com.sap.sailing.gwt.ui.shared.LeaderboardDTO;
|
import com.sap.sailing.gwt.ui.shared.LeaderboardDTO;
|
||||||
import com.sap.sailing.gwt.ui.shared.LeaderboardRowDTO;
|
import com.sap.sailing.gwt.ui.shared.LeaderboardRowDTO;
|
||||||
@@ -16,7 +17,10 @@ public class LeaderboardPanelMock extends LeaderboardPanel {
|
|||||||
public LeaderboardPanelMock(SailingServiceAsync sailingService,
|
public LeaderboardPanelMock(SailingServiceAsync sailingService,
|
||||||
String leaderboardName, ErrorReporter errorReporter,
|
String leaderboardName, ErrorReporter errorReporter,
|
||||||
StringMessages stringConstants) {
|
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
|
// 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() {
|
private void moveToGroup() {
|
||||||
LeaderboardGroupDTO selectedGroup = groupsSelectionModel.getSelectedObject();
|
LeaderboardGroupDTO selectedGroup = groupsSelectionModel.getSelectedObject();
|
||||||
Set<LeaderboardDTO> selectedLeaderboards = leaderboardsSelectionModel.getSelectedSet();
|
ArrayList<LeaderboardDTO> selectedLeaderboards = new ArrayList<LeaderboardDTO>(leaderboardsSelectionModel.getSelectedSet());
|
||||||
if (selectedGroup != null && selectedLeaderboards != null && selectedLeaderboards.size() > 0) {
|
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) {
|
for (LeaderboardDTO leaderboard : selectedLeaderboards) {
|
||||||
if (!selectedGroup.leaderboards.contains(leaderboard)) {
|
if (!selectedGroup.leaderboards.contains(leaderboard)) {
|
||||||
selectedGroup.leaderboards.add(leaderboard);
|
selectedGroup.leaderboards.add(leaderboard);
|
||||||
|
|||||||
@@ -230,10 +230,11 @@ public class RaceMap extends SimplePanel implements TimeListener, CompetitorSele
|
|||||||
map.addMapZoomEndHandler(new MapZoomEndHandler() {
|
map.addMapZoomEndHandler(new MapZoomEndHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void onZoomEnd(MapZoomEndEvent event) {
|
public void onZoomEnd(MapZoomEndEvent event) {
|
||||||
|
map.checkResizeAndCenter();
|
||||||
mapZoomedOrPannedSinceLastRaceSelectionChange = true;
|
mapZoomedOrPannedSinceLastRaceSelectionChange = true;
|
||||||
Set<CompetitorDTO> competitorDTOsOfUnusedMarkers = new HashSet<CompetitorDTO>(boatMarkers.keySet());
|
Set<CompetitorDTO> competitorDTOsOfUnusedMarkers = new HashSet<CompetitorDTO>(boatMarkers.keySet());
|
||||||
for (CompetitorDTO competitorDTO : getCompetitorsToShow()) {
|
for (CompetitorDTO competitorDTO : getCompetitorsToShow()) {
|
||||||
boolean usedExistingMarker = updateMarkerForCompetitor(competitorDTO);
|
boolean usedExistingMarker = updateBoatMarkerForCompetitor(competitorDTO);
|
||||||
if (usedExistingMarker) {
|
if (usedExistingMarker) {
|
||||||
competitorDTOsOfUnusedMarkers.remove(competitorDTO);
|
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 {@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
|
* result guarantees that, when used in
|
||||||
* {@link SailingServiceAsync#getBoatPositions(String, String, Map, Map, boolean, AsyncCallback)}, for each
|
* {@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).
|
* 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()
|
* @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
|
* 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
|
* those requested or need to be replaced
|
||||||
*/
|
*/
|
||||||
protected Triple<Map<CompetitorDTO, Date>, Map<CompetitorDTO, Date>, Map<CompetitorDTO, Boolean>> computeFromAndTo(
|
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.before(timepointOfFirstKnownFix) && timepointOfLastKnownFix != null
|
||||||
&& !tailstart.after(timepointOfLastKnownFix)) {
|
&& !tailstart.after(timepointOfLastKnownFix)) {
|
||||||
// the beginning of what we need is contained in the interval we already have; skip what we already have
|
// 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;
|
fromDate = timepointOfLastKnownFix;
|
||||||
overlap = true;
|
overlap = true;
|
||||||
} else {
|
} 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
|
* Adds the 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>
|
* competitor. If <code>overlapsWithKnownFixes</code> indicates that the 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.
|
* overlap with those already known, the fixes are merged into the list of already known fixes for the competitor.
|
||||||
* Otherwise, the raceMapData.fixes received in <code>result</code> replace those known so far for the respective 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,
|
protected void updateFixes(Map<CompetitorDTO, List<GPSFixDTO>> result,
|
||||||
Map<CompetitorDTO, Boolean> overlapsWithKnownFixes) {
|
Map<CompetitorDTO, Boolean> overlapsWithKnownFixes) {
|
||||||
@@ -424,8 +426,8 @@ public class RaceMap extends SimplePanel implements TimeListener, CompetitorSele
|
|||||||
}
|
}
|
||||||
if (!overlapsWithKnownFixes.get(e.getKey())) {
|
if (!overlapsWithKnownFixes.get(e.getKey())) {
|
||||||
fixesForCompetitor.clear();
|
fixesForCompetitor.clear();
|
||||||
// to re-establish the invariants for raceMapData.tails, raceMapData.firstShownFix and raceMapData.lastShownFix, we now need to remove
|
// 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 raceMapData.firstShownFix and raceMapData.lastShownFix
|
// all points from the competitor's polyline and clear the entries in firstShownFix and lastShownFix
|
||||||
if (map != null && tails.containsKey(e.getKey())) {
|
if (map != null && tails.containsKey(e.getKey())) {
|
||||||
map.removeOverlay(tails.remove(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);
|
updateTail(tail, competitorDTO, from, to);
|
||||||
competitorDTOsOfUnusedTails.remove(competitorDTO);
|
competitorDTOsOfUnusedTails.remove(competitorDTO);
|
||||||
}
|
}
|
||||||
boolean usedExistingMarker = updateMarkerForCompetitor(competitorDTO);
|
boolean usedExistingMarker = updateBoatMarkerForCompetitor(competitorDTO);
|
||||||
if (usedExistingMarker) {
|
if (usedExistingMarker) {
|
||||||
competitorDTOsOfUnusedMarkers.remove(competitorDTO);
|
competitorDTOsOfUnusedMarkers.remove(competitorDTO);
|
||||||
}
|
}
|
||||||
@@ -499,88 +501,14 @@ public class RaceMap extends SimplePanel implements TimeListener, CompetitorSele
|
|||||||
|
|
||||||
private void zoomMapToNewBounds(LatLngBounds newBounds) {
|
private void zoomMapToNewBounds(LatLngBounds newBounds) {
|
||||||
if (newBounds != null) {
|
if (newBounds != null) {
|
||||||
map.setZoomLevel(map.getBoundsZoomLevel(newBounds));
|
boolean oldMapZoomedOrPannedSinceLastRaceSelectionChange = mapZoomedOrPannedSinceLastRaceSelectionChange;
|
||||||
map.setCenter(newBounds.getCenter());
|
map.setCenter(newBounds.getCenter());
|
||||||
map.checkResizeAndCenter();
|
map.setZoomLevel(map.getBoundsZoomLevel(newBounds));
|
||||||
|
mapZoomedOrPannedSinceLastRaceSelectionChange = oldMapZoomedOrPannedSinceLastRaceSelectionChange;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private boolean updateBoatMarkerForCompetitor(CompetitorDTO competitorDTO) {
|
||||||
* 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) {
|
|
||||||
boolean usedExistingMarker = false;
|
boolean usedExistingMarker = false;
|
||||||
if (lastShownFix.containsKey(competitorDTO) && lastShownFix.get(competitorDTO) != -1) {
|
if (lastShownFix.containsKey(competitorDTO) && lastShownFix.get(competitorDTO) != -1) {
|
||||||
GPSFixDTO lastPos = getBoatFix(competitorDTO);
|
GPSFixDTO lastPos = getBoatFix(competitorDTO);
|
||||||
@@ -1179,7 +1107,7 @@ public class RaceMap extends SimplePanel implements TimeListener, CompetitorSele
|
|||||||
}
|
}
|
||||||
for (CompetitorDTO competitor : competitors) {
|
for (CompetitorDTO competitor : competitors) {
|
||||||
List<GPSFixDTO> competitorFixes = forMap.fixes.get(competitor);
|
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;
|
PositionDTO competitorPosition = competitorFix != null ? competitorFix.position : null;
|
||||||
LatLng competitorLatLng = competitorPosition != null ? LatLng.newInstance(competitorPosition.latDeg,
|
LatLng competitorLatLng = competitorPosition != null ? LatLng.newInstance(competitorPosition.latDeg,
|
||||||
competitorPosition.lngDeg) : null;
|
competitorPosition.lngDeg) : null;
|
||||||
@@ -1235,8 +1163,7 @@ public class RaceMap extends SimplePanel implements TimeListener, CompetitorSele
|
|||||||
if (newBounds == null) {
|
if (newBounds == null) {
|
||||||
newBounds = bounds;
|
newBounds = bounds;
|
||||||
} else {
|
} else {
|
||||||
newBounds.extend(bounds.getNorthEast());
|
newBounds.extend(markLatLng);
|
||||||
newBounds.extend(bounds.getSouthWest());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ public class RaceMapSettings {
|
|||||||
|
|
||||||
private RaceMapZoomSettings zoomSettings;
|
private RaceMapZoomSettings zoomSettings;
|
||||||
|
|
||||||
private long tailLengthInMilliseconds = 30000l;
|
private long tailLengthInMilliseconds = 300000l;
|
||||||
|
|
||||||
public RaceMapSettings() {
|
public RaceMapSettings() {
|
||||||
maneuverTypesToShow = new HashSet<ManeuverType>();
|
maneuverTypesToShow = new HashSet<ManeuverType>();
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ public class RaceMapZoomSettings {
|
|||||||
typesToConsiderOnZoom = new ArrayList<ZoomTypes>();
|
typesToConsiderOnZoom = new ArrayList<ZoomTypes>();
|
||||||
typesToConsiderOnZoom.add(ZoomTypes.BOATS);
|
typesToConsiderOnZoom.add(ZoomTypes.BOATS);
|
||||||
typesToConsiderOnZoom.add(ZoomTypes.TAILS);
|
typesToConsiderOnZoom.add(ZoomTypes.TAILS);
|
||||||
|
typesToConsiderOnZoom.add(ZoomTypes.BUOYS);
|
||||||
zoomToSelectedCompetitors = false;
|
zoomToSelectedCompetitors = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -92,6 +92,7 @@ public class WindChart implements Component<WindChartSettings>, RaceSelectionCha
|
|||||||
chart.setToolTip(new ToolTip().setEnabled(true).setFormatter(new ToolTipFormatter() {
|
chart.setToolTip(new ToolTip().setEnabled(true).setFormatter(new ToolTipFormatter() {
|
||||||
@Override
|
@Override
|
||||||
public String format(ToolTipData toolTipData) {
|
public String format(ToolTipData toolTipData) {
|
||||||
|
// TODO consider using toolTipData.getPoint().getName() instead...
|
||||||
return "<b>" + toolTipData.getSeriesName() + (toolTipData.getPointName() != null ? " "+toolTipData.getPointName() : "")
|
return "<b>" + toolTipData.getSeriesName() + (toolTipData.getPointName() != null ? " "+toolTipData.getPointName() : "")
|
||||||
+ "</b><br/>" +
|
+ "</b><br/>" +
|
||||||
dateFormat.format(new Date(toolTipData.getXAsLong())) + ": " +
|
dateFormat.format(new Date(toolTipData.getXAsLong())) + ": " +
|
||||||
@@ -115,6 +116,7 @@ public class WindChart implements Component<WindChartSettings>, RaceSelectionCha
|
|||||||
onRaceSelectionChange(raceSelectionProvider.getSelectedRaces());
|
onRaceSelectionChange(raceSelectionProvider.getSelectedRaces());
|
||||||
}
|
}
|
||||||
showVisibleSeries();
|
showVisibleSeries();
|
||||||
|
timer.addTimeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -154,7 +156,11 @@ public class WindChart implements Component<WindChartSettings>, RaceSelectionCha
|
|||||||
return newSeries;
|
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");
|
final NumberFormat numberFormat = NumberFormat.getFormat("0");
|
||||||
for (Map.Entry<WindSource, WindTrackInfoDTO> e : result.windTrackInfoByWindSource.entrySet()) {
|
for (Map.Entry<WindSource, WindTrackInfoDTO> e : result.windTrackInfoByWindSource.entrySet()) {
|
||||||
WindSource windSource = e.getKey();
|
WindSource windSource = e.getKey();
|
||||||
@@ -175,7 +181,16 @@ public class WindChart implements Component<WindChartSettings>, RaceSelectionCha
|
|||||||
}
|
}
|
||||||
points[i++] = newPoint;
|
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
|
* 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
|
@Override
|
||||||
public void updateSettings(WindChartSettings newSettings) {
|
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,
|
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.
|
// 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
|
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
|
@Override
|
||||||
public void onSuccess(WindInfoForRaceDTO result) {
|
public void onSuccess(WindInfoForRaceDTO result) {
|
||||||
if (result != null) {
|
if (result != null) {
|
||||||
updateStripChartSeries(result);
|
updateStripChartSeries(result, append);
|
||||||
} else {
|
} 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();
|
selectedRaceIdentifier = selectedRaces.iterator().next();
|
||||||
timeOfEarliestRequestInMillis = null;
|
timeOfEarliestRequestInMillis = null;
|
||||||
timeOfLatestRequestInMillis = null;
|
timeOfLatestRequestInMillis = null;
|
||||||
loadData(selectedRaceIdentifier, /* from */ null, /* to */ null);
|
loadData(selectedRaceIdentifier, /* from */ null, /* to */ null, /* append */ false);
|
||||||
} else {
|
} else {
|
||||||
clearChart();
|
clearChart();
|
||||||
}
|
}
|
||||||
@@ -254,15 +276,15 @@ public class WindChart implements Component<WindChartSettings>, RaceSelectionCha
|
|||||||
if (timer.getPlayMode() == PlayModes.Live) {
|
if (timer.getPlayMode() == PlayModes.Live) {
|
||||||
// is date before first cache entry or is cache empty?
|
// is date before first cache entry or is cache empty?
|
||||||
if (timeOfEarliestRequestInMillis == null || timeOfEarliestRequestInMillis > date.getTime()) {
|
if (timeOfEarliestRequestInMillis == null || timeOfEarliestRequestInMillis > date.getTime()) {
|
||||||
loadData(selectedRaceIdentifier, null, date);
|
loadData(selectedRaceIdentifier, null, date, /* append */ true);
|
||||||
} else if (timeOfLatestRequestInMillis < date.getTime()) {
|
} 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
|
// otherwise the cache spans across date and so we don't need to load anything
|
||||||
} else {
|
} else {
|
||||||
// assuming play mode is replay / non-live
|
// assuming play mode is replay / non-live
|
||||||
if (timeOfLatestRequestInMillis == null) {
|
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);
|
grid.setWidget(3, 0, null);
|
||||||
VerticalPanel windDisplay = new VerticalPanel();
|
VerticalPanel windDisplay = new VerticalPanel();
|
||||||
grid.setWidget(3, 0, windDisplay);
|
grid.setWidget(3, 0, windDisplay);
|
||||||
windChart.updateStripChartSeries(result);
|
windChart.updateStripChartSeries(result, /* append */ false);
|
||||||
for (Map.Entry<WindSource, WindTrackInfoDTO> e : result.windTrackInfoByWindSource.entrySet()) {
|
for (Map.Entry<WindSource, WindTrackInfoDTO> e : result.windTrackInfoByWindSource.entrySet()) {
|
||||||
Label windSourceLabel = new Label(stringMessages.windSource()+": "+e.getKey()+
|
Label windSourceLabel = new Label(stringMessages.windSource()+": "+e.getKey()+
|
||||||
", "+stringMessages.dampeningInterval()+" "+e.getValue().dampeningIntervalInMilliseconds+"ms");
|
", "+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) {
|
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
|
// 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
|
// leg markers
|
||||||
long livePlayDelayInMillis = timer.getLivePlayDelayInMillis();
|
boolean liveModeToBeMadePossible = isLiveModeToBeMadePossible();
|
||||||
long liveTimePointInMillis = System.currentTimeMillis() - livePlayDelayInMillis;
|
setLiveGenerallyPossible(liveModeToBeMadePossible);
|
||||||
if (liveTimePointInMillis < raceTimesInfo.timePointOfNewestEvent.getTime()
|
setJumpToLiveEnablement(liveModeToBeMadePossible && timer.getPlayMode() != PlayModes.Live);
|
||||||
&& 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 timerAlreadyInitialized = getMin() != null && getMax() != null && sliderBar.getCurrentValue() != null;
|
boolean timerAlreadyInitialized = getMin() != null && getMax() != null && sliderBar.getCurrentValue() != null;
|
||||||
initMinMax(raceTimesInfo);
|
initMinMax(raceTimesInfo);
|
||||||
if (!timerAlreadyInitialized) {
|
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
|
@Override
|
||||||
public void onRaceSelectionChange(List<RaceIdentifier> selectedRaces) {
|
public void onRaceSelectionChange(List<RaceIdentifier> selectedRaces) {
|
||||||
if (selectedRaces != null && !selectedRaces.isEmpty()) {
|
if (selectedRaces != null && !selectedRaces.isEmpty()) {
|
||||||
@@ -133,20 +139,34 @@ public class RaceTimePanel extends TimePanel<RaceTimePanelSettings> implements R
|
|||||||
private void initMinMax(RaceTimesInfoDTO newRaceTimesInfo) {
|
private void initMinMax(RaceTimesInfoDTO newRaceTimesInfo) {
|
||||||
Date min = null;
|
Date min = null;
|
||||||
Date max = 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()) {
|
switch (timer.getPlayMode()) {
|
||||||
case Live:
|
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) {
|
if (newRaceTimesInfo.timePointOfNewestEvent != null) {
|
||||||
max = newRaceTimesInfo.timePointOfNewestEvent;
|
max = newRaceTimesInfo.timePointOfNewestEvent;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Replay:
|
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) {
|
if (newRaceTimesInfo.endOfRace != null) {
|
||||||
max = newRaceTimesInfo.endOfRace;
|
max = new Date(tempEndOfRace.getTime() + extensionTime);
|
||||||
} else if (newRaceTimesInfo.timePointOfNewestEvent != null) {
|
} else if (newRaceTimesInfo.timePointOfNewestEvent != null) {
|
||||||
max = newRaceTimesInfo.timePointOfNewestEvent;
|
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
|
* 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
|
* 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
|
@Override
|
||||||
public Widget getAdditionalWidget(DataEntryDialog<RaceTimePanelSettings> dialog) {
|
public Widget getAdditionalWidget(DataEntryDialog<RaceTimePanelSettings> dialog) {
|
||||||
Widget widget = super.getAdditionalWidget(dialog);
|
Widget widget = super.getAdditionalWidget(dialog);
|
||||||
|
|
||||||
HorizontalPanel labelAndTDelayForLiveBoxPanel = new HorizontalPanel();
|
HorizontalPanel labelAndTDelayForLiveBoxPanel = new HorizontalPanel();
|
||||||
labelAndTDelayForLiveBoxPanel.setSpacing(5);
|
labelAndTDelayForLiveBoxPanel.setSpacing(5);
|
||||||
labelAndTDelayForLiveBoxPanel.add(new Label("Delay for live mode:"));
|
labelAndTDelayForLiveBoxPanel.add(new Label(getStringMessages().delayForLiveMode()));
|
||||||
RaceTimesInfoDTO raceTimesInfo = initialSettings.getRaceTimesInfo();
|
RaceTimesInfoDTO raceTimesInfo = initialSettings.getRaceTimesInfo();
|
||||||
if(raceTimesInfo != null && raceTimesInfo.startOfTracking != null) {
|
if (raceTimesInfo != null && raceTimesInfo.startOfTracking != null) {
|
||||||
long delayforLiveModeInMs = System.currentTimeMillis() - raceTimesInfo.getStartOfTracking().getTime();
|
long delayforLiveModeInMs = System.currentTimeMillis() - raceTimesInfo.getStartOfTracking().getTime();
|
||||||
labelAndTDelayForLiveBoxPanel.add(new Label(delayforLiveModeInMs / 1000 + " s"));
|
labelAndTDelayForLiveBoxPanel.add(new Label(delayforLiveModeInMs / 1000 + " s"));
|
||||||
}
|
}
|
||||||
mainContentPanel.add(labelAndTDelayForLiveBoxPanel);
|
mainContentPanel.add(labelAndTDelayForLiveBoxPanel);
|
||||||
|
|
||||||
return widget;
|
return widget;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -159,8 +159,6 @@ public interface SailingService extends RemoteService {
|
|||||||
void renameLeaderboardGroup(String oldName, String newName);
|
void renameLeaderboardGroup(String oldName, String newName);
|
||||||
|
|
||||||
void removeLeaderboardGroup(String groupName);
|
void removeLeaderboardGroup(String groupName);
|
||||||
|
|
||||||
LeaderboardGroupDTO createLeaderboardGroup(String groupName, String description, List<String> leaderboardNames);
|
|
||||||
|
|
||||||
LeaderboardGroupDTO createLeaderboardGroup(String groupName, String description);
|
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.WindSource;
|
||||||
import com.sap.sailing.domain.common.impl.Util.Pair;
|
import com.sap.sailing.domain.common.impl.Util.Pair;
|
||||||
import com.sap.sailing.domain.leaderboard.RaceInLeaderboard;
|
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.CompetitorDTO;
|
||||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||||
import com.sap.sailing.gwt.ui.shared.GPSFixDTO;
|
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,
|
void getManeuvers(RaceIdentifier raceIdentifier, Map<CompetitorDTO, Date> from, Map<CompetitorDTO, Date> to,
|
||||||
AsyncCallback<Map<CompetitorDTO, List<ManeuverDTO>>> callback);
|
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);
|
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);
|
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);
|
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 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);
|
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);
|
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 directionChange();
|
||||||
String speedChange();
|
String speedChange();
|
||||||
String degreesShort();
|
String degreesShort();
|
||||||
|
String delayForLiveMode();
|
||||||
}
|
}
|
||||||
@@ -255,4 +255,5 @@ competitorCharts=Competitor charts
|
|||||||
selectAtLeastOneCompetitor=Select at least one competitor
|
selectAtLeastOneCompetitor=Select at least one competitor
|
||||||
directionChange=Direction change
|
directionChange=Direction change
|
||||||
speedChange=Speed 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
|
selectAtLeastOneCompetitor=Mindestens einen Segler auswählen
|
||||||
directionChange=Richtungsänderung
|
directionChange=Richtungsänderung
|
||||||
speedChange=Geschwindigkeitsä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
|
@Override
|
||||||
public void onValueChange(ValueChangeEvent<Double> newValue) {
|
public void onValueChange(ValueChangeEvent<Double> newValue) {
|
||||||
if(sliderBar.getCurrentValue() != null) {
|
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());
|
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);
|
playPauseImage.getElement().getStyle().setPadding(3, Style.Unit.PX);
|
||||||
playControlPanel.add(playPauseImage);
|
playControlPanel.add(playPauseImage);
|
||||||
|
|
||||||
backToLivePlayButton = new Button("Live");
|
backToLivePlayButton = new Button(stringMessages.playModeLive());
|
||||||
backToLivePlayButton.addClickHandler(new ClickHandler() {
|
backToLivePlayButton.addClickHandler(new ClickHandler() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(ClickEvent event) {
|
public void onClick(ClickEvent event) {
|
||||||
|
TimePanel.this.timer.setPlayMode(PlayModes.Live);
|
||||||
TimePanel.this.timer.play();
|
TimePanel.this.timer.play();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -327,20 +332,22 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void playStateChanged(PlayStates playState, PlayModes playMode) {
|
public void playStateChanged(PlayStates playState, PlayModes playMode) {
|
||||||
|
boolean liveModeToBeMadePossible = isLiveModeToBeMadePossible();
|
||||||
|
setLiveGenerallyPossible(liveModeToBeMadePossible);
|
||||||
|
setJumpToLiveEnablement(liveModeToBeMadePossible && playMode != PlayModes.Live);
|
||||||
switch (playState) {
|
switch (playState) {
|
||||||
case Playing:
|
case Playing:
|
||||||
playPauseImage.setResource(pauseButtonImg);
|
playPauseImage.setResource(pauseButtonImg);
|
||||||
if (playMode == PlayModes.Live)
|
if (playMode == PlayModes.Live) {
|
||||||
playModeImage.setResource(playModeLiveActiveImg);
|
playModeImage.setResource(playModeLiveActiveImg);
|
||||||
else
|
} else {
|
||||||
playModeImage.setResource(playModeReplayActiveImg);
|
playModeImage.setResource(playModeReplayActiveImg);
|
||||||
backToLivePlayButton.setEnabled(false);
|
}
|
||||||
break;
|
break;
|
||||||
case Paused:
|
case Paused:
|
||||||
case Stopped:
|
case Stopped:
|
||||||
playPauseImage.setResource(playButtonImg);
|
playPauseImage.setResource(playButtonImg);
|
||||||
playModeImage.setResource(playModeInactiveImg);
|
playModeImage.setResource(playModeInactiveImg);
|
||||||
backToLivePlayButton.setEnabled(true);
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -350,7 +357,6 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
|
|||||||
timeDelayLabel.setText(stringMessages.timeDelay() + ": " + timer.getCurrentDelayInMillis() / 1000 + " s");
|
timeDelayLabel.setText(stringMessages.timeDelay() + ": " + timer.getCurrentDelayInMillis() / 1000 + " s");
|
||||||
timeDelayLabel.setVisible(true);
|
timeDelayLabel.setVisible(true);
|
||||||
sliderBar.setEnabled(true);
|
sliderBar.setEnabled(true);
|
||||||
backToLivePlayButton.setVisible(true);
|
|
||||||
playSpeedBox.setEnabled(false);
|
playSpeedBox.setEnabled(false);
|
||||||
slowDownButton.setEnabled(false);
|
slowDownButton.setEnabled(false);
|
||||||
speedUpButton.setEnabled(false);
|
speedUpButton.setEnabled(false);
|
||||||
@@ -360,13 +366,30 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
|
|||||||
timeDelayLabel.setText("");
|
timeDelayLabel.setText("");
|
||||||
playModeLabel.setText(stringMessages.playModeReplay());
|
playModeLabel.setText(stringMessages.playModeReplay());
|
||||||
sliderBar.setEnabled(true);
|
sliderBar.setEnabled(true);
|
||||||
backToLivePlayButton.setVisible(false);
|
|
||||||
playSpeedBox.setEnabled(true);
|
playSpeedBox.setEnabled(true);
|
||||||
slowDownButton.setEnabled(true);
|
slowDownButton.setEnabled(true);
|
||||||
speedUpButton.setEnabled(true);
|
speedUpButton.setEnabled(true);
|
||||||
break;
|
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")
|
@SuppressWarnings("unchecked")
|
||||||
public T getSettings() {
|
public T getSettings() {
|
||||||
@@ -396,12 +419,11 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
|
|||||||
boolean delayChanged = newSettings.getDelayToLivePlayInSeconds() != getSettings().getDelayToLivePlayInSeconds();
|
boolean delayChanged = newSettings.getDelayToLivePlayInSeconds() != getSettings().getDelayToLivePlayInSeconds();
|
||||||
if (delayChanged) {
|
if (delayChanged) {
|
||||||
timer.setDelay(1000l * newSettings.getDelayToLivePlayInSeconds());
|
timer.setDelay(1000l * newSettings.getDelayToLivePlayInSeconds());
|
||||||
|
|
||||||
if(timer.getPlayMode() == PlayModes.Live) {
|
if(timer.getPlayMode() == PlayModes.Live) {
|
||||||
timeDelayLabel.setText(String.valueOf(newSettings.getDelayToLivePlayInSeconds()) + " s");
|
timeDelayLabel.setText(String.valueOf(newSettings.getDelayToLivePlayInSeconds()) + " s");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
timer.setRefreshInterval(getSettings().getRefreshInterval());
|
timer.setRefreshInterval(newSettings.getRefreshInterval());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -416,7 +438,6 @@ public class TimePanel<T extends TimePanelSettings> extends FormPanel implements
|
|||||||
if (child instanceof RequiresResize) {
|
if (child instanceof RequiresResize) {
|
||||||
((RequiresResize) child).onResize();
|
((RequiresResize) child).onResize();
|
||||||
}
|
}
|
||||||
|
|
||||||
sliderBar.onResize();
|
sliderBar.onResize();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,10 @@ public class TimePanelSettingsDialogComponent<T extends TimePanelSettings> imple
|
|||||||
this.stringMessages = stringMessages;
|
this.stringMessages = stringMessages;
|
||||||
initialSettings = settings;
|
initialSettings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected StringMessages getStringMessages() {
|
||||||
|
return stringMessages;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Widget getAdditionalWidget(DataEntryDialog<T> dialog) {
|
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
|
* 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)
|
* @author Axel Uhl (d043530)
|
||||||
*
|
*
|
||||||
@@ -43,7 +47,8 @@ public class Timer {
|
|||||||
private PlayStates playState;
|
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;
|
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.
|
* 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;
|
private double playSpeedFactor;
|
||||||
|
|
||||||
@@ -143,36 +149,46 @@ public class Timer {
|
|||||||
return time;
|
return time;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* When setting the play mode to live, the timer will automatically be put into play state playing.
|
||||||
|
*/
|
||||||
public void setPlayMode(PlayModes newPlayMode) {
|
public void setPlayMode(PlayModes newPlayMode) {
|
||||||
if (this.playMode != newPlayMode) {
|
if (this.playMode != newPlayMode) {
|
||||||
this.playMode = newPlayMode;
|
this.playMode = newPlayMode;
|
||||||
for (PlayStateListener playStateListener : playStateListeners) {
|
for (PlayStateListener playStateListener : playStateListeners) {
|
||||||
playStateListener.playStateChanged(playState, playMode);
|
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
|
* 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() {
|
public void pause() {
|
||||||
if (playState == PlayStates.Playing) {
|
if (playState == PlayStates.Playing) {
|
||||||
playState = PlayStates.Paused; // this will cause the repeating command to stop executing
|
playState = PlayStates.Paused; // this will cause the repeating command to stop executing
|
||||||
|
|
||||||
for (PlayStateListener playStateListener : playStateListeners) {
|
for (PlayStateListener playStateListener : playStateListeners) {
|
||||||
playStateListener.playStateChanged(playState, playMode);
|
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() {
|
public void stop() {
|
||||||
if (playState == PlayStates.Playing) {
|
if (playState == PlayStates.Playing) {
|
||||||
playState = PlayStates.Stopped;
|
playState = PlayStates.Stopped;
|
||||||
|
|
||||||
for (PlayStateListener playStateListener : playStateListeners) {
|
for (PlayStateListener playStateListener : playStateListeners) {
|
||||||
playStateListener.playStateChanged(playState, playMode);
|
playStateListener.playStateChanged(playState, playMode);
|
||||||
}
|
}
|
||||||
|
setPlayMode(PlayModes.Replay);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,10 +211,11 @@ public class Timer {
|
|||||||
public boolean execute() {
|
public boolean execute() {
|
||||||
if (time != null && playState == PlayStates.Playing) {
|
if (time != null && playState == PlayStates.Playing) {
|
||||||
long newTime = time.getTime();
|
long newTime = time.getTime();
|
||||||
if (playMode == PlayModes.Replay)
|
if (playMode == PlayModes.Replay) {
|
||||||
newTime += (long) playSpeedFactor * refreshInterval;
|
newTime += (long) playSpeedFactor * refreshInterval;
|
||||||
else
|
} else {
|
||||||
newTime += refreshInterval;
|
newTime = System.currentTimeMillis() - getLivePlayDelayInMillis();
|
||||||
|
}
|
||||||
setTime(newTime);
|
setTime(newTime);
|
||||||
}
|
}
|
||||||
if (refreshIntervalChanged) {
|
if (refreshIntervalChanged) {
|
||||||
|
|||||||
@@ -124,6 +124,7 @@ implements CompetitorSelectionChangeListener, RaceSelectionChangeListener, TimeL
|
|||||||
if(!selectedRaces.isEmpty()) {
|
if(!selectedRaces.isEmpty()) {
|
||||||
loadData();
|
loadData();
|
||||||
}
|
}
|
||||||
|
timer.addTimeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void selectRace(final RaceIdentifier selectedRace) {
|
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.StringMessages;
|
||||||
import com.sap.sailing.gwt.ui.client.UserAgentChecker.UserAgentTypes;
|
import com.sap.sailing.gwt.ui.client.UserAgentChecker.UserAgentTypes;
|
||||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
|
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.CompetitorDTO;
|
||||||
import com.sap.sailing.gwt.ui.shared.LeaderboardDTO;
|
import com.sap.sailing.gwt.ui.shared.LeaderboardDTO;
|
||||||
import com.sap.sailing.gwt.ui.shared.LeaderboardEntryDTO;
|
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,
|
public EditableLeaderboardPanel(SailingServiceAsync sailingService, String leaderboardName, String leaderboardGroupName,
|
||||||
ErrorReporter errorReporter, StringMessages stringConstants, UserAgentTypes userAgentType) {
|
ErrorReporter errorReporter, StringMessages stringConstants, UserAgentTypes userAgentType) {
|
||||||
super(sailingService, null, new CompetitorSelectionModel(/* hasMultiSelection */ true), leaderboardName, leaderboardGroupName,
|
super(sailingService, LeaderboardSettingsFactory.getInstance().createNewDefaultSettings(
|
||||||
errorReporter, stringConstants, userAgentType);
|
/* autoExpandFirstRace */false), new CompetitorSelectionModel(/* hasMultiSelection */true),
|
||||||
|
leaderboardName, leaderboardGroupName, errorReporter, stringConstants, userAgentType);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -32,31 +32,26 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint {
|
|||||||
@Override
|
@Override
|
||||||
public void onModuleLoad() {
|
public void onModuleLoad() {
|
||||||
super.onModuleLoad();
|
super.onModuleLoad();
|
||||||
|
|
||||||
eventName = Window.Location.getParameter("eventName");
|
eventName = Window.Location.getParameter("eventName");
|
||||||
raceName = Window.Location.getParameter("raceName");
|
raceName = Window.Location.getParameter("raceName");
|
||||||
String leaderboardNameParamValue = Window.Location.getParameter("leaderboardName");
|
String leaderboardNameParamValue = Window.Location.getParameter("leaderboardName");
|
||||||
String leaderboardGroupNameParamValue = Window.Location.getParameter("leaderboardGroupName");
|
String leaderboardGroupNameParamValue = Window.Location.getParameter("leaderboardGroupName");
|
||||||
|
|
||||||
if(leaderboardNameParamValue == null || leaderboardNameParamValue.isEmpty()) {
|
if(leaderboardNameParamValue == null || leaderboardNameParamValue.isEmpty()) {
|
||||||
leaderboardName = DefaultLeaderboardName.DEFAULT_LEADERBOARD_NAME;
|
leaderboardName = DefaultLeaderboardName.DEFAULT_LEADERBOARD_NAME;
|
||||||
} else {
|
} else {
|
||||||
leaderboardName = leaderboardNameParamValue;
|
leaderboardName = leaderboardNameParamValue;
|
||||||
}
|
}
|
||||||
|
if(leaderboardGroupNameParamValue != null && !leaderboardGroupNameParamValue.isEmpty()) {
|
||||||
if(leaderboardGroupNameParamValue != null && !leaderboardGroupNameParamValue.isEmpty())
|
|
||||||
leaderboardGroupName = leaderboardGroupNameParamValue;
|
leaderboardGroupName = leaderboardGroupNameParamValue;
|
||||||
|
}
|
||||||
if (eventName == null || eventName.isEmpty() || raceName == null || raceName.isEmpty()) {
|
if (eventName == null || eventName.isEmpty() || raceName == null || raceName.isEmpty()) {
|
||||||
createErrorPage("This page requires a valid event name and race name.");
|
createErrorPage("This page requires a valid event name and race name.");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final ParallelExecutionCallback<List<String>> getLeaderboardNamesCallback = new ParallelExecutionCallback<List<String>>();
|
final ParallelExecutionCallback<List<String>> getLeaderboardNamesCallback = new ParallelExecutionCallback<List<String>>();
|
||||||
final ParallelExecutionCallback<List<EventDTO>> listEventsCallback = new ParallelExecutionCallback<List<EventDTO>>();
|
final ParallelExecutionCallback<List<EventDTO>> listEventsCallback = new ParallelExecutionCallback<List<EventDTO>>();
|
||||||
final ParallelExecutionCallback<LeaderboardGroupDTO> getLeaderboardGroupByNameCallback = new ParallelExecutionCallback<LeaderboardGroupDTO>();
|
final ParallelExecutionCallback<LeaderboardGroupDTO> getLeaderboardGroupByNameCallback = new ParallelExecutionCallback<LeaderboardGroupDTO>();
|
||||||
final ParallelExecutionCallback<UserDTO> getUserCallback = new ParallelExecutionCallback<UserDTO>();
|
final ParallelExecutionCallback<UserDTO> getUserCallback = new ParallelExecutionCallback<UserDTO>();
|
||||||
|
|
||||||
if (leaderboardGroupName != null) {
|
if (leaderboardGroupName != null) {
|
||||||
new ParallelExecutionHolder(getLeaderboardNamesCallback, getLeaderboardGroupByNameCallback, listEventsCallback, getUserCallback) {
|
new ParallelExecutionHolder(getLeaderboardNamesCallback, getLeaderboardGroupByNameCallback, listEventsCallback, getUserCallback) {
|
||||||
@Override
|
@Override
|
||||||
@@ -81,21 +76,19 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
sailingService.listEvents(false, listEventsCallback);
|
sailingService.listEvents(false, listEventsCallback);
|
||||||
sailingService.getLeaderboardNames(getLeaderboardNamesCallback);
|
sailingService.getLeaderboardNames(getLeaderboardNamesCallback);
|
||||||
if(leaderboardGroupName != null)
|
if(leaderboardGroupName != null) {
|
||||||
sailingService.getLeaderboardGroupByName(leaderboardGroupNameParamValue, getLeaderboardGroupByNameCallback);
|
sailingService.getLeaderboardGroupByName(leaderboardGroupNameParamValue, getLeaderboardGroupByNameCallback);
|
||||||
|
}
|
||||||
userManagementService.getUser(getUserCallback);
|
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)) {
|
if (!leaderboardNames.contains(leaderboardName)) {
|
||||||
createErrorPage(stringMessages.noSuchLeaderboard());
|
createErrorPage(stringMessages.noSuchLeaderboard());
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (leaderboardGroupName != null && leaderboardGroup != null) {
|
if (leaderboardGroupName != null && leaderboardGroup != null) {
|
||||||
boolean foundLeaderboard = false;
|
boolean foundLeaderboard = false;
|
||||||
for(LeaderboardDTO leaderBoard: leaderboardGroup.leaderboards) {
|
for(LeaderboardDTO leaderBoard: leaderboardGroup.leaderboards) {
|
||||||
@@ -109,13 +102,11 @@ public class RaceBoardEntryPoint extends AbstractEntryPoint {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
selectedRace = findRace(eventName, raceName, events);
|
selectedRace = findRace(eventName, raceName, events);
|
||||||
if(selectedRace == null) {
|
if(selectedRace == null) {
|
||||||
createErrorPage("Could not obtain a race with name " + raceName + " for an event with name " + eventName);
|
createErrorPage("Could not obtain a race with name " + raceName + " for an event with name " + eventName);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
createRaceBoardPanel(selectedRace, events, user);
|
createRaceBoardPanel(selectedRace, events, user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -75,13 +75,11 @@ public class RaceBoardPanel extends FormPanel implements EventDisplayer, RaceSel
|
|||||||
private final RaceTimePanel timePanel;
|
private final RaceTimePanel timePanel;
|
||||||
private final Timer timer;
|
private final Timer timer;
|
||||||
private final RaceSelectionProvider raceSelectionProvider;
|
private final RaceSelectionProvider raceSelectionProvider;
|
||||||
private final UserDTO user;
|
|
||||||
|
|
||||||
public RaceBoardPanel(SailingServiceAsync sailingService, UserDTO theUser, RaceSelectionProvider theRaceSelectionProvider, String leaderboardName,
|
public RaceBoardPanel(SailingServiceAsync sailingService, UserDTO theUser, RaceSelectionProvider theRaceSelectionProvider, String leaderboardName,
|
||||||
String leaderboardGroupName, ErrorReporter errorReporter, final StringMessages stringMessages, UserAgentTypes userAgentType) {
|
String leaderboardGroupName, ErrorReporter errorReporter, final StringMessages stringMessages, UserAgentTypes userAgentType) {
|
||||||
this.sailingService = sailingService;
|
this.sailingService = sailingService;
|
||||||
this.raceSelectionProvider = theRaceSelectionProvider;
|
this.raceSelectionProvider = theRaceSelectionProvider;
|
||||||
this.user = theUser;
|
|
||||||
this.scrollOffset = 0;
|
this.scrollOffset = 0;
|
||||||
raceSelectionProvider.addRaceSelectionChangeListener(this);
|
raceSelectionProvider.addRaceSelectionChangeListener(this);
|
||||||
racesByIdentifier = new HashMap<RaceIdentifier, RaceDTO>();
|
racesByIdentifier = new HashMap<RaceIdentifier, RaceDTO>();
|
||||||
@@ -113,14 +111,7 @@ public class RaceBoardPanel extends FormPanel implements EventDisplayer, RaceSel
|
|||||||
|
|
||||||
boolean showLeaderboard = true;
|
boolean showLeaderboard = true;
|
||||||
boolean showMap = true;
|
boolean showMap = true;
|
||||||
boolean showWindCharts = true;
|
|
||||||
boolean showCompetitorCharts = true;
|
boolean showCompetitorCharts = true;
|
||||||
|
|
||||||
if (user == null) {
|
|
||||||
// anonymous user
|
|
||||||
showWindCharts = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// create the default leaderboard and select the right race
|
// create the default leaderboard and select the right race
|
||||||
if(showLeaderboard) {
|
if(showLeaderboard) {
|
||||||
LeaderboardSettings leaderBoardSettings = LeaderboardSettingsFactory.getInstance().createNewSettingsForPlayMode(timer.getPlayMode());
|
LeaderboardSettings leaderBoardSettings = LeaderboardSettingsFactory.getInstance().createNewSettingsForPlayMode(timer.getPlayMode());
|
||||||
@@ -142,16 +133,15 @@ public class RaceBoardPanel extends FormPanel implements EventDisplayer, RaceSel
|
|||||||
raceMap.onRaceSelectionChange(Collections.singletonList(selectedRaceIdentifier));
|
raceMap.onRaceSelectionChange(Collections.singletonList(selectedRaceIdentifier));
|
||||||
collapsableViewers.add(raceMapViewer);
|
collapsableViewers.add(raceMapViewer);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(showWindCharts) {
|
WindChartSettings windChartSettings = new WindChartSettings(WindSource.values());
|
||||||
WindChartSettings windChartSettings = new WindChartSettings(WindSource.values());
|
WindChart windChart = new WindChart(sailingService, raceSelectionProvider, timer, windChartSettings,
|
||||||
WindChart windChart = new WindChart(sailingService, raceSelectionProvider, timer, windChartSettings, stringMessages, errorReporter);
|
stringMessages, errorReporter);
|
||||||
CollapsableComponentViewer<WindChartSettings> windChartViewer = new CollapsableComponentViewer<WindChartSettings>(
|
CollapsableComponentViewer<WindChartSettings> windChartViewer = new CollapsableComponentViewer<WindChartSettings>(
|
||||||
windChart, "auto", "400px", stringMessages);
|
windChart, "auto", "400px", stringMessages);
|
||||||
windChart.onRaceSelectionChange(raceSelectionProvider.getSelectedRaces());
|
windChart.onRaceSelectionChange(raceSelectionProvider.getSelectedRaces());
|
||||||
collapsableViewers.add(windChartViewer);
|
collapsableViewers.add(windChartViewer);
|
||||||
}
|
if (showCompetitorCharts) {
|
||||||
if(showCompetitorCharts) {
|
|
||||||
// DON'T DELETE -> this is temporary for testing of different chart types
|
// DON'T DELETE -> this is temporary for testing of different chart types
|
||||||
// ChartPanel competitorCharts = new ChartPanel(sailingService, competitorSelectionModel, raceSelectionProvider,
|
// ChartPanel competitorCharts = new ChartPanel(sailingService, competitorSelectionModel, raceSelectionProvider,
|
||||||
// timer, DetailType.WINDWARD_DISTANCE_TO_OVERALL_LEADER, stringMessages, errorReporter);
|
// timer, DetailType.WINDWARD_DISTANCE_TO_OVERALL_LEADER, stringMessages, errorReporter);
|
||||||
|
|||||||
@@ -781,7 +781,7 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
|
|||||||
while (iterator.hasNext()) {
|
while (iterator.hasNext()) {
|
||||||
MarkPassing currentMarkPassing = iterator.next();
|
MarkPassing currentMarkPassing = iterator.next();
|
||||||
long diff = currentMarkPassing.getTimePoint().asMillis() - currentTimeToCheck;
|
long diff = currentMarkPassing.getTimePoint().asMillis() - currentTimeToCheck;
|
||||||
if(diff > maxTimeFrameInMs) {
|
if (diff > maxTimeFrameInMs) {
|
||||||
// reset the check
|
// reset the check
|
||||||
currentCompetitorsInTime = 0;
|
currentCompetitorsInTime = 0;
|
||||||
currentTimeToCheck = currentMarkPassing.getTimePoint().asMillis();
|
currentTimeToCheck = currentMarkPassing.getTimePoint().asMillis();
|
||||||
@@ -1576,14 +1576,9 @@ public class SailingServiceImpl extends RemoteServiceServlet implements SailingS
|
|||||||
getService().removeLeaderboardGroup(groupName);
|
getService().removeLeaderboardGroup(groupName);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public LeaderboardGroupDTO createLeaderboardGroup(String groupName, String description, List<String> leaderboardNames) {
|
|
||||||
return convertToLeaderboardGroupDTO(getService().addLeaderboardGroup(groupName, description, leaderboardNames));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public LeaderboardGroupDTO createLeaderboardGroup(String groupName, String description) {
|
public LeaderboardGroupDTO createLeaderboardGroup(String groupName, String description) {
|
||||||
return createLeaderboardGroup(groupName, description, new ArrayList<String>());
|
return convertToLeaderboardGroupDTO(getService().addLeaderboardGroup(groupName, description, new ArrayList<String>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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.SailingServiceAsync;
|
||||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel;
|
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.EventDTO;
|
||||||
import com.sap.sailing.gwt.ui.shared.LeaderboardDTO;
|
import com.sap.sailing.gwt.ui.shared.LeaderboardDTO;
|
||||||
|
|
||||||
@@ -479,7 +480,8 @@ public class OverviewEventPanel extends AbstractEventPanel {
|
|||||||
}
|
}
|
||||||
currentLeaderboard = boardToDisplay;
|
currentLeaderboard = boardToDisplay;
|
||||||
CompetitorSelectionModel competitorSelectionModel = new CompetitorSelectionModel(true);
|
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);
|
currentLeaderboard.name, null, errorReporter, stringConstants, null);
|
||||||
leaderboardPanel.add(displayedLeaderboardPanel);
|
leaderboardPanel.add(displayedLeaderboardPanel);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,10 +20,9 @@ public class SpectatorEntryPoint extends AbstractEntryPoint implements EventRefr
|
|||||||
@Override
|
@Override
|
||||||
public void onModuleLoad() {
|
public void onModuleLoad() {
|
||||||
super.onModuleLoad();
|
super.onModuleLoad();
|
||||||
|
|
||||||
String groupParamValue = Window.Location.getParameter("leaderboardGroupName");
|
String groupParamValue = Window.Location.getParameter("leaderboardGroupName");
|
||||||
final String groupName;
|
final String groupName;
|
||||||
if(groupParamValue == null || groupParamValue.isEmpty()) {
|
if (groupParamValue == null || groupParamValue.isEmpty()) {
|
||||||
groupName = null;
|
groupName = null;
|
||||||
} else {
|
} else {
|
||||||
groupName = groupParamValue;
|
groupName = groupParamValue;
|
||||||
@@ -36,7 +35,6 @@ public class SpectatorEntryPoint extends AbstractEntryPoint implements EventRefr
|
|||||||
public void onSuccess(LeaderboardGroupDTO group) {}
|
public void onSuccess(LeaderboardGroupDTO group) {}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
String root = Window.Location.getParameter("root");
|
String root = Window.Location.getParameter("root");
|
||||||
//Check if the root contains an allowed value
|
//Check if the root contains an allowed value
|
||||||
if (root != null) {
|
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);
|
TrackedRace getExistingTrackedRace(RaceIdentifier raceIdentifier);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Obtains an unmodifiable map of the leaderboard groups configured in this service keyed by their names.
|
||||||
|
*/
|
||||||
Map<String, LeaderboardGroup> getLeaderboardGroups();
|
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);
|
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);
|
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);
|
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);
|
void renameLeaderboardGroup(String oldName, String newName);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the group data in the persistant store.
|
||||||
|
*/
|
||||||
void updateStoredLeaderboardGroup(LeaderboardGroup leaderboardGroup);
|
void updateStoredLeaderboardGroup(LeaderboardGroup leaderboardGroup);
|
||||||
|
|
||||||
void addExpeditionListener(ExpeditionListener listener, boolean validMessagesOnly) throws SocketException;
|
void addExpeditionListener(ExpeditionListener listener, boolean validMessagesOnly) throws SocketException;
|
||||||
|
|||||||
@@ -7,6 +7,8 @@ import java.net.InetAddress;
|
|||||||
import java.net.SocketException;
|
import java.net.SocketException;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.concurrent.LinkedBlockingDeque;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
public abstract class UDPReceiver<MessageType extends UDPMessage, ListenerType extends UDPMessageListener<MessageType>> implements Runnable {
|
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 int listeningOnPort;
|
||||||
|
|
||||||
private final DatagramSocket udpSocket;
|
private final DatagramSocket udpSocket;
|
||||||
|
|
||||||
|
private final Map<ListenerType, ToListenerDispatcher> listenerThreads;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* For each listener remembers if the listener is only interested in valid messages.
|
* 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 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,
|
* You need call {@link #run} to actually start receiving events. To do this asynchronously,
|
||||||
* start this object in a new thread.
|
* start this object in a new thread.
|
||||||
@@ -36,6 +79,7 @@ public abstract class UDPReceiver<MessageType extends UDPMessage, ListenerType e
|
|||||||
udpSocket = new DatagramSocket(listeningOnPort);
|
udpSocket = new DatagramSocket(listeningOnPort);
|
||||||
listeners = new HashMap<ListenerType, Boolean>();
|
listeners = new HashMap<ListenerType, Boolean>();
|
||||||
this.listeningOnPort = listeningOnPort;
|
this.listeningOnPort = listeningOnPort;
|
||||||
|
listenerThreads = new HashMap<ListenerType, ToListenerDispatcher>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void stop() throws SocketException, IOException {
|
public void stop() throws SocketException, IOException {
|
||||||
@@ -78,7 +122,8 @@ public abstract class UDPReceiver<MessageType extends UDPMessage, ListenerType e
|
|||||||
for (ListenerType listener : listeners.keySet()) {
|
for (ListenerType listener : listeners.keySet()) {
|
||||||
if (!listeners.get(listener) || msg.isValid()) {
|
if (!listeners.get(listener) || msg.isValid()) {
|
||||||
try {
|
try {
|
||||||
listener.received(msg);
|
ToListenerDispatcher dispatcher = listenerThreads.get(listener);
|
||||||
|
dispatcher.dispatch(msg);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
logger.info("Exception while dispatching UDP packet received to "+listener+": "+t.getMessage());
|
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) {
|
public synchronized void addListener(ListenerType listener, boolean validMessagesOnly) {
|
||||||
|
ToListenerDispatcher listenerThread = new ToListenerDispatcher(listener);
|
||||||
|
listenerThread.start();
|
||||||
|
listenerThreads.put(listener, listenerThread);
|
||||||
listeners.put(listener, validMessagesOnly);
|
listeners.put(listener, validMessagesOnly);
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void removeListener(ListenerType listener) {
|
public synchronized void removeListener(ListenerType listener) {
|
||||||
listeners.remove(listener);
|
listeners.remove(listener);
|
||||||
|
listenerThreads.remove(listener).halt();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getPort() {
|
public int getPort() {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ active_branch=$(git symbolic-ref -q HEAD)
|
|||||||
active_branch=`basename $active_branch`
|
active_branch=`basename $active_branch`
|
||||||
echo Active branch is: $active_branch
|
echo Active branch is: $active_branch
|
||||||
cd $exec_dir
|
cd $exec_dir
|
||||||
server_instance=`basename \`pwd\` | sed -e 's/[12]$//'`
|
server_instance=`basename \`pwd\``
|
||||||
echo Active server is: $server_instance
|
echo Active server is: $server_instance
|
||||||
if [ $active_branch == $server_instance ]
|
if [ $active_branch == $server_instance ]
|
||||||
then
|
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
|
||||||